From f0e029f5ddbd31d87c937004051a18feff54cf9f Mon Sep 17 00:00:00 2001 From: Kachalov Anton Date: Thu, 4 Aug 2005 11:03:31 +0000 Subject: [PATCH] 0.7.1-alt1 - 0.7.1 - Updated: * Kqemu to 0.7.1-1 * GTK support --- .gear-rules | 4 +- kqemu-permission | 35 + kqemu/Changelog | 11 + kqemu/Makefile | 15 +- kqemu/Makefile.freebsd | 10 + kqemu/Makefile.winnt | 38 + kqemu/kmod.c | 312 --- kqemu/kqemu-doc.html | 80 +- kqemu/kqemu-doc.texi | 50 +- kqemu/kqemu-freebsd.c | 301 +++ kqemu/kqemu-linux.c | 365 ++++ kqemu/kqemu-mod-i386-win32.o | Bin 0 -> 34058 bytes kqemu/kqemu-mod-i386.o | Bin 36666 -> 33969 bytes kqemu/kqemu-mod-x86_64.o | Bin 0 -> 36909 bytes kqemu/kqemu-win32.c | 333 +++ kqemu/kqemu.h | 68 +- kqemu/kqemu.reg | 7 + kqemu/kqemu.sys | Bin 0 -> 56431 bytes qemu-0.7.0-alt-makefile.patch | 53 - qemu-0.7.1-alt-guiargs.patch | 19 + qemu-0.7.1-alt-makefile.patch | 53 + qemu-gtk/callbacks.c | 171 ++ qemu-gtk/callbacks.h | 82 + qemu-gtk/fullscreen.h | 6 + qemu-gtk/gdk_keysym.h | 280 +++ qemu-gtk/gdk_set_window_pointer.c | 54 + qemu-gtk/gtk2.c | 946 ++++++++ qemu-gtk/gtk2gui.c | 872 ++++++++ qemu-gtk/interface.c | 1172 ++++++++++ qemu-gtk/interface.h | 9 + qemu-gtk/null_fs.c | 21 + qemu-gtk/qemu-gtk-patch.diff | 258 +++ qemu-gtk/support.c | 144 ++ qemu-gtk/support.h | 69 + qemu-gtk/xvid_fs.c | 99 + qemu.spec | 35 +- qemu/.#block.c.1.18 | 655 ------ qemu/.cvsignore | 2 + qemu/Changelog | 11 + qemu/Makefile | 6 +- qemu/Makefile.target | 108 +- qemu/VERSION | 2 +- qemu/audio/ossaudio.c | 2 +- qemu/block-cow.c | 3 +- qemu/block-dmg.c | 4 +- qemu/block-qcow.c | 21 +- qemu/block-vmdk.c | 105 +- qemu/block-vpc.c | 3 +- qemu/block-vvfat.c | 1742 +++++++++++++++ qemu/block.c | 42 +- qemu/configure | 38 +- qemu/cpu-all.h | 7 + qemu/cpu-defs.h | 12 +- qemu/cpu-exec.c | 125 +- qemu/dis-asm.h | 20 + qemu/disas.c | 39 + qemu/dyngen-exec.h | 1 + qemu/elf.h | 18 + qemu/exec-all.h | 9 +- qemu/exec.c | 183 +- qemu/gdbstub.c | 55 +- qemu/hw/apic.c | 553 ++++- qemu/hw/cirrus_vga_rop2.h | 2 +- qemu/hw/cuda.c | 90 +- qemu/hw/elf_ops.h | 218 ++ qemu/hw/heathrow_pic.c | 168 ++ qemu/hw/i8259.c | 156 +- qemu/hw/ide.c | 271 ++- qemu/hw/magic-load.c | 282 +-- qemu/hw/mips_r4k.c | 254 +++ qemu/hw/ne2000.c | 13 +- qemu/hw/openpic.c | 3 +- qemu/hw/pc.c | 53 +- qemu/hw/pci.c | 453 +++- qemu/hw/ppc.c | 28 +- qemu/hw/ppc_chrp.c | 445 +++- qemu/hw/ppc_prep.c | 62 +- qemu/hw/slavio_intctl.c | 6 +- qemu/hw/slavio_misc.c | 240 ++ qemu/hw/sun4m.c | 24 +- qemu/hw/sun4u.c | 375 ++++ qemu/hw/vga.c | 21 +- qemu/hw/vga_int.h | 2 + qemu/linux-user/arm-semi.c | 2 +- qemu/linux-user/main.c | 74 +- qemu/linux-user/syscall.c | 84 +- qemu/mips-dis.c | 3998 ++++++++++++++++++++++++++++++++++ qemu/monitor.c | 43 +- qemu/pc-bios/README | 6 +- qemu/pc-bios/ohw.diff | 1843 ++++++++++++++++ qemu/pc-bios/ppc_rom.bin | Bin 524288 -> 524288 bytes qemu/pc-bios/video.x | Bin 0 -> 12192 bytes qemu/ppc-dis.c | 46 +- qemu/ppc.ld | 10 + qemu/qemu-doc.html | 187 +- qemu/qemu-doc.texi | 105 +- qemu/qemu-img.1 | 5 +- qemu/qemu-img.c | 9 +- qemu/qemu-img.texi | 3 +- qemu/qemu-tech.html | 37 +- qemu/qemu-tech.texi | 28 +- qemu/qemu.1 | 17 +- qemu/sdl.c | 1 - qemu/slirp/bootp.c | 2 +- qemu/slirp/bootp.h | 6 +- qemu/slirp/ip_icmp.h | 4 +- qemu/slirp/libslirp.h | 8 + qemu/slirp/slirp_config.h | 3 +- qemu/slirp/udp.c | 18 +- qemu/slirp/udp.h | 1 + qemu/softmmu_header.h | 4 + qemu/softmmu_template.h | 20 +- qemu/target-arm/op.c | 17 + qemu/target-arm/translate.c | 17 +- qemu/target-i386/cpu.h | 10 + qemu/target-i386/exec.h | 6 +- qemu/target-i386/helper.c | 104 +- qemu/target-i386/helper2.c | 69 +- qemu/target-i386/op.c | 33 +- qemu/target-i386/translate.c | 120 +- qemu/target-mips/cpu.h | 247 +++ qemu/target-mips/exec.h | 166 ++ qemu/target-mips/helper.c | 422 ++++ qemu/target-mips/mips-defs.h | 58 + qemu/target-mips/op.c | 668 ++++++ qemu/target-mips/op_helper.c | 677 ++++++ qemu/target-mips/op_helper_mem.c | 143 ++ qemu/target-mips/op_mem.c | 113 + qemu/target-mips/op_template.c | 65 + qemu/target-mips/translate.c | 1688 ++++++++++++++ qemu/target-ppc/cpu.h | 1116 +++++++--- qemu/target-ppc/exec.h | 28 +- qemu/target-ppc/helper.c | 1041 ++++++--- qemu/target-ppc/op.c | 349 +-- qemu/target-ppc/op_helper.c | 664 +++--- qemu/target-ppc/op_helper_mem.h | 8 + qemu/target-ppc/op_template.h | 6 +- qemu/target-ppc/translate.c | 1033 ++------- qemu/target-ppc/translate_init.c | 2069 ++++++++++++++++++ qemu/target-sparc/cpu.h | 109 +- qemu/target-sparc/exec.h | 39 +- qemu/target-sparc/fbranch_template.h | 89 + qemu/target-sparc/fop_template.h | 28 - qemu/target-sparc/helper.c | 370 ++-- qemu/target-sparc/op.c | 846 +++++-- qemu/target-sparc/op_helper.c | 764 ++++++- qemu/target-sparc/op_mem.h | 49 +- qemu/target-sparc/translate.c | 1432 ++++++++++-- qemu/translate-all.c | 2 + qemu/vl.c | 198 +- qemu/vl.h | 106 +- 151 files changed, 29698 insertions(+), 4739 deletions(-) create mode 100644 kqemu-permission create mode 100644 kqemu/Changelog create mode 100644 kqemu/Makefile.freebsd create mode 100644 kqemu/Makefile.winnt delete mode 100644 kqemu/kmod.c create mode 100644 kqemu/kqemu-freebsd.c create mode 100644 kqemu/kqemu-linux.c create mode 100644 kqemu/kqemu-mod-i386-win32.o create mode 100644 kqemu/kqemu-mod-x86_64.o create mode 100644 kqemu/kqemu-win32.c create mode 100644 kqemu/kqemu.reg create mode 100755 kqemu/kqemu.sys delete mode 100644 qemu-0.7.0-alt-makefile.patch create mode 100644 qemu-0.7.1-alt-guiargs.patch create mode 100644 qemu-0.7.1-alt-makefile.patch create mode 100644 qemu-gtk/callbacks.c create mode 100644 qemu-gtk/callbacks.h create mode 100644 qemu-gtk/fullscreen.h create mode 100644 qemu-gtk/gdk_keysym.h create mode 100644 qemu-gtk/gdk_set_window_pointer.c create mode 100644 qemu-gtk/gtk2.c create mode 100644 qemu-gtk/gtk2gui.c create mode 100644 qemu-gtk/interface.c create mode 100644 qemu-gtk/interface.h create mode 100644 qemu-gtk/null_fs.c create mode 100644 qemu-gtk/qemu-gtk-patch.diff create mode 100644 qemu-gtk/support.c create mode 100644 qemu-gtk/support.h create mode 100644 qemu-gtk/xvid_fs.c delete mode 100644 qemu/.#block.c.1.18 create mode 100644 qemu/block-vvfat.c create mode 100644 qemu/hw/elf_ops.h create mode 100644 qemu/hw/heathrow_pic.c create mode 100644 qemu/hw/mips_r4k.c create mode 100644 qemu/hw/slavio_misc.c create mode 100644 qemu/hw/sun4u.c create mode 100644 qemu/mips-dis.c create mode 100644 qemu/pc-bios/ohw.diff create mode 100644 qemu/pc-bios/video.x create mode 100644 qemu/target-mips/cpu.h create mode 100644 qemu/target-mips/exec.h create mode 100644 qemu/target-mips/helper.c create mode 100644 qemu/target-mips/mips-defs.h create mode 100644 qemu/target-mips/op.c create mode 100644 qemu/target-mips/op_helper.c create mode 100644 qemu/target-mips/op_helper_mem.c create mode 100644 qemu/target-mips/op_mem.c create mode 100644 qemu/target-mips/op_template.c create mode 100644 qemu/target-mips/translate.c create mode 100644 qemu/target-ppc/translate_init.c create mode 100644 qemu/target-sparc/fbranch_template.h diff --git a/.gear-rules b/.gear-rules index f6aa8b2..50182cf 100644 --- a/.gear-rules +++ b/.gear-rules @@ -1,3 +1,5 @@ copy: *.patch -tar.bz2: kqemu name=kqemu-0.6.2 base=kqemu +tar.bz2: kqemu name=kqemu-@version@.1 base=kqemu +copy: kqemu-permission tar.bz2: qemu +tar.bz2: qemu-gtk name=qemu-gtk-20050716 base= diff --git a/kqemu-permission b/kqemu-permission new file mode 100644 index 0000000..251934c --- /dev/null +++ b/kqemu-permission @@ -0,0 +1,35 @@ +From fabrice@bellard.org Thu Jun 16 22:12:46 2005 +Return-Path: +Received: from [84.99.204.186] (186.204.99-84.rev.gaoland.net [84.99.204.186]) + (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) + (No client certificate requested) + by ssl.polytechnique.org (Postfix) with ESMTP id 85EC333185 + for ; Thu, 16 Jun 2005 20:12:39 +0200 (CEST) +Date: Thu, 16 Jun 2005 20:12:40 +0200 +From: Fabrice Bellard +To: "Anton D. Kachalov" +Subject: Re: KQEMU distribution request +Content-Type: text/plain; charset=us-ascii; format=flowed +Content-Transfer-Encoding: 7bit + +Hi, + +Sorry for the delay - you have my permission to distribute kqemu +provided your distribution is available free of charge for the users. + +Regards, + +Fabrice. + +Anton D. Kachalov wrote: +> Good day! +> I mantain qemu package in ALT Linux (http://www.altlinux.org) distribs. +> May I ask You to grant distributions rights to kqemu module (FTP, CD, ISO)? +> +> Thank you. +> +> Rgds, +> Anton +> +> + diff --git a/kqemu/Changelog b/kqemu/Changelog new file mode 100644 index 0000000..610c29b --- /dev/null +++ b/kqemu/Changelog @@ -0,0 +1,11 @@ +version 0.7.1-1: + +- FreeBSD compile fixes - added x86_64 support +- __PAGE_KERNEL_EXEC fix for Linux 2.6 + +version 0.7.1: + +- PAE fixes +- x86_64 support +- 'syscall' insn support in 32 bit mode +- Windows support diff --git a/kqemu/Makefile b/kqemu/Makefile index 1853a8d..e28e5ea 100644 --- a/kqemu/Makefile +++ b/kqemu/Makefile @@ -22,11 +22,14 @@ kqemu.o: endif # !CONFIG_KBUILD26 clean: - rm -f kqemu.o kqemu.ko kmod.o kqemu-mod.o kqemu.mod.c *~ + rm -f kqemu.o kqemu.ko kqemu-linux.o kqemu-mod.o kqemu.mod.c *~ -FILES=Makefile README LICENSE install.sh kmod.c kqemu.h kqemu-mod-i386.o \ +FILES=Makefile README Changelog LICENSE install.sh kqemu-linux.c kqemu.h \ + kqemu-mod-i386.o kqemu-mod-x86_64.o \ + kqemu-freebsd.c Makefile.freebsd \ + kqemu-win32.c kqemu.sys kqemu.reg kqemu-mod-i386-win32.o Makefile.winnt \ kqemu-doc.texi kqemu-doc.html -VERSION=0.6.2 +VERSION=0.7.1-1 tar: cd .. ; tar zcvf /tmp/kqemu-$(VERSION).tar.gz $(addprefix kqemu/, $(FILES)) @@ -43,7 +46,7 @@ ifeq ($(PATCHLEVEL),4) # called from 2.4 kernel kbuild obj-m:= kqemu.o -kqemu-objs:= kmod.o kqemu-mod-i386.o +kqemu-objs:= kqemu-linux.o kqemu-mod-$(ARCH).o include $(TOPDIR)/Rules.make @@ -54,9 +57,9 @@ else # called from 2.6 kernel kbuild obj-m:= kqemu.o -kqemu-objs:= kmod.o kqemu-mod.o +kqemu-objs:= kqemu-linux.o kqemu-mod.o -$(obj)/kqemu-mod.o: $(src)/kqemu-mod-i386.o +$(obj)/kqemu-mod.o: $(src)/kqemu-mod-$(ARCH).o cp $< $@ endif endif # PATCHLEVEL diff --git a/kqemu/Makefile.freebsd b/kqemu/Makefile.freebsd new file mode 100644 index 0000000..096fb42 --- /dev/null +++ b/kqemu/Makefile.freebsd @@ -0,0 +1,10 @@ +KMOD= kqemu +SRCS= kqemu-freebsd.c +.if ${MACHINE_ARCH} == "i386" +OBJS= kqemu-mod-i386.o +.elif ${MACHINE_ARCH} == "amd64" +OBJS= kqemu-mod-x86_64.o +.endif +WERROR= + +.include diff --git a/kqemu/Makefile.winnt b/kqemu/Makefile.winnt new file mode 100644 index 0000000..794cbef --- /dev/null +++ b/kqemu/Makefile.winnt @@ -0,0 +1,38 @@ +# +# kqemu for Windows NT Makefile +# (c) Filip Navara +# +OBJECTS = kqemu-mod-i386-win32.o kqemu-win32.o +CROSS_PREFIX=i386-mingw32- + +TARGET = kqemu.sys + +CFLAGS = -O2 -I. + +all: $(OBJECTS) $(TARGET) + +clean: + rm -f kqemu-win32.o $(TARGET) + +%.o: %.c + $(CROSS_PREFIX)gcc $(CFLAGS) -c $< -o $@ + +%.coff: %.rc + $(CROSS_PREFIX)windres -i $< -o $@ + +%.a: %.def + $(CROSS_PREFIX)dlltool -d $< -l $@ --kill-at + +$(TARGET): $(OBJECTS) + $(CROSS_PREFIX)gcc -Wl,--base-file,base.tmp -Wl,--entry,_DriverEntry@8 \ + -nostartfiles -nostdlib -o junk.tmp $(OBJECTS) -lntoskrnl -lhal + rm junk.tmp + $(CROSS_PREFIX)dlltool --as=as --dllname $(TARGET) --base-file base.tmp \ + --output-exp temp.exp + rm base.tmp + $(CROSS_PREFIX)gcc -Wl,--subsystem,native -Wl,--image-base,0x10000 \ + -Wl,--file-alignment,0x1000 -Wl,--section-alignment,0x1000 \ + -Wl,--entry,_DriverEntry@8 -Wl,--stack,0x40000 -Wl,temp.exp \ + -mdll -nostartfiles -nostdlib -o $(TARGET) \ + $(OBJECTS) -lntoskrnl -lhal + rm temp.exp diff --git a/kqemu/kmod.c b/kqemu/kmod.c deleted file mode 100644 index fd29834..0000000 --- a/kqemu/kmod.c +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Linux kernel wrapper for KQEMU - * Copyright (c) 2004-2005 Fabrice Bellard - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "kqemu.h" - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19) -#error "Linux 2.4.19 or above needed" -#endif - -#ifndef page_to_pfn -#define page_to_pfn(page) ((page) - mem_map) -#define pfn_to_page(pfn) (mem_map + (pfn)) -#endif - -//#define DEBUG - -#ifdef DEBUG -int lock_count; -int page_alloc_count; -#endif - -/* lock the page at virtual address 'user_addr' and return its - page index. Return -1 if error */ -unsigned long CDECL kqemu_lock_user_page(unsigned long user_addr) -{ - int ret; - struct page *page; - - ret = get_user_pages(current, current->mm, - user_addr, - 1, /* 1 page. */ - 1, /* 'write': intent to write. */ - 0, /* 'force': ? */ - &page, - NULL); - if (ret != 1) - return -1; - /* we ensure here that the page cannot be swapped out by the - kernel. */ - /* XXX: This test may be incorrect for 2.6 kernels */ - if (!page->mapping) { - put_page(page); - return -1; - } -#ifdef DEBUG - lock_count++; -#endif - return page_to_pfn(page); -} - -void CDECL kqemu_unlock_user_page(unsigned long page_index) -{ - struct page *page; - page = pfn_to_page(page_index); - set_page_dirty(page); - put_page(page); -#ifdef DEBUG - lock_count--; -#endif -} - -/* Allocate a new page. The page must be mapped in the kernel - space. Return the page_index or -1 if error */ -unsigned long CDECL kqemu_alloc_zeroed_page(void) -{ - unsigned long ptr; - ptr = get_zeroed_page(GFP_KERNEL); - if (!ptr) - return -1; -#ifdef DEBUG - page_alloc_count++; -#endif - return __pa(ptr) >> PAGE_SHIFT; -} - -void CDECL kqemu_free_page(unsigned long page_index) -{ - struct page *page; - page = pfn_to_page(page_index); - __free_page(page); -#ifdef DEBUG - page_alloc_count--; -#endif -} - -/* return a kernel address of the physical page page_index */ -void * CDECL kqemu_page_kaddr(unsigned long page_index) -{ - return __va(page_index << PAGE_SHIFT); -} - -/* contraint: each page of the vmalloced area must be in the first 4 - GB of physical memory */ -void * CDECL kqemu_vmalloc(unsigned int size) -{ - return vmalloc(size); -} - -void CDECL kqemu_vfree(void *ptr) -{ - return vfree(ptr); -} - -unsigned long CDECL kqemu_vmalloc_to_phys(const void *vaddr) -{ - struct page *page; - page = vmalloc_to_page((void *)vaddr); - if (!page) - return -1; - return page_to_pfn(page); -} - -/* return TRUE if a signal is pending (i.e. the guest must stop - execution) */ -int CDECL kqemu_schedule(void) -{ - if (need_resched()) { - schedule(); - } - return signal_pending(current); -} - -char log_buf[4096]; - -void CDECL kqemu_log(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - vsnprintf(log_buf, sizeof(log_buf), fmt, ap); - printk("kqemu: %s", log_buf); - va_end(ap); -} - -/*********************************************************/ - -#define KQEMU_MAX_INSTANCES 4 - -static int kqemu_nb_instances = 0; -static spinlock_t kqemu_lock = SPIN_LOCK_UNLOCKED; -static int max_locked_pages; - -struct kqemu_instance { - struct semaphore sem; - struct kqemu_state *state; -}; - -static int kqemu_open(struct inode *inode, struct file *filp) -{ - struct kqemu_instance *ks; - - spin_lock(&kqemu_lock); - if (kqemu_nb_instances >= KQEMU_MAX_INSTANCES) { - spin_unlock(&kqemu_lock); - return -ENOMEM; - } - kqemu_nb_instances++; - spin_unlock(&kqemu_lock); - - ks = kmalloc(sizeof(struct kqemu_instance), GFP_KERNEL); - if (!ks) - return -ENOMEM; - init_MUTEX(&ks->sem); - ks->state = NULL; - filp->private_data = ks; - return 0; -} - -static int kqemu_release(struct inode *inode, struct file *filp) -{ - struct kqemu_instance *ks = filp->private_data; - - down(&ks->sem); - if (ks->state) { - kqemu_delete(ks->state); - ks->state = NULL; - } - up(&ks->sem); - - kfree(ks); - - spin_lock(&kqemu_lock); - kqemu_nb_instances--; - spin_unlock(&kqemu_lock); - -#ifdef DEBUG - printk("lock_count=%d page_alloc_count=%d\n", - lock_count, page_alloc_count); -#endif - return 0; -} - -static int kqemu_ioctl(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg) -{ - struct kqemu_instance *ks = filp->private_data; - struct kqemu_state *s = ks->state; - long ret; - - down(&ks->sem); - switch(cmd) { - case KQEMU_INIT: - { - struct kqemu_init d1, *d = &d1; - if (s) { - ret = -EIO; - break; - } - if (copy_from_user(d, (void *)arg, sizeof(*d))) { - ret = -EFAULT; - break; - } - s = kqemu_init(d, max_locked_pages); - if (!s) { - ret = -ENOMEM; - break; - } - ks->state = s; - ret = 0; - } - break; - case KQEMU_EXEC: - { - struct kqemu_cpu_state *ctx; - if (!s) { - ret = -EIO; - break; - } - - ctx = kqemu_get_cpu_state(s); - if (copy_from_user(ctx, (void *)arg, sizeof(*ctx))) { - ret = -EFAULT; - break; - } - unlock_kernel(); - ret = kqemu_exec(s); - lock_kernel(); - if (copy_to_user((void *)arg, ctx, sizeof(*ctx))) { - ret = -EFAULT; - break; - } - } - break; - case KQEMU_GET_VERSION: - { - if (put_user(KQEMU_VERSION, (int *)arg) < 0) { - ret = -EFAULT; - } else { - ret = 0; - } - } - break; - default: - ret = -ENOIOCTLCMD; - break; - } - up(&ks->sem); - return ret; -} - -static struct file_operations kqemu_fops = { - owner: THIS_MODULE, - ioctl: kqemu_ioctl, - open: kqemu_open, - release: kqemu_release, -}; - -int init_module(void) -{ - int ret; - struct sysinfo si; - - printk("QEMU Accelerator Module version %d.%d.%d, Copyright (c) 2005 Fabrice Bellard\n" - "This is a proprietary product. Read the LICENSE file for more information\n" - "Redistribution of this module is prohibited without authorization\n", - (KQEMU_VERSION >> 16), - (KQEMU_VERSION >> 8) & 0xff, - (KQEMU_VERSION) & 0xff); - si_meminfo(&si); - max_locked_pages = si.totalram / (2 * KQEMU_MAX_INSTANCES); - if (max_locked_pages > 32768) - max_locked_pages = 32768; - - ret = register_chrdev(KQEMU_MAJOR, "kqemu", &kqemu_fops); - if (ret < 0) { - printk("kqemu: could not get major %d\n", KQEMU_MAJOR); - return ret; - } - printk("KQEMU installed, max_instances=%d max_locked_mem=%dkB.\n", - KQEMU_MAX_INSTANCES, - max_locked_pages * 4); - return 0; -} - -void cleanup_module(void) -{ - unregister_chrdev(KQEMU_MAJOR, "kqemu"); -} diff --git a/kqemu/kqemu-doc.html b/kqemu/kqemu-doc.html index 8db0b4d..275511a 100644 --- a/kqemu/kqemu-doc.html +++ b/kqemu/kqemu-doc.html @@ -1,6 +1,6 @@ - + QEMU Accelerator User Documentation @@ -14,9 +14,10 @@
  • 2. Installation -
  • 3. Usage +
  • 3. Usage


    @@ -34,19 +35,15 @@ run much faster when emulating a PC on an x86 host.

    -KQEMU is currently only supported for an x86 Linux 2.4 or 2.6 host -system, but more host OSes (Windows, *BSD) and processors (x86_64) -will be supported in the future. +KQEMU is supported on x86 or x86_64 Linux 2.4 or 2.6 +hosts. Experimental versions are available for FreeBSD and Windows +NT/2000/2003/XP.

    2. Installation

    -

    -KQEMU is currently only supported on Linux/x86. - -

    2.1 QEMU Compilation

    @@ -89,7 +86,7 @@ to install QEMU in `/usr/local'. -

    2.2 QEMU Accelerator Installation

    +

    2.2 QEMU Accelerator Installation for Linux

    If you use x86 Linux, the compilation of the QEMU Accelerator Kernel @@ -150,21 +147,53 @@ in `/etc/rc.d/rc.local'.

    -If your distribution uses udev (like Fedora), the `/dev/kqemu' is -not created automatically (yet) at every reboot. You can add the -following in `/etc/rc.d/rc.local': +If your distribution uses udev (like Fedora), use the kqemu module +option major=0 to have the device `/dev/kqemu' automatically +created: + + + +

    +# Load the KQEMU kernel module
    +/sbin/modprobe kqemu major=0
    +
    + +

    +It is usually necessary to change the device access rights by doing as +root: chmod 666 /dev/kqemu. + + +

    +If the major number 250 is already used by another driver, you can use +the option major=N to set an alternate major number. + + + + +

    2.3 QEMU Accelerator Installation for Windows

    + +

    +Copy the kqemu driver `kqemu.sys' to +`c:\winnt\system32\drivers'. Then do: +

    +regedit kqemu.reg 
    +
    +

    +Now kqemu is installed and you must restart your system. + + +

    +In order to start kqemu, you must do:

    -# Create the KQEMU device
    -mknod /dev/kqemu c 250 0
    -chmod 666 /dev/kqemu
    +net start kqemu
     
    -

    3. Usage

    +

    3. Usage

    When QEMU is compiled with KQEMU support, the following option is @@ -197,8 +226,21 @@ as guest OSes. If your guest OS do not work with KQEMU, you can dynamically disable KQEMU with the `-no-kqemu' option. +

    +If you use kqemu on an x86_64 host, you must use +`qemu-system-x86_64' instead of `qemu'. + + +

    +To see if kqemu is enabled and working correctly, use the QEMU monitor +command: + +

    +info kqemu
    +
    +


    -This document was generated on 20 February 2005 using +This document was generated on 24 July 2005 using texi2html 1.56k. diff --git a/kqemu/kqemu-doc.texi b/kqemu/kqemu-doc.texi index c5ab7b8..e245a36 100644 --- a/kqemu/kqemu-doc.texi +++ b/kqemu/kqemu-doc.texi @@ -14,15 +14,13 @@ QEMU Accelerator (KQEMU) is a driver allowing the QEMU PC emulator to run much faster when emulating a PC on an x86 host. -KQEMU is currently only supported for an x86 Linux 2.4 or 2.6 host -system, but more host OSes (Windows, *BSD) and processors (x86_64) -will be supported in the future. +KQEMU is supported on x86 or x86_64 Linux 2.4 or 2.6 +hosts. Experimental versions are available for FreeBSD and Windows +NT/2000/2003/XP. @node kqemu_install @chapter Installation -KQEMU is currently only supported on Linux/x86. - @section QEMU Compilation First you must decompress the QEMU sources: @@ -50,7 +48,7 @@ make install @end example to install QEMU in @file{/usr/local}. -@section QEMU Accelerator Installation +@section QEMU Accelerator Installation for Linux If you use x86 Linux, the compilation of the QEMU Accelerator Kernel Module (KQEMU) is automatically activated provided you have the @@ -97,14 +95,34 @@ If you want that KQEMU is installed automatically at boot time, you can add in @file{/etc/rc.d/rc.local}. -If your distribution uses udev (like Fedora), the @file{/dev/kqemu} is -not created automatically (yet) at every reboot. You can add the -following in @file{/etc/rc.d/rc.local}: +If your distribution uses udev (like Fedora), use the kqemu module +option @code{major=0} to have the device @file{/dev/kqemu} automatically +created: + +@example +# Load the KQEMU kernel module +/sbin/modprobe kqemu major=0 +@end example + +It is usually necessary to change the device access rights by doing as +root: @code{chmod 666 /dev/kqemu}. + +If the major number 250 is already used by another driver, you can use +the option @code{major=N} to set an alternate major number. + +@section QEMU Accelerator Installation for Windows +Copy the kqemu driver @file{kqemu.sys} to +@file{c:\winnt\system32\drivers}. Then do: @example -# Create the KQEMU device -mknod /dev/kqemu c 250 0 -chmod 666 /dev/kqemu +regedit kqemu.reg +@end example + +Now kqemu is installed and you must restart your system. + +In order to start kqemu, you must do: +@example +net start kqemu @end example @chapter Usage @@ -132,3 +150,11 @@ KQEMU has only been tested with Linux 2.4, Linux 2.6 and Windows 2000 as guest OSes. If your guest OS do not work with KQEMU, you can dynamically disable KQEMU with the @option{-no-kqemu} option. +If you use kqemu on an x86_64 host, you must use +@file{qemu-system-x86_64} instead of @file{qemu}. + +To see if kqemu is enabled and working correctly, use the QEMU monitor +command: +@example +info kqemu +@end example diff --git a/kqemu/kqemu-freebsd.c b/kqemu/kqemu-freebsd.c new file mode 100644 index 0000000..a25f24f --- /dev/null +++ b/kqemu/kqemu-freebsd.c @@ -0,0 +1,301 @@ +/* $Id: kqemu-freebsd.c,v 1.3 2005/07/28 21:43:47 bellard Exp $ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define __KERNEL__ + +#include "kqemu.h" + +MALLOC_DECLARE(M_KQEMU); +MALLOC_DEFINE(M_KQEMU, "kqemu", "kqemu buffers"); + +#define USER_BASE 0x1000 + +/* lock the page at virtual address 'user_addr' and return its + physical page index. Return -1 if error */ +struct kqemu_user_page *CDECL kqemu_lock_user_page(unsigned long *ppage_index, + unsigned long user_addr) +{ + struct vmspace *vm = curproc->p_vmspace; + vm_offset_t va = user_addr; + vm_paddr_t pa = 0; + int ret; + pmap_t pmap; + ret = vm_map_wire(&vm->vm_map, va, va+PAGE_SIZE, VM_MAP_WIRE_USER); + if (ret != KERN_SUCCESS) { + printf("kqemu_lock_user_page(%08lx) failed, ret=%d\n", user_addr, ret); + return NULL; + } + pmap = vm_map_pmap(&vm->vm_map); + pa = pmap_extract(pmap, va); + // printf("kqemu_lock_user_page(%08lx) va=%08x pa=%08x\n", user_addr, va, pa); + *ppage_index = pa >> PAGE_SHIFT; + return (struct kqemu_user_page *)va; +} + +void CDECL kqemu_unlock_user_page(struct kqemu_user_page *page) +{ + struct vmspace *vm = curproc->p_vmspace; + vm_offset_t va; + int ret; + // printf("kqemu_unlock_user_page(%08lx)\n", page_index); + va = (vm_offset_t)page; + ret = vm_map_unwire(&vm->vm_map, va, va+PAGE_SIZE, VM_MAP_WIRE_USER); +#if 0 + if (ret != KERN_SUCCESS) { + printf("kqemu_unlock_user_page(%08lx) failed, ret=%d\n", page_index, ret); + } +#endif +} + +/* + * Allocate a new page. The page must be mapped in the kernel space. + * Return the page_index or -1 if error. + */ +struct kqemu_page *CDECL kqemu_alloc_zeroed_page(unsigned long *ppage_index) +{ + pmap_t pmap; + vm_offset_t va; + vm_paddr_t pa; + + va = kmem_alloc(kernel_map, PAGE_SIZE); + if (va == 0) { + printf("kqemu_alloc_zeroed_page: NULL\n"); + return -1; + } + pmap = vm_map_pmap(kernel_map); + pa = pmap_extract(pmap, va); + // printf("kqemu_alloc_zeroed_page: %08x\n", pa); + *ppage_index = pa >> PAGE_SHIFT; + return (struct kqemu_page *)va; +} + +void CDECL kqemu_free_page(struct kqemu_page *page) +{ + // printf("kqemu_free_page(%08lx)\n", page_index); + /* XXX: do it */ +} + +/* return kernel address of the physical page page_index */ +void * CDECL kqemu_page_kaddr(struct kqemu_page *page) +{ + vm_offset_t va = (vm_offset_t)page; + return (void *)va; +} + +/* contraint: each page of the vmalloced area must be in the first 4 + GB of physical memory */ +void * CDECL kqemu_vmalloc(unsigned int size) +{ + struct vmspace *vm = curproc->p_vmspace; + vm_offset_t va = USER_BASE; + int rv; + if (size % PAGE_SIZE != 0) { + printf("kqemu_vmalloc(%d) not a multiple of page size\n", size); + return NULL; + } + rv = vm_map_find(&vm->vm_map, NULL, 0, &va, size, 1, + VM_PROT_ALL, VM_PROT_ALL, 0); + if (rv != KERN_SUCCESS) { + printf("kqemu_vmalloc(%d) failed rv=%d\n", size, rv); + return NULL; + } + printf("kqemu_vmalloc(%d): %08x\n", size, va); + return (void *)va; +} + +void CDECL kqemu_vfree(void *ptr) +{ + printf("kqemu_vfree(%p)\n", ptr); +} + +/* return the physical page index for a given virtual page */ +unsigned long CDECL kqemu_vmalloc_to_phys(const void *vaddr) +{ + struct vmspace *vm = curproc->p_vmspace; + vm_paddr_t pa; + pmap_t pmap; + + pmap = vm_map_pmap(&vm->vm_map); + pa = pmap_extract(pmap, (vm_offset_t)vaddr); + if (pa == 0) { + printf("kqemu_vmalloc_to_phys(%p)->error\n", vaddr); + return -1; + } + printf("kqemu_vmalloc_to_phys(%p)->%08x\n", vaddr, pa); + return pa >> PAGE_SHIFT; +} + +/* Map a IO area in the kernel address space and return its + address. Return NULL if error or not implemented. */ +void * CDECL kqemu_io_map(unsigned long page_index, unsigned int size) +{ + return NULL; +} + +/* Unmap the IO area */ +void CDECL kqemu_io_unmap(void *ptr, unsigned int size) +{ +} + +/* return TRUE if a signal is pending (i.e. the guest must stop + execution) */ +int CDECL kqemu_schedule(void) +{ + // printf("kqemu_schedule\n"); + mtx_lock_spin(&sched_lock); + mi_switch(SW_VOL, NULL); + mtx_unlock_spin(&sched_lock); + return SIGPENDING(curthread); +} + +static char log_buf[4096]; + +void CDECL kqemu_log(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vsnprintf(log_buf, sizeof(log_buf), fmt, ap); + printf("kqemu: %s", log_buf); + va_end(ap); +} + +struct kqemu_instance { + // struct semaphore sem; + struct kqemu_state *state; +}; + +static d_close_t kqemu_close; +static d_open_t kqemu_open; +static d_ioctl_t kqemu_ioctl; + +static struct cdevsw kqemu_cdevsw = { + .d_version = D_VERSION, + .d_flags = D_NEEDGIANT, + .d_open = kqemu_open, + .d_ioctl = kqemu_ioctl, + .d_close = kqemu_close, + .d_name = "kqemu" +}; + +/* For use with make_dev(9)/destroy_dev(9). */ +static struct cdev *kqemu_dev; + +/* ARGSUSED */ +static int +kqemu_open(struct cdev *dev, int flags, int fmt __unused, + struct thread *td) +{ + struct kqemu_instance *ks; + ks = malloc(sizeof(struct kqemu_instance), M_KQEMU, M_WAITOK); + if (ks == NULL) { + printf("malloc failed\n"); + return ENOMEM; + } + ks->state = NULL; + dev->si_drv1 = ks; + return 0; +} + +/* ARGSUSED */ +static int +kqemu_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, + int flags __unused, struct thread *td) +{ + int error = 0; + int ret; + struct kqemu_instance *ks = dev->si_drv1; + struct kqemu_state *s = ks->state; + + switch(cmd) { + case KQEMU_INIT: { + struct kqemu_init d1, *d = &d1; + if (s != NULL) { + error = EIO; + break; + } + d1 = *(struct kqemu_init *)addr; + printf("ram_base=%p ram_size=%ld\n", d1.ram_base, d1.ram_size); + s = kqemu_init(d, 16000); + if (s == NULL) { + error = ENOMEM; + break; + } + ks->state = s; + break; + } + case KQEMU_EXEC: { + struct kqemu_cpu_state *ctx; + if (s == NULL) { + error = EIO; + break; + } + ctx = kqemu_get_cpu_state(s); + *ctx = *(struct kqemu_cpu_state *)addr; + DROP_GIANT(); + ret = kqemu_exec(s); + PICKUP_GIANT(); + *(struct kqemu_cpu_state *)addr = *ctx; + break; + } + case KQEMU_GET_VERSION: + *(int *)addr = KQEMU_VERSION; + break; + default: + error = EINVAL; + } + return error; +} + +/* ARGSUSED */ +static int +kqemu_close(struct cdev *dev __unused, int flags, int fmt __unused, + struct thread *td) +{ + return 0; +} + +/* ARGSUSED */ +static int +kqemu_modevent(module_t mod __unused, int type, void *data __unused) +{ + int error = 0; + + switch (type) { + case MOD_LOAD: + printf("kqemu version 0x%08x\n", KQEMU_VERSION); + kqemu_dev = make_dev(&kqemu_cdevsw, 0, + UID_ROOT, GID_WHEEL, 0666, "kqemu"); + break; + case MOD_UNLOAD: + destroy_dev(kqemu_dev); + break; + case MOD_SHUTDOWN: + break; + default: + error = EOPNOTSUPP; + break; + } + return (error); +} + +DEV_MODULE(kqemu, kqemu_modevent, NULL); +MODULE_VERSION(kqemu, 1); diff --git a/kqemu/kqemu-linux.c b/kqemu/kqemu-linux.c new file mode 100644 index 0000000..0f18ec5 --- /dev/null +++ b/kqemu/kqemu-linux.c @@ -0,0 +1,365 @@ +/* + * Linux kernel wrapper for KQEMU + * Copyright (c) 2004-2005 Fabrice Bellard + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kqemu.h" + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19) +#error "Linux 2.4.19 or above needed" +#endif + +#ifndef page_to_pfn +#define page_to_pfn(page) ((page) - mem_map) +#define pfn_to_page(pfn) (mem_map + (pfn)) +#endif + +#ifdef PAGE_KERNEL_EXEC +#if defined(__i386__) +/* problem : i386 kernels usually don't export __PAGE_KERNEL_EXEC */ +#undef PAGE_KERNEL_EXEC +#define PAGE_KERNEL_EXEC __pgprot(__PAGE_KERNEL & ~_PAGE_NX) +#endif +#else +#define PAGE_KERNEL_EXEC PAGE_KERNEL +#endif + +//#define DEBUG + +#ifdef DEBUG +int lock_count; +int page_alloc_count; +#endif + +/* if 0 is used, then devfs/udev is used to automatically create the + device */ +int major = 250; +MODULE_PARM(major,"i"); + +/* lock the page at virtual address 'user_addr' and return its + page index. Return -1 if error */ +struct kqemu_user_page *CDECL kqemu_lock_user_page(unsigned long *ppage_index, + unsigned long user_addr) +{ + int ret; + struct page *page; + + ret = get_user_pages(current, current->mm, + user_addr, + 1, /* 1 page. */ + 1, /* 'write': intent to write. */ + 0, /* 'force': ? */ + &page, + NULL); + if (ret != 1) + return NULL; + /* we ensure here that the page cannot be swapped out by the + kernel. */ + /* XXX: This test may be incorrect for 2.6 kernels */ + if (!page->mapping) { + put_page(page); + return NULL; + } +#ifdef DEBUG + lock_count++; +#endif + *ppage_index = page_to_pfn(page); + return (struct kqemu_user_page *)page; +} + +void CDECL kqemu_unlock_user_page(struct kqemu_user_page *page1) +{ + struct page *page = (struct page *)page1; + set_page_dirty(page); + put_page(page); +#ifdef DEBUG + lock_count--; +#endif +} + +/* Allocate a new page. The page must be mapped in the kernel + space. Return the page_index or -1 if error */ +struct kqemu_page *CDECL kqemu_alloc_zeroed_page(unsigned long *ppage_index) +{ + unsigned long vaddr; + struct page *page; + + vaddr = get_zeroed_page(GFP_KERNEL); + if (!vaddr) + return NULL; +#ifdef DEBUG + page_alloc_count++; +#endif + page = virt_to_page(vaddr); + *ppage_index = page_to_pfn(page); + return (struct kqemu_page *)page; +} + +void CDECL kqemu_free_page(struct kqemu_page *page1) +{ + struct page *page = (struct page *)page1; + __free_page(page); +#ifdef DEBUG + page_alloc_count--; +#endif +} + +/* return a kernel address of the physical page page_index */ +void * CDECL kqemu_page_kaddr(struct kqemu_page *page1) +{ + struct page *page = (struct page *)page1; + return page_address(page); +} + +/* contraint: each page of the vmalloced area must be in the first 4 + GB of physical memory. Moreover, execution of code should be + enabled in the allocated area. */ +void * CDECL kqemu_vmalloc(unsigned int size) +{ + return __vmalloc(size, GFP_KERNEL, PAGE_KERNEL_EXEC); +} + +void CDECL kqemu_vfree(void *ptr) +{ + return vfree(ptr); +} + +unsigned long CDECL kqemu_vmalloc_to_phys(const void *vaddr) +{ + struct page *page; + page = vmalloc_to_page((void *)vaddr); + if (!page) + return -1; + return page_to_pfn(page); +} + +/* Map a IO area in the kernel address space and return its + address. Return NULL if error or not implemented. */ +void * CDECL kqemu_io_map(unsigned long page_index, unsigned int size) +{ + return ioremap(page_index << PAGE_SHIFT, size); +} + +/* Unmap the IO area */ +void CDECL kqemu_io_unmap(void *ptr, unsigned int size) +{ + return iounmap(ptr); +} + +/* return TRUE if a signal is pending (i.e. the guest must stop + execution) */ +int CDECL kqemu_schedule(void) +{ + if (need_resched()) { + schedule(); + } + return signal_pending(current); +} + +char log_buf[4096]; + +void CDECL kqemu_log(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vsnprintf(log_buf, sizeof(log_buf), fmt, ap); + printk("kqemu: %s", log_buf); + va_end(ap); +} + +/*********************************************************/ + +#define KQEMU_MAX_INSTANCES 4 + +static int kqemu_nb_instances = 0; +static spinlock_t kqemu_lock = SPIN_LOCK_UNLOCKED; +static int max_locked_pages; + +struct kqemu_instance { + struct semaphore sem; + struct kqemu_state *state; +}; + +static int kqemu_open(struct inode *inode, struct file *filp) +{ + struct kqemu_instance *ks; + + spin_lock(&kqemu_lock); + if (kqemu_nb_instances >= KQEMU_MAX_INSTANCES) { + spin_unlock(&kqemu_lock); + return -ENOMEM; + } + kqemu_nb_instances++; + spin_unlock(&kqemu_lock); + + ks = kmalloc(sizeof(struct kqemu_instance), GFP_KERNEL); + if (!ks) + return -ENOMEM; + init_MUTEX(&ks->sem); + ks->state = NULL; + filp->private_data = ks; + return 0; +} + +static int kqemu_release(struct inode *inode, struct file *filp) +{ + struct kqemu_instance *ks = filp->private_data; + + down(&ks->sem); + if (ks->state) { + kqemu_delete(ks->state); + ks->state = NULL; + } + up(&ks->sem); + + kfree(ks); + + spin_lock(&kqemu_lock); + kqemu_nb_instances--; + spin_unlock(&kqemu_lock); + +#ifdef DEBUG + printk("lock_count=%d page_alloc_count=%d\n", + lock_count, page_alloc_count); +#endif + return 0; +} + +static int kqemu_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct kqemu_instance *ks = filp->private_data; + struct kqemu_state *s = ks->state; + long ret; + + down(&ks->sem); + switch(cmd) { + case KQEMU_INIT: + { + struct kqemu_init d1, *d = &d1; + if (s) { + ret = -EIO; + break; + } + if (copy_from_user(d, (void *)arg, sizeof(*d))) { + ret = -EFAULT; + break; + } + s = kqemu_init(d, max_locked_pages); + if (!s) { + ret = -ENOMEM; + break; + } + ks->state = s; + ret = 0; + } + break; + case KQEMU_EXEC: + { + struct kqemu_cpu_state *ctx; + if (!s) { + ret = -EIO; + break; + } + + ctx = kqemu_get_cpu_state(s); + if (copy_from_user(ctx, (void *)arg, sizeof(*ctx))) { + ret = -EFAULT; + break; + } + unlock_kernel(); + ret = kqemu_exec(s); + lock_kernel(); + if (copy_to_user((void *)arg, ctx, sizeof(*ctx))) { + ret = -EFAULT; + break; + } + } + break; + case KQEMU_GET_VERSION: + { + if (put_user(KQEMU_VERSION, (int *)arg) < 0) { + ret = -EFAULT; + } else { + ret = 0; + } + } + break; + default: + ret = -ENOIOCTLCMD; + break; + } + up(&ks->sem); + return ret; +} + +static struct file_operations kqemu_fops = { + owner: THIS_MODULE, + ioctl: kqemu_ioctl, + open: kqemu_open, + release: kqemu_release, +}; + +static struct miscdevice kqemu_dev = +{ + .minor = MISC_DYNAMIC_MINOR, + .name = "kqemu", + .fops = &kqemu_fops, +}; + +int init_module(void) +{ + int ret; + struct sysinfo si; + + printk("QEMU Accelerator Module version %d.%d.%d, Copyright (c) 2005 Fabrice Bellard\n" + "This is a proprietary product. Read the LICENSE file for more information\n" + "Redistribution of this module is prohibited without authorization\n", + (KQEMU_VERSION >> 16), + (KQEMU_VERSION >> 8) & 0xff, + (KQEMU_VERSION) & 0xff); + si_meminfo(&si); + max_locked_pages = si.totalram / (2 * KQEMU_MAX_INSTANCES); + if (max_locked_pages > 32768) + max_locked_pages = 32768; + + if (major > 0) { + ret = register_chrdev(major, "kqemu", &kqemu_fops); + if (ret < 0) { + printk("kqemu: could not get major %d\n", major); + return ret; + } + } else { + ret = misc_register (&kqemu_dev); + if (ret < 0) { + printk("kqemu: could not create device\n"); + return ret; + } + } + printk("KQEMU installed, max_instances=%d max_locked_mem=%dkB.\n", + KQEMU_MAX_INSTANCES, + max_locked_pages * 4); + return 0; +} + +void cleanup_module(void) +{ + if (major > 0) + unregister_chrdev(major, "kqemu"); + else + misc_deregister (&kqemu_dev); +} diff --git a/kqemu/kqemu-mod-i386-win32.o b/kqemu/kqemu-mod-i386-win32.o new file mode 100644 index 0000000000000000000000000000000000000000..05a31a8434b6e631b8ad5c405514867034ff01c8 GIT binary patch literal 34058 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 literal 0 HcmV?d00001 diff --git a/kqemu/kqemu-mod-i386.o b/kqemu/kqemu-mod-i386.o index 4404f30145a664908de5567cddb1e1b48e59d496..6c97b76d174ba96ca91f597ed564376a5bb16732 100644 GIT binary patch literal 33969 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 Xwe#W}^%Q?uUbCQlPIs{>Q62v;KQY)K literal 36666 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 NGd0n)V3dxA{|}?RfSLdR diff --git a/kqemu/kqemu-mod-x86_64.o b/kqemu/kqemu-mod-x86_64.o new file mode 100644 index 0000000000000000000000000000000000000000..67df14e0c7459627f393b6e1f8300b811fb63c57 GIT binary patch literal 36909 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 literal 0 HcmV?d00001 diff --git a/kqemu/kqemu-win32.c b/kqemu/kqemu-win32.c new file mode 100644 index 0000000..d8c662e --- /dev/null +++ b/kqemu/kqemu-win32.c @@ -0,0 +1,333 @@ +/* + * Windows NT kernel wrapper for KQEMU + * + * Copyright (C) 2005 Filip Navara + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +#undef CDECL +#define __KERNEL__ +#include "kqemu.h" + +/* XXX: make it dynamic according to available RAM */ +#define MAX_LOCKED_PAGES (16386 / 4) + +/* lock the page at virtual address 'user_addr' and return its + page index. Return -1 if error */ +struct kqemu_user_page *CDECL kqemu_lock_user_page(unsigned long *ppage_index, + unsigned long user_addr) +{ + PMDL mdl; + PPFN_NUMBER mdl_pages; + + if (user_addr & 0xfff) { + DbgPrint("kqemu: unaligned user memory\n"); + return NULL; + } + + mdl = ExAllocatePool(NonPagedPool, sizeof(MDL) + sizeof(PFN_NUMBER)); + if (mdl == NULL) { + DbgPrint("kqemu: Not enough memory for MDL structure\n"); + return NULL; + } + mdl_pages = (PPFN_NUMBER)(mdl + 1); + + MmInitializeMdl(mdl, user_addr, PAGE_SIZE); + MmProbeAndLockPages(mdl, KernelMode, IoModifyAccess); + *ppage_index = mdl_pages[0]; + return (struct kqemu_user_page *)mdl; +} + +void CDECL kqemu_unlock_user_page(struct kqemu_user_page *page) +{ + PMDL mdl = (PMDL)page; + + MmUnlockPages(mdl); + ExFreePool(mdl); +} + +struct kqemu_page *CDECL kqemu_alloc_zeroed_page(unsigned long *ppage_index) +{ + void *ptr; + LARGE_INTEGER pa; + + ptr = MmAllocateNonCachedMemory(PAGE_SIZE); + if (!ptr) + return NULL; + RtlZeroMemory(ptr, PAGE_SIZE); + pa = MmGetPhysicalAddress(ptr); + *ppage_index = (unsigned long)(pa.QuadPart >> PAGE_SHIFT); + return (struct kqemu_page *)ptr; +} + +void CDECL kqemu_free_page(struct kqemu_page *page) +{ + void *ptr = page; + + if (!ptr) + return; + MmFreeNonCachedMemory(ptr, PAGE_SIZE); +} + +void * CDECL kqemu_page_kaddr(struct kqemu_page *page) +{ + void *ptr = page; + return ptr; +} + +void * CDECL kqemu_vmalloc(unsigned int size) +{ + void * ptr; + + ptr = ExAllocatePoolWithTag(NonPagedPool, size, TAG('K','Q','M','U')); + if (!ptr) + return NULL; + RtlZeroMemory(ptr, size); + return ptr; +} + +void CDECL kqemu_vfree(void *ptr) +{ + if (!ptr) + return; + ExFreePool(ptr); +} + +unsigned long CDECL kqemu_vmalloc_to_phys(const void *vaddr) +{ + LARGE_INTEGER pa; + + pa = MmGetPhysicalAddress((void *)vaddr); + return (unsigned long)(pa.QuadPart >> PAGE_SHIFT); +} + +/* Map a IO area in the kernel address space and return its + address. Return NULL if error or not implemented. */ +void * CDECL kqemu_io_map(unsigned long page_index, unsigned int size) +{ +#if 1 + PHYSICAL_ADDRESS pa; + + pa.QuadPart = page_index << PAGE_SHIFT; + return MmMapIoSpace(pa, size, MmNonCached); +#else + /* XXX: mingw32 tools too old */ + return NULL; +#endif +} + +/* Unmap the IO area */ +void CDECL kqemu_io_unmap(void *ptr, unsigned int size) +{ + return MmUnmapIoSpace(ptr, size); +} + +/* return TRUE if a signal is pending (i.e. the guest must stop + execution) */ +int CDECL kqemu_schedule(void) +{ + /* XXX: do it */ + return TRUE; +} + +void CDECL kqemu_log(const char *fmt, ...) +{ + /* XXX: format parameters */ + DbgPrint("%s", fmt); +} + +struct kqemu_instance { + struct kqemu_state *state; +}; + +NTSTATUS STDCALL +KQemuCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp); + struct kqemu_instance *State; + + State = kqemu_vmalloc(sizeof(struct kqemu_instance)); + if (State == NULL) + { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_INSUFFICIENT_RESOURCES; + } + + IrpStack->FileObject->FsContext = State; + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; +} + +NTSTATUS STDCALL +KQemuClose(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp); + struct kqemu_instance *State = IrpStack->FileObject->FsContext; + + if (State->state) { + kqemu_delete(State->state); + State->state = NULL; + } + kqemu_vfree(State); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; +} + +NTSTATUS STDCALL +KQemuDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp); + struct kqemu_instance *State = IrpStack->FileObject->FsContext; + NTSTATUS Status; + int ret; + + Irp->IoStatus.Information = 0; + + switch (IrpStack->Parameters.DeviceIoControl.IoControlCode) + { + case KQEMU_INIT: + if (State->state) { + Status = STATUS_INVALID_PARAMETER; + break; + } + if (IrpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(struct kqemu_init)) + { + Status = STATUS_INVALID_PARAMETER; + break; + } + State->state = kqemu_init((struct kqemu_init *)Irp->AssociatedIrp.SystemBuffer, + MAX_LOCKED_PAGES); + if (!State->state) { + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + Status = STATUS_SUCCESS; + break; + + case KQEMU_EXEC: + { + struct kqemu_cpu_state *ctx; + + if (!State->state) { + Status = STATUS_INVALID_PARAMETER; + break; + } + if (IrpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(*ctx) || + IrpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(*ctx)) + { + Status = STATUS_INVALID_PARAMETER; + break; + } + + ctx = kqemu_get_cpu_state(State->state); + + RtlCopyMemory(ctx, Irp->AssociatedIrp.SystemBuffer, + sizeof(*ctx)); + ret = kqemu_exec(State->state); + RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, ctx, sizeof(*ctx)); + Irp->IoStatus.Information = sizeof(*ctx); + Status = STATUS_SUCCESS; + } + break; + + case KQEMU_GET_VERSION: + if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(int)) + { + Status = STATUS_INVALID_PARAMETER; + break; + } + + *((int *)Irp->AssociatedIrp.SystemBuffer) = KQEMU_VERSION; + Irp->IoStatus.Information = sizeof(int); + Status = STATUS_SUCCESS; + break; + + default: + Status = STATUS_INVALID_PARAMETER; + break; + } + + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return Status; +} + +VOID STDCALL +KQemuUnload(PDRIVER_OBJECT DriverObject) +{ + IoDeleteDevice(DriverObject->DeviceObject); +} + +NTSTATUS STDCALL +DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) +{ + PDEVICE_OBJECT DeviceObject; + UNICODE_STRING DeviceName; + UNICODE_STRING SymlinkName; + NTSTATUS Status; + + DbgPrint("QEMU Accelerator Module version %d.%d.%d\n", + (KQEMU_VERSION >> 16), + (KQEMU_VERSION >> 8) & 0xff, + (KQEMU_VERSION) & 0xff); + + MmLockPagableCodeSection(DriverEntry); + + DriverObject->MajorFunction[IRP_MJ_CREATE] = KQemuCreate; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = KQemuClose; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KQemuDeviceControl; + DriverObject->DriverUnload = KQemuUnload; + + RtlInitUnicodeString(&DeviceName, L"\\Device\\kqemu"); + RtlInitUnicodeString(&SymlinkName, L"\\??\\kqemu"); + + Status = IoCreateDevice(DriverObject, 0, + &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, + &DeviceObject); + if (!NT_SUCCESS(Status)) + return Status; + + /* Create the dos device link */ + IoCreateSymbolicLink(&SymlinkName, &DeviceName); + + return STATUS_SUCCESS; +} diff --git a/kqemu/kqemu.h b/kqemu/kqemu.h index c9bafa6..f92933b 100644 --- a/kqemu/kqemu.h +++ b/kqemu/kqemu.h @@ -1,21 +1,23 @@ #ifndef KQEMU_H #define KQEMU_H -#define KQEMU_MAJOR 250 - -#define KQEMU_VERSION 0x010000 +#define KQEMU_VERSION 0x010100 struct kqemu_segment_cache { uint32_t selector; - uint32_t base; + unsigned long base; uint32_t limit; uint32_t flags; }; struct kqemu_cpu_state { - uint32_t regs[8]; - uint32_t eip; - uint32_t eflags; +#ifdef __x86_64__ + unsigned long regs[16]; +#else + unsigned long regs[8]; +#endif + unsigned long eip; + unsigned long eflags; uint32_t dummy0, dummy1, dumm2, dummy3, dummy4; @@ -25,28 +27,32 @@ struct kqemu_cpu_state { struct kqemu_segment_cache gdt; /* only base and limit are used */ struct kqemu_segment_cache idt; /* only base and limit are used */ - uint32_t cr0; - uint32_t dumm5; - uint32_t cr2; - uint32_t cr3; - uint32_t cr4; + unsigned long cr0; + unsigned long dummy5; + unsigned long cr2; + unsigned long cr3; + unsigned long cr4; uint32_t a20_mask; - uint32_t dr0; - uint32_t dr1; - uint32_t dr2; - uint32_t dr3; - uint32_t dr6; - uint32_t dr7; + uint64_t efer __attribute__((aligned(8))); + + unsigned long dr0; + unsigned long dr1; + unsigned long dr2; + unsigned long dr3; + unsigned long dr6; + unsigned long dr7; int cpl; /* currently only 3 */ uint32_t error_code; /* error_code when exiting with an exception */ - uint32_t next_eip; /* next eip value when exiting with an interrupt */ + unsigned long next_eip; /* next eip value when exiting with an interrupt */ unsigned int nb_pages_to_flush; /* number of pages to flush, KQEMU_FLUSH_ALL means full flush */ #define KQEMU_MAX_PAGES_TO_FLUSH 512 #define KQEMU_FLUSH_ALL (KQEMU_MAX_PAGES_TO_FLUSH + 1) + + long retval; }; struct kqemu_init { @@ -63,10 +69,17 @@ struct kqemu_init { #define KQEMU_RET_SOFTMMU 0x0200 /* emulation needed (I/O or unsupported INSN) */ #define KQEMU_RET_INTR 0x0201 /* interrupted by a signal */ +#define KQEMU_RET_SYSCALL 0x0300 /* syscall insn */ +#ifdef _WIN32 +#define KQEMU_EXEC CTL_CODE(FILE_DEVICE_UNKNOWN, 1, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) +#define KQEMU_INIT CTL_CODE(FILE_DEVICE_UNKNOWN, 2, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define KQEMU_GET_VERSION CTL_CODE(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_READ_ACCESS) +#else #define KQEMU_EXEC _IOWR('q', 1, struct kqemu_cpu_state) #define KQEMU_INIT _IOW('q', 2, struct kqemu_init) #define KQEMU_GET_VERSION _IOR('q', 3, int) +#endif #ifdef __KERNEL__ struct kqemu_state; @@ -79,17 +92,24 @@ long CDECL kqemu_exec(struct kqemu_state *s); void CDECL kqemu_delete(struct kqemu_state *s); /* callbacks */ -unsigned long CDECL kqemu_lock_user_page(unsigned long user_addr); -void CDECL kqemu_unlock_user_page(unsigned long page_index); +struct kqemu_page; /* opaque data for host page */ +struct kqemu_user_page; /* opaque data for host user page */ + +struct kqemu_user_page *CDECL kqemu_lock_user_page(unsigned long *ppage_index, + unsigned long user_addr); +void CDECL kqemu_unlock_user_page(struct kqemu_user_page *page); -unsigned long CDECL kqemu_alloc_zeroed_page(void); -void CDECL kqemu_free_page(unsigned long page_index); -void * CDECL kqemu_page_kaddr(unsigned long page_index); +struct kqemu_page *CDECL kqemu_alloc_zeroed_page(unsigned long *ppage_index); +void CDECL kqemu_free_page(struct kqemu_page *page); +void * CDECL kqemu_page_kaddr(struct kqemu_page *page); void * CDECL kqemu_vmalloc(unsigned int size); void CDECL kqemu_vfree(void *ptr); unsigned long CDECL kqemu_vmalloc_to_phys(const void *vaddr); +void * CDECL kqemu_io_map(unsigned long page_index, unsigned int size); +void CDECL kqemu_io_unmap(void *ptr, unsigned int size); + int CDECL kqemu_schedule(void); void CDECL kqemu_log(const char *fmt, ...); diff --git a/kqemu/kqemu.reg b/kqemu/kqemu.reg new file mode 100644 index 0000000..73c05ab --- /dev/null +++ b/kqemu/kqemu.reg @@ -0,0 +1,7 @@ +REGEDIT4 + +[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\kqemu] +"Type"=dword:00000001 +"Start"=dword:00000003 +"ErrorControl"=dword:00000001 +"DisplayName"="kqemu" diff --git a/kqemu/kqemu.sys b/kqemu/kqemu.sys new file mode 100755 index 0000000000000000000000000000000000000000..d33177c1d95b91fa9896a64e7b673bdc7bb72c6c GIT binary patch literal 56431 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 argc) +@@ -3546,7 +3548,6 @@ + boot_device = 'd'; + } + +- gui_checkargs(&argc, &argv); + #if !defined(CONFIG_SOFTMMU) + /* must avoid mmap() usage of glibc by setting a buffer "by hand" */ + { diff --git a/qemu-0.7.1-alt-makefile.patch b/qemu-0.7.1-alt-makefile.patch new file mode 100644 index 0000000..3c2ed04 --- /dev/null +++ b/qemu-0.7.1-alt-makefile.patch @@ -0,0 +1,53 @@ +--- qemu-0.7.1/Makefile.orig 2004-11-14 23:51:33 +0300 ++++ qemu-0.7.1/Makefile 2004-11-26 17:52:44 +0300 +@@ -54,21 +54,21 @@ + common de-ch es fo fr-ca hu ja mk nl-be pt sl tr + + install: all +- mkdir -p "$(bindir)" +- install -m 755 -s $(TOOLS) "$(bindir)" +- mkdir -p "$(datadir)" ++ mkdir -p "$(DESTDIR)$(bindir)" ++ install -m 755 -s $(TOOLS) "$(DESTDIR)$(bindir)" ++ mkdir -p "$(DESTDIR)$(datadir)" + install -m 644 pc-bios/bios.bin pc-bios/vgabios.bin \ + pc-bios/vgabios-cirrus.bin \ + pc-bios/ppc_rom.bin pc-bios/video.x \ + pc-bios/proll.elf \ +- pc-bios/linux_boot.bin "$(datadir)" +- mkdir -p "$(docdir)" +- install -m 644 qemu-doc.html qemu-tech.html "$(docdir)" ++ pc-bios/linux_boot.bin "$(DESTDIR)$(datadir)" ++ mkdir -p "$(DESTDIR)$(docdir)" ++ install -m 644 qemu-doc.html qemu-tech.html "$(DESTDIR)$(docdir)" + ifndef CONFIG_WIN32 +- mkdir -p "$(mandir)/man1" +- install qemu.1 qemu-img.1 "$(mandir)/man1" +- mkdir -p "$(datadir)/keymaps" +- install -m 644 $(addprefix keymaps/,$(KEYMAPS)) "$(datadir)/keymaps" ++ mkdir -p "$(DESTDIR)$(mandir)/man1" ++ install qemu.1 qemu-img.1 "$(DESTDIR)$(mandir)/man1" ++ mkdir -p "$(DESTDIR)$(datadir)/keymaps" ++ install -m 644 $(addprefix keymaps/,$(KEYMAPS)) "$(DESTDIR)$(datadir)/keymaps" + endif + for d in $(TARGET_DIRS); do \ + $(MAKE) -C $$d $@ || exit 1 ; \ +--- qemu-0.7.1/Makefile.target.orig 2004-11-14 23:51:33 +0300 ++++ qemu-0.7.1/Makefile.target 2004-11-26 17:47:49 +0300 +@@ -320,6 +320,7 @@ + # specific flags are needed for non soft mmu emulator + ifdef CONFIG_STATIC + VL_LDFLAGS+=-static ++VL_LIBS+=-ldl + endif + ifndef CONFIG_SOFTMMU + VL_LDFLAGS+=-Wl,-T,$(SRC_PATH)/i386-vl.ld +@@ -397,7 +397,7 @@ + + install: all + ifneq ($(PROGS),) +- install -m 755 -s $(PROGS) "$(bindir)" ++ $(INSTALL) -m755 $(PROGS) "$(DESTDIR)$(bindir)" + endif + + ifneq ($(wildcard .depend),) diff --git a/qemu-gtk/callbacks.c b/qemu-gtk/callbacks.c new file mode 100644 index 0000000..14b077d --- /dev/null +++ b/qemu-gtk/callbacks.c @@ -0,0 +1,171 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "callbacks.h" +#include "interface.h" +#include "support.h" +#include "vl.h" + + +void +on_nouveau1_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + +} + + +void +on_ouvrir1_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + +} + + +void +on_enregistrer1_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + +} + + +void +on_enregistrer_sous1_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + +} + + +void +on_fermer1_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + +} + + +void +on_quitter1_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + +} + + +void +on_couper1_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + +} + + +void +on_copier1_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + +} + + +void +on_coller1_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + +} + + +void +on_supprimer1_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + +} + + +void +on_preferences1_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + +} + + +void +on_machine1_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + +} + + +void +on_run1_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + +} + + +void +on_run_in_a_snapshot1_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + +} + + +void +on_pause2_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + vm_stop(EXCP_INTERRUPT); +} + + +void +on_continue1_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + vm_start(); +} + + +void +on_reset1_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + qemu_system_reset_request(); +} + + +void +on_shutdown1_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + qemu_system_shutdown_request(); +} + + +void +on_zoom1_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + +} + + +void +on____propos1_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + +} + diff --git a/qemu-gtk/callbacks.h b/qemu-gtk/callbacks.h new file mode 100644 index 0000000..b5d8d1f --- /dev/null +++ b/qemu-gtk/callbacks.h @@ -0,0 +1,82 @@ +#include + + +void +on_nouveau1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_ouvrir1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_enregistrer1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_enregistrer_sous1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_fermer1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_quitter1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_couper1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_copier1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_coller1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_supprimer1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_preferences1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_machine1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_run1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_run_in_a_snapshot1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_pause2_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_continue1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_reset1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_shutdown1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_zoom1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on____propos1_activate (GtkMenuItem *menuitem, + gpointer user_data); diff --git a/qemu-gtk/fullscreen.h b/qemu-gtk/fullscreen.h new file mode 100644 index 0000000..3802b40 --- /dev/null +++ b/qemu-gtk/fullscreen.h @@ -0,0 +1,6 @@ +/* fullscreen defines */ + +int fullscreen_init(void); +int fullscreen_switch(int x, int y, int w, int h); +int fullscreen_reset(void); +void fullscreen_cleanup(void); diff --git a/qemu-gtk/gdk_keysym.h b/qemu-gtk/gdk_keysym.h new file mode 100644 index 0000000..2fab099 --- /dev/null +++ b/qemu-gtk/gdk_keysym.h @@ -0,0 +1,280 @@ +#include + +typedef struct { + const char* name; + int keysym; +} name2keysym_t; +static name2keysym_t name2keysym[]={ +/* ascii */ + { "space", 0x020}, + { "exclam", 0x021}, + { "quotedbl", 0x022}, + { "numbersign", 0x023}, + { "dollar", 0x024}, + { "percent", 0x025}, + { "ampersand", 0x026}, + { "apostrophe", 0x027}, + { "parenleft", 0x028}, + { "parenright", 0x029}, + { "asterisk", 0x02a}, + { "plus", 0x02b}, + { "comma", 0x02c}, + { "minus", 0x02d}, + { "period", 0x02e}, + { "slash", 0x02f}, + { "0", 0x030}, + { "1", 0x031}, + { "2", 0x032}, + { "3", 0x033}, + { "4", 0x034}, + { "5", 0x035}, + { "6", 0x036}, + { "7", 0x037}, + { "8", 0x038}, + { "9", 0x039}, + { "colon", 0x03a}, + { "semicolon", 0x03b}, + { "less", 0x03c}, + { "equal", 0x03d}, + { "greater", 0x03e}, + { "question", 0x03f}, + { "at", 0x040}, + { "A", 0x041}, + { "B", 0x042}, + { "C", 0x043}, + { "D", 0x044}, + { "E", 0x045}, + { "F", 0x046}, + { "G", 0x047}, + { "H", 0x048}, + { "I", 0x049}, + { "J", 0x04a}, + { "K", 0x04b}, + { "L", 0x04c}, + { "M", 0x04d}, + { "N", 0x04e}, + { "O", 0x04f}, + { "P", 0x050}, + { "Q", 0x051}, + { "R", 0x052}, + { "S", 0x053}, + { "T", 0x054}, + { "U", 0x055}, + { "V", 0x056}, + { "W", 0x057}, + { "X", 0x058}, + { "Y", 0x059}, + { "Z", 0x05a}, + { "bracketleft", 0x05b}, + { "backslash", 0x05c}, + { "bracketright", 0x05d}, + { "asciicircum", 0x05e}, + { "underscore", 0x05f}, + { "grave", 0x060}, + { "a", 0x061}, + { "b", 0x062}, + { "c", 0x063}, + { "d", 0x064}, + { "e", 0x065}, + { "f", 0x066}, + { "g", 0x067}, + { "h", 0x068}, + { "i", 0x069}, + { "j", 0x06a}, + { "k", 0x06b}, + { "l", 0x06c}, + { "m", 0x06d}, + { "n", 0x06e}, + { "o", 0x06f}, + { "p", 0x070}, + { "q", 0x071}, + { "r", 0x072}, + { "s", 0x073}, + { "t", 0x074}, + { "u", 0x075}, + { "v", 0x076}, + { "w", 0x077}, + { "x", 0x078}, + { "y", 0x079}, + { "z", 0x07a}, + { "braceleft", 0x07b}, + { "bar", 0x07c}, + { "braceright", 0x07d}, + { "asciitilde", 0x07e}, + +/* latin 1 extensions */ +{ "nobreakspace", 0x0a0}, +{ "exclamdown", 0x0a1}, +{ "cent", 0x0a2}, +{ "sterling", 0x0a3}, +{ "currency", 0x0a4}, +{ "yen", 0x0a5}, +{ "brokenbar", 0x0a6}, +{ "section", 0x0a7}, +{ "diaeresis", 0x0a8}, +{ "copyright", 0x0a9}, +{ "ordfeminine", 0x0aa}, +{ "guillemotleft", 0x0ab}, +{ "notsign", 0x0ac}, +{ "hyphen", 0x0ad}, +{ "registered", 0x0ae}, +{ "macron", 0x0af}, +{ "degree", 0x0b0}, +{ "plusminus", 0x0b1}, +{ "twosuperior", 0x0b2}, +{ "threesuperior", 0x0b3}, +{ "acute", 0x0b4}, +{ "mu", 0x0b5}, +{ "paragraph", 0x0b6}, +{ "periodcentered", 0x0b7}, +{ "cedilla", 0x0b8}, +{ "onesuperior", 0x0b9}, +{ "masculine", 0x0ba}, +{ "guillemotright", 0x0bb}, +{ "onequarter", 0x0bc}, +{ "onehalf", 0x0bd}, +{ "threequarters", 0x0be}, +{ "questiondown", 0x0bf}, +{ "Agrave", 0x0c0}, +{ "Aacute", 0x0c1}, +{ "Acircumflex", 0x0c2}, +{ "Atilde", 0x0c3}, +{ "Adiaeresis", 0x0c4}, +{ "Aring", 0x0c5}, +{ "AE", 0x0c6}, +{ "Ccedilla", 0x0c7}, +{ "Egrave", 0x0c8}, +{ "Eacute", 0x0c9}, +{ "Ecircumflex", 0x0ca}, +{ "Ediaeresis", 0x0cb}, +{ "Igrave", 0x0cc}, +{ "Iacute", 0x0cd}, +{ "Icircumflex", 0x0ce}, +{ "Idiaeresis", 0x0cf}, +{ "ETH", 0x0d0}, +{ "Eth", 0x0d0}, +{ "Ntilde", 0x0d1}, +{ "Ograve", 0x0d2}, +{ "Oacute", 0x0d3}, +{ "Ocircumflex", 0x0d4}, +{ "Otilde", 0x0d5}, +{ "Odiaeresis", 0x0d6}, +{ "multiply", 0x0d7}, +{ "Ooblique", 0x0d8}, +{ "Oslash", 0x0d8}, +{ "Ugrave", 0x0d9}, +{ "Uacute", 0x0da}, +{ "Ucircumflex", 0x0db}, +{ "Udiaeresis", 0x0dc}, +{ "Yacute", 0x0dd}, +{ "THORN", 0x0de}, +{ "Thorn", 0x0de}, +{ "ssharp", 0x0df}, +{ "agrave", 0x0e0}, +{ "aacute", 0x0e1}, +{ "acircumflex", 0x0e2}, +{ "atilde", 0x0e3}, +{ "adiaeresis", 0x0e4}, +{ "aring", 0x0e5}, +{ "ae", 0x0e6}, +{ "ccedilla", 0x0e7}, +{ "egrave", 0x0e8}, +{ "eacute", 0x0e9}, +{ "ecircumflex", 0x0ea}, +{ "ediaeresis", 0x0eb}, +{ "igrave", 0x0ec}, +{ "iacute", 0x0ed}, +{ "icircumflex", 0x0ee}, +{ "idiaeresis", 0x0ef}, +{ "eth", 0x0f0}, +{ "ntilde", 0x0f1}, +{ "ograve", 0x0f2}, +{ "oacute", 0x0f3}, +{ "ocircumflex", 0x0f4}, +{ "otilde", 0x0f5}, +{ "odiaeresis", 0x0f6}, +{ "division", 0x0f7}, +{ "oslash", 0x0f8}, +{ "ooblique", 0x0f8}, +{ "ugrave", 0x0f9}, +{ "uacute", 0x0fa}, +{ "ucircumflex", 0x0fb}, +{ "udiaeresis", 0x0fc}, +{ "yacute", 0x0fd}, +{ "thorn", 0x0fe}, +{ "ydiaeresis", 0x0ff}, +{"EuroSign", GDK_EuroSign}, + + /* modifiers */ +{"Control_L", GDK_Control_L}, +{"Control_R", GDK_Control_R}, +{"Alt_L", GDK_Alt_L}, +{"Alt_R", GDK_Alt_R}, +{"Caps_Lock", GDK_Caps_Lock}, +{"Meta_L", GDK_Meta_L}, +{"Meta_R", GDK_Meta_R}, +{"Shift_L", GDK_Shift_L}, +{"Shift_R", GDK_Shift_R}, +{"Super_L", GDK_Super_L}, +{"Super_R", GDK_Super_R}, + + /* special keys */ +{"BackSpace", GDK_BackSpace}, +{"Tab", GDK_Tab}, +{"Return", GDK_Return}, +{"Right", GDK_Right}, +{"Left", GDK_Left}, +{"Up", GDK_Up}, +{"Down", GDK_Down}, +{"Page_Down", GDK_Page_Down}, +{"Page_Up", GDK_Page_Up}, +{"Insert", GDK_Insert}, +{"Delete", GDK_Delete}, +{"Home", GDK_Home}, +{"End", GDK_End}, +{"Scroll_Lock", GDK_Scroll_Lock}, +{"F1", GDK_F1}, +{"F2", GDK_F2}, +{"F3", GDK_F3}, +{"F4", GDK_F4}, +{"F5", GDK_F5}, +{"F6", GDK_F6}, +{"F7", GDK_F7}, +{"F8", GDK_F8}, +{"F9", GDK_F9}, +{"F10", GDK_F10}, +{"F11", GDK_F11}, +{"F12", GDK_F12}, +{"F13", GDK_F13}, +{"F14", GDK_F14}, +{"F15", GDK_F15}, +{"Sys_Req", GDK_Sys_Req}, +{"KP_0", GDK_KP_0}, +{"KP_1", GDK_KP_1}, +{"KP_2", GDK_KP_2}, +{"KP_3", GDK_KP_3}, +{"KP_4", GDK_KP_4}, +{"KP_5", GDK_KP_5}, +{"KP_6", GDK_KP_6}, +{"KP_7", GDK_KP_7}, +{"KP_8", GDK_KP_8}, +{"KP_9", GDK_KP_9}, +{"KP_Add", GDK_KP_Add}, +{"KP_Decimal", GDK_KP_Decimal}, +{"KP_Divide", GDK_KP_Divide}, +{"KP_Enter", GDK_KP_Enter}, +{"KP_Equal", GDK_KP_Equal}, +{"KP_Multiply", GDK_KP_Multiply}, +{"KP_Subtract", GDK_KP_Subtract}, +{"help", GDK_Help}, +{"Menu", GDK_Menu}, +{"Power", GDK_VoidSymbol}, +{"Print", GDK_Print}, +{"Mode_switch", GDK_Mode_switch}, +{"Multi_Key", GDK_Multi_key}, +{"Num_Lock", GDK_Num_Lock}, +{"Pause", GDK_Pause}, +{"Escape", GDK_Escape}, + +{0,0}, +}; diff --git a/qemu-gtk/gdk_set_window_pointer.c b/qemu-gtk/gdk_set_window_pointer.c new file mode 100644 index 0000000..692eb7d --- /dev/null +++ b/qemu-gtk/gdk_set_window_pointer.c @@ -0,0 +1,54 @@ +/* TODO: figure out how to handle linux framebuffer case - need to call the gdk-fb specific handle_mouse_movement() function in gdkmouse-fb.c ... that gets ugly fast .. */ + +#ifndef _WIN32 + +#include +#include +#include + + GdkWindow* +gdk_window_set_pointer (GdkWindow *window, + gint x, + gint y) +{ + GdkWindow *return_val; + + return_val = NULL; + XWarpPointer (GDK_WINDOW_XDISPLAY(window), None, GDK_WINDOW_XID(window), 0, 0, 0, 0, x, y); + + return return_val; +} + +#else + +/* untested code based on MSDN library code... URL is : + + http://msdn.microsoft.com/library/default.asp?url=/library/ + en-us/winui/winui/windowsuserinterface/resources/cursors/ + usingcursors.asp + +Someone who codes on Windows want to tell me how to actually make this work?? + +*/ + +#include +#include +#include + + GdkWindow* +gdk_window_set_pointer (GdkWindow *window, + gint x, + gint y) +{ + GdkWindow *return_val; + POINT pt; + pt.x = x; + pt.y = y; + ClientToScreen(GDK_WINDOW_HWND(window), &pt); + SetCursorPos(pt.x, pt.y); + return_val = NULL; + return return_val; +} + +#endif + diff --git a/qemu-gtk/gtk2.c b/qemu-gtk/gtk2.c new file mode 100644 index 0000000..35d24f3 --- /dev/null +++ b/qemu-gtk/gtk2.c @@ -0,0 +1,946 @@ +/* + * QEMU GTK2 display driver + * based on SDL driver by Fabrice + * + * Copyright (c) 2005 Jim Brown + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +#include +#include + +#include "fullscreen.h" + +/* define our own bitshift enums to allow qemugtk to know difference between left and right alt - something gtk doesnt provide in its modifiers mask. this uses qemu's own modifier_state[] map in order to guess correctly */ +typedef enum +{ + gtkshiftleft = 1 << 0, + gtkshiftright = 1 << 1, + gtkcontrolleft = 1 << 2, + gtkcontrolright = 1 << 3, + gtkaltleft = 1 << 4, + gtkaltright = 1 << 5, + gtkcapslock = 1 << 6 +} gtk2keymod; + + +static GtkWidget *window, *swindow; +static GtkWidget *event_port, *menubar; +static GdkImage *image=NULL; +static GdkCursor *invisible_cursor; +static int ox = 0, oy = 0; +static int gui_grab; /* if true, all keyboard/mouse events are grabbed */ +static int gui_rgrab; /* if true, all keyboard/mouse events are grabbed */ +static int last_vm_running; +static int gui_saved_grab; +static int gui_fullscreen; +static int gui_key_modifier_pressed; +static int gui_keysym; +static int gui_fullscreen_initial_grab; +static int gui_grab_code = gtkaltleft | gtkcontrolleft; +static uint8_t modifiers_state[256]; +static int window_resize = 0; +static unsigned int cw, ch; +static gint win_x, win_y, win_w, win_h; +static GtkWidget * box; /* because GtkWindow/GtkBin holds only one widget */ + +static gboolean gtk2_expose(GtkWidget *wid, GdkEventExpose *event) +{ + if (gui_fullscreen && wid == event_port) return FALSE; + if (!gui_fullscreen && wid == window) return FALSE; + gdk_draw_image(wid->window, wid->style->fg_gc[GTK_WIDGET_STATE(wid)], image, event->area.x, event->area.y, event->area.x, event->area.y, event->area.width, event->area.height); + return TRUE; +} + +static void gtk2_update(DisplayState *ds, int x, int y, int w, int h) +{ + // printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h); + GdkEventExpose ev; + ev.area.x = x; + ev.area.y = y; + ev.area.width = w; + ev.area.height = h; + /* catch the first resize done by the init function - make sure we don't ** + ** try to draw an image until screen has been finalized/realized/etc */ + if (event_port->window != NULL) + { + if (gui_fullscreen) + gtk2_expose(window, &ev); + else + gtk2_expose(event_port, &ev); + } +} + +static void gtk2_resize(DisplayState *ds, int w, int h) +{ + + // printf(" resizing to %d %d\n", w, h); + + if (gui_fullscreen) + { + if (cw != w || ch != h) + { + fullscreen_switch(win_x, win_y, w, h); /* changing video modes */ +/* apparently GTK enjoys moving static windows all over the place while +they are being resized - so we have to be tricky */ + //gtk_window_move(GTK_WINDOW(window), win_x, win_y); + //gtk_window_move(GTK_WINDOW(window), 0, 0); + gdk_window_move_resize(window->window, 0, 0, w, h); + } + } + + cw = w; ch = h; + + if (image) + g_object_unref(image); + +/* gdk_visual_get_best_with_depth() ??? but then how to paint onto window? */ + image = gdk_image_new(GDK_IMAGE_NORMAL, gdk_visual_get_system(), w, h); + gdk_image_set_colormap(image, gdk_colormap_get_system()); + + gtk_widget_set_size_request(event_port, w, h); + if (window_resize) + gdk_window_move_resize(window->window, win_x, win_y, w+25, h+50); + + ds->data = image->mem; + ds->linesize = image->bpl; + ds->depth = image->bits_per_pixel; + ds->width = w; + ds->height = h; + gtk2_update(ds, 0, 0, w, h); +} + +static gboolean gtk2_config(GtkWidget *wid, GdkEventConfigure *ev, DisplayState *ds) +{ + if (!gui_fullscreen) + { + win_x = ev->x; + win_y = ev->y; + win_w = ev->width; + win_h = ev->height; + } + return FALSE; +} + +/* generic keyboard conversion */ + +#include "gdk_keysym.h" +#include "keymaps.c" + +static kbd_layout_t *kbd_layout = NULL; + +static uint8_t gtk2_keyevent_to_keycode_generic(const GdkEventKey *ev) +{ + int keysym; + /* workaround for X11+SDL bug with AltGR - is it still needed for Gtk2? */ + keysym = ev->keyval; + if (keysym == 0 && ev->hardware_keycode == 113) + keysym = GDK_Mode_switch; + return keysym2scancode(kbd_layout, keysym); +} + +/* specific keyboard conversions from scan codes */ + +#if defined(_WIN32) + +#include + +static UINT vk2scan(UINT vk) +{ + return MapVirtualKey(vk,0); +} +static uint8_t gtk2_keyevent_to_keycode(const GdkEventKey *ev) +{ + return (uint8_t)vk2scan((UINT)(ev->hardware_keycode)); +} + +#else + +static const uint8_t x_keycode_to_pc_keycode[61] = { + 0xc7, /* 97 Home */ + 0xc8, /* 98 Up */ + 0xc9, /* 99 PgUp */ + 0xcb, /* 100 Left */ + 0x4c, /* 101 KP-5 */ + 0xcd, /* 102 Right */ + 0xcf, /* 103 End */ + 0xd0, /* 104 Down */ + 0xd1, /* 105 PgDn */ + 0xd2, /* 106 Ins */ + 0xd3, /* 107 Del */ + 0x9c, /* 108 Enter */ + 0x9d, /* 109 Ctrl-R */ + 0x0, /* 110 Pause */ + 0xb7, /* 111 Print */ + 0xb5, /* 112 Divide */ + 0xb8, /* 113 Alt-R */ + 0xc6, /* 114 Break */ + 0xdb, /* 115 windows left button */ + 0xdc, /* 116 windows right button */ + 0xdd, /* 117 right menu button */ + 0x0, /* 118 */ + 0x0, /* 119 */ + 0x70, /* 120 Hiragana_Katakana */ + 0x0, /* 121 */ + 0x0, /* 122 */ + 0x73, /* 123 backslash */ + 0x0, /* 124 */ + 0x0, /* 125 */ + 0x0, /* 126 */ + 0x0, /* 127 */ + 0x0, /* 128 */ + 0x79, /* 129 Henkan */ + 0x0, /* 130 */ + 0x7b, /* 131 Muhenkan */ + 0x0, /* 132 */ + 0x7d, /* 133 Yen */ + 0x0, /* 134 */ + 0x0, /* 135 */ + 0x47, /* 136 KP_7 */ + 0x48, /* 137 KP_8 */ + 0x49, /* 138 KP_9 */ + 0x4b, /* 139 KP_4 */ + 0x4c, /* 140 KP_5 */ + 0x4d, /* 141 KP_6 */ + 0x4f, /* 142 KP_1 */ + 0x50, /* 143 KP_2 */ + 0x51, /* 144 KP_3 */ + 0x52, /* 145 KP_0 */ + 0x53, /* 146 KP_. */ + 0x47, /* 147 KP_HOME */ + 0x48, /* 148 KP_UP */ + 0x49, /* 149 KP_PgUp */ + 0x4b, /* 150 KP_Left */ + 0x4c, /* 151 KP_ */ + 0x4d, /* 152 KP_Right */ + 0x4f, /* 153 KP_End */ + 0x50, /* 154 KP_Down */ + 0x51, /* 155 KP_PgDn */ + 0x52, /* 156 KP_Ins */ + 0x53, /* 157 KP_Del */ +}; + +static uint8_t gtk2_keyevent_to_keycode(const GdkEventKey *ev) +{ + int keycode; + + keycode = ev->hardware_keycode; + + if (keycode < 9) { + keycode = 0; + } else if (keycode < 97) { + keycode -= 8; /* just an offset */ + } else if (keycode < 158) { + /* use conversion table */ + keycode = x_keycode_to_pc_keycode[keycode - 97]; + } else { + keycode = 0; + } + return keycode; +} + +#endif + +static void reset_keys(void) +{ + int i; + for(i = 0; i < 256; i++) { + if (modifiers_state[i]) { + if (i & 0x80) + kbd_put_keycode(0xe0); + kbd_put_keycode(i | 0x80); + modifiers_state[i] = 0; + } + } +} + +/* convert GDK modifiers and invoke ugly hack to distinguish +between left and right shift/control/alt */ +static guint gtk2_GetModState(const GdkEventKey *ev) +{ + guint key = 0, keyval = ev->keyval, state = ev->state; + switch(keyval) + { + case GDK_Shift_L: + if (ev->type != GDK_KEY_RELEASE) + key |= gtkshiftleft; + keyval = 1; + break; + case GDK_Shift_R: + if (ev->type != GDK_KEY_RELEASE) + key |= gtkshiftright; + keyval = 1; + break; + case GDK_Control_L: + if (ev->type != GDK_KEY_RELEASE) + key |= gtkcontrolleft; + keyval = 2; + break; + case GDK_Control_R: + if (ev->type != GDK_KEY_RELEASE) + key |= gtkcontrolright; + keyval = 2; + break; + case GDK_Alt_L: + if (ev->type != GDK_KEY_RELEASE) + key |= gtkaltleft; + keyval = 3; + break; + case GDK_Alt_R: + if (ev->type != GDK_KEY_RELEASE) + key |= gtkaltright; + keyval = 3; + break; + case GDK_Caps_Lock: + if (ev->type != GDK_KEY_RELEASE) + key |= gtkcapslock; + keyval = 4; + break; + default: + keyval = 0; + break; + } + if (keyval != 1 && (state & GDK_SHIFT_MASK)) + { + if (modifiers_state[0x2a]) + key |= gtkshiftleft; + if (modifiers_state[0x36]) + key |= gtkshiftright; + } + if (keyval != 2 && (state & GDK_CONTROL_MASK)) + { + if (modifiers_state[0x1d]) + key |= gtkcontrolleft; + if (modifiers_state[0x9d]) + key |= gtkcontrolright; + } + if (keyval != 3 && (state & GDK_MOD1_MASK)) /* fixme: need to do a check to make sure that alt is mapped to GDK_MOD1_MASK in the GDK_Keymap */ + { + if (modifiers_state[0x38]) + key |= gtkaltleft; + if (modifiers_state[0xb8]) + key |= gtkaltright; + } + if (keyval != 4 && (state & GDK_LOCK_MASK)) + key |= gtkcapslock; + return key; +} + +static void gtk2_process_key(GdkEventKey *ev) +{ + int keycode, v; + + if (ev->keyval == GDK_Pause) { + /* specific case */ + v = 0; + if (ev->type == GDK_KEY_RELEASE) + v |= 0x80; + kbd_put_keycode(0xe1); + kbd_put_keycode(0x1d | v); + kbd_put_keycode(0x45 | v); + return; + } + + if (kbd_layout) { + keycode = gtk2_keyevent_to_keycode_generic(ev); + } else { + keycode = gtk2_keyevent_to_keycode(ev); + } + + switch(keycode) { + case 0x00: + /* sent when leaving window: reset the modifiers state */ + reset_keys(); + return; + case 0x2a: /* Left Shift */ + case 0x36: /* Right Shift */ + case 0x1d: /* Left CTRL */ + case 0x9d: /* Right CTRL */ + case 0x38: /* Left ALT */ + case 0xb8: /* Right ALT */ + if (ev->type == GDK_KEY_RELEASE) + modifiers_state[keycode] = 0; + else + modifiers_state[keycode] = 1; + break; + case 0x45: /* num lock */ + case 0x3a: /* caps lock */ + /* GTK does send the key up event, so we dont generate it */ + /*kbd_put_keycode(keycode); + kbd_put_keycode(keycode | 0x80); + return;*/ + break; + } + + /* now send the key code */ + if (keycode & 0x80) + kbd_put_keycode(0xe0); + if (ev->type == GDK_KEY_RELEASE) + kbd_put_keycode(keycode | 0x80); + else + kbd_put_keycode(keycode & 0x7f); +} + +static void gtk2_update_caption(void) +{ + char buf[1024]; + strcpy(buf, "QEMU Gtk"); + if (!vm_running) { + strcat(buf, " [Stopped]"); + } + if (gui_grab) { + strcat(buf, " - Press Ctrl-Alt to exit grab"); + } + gtk_window_set_title(GTK_WINDOW(window), buf); +} + +/* what a nasty hack. this should be a part of the GDK, not external!!! */ +#include "gdk_set_window_pointer.c" + +static void gtk2_grab_start(void) +{ + gint y; + guint events; + GdkModifierType state; /* dummy var */ + GtkWidget * grab; + if (gui_fullscreen) /* make sure grab is on correct x window */ + grab = window; + else + grab = event_port; +if (gui_rgrab || gui_fullscreen) +{ + events = GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_SCROLL_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK; + y = gdk_pointer_grab(grab->window, TRUE, events, grab->window, invisible_cursor, GDK_CURRENT_TIME); + if (y) + printf("GTK Warning - pointer grab failed!\n"); + y = gdk_keyboard_grab(grab->window, TRUE, GDK_CURRENT_TIME); + if (y) + printf("GTK Warning - keyboard grab failed!\n"); +} + /* do a dummy read to avoid moving mouse - set ox and oy to stay in sync */ + gdk_window_get_pointer(event_port->window, &ox, &oy, &state); + gui_grab = 1; + gtk2_update_caption(); +} + +static void gtk2_grab_end(void) +{ + GtkWidget * grab; + if (gui_fullscreen) + grab = window; + else + grab = event_port; +if (gui_rgrab || gui_fullscreen) +{ + gdk_pointer_ungrab(GDK_CURRENT_TIME); + gdk_keyboard_ungrab(GDK_CURRENT_TIME); + gdk_window_set_pointer(grab->window, win_x, win_y); +} + gui_grab = 0; + gtk2_update_caption(); +} + +static gboolean gtk2_send_mouse_event(GtkWidget *wid, GdkEvent *ev) +{ + int x, y, dx, dy, dz, state, buttons; + GtkWidget * grab; + if (gui_fullscreen) /* make sure grab is on correct x window */ + grab = window; + else + grab = event_port; + + if (gui_fullscreen && wid == event_port) return FALSE; + if (!gui_fullscreen && wid == window) return FALSE; + if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS) + return TRUE; /* double or triple click - superflurious - ignore */ + + if (gui_grab) + { + + if (ev->type == GDK_MOTION_NOTIFY && ev->motion.is_hint) + { + gdk_window_get_pointer(ev->motion.window, &x, &y, (GdkModifierType*)&state); + } + else + { + x = ev->motion.x; + y = ev->motion.y; + /* scroll.state occupies a different position in the union */ + if (ev->type == GDK_SCROLL) + state = ev->scroll.state; + else + state = ev->motion.state; + } + + dx = x - ox; + dy = y - oy; + // prevent infinite looping - 2.6.X + if ((ev->type == GDK_MOTION_NOTIFY) && (dx == 0) && (dy == 0)) return TRUE; + dz = 0; + ox = x; + oy = y; + + buttons = 0; + if ((state & GDK_BUTTON1_MASK)) + buttons |= MOUSE_EVENT_LBUTTON; + if ((state & GDK_BUTTON3_MASK)) + buttons |= MOUSE_EVENT_RBUTTON; + if ((state & GDK_BUTTON2_MASK)) + buttons |= MOUSE_EVENT_MBUTTON; + + if (ev->type == GDK_BUTTON_PRESS) + { + if (ev->button.button == 1) + buttons |= MOUSE_EVENT_LBUTTON; + if (ev->button.button == 3) + buttons |= MOUSE_EVENT_RBUTTON; + if (ev->button.button == 2) + buttons |= MOUSE_EVENT_MBUTTON; + } + else if (ev->type == GDK_BUTTON_RELEASE) + { + /* not sure if this is really necessary, but just to be on the safe side ** + ** reset qemu's mask so that a button thats being released will be shown ** + * missing from the mask (which lets the guest know the button was relased */ + buttons = 0; + + if ((state & GDK_BUTTON1_MASK) && ev->button.button != 1) + buttons |= MOUSE_EVENT_LBUTTON; + if ((state & GDK_BUTTON3_MASK) && ev->button.button != 3) + buttons |= MOUSE_EVENT_RBUTTON; + if ((state & GDK_BUTTON2_MASK) && ev->button.button != 2) + buttons |= MOUSE_EVENT_MBUTTON; + } + else if (ev->type == GDK_SCROLL) + { + /* test wheel - copied from Sebastien Bechet's gtk.c */ + if (ev->scroll.direction == GDK_SCROLL_UP) + dz--; + if (ev->scroll.direction == GDK_SCROLL_DOWN) + dz++; + } + + if ((ev->type == GDK_MOTION_NOTIFY) && (gui_rgrab || gui_fullscreen)) + { + /* wrap the x,y coordinates back onto the window */ + if (ev->motion.x <= (cw/4)) + x = ((3*cw/4)-1); + if (ev->motion.y <= (ch/4)) + y = ((3*ch/4)-1); + if (ev->motion.x >= (3*cw/4)) + x = (cw/4)+1; + if (ev->motion.y >= (3*ch/4)) + y = (ch/4)+1; + + /* make internal mouse move invisible */ + ox = x; + oy = y; + + gdk_window_set_pointer(grab->window, (gint)x, (gint)y); + } + + kbd_mouse_event(dx, dy, dz, buttons); + + } + else + { + + if (ev->type == GDK_BUTTON_PRESS && ev->button.button == 1) + { + /* start grabbing all events */ + gtk2_grab_start(); + } + + } + return TRUE; +} + +static void toggle_full_screen(DisplayState *ds) +{ + gui_fullscreen = !gui_fullscreen; + gtk2_resize(ds, image->width, image->height); + if (gui_fullscreen) { + gui_saved_grab = gui_grab; + gtk2_grab_start(); + gtk_window_get_position(GTK_WINDOW(window), &win_x, &win_y); + gdk_window_move_resize(window->window, 0, 0, ds->width, ds->height); + gtk_widget_hide(box); + fullscreen_switch(win_x, win_y, ds->width, ds->height); + } else { + fullscreen_reset(); + gdk_window_move_resize(window->window, win_x, win_y, win_w, win_h); + gui_fullscreen = !gui_fullscreen; + if (!gui_saved_grab) + gtk2_grab_end(); + gui_fullscreen = !gui_fullscreen; + gtk_widget_show(box); + } + vga_invalidate_display(); + vga_update_display(); +} + +static gboolean gtk2_key_press(GtkWidget *wid, GdkEventKey *ev, DisplayState *ds) +{ + int mod_state, internal_key = 0; + if (ev->type == GDK_KEY_PRESS) { + mod_state = (gtk2_GetModState(ev) & (int)gui_grab_code) == (int)gui_grab_code; + gui_key_modifier_pressed = mod_state; + if (gui_key_modifier_pressed) { + int keycode; + internal_key = 1; + keycode = gtk2_keyevent_to_keycode(ev); + switch(keycode) { + case 0x21: /* 'f' key on US keyboard */ + toggle_full_screen(ds); + gui_keysym = 1; + break; + case 0x02 ... 0x0a: /* '1' to '9' keys */ + console_select(keycode - 0x02); + if (is_active_console(vga_console)) { + /* tell the vga console to redisplay itself */ + vga_invalidate_display(); + } else { + /* display grab if going to a text console */ + if (gui_grab) + gtk2_grab_end(); + } + gui_keysym = 1; + break; + default: + break; + } + } else if (!is_active_console(vga_console)) { + int keysym; + keysym = 0; + if (ev->state & GDK_CONTROL_MASK) { + switch(ev->keyval) { + case GDK_Up: keysym = QEMU_KEY_CTRL_UP; break; + case GDK_Down: keysym = QEMU_KEY_CTRL_DOWN; break; + case GDK_Left: keysym = QEMU_KEY_CTRL_LEFT; break; + case GDK_Right: keysym = QEMU_KEY_CTRL_RIGHT; break; + case GDK_Home: keysym = QEMU_KEY_CTRL_HOME; break; + case GDK_End: keysym = QEMU_KEY_CTRL_END; break; + case GDK_Page_Up: keysym = QEMU_KEY_CTRL_PAGEUP; break; + case GDK_Page_Down: keysym = QEMU_KEY_CTRL_PAGEDOWN; break; + default: break; + } + } else { + switch(ev->keyval) { + case GDK_Up: keysym = QEMU_KEY_UP; break; + case GDK_Down: keysym = QEMU_KEY_DOWN; break; + case GDK_Left: keysym = QEMU_KEY_LEFT; break; + case GDK_Right: keysym = QEMU_KEY_RIGHT; break; + case GDK_Home: keysym = QEMU_KEY_HOME; break; + case GDK_End: keysym = QEMU_KEY_END; break; + case GDK_Page_Up: keysym = QEMU_KEY_PAGEUP; break; + case GDK_Page_Down: keysym = QEMU_KEY_PAGEDOWN; break; + case GDK_BackSpace: keysym = QEMU_KEY_BACKSPACE; break; + case GDK_Delete: keysym = QEMU_KEY_DELETE; break; + default: break; + } + } + if (keysym) { + kbd_put_keysym(keysym); + } /*else if (ev->key.keysym.unicode != 0) { + kbd_put_keysym(ev->key.keysym.unicode); + }*/ + } + } else if (ev->type == GDK_KEY_RELEASE) { + mod_state = (gtk2_GetModState(ev) & gui_grab_code); + if (!mod_state) { + if (gui_key_modifier_pressed) { + if (gui_keysym == 0) { + /* exit/enter grab if pressing Ctrl-Alt */ + if (!gui_grab) + gtk2_grab_start(); + else + gtk2_grab_end(); + /* SDL does not send back all the + modifiers key, so we must correct it */ + reset_keys(); + return TRUE; + } + gui_key_modifier_pressed = 0; + gui_keysym = 0; + internal_key = 1; + } + } + } + if (is_active_console(vga_console) && !internal_key) + gtk2_process_key(ev); + else if (internal_key && ev->type == GDK_KEY_RELEASE) + { + modifiers_state[0x38] = 0; + modifiers_state[0x1d] = 0; + kbd_put_keycode(0x38 | 0x80); + kbd_put_keycode(0x1d | 0x80); + } + else if (internal_key && ev->type == GDK_KEY_PRESS) + { + modifiers_state[0x38] = 1; + modifiers_state[0x1d] = 1; + } + return TRUE; +} + +static void gtk2_refresh(DisplayState *ds) +{ + if (last_vm_running != vm_running) { + last_vm_running = vm_running; + gtk2_update_caption(); + } + if (ds->data != image->mem) + { + ds->data = image->mem; + } + + if (is_active_console(vga_console)) + vga_update_display(); + while (gtk_events_pending()) + gtk_main_iteration(); +} + +static void gtk2_cleanup(void) +{ + if (gtk_main_level() != 0) + gtk_main_quit(); + fullscreen_cleanup(); +} + +static gboolean gtk2_deletewin(void) +{ + /* signal qemu that its time to shut itself off - this is the place that we hook to trap attempts to close the main qemu window */ + qemu_system_shutdown_request(); + return FALSE; + return TRUE; /* dont close the window right away! give qemu time to think */ +} + +static void gtk2_destroy(void) +{ + /* ideally we would call a hook here so qemu could clean itself up */ + gtk2_cleanup(); +} + +GtkWidget * gui_menu_add(const gchar * name) +{ + GtkWidget *menu, *menu_name; + menu = gtk_menu_new(); + menu_name = gtk_menu_item_new_with_label(name); + gtk_menu_shell_append(GTK_MENU_SHELL(menubar), menu_name); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_name), menu); + gtk_widget_show(menu_name); + return menu; +} + +GtkWidget * gui_menuitem_add(GtkWidget * menu, const gchar * name, void (*item_handler)(void)) +{ + GtkWidget *menu_item; + menu_item = gtk_menu_item_new_with_label(name); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); + g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(item_handler), NULL); + gtk_widget_show(menu_item); + return menu_item; +} + +GtkWidget * gui_menusep_add(GtkWidget * menu) +{ + GtkWidget *menu_item; + menu_item = gtk_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); + gtk_widget_show(menu_item); + return menu_item; +} + +GtkWidget * gui_submenu_add(GtkWidget * menu, const gchar * name) +{ + GtkWidget *submenu, *menu_name; + submenu = gtk_menu_new(); + menu_name = gtk_menu_item_new_with_label(name); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_name); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_name), submenu); + gtk_widget_show(menu_name); + return submenu; +} + +void base_vc_switch(int i) +{ + console_select(i); + if (is_active_console(vga_console)) { + /* tell the vga console to redisplay itself */ + vga_invalidate_display(); + } else { + /* display grab if going to a text console */ + if (gui_grab) + gtk2_grab_end(); + } +} +void vc_guest(void) { base_vc_switch(0); } +void vc_monitor(void) { base_vc_switch(1); } +void vc_serial(void) { base_vc_switch(2); } +void vc_para(void) { base_vc_switch(3); } +void nop(void) {} + +void gtk2_menu_init(void) +{ + GtkWidget * menu; + menu = gui_menu_add("Monitor"); + gui_menuitem_add(menu, "Reset", qemu_system_reset_request); + gui_menuitem_add(menu, "Quit", qemu_system_shutdown_request); + menu = gui_menu_add("Virtual Console"); + menu = gui_submenu_add(menu, "Switch to"); + gui_menuitem_add(menu, "Guest", vc_guest); + gui_menuitem_add(menu, "Monitor", vc_monitor); + gui_menuitem_add(menu, "Serial", vc_serial); + gui_menuitem_add(menu, "Parallel", vc_para); + menu = gui_menu_add("Help"); + gui_menuitem_add(menu, "About", nop); +} + +static int gargc=0; +static char ** gargv=NULL; +void gtk2_display_init(DisplayState *ds, int full_screen) +{ + int events; + +#if defined(__APPLE__) + /* always use generic keymaps */ + if (!keyboard_layout) + keyboard_layout = "en-us"; +#endif + if(keyboard_layout) { + kbd_layout = init_keyboard_layout(keyboard_layout); + if (!kbd_layout) + exit(1); + } + +/* note - this strips the GTK-specific args after the main program is done with them */ + if (!gtk_init_check (&gargc,&gargv)) + { + fprintf(stderr, "Could not load GTK\n"); + exit(0); + } + gui_rgrab = 0; + fullscreen_init(); + +/* note: adding GDK_DRAG_* and GDK_DROP_* would provide a mechanism for supporting drag and drop between host and guest */ + events = GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_FOCUS_CHANGE_MASK | GDK_SCROLL_MASK | GDK_STRUCTURE_MASK; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + swindow = gtk_scrolled_window_new(NULL,NULL); + event_port = gtk_event_box_new(); + menubar = gtk_menu_bar_new(); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS); + box = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(window), box); + gtk_box_pack_start(GTK_BOX(box), menubar, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(box), swindow, TRUE, TRUE, 0); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(swindow), event_port); + gtk_widget_set_events(event_port, events); + gtk_window_set_default_size(GTK_WINDOW(window), 720, 420); + gtk_widget_set_size_request(window, 0, 0); + gtk_widget_set_size_request(swindow, 720, 400); + gtk_widget_set_size_request(event_port, 720, 400); + gtk_window_set_gravity(GTK_WINDOW(window), GDK_GRAVITY_STATIC); + gtk2_menu_init(); + + g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(gtk2_deletewin), NULL); + g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk2_destroy), NULL); + gtk_container_set_border_width(GTK_CONTAINER(window), 0); + + g_signal_connect(G_OBJECT(window), "configure_event", G_CALLBACK(gtk2_config), NULL); + g_signal_connect(G_OBJECT(window), "expose_event", G_CALLBACK(gtk2_expose), NULL); + g_signal_connect(G_OBJECT(window), "motion_notify_event", G_CALLBACK(gtk2_send_mouse_event), NULL); + g_signal_connect(G_OBJECT(window), "button_press_event", G_CALLBACK(gtk2_send_mouse_event), NULL); + g_signal_connect(G_OBJECT(window), "button_release_event", G_CALLBACK(gtk2_send_mouse_event), NULL); + g_signal_connect(G_OBJECT(window), "scroll_event", G_CALLBACK(gtk2_send_mouse_event), NULL); + g_signal_connect(G_OBJECT(event_port), "expose_event", G_CALLBACK(gtk2_expose), NULL); + g_signal_connect(G_OBJECT(event_port), "motion_notify_event", G_CALLBACK(gtk2_send_mouse_event), NULL); + g_signal_connect(G_OBJECT(event_port), "button_press_event", G_CALLBACK(gtk2_send_mouse_event), NULL); + g_signal_connect(G_OBJECT(event_port), "button_release_event", G_CALLBACK(gtk2_send_mouse_event), NULL); + g_signal_connect(G_OBJECT(event_port), "scroll_event", G_CALLBACK(gtk2_send_mouse_event), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(gtk2_key_press), ds); + g_signal_connect(G_OBJECT(window), "key_release_event", G_CALLBACK(gtk2_key_press), ds); + + ds->dpy_update = gtk2_update; + ds->dpy_resize = gtk2_resize; + ds->dpy_refresh = gtk2_refresh; + + gchar nullpixdata[1] = { 0 }; + GdkColor nullcolor = { 0, 0, 0, 0 }; + GdkPixmap *invis = gdk_bitmap_create_from_data(window->window, nullpixdata, 1, 1); + invisible_cursor = gdk_cursor_new_from_pixmap(invis, invis, &nullcolor, &nullcolor, 0, 0); + + gtk2_resize(ds, 720, 400); + gtk2_update_caption(); + gui_grab = 0; + + gtk_widget_show(event_port); + gtk_widget_show(swindow); + gtk_widget_show(menubar); + gtk_widget_show(box); + gtk_widget_show(window); + if (full_screen) { + gui_fullscreen = 1; + gui_fullscreen_initial_grab = 1; + gtk2_grab_start(); + } +} + +/* we are allowed to look at the args, but not allowed to modify them +with the exception that we have to remove args that are meant for us, as +main qemu will not recognize them */ +void gui_checkargs(int * pargc, char *** pargv) +{ + int argc = *pargc; + char ** argv = *pargv; + char ** vargv; + int i, j = 0; + vargv = malloc(argc * sizeof(char*)); + for (i = 0; i < argc; i++) + { + if (!strcmp(argv[i], "-window-resize-on")) + { + window_resize = 1; + (*pargc)--; + } + else if (!strcmp(argv[i], "-raw-grab")) + { + gui_rgrab = 1; + (*pargc)--; + } + else + { + vargv[j] = argv[i]; + j++; + } + } + /* so lets hide them */ + for (i = 0; i < *pargc; i++) + { + (*pargv)[i] = vargv[i]; + } + if (argc != (*pargc)) (*pargv)[*pargc] = NULL; + free(vargv); + /* save a copy so we can pass them to gtk_init() later */ + gargc = *pargc; + gargv = *pargv; +} + diff --git a/qemu-gtk/gtk2gui.c b/qemu-gtk/gtk2gui.c new file mode 100644 index 0000000..370730e --- /dev/null +++ b/qemu-gtk/gtk2gui.c @@ -0,0 +1,872 @@ +/* + * QEMU GTK2 display driver + * based on SDL driver by Fabrice + * + * Copyright (c) 2005 Jim Brown + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +#include +#include + +#include "fullscreen.h" + +#include "interface.h" +#include "callbacks.h" +#include "support.h" + +/* define our own bitshift enums to allow qemugtk to know difference between left and right alt - something gtk doesnt provide in its modifiers mask. this uses qemu's own modifier_state[] map in order to guess correctly */ +typedef enum +{ + gtkshiftleft = 1 << 0, + gtkshiftright = 1 << 1, + gtkcontrolleft = 1 << 2, + gtkcontrolright = 1 << 3, + gtkaltleft = 1 << 4, + gtkaltright = 1 << 5, + gtkcapslock = 1 << 6 +} gtk2keymod; + + +static GtkWidget *window, *swindow; +static GtkWidget *event_port, *menubar; +static GdkImage *image=NULL; +static GdkCursor *invisible_cursor; +static int ox = 0, oy = 0; +static int gui_grab; /* if true, all keyboard/mouse events are grabbed */ +static int gui_rgrab; /* if true, all keyboard/mouse events are grabbed */ +static int last_vm_running; +static int gui_saved_grab; +static int gui_fullscreen; +static int gui_key_modifier_pressed; +static int gui_keysym; +static int gui_fullscreen_initial_grab; +static int gui_grab_code = gtkaltleft | gtkcontrolleft; +static uint8_t modifiers_state[256]; +static int window_resize = 0; +static unsigned int cw, ch; +static gint win_x, win_y, win_w, win_h; +static GtkWidget * box; /* because GtkWindow/GtkBin holds only one widget */ + +static gboolean gtk2_expose(GtkWidget *wid, GdkEventExpose *event) +{ + if (gui_fullscreen && wid == event_port) return FALSE; + if (!gui_fullscreen && wid == window) return FALSE; + gdk_draw_image(wid->window, wid->style->fg_gc[GTK_WIDGET_STATE(wid)], image, event->area.x, event->area.y, event->area.x, event->area.y, event->area.width, event->area.height); + return TRUE; +} + +static void gtk2_update(DisplayState *ds, int x, int y, int w, int h) +{ + // printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h); + GdkEventExpose ev; + ev.area.x = x; + ev.area.y = y; + ev.area.width = w; + ev.area.height = h; + /* catch the first resize done by the init function - make sure we don't ** + ** try to draw an image until screen has been finalized/realized/etc */ + if (event_port->window != NULL) + { + if (gui_fullscreen) + gtk2_expose(window, &ev); + else + gtk2_expose(event_port, &ev); + } +} + +static void gtk2_resize(DisplayState *ds, int w, int h) +{ + + // printf(" resizing to %d %d\n", w, h); + + if (gui_fullscreen) + { + if (cw != w || ch != h) + { + fullscreen_switch(win_x, win_y, w, h); /* changing video modes */ +/* apparently GTK enjoys moving static windows all over the place while +they are being resized - so we have to be tricky */ + //gtk_window_move(GTK_WINDOW(window), win_x, win_y); + //gtk_window_move(GTK_WINDOW(window), 0, 0); + gdk_window_move_resize(window->window, 0, 0, w, h); + } + } + + cw = w; ch = h; + + if (image) + g_object_unref(image); + +/* gdk_visual_get_best_with_depth() ??? but then how to paint onto window? */ + image = gdk_image_new(GDK_IMAGE_NORMAL, gdk_visual_get_system(), w, h); + gdk_image_set_colormap(image, gdk_colormap_get_system()); + + gtk_widget_set_size_request(event_port, w, h); + if (window_resize && (window->window != NULL)) + gdk_window_move_resize(window->window, win_x, win_y, w+25, h+50); + + ds->data = image->mem; + ds->linesize = image->bpl; + ds->depth = image->bits_per_pixel; + ds->width = w; + ds->height = h; + gtk2_update(ds, 0, 0, w, h); +} + +static gboolean gtk2_config(GtkWidget *wid, GdkEventConfigure *ev, DisplayState *ds) +{ + if (!gui_fullscreen) + { + win_x = ev->x; + win_y = ev->y; + win_w = ev->width; + win_h = ev->height; + } + return FALSE; +} + +/* generic keyboard conversion */ + +#include "gdk_keysym.h" +#include "keymaps.c" + +static kbd_layout_t *kbd_layout = NULL; + +static uint8_t gtk2_keyevent_to_keycode_generic(const GdkEventKey *ev) +{ + int keysym; + /* workaround for X11+SDL bug with AltGR - is it still needed for Gtk2? */ + keysym = ev->keyval; + if (keysym == 0 && ev->hardware_keycode == 113) + keysym = GDK_Mode_switch; + return keysym2scancode(kbd_layout, keysym); +} + +/* specific keyboard conversions from scan codes */ + +#if defined(_WIN32) + +#include + +static UINT vk2scan(UINT vk) +{ + return MapVirtualKey(vk,0); +} +static uint8_t gtk2_keyevent_to_keycode(const GdkEventKey *ev) +{ + return (uint8_t)vk2scan((UINT)(ev->hardware_keycode)); +} + +#else + +static const uint8_t x_keycode_to_pc_keycode[61] = { + 0xc7, /* 97 Home */ + 0xc8, /* 98 Up */ + 0xc9, /* 99 PgUp */ + 0xcb, /* 100 Left */ + 0x4c, /* 101 KP-5 */ + 0xcd, /* 102 Right */ + 0xcf, /* 103 End */ + 0xd0, /* 104 Down */ + 0xd1, /* 105 PgDn */ + 0xd2, /* 106 Ins */ + 0xd3, /* 107 Del */ + 0x9c, /* 108 Enter */ + 0x9d, /* 109 Ctrl-R */ + 0x0, /* 110 Pause */ + 0xb7, /* 111 Print */ + 0xb5, /* 112 Divide */ + 0xb8, /* 113 Alt-R */ + 0xc6, /* 114 Break */ + 0xdb, /* 115 windows left button */ + 0xdc, /* 116 windows right button */ + 0xdd, /* 117 right menu button */ + 0x0, /* 118 */ + 0x0, /* 119 */ + 0x70, /* 120 Hiragana_Katakana */ + 0x0, /* 121 */ + 0x0, /* 122 */ + 0x73, /* 123 backslash */ + 0x0, /* 124 */ + 0x0, /* 125 */ + 0x0, /* 126 */ + 0x0, /* 127 */ + 0x0, /* 128 */ + 0x79, /* 129 Henkan */ + 0x0, /* 130 */ + 0x7b, /* 131 Muhenkan */ + 0x0, /* 132 */ + 0x7d, /* 133 Yen */ + 0x0, /* 134 */ + 0x0, /* 135 */ + 0x47, /* 136 KP_7 */ + 0x48, /* 137 KP_8 */ + 0x49, /* 138 KP_9 */ + 0x4b, /* 139 KP_4 */ + 0x4c, /* 140 KP_5 */ + 0x4d, /* 141 KP_6 */ + 0x4f, /* 142 KP_1 */ + 0x50, /* 143 KP_2 */ + 0x51, /* 144 KP_3 */ + 0x52, /* 145 KP_0 */ + 0x53, /* 146 KP_. */ + 0x47, /* 147 KP_HOME */ + 0x48, /* 148 KP_UP */ + 0x49, /* 149 KP_PgUp */ + 0x4b, /* 150 KP_Left */ + 0x4c, /* 151 KP_ */ + 0x4d, /* 152 KP_Right */ + 0x4f, /* 153 KP_End */ + 0x50, /* 154 KP_Down */ + 0x51, /* 155 KP_PgDn */ + 0x52, /* 156 KP_Ins */ + 0x53, /* 157 KP_Del */ +}; + +static uint8_t gtk2_keyevent_to_keycode(const GdkEventKey *ev) +{ + int keycode; + + keycode = ev->hardware_keycode; + + if (keycode < 9) { + keycode = 0; + } else if (keycode < 97) { + keycode -= 8; /* just an offset */ + } else if (keycode < 158) { + /* use conversion table */ + keycode = x_keycode_to_pc_keycode[keycode - 97]; + } else { + keycode = 0; + } + return keycode; +} + +#endif + +static void reset_keys(void) +{ + int i; + for(i = 0; i < 256; i++) { + if (modifiers_state[i]) { + if (i & 0x80) + kbd_put_keycode(0xe0); + kbd_put_keycode(i | 0x80); + modifiers_state[i] = 0; + } + } +} + +/* convert GDK modifiers and invoke ugly hack to distinguish +between left and right shift/control/alt */ +static guint gtk2_GetModState(const GdkEventKey *ev) +{ + guint key = 0, keyval = ev->keyval, state = ev->state; + switch(keyval) + { + case GDK_Shift_L: + if (ev->type != GDK_KEY_RELEASE) + key |= gtkshiftleft; + keyval = 1; + break; + case GDK_Shift_R: + if (ev->type != GDK_KEY_RELEASE) + key |= gtkshiftright; + keyval = 1; + break; + case GDK_Control_L: + if (ev->type != GDK_KEY_RELEASE) + key |= gtkcontrolleft; + keyval = 2; + break; + case GDK_Control_R: + if (ev->type != GDK_KEY_RELEASE) + key |= gtkcontrolright; + keyval = 2; + break; + case GDK_Alt_L: + if (ev->type != GDK_KEY_RELEASE) + key |= gtkaltleft; + keyval = 3; + break; + case GDK_Alt_R: + if (ev->type != GDK_KEY_RELEASE) + key |= gtkaltright; + keyval = 3; + break; + case GDK_Caps_Lock: + if (ev->type != GDK_KEY_RELEASE) + key |= gtkcapslock; + keyval = 4; + break; + default: + keyval = 0; + break; + } + if (keyval != 1 && (state & GDK_SHIFT_MASK)) + { + if (modifiers_state[0x2a]) + key |= gtkshiftleft; + if (modifiers_state[0x36]) + key |= gtkshiftright; + } + if (keyval != 2 && (state & GDK_CONTROL_MASK)) + { + if (modifiers_state[0x1d]) + key |= gtkcontrolleft; + if (modifiers_state[0x9d]) + key |= gtkcontrolright; + } + if (keyval != 3 && (state & GDK_MOD1_MASK)) /* fixme: need to do a check to make sure that alt is mapped to GDK_MOD1_MASK in the GDK_Keymap */ + { + if (modifiers_state[0x38]) + key |= gtkaltleft; + if (modifiers_state[0xb8]) + key |= gtkaltright; + } + if (keyval != 4 && (state & GDK_LOCK_MASK)) + key |= gtkcapslock; + return key; +} + +static void gtk2_process_key(GdkEventKey *ev) +{ + int keycode, v; + + if (ev->keyval == GDK_Pause) { + /* specific case */ + v = 0; + if (ev->type == GDK_KEY_RELEASE) + v |= 0x80; + kbd_put_keycode(0xe1); + kbd_put_keycode(0x1d | v); + kbd_put_keycode(0x45 | v); + return; + } + + if (kbd_layout) { + keycode = gtk2_keyevent_to_keycode_generic(ev); + } else { + keycode = gtk2_keyevent_to_keycode(ev); + } + + switch(keycode) { + case 0x00: + /* sent when leaving window: reset the modifiers state */ + reset_keys(); + return; + case 0x2a: /* Left Shift */ + case 0x36: /* Right Shift */ + case 0x1d: /* Left CTRL */ + case 0x9d: /* Right CTRL */ + case 0x38: /* Left ALT */ + case 0xb8: /* Right ALT */ + if (ev->type == GDK_KEY_RELEASE) + modifiers_state[keycode] = 0; + else + modifiers_state[keycode] = 1; + break; + case 0x45: /* num lock */ + case 0x3a: /* caps lock */ + /* GTK does send the key up event, so we dont generate it */ + /*kbd_put_keycode(keycode); + kbd_put_keycode(keycode | 0x80); + return;*/ + break; + } + + /* now send the key code */ + if (keycode & 0x80) + kbd_put_keycode(0xe0); + if (ev->type == GDK_KEY_RELEASE) + kbd_put_keycode(keycode | 0x80); + else + kbd_put_keycode(keycode & 0x7f); +} + +static void gtk2_update_caption(void) +{ + char buf[1024]; + strcpy(buf, "QEMU Gtk"); + if (!vm_running) { + strcat(buf, " [Stopped]"); + } + if (gui_grab) { + strcat(buf, " - Press Ctrl-Alt to exit grab"); + } + gtk_window_set_title(GTK_WINDOW(window), buf); +} + +/* what a nasty hack. this should be a part of the GDK, not external!!! */ +#include "gdk_set_window_pointer.c" + +static void gtk2_grab_start(void) +{ + gint y; + guint events; + GdkModifierType state; /* dummy var */ + GtkWidget * grab; + if (gui_fullscreen) /* make sure grab is on correct x window */ + grab = window; + else + grab = event_port; +if (gui_rgrab || gui_fullscreen) +{ + events = GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_SCROLL_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK; + y = gdk_pointer_grab(grab->window, TRUE, events, grab->window, invisible_cursor, GDK_CURRENT_TIME); + if (y) + printf("GTK Warning - pointer grab failed!\n"); + y = gdk_keyboard_grab(grab->window, TRUE, GDK_CURRENT_TIME); + if (y) + printf("GTK Warning - keyboard grab failed!\n"); +} + /* do a dummy read to avoid moving mouse - set ox and oy to stay in sync */ + gdk_window_get_pointer(event_port->window, &ox, &oy, &state); + gui_grab = 1; + gtk2_update_caption(); +} + +static void gtk2_grab_end(void) +{ + GtkWidget * grab; + if (gui_fullscreen) + grab = window; + else + grab = event_port; +if (gui_rgrab || gui_fullscreen) +{ + gdk_pointer_ungrab(GDK_CURRENT_TIME); + gdk_keyboard_ungrab(GDK_CURRENT_TIME); + gdk_window_set_pointer(grab->window, win_x, win_y); +} + gui_grab = 0; + gtk2_update_caption(); +} + +static gboolean gtk2_send_mouse_event(GtkWidget *wid, GdkEvent *ev) +{ + int x, y, dx, dy, dz, state, buttons; + GtkWidget * grab; + if (gui_fullscreen) /* make sure grab is on correct x window */ + grab = window; + else + grab = event_port; + + if (gui_fullscreen && wid == event_port) return FALSE; + if (!gui_fullscreen && wid == window) return FALSE; + if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS) + return TRUE; /* double or triple click - superflurious - ignore */ + + if (gui_grab) + { + + if (ev->type == GDK_MOTION_NOTIFY && ev->motion.is_hint) + { + gdk_window_get_pointer(ev->motion.window, &x, &y, (GdkModifierType*)&state); + } + else + { + x = ev->motion.x; + y = ev->motion.y; + /* scroll.state occupies a different position in the union */ + if (ev->type == GDK_SCROLL) + state = ev->scroll.state; + else + state = ev->motion.state; + } + + dx = x - ox; + dy = y - oy; + // prevent infinite looping - 2.6.X + if ((ev->type == GDK_MOTION_NOTIFY) && (dx == 0) && (dy == 0)) return TRUE; + dz = 0; + ox = x; + oy = y; + + buttons = 0; + if ((state & GDK_BUTTON1_MASK)) + buttons |= MOUSE_EVENT_LBUTTON; + if ((state & GDK_BUTTON3_MASK)) + buttons |= MOUSE_EVENT_RBUTTON; + if ((state & GDK_BUTTON2_MASK)) + buttons |= MOUSE_EVENT_MBUTTON; + + if (ev->type == GDK_BUTTON_PRESS) + { + if (ev->button.button == 1) + buttons |= MOUSE_EVENT_LBUTTON; + if (ev->button.button == 3) + buttons |= MOUSE_EVENT_RBUTTON; + if (ev->button.button == 2) + buttons |= MOUSE_EVENT_MBUTTON; + } + else if (ev->type == GDK_BUTTON_RELEASE) + { + /* not sure if this is really necessary, but just to be on the safe side ** + ** reset qemu's mask so that a button thats being released will be shown ** + * missing from the mask (which lets the guest know the button was relased */ + buttons = 0; + + if ((state & GDK_BUTTON1_MASK) && ev->button.button != 1) + buttons |= MOUSE_EVENT_LBUTTON; + if ((state & GDK_BUTTON3_MASK) && ev->button.button != 3) + buttons |= MOUSE_EVENT_RBUTTON; + if ((state & GDK_BUTTON2_MASK) && ev->button.button != 2) + buttons |= MOUSE_EVENT_MBUTTON; + } + else if (ev->type == GDK_SCROLL) + { + /* test wheel - copied from Sebastien Bechet's gtk.c */ + if (ev->scroll.direction == GDK_SCROLL_UP) + dz--; + if (ev->scroll.direction == GDK_SCROLL_DOWN) + dz++; + } + + if ((ev->type == GDK_MOTION_NOTIFY) && (gui_rgrab || gui_fullscreen)) + { + /* wrap the x,y coordinates back onto the window */ + if (ev->motion.x <= (cw/4)) + x = ((3*cw/4)-1); + if (ev->motion.y <= (ch/4)) + y = ((3*ch/4)-1); + if (ev->motion.x >= (3*cw/4)) + x = (cw/4)+1; + if (ev->motion.y >= (3*ch/4)) + y = (ch/4)+1; + + /* make internal mouse move invisible */ + ox = x; + oy = y; + + gdk_window_set_pointer(grab->window, (gint)x, (gint)y); + } + + kbd_mouse_event(dx, dy, dz, buttons); + + } + else + { + + if (ev->type == GDK_BUTTON_PRESS && ev->button.button == 1) + { + /* start grabbing all events */ + gtk2_grab_start(); + } + + } + return TRUE; +} + +static void toggle_full_screen(DisplayState *ds) +{ + gui_fullscreen = !gui_fullscreen; + gtk2_resize(ds, image->width, image->height); + if (gui_fullscreen) { + gui_saved_grab = gui_grab; + gtk2_grab_start(); + gtk_window_get_position(GTK_WINDOW(window), &win_x, &win_y); + gdk_window_move_resize(window->window, 0, 0, ds->width, ds->height); + gtk_widget_hide(box); + fullscreen_switch(win_x, win_y, ds->width, ds->height); + } else { + fullscreen_reset(); + gdk_window_move_resize(window->window, win_x, win_y, win_w, win_h); + gui_fullscreen = !gui_fullscreen; + if (!gui_saved_grab) + gtk2_grab_end(); + gui_fullscreen = !gui_fullscreen; + gtk_widget_show(box); + } + vga_invalidate_display(); + vga_update_display(); +} + +static gboolean gtk2_key_press(GtkWidget *wid, GdkEventKey *ev, DisplayState *ds) +{ + int mod_state, internal_key = 0; + if (ev->type == GDK_KEY_PRESS) { + mod_state = (gtk2_GetModState(ev) & (int)gui_grab_code) == (int)gui_grab_code; + gui_key_modifier_pressed = mod_state; + if (gui_key_modifier_pressed) { + int keycode; + internal_key = 1; + keycode = gtk2_keyevent_to_keycode(ev); + switch(keycode) { + case 0x21: /* 'f' key on US keyboard */ + toggle_full_screen(ds); + gui_keysym = 1; + break; + case 0x02 ... 0x0a: /* '1' to '9' keys */ + console_select(keycode - 0x02); + if (is_active_console(vga_console)) { + /* tell the vga console to redisplay itself */ + vga_invalidate_display(); + } else { + /* display grab if going to a text console */ + if (gui_grab) + gtk2_grab_end(); + } + gui_keysym = 1; + break; + default: + break; + } + } else if (!is_active_console(vga_console)) { + int keysym; + keysym = 0; + if (ev->state & GDK_CONTROL_MASK) { + switch(ev->keyval) { + case GDK_Up: keysym = QEMU_KEY_CTRL_UP; break; + case GDK_Down: keysym = QEMU_KEY_CTRL_DOWN; break; + case GDK_Left: keysym = QEMU_KEY_CTRL_LEFT; break; + case GDK_Right: keysym = QEMU_KEY_CTRL_RIGHT; break; + case GDK_Home: keysym = QEMU_KEY_CTRL_HOME; break; + case GDK_End: keysym = QEMU_KEY_CTRL_END; break; + case GDK_Page_Up: keysym = QEMU_KEY_CTRL_PAGEUP; break; + case GDK_Page_Down: keysym = QEMU_KEY_CTRL_PAGEDOWN; break; + default: break; + } + } else { + switch(ev->keyval) { + case GDK_Up: keysym = QEMU_KEY_UP; break; + case GDK_Down: keysym = QEMU_KEY_DOWN; break; + case GDK_Left: keysym = QEMU_KEY_LEFT; break; + case GDK_Right: keysym = QEMU_KEY_RIGHT; break; + case GDK_Home: keysym = QEMU_KEY_HOME; break; + case GDK_End: keysym = QEMU_KEY_END; break; + case GDK_Page_Up: keysym = QEMU_KEY_PAGEUP; break; + case GDK_Page_Down: keysym = QEMU_KEY_PAGEDOWN; break; + case GDK_BackSpace: keysym = QEMU_KEY_BACKSPACE; break; + case GDK_Delete: keysym = QEMU_KEY_DELETE; break; + default: break; + } + } + if (keysym) { + kbd_put_keysym(keysym); + } /*else if (ev->key.keysym.unicode != 0) { + kbd_put_keysym(ev->key.keysym.unicode); + }*/ + } + } else if (ev->type == GDK_KEY_RELEASE) { + mod_state = (gtk2_GetModState(ev) & gui_grab_code); + if (!mod_state) { + if (gui_key_modifier_pressed) { + if (gui_keysym == 0) { + /* exit/enter grab if pressing Ctrl-Alt */ + if (!gui_grab) + gtk2_grab_start(); + else + gtk2_grab_end(); + /* SDL does not send back all the + modifiers key, so we must correct it */ + reset_keys(); + return TRUE; + } + gui_key_modifier_pressed = 0; + gui_keysym = 0; + internal_key = 1; + } + } + } + if (is_active_console(vga_console) && !internal_key) + gtk2_process_key(ev); + else if (internal_key && ev->type == GDK_KEY_RELEASE) + { + modifiers_state[0x38] = 0; + modifiers_state[0x1d] = 0; + kbd_put_keycode(0x38 | 0x80); + kbd_put_keycode(0x1d | 0x80); + } + else if (internal_key && ev->type == GDK_KEY_PRESS) + { + modifiers_state[0x38] = 1; + modifiers_state[0x1d] = 1; + } + return TRUE; +} + +static void gtk2_refresh(DisplayState *ds) +{ + if (last_vm_running != vm_running) { + last_vm_running = vm_running; + gtk2_update_caption(); + } + if (ds->data != image->mem) + { + ds->data = image->mem; + } + + if (is_active_console(vga_console)) + vga_update_display(); + while (gtk_events_pending()) + gtk_main_iteration(); +} + +static void gtk2_cleanup(void) +{ + if (gtk_main_level() != 0) + gtk_main_quit(); + fullscreen_cleanup(); +} + +static gboolean gtk2_deletewin(void) +{ + /* signal qemu that its time to shut itself off - this is the place that we hook to trap attempts to close the main qemu window */ + qemu_system_shutdown_request(); + return FALSE; + return TRUE; /* dont close the window right away! give qemu time to think */ +} + +static void gtk2_destroy(void) +{ + /* ideally we would call a hook here so qemu could clean itself up */ + gtk2_cleanup(); +} + +static int gargc=0; +static char ** gargv=NULL; +void gtk2_display_init(DisplayState *ds, int full_screen) +{ + int events; + +#if defined(__APPLE__) + /* always use generic keymaps */ + if (!keyboard_layout) + keyboard_layout = "en-us"; +#endif + if(keyboard_layout) { + kbd_layout = init_keyboard_layout(keyboard_layout); + if (!kbd_layout) + exit(1); + } + +/* note - this strips the GTK-specific args after the main program is done with them */ + if (!gtk_init_check (&gargc,&gargv)) + { + fprintf(stderr, "Could not load GTK\n"); + exit(0); + } + gui_rgrab = 0; + fullscreen_init(); + +/* note: adding GDK_DRAG_* and GDK_DROP_* would provide a mechanism for supporting drag and drop between host and guest */ + events = GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_FOCUS_CHANGE_MASK | GDK_SCROLL_MASK | GDK_STRUCTURE_MASK; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + swindow = gtk_scrolled_window_new(NULL,NULL); + event_port = gtk_event_box_new(); + menubar = gtk_menu_bar_new(); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS); + box = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(window), box); + create_winmain(window, box, swindow); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(swindow), event_port); + gtk_widget_set_events(event_port, events); + gtk_window_set_default_size(GTK_WINDOW(window), 720, 420); + gtk_widget_set_size_request(window, 0, 0); + gtk_widget_set_size_request(swindow, 720, 400); + gtk_widget_set_size_request(event_port, 720, 400); + gtk_window_set_gravity(GTK_WINDOW(window), GDK_GRAVITY_STATIC); + + g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(gtk2_deletewin), NULL); + g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk2_destroy), NULL); + gtk_container_set_border_width(GTK_CONTAINER(window), 0); + + g_signal_connect(G_OBJECT(window), "configure_event", G_CALLBACK(gtk2_config), NULL); + g_signal_connect(G_OBJECT(window), "expose_event", G_CALLBACK(gtk2_expose), NULL); + g_signal_connect(G_OBJECT(window), "motion_notify_event", G_CALLBACK(gtk2_send_mouse_event), NULL); + g_signal_connect(G_OBJECT(window), "button_press_event", G_CALLBACK(gtk2_send_mouse_event), NULL); + g_signal_connect(G_OBJECT(window), "button_release_event", G_CALLBACK(gtk2_send_mouse_event), NULL); + g_signal_connect(G_OBJECT(window), "scroll_event", G_CALLBACK(gtk2_send_mouse_event), NULL); + g_signal_connect(G_OBJECT(event_port), "expose_event", G_CALLBACK(gtk2_expose), NULL); + g_signal_connect(G_OBJECT(event_port), "motion_notify_event", G_CALLBACK(gtk2_send_mouse_event), NULL); + g_signal_connect(G_OBJECT(event_port), "button_press_event", G_CALLBACK(gtk2_send_mouse_event), NULL); + g_signal_connect(G_OBJECT(event_port), "button_release_event", G_CALLBACK(gtk2_send_mouse_event), NULL); + g_signal_connect(G_OBJECT(event_port), "scroll_event", G_CALLBACK(gtk2_send_mouse_event), NULL); + g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(gtk2_key_press), ds); + g_signal_connect(G_OBJECT(window), "key_release_event", G_CALLBACK(gtk2_key_press), ds); + + ds->dpy_update = gtk2_update; + ds->dpy_resize = gtk2_resize; + ds->dpy_refresh = gtk2_refresh; + + gchar nullpixdata[1] = { 0 }; + GdkColor nullcolor = { 0, 0, 0, 0 }; + GdkPixmap *invis = gdk_bitmap_create_from_data(window->window, nullpixdata, 1, 1); + invisible_cursor = gdk_cursor_new_from_pixmap(invis, invis, &nullcolor, &nullcolor, 0, 0); + + gtk2_resize(ds, 720, 400); + gtk2_update_caption(); + gui_grab = 0; + + gtk_widget_show(event_port); + gtk_widget_show(swindow); + gtk_widget_show(box); + gtk_widget_show(window); + if (full_screen) { + gui_fullscreen = 1; + gui_fullscreen_initial_grab = 1; + gtk2_grab_start(); + } +} + +/* we are allowed to look at the args, but not allowed to modify them +with the exception that we have to remove args that are meant for us, as +main qemu will not recognize them */ +void gui_checkargs(int * pargc, char *** pargv) +{ + int argc = *pargc; + char ** argv = *pargv; + char ** vargv; + int i, j = 0; + vargv = malloc(argc * sizeof(char*)); + for (i = 0; i < argc; i++) + { + if (!strcmp(argv[i], "-window-resize-on")) + { + window_resize = 1; + (*pargc)--; + } + else if (!strcmp(argv[i], "-raw-grab")) + { + gui_rgrab = 1; + (*pargc)--; + } + else + { + vargv[j] = argv[i]; + j++; + } + } + /* so lets hide them */ + for (i = 0; i < *pargc; i++) + { + (*pargv)[i] = vargv[i]; + } + if (argc != (*pargc)) (*pargv)[*pargc] = NULL; + free(vargv); + /* save a copy so we can pass them to gtk_init() later */ + gargc = *pargc; + gargv = *pargv; +} + diff --git a/qemu-gtk/interface.c b/qemu-gtk/interface.c new file mode 100644 index 0000000..d71b48c --- /dev/null +++ b/qemu-gtk/interface.c @@ -0,0 +1,1172 @@ +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#include "callbacks.h" +#include "interface.h" +#include "support.h" + +#define GLADE_HOOKUP_OBJECT(component,widget,name) \ + g_object_set_data_full (G_OBJECT (component), name, \ + gtk_widget_ref (widget), (GDestroyNotify) gtk_widget_unref) + +#define GLADE_HOOKUP_OBJECT_NO_REF(component,widget,name) \ + g_object_set_data (G_OBJECT (component), name, widget) + +GtkWidget* +create_filechoosernew (void) +{ + GtkWidget *filechoosernew; + GtkWidget *dialog_vbox1; + GtkWidget *dialog_action_area1; + GtkWidget *button1; + GtkWidget *button2; + + filechoosernew = gtk_file_chooser_dialog_new (_("Open File"), NULL, GTK_FILE_CHOOSER_ACTION_OPEN, NULL); + gtk_window_set_destroy_with_parent (GTK_WINDOW (filechoosernew), TRUE); + gtk_window_set_type_hint (GTK_WINDOW (filechoosernew), GDK_WINDOW_TYPE_HINT_DIALOG); + + dialog_vbox1 = GTK_DIALOG (filechoosernew)->vbox; + gtk_widget_show (dialog_vbox1); + + dialog_action_area1 = GTK_DIALOG (filechoosernew)->action_area; + gtk_widget_show (dialog_action_area1); + gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area1), GTK_BUTTONBOX_END); + + button1 = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (button1); + gtk_dialog_add_action_widget (GTK_DIALOG (filechoosernew), button1, GTK_RESPONSE_CANCEL); + GTK_WIDGET_SET_FLAGS (button1, GTK_CAN_DEFAULT); + + button2 = gtk_button_new_from_stock ("gtk-open"); + gtk_widget_show (button2); + gtk_dialog_add_action_widget (GTK_DIALOG (filechoosernew), button2, GTK_RESPONSE_OK); + GTK_WIDGET_SET_FLAGS (button2, GTK_CAN_DEFAULT); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (filechoosernew, filechoosernew, "filechoosernew"); + GLADE_HOOKUP_OBJECT_NO_REF (filechoosernew, dialog_vbox1, "dialog_vbox1"); + GLADE_HOOKUP_OBJECT_NO_REF (filechoosernew, dialog_action_area1, "dialog_action_area1"); + GLADE_HOOKUP_OBJECT (filechoosernew, button1, "button1"); + GLADE_HOOKUP_OBJECT (filechoosernew, button2, "button2"); + + gtk_widget_grab_default (button2); + return filechoosernew; +} + +GtkWidget* +create_filechooseropen (void) +{ + GtkWidget *filechooseropen; + GtkWidget *dialog_vbox2; + GtkWidget *dialog_action_area2; + GtkWidget *button3; + GtkWidget *button4; + + filechooseropen = gtk_file_chooser_dialog_new (_("Open File"), NULL, GTK_FILE_CHOOSER_ACTION_OPEN, NULL); + gtk_window_set_type_hint (GTK_WINDOW (filechooseropen), GDK_WINDOW_TYPE_HINT_DIALOG); + + dialog_vbox2 = GTK_DIALOG (filechooseropen)->vbox; + gtk_widget_show (dialog_vbox2); + + dialog_action_area2 = GTK_DIALOG (filechooseropen)->action_area; + gtk_widget_show (dialog_action_area2); + gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area2), GTK_BUTTONBOX_END); + + button3 = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (button3); + gtk_dialog_add_action_widget (GTK_DIALOG (filechooseropen), button3, GTK_RESPONSE_CANCEL); + GTK_WIDGET_SET_FLAGS (button3, GTK_CAN_DEFAULT); + + button4 = gtk_button_new_from_stock ("gtk-open"); + gtk_widget_show (button4); + gtk_dialog_add_action_widget (GTK_DIALOG (filechooseropen), button4, GTK_RESPONSE_OK); + GTK_WIDGET_SET_FLAGS (button4, GTK_CAN_DEFAULT); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (filechooseropen, filechooseropen, "filechooseropen"); + GLADE_HOOKUP_OBJECT_NO_REF (filechooseropen, dialog_vbox2, "dialog_vbox2"); + GLADE_HOOKUP_OBJECT_NO_REF (filechooseropen, dialog_action_area2, "dialog_action_area2"); + GLADE_HOOKUP_OBJECT (filechooseropen, button3, "button3"); + GLADE_HOOKUP_OBJECT (filechooseropen, button4, "button4"); + + gtk_widget_grab_default (button4); + return filechooseropen; +} + +GtkWidget* +create_filechoosersave (void) +{ + GtkWidget *filechoosersave; + GtkWidget *dialog_vbox3; + GtkWidget *dialog_action_area3; + GtkWidget *button5; + GtkWidget *button6; + + filechoosersave = gtk_file_chooser_dialog_new (_("Save File As"), NULL, GTK_FILE_CHOOSER_ACTION_OPEN, NULL); + gtk_window_set_type_hint (GTK_WINDOW (filechoosersave), GDK_WINDOW_TYPE_HINT_DIALOG); + + dialog_vbox3 = GTK_DIALOG (filechoosersave)->vbox; + gtk_widget_show (dialog_vbox3); + + dialog_action_area3 = GTK_DIALOG (filechoosersave)->action_area; + gtk_widget_show (dialog_action_area3); + gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area3), GTK_BUTTONBOX_END); + + button5 = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (button5); + gtk_dialog_add_action_widget (GTK_DIALOG (filechoosersave), button5, GTK_RESPONSE_CANCEL); + GTK_WIDGET_SET_FLAGS (button5, GTK_CAN_DEFAULT); + + button6 = gtk_button_new_from_stock ("gtk-open"); + gtk_widget_show (button6); + gtk_dialog_add_action_widget (GTK_DIALOG (filechoosersave), button6, GTK_RESPONSE_OK); + GTK_WIDGET_SET_FLAGS (button6, GTK_CAN_DEFAULT); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (filechoosersave, filechoosersave, "filechoosersave"); + GLADE_HOOKUP_OBJECT_NO_REF (filechoosersave, dialog_vbox3, "dialog_vbox3"); + GLADE_HOOKUP_OBJECT_NO_REF (filechoosersave, dialog_action_area3, "dialog_action_area3"); + GLADE_HOOKUP_OBJECT (filechoosersave, button5, "button5"); + GLADE_HOOKUP_OBJECT (filechoosersave, button6, "button6"); + + gtk_widget_grab_default (button6); + return filechoosersave; +} + +GtkWidget* +create_winprefs (void) +{ + GtkWidget *winprefs3; + GtkWidget *vbox10; + GtkWidget *notebook3; + GtkWidget *vbox11; + GtkWidget *hbox33; + GtkWidget *label86; + GtkWidget *entry58; + GtkWidget *label87; + GtkWidget *hbox34; + GtkWidget *label88; + GtkWidget *combobox7; + GtkWidget *frame7; + GtkWidget *alignment7; + GtkWidget *vbox12; + GtkWidget *table13; + GtkWidget *entry59; + GtkWidget *entry60; + GtkWidget *entry61; + GtkWidget *entry62; + GtkWidget *button57; + GtkWidget *button58; + GtkWidget *button59; + GtkWidget *button60; + GtkWidget *button61; + GtkWidget *button62; + GtkWidget *entry63; + GtkWidget *entry64; + GtkWidget *checkbutton12; + GtkWidget *checkbutton13; + GtkWidget *checkbutton14; + GtkWidget *checkbutton15; + GtkWidget *checkbutton16; + GtkWidget *checkbutton17; + GtkWidget *hseparator2; + GtkWidget *checkbutton18; + GtkWidget *label89; + GtkWidget *label74; + GtkWidget *frame8; + GtkWidget *alignment8; + GtkWidget *vbox13; + GtkWidget *table14; + GtkWidget *entry65; + GtkWidget *entry66; + GtkWidget *entry67; + GtkWidget *button63; + GtkWidget *button64; + GtkWidget *label93; + GtkWidget *label92; + GtkWidget *label91; + GtkWidget *label90; + GtkWidget *frame9; + GtkWidget *alignment9; + GtkWidget *table15; + GtkWidget *hbox35; + GtkWidget *combobox8; + GtkWidget *entry68; + GtkWidget *label94; + GtkWidget *entry69; + GtkWidget *label95; + GtkWidget *button65; + GtkWidget *hbox36; + GtkWidget *combobox9; + GtkWidget *button66; + GtkWidget *label96; + GtkWidget *checkbutton19; + GtkWidget *label81; + GtkWidget *table16; + GtkWidget *label97; + GtkWidget *label98; + GtkWidget *label99; + GtkWidget *entry72; + GtkWidget *entry70; + GtkWidget *entry71; + GtkWidget *label85; + GtkWidget *hbuttonbox3; + GtkWidget *button55; + GtkWidget *button56; + GtkTooltips *tooltips; + + tooltips = gtk_tooltips_new (); + + winprefs3 = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (winprefs3), _("Preferences")); + + vbox10 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox10); + gtk_container_add (GTK_CONTAINER (winprefs3), vbox10); + + notebook3 = gtk_notebook_new (); + gtk_widget_show (notebook3); + gtk_box_pack_start (GTK_BOX (vbox10), notebook3, TRUE, TRUE, 0); + gtk_notebook_set_scrollable (GTK_NOTEBOOK (notebook3), TRUE); + + vbox11 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox11); + gtk_container_add (GTK_CONTAINER (notebook3), vbox11); + + hbox33 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox33); + gtk_box_pack_start (GTK_BOX (vbox11), hbox33, TRUE, TRUE, 0); + + label86 = gtk_label_new (_("Memory : ")); + gtk_widget_show (label86); + gtk_box_pack_start (GTK_BOX (hbox33), label86, FALSE, FALSE, 0); + gtk_misc_set_padding (GTK_MISC (label86), 2, 0); + + entry58 = gtk_entry_new (); + gtk_widget_show (entry58); + gtk_box_pack_start (GTK_BOX (hbox33), entry58, FALSE, FALSE, 0); + gtk_tooltips_set_tip (tooltips, entry58, _("example : 128"), NULL); + gtk_entry_set_max_length (GTK_ENTRY (entry58), 4); + gtk_entry_set_width_chars (GTK_ENTRY (entry58), 4); + + label87 = gtk_label_new (_("megabytes.")); + gtk_widget_show (label87); + gtk_box_pack_start (GTK_BOX (hbox33), label87, FALSE, FALSE, 0); + gtk_misc_set_padding (GTK_MISC (label87), 2, 0); + + hbox34 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox34); + gtk_box_pack_start (GTK_BOX (vbox11), hbox34, TRUE, TRUE, 0); + + label88 = gtk_label_new (_("Boot From")); + gtk_widget_show (label88); + gtk_box_pack_start (GTK_BOX (hbox34), label88, FALSE, FALSE, 0); + + combobox7 = gtk_combo_box_new_text (); + gtk_widget_show (combobox7); + gtk_box_pack_start (GTK_BOX (hbox34), combobox7, TRUE, TRUE, 0); + gtk_combo_box_append_text (GTK_COMBO_BOX (combobox7), _("Floppy Disk A")); + gtk_combo_box_append_text (GTK_COMBO_BOX (combobox7), _("Floppy Disk B")); + gtk_combo_box_append_text (GTK_COMBO_BOX (combobox7), _("IDE0 - Master")); + gtk_combo_box_append_text (GTK_COMBO_BOX (combobox7), _("IDE0 - Slave")); + gtk_combo_box_append_text (GTK_COMBO_BOX (combobox7), _("IDE1 - Master")); + gtk_combo_box_append_text (GTK_COMBO_BOX (combobox7), _("IDE1 - Slave")); + + frame7 = gtk_frame_new (NULL); + gtk_widget_show (frame7); + gtk_box_pack_start (GTK_BOX (vbox11), frame7, FALSE, FALSE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (frame7), GTK_SHADOW_OUT); + + alignment7 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment7); + gtk_container_add (GTK_CONTAINER (frame7), alignment7); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment7), 0, 0, 12, 0); + + vbox12 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox12); + gtk_container_add (GTK_CONTAINER (alignment7), vbox12); + + table13 = gtk_table_new (6, 3, FALSE); + gtk_widget_show (table13); + gtk_box_pack_start (GTK_BOX (vbox12), table13, FALSE, FALSE, 0); + + entry59 = gtk_entry_new (); + gtk_widget_show (entry59); + gtk_table_attach (GTK_TABLE (table13), entry59, 1, 2, 1, 2, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_entry_set_has_frame (GTK_ENTRY (entry59), FALSE); + + entry60 = gtk_entry_new (); + gtk_widget_show (entry60); + gtk_table_attach (GTK_TABLE (table13), entry60, 1, 2, 2, 3, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_entry_set_has_frame (GTK_ENTRY (entry60), FALSE); + + entry61 = gtk_entry_new (); + gtk_widget_show (entry61); + gtk_table_attach (GTK_TABLE (table13), entry61, 1, 2, 3, 4, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_entry_set_has_frame (GTK_ENTRY (entry61), FALSE); + + entry62 = gtk_entry_new (); + gtk_widget_show (entry62); + gtk_table_attach (GTK_TABLE (table13), entry62, 1, 2, 5, 6, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_entry_set_has_frame (GTK_ENTRY (entry62), FALSE); + + button57 = gtk_button_new_from_stock ("gtk-open"); + gtk_widget_show (button57); + gtk_table_attach (GTK_TABLE (table13), button57, 2, 3, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_button_set_relief (GTK_BUTTON (button57), GTK_RELIEF_NONE); + gtk_button_set_focus_on_click (GTK_BUTTON (button57), FALSE); + + button58 = gtk_button_new_from_stock ("gtk-open"); + gtk_widget_show (button58); + gtk_table_attach (GTK_TABLE (table13), button58, 2, 3, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_button_set_relief (GTK_BUTTON (button58), GTK_RELIEF_NONE); + + button59 = gtk_button_new_from_stock ("gtk-open"); + gtk_widget_show (button59); + gtk_table_attach (GTK_TABLE (table13), button59, 2, 3, 2, 3, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_button_set_relief (GTK_BUTTON (button59), GTK_RELIEF_NONE); + + button60 = gtk_button_new_from_stock ("gtk-open"); + gtk_widget_show (button60); + gtk_table_attach (GTK_TABLE (table13), button60, 2, 3, 3, 4, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_button_set_relief (GTK_BUTTON (button60), GTK_RELIEF_NONE); + + button61 = gtk_button_new_from_stock ("gtk-open"); + gtk_widget_show (button61); + gtk_table_attach (GTK_TABLE (table13), button61, 2, 3, 4, 5, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_button_set_relief (GTK_BUTTON (button61), GTK_RELIEF_NONE); + + button62 = gtk_button_new_from_stock ("gtk-open"); + gtk_widget_show (button62); + gtk_table_attach (GTK_TABLE (table13), button62, 2, 3, 5, 6, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_button_set_relief (GTK_BUTTON (button62), GTK_RELIEF_NONE); + + entry63 = gtk_entry_new (); + gtk_widget_show (entry63); + gtk_table_attach (GTK_TABLE (table13), entry63, 1, 2, 4, 5, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_entry_set_has_frame (GTK_ENTRY (entry63), FALSE); + + entry64 = gtk_entry_new (); + gtk_widget_show (entry64); + gtk_table_attach (GTK_TABLE (table13), entry64, 1, 2, 0, 1, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_entry_set_has_frame (GTK_ENTRY (entry64), FALSE); + + checkbutton12 = gtk_check_button_new_with_mnemonic (_("Floppy Disk A")); + gtk_widget_show (checkbutton12); + gtk_table_attach (GTK_TABLE (table13), checkbutton12, 0, 1, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + checkbutton13 = gtk_check_button_new_with_mnemonic (_("Floppy Disk B")); + gtk_widget_show (checkbutton13); + gtk_table_attach (GTK_TABLE (table13), checkbutton13, 0, 1, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + checkbutton14 = gtk_check_button_new_with_mnemonic (_("IDE0 - Master")); + gtk_widget_show (checkbutton14); + gtk_table_attach (GTK_TABLE (table13), checkbutton14, 0, 1, 2, 3, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + checkbutton15 = gtk_check_button_new_with_mnemonic (_("IDE1 - Master")); + gtk_widget_show (checkbutton15); + gtk_table_attach (GTK_TABLE (table13), checkbutton15, 0, 1, 4, 5, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + checkbutton16 = gtk_check_button_new_with_mnemonic (_("IDE0 - Slave")); + gtk_widget_show (checkbutton16); + gtk_table_attach (GTK_TABLE (table13), checkbutton16, 0, 1, 3, 4, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + checkbutton17 = gtk_check_button_new_with_mnemonic (_("IDE1 - Slave")); + gtk_widget_show (checkbutton17); + gtk_table_attach (GTK_TABLE (table13), checkbutton17, 0, 1, 5, 6, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + hseparator2 = gtk_hseparator_new (); + gtk_widget_show (hseparator2); + gtk_box_pack_start (GTK_BOX (vbox12), hseparator2, FALSE, FALSE, 0); + + checkbutton18 = gtk_check_button_new_with_mnemonic (_("IDE1 - Master is a CDROM")); + gtk_widget_show (checkbutton18); + gtk_box_pack_start (GTK_BOX (vbox12), checkbutton18, FALSE, FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (checkbutton18), 10); + gtk_button_set_focus_on_click (GTK_BUTTON (checkbutton18), FALSE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton18), TRUE); + + label89 = gtk_label_new (_("Disk")); + gtk_widget_show (label89); + gtk_frame_set_label_widget (GTK_FRAME (frame7), label89); + gtk_label_set_use_markup (GTK_LABEL (label89), TRUE); + + label74 = gtk_label_new (_("Standard Options")); + gtk_widget_show (label74); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook3), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook3), 0), label74); + + frame8 = gtk_frame_new (NULL); + gtk_widget_show (frame8); + gtk_container_add (GTK_CONTAINER (notebook3), frame8); + gtk_frame_set_shadow_type (GTK_FRAME (frame8), GTK_SHADOW_NONE); + + alignment8 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment8); + gtk_container_add (GTK_CONTAINER (frame8), alignment8); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment8), 0, 0, 12, 0); + + vbox13 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox13); + gtk_container_add (GTK_CONTAINER (alignment8), vbox13); + + table14 = gtk_table_new (3, 3, FALSE); + gtk_widget_show (table14); + gtk_box_pack_start (GTK_BOX (vbox13), table14, FALSE, TRUE, 0); + + entry65 = gtk_entry_new (); + gtk_widget_show (entry65); + gtk_table_attach (GTK_TABLE (table14), entry65, 1, 2, 0, 1, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, entry65, _("example : 00:40:F4:8A:32:D1"), NULL); + gtk_entry_set_max_length (GTK_ENTRY (entry65), 17); + gtk_entry_set_has_frame (GTK_ENTRY (entry65), FALSE); + + entry66 = gtk_entry_new (); + gtk_widget_show (entry66); + gtk_table_attach (GTK_TABLE (table14), entry66, 1, 2, 1, 2, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, entry66, _("example : /netboot"), NULL); + gtk_entry_set_has_frame (GTK_ENTRY (entry66), FALSE); + + entry67 = gtk_entry_new (); + gtk_widget_show (entry67); + gtk_table_attach (GTK_TABLE (table14), entry67, 1, 2, 2, 3, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, entry67, _("example : /share"), NULL); + gtk_entry_set_has_frame (GTK_ENTRY (entry67), FALSE); + + button63 = gtk_button_new_from_stock ("gtk-open"); + gtk_widget_show (button63); + gtk_table_attach (GTK_TABLE (table14), button63, 2, 3, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_button_set_relief (GTK_BUTTON (button63), GTK_RELIEF_NONE); + + button64 = gtk_button_new_from_stock ("gtk-open"); + gtk_widget_show (button64); + gtk_table_attach (GTK_TABLE (table14), button64, 2, 3, 2, 3, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_button_set_relief (GTK_BUTTON (button64), GTK_RELIEF_NONE); + + label93 = gtk_label_new (""); + gtk_widget_show (label93); + gtk_table_attach (GTK_TABLE (table14), label93, 2, 3, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 7); + gtk_misc_set_alignment (GTK_MISC (label93), 0, 0.5); + + label92 = gtk_label_new (_("SMB Local Access")); + gtk_widget_show (label92); + gtk_table_attach (GTK_TABLE (table14), label92, 0, 1, 2, 3, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 4, 0); + gtk_misc_set_alignment (GTK_MISC (label92), 0, 0.5); + + label91 = gtk_label_new (_("TFTP Local Access")); + gtk_widget_show (label91); + gtk_table_attach (GTK_TABLE (table14), label91, 0, 1, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 4, 0); + gtk_misc_set_alignment (GTK_MISC (label91), 0, 0.5); + + label90 = gtk_label_new (_("Mac Address")); + gtk_widget_show (label90); + gtk_table_attach (GTK_TABLE (table14), label90, 0, 1, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 4, 0); + + frame9 = gtk_frame_new (NULL); + gtk_widget_show (frame9); + gtk_box_pack_start (GTK_BOX (vbox13), frame9, FALSE, FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (frame9), 4); + gtk_frame_set_shadow_type (GTK_FRAME (frame9), GTK_SHADOW_IN); + + alignment9 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment9); + gtk_container_add (GTK_CONTAINER (frame9), alignment9); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment9), 0, 4, 12, 4); + + table15 = gtk_table_new (2, 1, FALSE); + gtk_widget_show (table15); + gtk_container_add (GTK_CONTAINER (alignment9), table15); + + hbox35 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox35); + gtk_table_attach (GTK_TABLE (table15), hbox35, 0, 1, 0, 1, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + combobox8 = gtk_combo_box_new_text (); + gtk_widget_show (combobox8); + gtk_box_pack_start (GTK_BOX (hbox35), combobox8, FALSE, FALSE, 0); + gtk_combo_box_append_text (GTK_COMBO_BOX (combobox8), _("TCP")); + gtk_combo_box_append_text (GTK_COMBO_BOX (combobox8), _("UDP")); + + entry68 = gtk_entry_new (); + gtk_widget_show (entry68); + gtk_box_pack_start (GTK_BOX (hbox35), entry68, FALSE, FALSE, 0); + gtk_tooltips_set_tip (tooltips, entry68, _("Example : 80"), NULL); + gtk_entry_set_has_frame (GTK_ENTRY (entry68), FALSE); + + label94 = gtk_label_new (_("host port to ")); + gtk_widget_show (label94); + gtk_box_pack_start (GTK_BOX (hbox35), label94, TRUE, TRUE, 0); + + entry69 = gtk_entry_new (); + gtk_widget_show (entry69); + gtk_box_pack_start (GTK_BOX (hbox35), entry69, FALSE, FALSE, 0); + gtk_tooltips_set_tip (tooltips, entry69, _("Example : 80"), NULL); + gtk_entry_set_has_frame (GTK_ENTRY (entry69), FALSE); + + label95 = gtk_label_new (_("guest port.")); + gtk_widget_show (label95); + gtk_box_pack_start (GTK_BOX (hbox35), label95, TRUE, TRUE, 0); + + button65 = gtk_button_new_from_stock ("gtk-add"); + gtk_widget_show (button65); + gtk_box_pack_start (GTK_BOX (hbox35), button65, FALSE, FALSE, 0); + gtk_button_set_relief (GTK_BUTTON (button65), GTK_RELIEF_NONE); + + hbox36 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox36); + gtk_table_attach (GTK_TABLE (table15), hbox36, 0, 1, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + combobox9 = gtk_combo_box_new_text (); + gtk_widget_show (combobox9); + gtk_box_pack_start (GTK_BOX (hbox36), combobox9, TRUE, TRUE, 0); + + button66 = gtk_button_new_from_stock ("gtk-remove"); + gtk_widget_show (button66); + gtk_box_pack_start (GTK_BOX (hbox36), button66, FALSE, FALSE, 0); + gtk_button_set_relief (GTK_BUTTON (button66), GTK_RELIEF_NONE); + + label96 = gtk_label_new (_("Redirect")); + gtk_widget_show (label96); + gtk_frame_set_label_widget (GTK_FRAME (frame9), label96); + gtk_label_set_use_markup (GTK_LABEL (label96), TRUE); + + checkbutton19 = gtk_check_button_new_with_mnemonic (_("Activate Network Card")); + gtk_widget_show (checkbutton19); + gtk_frame_set_label_widget (GTK_FRAME (frame8), checkbutton19); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton19), TRUE); + + label81 = gtk_label_new (_("Network Options")); + gtk_widget_show (label81); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook3), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook3), 1), label81); + + table16 = gtk_table_new (3, 2, FALSE); + gtk_widget_show (table16); + gtk_container_add (GTK_CONTAINER (notebook3), table16); + + label97 = gtk_label_new (_("Monitor Redirect")); + gtk_widget_show (label97); + gtk_table_attach (GTK_TABLE (table16), label97, 0, 1, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_misc_set_alignment (GTK_MISC (label97), 0, 0.5); + gtk_misc_set_padding (GTK_MISC (label97), 0, 4); + + label98 = gtk_label_new (_("Serial Redirect")); + gtk_widget_show (label98); + gtk_table_attach (GTK_TABLE (table16), label98, 0, 1, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_misc_set_alignment (GTK_MISC (label98), 0, 0.5); + gtk_misc_set_padding (GTK_MISC (label98), 0, 4); + + label99 = gtk_label_new (_("Command Line")); + gtk_widget_show (label99); + gtk_table_attach (GTK_TABLE (table16), label99, 0, 1, 2, 3, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_misc_set_alignment (GTK_MISC (label99), 0, 0.5); + gtk_misc_set_padding (GTK_MISC (label99), 0, 4); + + entry72 = gtk_entry_new (); + gtk_widget_show (entry72); + gtk_table_attach (GTK_TABLE (table16), entry72, 1, 2, 0, 1, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 4); + gtk_entry_set_has_frame (GTK_ENTRY (entry72), FALSE); + + entry70 = gtk_entry_new (); + gtk_widget_show (entry70); + gtk_table_attach (GTK_TABLE (table16), entry70, 1, 2, 1, 2, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 4); + gtk_entry_set_has_frame (GTK_ENTRY (entry70), FALSE); + + entry71 = gtk_entry_new (); + gtk_widget_show (entry71); + gtk_table_attach (GTK_TABLE (table16), entry71, 1, 2, 2, 3, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 4); + gtk_entry_set_has_frame (GTK_ENTRY (entry71), FALSE); + + label85 = gtk_label_new (_("Debug/Expert Options")); + gtk_widget_show (label85); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook3), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook3), 2), label85); + + hbuttonbox3 = gtk_hbutton_box_new (); + gtk_widget_show (hbuttonbox3); + gtk_box_pack_start (GTK_BOX (vbox10), hbuttonbox3, FALSE, FALSE, 0); + gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox3), GTK_BUTTONBOX_END); + + button55 = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (button55); + gtk_container_add (GTK_CONTAINER (hbuttonbox3), button55); + GTK_WIDGET_SET_FLAGS (button55, GTK_CAN_DEFAULT); + + button56 = gtk_button_new_from_stock ("gtk-ok"); + gtk_widget_show (button56); + gtk_container_add (GTK_CONTAINER (hbuttonbox3), button56); + GTK_WIDGET_SET_FLAGS (button56, GTK_CAN_DEFAULT); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (winprefs3, winprefs3, "winprefs3"); + GLADE_HOOKUP_OBJECT (winprefs3, vbox10, "vbox10"); + GLADE_HOOKUP_OBJECT (winprefs3, notebook3, "notebook3"); + GLADE_HOOKUP_OBJECT (winprefs3, vbox11, "vbox11"); + GLADE_HOOKUP_OBJECT (winprefs3, hbox33, "hbox33"); + GLADE_HOOKUP_OBJECT (winprefs3, label86, "label86"); + GLADE_HOOKUP_OBJECT (winprefs3, entry58, "entry58"); + GLADE_HOOKUP_OBJECT (winprefs3, label87, "label87"); + GLADE_HOOKUP_OBJECT (winprefs3, hbox34, "hbox34"); + GLADE_HOOKUP_OBJECT (winprefs3, label88, "label88"); + GLADE_HOOKUP_OBJECT (winprefs3, combobox7, "combobox7"); + GLADE_HOOKUP_OBJECT (winprefs3, frame7, "frame7"); + GLADE_HOOKUP_OBJECT (winprefs3, alignment7, "alignment7"); + GLADE_HOOKUP_OBJECT (winprefs3, vbox12, "vbox12"); + GLADE_HOOKUP_OBJECT (winprefs3, table13, "table13"); + GLADE_HOOKUP_OBJECT (winprefs3, entry59, "entry59"); + GLADE_HOOKUP_OBJECT (winprefs3, entry60, "entry60"); + GLADE_HOOKUP_OBJECT (winprefs3, entry61, "entry61"); + GLADE_HOOKUP_OBJECT (winprefs3, entry62, "entry62"); + GLADE_HOOKUP_OBJECT (winprefs3, button57, "button57"); + GLADE_HOOKUP_OBJECT (winprefs3, button58, "button58"); + GLADE_HOOKUP_OBJECT (winprefs3, button59, "button59"); + GLADE_HOOKUP_OBJECT (winprefs3, button60, "button60"); + GLADE_HOOKUP_OBJECT (winprefs3, button61, "button61"); + GLADE_HOOKUP_OBJECT (winprefs3, button62, "button62"); + GLADE_HOOKUP_OBJECT (winprefs3, entry63, "entry63"); + GLADE_HOOKUP_OBJECT (winprefs3, entry64, "entry64"); + GLADE_HOOKUP_OBJECT (winprefs3, checkbutton12, "checkbutton12"); + GLADE_HOOKUP_OBJECT (winprefs3, checkbutton13, "checkbutton13"); + GLADE_HOOKUP_OBJECT (winprefs3, checkbutton14, "checkbutton14"); + GLADE_HOOKUP_OBJECT (winprefs3, checkbutton15, "checkbutton15"); + GLADE_HOOKUP_OBJECT (winprefs3, checkbutton16, "checkbutton16"); + GLADE_HOOKUP_OBJECT (winprefs3, checkbutton17, "checkbutton17"); + GLADE_HOOKUP_OBJECT (winprefs3, hseparator2, "hseparator2"); + GLADE_HOOKUP_OBJECT (winprefs3, checkbutton18, "checkbutton18"); + GLADE_HOOKUP_OBJECT (winprefs3, label89, "label89"); + GLADE_HOOKUP_OBJECT (winprefs3, label74, "label74"); + GLADE_HOOKUP_OBJECT (winprefs3, frame8, "frame8"); + GLADE_HOOKUP_OBJECT (winprefs3, alignment8, "alignment8"); + GLADE_HOOKUP_OBJECT (winprefs3, vbox13, "vbox13"); + GLADE_HOOKUP_OBJECT (winprefs3, table14, "table14"); + GLADE_HOOKUP_OBJECT (winprefs3, entry65, "entry65"); + GLADE_HOOKUP_OBJECT (winprefs3, entry66, "entry66"); + GLADE_HOOKUP_OBJECT (winprefs3, entry67, "entry67"); + GLADE_HOOKUP_OBJECT (winprefs3, button63, "button63"); + GLADE_HOOKUP_OBJECT (winprefs3, button64, "button64"); + GLADE_HOOKUP_OBJECT (winprefs3, label93, "label93"); + GLADE_HOOKUP_OBJECT (winprefs3, label92, "label92"); + GLADE_HOOKUP_OBJECT (winprefs3, label91, "label91"); + GLADE_HOOKUP_OBJECT (winprefs3, label90, "label90"); + GLADE_HOOKUP_OBJECT (winprefs3, frame9, "frame9"); + GLADE_HOOKUP_OBJECT (winprefs3, alignment9, "alignment9"); + GLADE_HOOKUP_OBJECT (winprefs3, table15, "table15"); + GLADE_HOOKUP_OBJECT (winprefs3, hbox35, "hbox35"); + GLADE_HOOKUP_OBJECT (winprefs3, combobox8, "combobox8"); + GLADE_HOOKUP_OBJECT (winprefs3, entry68, "entry68"); + GLADE_HOOKUP_OBJECT (winprefs3, label94, "label94"); + GLADE_HOOKUP_OBJECT (winprefs3, entry69, "entry69"); + GLADE_HOOKUP_OBJECT (winprefs3, label95, "label95"); + GLADE_HOOKUP_OBJECT (winprefs3, button65, "button65"); + GLADE_HOOKUP_OBJECT (winprefs3, hbox36, "hbox36"); + GLADE_HOOKUP_OBJECT (winprefs3, combobox9, "combobox9"); + GLADE_HOOKUP_OBJECT (winprefs3, button66, "button66"); + GLADE_HOOKUP_OBJECT (winprefs3, label96, "label96"); + GLADE_HOOKUP_OBJECT (winprefs3, checkbutton19, "checkbutton19"); + GLADE_HOOKUP_OBJECT (winprefs3, label81, "label81"); + GLADE_HOOKUP_OBJECT (winprefs3, table16, "table16"); + GLADE_HOOKUP_OBJECT (winprefs3, label97, "label97"); + GLADE_HOOKUP_OBJECT (winprefs3, label98, "label98"); + GLADE_HOOKUP_OBJECT (winprefs3, label99, "label99"); + GLADE_HOOKUP_OBJECT (winprefs3, entry72, "entry72"); + GLADE_HOOKUP_OBJECT (winprefs3, entry70, "entry70"); + GLADE_HOOKUP_OBJECT (winprefs3, entry71, "entry71"); + GLADE_HOOKUP_OBJECT (winprefs3, label85, "label85"); + GLADE_HOOKUP_OBJECT (winprefs3, hbuttonbox3, "hbuttonbox3"); + GLADE_HOOKUP_OBJECT (winprefs3, button55, "button55"); + GLADE_HOOKUP_OBJECT (winprefs3, button56, "button56"); + GLADE_HOOKUP_OBJECT_NO_REF (winprefs3, tooltips, "tooltips"); + + return winprefs3; +} + +GtkWidget* +create_winmain (GtkWidget * winmain3, GtkWidget * vbox35, GtkWidget * scroll_window35) +{ + GtkWidget *menubar1; + GtkWidget *menuitem1; + GtkWidget *menuitem1_menu; + GtkWidget *nouveau1; + GtkWidget *ouvrir1; + GtkWidget *enregistrer1; + GtkWidget *enregistrer_sous1; + GtkWidget *separatormenuitem1; + GtkWidget *fermer1; + GtkWidget *image99; + GtkWidget *quitter1; + GtkWidget *menuitem2; + GtkWidget *menuitem2_menu; + GtkWidget *couper1; + GtkWidget *copier1; + GtkWidget *coller1; + GtkWidget *supprimer1; + GtkWidget *preferences1; + GtkWidget *image100; + GtkWidget *machine1; + GtkWidget *machine1_menu; + GtkWidget *run1; + GtkWidget *image101; + GtkWidget *run_in_a_snapshot1; + GtkWidget *image102; + GtkWidget *s__parateur1; + GtkWidget *pause2; + GtkWidget *image103; + GtkWidget *continue1; + GtkWidget *image104; + GtkWidget *reset1; + GtkWidget *image105; + GtkWidget *shutdown1; + GtkWidget *image106; + GtkWidget *menuitem3; + GtkWidget *menuitem3_menu; + GtkWidget *zoom1; + GtkWidget *image107; + GtkWidget *menuitem4; + GtkWidget *menuitem4_menu; + GtkWidget *___propos1; + GtkWidget *toolbar2; + GtkIconSize tmp_toolbar_icon_size; + GtkWidget *toolbutton3; + GtkWidget *toolbutton4; + GtkWidget *separatortoolitem1; + GtkWidget *tmp_image; + GtkWidget *toolbutton5; + GtkWidget *toolbutton6; + GtkWidget *toolbutton7; + GtkWidget *toolbutton8; + GtkWidget *separatortoolitem2; + GtkWidget *toolbutton1; + GtkWidget *toolbutton2; + GtkWidget *statusbar3; + GtkAccelGroup *accel_group; + + accel_group = gtk_accel_group_new (); + + menubar1 = gtk_menu_bar_new (); + gtk_widget_show (menubar1); + gtk_box_pack_start (GTK_BOX (vbox35), menubar1, FALSE, FALSE, 0); + + menuitem1 = gtk_menu_item_new_with_mnemonic (_("_Fichier")); + gtk_widget_show (menuitem1); + gtk_container_add (GTK_CONTAINER (menubar1), menuitem1); + + menuitem1_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem1), menuitem1_menu); + + nouveau1 = gtk_image_menu_item_new_from_stock ("gtk-new", accel_group); + gtk_widget_show (nouveau1); + gtk_container_add (GTK_CONTAINER (menuitem1_menu), nouveau1); + + ouvrir1 = gtk_image_menu_item_new_from_stock ("gtk-open", accel_group); + gtk_widget_show (ouvrir1); + gtk_container_add (GTK_CONTAINER (menuitem1_menu), ouvrir1); + + enregistrer1 = gtk_image_menu_item_new_from_stock ("gtk-save", accel_group); + gtk_widget_show (enregistrer1); + gtk_container_add (GTK_CONTAINER (menuitem1_menu), enregistrer1); + + enregistrer_sous1 = gtk_image_menu_item_new_from_stock ("gtk-save-as", accel_group); + gtk_widget_show (enregistrer_sous1); + gtk_container_add (GTK_CONTAINER (menuitem1_menu), enregistrer_sous1); + + separatormenuitem1 = gtk_separator_menu_item_new (); + gtk_widget_show (separatormenuitem1); + gtk_container_add (GTK_CONTAINER (menuitem1_menu), separatormenuitem1); + gtk_widget_set_sensitive (separatormenuitem1, FALSE); + + fermer1 = gtk_image_menu_item_new_with_mnemonic (_("_Fermer")); + gtk_widget_show (fermer1); + gtk_container_add (GTK_CONTAINER (menuitem1_menu), fermer1); + gtk_widget_add_accelerator (fermer1, "activate", accel_group, + GDK_W, GDK_CONTROL_MASK, + GTK_ACCEL_VISIBLE); + + image99 = gtk_image_new_from_stock ("gtk-close", GTK_ICON_SIZE_MENU); + gtk_widget_show (image99); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (fermer1), image99); + + quitter1 = gtk_image_menu_item_new_from_stock ("gtk-quit", accel_group); + gtk_widget_show (quitter1); + gtk_container_add (GTK_CONTAINER (menuitem1_menu), quitter1); + + menuitem2 = gtk_menu_item_new_with_mnemonic (_("_\303\211dition")); + gtk_widget_show (menuitem2); + gtk_container_add (GTK_CONTAINER (menubar1), menuitem2); + + menuitem2_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem2), menuitem2_menu); + + couper1 = gtk_image_menu_item_new_from_stock ("gtk-cut", accel_group); + gtk_widget_show (couper1); + gtk_container_add (GTK_CONTAINER (menuitem2_menu), couper1); + + copier1 = gtk_image_menu_item_new_from_stock ("gtk-copy", accel_group); + gtk_widget_show (copier1); + gtk_container_add (GTK_CONTAINER (menuitem2_menu), copier1); + + coller1 = gtk_image_menu_item_new_from_stock ("gtk-paste", accel_group); + gtk_widget_show (coller1); + gtk_container_add (GTK_CONTAINER (menuitem2_menu), coller1); + + supprimer1 = gtk_image_menu_item_new_from_stock ("gtk-delete", accel_group); + gtk_widget_show (supprimer1); + gtk_container_add (GTK_CONTAINER (menuitem2_menu), supprimer1); + + preferences1 = gtk_image_menu_item_new_with_mnemonic (_("_Pr\303\251f\303\251rences")); + gtk_widget_show (preferences1); + gtk_container_add (GTK_CONTAINER (menuitem2_menu), preferences1); + + image100 = gtk_image_new_from_stock ("gtk-properties", GTK_ICON_SIZE_MENU); + gtk_widget_show (image100); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (preferences1), image100); + + machine1 = gtk_menu_item_new_with_mnemonic (_("Machine")); + gtk_widget_show (machine1); + gtk_container_add (GTK_CONTAINER (menubar1), machine1); + + machine1_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (machine1), machine1_menu); + + run1 = gtk_image_menu_item_new_with_mnemonic (_("_Run")); + gtk_widget_show (run1); + gtk_container_add (GTK_CONTAINER (machine1_menu), run1); + + image101 = gtk_image_new_from_stock ("gtk-execute", GTK_ICON_SIZE_MENU); + gtk_widget_show (image101); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (run1), image101); + + run_in_a_snapshot1 = gtk_image_menu_item_new_with_mnemonic (_("Run in a _snapshot")); + gtk_widget_show (run_in_a_snapshot1); + gtk_container_add (GTK_CONTAINER (machine1_menu), run_in_a_snapshot1); + + image102 = gtk_image_new_from_stock ("gtk-execute", GTK_ICON_SIZE_MENU); + gtk_widget_show (image102); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (run_in_a_snapshot1), image102); + + s__parateur1 = gtk_separator_menu_item_new (); + gtk_widget_show (s__parateur1); + gtk_container_add (GTK_CONTAINER (machine1_menu), s__parateur1); + gtk_widget_set_sensitive (s__parateur1, FALSE); + + pause2 = gtk_image_menu_item_new_with_mnemonic (_("_Pause")); + gtk_widget_show (pause2); + gtk_container_add (GTK_CONTAINER (machine1_menu), pause2); + + image103 = gtk_image_new_from_stock ("gtk-no", GTK_ICON_SIZE_MENU); + gtk_widget_show (image103); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (pause2), image103); + + continue1 = gtk_image_menu_item_new_with_mnemonic (_("_Continue")); + gtk_widget_show (continue1); + gtk_container_add (GTK_CONTAINER (machine1_menu), continue1); + + image104 = gtk_image_new_from_stock ("gtk-yes", GTK_ICON_SIZE_MENU); + gtk_widget_show (image104); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (continue1), image104); + + reset1 = gtk_image_menu_item_new_with_mnemonic (_("_Reset")); + gtk_widget_show (reset1); + gtk_container_add (GTK_CONTAINER (machine1_menu), reset1); + + image105 = gtk_image_new_from_stock ("gtk-refresh", GTK_ICON_SIZE_MENU); + gtk_widget_show (image105); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (reset1), image105); + + shutdown1 = gtk_image_menu_item_new_with_mnemonic (_("_Shutdown")); + gtk_widget_show (shutdown1); + gtk_container_add (GTK_CONTAINER (machine1_menu), shutdown1); + + image106 = gtk_image_new_from_stock ("gtk-stop", GTK_ICON_SIZE_MENU); + gtk_widget_show (image106); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (shutdown1), image106); + + menuitem3 = gtk_menu_item_new_with_mnemonic (_("Afficha_ge")); + gtk_widget_show (menuitem3); + gtk_container_add (GTK_CONTAINER (menubar1), menuitem3); + + menuitem3_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem3), menuitem3_menu); + + zoom1 = gtk_image_menu_item_new_with_mnemonic (_("_Zoom")); + gtk_widget_show (zoom1); + gtk_container_add (GTK_CONTAINER (menuitem3_menu), zoom1); + + image107 = gtk_image_new_from_stock ("gtk-zoom-100", GTK_ICON_SIZE_MENU); + gtk_widget_show (image107); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (zoom1), image107); + + menuitem4 = gtk_menu_item_new_with_mnemonic (_("_Aide")); + gtk_widget_show (menuitem4); + gtk_container_add (GTK_CONTAINER (menubar1), menuitem4); + + menuitem4_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem4), menuitem4_menu); + + ___propos1 = gtk_menu_item_new_with_mnemonic (_("\303\200 _propos")); + gtk_widget_show (___propos1); + gtk_container_add (GTK_CONTAINER (menuitem4_menu), ___propos1); + + toolbar2 = gtk_toolbar_new (); + gtk_widget_show (toolbar2); + gtk_box_pack_start (GTK_BOX (vbox35), toolbar2, FALSE, FALSE, 0); + gtk_toolbar_set_style (GTK_TOOLBAR (toolbar2), GTK_TOOLBAR_ICONS); + tmp_toolbar_icon_size = gtk_toolbar_get_icon_size (GTK_TOOLBAR (toolbar2)); + + toolbutton3 = (GtkWidget*) gtk_tool_button_new_from_stock ("gtk-execute"); + gtk_widget_show (toolbutton3); + gtk_container_add (GTK_CONTAINER (toolbar2), toolbutton3); + + toolbutton4 = (GtkWidget*) gtk_tool_button_new_from_stock ("gtk-stop"); + gtk_widget_show (toolbutton4); + gtk_container_add (GTK_CONTAINER (toolbar2), toolbutton4); + + separatortoolitem1 = (GtkWidget*) gtk_separator_tool_item_new (); + gtk_widget_show (separatortoolitem1); + gtk_container_add (GTK_CONTAINER (toolbar2), separatortoolitem1); + + tmp_image = gtk_image_new_from_stock ("gtk-yes", tmp_toolbar_icon_size); + gtk_widget_show (tmp_image); + toolbutton5 = (GtkWidget*) gtk_tool_button_new (tmp_image, _("Continue")); + gtk_widget_show (toolbutton5); + gtk_container_add (GTK_CONTAINER (toolbar2), toolbutton5); + + tmp_image = gtk_image_new_from_stock ("gtk-no", tmp_toolbar_icon_size); + gtk_widget_show (tmp_image); + toolbutton6 = (GtkWidget*) gtk_tool_button_new (tmp_image, _("Pause")); + gtk_widget_show (toolbutton6); + gtk_container_add (GTK_CONTAINER (toolbar2), toolbutton6); + + tmp_image = gtk_image_new_from_stock ("gtk-refresh", tmp_toolbar_icon_size); + gtk_widget_show (tmp_image); + toolbutton7 = (GtkWidget*) gtk_tool_button_new (tmp_image, _("Reset")); + gtk_widget_show (toolbutton7); + gtk_container_add (GTK_CONTAINER (toolbar2), toolbutton7); + + tmp_image = gtk_image_new_from_stock ("gtk-zoom-100", tmp_toolbar_icon_size); + gtk_widget_show (tmp_image); + toolbutton8 = (GtkWidget*) gtk_tool_button_new (tmp_image, _("Zoom")); + gtk_widget_show (toolbutton8); + gtk_container_add (GTK_CONTAINER (toolbar2), toolbutton8); + + separatortoolitem2 = (GtkWidget*) gtk_separator_tool_item_new (); + gtk_widget_show (separatortoolitem2); + gtk_container_add (GTK_CONTAINER (toolbar2), separatortoolitem2); + + toolbutton1 = (GtkWidget*) gtk_tool_button_new_from_stock ("gtk-help"); + gtk_widget_show (toolbutton1); + gtk_container_add (GTK_CONTAINER (toolbar2), toolbutton1); + + toolbutton2 = (GtkWidget*) gtk_tool_button_new_from_stock ("gtk-quit"); + gtk_widget_show (toolbutton2); + gtk_container_add (GTK_CONTAINER (toolbar2), toolbutton2); + + gtk_box_pack_start (GTK_BOX (vbox35), scroll_window35, TRUE, TRUE, 0); + + statusbar3 = gtk_statusbar_new (); + gtk_widget_show (statusbar3); + gtk_box_pack_start (GTK_BOX (vbox35), statusbar3, FALSE, FALSE, 0); + + g_signal_connect ((gpointer) nouveau1, "activate", + G_CALLBACK (on_nouveau1_activate), + NULL); + g_signal_connect ((gpointer) ouvrir1, "activate", + G_CALLBACK (on_ouvrir1_activate), + NULL); + g_signal_connect ((gpointer) enregistrer1, "activate", + G_CALLBACK (on_enregistrer1_activate), + NULL); + g_signal_connect ((gpointer) enregistrer_sous1, "activate", + G_CALLBACK (on_enregistrer_sous1_activate), + NULL); + g_signal_connect ((gpointer) fermer1, "activate", + G_CALLBACK (on_fermer1_activate), + NULL); + g_signal_connect ((gpointer) quitter1, "activate", + G_CALLBACK (on_quitter1_activate), + NULL); + g_signal_connect ((gpointer) couper1, "activate", + G_CALLBACK (on_couper1_activate), + NULL); + g_signal_connect ((gpointer) copier1, "activate", + G_CALLBACK (on_copier1_activate), + NULL); + g_signal_connect ((gpointer) coller1, "activate", + G_CALLBACK (on_coller1_activate), + NULL); + g_signal_connect ((gpointer) supprimer1, "activate", + G_CALLBACK (on_supprimer1_activate), + NULL); + g_signal_connect ((gpointer) preferences1, "activate", + G_CALLBACK (on_preferences1_activate), + NULL); + g_signal_connect ((gpointer) machine1, "activate", + G_CALLBACK (on_machine1_activate), + NULL); + g_signal_connect ((gpointer) run1, "activate", + G_CALLBACK (on_run1_activate), + NULL); + g_signal_connect ((gpointer) run_in_a_snapshot1, "activate", + G_CALLBACK (on_run_in_a_snapshot1_activate), + NULL); + g_signal_connect ((gpointer) pause2, "activate", + G_CALLBACK (on_pause2_activate), + NULL); + g_signal_connect ((gpointer) continue1, "activate", + G_CALLBACK (on_continue1_activate), + NULL); + g_signal_connect ((gpointer) reset1, "activate", + G_CALLBACK (on_reset1_activate), + NULL); + g_signal_connect ((gpointer) shutdown1, "activate", + G_CALLBACK (on_shutdown1_activate), + NULL); + g_signal_connect ((gpointer) zoom1, "activate", + G_CALLBACK (on_zoom1_activate), + NULL); + g_signal_connect ((gpointer) ___propos1, "activate", + G_CALLBACK (on____propos1_activate), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (winmain3, winmain3, "winmain3"); + GLADE_HOOKUP_OBJECT (winmain3, vbox35, "vbox35"); + GLADE_HOOKUP_OBJECT (winmain3, menubar1, "menubar1"); + GLADE_HOOKUP_OBJECT (winmain3, menuitem1, "menuitem1"); + GLADE_HOOKUP_OBJECT (winmain3, menuitem1_menu, "menuitem1_menu"); + GLADE_HOOKUP_OBJECT (winmain3, nouveau1, "nouveau1"); + GLADE_HOOKUP_OBJECT (winmain3, ouvrir1, "ouvrir1"); + GLADE_HOOKUP_OBJECT (winmain3, enregistrer1, "enregistrer1"); + GLADE_HOOKUP_OBJECT (winmain3, enregistrer_sous1, "enregistrer_sous1"); + GLADE_HOOKUP_OBJECT (winmain3, separatormenuitem1, "separatormenuitem1"); + GLADE_HOOKUP_OBJECT (winmain3, fermer1, "fermer1"); + GLADE_HOOKUP_OBJECT (winmain3, image99, "image99"); + GLADE_HOOKUP_OBJECT (winmain3, quitter1, "quitter1"); + GLADE_HOOKUP_OBJECT (winmain3, menuitem2, "menuitem2"); + GLADE_HOOKUP_OBJECT (winmain3, menuitem2_menu, "menuitem2_menu"); + GLADE_HOOKUP_OBJECT (winmain3, couper1, "couper1"); + GLADE_HOOKUP_OBJECT (winmain3, copier1, "copier1"); + GLADE_HOOKUP_OBJECT (winmain3, coller1, "coller1"); + GLADE_HOOKUP_OBJECT (winmain3, supprimer1, "supprimer1"); + GLADE_HOOKUP_OBJECT (winmain3, preferences1, "preferences1"); + GLADE_HOOKUP_OBJECT (winmain3, image100, "image100"); + GLADE_HOOKUP_OBJECT (winmain3, machine1, "machine1"); + GLADE_HOOKUP_OBJECT (winmain3, machine1_menu, "machine1_menu"); + GLADE_HOOKUP_OBJECT (winmain3, run1, "run1"); + GLADE_HOOKUP_OBJECT (winmain3, image101, "image101"); + GLADE_HOOKUP_OBJECT (winmain3, run_in_a_snapshot1, "run_in_a_snapshot1"); + GLADE_HOOKUP_OBJECT (winmain3, image102, "image102"); + GLADE_HOOKUP_OBJECT (winmain3, s__parateur1, "s__parateur1"); + GLADE_HOOKUP_OBJECT (winmain3, pause2, "pause2"); + GLADE_HOOKUP_OBJECT (winmain3, image103, "image103"); + GLADE_HOOKUP_OBJECT (winmain3, continue1, "continue1"); + GLADE_HOOKUP_OBJECT (winmain3, image104, "image104"); + GLADE_HOOKUP_OBJECT (winmain3, reset1, "reset1"); + GLADE_HOOKUP_OBJECT (winmain3, image105, "image105"); + GLADE_HOOKUP_OBJECT (winmain3, shutdown1, "shutdown1"); + GLADE_HOOKUP_OBJECT (winmain3, image106, "image106"); + GLADE_HOOKUP_OBJECT (winmain3, menuitem3, "menuitem3"); + GLADE_HOOKUP_OBJECT (winmain3, menuitem3_menu, "menuitem3_menu"); + GLADE_HOOKUP_OBJECT (winmain3, zoom1, "zoom1"); + GLADE_HOOKUP_OBJECT (winmain3, image107, "image107"); + GLADE_HOOKUP_OBJECT (winmain3, menuitem4, "menuitem4"); + GLADE_HOOKUP_OBJECT (winmain3, menuitem4_menu, "menuitem4_menu"); + GLADE_HOOKUP_OBJECT (winmain3, ___propos1, "___propos1"); + GLADE_HOOKUP_OBJECT (winmain3, toolbar2, "toolbar2"); + GLADE_HOOKUP_OBJECT (winmain3, toolbutton3, "toolbutton3"); + GLADE_HOOKUP_OBJECT (winmain3, toolbutton4, "toolbutton4"); + GLADE_HOOKUP_OBJECT (winmain3, separatortoolitem1, "separatortoolitem1"); + GLADE_HOOKUP_OBJECT (winmain3, toolbutton5, "toolbutton5"); + GLADE_HOOKUP_OBJECT (winmain3, toolbutton6, "toolbutton6"); + GLADE_HOOKUP_OBJECT (winmain3, toolbutton7, "toolbutton7"); + GLADE_HOOKUP_OBJECT (winmain3, toolbutton8, "toolbutton8"); + GLADE_HOOKUP_OBJECT (winmain3, separatortoolitem2, "separatortoolitem2"); + GLADE_HOOKUP_OBJECT (winmain3, toolbutton1, "toolbutton1"); + GLADE_HOOKUP_OBJECT (winmain3, toolbutton2, "toolbutton2"); + GLADE_HOOKUP_OBJECT (winmain3, statusbar3, "statusbar3"); + + gtk_window_add_accel_group (GTK_WINDOW (winmain3), accel_group); + + return winmain3; +} + diff --git a/qemu-gtk/interface.h b/qemu-gtk/interface.h new file mode 100644 index 0000000..7484575 --- /dev/null +++ b/qemu-gtk/interface.h @@ -0,0 +1,9 @@ +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +GtkWidget* create_filechoosernew (void); +GtkWidget* create_filechooseropen (void); +GtkWidget* create_filechoosersave (void); +GtkWidget* create_winprefs (void); +GtkWidget* create_winmain (GtkWidget *, GtkWidget *, GtkWidget *); diff --git a/qemu-gtk/null_fs.c b/qemu-gtk/null_fs.c new file mode 100644 index 0000000..9135088 --- /dev/null +++ b/qemu-gtk/null_fs.c @@ -0,0 +1,21 @@ +/* dummy functions - return successful but dont actually change the mode */ +/* use this with Win32 GTK until someone writes a win32_fs.c */ + +int fullscreen_init(void) +{ + return 1; +} + +int fullscreen_switch(int x, int y, int w, int h) +{ + return 1; +} + +int fullscreen_reset(void) +{ + return 1; +} + +void fullscreen_cleanup(void) +{ +} diff --git a/qemu-gtk/qemu-gtk-patch.diff b/qemu-gtk/qemu-gtk-patch.diff new file mode 100644 index 0000000..e30e9bb --- /dev/null +++ b/qemu-gtk/qemu-gtk-patch.diff @@ -0,0 +1,258 @@ +diff -u qemu.orig/Makefile.target qemu/Makefile.target +--- qemu.orig/Makefile.target Sat Jul 16 18:00:05 2005 ++++ qemu/Makefile.target Sat Jul 16 17:58:18 2005 +@@ -361,6 +361,13 @@ + ifdef CONFIG_SDL + VL_OBJS+=sdl.o + endif ++ifdef CONFIG_GTK ++VL_OBJS+=gtk2.o ++VL_OBJS+=callbacks.o ++VL_OBJS+=interface.o ++VL_OBJS+=support.o ++VL_OBJS+=fullscreen.o ++endif + ifdef CONFIG_COCOA + VL_OBJS+=cocoa.o + COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa +@@ -396,13 +403,28 @@ + endif + + $(QEMU_SYSTEM): $(VL_OBJS) libqemu.a +- $(CC) $(VL_LDFLAGS) -o $@ $^ $(LIBS) $(SDL_LIBS) $(COCOA_LIBS) $(VL_LIBS) ++ $(CC) $(VL_LDFLAGS) -o $@ $^ $(LIBS) $(SDL_LIBS) $(COCOA_LIBS) $(GTK_LIBS) $(FS_LIBS) $(VL_LIBS) + + cocoa.o: cocoa.m + $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< + + sdl.o: sdl.c keymaps.c sdl_keysym.h + $(CC) $(CFLAGS) $(DEFINES) $(SDL_CFLAGS) -c -o $@ $< ++ ++gtk2.o: gtk2gui.c keymaps.c gdk_keysym.h fullscreen.h interface.h callbacks.h support.h ++ $(CC) $(CFLAGS) $(DEFINES) $(GTK_CFLAGS) -c -o $@ $< ++ ++callbacks.o: callbacks.c callbacks.h interface.h support.h ++ $(CC) $(CFLAGS) $(DEFINES) $(GTK_CFLAGS) -c -o $@ $< ++ ++support.o: support.c callbacks.h interface.h support.h ++ $(CC) $(CFLAGS) $(DEFINES) $(GTK_CFLAGS) -c -o $@ $< ++ ++interface.o: interface.c callbacks.h interface.h support.h ++ $(CC) $(CFLAGS) $(DEFINES) $(GTK_CFLAGS) -c -o $@ $< ++ ++fullscreen.o: $(FSDRV) fullscreen.h ++ $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< + + sdlaudio.o: sdlaudio.c + $(CC) $(CFLAGS) $(DEFINES) $(SDL_CFLAGS) -c -o $@ $< +diff -u qemu.orig/configure qemu/configure +--- qemu.orig/configure Mon May 30 19:40:14 2005 ++++ qemu/configure Mon May 30 20:10:11 2005 +@@ -15,6 +15,7 @@ + TMPO="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.o" + TMPE="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}" + TMPS="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.S" ++TMPF="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}-conf" + + # default parameters + prefix="" +@@ -171,6 +172,10 @@ + ;; + --disable-sdl) sdl="no" + ;; ++ --enable-gtk) gtk="yes" ++ ;; ++ --set-fs-driver=*) fsdrv=`echo $opt | cut -d '=' -f 2` ++ ;; + --enable-fmod) fmod="yes" + ;; + --fmod-lib=*) fmod_lib=${opt#--fmod-lib=} +@@ -311,6 +316,64 @@ + fi # cross compilation + fi # -z $sdl + ++########################################## ++# GTK probe ++ ++gtk_too_old=no ++ ++if test -z "$gtk" ; then ++ ++gtk=no ++ ++# normal GTK probe ++cat > $TMPC << EOF ++#include ++#include ++int main(int argc, char **argv) { gtk_init(&argc, &argv); return EXIT_SUCCESS; } ++EOF ++ ++if $cc -o $TMPE `pkg-config --cflags --libs gtk+-2.0 2> /dev/null` $TMPC 2> /dev/null ; then ++_gtkversion=`pkg-config --modversion gtk+-2.0 | sed 's/[^0-9]//g'` ++if test "$_sdlversion" -lt 240 ; then ++ gtk_too_old=yes ++else ++ gtk=yes ++ ++fi ++ ++fi # gtk compile test ++ ++fi # -z $gtk ++ ++if [ "$gtk" = "yes" ]; then ++ ++if [ "$fsdrv" = "" ] ; then ++ ++if [ "$bsd" = "yes" -o "$linux" = "yes" ]; then ++ fsdrv=xvid_fs.c ++else ++ fsdrv=null_fs.c ++fi ++ ++fi # fsdrv test ++ ++if [ "$fsdrv" = "xvid_fs.c" -o "$fsdrv" = "null_fs.c" ]; then ++ echo "fsdrv=$fsdrv" >> $TMPF ++else ++ echo "Warning: unknown gtk fullscreen driver: $fsdrv - using null driver" ++ echo 'fsdrv=null_fs.c' >> $TMPF ++ fsdrv=null_fs.c ++fi ++ ++if [ "$fsdrv" = "xvid_fs.c" ]; then ++ FS_LIBS="-lX11 -lXxf86vm -lXext" ++ [ "$cpu" = "x86_64" ] && lib=lib64 ++ FS_LIBS="$FS_LIBS -L/usr/X11R6/${lib:-lib}" ++ echo FS_LIBS=\"$FS_LIBS\" >> $TMPF ++fi ++ ++fi # gtk=yes test ++ + if test x"$1" = x"-h" -o x"$1" = x"--help" ; then + cat << EOF + +@@ -434,6 +495,8 @@ + if test "$sdl" != "no" ; then + echo "SDL static link $sdl_static" + fi ++echo "GTK support $gtk" ++echo "GTK FS driver $fsdrv" + echo "mingw32 support $mingw32" + echo "Adlib support $adlib" + echo -n "FMOD support $fmod" +@@ -643,6 +706,8 @@ + interp_prefix1=`echo "$interp_prefix" | sed "s/%M/$target_cpu/g"` + echo "#define CONFIG_QEMU_PREFIX \"$interp_prefix1\"" >> $config_h + ++. $TMPF ++ + if test "$target_cpu" = "i386" ; then + echo "TARGET_ARCH=i386" >> $config_mak + echo "#define TARGET_ARCH \"i386\"" >> $config_h +@@ -720,6 +785,17 @@ + fi + fi + ++if test "$gtk" = "yes" ; then ++ . $TMPF ++ echo "#define CONFIG_GTK 1" >> $config_h ++ echo "CONFIG_GTK=yes" >> $config_mak ++ echo "GTK_LIBS=`pkg-config --libs gtk+-2.0`" >> $config_mak ++ echo "GTK_CFLAGS=`pkg-config --cflags gtk+-2.0`" >> $config_mak ++ echo "FSDRV=$fsdrv" >> $config_mak ++ echo "FS_LIBS=$FS_LIBS" >> $config_mak ++ echo "" >> $config_mak ++fi ++ + if test "$cocoa" = "yes" ; then + echo "#define CONFIG_COCOA 1" >> $config_h + echo "CONFIG_COCOA=yes" >> $config_mak +@@ -739,4 +815,4 @@ + done + fi + +-rm -f $TMPO $TMPC $TMPE $TMPS ++rm -f $TMPO $TMPC $TMPE $TMPS $TMPF +diff -u qemu.orig/vl.h qemu/vl.h +--- qemu.orig/vl.h Mon May 30 19:40:14 2005 ++++ qemu/vl.h Sat May 28 16:16:18 2005 +@@ -579,6 +579,9 @@ + /* sdl.c */ + void sdl_display_init(DisplayState *ds, int full_screen); + ++/* gtk2.c */ ++void gtk2_display_init(DisplayState *ds, int full_screen); ++ + /* cocoa.m */ + void cocoa_display_init(DisplayState *ds, int full_screen); + +diff -u qemu.orig/vl.c qemu/vl.c +--- qemu.orig/vl.c Tue May 31 14:53:22 2005 ++++ qemu/vl.c Tue May 31 14:52:55 2005 +@@ -147,10 +147,13 @@ + TextConsole *vga_console; + CharDriverState *serial_hds[MAX_SERIAL_PORTS]; + CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; ++int use_gtk = 0; + #ifdef TARGET_I386 + int win2k_install_hack = 0; + #endif + ++void gui_checkargs(int *, char ***); ++ + /***********************************************************/ + /* x86 ISA bus support */ + +@@ -2877,6 +2878,7 @@ + QEMU_OPTION_cirrusvga, + QEMU_OPTION_g, + QEMU_OPTION_std_vga, ++ QEMU_OPTION_use_gtk, + QEMU_OPTION_monitor, + QEMU_OPTION_serial, + QEMU_OPTION_parallel, +@@ -2947,6 +2949,7 @@ + { "localtime", 0, QEMU_OPTION_localtime }, + { "isa", 0, QEMU_OPTION_isa }, + { "std-vga", 0, QEMU_OPTION_std_vga }, ++ { "use-gtk", 0, QEMU_OPTION_use_gtk }, + { "monitor", 1, QEMU_OPTION_monitor }, + { "serial", 1, QEMU_OPTION_serial }, + { "parallel", 1, QEMU_OPTION_parallel }, +@@ -3345,6 +3348,9 @@ + case QEMU_OPTION_std_vga: + cirrus_vga_enabled = 0; + break; ++ case QEMU_OPTION_use_gtk: ++ use_gtk = 1; ++ break; + case QEMU_OPTION_g: + { + const char *p; +@@ -3090,6 +3092,7 @@ + const char *loadvm = NULL; + QEMUMachine *machine; + ++ gui_checkargs(&argc, &argv); + #if !defined(CONFIG_SOFTMMU) + /* we never want that malloc() uses mmap() */ + mallopt(M_MMAP_THRESHOLD, 4096 * 1024); +@@ -3599,7 +3605,17 @@ + if (nographic) { + dumb_display_init(ds); + } else { ++#if defined(CONFIG_GTK) + #if defined(CONFIG_SDL) ++ /* so we can choose */ ++ if (use_gtk) ++ gtk2_display_init(ds, full_screen); ++ else ++ sdl_display_init(ds, full_screen); ++#else ++ gtk2_display_init(ds, full_screen); ++#endif ++#elif defined(CONFIG_SDL) + sdl_display_init(ds, full_screen); + #elif defined(CONFIG_COCOA) + cocoa_display_init(ds, full_screen); diff --git a/qemu-gtk/support.c b/qemu-gtk/support.c new file mode 100644 index 0000000..00aff29 --- /dev/null +++ b/qemu-gtk/support.c @@ -0,0 +1,144 @@ +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include + +#include + +#include "support.h" + +GtkWidget* +lookup_widget (GtkWidget *widget, + const gchar *widget_name) +{ + GtkWidget *parent, *found_widget; + + for (;;) + { + if (GTK_IS_MENU (widget)) + parent = gtk_menu_get_attach_widget (GTK_MENU (widget)); + else + parent = widget->parent; + if (!parent) + parent = (GtkWidget*) g_object_get_data (G_OBJECT (widget), "GladeParentKey"); + if (parent == NULL) + break; + widget = parent; + } + + found_widget = (GtkWidget*) g_object_get_data (G_OBJECT (widget), + widget_name); + if (!found_widget) + g_warning ("Widget not found: %s", widget_name); + return found_widget; +} + +static GList *pixmaps_directories = NULL; + +/* Use this function to set the directory containing installed pixmaps. */ +void +add_pixmap_directory (const gchar *directory) +{ + pixmaps_directories = g_list_prepend (pixmaps_directories, + g_strdup (directory)); +} + +/* This is an internally used function to find pixmap files. */ +static gchar* +find_pixmap_file (const gchar *filename) +{ + GList *elem; + + /* We step through each of the pixmaps directory to find it. */ + elem = pixmaps_directories; + while (elem) + { + gchar *pathname = g_strdup_printf ("%s%s%s", (gchar*)elem->data, + G_DIR_SEPARATOR_S, filename); + if (g_file_test (pathname, G_FILE_TEST_EXISTS)) + return pathname; + g_free (pathname); + elem = elem->next; + } + return NULL; +} + +/* This is an internally used function to create pixmaps. */ +GtkWidget* +create_pixmap (GtkWidget *widget, + const gchar *filename) +{ + gchar *pathname = NULL; + GtkWidget *pixmap; + + if (!filename || !filename[0]) + return gtk_image_new (); + + pathname = find_pixmap_file (filename); + + if (!pathname) + { + g_warning (_("Couldn't find pixmap file: %s"), filename); + return gtk_image_new (); + } + + pixmap = gtk_image_new_from_file (pathname); + g_free (pathname); + return pixmap; +} + +/* This is an internally used function to create pixmaps. */ +GdkPixbuf* +create_pixbuf (const gchar *filename) +{ + gchar *pathname = NULL; + GdkPixbuf *pixbuf; + GError *error = NULL; + + if (!filename || !filename[0]) + return NULL; + + pathname = find_pixmap_file (filename); + + if (!pathname) + { + g_warning (_("Couldn't find pixmap file: %s"), filename); + return NULL; + } + + pixbuf = gdk_pixbuf_new_from_file (pathname, &error); + if (!pixbuf) + { + fprintf (stderr, "Failed to load pixbuf file: %s: %s\n", + pathname, error->message); + g_error_free (error); + } + g_free (pathname); + return pixbuf; +} + +/* This is used to set ATK action descriptions. */ +void +glade_set_atk_action_description (AtkAction *action, + const gchar *action_name, + const gchar *description) +{ + gint n_actions, i; + + n_actions = atk_action_get_n_actions (action); + for (i = 0; i < n_actions; i++) + { + if (!strcmp (atk_action_get_name (action, i), action_name)) + atk_action_set_description (action, i, description); + } +} + diff --git a/qemu-gtk/support.h b/qemu-gtk/support.h new file mode 100644 index 0000000..a32649e --- /dev/null +++ b/qemu-gtk/support.h @@ -0,0 +1,69 @@ +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +/* + * Standard gettext macros. + */ +#ifdef ENABLE_NLS +# include +# undef _ +# define _(String) dgettext (PACKAGE, String) +# define Q_(String) g_strip_context ((String), gettext (String)) +# ifdef gettext_noop +# define N_(String) gettext_noop (String) +# else +# define N_(String) (String) +# endif +#else +# define textdomain(String) (String) +# define gettext(String) (String) +# define dgettext(Domain,Message) (Message) +# define dcgettext(Domain,Message,Type) (Message) +# define bindtextdomain(Domain,Directory) (Domain) +# define _(String) (String) +# define Q_(String) g_strip_context ((String), (String)) +# define N_(String) (String) +#endif + + +/* + * Public Functions. + */ + +/* + * This function returns a widget in a component created by Glade. + * Call it with the toplevel widget in the component (i.e. a window/dialog), + * or alternatively any widget in the component, and the name of the widget + * you want returned. + */ +GtkWidget* lookup_widget (GtkWidget *widget, + const gchar *widget_name); + + +/* Use this function to set the directory containing installed pixmaps. */ +void add_pixmap_directory (const gchar *directory); + + +/* + * Private Functions. + */ + +/* This is used to create the pixmaps used in the interface. */ +GtkWidget* create_pixmap (GtkWidget *widget, + const gchar *filename); + +/* This is used to create the pixbufs used in the interface. */ +GdkPixbuf* create_pixbuf (const gchar *filename); + +/* This is used to set ATK action descriptions. */ +void glade_set_atk_action_description (AtkAction *action, + const gchar *action_name, + const gchar *description); + diff --git a/qemu-gtk/xvid_fs.c b/qemu-gtk/xvid_fs.c new file mode 100644 index 0000000..5f94bc0 --- /dev/null +++ b/qemu-gtk/xvid_fs.c @@ -0,0 +1,99 @@ +/* Use X11 Xvid extension to implement fullscreen mode */ + +#include +#include +#include +#include +#include +#include + +static Display *dpy = NULL; +static XF86VidModeModeInfo **mode_list = NULL; +static XF86VidModeModeInfo * modeinfo = NULL; +static int count = 0; + +int fullscreen_init(void) +{ + int event, error, dotclock; + XF86VidModeModeLine * modeline; + + /* load up X display and make sure we have the Xvid extention */ + dpy = XOpenDisplay(""); /* FIXME: make this the one that Gtk uses */ + if (dpy == NULL) + return 0; + if (!XF86VidModeQueryExtension(dpy, &event, &error)) + return 0; + + /* get list of all modes */ + XF86VidModeGetAllModeLines(dpy, XDefaultScreen(dpy), &count, &mode_list); + + /* get current modeline */ + modeline = (XF86VidModeModeLine *)malloc(sizeof(XF86VidModeModeLine)); + XF86VidModeGetModeLine(dpy, XDefaultScreen(dpy), &dotclock, modeline); + + /* convert to ModeInfo structure */ + modeinfo = (XF86VidModeModeInfo *)malloc(sizeof(XF86VidModeModeInfo)); + modeinfo->dotclock = dotclock; + modeinfo->hdisplay = modeline->hdisplay; + modeinfo->hsyncstart = modeline->hsyncstart; + modeinfo->hsyncend = modeline->hsyncend; + modeinfo->htotal = modeline->htotal; + modeinfo->vdisplay = modeline->vdisplay; + modeinfo->vsyncstart = modeline->vsyncstart; + modeinfo->vsyncend = modeline->vsyncend; + modeinfo->vtotal = modeline->vtotal; + modeinfo->flags = modeline->flags; + modeinfo->privsize = modeline->privsize; + modeinfo->private = modeline->private; + free(modeline); + + return 1; +} + +int fullscreen_switch(int x, int y, int w, int h) +{ + int i; + for (i = 0; i < count; i++) + { + if (w == mode_list[i]->hdisplay) + if (h == mode_list[i]->vdisplay) + { + XF86VidModeSwitchToMode(dpy, XDefaultScreen(dpy), mode_list[i]); + XF86VidModeSetViewPort(dpy, XDefaultScreen(dpy), 0, 0); + XFlush(dpy); + return 1; + } + } + return 0; +} + +int fullscreen_reset(void) +{ + XF86VidModeSwitchToMode(dpy, XDefaultScreen(dpy), modeinfo); + XFlush(dpy); + return 1; +} + +void fullscreen_cleanup(void) +{ + int i; + if (modeinfo) + { + if (modeinfo->privsize != 0) + free(modeinfo->private); + free(modeinfo); + } + if (mode_list) + { + for (i = 0; i < count; i++) + { + if (mode_list[i]) + { + if (mode_list[i]->privsize != 0) + free(mode_list[i]->private); + //free(mode_list[i]); + } + } + free(mode_list); + } +} diff --git a/qemu.spec b/qemu.spec index 8703995..e95889d 100644 --- a/qemu.spec +++ b/qemu.spec @@ -1,8 +1,9 @@ %define kmodule_name kqemu -%define kqemuver 0.6.2 +%define kqemuver 0.7.1.1 +%define gtkver 20050716 Name: qemu -Version: 0.7.0 +Version: 0.7.1 Release: alt1 Summary: QEMU CPU Emulator @@ -12,10 +13,13 @@ Group: Emulators URL: http://fabrice.bellard.free.fr/qemu/ Source: %name-%version.tar.bz2 Source1: kqemu-%kqemuver.tar.bz2 +Source2: kqemu-permission +Source3: qemu-gtk-%gtkver.tar.bz2 -Patch: %name-0.7.0-alt-makefile.patch +Patch: %name-0.7.1-alt-makefile.patch Patch1: %name-0.6.2-alt-hdtrans.patch Patch2: %name-0.7.0-alt-kqemu.patch +Patch3: %name-0.7.1-alt-guiargs.patch # for %_bindir/qemu* %set_verify_elf_method textrel=relaxed @@ -23,7 +27,7 @@ Patch2: %name-0.7.0-alt-kqemu.patch BuildRequires: libSDL-devel-static xorg-x11-devel-static BuildRequires: glibc-devel-static zlib-devel-static BuildRequires: libaudiofile-devel-static esound-devel-static libalsa-devel-static -BuildRequires: tetex-core +BuildRequires: tetex-core libgtk+2-devel %description QEMU is a fast processor emulator using dynamic translation to achieve @@ -51,23 +55,28 @@ Group: Development/Kernel %kmodule_name modules sources for Linux kernel %prep -%setup -q -a1 +%setup -q -a1 -a3 %patch -p1 %patch1 -p1 %patch2 -p0 +%__patch -p1 < qemu-gtk-patch.diff +%patch3 -p1 %__cp -a kqemu kernel-source-%kmodule_name-%kqemuver +%__cp -a %SOURCE2 kernel-source-%kmodule_name-%kqemuver/PERMISSION %__tar -cjf %_builddir/%name-%version/kernel-source-%kmodule_name-%kqemuver.tar.bz2 kernel-source-%kmodule_name-%kqemuver %build -./configure --prefix=%_prefix --enable-adlib --with-kqemu +./configure --prefix=%_prefix --enable-adlib --with-kqemu --enable-gtk # XXX asm error with CFLAGS='%optflags' # XXX segfault with LDFLAGS= %make_build +%ifarch %ix86 cd tests %__subst -p s/-static//g Makefile %make +%endif %install %make_install install DESTDIR=%buildroot @@ -78,14 +87,24 @@ cd tests %files %doc Changelog README TODO qemu-doc.html qemu-tech.html %_bindir/qemu* +%_datadir/qemu %_man1dir/qemu* -%dir %_datadir/qemu -%_datadir/qemu/*.bin %files -n kernel-source-%kmodule_name-%kqemuver %_usrsrc/kernel/sources/* %changelog +* Thu Aug 04 2005 Kachalov Anton 0.7.1-alt1 +- 0.7.1 +- Updated: + * Kqemu to 0.7.1-1 + * GTK support + +* Thu Jun 23 2005 Kachalov Anton 0.7.0-alt2 +- Added: + * GTK support (-use-gtk option) + * Distribution permission from Fabrice Bellard to Kqemu + * Fri Apr 29 2005 Kachalov Anton 0.7.0-alt1 - 0.7.0 - Kqemu support diff --git a/qemu/.#block.c.1.18 b/qemu/.#block.c.1.18 deleted file mode 100644 index dff1365..0000000 --- a/qemu/.#block.c.1.18 +++ /dev/null @@ -1,655 +0,0 @@ -/* - * QEMU System Emulator block driver - * - * Copyright (c) 2003 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "vl.h" -#include "block_int.h" - -#ifdef _BSD -#include -#include -#include -#include -#include -#endif - -static BlockDriverState *bdrv_first; -static BlockDriver *first_drv; - -void bdrv_register(BlockDriver *bdrv) -{ - bdrv->next = first_drv; - first_drv = bdrv; -} - -/* create a new block device (by default it is empty) */ -BlockDriverState *bdrv_new(const char *device_name) -{ - BlockDriverState **pbs, *bs; - - bs = qemu_mallocz(sizeof(BlockDriverState)); - if(!bs) - return NULL; - pstrcpy(bs->device_name, sizeof(bs->device_name), device_name); - if (device_name[0] != '\0') { - /* insert at the end */ - pbs = &bdrv_first; - while (*pbs != NULL) - pbs = &(*pbs)->next; - *pbs = bs; - } - return bs; -} - -BlockDriver *bdrv_find_format(const char *format_name) -{ - BlockDriver *drv1; - for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) { - if (!strcmp(drv1->format_name, format_name)) - return drv1; - } - return NULL; -} - -int bdrv_create(BlockDriver *drv, - const char *filename, int64_t size_in_sectors, - const char *backing_file, int flags) -{ - if (!drv->bdrv_create) - return -ENOTSUP; - return drv->bdrv_create(filename, size_in_sectors, backing_file, flags); -} - -#ifdef _WIN32 -static void get_tmp_filename(char *filename, int size) -{ - /* XXX: find a better function */ - tmpnam(filename); -} -#else -static void get_tmp_filename(char *filename, int size) -{ - int fd; - /* XXX: race condition possible */ - pstrcpy(filename, size, "/tmp/vl.XXXXXX"); - fd = mkstemp(filename); - close(fd); -} -#endif - -/* XXX: force raw format if block or character device ? It would - simplify the BSD case */ -static BlockDriver *find_image_format(const char *filename) -{ - int fd, ret, score, score_max; - BlockDriver *drv1, *drv; - uint8_t *buf; - size_t bufsize = 1024; - - fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); - if (fd < 0) - return NULL; -#ifdef DIOCGSECTORSIZE - { - unsigned int sectorsize = 512; - if (!ioctl(fd, DIOCGSECTORSIZE, §orsize) && - sectorsize > bufsize) - bufsize = sectorsize; - } -#endif - buf = malloc(bufsize); - if (!buf) - return NULL; - ret = read(fd, buf, bufsize); - if (ret < 0) { - close(fd); - free(buf); - return NULL; - } - close(fd); - - drv = NULL; - score_max = 0; - for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) { - score = drv1->bdrv_probe(buf, ret, filename); - if (score > score_max) { - score_max = score; - drv = drv1; - } - } - free(buf); - return drv; -} - -int bdrv_open(BlockDriverState *bs, const char *filename, int snapshot) -{ - return bdrv_open2(bs, filename, snapshot, NULL); -} - -int bdrv_open2(BlockDriverState *bs, const char *filename, int snapshot, - BlockDriver *drv) -{ - int ret; - char tmp_filename[1024]; - - bs->read_only = 0; - bs->is_temporary = 0; - bs->encrypted = 0; - - if (snapshot) { - BlockDriverState *bs1; - int64_t total_size; - - /* if snapshot, we create a temporary backing file and open it - instead of opening 'filename' directly */ - - /* if there is a backing file, use it */ - bs1 = bdrv_new(""); - if (!bs1) { - return -1; - } - if (bdrv_open(bs1, filename, 0) < 0) { - bdrv_delete(bs1); - return -1; - } - total_size = bs1->total_sectors; - bdrv_delete(bs1); - - get_tmp_filename(tmp_filename, sizeof(tmp_filename)); - /* XXX: use cow for linux as it is more efficient ? */ - if (bdrv_create(&bdrv_qcow, tmp_filename, - total_size, filename, 0) < 0) { - return -1; - } - filename = tmp_filename; - bs->is_temporary = 1; - } - - pstrcpy(bs->filename, sizeof(bs->filename), filename); - if (!drv) { - drv = find_image_format(filename); - if (!drv) - return -1; - } - bs->drv = drv; - bs->opaque = qemu_mallocz(drv->instance_size); - if (bs->opaque == NULL && drv->instance_size > 0) - return -1; - - ret = drv->bdrv_open(bs, filename); - if (ret < 0) { - qemu_free(bs->opaque); - return -1; - } -#ifndef _WIN32 - if (bs->is_temporary) { - unlink(filename); - } -#endif - if (bs->backing_file[0] != '\0' && drv->bdrv_is_allocated) { - /* if there is a backing file, use it */ - bs->backing_hd = bdrv_new(""); - if (!bs->backing_hd) { - fail: - bdrv_close(bs); - return -1; - } - if (bdrv_open(bs->backing_hd, bs->backing_file, 0) < 0) - goto fail; - } - - bs->inserted = 1; - - /* call the change callback */ - if (bs->change_cb) - bs->change_cb(bs->change_opaque); - - return 0; -} - -void bdrv_close(BlockDriverState *bs) -{ - if (bs->inserted) { - if (bs->backing_hd) - bdrv_delete(bs->backing_hd); - bs->drv->bdrv_close(bs); - qemu_free(bs->opaque); -#ifdef _WIN32 - if (bs->is_temporary) { - unlink(bs->filename); - } -#endif - bs->opaque = NULL; - bs->drv = NULL; - bs->inserted = 0; - - /* call the change callback */ - if (bs->change_cb) - bs->change_cb(bs->change_opaque); - } -} - -void bdrv_delete(BlockDriverState *bs) -{ - /* XXX: remove the driver list */ - bdrv_close(bs); - qemu_free(bs); -} - -/* commit COW file into the raw image */ -int bdrv_commit(BlockDriverState *bs) -{ - int64_t i; - int n, j; - unsigned char sector[512]; - - if (!bs->inserted) - return -ENOENT; - - if (bs->read_only) { - return -EACCES; - } - - if (!bs->backing_hd) { - return -ENOTSUP; - } - - for (i = 0; i < bs->total_sectors;) { - if (bs->drv->bdrv_is_allocated(bs, i, 65536, &n)) { - for(j = 0; j < n; j++) { - if (bdrv_read(bs, i, sector, 1) != 0) { - return -EIO; - } - - if (bdrv_write(bs->backing_hd, i, sector, 1) != 0) { - return -EIO; - } - i++; - } - } else { - i += n; - } - } - return 0; -} - -/* return -1 if error */ -int bdrv_read(BlockDriverState *bs, int64_t sector_num, - uint8_t *buf, int nb_sectors) -{ - int ret, n; - BlockDriver *drv = bs->drv; - - if (!bs->inserted) - return -1; - - while (nb_sectors > 0) { - if (sector_num == 0 && bs->boot_sector_enabled) { - memcpy(buf, bs->boot_sector_data, 512); - n = 1; - } else if (bs->backing_hd) { - if (drv->bdrv_is_allocated(bs, sector_num, nb_sectors, &n)) { - ret = drv->bdrv_read(bs, sector_num, buf, n); - if (ret < 0) - return -1; - } else { - /* read from the base image */ - ret = bdrv_read(bs->backing_hd, sector_num, buf, n); - if (ret < 0) - return -1; - } - } else { - ret = drv->bdrv_read(bs, sector_num, buf, nb_sectors); - if (ret < 0) - return -1; - /* no need to loop */ - break; - } - nb_sectors -= n; - sector_num += n; - buf += n * 512; - } - return 0; -} - -/* return -1 if error */ -int bdrv_write(BlockDriverState *bs, int64_t sector_num, - const uint8_t *buf, int nb_sectors) -{ - if (!bs->inserted) - return -1; - if (bs->read_only) - return -1; - return bs->drv->bdrv_write(bs, sector_num, buf, nb_sectors); -} - -void bdrv_get_geometry(BlockDriverState *bs, int64_t *nb_sectors_ptr) -{ - *nb_sectors_ptr = bs->total_sectors; -} - -/* force a given boot sector. */ -void bdrv_set_boot_sector(BlockDriverState *bs, const uint8_t *data, int size) -{ - bs->boot_sector_enabled = 1; - if (size > 512) - size = 512; - memcpy(bs->boot_sector_data, data, size); - memset(bs->boot_sector_data + size, 0, 512 - size); -} - -void bdrv_set_geometry_hint(BlockDriverState *bs, - int cyls, int heads, int secs) -{ - bs->cyls = cyls; - bs->heads = heads; - bs->secs = secs; -} - -void bdrv_set_type_hint(BlockDriverState *bs, int type) -{ - bs->type = type; - bs->removable = ((type == BDRV_TYPE_CDROM || - type == BDRV_TYPE_FLOPPY)); -} - -void bdrv_set_translation_hint(BlockDriverState *bs, int translation) -{ - bs->translation = translation; -} - -void bdrv_get_geometry_hint(BlockDriverState *bs, - int *pcyls, int *pheads, int *psecs) -{ - *pcyls = bs->cyls; - *pheads = bs->heads; - *psecs = bs->secs; -} - -int bdrv_get_type_hint(BlockDriverState *bs) -{ - return bs->type; -} - -int bdrv_get_translation_hint(BlockDriverState *bs) -{ - return bs->translation; -} - -int bdrv_is_removable(BlockDriverState *bs) -{ - return bs->removable; -} - -int bdrv_is_read_only(BlockDriverState *bs) -{ - return bs->read_only; -} - -int bdrv_is_inserted(BlockDriverState *bs) -{ - return bs->inserted; -} - -int bdrv_is_locked(BlockDriverState *bs) -{ - return bs->locked; -} - -void bdrv_set_locked(BlockDriverState *bs, int locked) -{ - bs->locked = locked; -} - -void bdrv_set_change_cb(BlockDriverState *bs, - void (*change_cb)(void *opaque), void *opaque) -{ - bs->change_cb = change_cb; - bs->change_opaque = opaque; -} - -int bdrv_is_encrypted(BlockDriverState *bs) -{ - if (bs->backing_hd && bs->backing_hd->encrypted) - return 1; - return bs->encrypted; -} - -int bdrv_set_key(BlockDriverState *bs, const char *key) -{ - int ret; - if (bs->backing_hd && bs->backing_hd->encrypted) { - ret = bdrv_set_key(bs->backing_hd, key); - if (ret < 0) - return ret; - if (!bs->encrypted) - return 0; - } - if (!bs->encrypted || !bs->drv || !bs->drv->bdrv_set_key) - return -1; - return bs->drv->bdrv_set_key(bs, key); -} - -void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size) -{ - if (!bs->inserted || !bs->drv) { - buf[0] = '\0'; - } else { - pstrcpy(buf, buf_size, bs->drv->format_name); - } -} - -void bdrv_iterate_format(void (*it)(void *opaque, const char *name), - void *opaque) -{ - BlockDriver *drv; - - for (drv = first_drv; drv != NULL; drv = drv->next) { - it(opaque, drv->format_name); - } -} - -BlockDriverState *bdrv_find(const char *name) -{ - BlockDriverState *bs; - - for (bs = bdrv_first; bs != NULL; bs = bs->next) { - if (!strcmp(name, bs->device_name)) - return bs; - } - return NULL; -} - -void bdrv_iterate(void (*it)(void *opaque, const char *name), void *opaque) -{ - BlockDriverState *bs; - - for (bs = bdrv_first; bs != NULL; bs = bs->next) { - it(opaque, bs->device_name); - } -} - -const char *bdrv_get_device_name(BlockDriverState *bs) -{ - return bs->device_name; -} - -void bdrv_info(void) -{ - BlockDriverState *bs; - - for (bs = bdrv_first; bs != NULL; bs = bs->next) { - term_printf("%s:", bs->device_name); - term_printf(" type="); - switch(bs->type) { - case BDRV_TYPE_HD: - term_printf("hd"); - break; - case BDRV_TYPE_CDROM: - term_printf("cdrom"); - break; - case BDRV_TYPE_FLOPPY: - term_printf("floppy"); - break; - } - term_printf(" removable=%d", bs->removable); - if (bs->removable) { - term_printf(" locked=%d", bs->locked); - } - if (bs->inserted) { - term_printf(" file=%s", bs->filename); - if (bs->backing_file[0] != '\0') - term_printf(" backing_file=%s", bs->backing_file); - term_printf(" ro=%d", bs->read_only); - term_printf(" drv=%s", bs->drv->format_name); - if (bs->encrypted) - term_printf(" encrypted"); - } else { - term_printf(" [not inserted]"); - } - term_printf("\n"); - } -} - - -/**************************************************************/ -/* RAW block driver */ - -typedef struct BDRVRawState { - int fd; -} BDRVRawState; - -static int raw_probe(const uint8_t *buf, int buf_size, const char *filename) -{ - return 1; /* maybe */ -} - -static int raw_open(BlockDriverState *bs, const char *filename) -{ - BDRVRawState *s = bs->opaque; - int fd; - int64_t size; - - fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); - if (fd < 0) { - fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); - if (fd < 0) - return -1; - bs->read_only = 1; - } -#ifdef _BSD - { - struct stat sb; - if (!fstat(fd, &sb) && (S_IFCHR & sb.st_mode)) { -#ifdef DIOCGMEDIASIZE - if (ioctl(fd, DIOCGMEDIASIZE, (off_t *)&size)) -#endif - size = lseek(fd, 0LL, SEEK_END); - } else -#endif - { - size = lseek(fd, 0, SEEK_END); - } -#ifdef _WIN32 - /* On Windows hosts it can happen that we're unable to get file size - for CD-ROM raw device (it's inherent limitation of the CDFS driver). */ - if (size == -1) - size = LONG_LONG_MAX; -#endif - bs->total_sectors = size / 512; - s->fd = fd; - return 0; -} - -static int raw_read(BlockDriverState *bs, int64_t sector_num, - uint8_t *buf, int nb_sectors) -{ - BDRVRawState *s = bs->opaque; - int ret; - - lseek(s->fd, sector_num * 512, SEEK_SET); - ret = read(s->fd, buf, nb_sectors * 512); - if (ret != nb_sectors * 512) - return -1; - return 0; -} - -static int raw_write(BlockDriverState *bs, int64_t sector_num, - const uint8_t *buf, int nb_sectors) -{ - BDRVRawState *s = bs->opaque; - int ret; - - lseek(s->fd, sector_num * 512, SEEK_SET); - ret = write(s->fd, buf, nb_sectors * 512); - if (ret != nb_sectors * 512) - return -1; - return 0; -} - -static void raw_close(BlockDriverState *bs) -{ - BDRVRawState *s = bs->opaque; - close(s->fd); -} - -static int raw_create(const char *filename, int64_t total_size, - const char *backing_file, int flags) -{ - int fd; - - if (flags || backing_file) - return -ENOTSUP; - - fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, - 0644); - if (fd < 0) - return -EIO; - ftruncate(fd, total_size * 512); - close(fd); - return 0; -} - -BlockDriver bdrv_raw = { - "raw", - sizeof(BDRVRawState), - raw_probe, - raw_open, - raw_read, - raw_write, - raw_close, - raw_create, -}; - -void bdrv_init(void) -{ - bdrv_register(&bdrv_raw); -#ifndef _WIN32 - bdrv_register(&bdrv_cow); -#endif - bdrv_register(&bdrv_qcow); - bdrv_register(&bdrv_vmdk); - bdrv_register(&bdrv_cloop); - bdrv_register(&bdrv_dmg); - bdrv_register(&bdrv_bochs); -} diff --git a/qemu/.cvsignore b/qemu/.cvsignore index 50debc4..6d6858b 100644 --- a/qemu/.cvsignore +++ b/qemu/.cvsignore @@ -6,6 +6,7 @@ i386 i386-softmmu i386-user ppc-softmmu +ppc64-softmmu ppc-user qemu-doc.html qemu-tech.html @@ -19,3 +20,4 @@ sparc-softmmu x86_64-softmmu sparc64-user sparc64-softmmu +mips-softmmu diff --git a/qemu/Changelog b/qemu/Changelog index c257d61..dcd252c 100644 --- a/qemu/Changelog +++ b/qemu/Changelog @@ -1,3 +1,14 @@ +version 0.7.1: + + - read-only Virtual FAT support (Johannes Schindelin) + - Windows 2000 install disk full hack (original idea from Vladimir + N. Oleynik) + - VMDK disk image creation (Filip Navara) + - SPARC64 progress (Blue Swirl) + - initial MIPS support (Jocelyn mayer) + - MIPS improvements (Ralf Baechle) + - 64 bit fixes in user networking (initial patch by Gwenole Beauchesne) + version 0.7.0: - better BIOS translation and HDD geometry auto-detection diff --git a/qemu/Makefile b/qemu/Makefile index 2a4f329..8736090 100644 --- a/qemu/Makefile +++ b/qemu/Makefile @@ -25,7 +25,7 @@ else endif endif -qemu-img$(EXESUF): qemu-img.c block.c block-cow.c block-qcow.c aes.c block-vmdk.c block-cloop.c block-dmg.c block-bochs.c block-vpc.c +qemu-img$(EXESUF): qemu-img.c block.c block-cow.c block-qcow.c aes.c block-vmdk.c block-cloop.c block-dmg.c block-bochs.c block-vpc.c block-vvfat.c $(CC) -DQEMU_TOOL $(CFLAGS) $(LDFLAGS) $(DEFINES) -o $@ $^ -lz $(LIBS) dyngen$(EXESUF): dyngen.c @@ -59,7 +59,7 @@ install: all mkdir -p "$(datadir)" install -m 644 pc-bios/bios.bin pc-bios/vgabios.bin \ pc-bios/vgabios-cirrus.bin \ - pc-bios/ppc_rom.bin \ + pc-bios/ppc_rom.bin pc-bios/video.x \ pc-bios/proll.elf \ pc-bios/linux_boot.bin "$(datadir)" mkdir -p "$(docdir)" @@ -112,6 +112,7 @@ tarbin: $(bindir)/qemu-system-ppc \ $(bindir)/qemu-system-sparc \ $(bindir)/qemu-system-x86_64 \ + $(bindir)/qemu-system-mips \ $(bindir)/qemu-i386 \ $(bindir)/qemu-arm \ $(bindir)/qemu-sparc \ @@ -121,6 +122,7 @@ tarbin: $(datadir)/vgabios.bin \ $(datadir)/vgabios-cirrus.bin \ $(datadir)/ppc_rom.bin \ + $(datadir)/video.x \ $(datadir)/proll.elf \ $(datadir)/linux_boot.bin \ $(docdir)/qemu-doc.html \ diff --git a/qemu/Makefile.target b/qemu/Makefile.target index 1e6ff27..9a6b4d7 100644 --- a/qemu/Makefile.target +++ b/qemu/Makefile.target @@ -4,6 +4,9 @@ TARGET_BASE_ARCH:=$(TARGET_ARCH) ifeq ($(TARGET_ARCH), x86_64) TARGET_BASE_ARCH:=i386 endif +ifeq ($(TARGET_ARCH), ppc64) +TARGET_BASE_ARCH:=ppc +endif ifeq ($(TARGET_ARCH), sparc64) TARGET_BASE_ARCH:=sparc endif @@ -44,74 +47,10 @@ endif ifdef CONFIG_USER_ONLY PROGS=$(QEMU_USER) else -ifeq ($(TARGET_ARCH), i386) - -ifeq ($(ARCH), i386) PROGS+=$(QEMU_SYSTEM) ifndef CONFIG_SOFTMMU CONFIG_STATIC=y endif -else -# the system emulator using soft mmu is portable -ifdef CONFIG_SOFTMMU -PROGS+=$(QEMU_SYSTEM) -endif -endif # ARCH != i386 - -endif # TARGET_ARCH = i386 - -ifeq ($(TARGET_ARCH), x86_64) -ifdef CONFIG_SOFTMMU -PROGS+=$(QEMU_SYSTEM) -endif -endif # TARGET_ARCH = x86_64 - -ifeq ($(TARGET_ARCH), ppc) - -ifeq ($(ARCH), ppc) -PROGS+=$(QEMU_SYSTEM) -endif - -ifeq ($(ARCH), i386) -ifdef CONFIG_SOFTMMU -PROGS+=$(QEMU_SYSTEM) -endif -endif # ARCH = i386 - -ifeq ($(ARCH), x86_64) -ifdef CONFIG_SOFTMMU -PROGS+=$(QEMU_SYSTEM) -endif -endif # ARCH = x86_64 - -endif # TARGET_ARCH = ppc - -ifeq ($(TARGET_ARCH), sparc) - -ifeq ($(ARCH), ppc) -PROGS+=$(QEMU_SYSTEM) -endif - -ifeq ($(ARCH), i386) -ifdef CONFIG_SOFTMMU -PROGS+=$(QEMU_SYSTEM) -endif -endif # ARCH = i386 - -ifeq ($(ARCH), x86_64) -ifdef CONFIG_SOFTMMU -PROGS+=$(QEMU_SYSTEM) -endif -endif # ARCH = x86_64 - -endif # TARGET_ARCH = sparc - -ifeq ($(TARGET_ARCH), sparc64) -ifdef CONFIG_SOFTMMU -PROGS+=$(QEMU_SYSTEM) -endif -endif # TARGET_ARCH = sparc64 - endif # !CONFIG_USER_ONLY ifdef CONFIG_STATIC @@ -259,7 +198,11 @@ ifeq ($(TARGET_ARCH), x86_64) LIBOBJS+=helper.o helper2.o endif -ifeq ($(TARGET_ARCH), ppc) +ifeq ($(TARGET_BASE_ARCH), ppc) +LIBOBJS+= op_helper.o helper.o +endif + +ifeq ($(TARGET_ARCH), mips) LIBOBJS+= op_helper.o helper.o endif @@ -285,9 +228,12 @@ endif ifeq ($(findstring alpha, $(TARGET_ARCH) $(ARCH)),alpha) LIBOBJS+=alpha-dis.o endif -ifeq ($(findstring ppc, $(TARGET_ARCH) $(ARCH)),ppc) +ifeq ($(findstring ppc, $(TARGET_BASE_ARCH) $(ARCH)),ppc) LIBOBJS+=ppc-dis.o endif +ifeq ($(findstring mips, $(TARGET_ARCH) $(ARCH)),mips) +LIBOBJS+=mips-dis.o +endif ifeq ($(findstring sparc, $(TARGET_BASE_ARCH) $(ARCH)),sparc) LIBOBJS+=sparc-dis.o endif @@ -314,7 +260,7 @@ endif # must use static linking to avoid leaving stuff in virtual address space VL_OBJS=vl.o osdep.o block.o readline.o monitor.o pci.o console.o -VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o block-dmg.o block-bochs.o block-vpc.o +VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o block-dmg.o block-bochs.o block-vpc.o block-vvfat.o SOUND_HW = sb16.o AUDIODRV = audio.o noaudio.o wavaudio.o @@ -343,13 +289,24 @@ VL_OBJS+= ide.o ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pc.o VL_OBJS+= cirrus_vga.o mixeng.o apic.o parallel.o endif -ifeq ($(TARGET_ARCH), ppc) +ifeq ($(TARGET_BASE_ARCH), ppc) VL_OBJS+= ppc.o ide.o ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) VL_OBJS+= mc146818rtc.o serial.o i8259.o i8254.o fdc.o m48t59.o -VL_OBJS+= ppc_prep.o ppc_chrp.o cuda.o adb.o openpic.o mixeng.o +VL_OBJS+= ppc_prep.o ppc_chrp.o cuda.o adb.o openpic.o heathrow_pic.o mixeng.o +endif +ifeq ($(TARGET_ARCH), mips) +VL_OBJS+= mips_r4k.o dma.o vga.o serial.o ne2000.o i8259.o +#VL_OBJS+= #ide.o pckbd.o i8254.o fdc.o m48t59.o endif ifeq ($(TARGET_BASE_ARCH), sparc) -VL_OBJS+= sun4m.o tcx.o lance.o iommu.o m48t08.o magic-load.o slavio_intctl.o slavio_timer.o slavio_serial.o fdc.o esp.o +ifeq ($(TARGET_ARCH), sparc64) +VL_OBJS+= sun4u.o ide.o ne2000.o pckbd.o vga.o +VL_OBJS+= fdc.o mc146818rtc.o serial.o m48t59.o +VL_OBJS+= cirrus_vga.o parallel.o +VL_OBJS+= magic-load.o +else +VL_OBJS+= sun4m.o tcx.o lance.o iommu.o m48t08.o magic-load.o slavio_intctl.o slavio_timer.o slavio_serial.o slavio_misc.o fdc.o esp.o +endif endif ifdef CONFIG_GDBSTUB VL_OBJS+=gdbstub.o @@ -442,12 +399,19 @@ op.o: op.c op_template.h endif ifeq ($(TARGET_BASE_ARCH), sparc) -op.o: op.c op_template.h op_mem.h +op.o: op.c op_template.h op_mem.h fop_template.h fbranch_template.h +magic_load.o: elf_op.h endif -ifeq ($(TARGET_ARCH), ppc) +ifeq ($(TARGET_BASE_ARCH), ppc) op.o: op.c op_template.h op_mem.h op_helper.o: op_helper_mem.h +translate.o: translate.c translate_init.c +endif + +ifeq ($(TARGET_ARCH), mips) +op.o: op.c op_template.c op_mem.c +op_helper.o: op_helper_mem.c endif mixeng.o: mixeng.c mixeng.h mixeng_template.h diff --git a/qemu/VERSION b/qemu/VERSION index bcaffe1..7deb86f 100644 --- a/qemu/VERSION +++ b/qemu/VERSION @@ -1 +1 @@ -0.7.0 \ No newline at end of file +0.7.1 \ No newline at end of file diff --git a/qemu/audio/ossaudio.c b/qemu/audio/ossaudio.c index ee897c9..5246ebb 100644 --- a/qemu/audio/ossaudio.c +++ b/qemu/audio/ossaudio.c @@ -127,7 +127,7 @@ static int oss_open (struct oss_params *req, struct oss_params *obt, int *pfd) int fmt, freq, nchannels; const char *dspname = conf.dspname; - fd = open (dspname, O_RDWR | O_NONBLOCK); + fd = open (dspname, O_WRONLY | O_NONBLOCK); if (-1 == fd) { dolog ("Could not initialize audio hardware. Failed to open `%s':\n" "Reason:%s\n", diff --git a/qemu/block-cow.c b/qemu/block-cow.c index 15270df..eeeab70 100644 --- a/qemu/block-cow.c +++ b/qemu/block-cow.c @@ -54,7 +54,8 @@ static int cow_probe(const uint8_t *buf, int buf_size, const char *filename) { const struct cow_header_v2 *cow_header = (const void *)buf; - if (be32_to_cpu(cow_header->magic) == COW_MAGIC && + if (buf_size >= sizeof(struct cow_header_v2) && + be32_to_cpu(cow_header->magic) == COW_MAGIC && be32_to_cpu(cow_header->version) == COW_VERSION) return 100; else diff --git a/qemu/block-dmg.c b/qemu/block-dmg.c index 582e3cb..5df7235 100644 --- a/qemu/block-dmg.c +++ b/qemu/block-dmg.c @@ -91,7 +91,9 @@ static int dmg_open(BlockDriverState *bs, const char *filename) if(lseek(s->fd,-0x1d8,SEEK_END)<0) { dmg_close: close(s->fd); - return -1; + /* open raw instead */ + bs->drv=&bdrv_raw; + return bs->drv->bdrv_open(bs,filename); } info_begin=read_off(s->fd); if(info_begin==0) diff --git a/qemu/block-qcow.c b/qemu/block-qcow.c index a473298..ca05be8 100644 --- a/qemu/block-qcow.c +++ b/qemu/block-qcow.c @@ -80,8 +80,9 @@ static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset); static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) { const QCowHeader *cow_header = (const void *)buf; - - if (be32_to_cpu(cow_header->magic) == QCOW_MAGIC && + + if (buf_size >= sizeof(QCowHeader) && + be32_to_cpu(cow_header->magic) == QCOW_MAGIC && be32_to_cpu(cow_header->version) == QCOW_VERSION) return 100; else @@ -551,9 +552,19 @@ static int qcow_create(const char *filename, int64_t total_size, header_size = sizeof(header); backing_filename_len = 0; if (backing_file) { - realpath(backing_file, backing_filename); - if (stat(backing_filename, &st) != 0) { - return -1; + const char *p; + /* XXX: this is a hack: we do not attempt to check for URL + like syntax */ + p = strchr(backing_file, ':'); + if (p && (p - backing_file) >= 2) { + /* URL like but exclude "c:" like filenames */ + pstrcpy(backing_filename, sizeof(backing_filename), + backing_file); + } else { + realpath(backing_file, backing_filename); + if (stat(backing_filename, &st) != 0) { + return -1; + } } header.mtime = cpu_to_be32(st.st_mtime); header.backing_file_offset = cpu_to_be64(header_size); diff --git a/qemu/block-vmdk.c b/qemu/block-vmdk.c index f80bf07..3bbd149 100644 --- a/qemu/block-vmdk.c +++ b/qemu/block-vmdk.c @@ -315,6 +315,109 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num, return 0; } +static int vmdk_create(const char *filename, int64_t total_size, + const char *backing_file, int flags) +{ + int fd, i; + VMDK4Header header; + uint32_t tmp, magic, grains, gd_size, gt_size, gt_count; + char *desc_template = + "# Disk DescriptorFile\n" + "version=1\n" + "CID=%x\n" + "parentCID=ffffffff\n" + "createType=\"monolithicSparse\"\n" + "\n" + "# Extent description\n" + "RW %lu SPARSE \"%s\"\n" + "\n" + "# The Disk Data Base \n" + "#DDB\n" + "\n" + "ddb.virtualHWVersion = \"3\"\n" + "ddb.geometry.cylinders = \"%lu\"\n" + "ddb.geometry.heads = \"16\"\n" + "ddb.geometry.sectors = \"63\"\n" + "ddb.adapterType = \"ide\"\n"; + char desc[1024]; + const char *real_filename, *temp_str; + + /* XXX: add support for backing file */ + + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, + 0644); + if (fd < 0) + return -1; + magic = cpu_to_be32(VMDK4_MAGIC); + memset(&header, 0, sizeof(header)); + header.version = cpu_to_le32(1); + header.flags = cpu_to_le32(3); /* ?? */ + header.capacity = cpu_to_le64(total_size); + header.granularity = cpu_to_le64(128); + header.num_gtes_per_gte = cpu_to_le32(512); + + grains = (total_size + header.granularity - 1) / header.granularity; + gt_size = ((header.num_gtes_per_gte * sizeof(uint32_t)) + 511) >> 9; + gt_count = (grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte; + gd_size = (gt_count * sizeof(uint32_t) + 511) >> 9; + + header.desc_offset = 1; + header.desc_size = 20; + header.rgd_offset = header.desc_offset + header.desc_size; + header.gd_offset = header.rgd_offset + gd_size + (gt_size * gt_count); + header.grain_offset = + ((header.gd_offset + gd_size + (gt_size * gt_count) + + header.granularity - 1) / header.granularity) * + header.granularity; + + header.desc_offset = cpu_to_le64(header.desc_offset); + header.desc_size = cpu_to_le64(header.desc_size); + header.rgd_offset = cpu_to_le64(header.rgd_offset); + header.gd_offset = cpu_to_le64(header.gd_offset); + header.grain_offset = cpu_to_le64(header.grain_offset); + + header.check_bytes[0] = 0xa; + header.check_bytes[1] = 0x20; + header.check_bytes[2] = 0xd; + header.check_bytes[3] = 0xa; + + /* write all the data */ + write(fd, &magic, sizeof(magic)); + write(fd, &header, sizeof(header)); + + ftruncate(fd, header.grain_offset << 9); + + /* write grain directory */ + lseek(fd, le64_to_cpu(header.rgd_offset) << 9, SEEK_SET); + for (i = 0, tmp = header.rgd_offset + gd_size; + i < gt_count; i++, tmp += gt_size) + write(fd, &tmp, sizeof(tmp)); + + /* write backup grain directory */ + lseek(fd, le64_to_cpu(header.gd_offset) << 9, SEEK_SET); + for (i = 0, tmp = header.gd_offset + gd_size; + i < gt_count; i++, tmp += gt_size) + write(fd, &tmp, sizeof(tmp)); + + /* compose the descriptor */ + real_filename = filename; + if ((temp_str = strrchr(real_filename, '\\')) != NULL) + real_filename = temp_str + 1; + if ((temp_str = strrchr(real_filename, '/')) != NULL) + real_filename = temp_str + 1; + if ((temp_str = strrchr(real_filename, ':')) != NULL) + real_filename = temp_str + 1; + sprintf(desc, desc_template, time(NULL), (unsigned long)total_size, + real_filename, total_size / (63 * 16)); + + /* write the descriptor */ + lseek(fd, le64_to_cpu(header.desc_offset) << 9, SEEK_SET); + write(fd, desc, strlen(desc)); + + close(fd); + return 0; +} + static void vmdk_close(BlockDriverState *bs) { BDRVVmdkState *s = bs->opaque; @@ -331,6 +434,6 @@ BlockDriver bdrv_vmdk = { vmdk_read, vmdk_write, vmdk_close, - NULL, /* no create yet */ + vmdk_create, vmdk_is_allocated, }; diff --git a/qemu/block-vpc.c b/qemu/block-vpc.c index 88ad575..e4c51ba 100644 --- a/qemu/block-vpc.c +++ b/qemu/block-vpc.c @@ -81,9 +81,8 @@ typedef struct BDRVVPCState { static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename) { - if (!strncmp(buf, "conectix", 8)) + if (buf_size >= 8 && !strncmp(buf, "conectix", 8)) return 100; - return 0; } diff --git a/qemu/block-vvfat.c b/qemu/block-vvfat.c new file mode 100644 index 0000000..7bce91e --- /dev/null +++ b/qemu/block-vvfat.c @@ -0,0 +1,1742 @@ +/* + * QEMU Block driver for virtual VFAT (shadows a local directory) + * + * Copyright (c) 2004 Johannes E. Schindelin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include "vl.h" +#include "block_int.h" + +// TODO: new file +// TODO: delete file +// TODO: make root directory larger +// TODO: make directory clusters connected, so they are reserved anyway... add a member which tells how many clusters are reserved after a directory +// TODO: introduce another member in mapping_t which says where the directory resides in s->directory (for mkdir and rmdir) +// in _read and _write, before treating direntries or file contents, get_mapping to know what it is. +// TODO: mkdir +// TODO: rmdir + +// TODO: when commit_data'ing a direntry and is_consistent, commit_remove +// TODO: reset MODE_MODIFIED when commit_remove'ing + +#define DEBUG + +/* dynamic array functions */ +typedef struct array_t { + char* pointer; + unsigned int size,next,item_size; +} array_t; + +static inline void array_init(array_t* array,unsigned int item_size) +{ + array->pointer=0; + array->size=0; + array->next=0; + array->item_size=item_size; +} + +static inline void array_free(array_t* array) +{ + if(array->pointer) + free(array->pointer); + array->size=array->next=0; +} + +/* make sure that memory is reserved at pointer[index*item_size] */ +static inline void* array_get(array_t* array,unsigned int index) { + if((index+1)*array->item_size>array->size) { + int new_size=(index+32)*array->item_size; + array->pointer=realloc(array->pointer,new_size); + if(!array->pointer) + return 0; + array->size=new_size; + array->next=index+1; + } + return array->pointer+index*array->item_size; +} + +static inline void* array_get_next(array_t* array) { + unsigned int next=array->next; + void* result=array_get(array,next); + array->next=next+1; + return result; +} + +static inline void* array_insert(array_t* array,unsigned int index,unsigned int count) { + if((array->next+count)*array->item_size>array->size) { + int increment=count*array->item_size; + array->pointer=realloc(array->pointer,array->size+increment); + if(!array->pointer) + return 0; + array->size+=increment; + } + memmove(array->pointer+(index+count)*array->item_size, + array->pointer+index*array->item_size, + (array->next-index)*array->item_size); + array->next+=count; + return array->pointer+index*array->item_size; +} + +/* this performs a "roll", so that the element which was at index_from becomes + * index_to, but the order of all other elements is preserved. */ +static inline int array_roll(array_t* array,int index_to,int index_from,int count) +{ + char* buf; + char* from; + char* to; + int is; + + if(!array || + index_to<0 || index_to>=array->next || + index_from<0 || index_from>=array->next) + return -1; + + if(index_to==index_from) + return 0; + + is=array->item_size; + from=array->pointer+index_from*is; + to=array->pointer+index_to*is; + buf=malloc(is*count); + memcpy(buf,from,is*count); + + if(index_tonext-1,index,1)) + return -1; + array->next--; + return 0; +} + +/* These structures are used to fake a disk and the VFAT filesystem. + * For this reason we need to use __attribute__((packed)). */ + +typedef struct bootsector_t { + uint8_t jump[3]; + uint8_t name[8]; + uint16_t sector_size; + uint8_t sectors_per_cluster; + uint16_t reserved_sectors; + uint8_t number_of_fats; + uint16_t root_entries; + uint16_t zero; + uint8_t media_type; + uint16_t sectors_per_fat; + uint16_t sectors_per_track; + uint16_t number_of_heads; + uint32_t hidden_sectors; + uint32_t total_sectors; + union { + struct { + uint8_t drive_number; + uint8_t current_head; + uint8_t signature; + uint32_t id; + uint8_t volume_label[11]; + } __attribute__((packed)) fat16; + struct { + uint32_t sectors_per_fat; + uint16_t flags; + uint8_t major,minor; + uint32_t first_cluster_of_root_directory; + uint16_t info_sector; + uint16_t backup_boot_sector; + uint16_t ignored; + } __attribute__((packed)) fat32; + } u; + uint8_t fat_type[8]; + uint8_t ignored[0x1c0]; + uint8_t magic[2]; +} __attribute__((packed)) bootsector_t; + +typedef struct partition_t { + uint8_t attributes; /* 0x80 = bootable */ + uint8_t start_head; + uint8_t start_sector; + uint8_t start_cylinder; + uint8_t fs_type; /* 0x6 = FAT16, 0xb = FAT32 */ + uint8_t end_head; + uint8_t end_sector; + uint8_t end_cylinder; + uint32_t start_sector_long; + uint32_t end_sector_long; +} __attribute__((packed)) partition_t; + +typedef struct mbr_t { + uint8_t ignored[0x1be]; + partition_t partition[4]; + uint8_t magic[2]; +} __attribute__((packed)) mbr_t; + +typedef struct direntry_t { + uint8_t name[8]; + uint8_t extension[3]; + uint8_t attributes; + uint8_t reserved[2]; + uint16_t ctime; + uint16_t cdate; + uint16_t adate; + uint16_t begin_hi; + uint16_t mtime; + uint16_t mdate; + uint16_t begin; + uint32_t size; +} __attribute__((packed)) direntry_t; + +/* this structure are used to transparently access the files */ + +typedef struct mapping_t { + /* begin is the first cluster, end is the last+1, + * offset is the offset in the file in clusters of this slice */ + off_t begin,end,offset; + char* filename; + + /* as s->directory is growable, no pointer may be used here */ + unsigned int dir_index; + enum { MODE_NORMAL,MODE_UNDEFINED,MODE_MODIFIED,MODE_DELETED,MODE_DIRECTORY } mode; +} mapping_t; + +/* this structure is used to hold sectors which need to be written, but it's + * not known yet where to write them. */ + +typedef struct commit_t { + uint32_t cluster_num; + uint8_t* buf; +} commit_t; + +/* write support exists for fat, direntry and file contents */ +typedef enum { + WRITE_UNDEFINED,WRITE_FAT,WRITE_DIRENTRY,WRITE_DATA +} write_action_t; + +/* here begins the real VVFAT driver */ + +typedef struct BDRVVVFATState { + unsigned int first_sectors_number; /* 1 for a single partition, 0x40 for a disk with partition table */ + unsigned char first_sectors[0x40*0x200]; + + int fat_type; /* 16 or 32 */ + array_t fat,directory,mapping; + + unsigned int cluster_size; + unsigned int sectors_per_cluster; + unsigned int sectors_per_fat; + unsigned int sectors_of_root_directory; + unsigned int sectors_for_directory; + unsigned int faked_sectors; /* how many sectors are faked before file data */ + uint32_t sector_count; /* total number of sectors of the partition */ + uint32_t cluster_count; /* total number of clusters of this partition */ + unsigned int first_file_mapping; /* index of the first mapping which is not a directory, but a file */ + uint32_t max_fat_value; + + int current_fd; + char current_fd_is_writable; /* =0 if read only, !=0 if read/writable */ + mapping_t* current_mapping; + unsigned char* cluster; + unsigned int current_cluster; + + /* write support */ + array_t commit; + /* for each file, the file contents, the direntry, and the fat entries are + * written, but not necessarily in that order */ + write_action_t action[3]; +} BDRVVVFATState; + + +static int vvfat_probe(const uint8_t *buf, int buf_size, const char *filename) +{ + if (strstart(filename, "fat:", NULL) || + strstart(filename, "fatrw:", NULL)) + return 100; + return 0; +} + +static void init_mbr(BDRVVVFATState* s) +{ + /* TODO: if the files mbr.img and bootsect.img exist, use them */ + mbr_t* real_mbr=(mbr_t*)s->first_sectors; + partition_t* partition=&(real_mbr->partition[0]); + + memset(s->first_sectors,0,512); + + partition->attributes=0x80; /* bootable */ + partition->start_head=1; + partition->start_sector=1; + partition->start_cylinder=0; + partition->fs_type=(s->fat_type==16?0x6:0xb); /* FAT16/FAT32 */ + partition->end_head=0xf; + partition->end_sector=0xff; /* end sector & upper 2 bits of cylinder */; + partition->end_cylinder=0xff; /* lower 8 bits of end cylinder */; + partition->start_sector_long=cpu_to_le32(0x3f); + partition->end_sector_long=cpu_to_le32(s->sector_count); + + real_mbr->magic[0]=0x55; real_mbr->magic[1]=0xaa; +} + +/* dest is assumed to hold 258 bytes, and pads with 0xffff up to next multiple of 26 */ +static inline int short2long_name(unsigned char* dest,const char* src) +{ + int i; + for(i=0;i<129 && src[i];i++) { + dest[2*i]=src[i]; + dest[2*i+1]=0; + } + dest[2*i]=dest[2*i+1]=0; + for(i=2*i+2;(i%26);i++) + dest[i]=0xff; + return i; +} + +static inline direntry_t* create_long_filename(BDRVVVFATState* s,const char* filename) +{ + char buffer[258]; + int length=short2long_name(buffer,filename), + number_of_entries=(length+25)/26,i; + direntry_t* entry; + + for(i=0;idirectory)); + entry->attributes=0xf; + entry->reserved[0]=0; + entry->begin=0; + entry->name[0]=(number_of_entries-i)|(i==0?0x40:0); + } + for(i=0;idirectory),s->directory.next-1-(i/26)); + entry->name[offset]=buffer[i]; + } + return array_get(&(s->directory),s->directory.next-number_of_entries); +} + +/* fat functions */ + +static inline uint8_t fat_chksum(direntry_t* entry) +{ + uint8_t chksum=0; + int i; + + for(i=0;i<11;i++) + chksum=(((chksum&0xfe)>>1)|((chksum&0x01)?0x80:0)) + +(unsigned char)entry->name[i]; + + return chksum; +} + +/* if return_time==0, this returns the fat_date, else the fat_time */ +static uint16_t fat_datetime(time_t time,int return_time) { + struct tm* t; +#ifdef _WIN32 + t=localtime(&time); /* this is not thread safe */ +#else + struct tm t1; + t=&t1; + localtime_r(&time,t); +#endif + if(return_time) + return cpu_to_le16((t->tm_sec/2)|(t->tm_min<<5)|(t->tm_hour<<11)); + return cpu_to_le16((t->tm_mday)|((t->tm_mon+1)<<5)|((t->tm_year-80)<<9)); +} + +static inline void fat_set(BDRVVVFATState* s,unsigned int cluster,uint32_t value) +{ + if(s->fat_type==12) { + assert(0); /* TODO */ + } else if(s->fat_type==16) { + uint16_t* entry=array_get(&(s->fat),cluster); + *entry=cpu_to_le16(value&0xffff); + } else { + uint32_t* entry=array_get(&(s->fat),cluster); + *entry=cpu_to_le32(value); + } +} + +static inline uint32_t fat_get(BDRVVVFATState* s,unsigned int cluster) +{ + //fprintf(stderr,"want to get fat for cluster %d\n",cluster); + if(s->fat_type==12) { + const uint8_t* x=s->fat.pointer+cluster*3/2; + return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff; + } else if(s->fat_type==16) { + uint16_t* entry=array_get(&(s->fat),cluster); + return le16_to_cpu(*entry); + } else { + uint32_t* entry=array_get(&(s->fat),cluster); + return le32_to_cpu(*entry); + } +} + +static inline int fat_eof(BDRVVVFATState* s,uint32_t fat_entry) +{ + if(fat_entry>s->max_fat_value-8) + return -1; + return 0; +} + +static inline void init_fat(BDRVVVFATState* s) +{ + int i; + + array_init(&(s->fat),(s->fat_type==32?4:2)); + array_get(&(s->fat),s->sectors_per_fat*0x200/s->fat.item_size-1); + memset(s->fat.pointer,0,s->fat.size); + fat_set(s,0,0x7ffffff8); + + for(i=1;isectors_for_directory/s->sectors_per_cluster-1;i++) + fat_set(s,i,i+1); + fat_set(s,i,0x7fffffff); + + switch(s->fat_type) { + case 12: s->max_fat_value=0xfff; break; + case 16: s->max_fat_value=0xffff; break; + case 32: s->max_fat_value=0xfffffff; break; + default: s->max_fat_value=0; /* error... */ + } + +} + +static inline int long2unix_name(unsigned char* dest,int dest_size,direntry_t* direntry_short) { + int i=-1,j; + int chksum=fat_chksum(direntry_short); + while(1) { + char* buf=(char*)(direntry_short+i); + if((buf[0]&0x3f)!=-i || direntry_short[i].reserved[1]!=chksum || + direntry_short[i].attributes!=0xf) { + if(i<-1) + return -3; + /* take short name */ + for(j=7;j>0 && direntry_short->name[j]==' ';j--); + if(j+1>dest_size) + return -1; + strncpy(dest,direntry_short->name,j+1); + dest+=j+1; dest_size-=j+1; + for(j=2;j>=0 && direntry_short->extension[j]==' ';j--); + if(j>=0) { + if(j+2>dest_size) + return -1; + dest[0]='.'; + strncpy(dest+1,direntry_short->extension,j+1); + } + return 0; + } + for(j=0;j<13;j++) { + dest_size--; + if(dest_size<0) + return -2; + dest[0]=buf[2*j+((j<5)?1:(j<11)?4:6)]; + if(dest[0]==0 && (buf[0]&0x40)!=0) + return 0; + dest++; + } + /* last entry, but no trailing \0? */ + if(buf[0]&0x40) + return -3; + i--; + } +} + +static inline direntry_t* create_short_filename(BDRVVVFATState* s,unsigned int directory_start,const char* filename,int is_dot) +{ + int i,long_index=s->directory.next; + direntry_t* entry=0; + direntry_t* entry_long=0; + + if(is_dot) { + entry=array_get_next(&(s->directory)); + memset(entry->name,0x20,11); + memcpy(entry->name,filename,strlen(filename)); + return entry; + } + + for(i=1;i<8 && filename[i] && filename[i]!='.';i++); + + entry_long=create_long_filename(s,filename); + + entry=array_get_next(&(s->directory)); + memset(entry->name,0x20,11); + strncpy(entry->name,filename,i); + + if(filename[i]) { + int len=strlen(filename); + for(i=len;i>0 && filename[i-1]!='.';i--); + if(i>0) + memcpy(entry->extension,filename+i,(len-i>3?3:len-i)); + } + + /* upcase & remove unwanted characters */ + for(i=10;i>=0;i--) { + if(i==10 || i==7) for(;i>1 && entry->name[i]==' ';i--); + if(entry->name[i]<=' ' || entry->name[i]>0x7f + || strchr("*?<>|\":/\\[];,+='",entry->name[i])) + entry->name[i]='_'; + else if(entry->name[i]>='a' && entry->name[i]<='z') + entry->name[i]+='A'-'a'; + } + + /* mangle duplicates */ + while(1) { + direntry_t* entry1=array_get(&(s->directory),directory_start); + int j; + + for(;entry1attributes&0xf) && !memcmp(entry1->name,entry->name,11)) + break; /* found dupe */ + if(entry1==entry) /* no dupe found */ + break; + + /* use all 8 characters of name */ + if(entry->name[7]==' ') { + int j; + for(j=6;j>0 && entry->name[j]==' ';j--) + entry->name[j]='~'; + } + + /* increment number */ + for(j=7;j>0 && entry->name[j]=='9';j--) + entry->name[j]='0'; + if(j>0) { + if(entry->name[j]<'0' || entry->name[j]>'9') + entry->name[j]='0'; + else + entry->name[j]++; + } + } + + /* calculate checksum; propagate to long name */ + if(entry_long) { + uint8_t chksum=fat_chksum(entry); + + /* calculate anew, because realloc could have taken place */ + entry_long=array_get(&(s->directory),long_index); + while(entry_longattributes==0xf) { + entry_long->reserved[1]=chksum; + entry_long++; + } + } + + return entry; +} + +static int read_directory(BDRVVVFATState* s,const char* dirname, + int first_cluster_of_parent) +{ + + DIR* dir=opendir(dirname); + struct dirent* entry; + struct stat st; + unsigned int start_of_directory=s->directory.next; + /* mappings before first_file_mapping are directories */ + unsigned int first_directory_mapping=s->first_file_mapping; + unsigned int first_cluster=(start_of_directory/0x10/s->sectors_per_cluster); + int i; + + if(!dir) + return -1; + + while((entry=readdir(dir))) { + unsigned int length=strlen(dirname)+2+strlen(entry->d_name); + char* buffer; + direntry_t* direntry; + int is_dot=!strcmp(entry->d_name,"."); + int is_dotdot=!strcmp(entry->d_name,".."); + + if(start_of_directory==1 && (is_dotdot || is_dot)) + continue; + + buffer=(char*)malloc(length); + snprintf(buffer,length,"%s/%s",dirname,entry->d_name); + + if(stat(buffer,&st)<0) { + free(buffer); + continue; + } + + /* create directory entry for this file */ + //fprintf(stderr,"create direntry at %d (cluster %d) for %s\n",s->directory.next,s->directory.next/0x10/s->sectors_per_cluster,entry->d_name); + direntry=create_short_filename(s,start_of_directory,entry->d_name,is_dot||is_dotdot); + direntry->attributes=(S_ISDIR(st.st_mode)?0x10:0x20); + direntry->reserved[0]=direntry->reserved[1]=0; + direntry->ctime=fat_datetime(st.st_ctime,1); + direntry->cdate=fat_datetime(st.st_ctime,0); + direntry->adate=fat_datetime(st.st_atime,0); + direntry->begin_hi=0; + direntry->mtime=fat_datetime(st.st_mtime,1); + direntry->mdate=fat_datetime(st.st_mtime,0); + if(is_dotdot) + direntry->begin=cpu_to_le16(first_cluster_of_parent); + else if(is_dot) + direntry->begin=cpu_to_le16(first_cluster); + else + direntry->begin=cpu_to_le16(0); /* do that later */ + direntry->size=cpu_to_le32(st.st_size); + + /* create mapping for this file */ + if(!is_dot && !is_dotdot) { + if(S_ISDIR(st.st_mode)) + s->current_mapping=(mapping_t*)array_insert(&(s->mapping),s->first_file_mapping++,1); + else + s->current_mapping=(mapping_t*)array_get_next(&(s->mapping)); + s->current_mapping->begin=0; + s->current_mapping->end=st.st_size; + s->current_mapping->offset=0; + s->current_mapping->filename=buffer; + s->current_mapping->dir_index=s->directory.next-1; + s->current_mapping->mode=(S_ISDIR(st.st_mode)?MODE_DIRECTORY:MODE_UNDEFINED); + } + } + closedir(dir); + + /* fill with zeroes up to the end of the cluster */ + while(s->directory.next%(0x10*s->sectors_per_cluster)) { + direntry_t* direntry=array_get_next(&(s->directory)); + memset(direntry,0,sizeof(direntry_t)); + } + + /* reserve next cluster also (for new files) */ + for(i=0;i<0x10*s->sectors_per_cluster;i++) { + direntry_t* direntry=array_get_next(&(s->directory)); + memset(direntry,0,sizeof(direntry_t)); + } + + /* was it the first directory? */ + if(start_of_directory==1) { + mapping_t* mapping=array_insert(&(s->mapping),0,1); + mapping->filename=strdup(dirname); + mapping->mode=MODE_DIRECTORY; + mapping->begin=0; + mapping->end=1; + mapping->offset=0; + mapping->dir_index=0xffffffff; + s->sectors_of_root_directory=s->directory.next/0x10; + } + + /* recurse directories */ + { + int i; + + //fprintf(stderr,"iterating subdirectories of %s (first cluster %d): %d to %d\n",dirname,first_cluster,first_directory_mapping,last_directory_mapping); + for(i=first_directory_mapping;ifirst_file_mapping;i++) { + mapping_t* mapping=array_get(&(s->mapping),i); + direntry_t* direntry=array_get(&(s->directory),mapping->dir_index); + /* the directory to be read can add more subdirectories */ + int last_dir_mapping=s->first_file_mapping; + + assert(mapping->mode==MODE_DIRECTORY); + /* first, tell the mapping where the directory will start */ + mapping->begin=s->directory.next/0x10/s->sectors_per_cluster; + if(i>0) { + mapping[-1].end=mapping->begin; + assert(mapping[-1].beginbegin); + } + /* then tell the direntry */ + direntry->begin=cpu_to_le16(mapping->begin); + //fprintf(stderr,"read directory %s (begin %d)\n",mapping->filename,(int)mapping->begin); + /* then read it */ + if(read_directory(s,mapping->filename,first_cluster)) + return -1; + + if(last_dir_mapping!=s->first_file_mapping) { + int diff=s->first_file_mapping-last_dir_mapping; + assert(diff>0); + + if(last_dir_mapping!=i+1) { + int count=last_dir_mapping-i-1; + int to=s->first_file_mapping-count; + + assert(count>0); + assert(to>i+1); + array_roll(&(s->mapping),to,i+1,count); + /* could have changed due to realloc */ + mapping=array_get(&(s->mapping),i); + mapping->end=mapping[1].begin; + } + i+=diff; + } + } + } + + return 0; +} + +static int init_directory(BDRVVVFATState* s,const char* dirname) +{ + bootsector_t* bootsector=(bootsector_t*)&(s->first_sectors[(s->first_sectors_number-1)*0x200]); + unsigned int i; + unsigned int cluster; + + memset(&(s->first_sectors[0]),0,0x40*0x200); + + /* TODO: if FAT32, this is probably wrong */ + s->sectors_per_fat=0xfc; + s->sectors_per_cluster=0x10; + s->cluster_size=s->sectors_per_cluster*0x200; + s->cluster=malloc(s->cluster_size); + + array_init(&(s->mapping),sizeof(mapping_t)); + array_init(&(s->directory),sizeof(direntry_t)); + array_init(&(s->commit),sizeof(commit_t)); + + /* add volume label */ + { + direntry_t* entry=array_get_next(&(s->directory)); + entry->attributes=0x28; /* archive | volume label */ + snprintf(entry->name,11,"QEMU VVFAT"); + } + + if(read_directory(s,dirname,0)) + return -1; + + /* make sure that the number of directory entries is multiple of 0x200/0x20 (to fit the last sector exactly) */ + s->sectors_for_directory=s->directory.next/0x10; + + s->faked_sectors=s->first_sectors_number+s->sectors_per_fat*2+s->sectors_for_directory; + s->cluster_count=(s->sector_count-s->faked_sectors)/s->sectors_per_cluster; + + /* Now build FAT, and write back information into directory */ + init_fat(s); + + cluster=s->sectors_for_directory/s->sectors_per_cluster; + assert(s->sectors_for_directory%s->sectors_per_cluster==0); + + /* set the end of the last read directory */ + if(s->first_file_mapping>0) { + mapping_t* mapping=array_get(&(s->mapping),s->first_file_mapping-1); + mapping->end=cluster; + } + + for(i=1;imapping.next;i++) { + mapping_t* mapping=array_get(&(s->mapping),i); + direntry_t* direntry=array_get(&(s->directory),mapping->dir_index); + if(mapping->mode==MODE_DIRECTORY) { + /* directory */ + int i; +#ifdef DEBUG + fprintf(stderr,"assert: %s %d < %d\n",mapping->filename,(int)mapping->begin,(int)mapping->end); +#endif + assert(mapping->beginend); + for(i=mapping->begin;iend-1;i++) + fat_set(s,i,i+1); + fat_set(s,i,0x7fffffff); + } else { + /* as the space is virtual, we can be sloppy about it */ + unsigned int end_cluster=cluster+mapping->end/s->cluster_size; + + if(end_cluster>=s->cluster_count) { + fprintf(stderr,"Directory does not fit in FAT%d\n",s->fat_type); + return -1; + } + mapping->begin=cluster; + mapping->mode=MODE_NORMAL; + mapping->offset=0; + direntry->size=cpu_to_le32(mapping->end); + if(direntry->size==0) { + direntry->begin=0; + mapping->end=cluster; + continue; + } + + direntry->begin=cpu_to_le16(cluster); + mapping->end=end_cluster+1; + for(;clustercurrent_mapping=0; + + bootsector->jump[0]=0xeb; + bootsector->jump[1]=0x3e; + bootsector->jump[2]=0x90; + memcpy(bootsector->name,"QEMU ",8); + bootsector->sector_size=cpu_to_le16(0x200); + bootsector->sectors_per_cluster=s->sectors_per_cluster; + bootsector->reserved_sectors=cpu_to_le16(1); + bootsector->number_of_fats=0x2; /* number of FATs */ + bootsector->root_entries=cpu_to_le16(s->sectors_of_root_directory*0x10); + bootsector->zero=0; + bootsector->media_type=(s->first_sectors_number==1?0xf0:0xf8); /* media descriptor */ + bootsector->sectors_per_fat=cpu_to_le16(s->sectors_per_fat); + bootsector->sectors_per_track=cpu_to_le16(0x3f); + bootsector->number_of_heads=cpu_to_le16(0x10); + bootsector->hidden_sectors=cpu_to_le32(s->first_sectors_number==1?0:0x3f); + /* TODO: if FAT32, adjust */ + bootsector->total_sectors=cpu_to_le32(s->sector_count); + + /* TODO: if FAT32, this is wrong */ + bootsector->u.fat16.drive_number=0x80; /* assume this is hda (TODO) */ + bootsector->u.fat16.current_head=0; + bootsector->u.fat16.signature=0x29; + bootsector->u.fat16.id=cpu_to_le32(0xfabe1afd); + + memcpy(bootsector->u.fat16.volume_label,"QEMU VVFAT ",11); + memcpy(bootsector->fat_type,(s->fat_type==12?"FAT12 ":s->fat_type==16?"FAT16 ":"FAT32 "),8); + bootsector->magic[0]=0x55; bootsector->magic[1]=0xaa; + + return 0; +} + +static int vvfat_open(BlockDriverState *bs, const char* dirname) +{ + BDRVVVFATState *s = bs->opaque; + int i; + + /* TODO: automatically determine which FAT type */ + s->fat_type=16; + s->sector_count=0xec04f; + + s->current_cluster=0xffffffff; + s->first_file_mapping=0; + + /* TODO: if simulating a floppy, this is 1, because there is no partition table */ + s->first_sectors_number=0x40; + + if (strstart(dirname, "fat:", &dirname)) { + /* read only is the default for safety */ + bs->read_only = 1; + } else if (strstart(dirname, "fatrw:", &dirname)) { + /* development only for now */ + bs->read_only = 0; + } else { + return -1; + } + if(init_directory(s,dirname)) + return -1; + + if(s->first_sectors_number==0x40) + init_mbr(s); + + /* TODO: this could be wrong for FAT32 */ + bs->cyls=1023; bs->heads=15; bs->secs=63; + bs->total_sectors=bs->cyls*bs->heads*bs->secs; + + /* write support */ + for(i=0;i<3;i++) + s->action[i]=WRITE_UNDEFINED; + return 0; +} + +static inline void vvfat_close_current_file(BDRVVVFATState *s) +{ + if(s->current_mapping) { + s->current_mapping = 0; + close(s->current_fd); + } +} + +/* mappings between index1 and index2-1 are supposed to be ordered + * return value is the index of the last mapping for which end>cluster_num + */ +static inline int find_mapping_for_cluster_aux(BDRVVVFATState* s,int cluster_num,int index1,int index2) +{ + int index3=index1+1; + //fprintf(stderr,"find_aux: cluster_num=%d, index1=%d,index2=%d\n",cluster_num,index1,index2); + while(1) { + mapping_t* mapping; + index3=(index1+index2)/2; + mapping=array_get(&(s->mapping),index3); + //fprintf(stderr,"index3: %d = (%d+%d)/2, end: %d\n",index3,index1,index2,(int)mapping->end); + if(mapping->end>cluster_num) { + assert(index2!=index3 || index2==0); + if(index2==index3) + return index2; + index2=index3; + } else { + if(index1==index3) + return index2; + index1=index3; + } + assert(index1<=index2); + } +} + +static inline mapping_t* find_mapping_for_cluster(BDRVVVFATState* s,int cluster_num) +{ + int index=find_mapping_for_cluster_aux(s,cluster_num,0,s->mapping.next); + mapping_t* mapping; + if(index>=s->mapping.next) + return 0; + mapping=array_get(&(s->mapping),index); + if(mapping->begin>cluster_num) + return 0; + return mapping; +} + +static int open_file(BDRVVVFATState* s,mapping_t* mapping,int flags) +{ + if(!mapping) + return -1; + assert(flags==O_RDONLY || flags==O_RDWR); + if(!s->current_mapping || + strcmp(s->current_mapping->filename,mapping->filename) || + (flags==O_RDWR && !s->current_fd_is_writable)) { + /* open file */ + int fd = open(mapping->filename, flags | O_BINARY | O_LARGEFILE); + if(fd<0) + return -1; + vvfat_close_current_file(s); + s->current_fd = fd; + s->current_fd_is_writable = (flags==O_RDWR?-1:0); + s->current_mapping = mapping; + } + return 0; +} + +static inline int read_cluster(BDRVVVFATState *s,int cluster_num) +{ + if(s->current_cluster != cluster_num) { + int result=0; + off_t offset; + if(!s->current_mapping + || s->current_mapping->begin>cluster_num + || s->current_mapping->end<=cluster_num) { + /* binary search of mappings for file */ + mapping_t* mapping=find_mapping_for_cluster(s,cluster_num); + if(open_file(s,mapping,O_RDONLY)) + return -2; + } + + offset=s->cluster_size*(cluster_num-s->current_mapping->begin+s->current_mapping->offset); + if(lseek(s->current_fd, offset, SEEK_SET)!=offset) + return -3; + result=read(s->current_fd,s->cluster,s->cluster_size); + if(result<0) { + s->current_cluster = -1; + return -1; + } + s->current_cluster = cluster_num; + } + return 0; +} + +static int vvfat_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + BDRVVVFATState *s = bs->opaque; + int i; + + // fprintf(stderr,"vvfat_read: sector %d+%d\n",(int)sector_num,nb_sectors); + + for(i=0;ifaked_sectors) { + if(sector_numfirst_sectors_number) + memcpy(buf+i*0x200,&(s->first_sectors[sector_num*0x200]),0x200); + else if(sector_num-s->first_sectors_numbersectors_per_fat) + memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number)*0x200]),0x200); + else if(sector_num-s->first_sectors_number-s->sectors_per_fatsectors_per_fat) + memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number-s->sectors_per_fat)*0x200]),0x200); + else if(sector_num-s->first_sectors_number-s->sectors_per_fat*2sectors_for_directory) + memcpy(buf+i*0x200,&(s->directory.pointer[(sector_num-s->first_sectors_number-s->sectors_per_fat*2)*0x200]),0x200); + } else { + uint32_t sector=sector_num-s->first_sectors_number-s->sectors_per_fat*2, + sector_offset_in_cluster=(sector%s->sectors_per_cluster), + cluster_num=sector/s->sectors_per_cluster; + if(read_cluster(s, cluster_num) != 0) { + //fprintf(stderr,"failed to read cluster %d\n",(int)cluster_num); + // TODO: strict: return -1; + memset(buf+i*0x200,0,0x200); + continue; + } + memcpy(buf+i*0x200,s->cluster+sector_offset_in_cluster*0x200,0x200); + } + } + return 0; +} + +static void print_direntry(direntry_t* direntry) +{ + if(!direntry) + return; + if(direntry->attributes==0xf) { + unsigned char* c=(unsigned char*)direntry; + int i; + for(i=1;i<11 && c[i] && c[i]!=0xff;i+=2) + fputc(c[i],stderr); + for(i=14;i<26 && c[i] && c[i]!=0xff;i+=2) + fputc(c[i],stderr); + for(i=28;i<32 && c[i] && c[i]!=0xff;i+=2) + fputc(c[i],stderr); + fputc('\n',stderr); + } else { + int i; + for(i=0;i<11;i++) + fputc(direntry->name[i],stderr); + fprintf(stderr,"attributes=0x%02x begin=%d size=%d\n", + direntry->attributes, + direntry->begin,direntry->size); + } +} + +static void print_changed_sector(BlockDriverState *bs,int64_t sector_num,const uint8_t *buf) +{ + BDRVVVFATState *s = bs->opaque; + + if(sector_numfirst_sectors_number) + return; + if(sector_numfirst_sectors_number+s->sectors_per_fat*2) { + int first=((sector_num-s->first_sectors_number)%s->sectors_per_fat); + int first_fat_entry=first*0x200/2; + int i; + + fprintf(stderr, "fat:\n"); + for(i=0;i<0x200;i+=2) { + uint16_t* f=array_get(&(s->fat),first_fat_entry+i/2); + if(memcmp(buf+i,f,2)) + fprintf(stderr,"%d(%d->%d) ",first_fat_entry+i/2,*f,*(uint16_t*)(buf+i)); + } + fprintf(stderr, "\n"); + } else if(sector_numfaked_sectors) { + direntry_t* d=(direntry_t*)buf; + int i; + fprintf(stderr, "directory:\n"); + for(i=0;i<0x200/sizeof(direntry_t);i++) { + direntry_t* d_old=(direntry_t*)(s->directory.pointer+0x200*(sector_num-s->first_sectors_number-s->sectors_per_fat*2)+i*sizeof(direntry_t)); + if(memcmp(d+i,d_old,sizeof(direntry_t))) { + fprintf(stderr, "old: "); print_direntry(d_old); + fprintf(stderr, "new: "); print_direntry(d+i); + fprintf(stderr, "\n"); + } + } + } else { + int sec=(sector_num-s->first_sectors_number-2*s->sectors_per_fat); + fprintf(stderr, "\tcluster: %d(+%d sectors)\n",sec/s->sectors_per_cluster,sec%s->sectors_per_cluster); + } +} + +char direntry_is_free(const direntry_t* direntry) +{ + return direntry->name[0]==0 || direntry->name[0]==0xe5; +} + +/* TODO: use this everywhere */ +static inline uint32_t begin_of_direntry(direntry_t* direntry) +{ + return le16_to_cpu(direntry->begin)|(le16_to_cpu(direntry->begin_hi)<<16); +} + +int consistency_check1(BDRVVVFATState *s) { + /* check all mappings */ + int i; + for(i=0;imapping.next;i++) { + mapping_t* mapping=array_get(&(s->mapping),i); + int j; + for(j=mapping->begin;jend-1;j++) + assert(fat_get(s,j)==j+1); + assert(fat_get(s,j)==(0x7fffffff&s->max_fat_value)); + } + return 0; +} + +int consistency_check2(BDRVVVFATState *s) { + /* check fat entries: consecutive fat entries should be mapped in one mapping */ + int i; + /* TODO: i=0 (mappings for direntries have to be sorted) */ + for(i=s->sectors_for_directory/s->sectors_per_cluster;ifat.next-1;i++) { + uint32_t j=fat_get(s,i); + if(j!=i+1 && j!=0 && !fat_eof(s,j)) { + mapping_t* mapping=find_mapping_for_cluster(s,i+1); + assert(mapping->begin==i+1); + } + } + return 0; +} + +int consistency_check3(BDRVVVFATState *s) { + /* check that for each file there is exactly one mapping per cluster */ + int i,count_non_next=0; + for(i=0;imapping.next;i++) { + mapping_t* mapping=array_get(&(s->mapping),i); + /* TODO: when directories are correctly adapted, add them here */ + assert(mapping->beginend); + if(mapping->mode==MODE_NORMAL) { + int j,count=0,count_next=0; + for(j=0;jmapping.next;j++) { + mapping_t* other=array_get(&(s->mapping),j); + if(mapping->beginend&&mapping->end>other->begin) + count++; + if(mapping->end==other->begin) + count_next++; + } + assert(count==1); /* no overlapping mappings */ + assert(count_next==1 || count_next==0); /* every mapping except the last one has a successor */ + if(!count_next) + count_non_next++; + } + } + assert(count_non_next==1); /* only one last mapping */ + return 0; +} + +static inline commit_t* commit_get_next(BDRVVVFATState* s) +{ + commit_t* commit=array_get_next(&(s->commit)); + if((commit->buf=malloc(s->cluster_size))==0) { + /* out of memory */ + s->commit.next--; + return 0; + } + return commit; +} + +int commit_remove(BDRVVVFATState* s,commit_t* commit) +{ + int index=commit-(commit_t*)s->commit.pointer; + free(commit->buf); + if(array_roll(&(s->commit),s->commit.next-1,index,1)) + return -1; + s->commit.next--; + return 0; +} + +/* TODO: the plan for write support: + * + * it seems that the direntries are written first, then the data is committed + * to the free sectors, then fat 1 is updated, then fat2. + * + * Plan: when sectors are written, do the following: + * + * - if they are in a directory, check if the entry has changed. if yes, + * look what has changed (different strategies for name, begin & size). + * + * if it is new (old entry is only 0's or has E5 at the start), create it, + * and also create mapping, but in a special mode "undefined" (TODO), + * because we cannot know which clusters belong to it yet. + * + * if it is zeroed, or has E5 at the start, look if has just moved. If yes, + * copy the entry to the new position. If no, delete the file. + * + * - if they are in data, and the cluster is undefined, add it to the commit + * list. if the cluster is defined (find_mapping), then write it into the + * corresponding file. + * + * If it is the last cluster (TODO: add a function + * fat_get(s,cluster); ), make sure not to write a complete cluster_size. + * + * If the data is in current_cluster, update s->cluster. + * + * - if they are in fat 1, update mappings, look in the commit list + * (assertions!) and if the cluster is now known (or changed from undefined + * state to defined state, like when begin or size changed in a direntry), + * write it. + * + * - if they are in fat 2, make sure they match with current fat. + * + */ + +void mapping_modify_from_direntry(BDRVVVFATState* s,mapping_t* mapping,direntry_t* direntry) +{ + int begin=le16_to_cpu(direntry->begin), + end=begin+le32_to_cpu(direntry->size)/s->cluster_size+1, + i; + mapping->mode = MODE_MODIFIED; + /* TODO: what if begin==0 (size==0)? */ + mapping->begin = begin; + /* TODO: why not just mapping->end = begin+1 ? */ + for(i=begin+1;iend = i; +} + +mapping_t* find_mapping_for_direntry(BDRVVVFATState* s,direntry_t* direntry) +{ + int i; + int dir_index=direntry-((direntry_t*)s->directory.pointer); + + /* TODO: support allocation for new clusters for directories (new/larger directory */ + assert(dir_index<0x200/0x20*s->sectors_for_directory); + + for(i=0;imapping.next;i++) { + mapping_t* mapping=array_get(&(s->mapping),i); + if(mapping->dir_index==dir_index && mapping->offset==0 && + mapping->mode!=MODE_UNDEFINED) + return mapping; + } + return 0; +} + +static inline uint32_t sector2cluster(BDRVVVFATState* s,off_t sector_num) +{ + return (sector_num-s->first_sectors_number-2*s->sectors_per_fat)/s->sectors_per_cluster; +} + +static inline uint32_t sector_offset_in_cluster(BDRVVVFATState* s,off_t sector_num) +{ + return (sector_num-s->first_sectors_number-2*s->sectors_per_fat)%s->sectors_per_cluster; +} + +static commit_t* get_commit_for_cluster(BDRVVVFATState* s,uint32_t cluster_num) +{ + int i; + for(i=0;icommit.next;i++) { + commit_t* commit=array_get(&(s->commit),i); + if(commit->cluster_num==cluster_num) + return commit; + } + return 0; +} + +static inline commit_t* create_or_get_commit_for_sector(BDRVVVFATState* s,off_t sector_num) +{ + int i; + commit_t* commit; + uint32_t cluster_num=sector2cluster(s,sector_num); + + for(i=0;icommit.next;i++) { + commit=array_get(&(s->commit),i); + if(commit->cluster_num==cluster_num) + return commit; + } + + commit=commit_get_next(s); + commit->cluster_num=cluster_num; + /* we can ignore read errors here */ + read_cluster(s,cluster_num); + memcpy(commit->buf,s->cluster,s->cluster_size); + return commit; +} + +static direntry_t* get_direntry_for_mapping(BDRVVVFATState* s,mapping_t* mapping) +{ + if(mapping->mode==MODE_UNDEFINED) + return 0; + if(mapping->dir_index>=0x200/0x20*s->sectors_for_directory) + return 0; + return (direntry_t*)(s->directory.pointer+sizeof(direntry_t)*mapping->dir_index); +} + +static void print_mappings(BDRVVVFATState* s) +{ + int i; + fprintf(stderr,"mapping:\n"); + for(i=0;imapping.next;i++) { + mapping_t* m=array_get(&(s->mapping),i); + direntry_t* d=get_direntry_for_mapping(s,m); + fprintf(stderr,"%02d %d-%d (%d) %s (dir: %d)",i,(int)m->begin,(int)m->end,(int)m->offset,m->filename,m->dir_index); + print_direntry(d); + fprintf(stderr,"\n"); + } + fprintf(stderr,"mappings end.\n"); +} + +/* TODO: statify all functions */ + +/* This function is only meant for file contents. + * It will return an error if used for other sectors. */ +static int write_cluster(BDRVVVFATState* s,uint32_t cluster_num,const uint8_t* buf) +{ + /* sector_offset is the sector_num relative to the first cluster */ + mapping_t* mapping=find_mapping_for_cluster(s,cluster_num); + direntry_t* direntry; + int next_cluster,write_size,last_cluster; + off_t offset; + + /* if this cluster is free, return error */ + next_cluster=fat_get(s,cluster_num); + if(next_cluster<2) + return -1; + + /* TODO: MODE_DIRECTORY */ + if(!mapping || mapping->mode==MODE_UNDEFINED || mapping->mode==MODE_DIRECTORY) + return -1; + direntry=get_direntry_for_mapping(s,mapping); + if(!direntry) + return -2; + + /* get size to write */ + last_cluster=fat_eof(s,next_cluster); + write_size=!last_cluster?s->cluster_size: + (le32_to_cpu(direntry->size)%s->cluster_size); + if(write_size<=0) + return 0; + //fprintf(stderr,"next_cluster: %d (%d), write_size: %d, %d, %d\n",next_cluster,s->max_fat_value-8,write_size,direntry->size,s->cluster_size); + + if(open_file(s,mapping,O_RDWR)) + return -4; + + offset=(cluster_num-mapping->begin+mapping->offset)*s->cluster_size; + if(lseek(s->current_fd,offset,SEEK_SET)!=offset) + return -3; + if(write(s->current_fd,buf,write_size)!=write_size) { + lseek(s->current_fd,0,SEEK_END); + vvfat_close_current_file(s); + return -2; + } + + /* seek to end of file, so it doesn't get truncated */ + if(!last_cluster) + lseek(s->current_fd,0,SEEK_END); + else { + ftruncate(s->current_fd,le32_to_cpu(direntry->size)); + vvfat_close_current_file(s); + } + + /* update s->cluster if necessary */ + if(cluster_num==s->current_cluster && s->cluster!=buf) + memcpy(s->cluster,buf,s->cluster_size); + + return 0; +} + +/* this function returns !=0 on error */ +int mapping_is_consistent(BDRVVVFATState* s,mapping_t* mapping) +{ + direntry_t* direntry=get_direntry_for_mapping(s,mapping); + uint32_t cluster_count=0; + int commit_count=0; /* number of commits for this file (we also write incomplete files; think "append") */ + //fprintf(stderr,"check direntry for %s\n",mapping->filename); + while(mapping) { + int i; + assert(mapping->beginend); + for(i=mapping->begin;iend-1;i++) { + if(i<=0 || fat_get(s,i)!=i+1) { + /*fprintf(stderr,"the fat mapping of %d is not %d, but %d\n", + i,i+1,fat_get(s,i));*/ + return -1; + } + if(get_commit_for_cluster(s,i)) + commit_count++; + } + if(get_commit_for_cluster(s,i)) + commit_count++; + + cluster_count+=mapping->end-mapping->begin; + + i=fat_get(s,mapping->end-1); + if(fat_eof(s,i)) + break; + + mapping=find_mapping_for_cluster(s,i); + if(!mapping) { + //fprintf(stderr,"No mapping found for %d\n",i); + print_mappings(s); + return -2; + } + } + + if(cluster_count!=(le32_to_cpu(direntry->size)+s->cluster_size-1)/s->cluster_size) { + //fprintf(stderr,"cluster_count is %d, but size is %d\n",cluster_count,le32_to_cpu(direntry->size)); + return -3; + } + + if(commit_count==0) + return -4; + + //fprintf(stderr,"okay\n"); + return 0; +} + +/* TODO: remember what comes third, and what's first in this OS: + * FAT, direntry or data. + * If the last written sector is either last in cluster or sector_num+nb_sectors-1, + * - commit every cluster for this file if mapping_is_consistent()==0 + * - if the last written sector is first_action, and last_action=third_action, clear commit + */ + +static int commit_cluster_aux(BDRVVVFATState* s,commit_t* commit) +{ + int result=write_cluster(s,commit->cluster_num,commit->buf); + return result; +} + + +static int commit_cluster(BDRVVVFATState* s,uint32_t cluster_num) +{ + commit_t* commit; + + /* commit the sectors of this cluster */ + commit=get_commit_for_cluster(s,cluster_num); + if(commit) + return commit_cluster_aux(s,commit); + return 0; +} + +/* this function checks the consistency for the direntry which belongs to + * the mapping. if everything is found consistent, the data is committed. + * this returns 0 if no error occurred (even if inconsistencies were found) */ +static inline int commit_data_if_consistent(BDRVVVFATState* s,mapping_t* mapping,write_action_t action) +{ + direntry_t* direntry; + + if(!mapping) + return 0; + + //fprintf(stderr,"7\n"); +#define d(x) fprintf(stderr,#x "\n") + direntry=get_direntry_for_mapping(s,mapping); + + //d(8); + + assert(action==WRITE_FAT || action==WRITE_DIRENTRY || action==WRITE_DATA); + + //d(9); + //fprintf(stderr,"mapping: 0x%x s=0x%x\n",(uint32_t)mapping,(uint32_t)s); + /*fprintf(stderr,"commit? file=%s, action=%s\n", + mapping->filename,action==WRITE_FAT?"fat":action==WRITE_DIRENTRY?"direntry":"data");*/ + + //d(10); + if(s->action[2]==WRITE_UNDEFINED) { + int i; + for(i=2;i>0 && s->action[i-1]==WRITE_UNDEFINED;i--); + if(i>0 && action!=s->action[i-1]) + s->action[i]=action; + assert(i<2 || s->action[0]!=s->action[2]); + } + //d(11); + + if(mapping_is_consistent(s,mapping)==0) { + uint32_t cluster_num=begin_of_direntry(direntry); + off_t remaining_bytes=le32_to_cpu(direntry->size); + //fprintf(stderr,"the data for %s was found consistent\n",mapping->filename); + while(remaining_bytes>0) { + commit_t* commit=get_commit_for_cluster(s,cluster_num); + if(!commit) + continue; + + //fprintf(stderr,"commit_cluster %d (%d), remaining: %d\n",cluster_num,s->max_fat_value-15,(int)remaining_bytes); + assert(cluster_num>1); + assert(cluster_nummax_fat_value-15); + if(commit_cluster(s,cluster_num)) { + fprintf(stderr,"error committing cluster %d\n",cluster_num); + return -1; + } + cluster_num=fat_get(s,cluster_num); + remaining_bytes-=s->cluster_size; + /* TODO: if(action==s->action[2]) { + commit_t* commit=get_commit_for_cluster(s,cluster_num); + commit_remove(s,commit); + } */ + } + } + //print_mappings(s); + //fprintf(stderr,"finish vvfat_write\n"); + return 0; +} + +static int vvfat_write(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + BDRVVVFATState *s = bs->opaque; + int i; + + /* fprintf(stderr,"vvfat_write %d+%d (%s)\n",(int)sector_num,nb_sectors, + (sector_num>=s->faked_sectors?"data": + (sector_num>=s->first_sectors_number+2*s->sectors_per_fat?"directory": + (sector_num>=s->first_sectors_number+s->sectors_per_fat?"fat 2": + (sector_num>=s->first_sectors_number?"fat 1":"boot sector"))))); */ + + for(i=0;ifirst_sectors_number) { + /* change the bootsector or partition table? no! */ + return -1; + } else if(sector_numfirst_sectors_number+s->sectors_per_fat) { + /* FAT 1 */ + int fat_entries_per_cluster=s->cluster_size*8/s->fat_type; + int first_cluster=(sector_num-s->first_sectors_number)*fat_entries_per_cluster,i; + mapping_t* mapping=0; + + /* write back */ + memcpy(s->fat.pointer+0x200*(sector_num-s->first_sectors_number), + buf,0x200); + + /* for each changed FAT entry, */ + for(i=0;isectors_for_directory/s->sectors_per_cluster) + continue; + + new_value=fat_get(s,first_cluster+i); + + /* check the current fat entry */ + if(new_value<2 || (new_value>=s->max_fat_value-0xf && !fat_eof(s,new_value))) { + /* free, reserved or bad cluster */ + mapping=find_mapping_for_cluster(s,first_cluster+i); + //assert(!mapping || mapping->mode==MODE_DELETED); + if(mapping && mapping->mode==MODE_DELETED && + first_cluster+i+1==mapping->end) + array_remove(&(s->mapping),mapping-(mapping_t*)s->mapping.pointer); + mapping=0; + continue; + } + + /* get the mapping for the current entry */ + if(!mapping || mapping->begin>new_value || mapping->end<=new_value) { + mapping=find_mapping_for_cluster(s,first_cluster+i); + } + + print_mappings(s); + fprintf(stderr,"fat_get(%d)=%d\n",first_cluster+i,new_value); + /* TODO: what if there's no mapping? this is valid. */ + /* TODO: refactor the rest of this clause so it can be called when the direntry changes, too */ + assert(mapping); + + if(new_value>1 && new_valuemax_fat_value-0xf) { + /* the cluster new_value points to is valid */ + + if(first_cluster+i+1==new_value) { + /* consecutive cluster */ + if(mapping->end<=new_value) + mapping->end=new_value+1; + } else { + mapping_t* next_mapping; + + /* the current mapping ends here */ + mapping->end=first_cluster+i+1; + + /* the next mapping */ + next_mapping=find_mapping_for_cluster(s,new_value); + if(next_mapping) { + assert(mapping!=next_mapping); + /* assert next mapping's filename is the same */ + assert(next_mapping->filename==mapping->filename); + assert(next_mapping->dir_index==mapping->dir_index); + /* assert next mapping is MODIFIED or UNDEFINED */ + assert(next_mapping->mode==MODE_MODIFIED || next_mapping->mode==MODE_UNDEFINED); + } else { + int index=find_mapping_for_cluster_aux(s,new_value,0,s->mapping.next); + next_mapping=array_insert(&(s->mapping),index,1); + next_mapping->filename=mapping->filename; + next_mapping->dir_index=mapping->dir_index; + next_mapping->mode=MODE_MODIFIED; + next_mapping->begin=0; + } + /* adjust offset of next mapping */ + next_mapping->offset=mapping->offset+mapping->end-mapping->begin; + /* set begin and possible end */ + if(next_mapping->begin!=new_value) { + next_mapping->begin=new_value; + next_mapping->end=new_value+1; + } + if(commit_data_if_consistent(s,mapping,WRITE_FAT)) + return -4; + mapping=0; + } + } else if(fat_eof(s,new_value)) { + /* the last cluster of the file */ + mapping->end=first_cluster+i+1; + if(commit_data_if_consistent(s,mapping,WRITE_FAT)) + return -4; + mapping=0; + } + } + } else if(sector_numfirst_sectors_number+2*s->sectors_per_fat) { + /* FAT 2: check if it is the same as FAT 1 */ + if(memcmp(array_get(&(s->fat),sector_num-s->first_sectors_number),buf,0x200)) + return -1; /* mismatch */ + } else if(sector_numfaked_sectors) { + /* direntry */ + /* - if they are in a directory, check if the entry has changed. + * if yes, look what has changed (different strategies for name, + * begin & size). + * + * if it is new (old entry is only 0's or has E5 at the start), + * create it, and also create mapping, but in a special mode + * "undefined", because we cannot know which clusters belong + * to it yet. + * + * if it is zeroed, or has E5 at the start, look if has just + * moved. If yes, copy the entry to the new position. If no, + * delete the file. + */ + mapping_t* dir_mapping=find_mapping_for_cluster(s,sector2cluster(s,sector_num)); + direntry_t *original=array_get(&(s->directory),sector_num-s->first_sectors_number-2*s->sectors_per_fat); + direntry_t *new_=(direntry_t*)buf; + int first_dir_index=(sector_num-s->first_sectors_number-2*s->sectors_per_fat)*0x200/0x20; + int j; + +#if 0 + fprintf(stderr,"direntry: consistency check\n"); + + if(s->commit.next==0) { + consistency_check1(s); + consistency_check2(s); + consistency_check3(s); + } +#endif + + assert(sizeof(direntry_t)==0x20); + + for(j=0;j<0x200/0x20;j++) { + //fprintf(stderr,"compare direntry %d: 0x%x,0x%x\n",j,(uint32_t)original+j,(uint32_t)new_+j); + if(memcmp(original+j,new_+j,sizeof(direntry_t))) { + //fprintf(stderr,"different\n"); + /* TODO: in create_short_filename, 0xe5->0x05 is not yet handled! */ + if(direntry_is_free(original+j)) { + mapping_t* mapping; + char buffer[4096]; + int fd,i; + + if(new_[j].attributes==0xf) + continue; /* long entry */ + + print_mappings(s); + //fprintf(stderr,"sector: %d cluster: %d\n",(int)sector_num,(int)sector2cluster(s,sector_num)); + + /* construct absolute path */ + strncpy(buffer,dir_mapping->filename,4096); + i=strlen(buffer); + if(i+2>=4096) + return -1; + buffer[i]='/'; + if(long2unix_name(buffer+i+1,4096-i-1,new_+j)) + return -2; + + /* new file/directory */ + if(new_[j].attributes&0x10) { +#ifdef _WIN32 +#define SEVENFIVEFIVE +#else +#define SEVENFIVEFIVE ,0755 +#endif + if(mkdir(buffer SEVENFIVEFIVE)) + return -3; + /* TODO: map direntry.begin as directory, together with new array_t direntries */ + assert(0); + } else { + fd=open(buffer,O_CREAT|O_EXCL,0644); + if(!fd) + return -3; + close(fd); + } + + /* create mapping */ + i=find_mapping_for_cluster_aux(s,begin_of_direntry(new_+j),0,s->mapping.next); + mapping=array_insert(&(s->mapping),i,1); + mapping->filename=strdup(buffer); + mapping->offset=0; + /* back pointer to direntry */ + mapping->dir_index=first_dir_index+j; + /* set mode to modified */ + mapping->mode=MODE_MODIFIED; + /* set begin to direntry.begin */ + mapping->begin=begin_of_direntry(new_+j); + /* set end to begin+1 */ + mapping->end=mapping->begin+1; + /* commit file contents */ + if(commit_data_if_consistent(s,mapping,WRITE_DIRENTRY)) { + fprintf(stderr,"error committing file contents for new file %s!\n",buffer); + return -4; + } + } else if(direntry_is_free(new_+j)) { + assert(0); + /* TODO: delete file */ + /* TODO: write direntry */ + /* TODO: modify mapping: set mode=deleted */ + } else { + /* modified file */ + mapping_t* mapping=0; + /* if direntry.begin has changed, + * set mode to modified, + * adapt begin, + * adapt end */ + /* TODO: handle rename */ + assert(!memcmp(new_[j].name,original[j].name,11)); + //fprintf(stderr,"1\n"); + if(new_[j].begin!=original[j].begin || new_[j].size/s->cluster_size!=original[j].size/s->cluster_size) { + //fprintf(stderr,"2\n"); + mapping = find_mapping_for_direntry(s,original+j); + //fprintf(stderr,"3\n"); + if(!mapping) /* this should never happen! */ + return -2; + mapping_modify_from_direntry(s,mapping,new_+j); + //fprintf(stderr,"4\n"); + if(commit_data_if_consistent(s,mapping,WRITE_DIRENTRY)) { + fprintf(stderr,"big error\n"); + return -4; + } + } + /* TODO: handle modified times and other attributes */ + + //fprintf(stderr,"5: mapping=0x%x, s=0x%x, s->mapping.pointer=0x%x\n",(uint32_t)mapping,(uint32_t)s,(uint32_t)s->mapping.pointer); + //fprintf(stderr,"6\n"); + } + } + } + /* write back direntries */ + memcpy(original,new_,0x200); + } else { + /* data */ + off_t sector=sector_num-s->first_sectors_number-2*s->sectors_per_fat; + off_t cluster=sector/s->sectors_per_cluster; + mapping_t* mapping=find_mapping_for_cluster(s,cluster); + if(mapping && mapping->mode==MODE_DELETED) + return -3; /* this is an error: no writes to these clusters before committed */ + { + /* as of yet, undefined: put into commits */ + commit_t* commit=create_or_get_commit_for_sector(s,sector_num); + + if(!commit) + return -1; /* out of memory */ + memcpy(commit->buf+0x200*sector_offset_in_cluster(s,sector_num),buf,0x200); + + //fprintf(stderr,"mapping: 0x%x\n",(uint32_t)mapping); + if(commit_data_if_consistent(s,mapping,WRITE_DATA)) + return -4; + } + } + } + return 0; +} + +static void vvfat_close(BlockDriverState *bs) +{ + BDRVVVFATState *s = bs->opaque; + + vvfat_close_current_file(s); + array_free(&(s->fat)); + array_free(&(s->directory)); + array_free(&(s->mapping)); + if(s->cluster) + free(s->cluster); +} + +BlockDriver bdrv_vvfat = { + "vvfat", + sizeof(BDRVVVFATState), + vvfat_probe, + vvfat_open, + vvfat_read, + vvfat_write, + vvfat_close, +}; + + diff --git a/qemu/block.c b/qemu/block.c index 8baa3b8..18eaf46 100644 --- a/qemu/block.c +++ b/qemu/block.c @@ -106,26 +106,29 @@ static BlockDriver *find_image_format(const char *filename) size_t bufsize = 1024; fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); - if (fd < 0) - return NULL; + if (fd < 0) { + buf = NULL; + ret = 0; + } else { #ifdef DIOCGSECTORSIZE - { - unsigned int sectorsize = 512; - if (!ioctl(fd, DIOCGSECTORSIZE, §orsize) && - sectorsize > bufsize) - bufsize = sectorsize; - } + { + unsigned int sectorsize = 512; + if (!ioctl(fd, DIOCGSECTORSIZE, §orsize) && + sectorsize > bufsize) + bufsize = sectorsize; + } #endif - buf = malloc(bufsize); - if (!buf) - return NULL; - ret = read(fd, buf, bufsize); - if (ret < 0) { + buf = qemu_malloc(bufsize); + if (!buf) + return NULL; + ret = read(fd, buf, bufsize); + if (ret < 0) { + close(fd); + qemu_free(buf); + return NULL; + } close(fd); - free(buf); - return NULL; } - close(fd); drv = NULL; score_max = 0; @@ -136,7 +139,7 @@ static BlockDriver *find_image_format(const char *filename) drv = drv1; } } - free(buf); + qemu_free(buf); return drv; } @@ -154,7 +157,7 @@ int bdrv_open2(BlockDriverState *bs, const char *filename, int snapshot, bs->read_only = 0; bs->is_temporary = 0; bs->encrypted = 0; - + if (snapshot) { BlockDriverState *bs1; int64_t total_size; @@ -183,7 +186,7 @@ int bdrv_open2(BlockDriverState *bs, const char *filename, int snapshot, filename = tmp_filename; bs->is_temporary = 1; } - + pstrcpy(bs->filename, sizeof(bs->filename), filename); if (!drv) { drv = find_image_format(filename); @@ -653,4 +656,5 @@ void bdrv_init(void) bdrv_register(&bdrv_dmg); bdrv_register(&bdrv_bochs); bdrv_register(&bdrv_vpc); + bdrv_register(&bdrv_vvfat); } diff --git a/qemu/configure b/qemu/configure index 05e8a59..b45408a 100755 --- a/qemu/configure +++ b/qemu/configure @@ -84,6 +84,7 @@ linux="no" kqemu="no" kernel_path="" cocoa="no" +check_gfx="yes" # OS specific targetos=`uname -s` @@ -152,6 +153,8 @@ for opt do ;; --cc=*) cc=`echo $opt | cut -d '=' -f 2` ;; + --host-cc=*) host_cc=`echo $opt | cut -d '=' -f 2` + ;; --make=*) make=`echo $opt | cut -d '=' -f 2` ;; --extra-cflags=*) CFLAGS="${opt#--extra-cflags=}" @@ -186,6 +189,8 @@ for opt do ;; --enable-cocoa) cocoa="yes" ; sdl="no" ;; + --disable-gfx-check) check_gfx="no" + ;; esac done @@ -210,11 +215,13 @@ fi if test -z "$target_list" ; then # these targets are portable - target_list="i386-softmmu ppc-softmmu sparc-softmmu x86_64-softmmu" + target_list="i386-softmmu ppc-softmmu sparc-softmmu x86_64-softmmu mips-softmmu" # the following are Linux specific if [ "$linux" = "yes" ] ; then target_list="i386-user arm-user armeb-user sparc-user ppc-user $target_list" fi +else + target_list=$(echo "$target_list" | sed -e 's/,/ /g') fi if test -z "$cross_prefix" ; then @@ -244,6 +251,12 @@ fi fi +# host long bits test +hostlongbits="32" +if test "$cpu" = "sparc64" -o "$cpu" = "ia64" -o "$cpu" = "x86_64" -o "$cpu" = "alpha"; then + hostlongbits="64" +fi + # check gcc options support cat > $TMPC <> $config_mak echo "#define WORDS_BIGENDIAN 1" >> $config_h fi +echo "#define HOST_LONG_BITS $hostlongbits" >> $config_h if test "$mingw32" = "yes" ; then echo "CONFIG_WIN32=yes" >> $config_mak echo "#define CONFIG_WIN32 1" >> $config_h @@ -594,6 +611,8 @@ target_bigendian="no" [ "$target_cpu" = "sparc" ] && target_bigendian=yes [ "$target_cpu" = "sparc64" ] && target_bigendian=yes [ "$target_cpu" = "ppc" ] && target_bigendian=yes +[ "$target_cpu" = "ppc64" ] && target_bigendian=yes +[ "$target_cpu" = "mips" ] && target_bigendian=yes target_softmmu="no" if expr $target : '.*-softmmu' > /dev/null ; then target_softmmu="yes" @@ -603,6 +622,14 @@ if expr $target : '.*-user' > /dev/null ; then target_user_only="yes" fi +if test "$target_user_only" = "no" -a "$check_gfx" = "yes" \ + -a "$sdl" = "no" -a "$cocoa" = "no" ; then + echo "ERROR: QEMU requires SDL or Cocoa for graphical output" + echo "To build QEMU with graphical output configure with --disable-gfx-check" + echo "Note that this will disable all output from the virtual graphics card." + exit 1; +fi + #echo "Creating $config_mak, $config_h and $target_dir/Makefile" mkdir -p $target_dir @@ -650,6 +677,11 @@ elif test "$target_cpu" = "ppc" ; then echo "TARGET_ARCH=ppc" >> $config_mak echo "#define TARGET_ARCH \"ppc\"" >> $config_h echo "#define TARGET_PPC 1" >> $config_h +elif test "$target_cpu" = "ppc64" ; then + echo "TARGET_ARCH=ppc64" >> $config_mak + echo "#define TARGET_ARCH \"ppc64\"" >> $config_h + echo "#define TARGET_PPC 1" >> $config_h + echo "#define TARGET_PPC64 1" >> $config_h elif test "$target_cpu" = "x86_64" ; then echo "TARGET_ARCH=x86_64" >> $config_mak echo "#define TARGET_ARCH \"x86_64\"" >> $config_h @@ -658,6 +690,10 @@ elif test "$target_cpu" = "x86_64" ; then if test $kqemu = "yes" -a "$target_softmmu" = "yes" -a $cpu = "x86_64" ; then echo "#define USE_KQEMU 1" >> $config_h fi +elif test "$target_cpu" = "mips" ; then + echo "TARGET_ARCH=mips" >> $config_mak + echo "#define TARGET_ARCH \"mips\"" >> $config_h + echo "#define TARGET_MIPS 1" >> $config_h else echo "Unsupported target CPU" exit 1 diff --git a/qemu/cpu-all.h b/qemu/cpu-all.h index 448f2c5..d50f5a2 100644 --- a/qemu/cpu-all.h +++ b/qemu/cpu-all.h @@ -617,6 +617,13 @@ void page_unprotect_range(uint8_t *data, unsigned long data_size); #define cpu_gen_code cpu_ppc_gen_code #define cpu_signal_handler cpu_ppc_signal_handler +#elif defined(TARGET_MIPS) +#define CPUState CPUMIPSState +#define cpu_init cpu_mips_init +#define cpu_exec cpu_mips_exec +#define cpu_gen_code cpu_mips_gen_code +#define cpu_signal_handler cpu_mips_signal_handler + #else #error unsupported target CPU diff --git a/qemu/cpu-defs.h b/qemu/cpu-defs.h index 042b1e8..83d1748 100644 --- a/qemu/cpu-defs.h +++ b/qemu/cpu-defs.h @@ -29,12 +29,6 @@ #error TARGET_LONG_BITS must be defined before including this header #endif -#if defined(__alpha__) || defined (__ia64__) || defined(__x86_64__) -#define HOST_LONG_BITS 64 -#else -#define HOST_LONG_BITS 32 -#endif - #ifndef TARGET_PHYS_ADDR_BITS #if TARGET_LONG_BITS >= HOST_LONG_BITS #define TARGET_PHYS_ADDR_BITS TARGET_LONG_BITS @@ -74,9 +68,9 @@ typedef uint64_t target_phys_addr_t; #define HOST_LONG_SIZE (HOST_LONG_BITS / 8) -#define EXCP_INTERRUPT 256 /* async interruption */ -#define EXCP_HLT 257 /* hlt instruction reached */ -#define EXCP_DEBUG 258 /* cpu stopped after a breakpoint or singlestep */ +#define EXCP_INTERRUPT 0x10000 /* async interruption */ +#define EXCP_HLT 0x10001 /* hlt instruction reached */ +#define EXCP_DEBUG 0x10002 /* cpu stopped after a breakpoint or singlestep */ #define MAX_BREAKPOINTS 32 diff --git a/qemu/cpu-exec.c b/qemu/cpu-exec.c index d414e33..e7f4322 100644 --- a/qemu/cpu-exec.c +++ b/qemu/cpu-exec.c @@ -47,6 +47,9 @@ void cpu_loop_exit(void) longjmp(env->jmp_env, 1); } #endif +#ifndef TARGET_SPARC +#define reg_T2 +#endif /* exit the current TB from a signal handler. The host registers are restored in a state compatible with the CPU emulator @@ -74,8 +77,12 @@ void cpu_resume_from_signal(CPUState *env1, void *puc) int cpu_exec(CPUState *env1) { - int saved_T0, saved_T1, saved_T2; + int saved_T0, saved_T1; +#if defined(reg_T2) + int saved_T2; +#endif CPUState *saved_env; +#if defined(TARGET_I386) #ifdef reg_EAX int saved_EAX; #endif @@ -100,6 +107,11 @@ int cpu_exec(CPUState *env1) #ifdef reg_EDI int saved_EDI; #endif +#elif defined(TARGET_SPARC) +#if defined(reg_REGWPTR) + uint32_t *saved_regwptr; +#endif +#endif #ifdef __sparc__ int saved_i7, tmp_T0; #endif @@ -115,7 +127,9 @@ int cpu_exec(CPUState *env1) env = env1; saved_T0 = T0; saved_T1 = T1; +#if defined(reg_T2) saved_T2 = T2; +#endif #ifdef __sparc__ /* we also save i7 because longjmp may not restore it */ asm volatile ("mov %%i7, %0" : "=r" (saved_i7)); @@ -164,7 +178,11 @@ int cpu_exec(CPUState *env1) env->cpsr = psr & ~CACHED_CPSR_BITS; } #elif defined(TARGET_SPARC) +#if defined(reg_REGWPTR) + saved_regwptr = REGWPTR; +#endif #elif defined(TARGET_PPC) +#elif defined(TARGET_MIPS) #else #error unsupported target CPU #endif @@ -203,6 +221,8 @@ int cpu_exec(CPUState *env1) env->exception_next_eip, 0); #elif defined(TARGET_PPC) do_interrupt(env); +#elif defined(TARGET_MIPS) + do_interrupt(env); #elif defined(TARGET_SPARC) do_interrupt(env->exception_index); #endif @@ -284,6 +304,19 @@ int cpu_exec(CPUState *env1) env->interrupt_request &= ~CPU_INTERRUPT_TIMER; } } +#elif defined(TARGET_MIPS) + if ((interrupt_request & CPU_INTERRUPT_HARD) && + (env->CP0_Status & (1 << CP0St_IE)) && + (env->CP0_Cause & 0x0000FF00) && + !(env->hflags & MIPS_HFLAG_EXL) && + !(env->hflags & MIPS_HFLAG_ERL) && + !(env->hflags & MIPS_HFLAG_DM)) { + /* Raise it */ + env->exception_index = EXCP_EXT_INTERRUPT; + env->error_code = 0; + do_interrupt(env); + env->interrupt_request &= ~CPU_INTERRUPT_HARD; + } #elif defined(TARGET_SPARC) if ((interrupt_request & CPU_INTERRUPT_HARD) && (env->psret != 0)) { @@ -354,9 +387,13 @@ int cpu_exec(CPUState *env1) cpu_dump_state(env, logfile, fprintf, 0); env->cpsr &= ~CACHED_CPSR_BITS; #elif defined(TARGET_SPARC) - cpu_dump_state (env, logfile, fprintf, 0); + REGWPTR = env->regbase + (env->cwp * 16); + env->regwptr = REGWPTR; + cpu_dump_state(env, logfile, fprintf, 0); #elif defined(TARGET_PPC) cpu_dump_state(env, logfile, fprintf, 0); +#elif defined(TARGET_MIPS) + cpu_dump_state(env, logfile, fprintf, 0); #else #error unsupported target CPU #endif @@ -376,7 +413,11 @@ int cpu_exec(CPUState *env1) cs_base = 0; pc = env->regs[15]; #elif defined(TARGET_SPARC) - flags = 0; +#ifdef TARGET_SPARC64 + flags = (env->pstate << 2) | ((env->lsu & (DMMU_E | IMMU_E)) >> 2); +#else + flags = env->psrs | ((env->mmuregs[0] & (MMU_E | MMU_NF)) << 1); +#endif cs_base = env->npc; pc = env->pc; #elif defined(TARGET_PPC) @@ -384,6 +425,10 @@ int cpu_exec(CPUState *env1) (msr_se << MSR_SE) | (msr_le << MSR_LE); cs_base = 0; pc = env->nip; +#elif defined(TARGET_MIPS) + flags = env->hflags & MIPS_HFLAGS_TMASK; + cs_base = NULL; + pc = env->PC; #else #error unsupported CPU #endif @@ -657,7 +702,11 @@ int cpu_exec(CPUState *env1) env->cpsr = compute_cpsr(); /* XXX: Save/restore host fpu exception state?. */ #elif defined(TARGET_SPARC) +#if defined(reg_REGWPTR) + REGWPTR = saved_regwptr; +#endif #elif defined(TARGET_PPC) +#elif defined(TARGET_MIPS) #else #error unsupported target CPU #endif @@ -666,7 +715,9 @@ int cpu_exec(CPUState *env1) #endif T0 = saved_T0; T1 = saved_T1; +#if defined(reg_T2) T2 = saved_T2; +#endif env = saved_env; return ret; } @@ -907,6 +958,57 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address, /* never comes here */ return 1; } + +#elif defined (TARGET_MIPS) +static inline int handle_cpu_signal(unsigned long pc, unsigned long address, + int is_write, sigset_t *old_set, + void *puc) +{ + TranslationBlock *tb; + int ret; + + if (cpu_single_env) + env = cpu_single_env; /* XXX: find a correct solution for multithread */ +#if defined(DEBUG_SIGNAL) + printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n", + pc, address, is_write, *(unsigned long *)old_set); +#endif + /* XXX: locking issue */ + if (is_write && page_unprotect(address, pc, puc)) { + return 1; + } + + /* see if it is an MMU fault */ + ret = cpu_ppc_handle_mmu_fault(env, address, is_write, msr_pr, 0); + if (ret < 0) + return 0; /* not an MMU fault */ + if (ret == 0) + return 1; /* the MMU fault was handled without causing real CPU fault */ + + /* now we have a real cpu fault */ + tb = tb_find_pc(pc); + if (tb) { + /* the PC is inside the translated code. It means that we have + a virtual CPU fault */ + cpu_restore_state(tb, env, pc, puc); + } + if (ret == 1) { +#if 0 + printf("PF exception: NIP=0x%08x error=0x%x %p\n", + env->nip, env->error_code, tb); +#endif + /* we restore the process signal mask as the sigreturn should + do it (XXX: use sigsetjmp) */ + sigprocmask(SIG_SETMASK, old_set, NULL); + do_raise_exception_err(env->exception_index, env->error_code); + } else { + /* activate soft MMU for this block */ + cpu_resume_from_signal(env, puc); + } + /* never comes here */ + return 1; +} + #else #error unsupported target CPU #endif @@ -1178,6 +1280,23 @@ int cpu_signal_handler(int host_signum, struct siginfo *info, void *puc) &uc->uc_sigmask, puc); } +#elif defined(__s390__) + +int cpu_signal_handler(int host_signum, struct siginfo *info, + void *puc) +{ + struct ucontext *uc = puc; + unsigned long pc; + int is_write; + + pc = uc->uc_mcontext.psw.addr; + /* XXX: compute is_write */ + is_write = 0; + return handle_cpu_signal(pc, (unsigned long)info->si_addr, + is_write, + &uc->uc_sigmask, puc); +} + #else #error host CPU specific signal handler needed diff --git a/qemu/dis-asm.h b/qemu/dis-asm.h index 8c4bb5d..880baf1 100644 --- a/qemu/dis-asm.h +++ b/qemu/dis-asm.h @@ -126,6 +126,24 @@ enum bfd_architecture #define bfd_mach_h8300h 2 #define bfd_mach_h8300s 3 bfd_arch_powerpc, /* PowerPC */ +#define bfd_mach_ppc 0 +#define bfd_mach_ppc64 1 +#define bfd_mach_ppc_403 403 +#define bfd_mach_ppc_403gc 4030 +#define bfd_mach_ppc_505 505 +#define bfd_mach_ppc_601 601 +#define bfd_mach_ppc_602 602 +#define bfd_mach_ppc_603 603 +#define bfd_mach_ppc_ec603e 6031 +#define bfd_mach_ppc_604 604 +#define bfd_mach_ppc_620 620 +#define bfd_mach_ppc_630 630 +#define bfd_mach_ppc_750 750 +#define bfd_mach_ppc_860 860 +#define bfd_mach_ppc_a35 35 +#define bfd_mach_ppc_rs64ii 642 +#define bfd_mach_ppc_rs64iii 643 +#define bfd_mach_ppc_7400 7400 bfd_arch_rs6000, /* IBM RS/6000 */ bfd_arch_hppa, /* HP PA RISC */ bfd_arch_d10v, /* Mitsubishi D10V */ @@ -404,6 +422,8 @@ extern int generic_symbol_at_address bfd_vma bfd_getl32 (const bfd_byte *addr); bfd_vma bfd_getb32 (const bfd_byte *addr); +bfd_vma bfd_getl16 (const bfd_byte *addr); +bfd_vma bfd_getb16 (const bfd_byte *addr); typedef enum bfd_boolean {false, true} boolean; #endif /* ! defined (DIS_ASM_H) */ diff --git a/qemu/disas.c b/qemu/disas.c index ded2c6d..aea8cfe 100644 --- a/qemu/disas.c +++ b/qemu/disas.c @@ -108,6 +108,24 @@ bfd_vma bfd_getb32 (const bfd_byte *addr) return (bfd_vma) v; } +bfd_vma bfd_getl16 (const bfd_byte *addr) +{ + unsigned long v; + + v = (unsigned long) addr[0]; + v |= (unsigned long) addr[1] << 8; + return (bfd_vma) v; +} + +bfd_vma bfd_getb16 (const bfd_byte *addr) +{ + unsigned long v; + + v = (unsigned long) addr[0] << 24; + v |= (unsigned long) addr[1] << 16; + return (bfd_vma) v; +} + #ifdef TARGET_ARM static int print_insn_thumb1(bfd_vma pc, disassemble_info *info) @@ -155,10 +173,20 @@ void target_disas(FILE *out, target_ulong code, target_ulong size, int flags) print_insn = print_insn_arm; #elif defined(TARGET_SPARC) print_insn = print_insn_sparc; +#ifdef TARGET_SPARC64 + disasm_info.mach = bfd_mach_sparc_v9b; +#endif #elif defined(TARGET_PPC) if (cpu_single_env->msr[MSR_LE]) disasm_info.endian = BFD_ENDIAN_LITTLE; +#ifdef TARGET_PPC64 + disasm_info.mach = bfd_mach_ppc64; +#else + disasm_info.mach = bfd_mach_ppc; +#endif print_insn = print_insn_ppc; +#elif defined(TARGET_MIPS) + print_insn = print_insn_big_mips; #else fprintf(out, "0x" TARGET_FMT_lx ": Asm output not supported on this arch\n", code); @@ -219,6 +247,10 @@ void disas(FILE *out, void *code, unsigned long size) print_insn = print_insn_sparc; #elif defined(__arm__) print_insn = print_insn_arm; +#elif defined(__MIPSEB__) + print_insn = print_insn_big_mips; +#elif defined(__MIPSEL__) + print_insn = print_insn_little_mips; #else fprintf(out, "0x%lx: Asm output not supported on this arch\n", (long) code); @@ -328,7 +360,14 @@ void monitor_disas(target_ulong pc, int nb_insn, int is_physical, int flags) #elif defined(TARGET_SPARC) print_insn = print_insn_sparc; #elif defined(TARGET_PPC) +#ifdef TARGET_PPC64 + disasm_info.mach = bfd_mach_ppc64; +#else + disasm_info.mach = bfd_mach_ppc; +#endif print_insn = print_insn_ppc; +#elif defined(TARGET_MIPS) + print_insn = print_insn_big_mips; #else term_printf("0x" TARGET_FMT_lx ": Asm output not supported on this arch\n", pc); diff --git a/qemu/dyngen-exec.h b/qemu/dyngen-exec.h index 2dc948b..946347d 100644 --- a/qemu/dyngen-exec.h +++ b/qemu/dyngen-exec.h @@ -218,6 +218,7 @@ extern int __op_jmp0, __op_jmp1, __op_jmp2, __op_jmp3; #endif #ifdef __s390__ #define EXIT_TB() asm volatile ("br %r14") +#define GOTO_LABEL_PARAM(n) asm volatile ("b " ASM_NAME(__op_gen_label) #n) #endif #ifdef __alpha__ #define EXIT_TB() asm volatile ("ret") diff --git a/qemu/elf.h b/qemu/elf.h index b5dca4b..0dc82e7 100644 --- a/qemu/elf.h +++ b/qemu/elf.h @@ -31,11 +31,29 @@ typedef int64_t Elf64_Sxword; #define PT_LOPROC 0x70000000 #define PT_HIPROC 0x7fffffff #define PT_MIPS_REGINFO 0x70000000 +#define PT_MIPS_OPTIONS 0x70000001 /* Flags in the e_flags field of the header */ +/* MIPS architecture level. */ +#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ +#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ +#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ +#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ +#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ +#define EF_MIPS_ARCH_32 0x50000000 /* MIPS32 code. */ +#define EF_MIPS_ARCH_64 0x60000000 /* MIPS64 code. */ + +/* The ABI of a file. */ +#define EF_MIPS_ABI_O32 0x00001000 /* O32 ABI. */ +#define EF_MIPS_ABI_O64 0x00002000 /* O32 extended for 64 bit. */ + #define EF_MIPS_NOREORDER 0x00000001 #define EF_MIPS_PIC 0x00000002 #define EF_MIPS_CPIC 0x00000004 +#define EF_MIPS_ABI2 0x00000020 +#define EF_MIPS_OPTIONS_FIRST 0x00000080 +#define EF_MIPS_32BITMODE 0x00000100 +#define EF_MIPS_ABI 0x0000f000 #define EF_MIPS_ARCH 0xf0000000 /* These constants define the different elf file types */ diff --git a/qemu/exec-all.h b/qemu/exec-all.h index 4579da9..5e809b0 100644 --- a/qemu/exec-all.h +++ b/qemu/exec-all.h @@ -28,7 +28,7 @@ #define tostring(s) #s #endif -#if GCC_MAJOR < 3 +#if __GNUC__ < 3 #define __builtin_expect(x, n) (x) #endif @@ -131,7 +131,7 @@ int tlb_set_page(CPUState *env, target_ulong vaddr, #elif defined(__powerpc__) #define CODE_GEN_BUFFER_SIZE (6 * 1024 * 1024) #else -#define CODE_GEN_BUFFER_SIZE (8 * 1024 * 1024) +#define CODE_GEN_BUFFER_SIZE (16 * 1024 * 1024) #endif //#define CODE_GEN_BUFFER_SIZE (128 * 1024) @@ -582,6 +582,8 @@ static inline target_ulong get_phys_addr_code(CPUState *env, target_ulong addr) is_user = ((env->hflags & HF_CPL_MASK) == 3); #elif defined (TARGET_PPC) is_user = msr_pr; +#elif defined (TARGET_MIPS) + is_user = ((env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM); #elif defined (TARGET_SPARC) is_user = (env->psrs == 0); #else @@ -613,7 +615,8 @@ static inline int kqemu_is_ok(CPUState *env) (env->eflags & IOPL_MASK) != IOPL_MASK && (env->cr[0] & CR0_PE_MASK) && (env->eflags & IF_MASK) && - !(env->eflags & VM_MASK)); + !(env->eflags & VM_MASK) && + (env->ldt.limit == 0 || env->ldt.limit == 0x27)); } #endif diff --git a/qemu/exec.c b/qemu/exec.c index 08914ae..26abb0f 100644 --- a/qemu/exec.c +++ b/qemu/exec.c @@ -51,6 +51,15 @@ #define MMAP_AREA_START 0x00000000 #define MMAP_AREA_END 0xa8000000 +#if defined(TARGET_SPARC64) +#define TARGET_PHYS_ADDR_SPACE_BITS 41 +#elif defined(TARGET_PPC64) +#define TARGET_PHYS_ADDR_SPACE_BITS 42 +#else +/* Note: for compatibility with kqemu, we use 32 bits for x86_64 */ +#define TARGET_PHYS_ADDR_SPACE_BITS 32 +#endif + TranslationBlock tbs[CODE_GEN_MAX_BLOCKS]; TranslationBlock *tb_hash[CODE_GEN_HASH_SIZE]; TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE]; @@ -83,6 +92,8 @@ typedef struct PhysPageDesc { uint32_t phys_offset; } PhysPageDesc; +/* Note: the VirtPage handling is absolete and will be suppressed + ASAP */ typedef struct VirtPageDesc { /* physical address of code page. It is valid only if 'valid_tag' matches 'virt_valid_tag' */ @@ -113,7 +124,13 @@ static PageDesc *l1_map[L1_SIZE]; PhysPageDesc **l1_phys_map; #if !defined(CONFIG_USER_ONLY) +#if TARGET_LONG_BITS > 32 +#define VIRT_L_BITS 9 +#define VIRT_L_SIZE (1 << VIRT_L_BITS) +static void *l1_virt_map[VIRT_L_SIZE]; +#else static VirtPageDesc *l1_virt_map[L1_SIZE]; +#endif static unsigned int virt_valid_tag; #endif @@ -176,8 +193,8 @@ static void page_init(void) #if !defined(CONFIG_USER_ONLY) virt_valid_tag = 1; #endif - l1_phys_map = qemu_vmalloc(L1_SIZE * sizeof(PhysPageDesc *)); - memset(l1_phys_map, 0, L1_SIZE * sizeof(PhysPageDesc *)); + l1_phys_map = qemu_vmalloc(L1_SIZE * sizeof(void *)); + memset(l1_phys_map, 0, L1_SIZE * sizeof(void *)); } static inline PageDesc *page_find_alloc(unsigned int index) @@ -205,80 +222,156 @@ static inline PageDesc *page_find(unsigned int index) return p + (index & (L2_SIZE - 1)); } -static inline PhysPageDesc *phys_page_find_alloc(unsigned int index) +static PhysPageDesc *phys_page_find_alloc(target_phys_addr_t index, int alloc) { - PhysPageDesc **lp, *p; + void **lp, **p; + + p = (void **)l1_phys_map; +#if TARGET_PHYS_ADDR_SPACE_BITS > 32 - lp = &l1_phys_map[index >> L2_BITS]; +#if TARGET_PHYS_ADDR_SPACE_BITS > (32 + L1_BITS) +#error unsupported TARGET_PHYS_ADDR_SPACE_BITS +#endif + lp = p + ((index >> (L1_BITS + L2_BITS)) & (L1_SIZE - 1)); p = *lp; if (!p) { /* allocate if not found */ + if (!alloc) + return NULL; + p = qemu_vmalloc(sizeof(void *) * L1_SIZE); + memset(p, 0, sizeof(void *) * L1_SIZE); + *lp = p; + } +#endif + lp = p + ((index >> L2_BITS) & (L1_SIZE - 1)); + p = *lp; + if (!p) { + /* allocate if not found */ + if (!alloc) + return NULL; p = qemu_vmalloc(sizeof(PhysPageDesc) * L2_SIZE); memset(p, 0, sizeof(PhysPageDesc) * L2_SIZE); *lp = p; } - return p + (index & (L2_SIZE - 1)); + return ((PhysPageDesc *)p) + (index & (L2_SIZE - 1)); } -static inline PhysPageDesc *phys_page_find(unsigned int index) +static inline PhysPageDesc *phys_page_find(target_phys_addr_t index) { - PhysPageDesc *p; - - p = l1_phys_map[index >> L2_BITS]; - if (!p) - return 0; - return p + (index & (L2_SIZE - 1)); + return phys_page_find_alloc(index, 0); } #if !defined(CONFIG_USER_ONLY) static void tlb_protect_code(CPUState *env, target_ulong addr); static void tlb_unprotect_code_phys(CPUState *env, unsigned long phys_addr, target_ulong vaddr); -static inline VirtPageDesc *virt_page_find_alloc(unsigned int index) +static VirtPageDesc *virt_page_find_alloc(target_ulong index, int alloc) { - VirtPageDesc **lp, *p; - - /* XXX: should not truncate for 64 bit addresses */ #if TARGET_LONG_BITS > 32 - index &= (L1_SIZE - 1); -#endif + void **p, **lp; + + p = l1_virt_map; + lp = p + ((index >> (5 * VIRT_L_BITS)) & (VIRT_L_SIZE - 1)); + p = *lp; + if (!p) { + if (!alloc) + return NULL; + p = qemu_mallocz(sizeof(void *) * VIRT_L_SIZE); + *lp = p; + } + lp = p + ((index >> (4 * VIRT_L_BITS)) & (VIRT_L_SIZE - 1)); + p = *lp; + if (!p) { + if (!alloc) + return NULL; + p = qemu_mallocz(sizeof(void *) * VIRT_L_SIZE); + *lp = p; + } + lp = p + ((index >> (3 * VIRT_L_BITS)) & (VIRT_L_SIZE - 1)); + p = *lp; + if (!p) { + if (!alloc) + return NULL; + p = qemu_mallocz(sizeof(void *) * VIRT_L_SIZE); + *lp = p; + } + lp = p + ((index >> (2 * VIRT_L_BITS)) & (VIRT_L_SIZE - 1)); + p = *lp; + if (!p) { + if (!alloc) + return NULL; + p = qemu_mallocz(sizeof(void *) * VIRT_L_SIZE); + *lp = p; + } + lp = p + ((index >> (1 * VIRT_L_BITS)) & (VIRT_L_SIZE - 1)); + p = *lp; + if (!p) { + if (!alloc) + return NULL; + p = qemu_mallocz(sizeof(VirtPageDesc) * VIRT_L_SIZE); + *lp = p; + } + return ((VirtPageDesc *)p) + (index & (VIRT_L_SIZE - 1)); +#else + VirtPageDesc *p, **lp; + lp = &l1_virt_map[index >> L2_BITS]; p = *lp; if (!p) { /* allocate if not found */ - p = qemu_malloc(sizeof(VirtPageDesc) * L2_SIZE); - memset(p, 0, sizeof(VirtPageDesc) * L2_SIZE); + if (!alloc) + return NULL; + p = qemu_mallocz(sizeof(VirtPageDesc) * L2_SIZE); *lp = p; } return p + (index & (L2_SIZE - 1)); +#endif } -static inline VirtPageDesc *virt_page_find(unsigned int index) +static inline VirtPageDesc *virt_page_find(target_ulong index) { - VirtPageDesc *p; + return virt_page_find_alloc(index, 0); +} - p = l1_virt_map[index >> L2_BITS]; - if (!p) - return 0; - return p + (index & (L2_SIZE - 1)); +#if TARGET_LONG_BITS > 32 +static void virt_page_flush_internal(void **p, int level) +{ + int i; + if (level == 0) { + VirtPageDesc *q = (VirtPageDesc *)p; + for(i = 0; i < VIRT_L_SIZE; i++) + q[i].valid_tag = 0; + } else { + level--; + for(i = 0; i < VIRT_L_SIZE; i++) { + if (p[i]) + virt_page_flush_internal(p[i], level); + } + } } +#endif static void virt_page_flush(void) { - int i, j; - VirtPageDesc *p; - virt_valid_tag++; if (virt_valid_tag == 0) { virt_valid_tag = 1; - for(i = 0; i < L1_SIZE; i++) { - p = l1_virt_map[i]; - if (p) { - for(j = 0; j < L2_SIZE; j++) - p[j].valid_tag = 0; +#if TARGET_LONG_BITS > 32 + virt_page_flush_internal(l1_virt_map, 5); +#else + { + int i, j; + VirtPageDesc *p; + for(i = 0; i < L1_SIZE; i++) { + p = l1_virt_map[i]; + if (p) { + for(j = 0; j < L2_SIZE; j++) + p[j].valid_tag = 0; + } } } +#endif } } #else @@ -945,7 +1038,7 @@ void tb_link(TranslationBlock *tb) /* save the code memory mappings (needed to invalidate the code) */ addr = tb->pc & TARGET_PAGE_MASK; - vp = virt_page_find_alloc(addr >> TARGET_PAGE_BITS); + vp = virt_page_find_alloc(addr >> TARGET_PAGE_BITS, 1); #ifdef DEBUG_TLB_CHECK if (vp->valid_tag == virt_valid_tag && vp->phys_addr != tb->page_addr[0]) { @@ -963,7 +1056,7 @@ void tb_link(TranslationBlock *tb) if (tb->page_addr[1] != -1) { addr += TARGET_PAGE_SIZE; - vp = virt_page_find_alloc(addr >> TARGET_PAGE_BITS); + vp = virt_page_find_alloc(addr >> TARGET_PAGE_BITS, 1); #ifdef DEBUG_TLB_CHECK if (vp->valid_tag == virt_valid_tag && vp->phys_addr != tb->page_addr[1]) { @@ -1330,7 +1423,7 @@ void tlb_flush_page(CPUState *env, target_ulong addr) TranslationBlock *tb; #if defined(DEBUG_TLB) - printf("tlb_flush_page: 0x%08x\n", addr); + printf("tlb_flush_page: " TARGET_FMT_lx "\n", addr); #endif /* must reset current TB so that interrupts cannot modify the links while we are modifying them */ @@ -1497,7 +1590,7 @@ void cpu_physical_memory_reset_dirty(target_ulong start, target_ulong end, } static inline void tlb_set_dirty1(CPUTLBEntry *tlb_entry, - unsigned long start) + unsigned long start) { unsigned long addr; if ((tlb_entry->address & ~TARGET_PAGE_MASK) == IO_MEM_NOTDIRTY) { @@ -1536,7 +1629,7 @@ int tlb_set_page(CPUState *env, target_ulong vaddr, TranslationBlock *first_tb; unsigned int index; target_ulong address; - unsigned long addend; + target_phys_addr_t addend; int ret; p = phys_page_find(paddr >> TARGET_PAGE_BITS); @@ -1553,7 +1646,7 @@ int tlb_set_page(CPUState *env, target_ulong vaddr, } } #if defined(DEBUG_TLB) - printf("tlb_set_page: vaddr=0x%08x paddr=0x%08x prot=%x u=%d c=%d smmu=%d pd=0x%08x\n", + printf("tlb_set_page: vaddr=" TARGET_FMT_lx " paddr=0x%08x prot=%x u=%d c=%d smmu=%d pd=0x%08x\n", vaddr, paddr, prot, is_user, (first_tb != NULL), is_softmmu, pd); #endif @@ -1572,7 +1665,7 @@ int tlb_set_page(CPUState *env, target_ulong vaddr, addend = (unsigned long)phys_ram_base + (pd & TARGET_PAGE_MASK); } - index = (vaddr >> 12) & (CPU_TLB_SIZE - 1); + index = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); addend -= vaddr; if (prot & PAGE_READ) { env->tlb_read[is_user][index].address = address; @@ -1635,7 +1728,7 @@ int tlb_set_page(CPUState *env, target_ulong vaddr, original mapping */ VirtPageDesc *vp; - vp = virt_page_find_alloc(vaddr >> TARGET_PAGE_BITS); + vp = virt_page_find_alloc(vaddr >> TARGET_PAGE_BITS, 1); vp->phys_addr = pd; vp->prot = prot; vp->valid_tag = virt_valid_tag; @@ -1859,13 +1952,13 @@ void cpu_register_physical_memory(target_phys_addr_t start_addr, unsigned long size, unsigned long phys_offset) { - unsigned long addr, end_addr; + target_phys_addr_t addr, end_addr; PhysPageDesc *p; size = (size + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK; end_addr = start_addr + size; for(addr = start_addr; addr != end_addr; addr += TARGET_PAGE_SIZE) { - p = phys_page_find_alloc(addr >> TARGET_PAGE_BITS); + p = phys_page_find_alloc(addr >> TARGET_PAGE_BITS, 1); p->phys_offset = phys_offset; if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM) phys_offset += TARGET_PAGE_SIZE; diff --git a/qemu/gdbstub.c b/qemu/gdbstub.c index 22c38e5..cbae320 100644 --- a/qemu/gdbstub.c +++ b/qemu/gdbstub.c @@ -1,7 +1,7 @@ /* * gdb server stub * - * Copyright (c) 2003 Fabrice Bellard + * Copyright (c) 2003-2005 Fabrice Bellard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -253,14 +253,14 @@ static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) } /* nip, msr, ccr, lnk, ctr, xer, mq */ registers[96] = tswapl(env->nip); - registers[97] = tswapl(_load_msr(env)); + registers[97] = tswapl(do_load_msr(env)); tmp = 0; for (i = 0; i < 8; i++) tmp |= env->crf[i] << (32 - ((i + 1) * 4)); registers[98] = tswapl(tmp); registers[99] = tswapl(env->lr); registers[100] = tswapl(env->ctr); - registers[101] = tswapl(_load_xer(env)); + registers[101] = tswapl(do_load_xer(env)); registers[102] = 0; return 103 * 4; @@ -282,18 +282,18 @@ static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size) } /* nip, msr, ccr, lnk, ctr, xer, mq */ env->nip = tswapl(registers[96]); - _store_msr(env, tswapl(registers[97])); + do_store_msr(env, tswapl(registers[97])); registers[98] = tswapl(registers[98]); for (i = 0; i < 8; i++) env->crf[i] = (registers[98] >> (32 - ((i + 1) * 4))) & 0xF; env->lr = tswapl(registers[99]); env->ctr = tswapl(registers[100]); - _store_xer(env, tswapl(registers[101])); + do_store_xer(env, tswapl(registers[101])); } #elif defined (TARGET_SPARC) static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) { - uint32_t *registers = (uint32_t *)mem_buf, tmp; + target_ulong *registers = (target_ulong *)mem_buf; int i; /* fill in g0..g7 */ @@ -308,10 +308,15 @@ static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) for (i = 0; i < 32; i++) { registers[i + 32] = tswapl(*((uint32_t *)&env->fpr[i])); } +#ifndef TARGET_SPARC64 /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */ registers[64] = tswapl(env->y); - tmp = GET_PSR(env); - registers[65] = tswapl(tmp); + { + target_ulong tmp; + + tmp = GET_PSR(env); + registers[65] = tswapl(tmp); + } registers[66] = tswapl(env->wim); registers[67] = tswapl(env->tbr); registers[68] = tswapl(env->pc); @@ -319,13 +324,24 @@ static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) registers[70] = tswapl(env->fsr); registers[71] = 0; /* csr */ registers[72] = 0; - - return 73 * 4; + return 73 * sizeof(target_ulong); +#else + for (i = 0; i < 32; i += 2) { + registers[i/2 + 64] = tswapl(*((uint64_t *)&env->fpr[i])); + } + registers[81] = tswapl(env->pc); + registers[82] = tswapl(env->npc); + registers[83] = tswapl(env->tstate[env->tl]); + registers[84] = tswapl(env->fsr); + registers[85] = tswapl(env->fprs); + registers[86] = tswapl(env->y); + return 87 * sizeof(target_ulong); +#endif } static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size) { - uint32_t *registers = (uint32_t *)mem_buf; + target_ulong *registers = (target_ulong *)mem_buf; int i; /* fill in g0..g7 */ @@ -334,12 +350,13 @@ static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size) } /* fill in register window */ for(i = 0; i < 24; i++) { - env->regwptr[i] = tswapl(registers[i]); + env->regwptr[i] = tswapl(registers[i + 8]); } /* fill in fprs */ for (i = 0; i < 32; i++) { *((uint32_t *)&env->fpr[i]) = tswapl(registers[i + 32]); } +#ifndef TARGET_SPARC64 /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */ env->y = tswapl(registers[64]); PUT_PSR(env, tswapl(registers[65])); @@ -348,6 +365,20 @@ static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size) env->pc = tswapl(registers[68]); env->npc = tswapl(registers[69]); env->fsr = tswapl(registers[70]); +#else + for (i = 0; i < 32; i += 2) { + uint64_t tmp; + tmp = tswapl(registers[i/2 + 64]) << 32; + tmp |= tswapl(registers[i/2 + 64 + 1]); + *((uint64_t *)&env->fpr[i]) = tmp; + } + env->pc = tswapl(registers[81]); + env->npc = tswapl(registers[82]); + env->tstate[env->tl] = tswapl(registers[83]); + env->fsr = tswapl(registers[84]); + env->fprs = tswapl(registers[85]); + env->y = tswapl(registers[86]); +#endif } #elif defined (TARGET_ARM) static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) diff --git a/qemu/hw/apic.c b/qemu/hw/apic.c index 486b9bf..a66e032 100644 --- a/qemu/hw/apic.c +++ b/qemu/hw/apic.c @@ -20,6 +20,7 @@ #include "vl.h" //#define DEBUG_APIC +//#define DEBUG_IOAPIC /* APIC Local Vector Table */ #define APIC_LVT_TIMER 0 @@ -39,6 +40,10 @@ #define APIC_DM_SIPI 6 #define APIC_DM_EXTINT 7 +/* APIC destination mode */ +#define APIC_DESTMODE_FLAT 0xf +#define APIC_DESTMODE_CLUSTER 1 + #define APIC_TRIGGER_EDGE 0 #define APIC_TRIGGER_LEVEL 1 @@ -49,6 +54,8 @@ #define APIC_INPUT_POLARITY (1<<13) #define APIC_SEND_PENDING (1<<12) +#define IOAPIC_NUM_PINS 0x18 + #define ESR_ILLEGAL_ADDRESS (1 << 7) #define APIC_SV_ENABLE (1 << 8) @@ -57,8 +64,11 @@ typedef struct APICState { CPUState *cpu_env; uint32_t apicbase; uint8_t id; + uint8_t arb_id; uint8_t tpr; uint32_t spurious_vec; + uint8_t log_dest; + uint8_t dest_mode; uint32_t isr[8]; /* in service register */ uint32_t tmr[8]; /* trigger mode register */ uint32_t irr[8]; /* interrupt request register */ @@ -71,9 +81,64 @@ typedef struct APICState { uint32_t initial_count; int64_t initial_count_load_time, next_time; QEMUTimer *timer; + + struct APICState *next_apic; } APICState; +struct IOAPICState { + uint8_t id; + uint8_t ioregsel; + + uint32_t irr; + uint64_t ioredtbl[IOAPIC_NUM_PINS]; +}; + static int apic_io_memory; +static APICState *first_local_apic = NULL; +static int last_apic_id = 0; + +static void apic_init_ipi(APICState *s); +static void apic_set_irq(APICState *s, int vector_num, int trigger_mode); +static void apic_update_irq(APICState *s); + +static void apic_bus_deliver(uint32_t deliver_bitmask, uint8_t delivery_mode, + uint8_t vector_num, uint8_t polarity, + uint8_t trigger_mode) +{ + APICState *apic_iter; + + switch (delivery_mode) { + case APIC_DM_LOWPRI: + case APIC_DM_FIXED: + /* XXX: arbitration */ + break; + + case APIC_DM_SMI: + case APIC_DM_NMI: + break; + + case APIC_DM_INIT: + /* normal INIT IPI sent to processors */ + for (apic_iter = first_local_apic; apic_iter != NULL; + apic_iter = apic_iter->next_apic) { + apic_init_ipi(apic_iter); + } + return; + + case APIC_DM_EXTINT: + /* handled in I/O APIC code */ + break; + + default: + return; + } + + for (apic_iter = first_local_apic; apic_iter != NULL; + apic_iter = apic_iter->next_apic) { + if (deliver_bitmask & (1 << apic_iter->id)) + apic_set_irq(apic_iter, vector_num, trigger_mode); + } +} void cpu_set_apic_base(CPUState *env, uint64_t val) { @@ -104,6 +169,7 @@ void cpu_set_apic_tpr(CPUX86State *env, uint8_t val) { APICState *s = env->apic_state; s->tpr = (val & 0x0f) << 4; + apic_update_irq(s); } uint8_t cpu_get_apic_tpr(CPUX86State *env) @@ -112,16 +178,24 @@ uint8_t cpu_get_apic_tpr(CPUX86State *env) return s->tpr >> 4; } -/* return -1 if no bit is set */ -static int get_highest_priority_int(uint32_t *tab) +static int fls_bit(int value) { - int i; - for(i = 0;i < 8; i++) { - if (tab[i] != 0) { - return i * 32 + ffs(tab[i]) - 1; - } - } - return -1; + unsigned int ret = 0; + +#ifdef HOST_I386 + __asm__ __volatile__ ("bsr %1, %0\n" : "+r" (ret) : "rm" (value)); + return ret; +#else + if (value > 0xffff) + value >>= 16, ret = 16; + if (value > 0xff) + value >>= 8, ret += 8; + if (value > 0xf) + value >>= 4, ret += 4; + if (value > 0x3) + value >>= 2, ret += 2; + return ret + (value >> 1); +#endif } static inline void set_bit(uint32_t *tab, int index) @@ -140,6 +214,18 @@ static inline void reset_bit(uint32_t *tab, int index) tab[i] &= ~mask; } +/* return -1 if no bit is set */ +static int get_highest_priority_int(uint32_t *tab) +{ + int i; + for(i = 7; i >= 0; i--) { + if (tab[i] != 0) { + return i * 32 + fls_bit(tab[i]); + } + } + return -1; +} + static int apic_get_ppr(APICState *s) { int tpr, isrv, ppr; @@ -156,16 +242,23 @@ static int apic_get_ppr(APICState *s) return ppr; } +static int apic_get_arb_pri(APICState *s) +{ + /* XXX: arbitration */ + return 0; +} + /* signal the CPU if an irq is pending */ static void apic_update_irq(APICState *s) { - int irrv, isrv; + int irrv, ppr; + if (!(s->spurious_vec & APIC_SV_ENABLE)) + return; irrv = get_highest_priority_int(s->irr); if (irrv < 0) return; - isrv = get_highest_priority_int(s->isr); - /* if the pending irq has less priority, we do not make a new request */ - if (isrv >= 0 && irrv >= isrv) + ppr = apic_get_ppr(s); + if (ppr && (irrv & 0xf0) <= (ppr & 0xf0)) return; cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD); } @@ -187,9 +280,116 @@ static void apic_eoi(APICState *s) if (isrv < 0) return; reset_bit(s->isr, isrv); + /* XXX: send the EOI packet to the APIC bus to allow the I/O APIC to + set the remote IRR bit for level triggered interrupts. */ apic_update_irq(s); } +static uint32_t apic_get_delivery_bitmask(uint8_t dest, uint8_t dest_mode) +{ + uint32_t mask = 0; + APICState *apic_iter; + + if (dest_mode == 0) { + if (dest == 0xff) + mask = 0xff; + else + mask = 1 << dest; + } else { + /* XXX: cluster mode */ + for (apic_iter = first_local_apic; apic_iter != NULL; + apic_iter = apic_iter->next_apic) { + if (dest & apic_iter->log_dest) + mask |= (1 << apic_iter->id); + } + } + + return mask; +} + + +static void apic_init_ipi(APICState *s) +{ + int i; + + for(i = 0; i < APIC_LVT_NB; i++) + s->lvt[i] = 1 << 16; /* mask LVT */ + s->tpr = 0; + s->spurious_vec = 0xff; + s->log_dest = 0; + s->dest_mode = 0; + memset(s->isr, 0, sizeof(s->isr)); + memset(s->tmr, 0, sizeof(s->tmr)); + memset(s->irr, 0, sizeof(s->irr)); + memset(s->lvt, 0, sizeof(s->lvt)); + s->esr = 0; + memset(s->icr, 0, sizeof(s->icr)); + s->divide_conf = 0; + s->count_shift = 0; + s->initial_count = 0; + s->initial_count_load_time = 0; + s->next_time = 0; +} + +static void apic_deliver(APICState *s, uint8_t dest, uint8_t dest_mode, + uint8_t delivery_mode, uint8_t vector_num, + uint8_t polarity, uint8_t trigger_mode) +{ + uint32_t deliver_bitmask = 0; + int dest_shorthand = (s->icr[0] >> 18) & 3; + APICState *apic_iter; + + switch (delivery_mode) { + case APIC_DM_LOWPRI: + /* XXX: serch for focus processor, arbitration */ + dest = s->id; + + case APIC_DM_INIT: + { + int trig_mode = (s->icr[0] >> 15) & 1; + int level = (s->icr[0] >> 14) & 1; + if (level == 0 && trig_mode == 1) { + for (apic_iter = first_local_apic; apic_iter != NULL; + apic_iter = apic_iter->next_apic) { + if (deliver_bitmask & (1 << apic_iter->id)) { + apic_iter->arb_id = apic_iter->id; + } + } + return; + } + } + break; + + case APIC_DM_SIPI: + for (apic_iter = first_local_apic; apic_iter != NULL; + apic_iter = apic_iter->next_apic) { + if (deliver_bitmask & (1 << apic_iter->id)) { + /* XXX: SMP support */ + /* apic_startup(apic_iter); */ + } + } + return; + } + + switch (dest_shorthand) { + case 0: + deliver_bitmask = apic_get_delivery_bitmask(dest, dest_mode); + break; + case 1: + deliver_bitmask = (1 << s->id); + break; + case 2: + deliver_bitmask = 0xffffffff; + break; + case 3: + deliver_bitmask = 0xffffffff & ~(1 << s->id); + break; + } + + apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, polarity, + trigger_mode); +} + int apic_get_interrupt(CPUState *env) { APICState *s = env->apic_state; @@ -207,6 +407,8 @@ int apic_get_interrupt(CPUState *env) if (intno < 0) return -1; reset_bit(s->irr, intno); + if (s->tpr && intno <= s->tpr) + return s->spurious_vec & 0xff; set_bit(s->isr, intno); apic_update_irq(s); return intno; @@ -220,7 +422,7 @@ static uint32_t apic_get_current_count(APICState *s) s->count_shift; if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) { /* periodic */ - val = s->initial_count - (d % (s->initial_count + 1)); + val = s->initial_count - (d % ((uint64_t)s->initial_count + 1)); } else { if (d >= s->initial_count) val = 0; @@ -238,11 +440,11 @@ static void apic_timer_update(APICState *s, int64_t current_time) d = (current_time - s->initial_count_load_time) >> s->count_shift; if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) { - d = ((d / (s->initial_count + 1)) + 1) * (s->initial_count + 1); + d = ((d / ((uint64_t)s->initial_count + 1)) + 1) * ((uint64_t)s->initial_count + 1); } else { if (d >= s->initial_count) goto no_timer; - d = s->initial_count + 1; + d = (uint64_t)s->initial_count + 1; } next_time = s->initial_count_load_time + (d << s->count_shift); qemu_mod_timer(s->timer, next_time); @@ -304,10 +506,19 @@ static uint32_t apic_mem_readl(void *opaque, target_phys_addr_t addr) case 0x08: val = s->tpr; break; + case 0x09: + val = apic_get_arb_pri(s); + break; case 0x0a: /* ppr */ val = apic_get_ppr(s); break; + case 0x0d: + val = s->log_dest << 24; + break; + case 0x0e: + val = s->dest_mode << 28; + break; case 0x0f: val = s->spurious_vec; break; @@ -372,16 +583,29 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) break; case 0x08: s->tpr = val; + apic_update_irq(s); break; case 0x0b: /* EOI */ apic_eoi(s); break; + case 0x0d: + s->log_dest = val >> 24; + break; + case 0x0e: + s->dest_mode = val >> 28; + break; case 0x0f: s->spurious_vec = val & 0x1ff; + apic_update_irq(s); break; case 0x30: + s->icr[0] = val; + apic_deliver(s, (s->icr[1] >> 24) & 0xff, (s->icr[0] >> 11) & 1, + (s->icr[0] >> 8) & 7, (s->icr[0] & 0xff), + (s->icr[0] >> 14) & 1, (s->icr[0] >> 15) & 1); + break; case 0x31: - s->icr[index & 1] = val; + s->icr[1] = val; break; case 0x32 ... 0x37: { @@ -410,7 +634,76 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) } } +static void apic_save(QEMUFile *f, void *opaque) +{ + APICState *s = opaque; + int i; + + qemu_put_be32s(f, &s->apicbase); + qemu_put_8s(f, &s->id); + qemu_put_8s(f, &s->arb_id); + qemu_put_8s(f, &s->tpr); + qemu_put_be32s(f, &s->spurious_vec); + qemu_put_8s(f, &s->log_dest); + qemu_put_8s(f, &s->dest_mode); + for (i = 0; i < 8; i++) { + qemu_put_be32s(f, &s->isr[i]); + qemu_put_be32s(f, &s->tmr[i]); + qemu_put_be32s(f, &s->irr[i]); + } + for (i = 0; i < APIC_LVT_NB; i++) { + qemu_put_be32s(f, &s->lvt[i]); + } + qemu_put_be32s(f, &s->esr); + qemu_put_be32s(f, &s->icr[0]); + qemu_put_be32s(f, &s->icr[1]); + qemu_put_be32s(f, &s->divide_conf); + qemu_put_be32s(f, &s->count_shift); + qemu_put_be32s(f, &s->initial_count); + qemu_put_be64s(f, &s->initial_count_load_time); + qemu_put_be64s(f, &s->next_time); +} + +static int apic_load(QEMUFile *f, void *opaque, int version_id) +{ + APICState *s = opaque; + int i; + + if (version_id != 1) + return -EINVAL; + + /* XXX: what if the base changes? (registered memory regions) */ + qemu_get_be32s(f, &s->apicbase); + qemu_get_8s(f, &s->id); + qemu_get_8s(f, &s->arb_id); + qemu_get_8s(f, &s->tpr); + qemu_get_be32s(f, &s->spurious_vec); + qemu_get_8s(f, &s->log_dest); + qemu_get_8s(f, &s->dest_mode); + for (i = 0; i < 8; i++) { + qemu_get_be32s(f, &s->isr[i]); + qemu_get_be32s(f, &s->tmr[i]); + qemu_get_be32s(f, &s->irr[i]); + } + for (i = 0; i < APIC_LVT_NB; i++) { + qemu_get_be32s(f, &s->lvt[i]); + } + qemu_get_be32s(f, &s->esr); + qemu_get_be32s(f, &s->icr[0]); + qemu_get_be32s(f, &s->icr[1]); + qemu_get_be32s(f, &s->divide_conf); + qemu_get_be32s(f, &s->count_shift); + qemu_get_be32s(f, &s->initial_count); + qemu_get_be64s(f, &s->initial_count_load_time); + qemu_get_be64s(f, &s->next_time); + return 0; +} +static void apic_reset(void *opaque) +{ + APICState *s = opaque; + apic_init_ipi(s); +} static CPUReadMemoryFunc *apic_mem_read[3] = { apic_mem_readb, @@ -427,27 +720,237 @@ static CPUWriteMemoryFunc *apic_mem_write[3] = { int apic_init(CPUState *env) { APICState *s; - int i; - s = malloc(sizeof(APICState)); + s = qemu_mallocz(sizeof(APICState)); if (!s) return -1; - memset(s, 0, sizeof(*s)); env->apic_state = s; + apic_init_ipi(s); + s->id = last_apic_id++; s->cpu_env = env; s->apicbase = 0xfee00000 | - MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE; - for(i = 0; i < APIC_LVT_NB; i++) - s->lvt[i] = 1 << 16; /* mask LVT */ - s->spurious_vec = 0xff; + (s->id ? 0 : MSR_IA32_APICBASE_BSP) | MSR_IA32_APICBASE_ENABLE; + /* XXX: mapping more APICs at the same memory location */ if (apic_io_memory == 0) { /* NOTE: the APIC is directly connected to the CPU - it is not on the global memory bus. */ apic_io_memory = cpu_register_io_memory(0, apic_mem_read, apic_mem_write, NULL); - cpu_register_physical_memory(s->apicbase & ~0xfff, 0x1000, apic_io_memory); + cpu_register_physical_memory(s->apicbase & ~0xfff, 0x1000, + apic_io_memory); } s->timer = qemu_new_timer(vm_clock, apic_timer, s); + + register_savevm("apic", 0, 1, apic_save, apic_load, s); + qemu_register_reset(apic_reset, s); + + s->next_apic = first_local_apic; + first_local_apic = s; + + return 0; +} + +static void ioapic_service(IOAPICState *s) +{ + uint8_t i; + uint8_t trig_mode; + uint8_t vector; + uint8_t delivery_mode; + uint32_t mask; + uint64_t entry; + uint8_t dest; + uint8_t dest_mode; + uint8_t polarity; + + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + mask = 1 << i; + if (s->irr & mask) { + entry = s->ioredtbl[i]; + if (!(entry & APIC_LVT_MASKED)) { + trig_mode = ((entry >> 15) & 1); + dest = entry >> 56; + dest_mode = (entry >> 11) & 1; + delivery_mode = (entry >> 8) & 7; + polarity = (entry >> 13) & 1; + if (trig_mode == APIC_TRIGGER_EDGE) + s->irr &= ~mask; + if (delivery_mode == APIC_DM_EXTINT) + vector = pic_read_irq(isa_pic); + else + vector = entry & 0xff; + apic_bus_deliver(apic_get_delivery_bitmask(dest, dest_mode), + delivery_mode, vector, polarity, trig_mode); + } + } + } +} + +void ioapic_set_irq(void *opaque, int vector, int level) +{ + IOAPICState *s = opaque; + + if (vector >= 0 && vector < IOAPIC_NUM_PINS) { + uint32_t mask = 1 << vector; + uint64_t entry = s->ioredtbl[vector]; + + if ((entry >> 15) & 1) { + /* level triggered */ + if (level) { + s->irr |= mask; + ioapic_service(s); + } else { + s->irr &= ~mask; + } + } else { + /* edge triggered */ + if (level) { + s->irr |= mask; + ioapic_service(s); + } + } + } +} + +static uint32_t ioapic_mem_readl(void *opaque, target_phys_addr_t addr) +{ + IOAPICState *s = opaque; + int index; + uint32_t val = 0; + + addr &= 0xff; + if (addr == 0x00) { + val = s->ioregsel; + } else if (addr == 0x10) { + switch (s->ioregsel) { + case 0x00: + val = s->id << 24; + break; + case 0x01: + val = 0x11 | ((IOAPIC_NUM_PINS - 1) << 16); /* version 0x11 */ + break; + case 0x02: + val = 0; + break; + default: + index = (s->ioregsel - 0x10) >> 1; + if (index >= 0 && index < IOAPIC_NUM_PINS) { + if (s->ioregsel & 1) + val = s->ioredtbl[index] >> 32; + else + val = s->ioredtbl[index] & 0xffffffff; + } + } +#ifdef DEBUG_IOAPIC + printf("I/O APIC read: %08x = %08x\n", s->ioregsel, val); +#endif + } + return val; +} + +static void ioapic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + IOAPICState *s = opaque; + int index; + + addr &= 0xff; + if (addr == 0x00) { + s->ioregsel = val; + return; + } else if (addr == 0x10) { +#ifdef DEBUG_IOAPIC + printf("I/O APIC write: %08x = %08x\n", s->ioregsel, val); +#endif + switch (s->ioregsel) { + case 0x00: + s->id = (val >> 24) & 0xff; + return; + case 0x01: + case 0x02: + return; + default: + index = (s->ioregsel - 0x10) >> 1; + if (index >= 0 && index < IOAPIC_NUM_PINS) { + if (s->ioregsel & 1) { + s->ioredtbl[index] &= 0xffffffff; + s->ioredtbl[index] |= (uint64_t)val << 32; + } else { + s->ioredtbl[index] &= ~0xffffffffULL; + s->ioredtbl[index] |= val; + } + ioapic_service(s); + } + } + } +} + +static void ioapic_save(QEMUFile *f, void *opaque) +{ + IOAPICState *s = opaque; + int i; + + qemu_put_8s(f, &s->id); + qemu_put_8s(f, &s->ioregsel); + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + qemu_put_be64s(f, &s->ioredtbl[i]); + } +} + +static int ioapic_load(QEMUFile *f, void *opaque, int version_id) +{ + IOAPICState *s = opaque; + int i; + + if (version_id != 1) + return -EINVAL; + + qemu_get_8s(f, &s->id); + qemu_get_8s(f, &s->ioregsel); + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + qemu_get_be64s(f, &s->ioredtbl[i]); + } return 0; } + +static void ioapic_reset(void *opaque) +{ + IOAPICState *s = opaque; + int i; + + memset(s, 0, sizeof(*s)); + for(i = 0; i < IOAPIC_NUM_PINS; i++) + s->ioredtbl[i] = 1 << 16; /* mask LVT */ +} + +static CPUReadMemoryFunc *ioapic_mem_read[3] = { + ioapic_mem_readl, + ioapic_mem_readl, + ioapic_mem_readl, +}; + +static CPUWriteMemoryFunc *ioapic_mem_write[3] = { + ioapic_mem_writel, + ioapic_mem_writel, + ioapic_mem_writel, +}; + +IOAPICState *ioapic_init(void) +{ + IOAPICState *s; + int io_memory; + + s = qemu_mallocz(sizeof(IOAPICState)); + if (!s) + return NULL; + ioapic_reset(s); + s->id = last_apic_id++; + + io_memory = cpu_register_io_memory(0, ioapic_mem_read, + ioapic_mem_write, s); + cpu_register_physical_memory(0xfec00000, 0x1000, io_memory); + + register_savevm("ioapic", 0, 1, ioapic_save, ioapic_load, s); + qemu_register_reset(ioapic_reset, s); + + return s; +} diff --git a/qemu/hw/cirrus_vga_rop2.h b/qemu/hw/cirrus_vga_rop2.h index 2f61740..da11d0f 100644 --- a/qemu/hw/cirrus_vga_rop2.h +++ b/qemu/hw/cirrus_vga_rop2.h @@ -61,8 +61,8 @@ glue(glue(glue(cirrus_patternfill_, ROP_NAME), _),DEPTH) pattern_pitch = 32; #endif pattern_y = s->cirrus_blt_srcaddr & 7; - pattern_x = skipleft; for(y = 0; y < bltheight; y++) { + pattern_x = skipleft; d = dst + skipleft; src1 = src + pattern_y * pattern_pitch; for (x = skipleft; x < bltwidth; x += (DEPTH / 8)) { diff --git a/qemu/hw/cuda.c b/qemu/hw/cuda.c index 7eea042..dec5ffb 100644 --- a/qemu/hw/cuda.c +++ b/qemu/hw/cuda.c @@ -23,6 +23,8 @@ */ #include "vl.h" +/* XXX: implement all timer modes */ + //#define DEBUG_CUDA //#define DEBUG_CUDA_PACKET @@ -41,6 +43,7 @@ #define IER_CLR 0 /* clear bits in IER */ #define SR_INT 0x04 /* Shift register full/empty */ #define T1_INT 0x40 /* Timer 1 interrupt */ +#define T2_INT 0x20 /* Timer 2 interrupt */ /* Bits in ACR */ #define T1MODE 0xc0 /* Timer 1 mode */ @@ -91,7 +94,8 @@ #define RTC_OFFSET 2082844800 typedef struct CUDATimer { - unsigned int latch; + int index; + uint16_t latch; uint16_t counter_value; /* counter value at load time */ int64_t load_time; int64_t next_irq_time; @@ -120,8 +124,9 @@ typedef struct CUDAState { int data_in_index; int data_out_index; + SetIRQFunc *set_irq; int irq; - openpic_t *openpic; + void *irq_opaque; uint8_t autopoll; uint8_t data_in[128]; uint8_t data_out[16]; @@ -140,9 +145,9 @@ static void cuda_timer_update(CUDAState *s, CUDATimer *ti, static void cuda_update_irq(CUDAState *s) { if (s->ifr & s->ier & (SR_INT | T1_INT)) { - openpic_set_irq(s->openpic, s->irq, 1); + s->set_irq(s->irq_opaque, s->irq, 1); } else { - openpic_set_irq(s->openpic, s->irq, 0); + s->set_irq(s->irq_opaque, s->irq, 0); } } @@ -153,10 +158,16 @@ static unsigned int get_counter(CUDATimer *s) d = muldiv64(qemu_get_clock(vm_clock) - s->load_time, CUDA_TIMER_FREQ, ticks_per_sec); - if (d <= s->counter_value) { - counter = d; + if (s->index == 0) { + /* the timer goes down from latch to -1 (period of latch + 2) */ + if (d <= (s->counter_value + 1)) { + counter = (s->counter_value - d) & 0xffff; + } else { + counter = (d - (s->counter_value + 1)) % (s->latch + 2); + counter = (s->latch - counter) & 0xffff; + } } else { - counter = s->latch - 1 - ((d - s->counter_value) % s->latch); + counter = (s->counter_value - d) & 0xffff; } return counter; } @@ -174,17 +185,27 @@ static void set_counter(CUDAState *s, CUDATimer *ti, unsigned int val) static int64_t get_next_irq_time(CUDATimer *s, int64_t current_time) { - int64_t d, next_time, base; + int64_t d, next_time; + unsigned int counter; + /* current counter value */ d = muldiv64(current_time - s->load_time, CUDA_TIMER_FREQ, ticks_per_sec); - if (d < s->counter_value) { - next_time = s->counter_value + 1; - } else - { - base = ((d - s->counter_value + 1) / s->latch); - base = (base * s->latch) + s->counter_value; - next_time = base + s->latch; + /* the timer goes down from latch to -1 (period of latch + 2) */ + if (d <= (s->counter_value + 1)) { + counter = (s->counter_value - d) & 0xffff; + } else { + counter = (d - (s->counter_value + 1)) % (s->latch + 2); + counter = (s->latch - counter) & 0xffff; + } + + /* Note: we consider the irq is raised on 0 */ + if (counter == 0xffff) { + next_time = d + s->latch + 1; + } else if (counter == 0) { + next_time = d + s->latch + 2; + } else { + next_time = d + counter; } #if 0 #ifdef DEBUG_CUDA @@ -248,17 +269,18 @@ static uint32_t cuda_readb(void *opaque, target_phys_addr_t addr) break; case 5: val = get_counter(&s->timers[0]) >> 8; - s->ifr &= ~T1_INT; cuda_update_irq(s); break; case 6: val = s->timers[0].latch & 0xff; break; case 7: + /* XXX: check this */ val = (s->timers[0].latch >> 8) & 0xff; break; case 8: val = get_counter(&s->timers[1]) & 0xff; + s->ifr &= ~T2_INT; break; case 9: val = get_counter(&s->timers[1]) >> 8; @@ -276,9 +298,11 @@ static uint32_t cuda_readb(void *opaque, target_phys_addr_t addr) break; case 13: val = s->ifr; + if (s->ifr & s->ier) + val |= 0x80; break; case 14: - val = s->ier; + val = s->ier | 0x80; break; default: case 15: @@ -316,12 +340,13 @@ static void cuda_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) s->dira = val; break; case 4: - val = val | (get_counter(&s->timers[0]) & 0xff00); - set_counter(s, &s->timers[0], val); + s->timers[0].latch = (s->timers[0].latch & 0xff00) | val; + cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock)); break; case 5: - val = (val << 8) | (get_counter(&s->timers[0]) & 0xff); - set_counter(s, &s->timers[0], val); + s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8); + s->ifr &= ~T1_INT; + set_counter(s, &s->timers[0], s->timers[0].latch); break; case 6: s->timers[0].latch = (s->timers[0].latch & 0xff00) | val; @@ -329,15 +354,15 @@ static void cuda_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) break; case 7: s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8); + s->ifr &= ~T1_INT; cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock)); break; case 8: - val = val | (get_counter(&s->timers[1]) & 0xff00); + s->timers[1].latch = val; set_counter(s, &s->timers[1], val); break; case 9: - val = (val << 8) | (get_counter(&s->timers[1]) & 0xff); - set_counter(s, &s->timers[1], val); + set_counter(s, &s->timers[1], (val << 8) | s->timers[1].latch); break; case 10: s->sr = val; @@ -545,7 +570,7 @@ static void cuda_receive_packet_from_host(CUDAState *s, #ifdef DEBUG_CUDA_PACKET { int i; - printf("cuda_receive_packet_to_host:\n"); + printf("cuda_receive_packet_from_host:\n"); for(i = 0; i < len; i++) printf(" %02x", data[i]); printf("\n"); @@ -605,19 +630,24 @@ static CPUReadMemoryFunc *cuda_read[] = { &cuda_readl, }; -int cuda_init(openpic_t *openpic, int irq) +int cuda_init(SetIRQFunc *set_irq, void *irq_opaque, int irq) { CUDAState *s = &cuda_state; int cuda_mem_index; - s->openpic = openpic; + s->set_irq = set_irq; + s->irq_opaque = irq_opaque; s->irq = irq; + s->timers[0].index = 0; s->timers[0].timer = qemu_new_timer(vm_clock, cuda_timer1, s); - s->timers[0].latch = 0x10000; + s->timers[0].latch = 0xffff; set_counter(s, &s->timers[0], 0xffff); - s->timers[1].latch = 0x10000; - s->ier = T1_INT | SR_INT; + + s->timers[1].index = 1; + s->timers[1].latch = 0; + // s->ier = T1_INT | SR_INT; + s->ier = 0; set_counter(s, &s->timers[1], 0xffff); s->adb_poll_timer = qemu_new_timer(vm_clock, cuda_adb_poll, s); diff --git a/qemu/hw/elf_ops.h b/qemu/hw/elf_ops.h new file mode 100644 index 0000000..1f3232d --- /dev/null +++ b/qemu/hw/elf_ops.h @@ -0,0 +1,218 @@ +#ifdef BSWAP_NEEDED +static void glue(bswap_ehdr, SZ)(struct elfhdr *ehdr) +{ + bswap16s(&ehdr->e_type); /* Object file type */ + bswap16s(&ehdr->e_machine); /* Architecture */ + bswap32s(&ehdr->e_version); /* Object file version */ + bswapSZs(&ehdr->e_entry); /* Entry point virtual address */ + bswapSZs(&ehdr->e_phoff); /* Program header table file offset */ + bswapSZs(&ehdr->e_shoff); /* Section header table file offset */ + bswap32s(&ehdr->e_flags); /* Processor-specific flags */ + bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */ + bswap16s(&ehdr->e_phentsize); /* Program header table entry size */ + bswap16s(&ehdr->e_phnum); /* Program header table entry count */ + bswap16s(&ehdr->e_shentsize); /* Section header table entry size */ + bswap16s(&ehdr->e_shnum); /* Section header table entry count */ + bswap16s(&ehdr->e_shstrndx); /* Section header string table index */ +} + +static void glue(bswap_phdr, SZ)(struct elf_phdr *phdr) +{ + bswap32s(&phdr->p_type); /* Segment type */ + bswapSZs(&phdr->p_offset); /* Segment file offset */ + bswapSZs(&phdr->p_vaddr); /* Segment virtual address */ + bswapSZs(&phdr->p_paddr); /* Segment physical address */ + bswapSZs(&phdr->p_filesz); /* Segment size in file */ + bswapSZs(&phdr->p_memsz); /* Segment size in memory */ + bswap32s(&phdr->p_flags); /* Segment flags */ + bswapSZs(&phdr->p_align); /* Segment alignment */ +} + +static void glue(bswap_shdr, SZ)(struct elf_shdr *shdr) +{ + bswap32s(&shdr->sh_name); + bswap32s(&shdr->sh_type); + bswapSZs(&shdr->sh_flags); + bswapSZs(&shdr->sh_addr); + bswapSZs(&shdr->sh_offset); + bswapSZs(&shdr->sh_size); + bswap32s(&shdr->sh_link); + bswap32s(&shdr->sh_info); + bswapSZs(&shdr->sh_addralign); + bswapSZs(&shdr->sh_entsize); +} + +static void glue(bswap_sym, SZ)(struct elf_sym *sym) +{ + bswap32s(&sym->st_name); + bswapSZs(&sym->st_value); + bswapSZs(&sym->st_size); + bswap16s(&sym->st_shndx); +} +#endif + +static int glue(find_phdr, SZ)(struct elfhdr *ehdr, int fd, struct elf_phdr *phdr, elf_word type) +{ + int i, retval; + + retval = lseek(fd, ehdr->e_phoff, SEEK_SET); + if (retval < 0) + return -1; + + for (i = 0; i < ehdr->e_phnum; i++) { + retval = read(fd, phdr, sizeof(*phdr)); + if (retval < 0) + return -1; + glue(bswap_phdr, SZ)(phdr); + if (phdr->p_type == type) + return 0; + } + return -1; +} + +static void * glue(find_shdr, SZ)(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, elf_word type) +{ + int i, retval; + + retval = lseek(fd, ehdr->e_shoff, SEEK_SET); + if (retval < 0) + return NULL; + + for (i = 0; i < ehdr->e_shnum; i++) { + retval = read(fd, shdr, sizeof(*shdr)); + if (retval < 0) + return NULL; + glue(bswap_shdr, SZ)(shdr); + if (shdr->sh_type == type) + return qemu_malloc(shdr->sh_size); + } + return NULL; +} + +static void * glue(find_strtab, SZ)(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, struct elf_shdr *symtab) +{ + int retval; + + retval = lseek(fd, ehdr->e_shoff + sizeof(struct elf_shdr) * symtab->sh_link, SEEK_SET); + if (retval < 0) + return NULL; + + retval = read(fd, shdr, sizeof(*shdr)); + if (retval < 0) + return NULL; + glue(bswap_shdr, SZ)(shdr); + if (shdr->sh_type == SHT_STRTAB) + return qemu_malloc(shdr->sh_size);; + return NULL; +} + +static int glue(read_program, SZ)(int fd, struct elf_phdr *phdr, void *dst, elf_word entry) +{ + int retval; + retval = lseek(fd, phdr->p_offset + entry - phdr->p_vaddr, SEEK_SET); + if (retval < 0) + return -1; + return read(fd, dst, phdr->p_filesz); +} + +static int glue(read_section, SZ)(int fd, struct elf_shdr *s, void *dst) +{ + int retval; + + retval = lseek(fd, s->sh_offset, SEEK_SET); + if (retval < 0) + return -1; + retval = read(fd, dst, s->sh_size); + if (retval < 0) + return -1; + return 0; +} + +static void * glue(process_section, SZ)(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, elf_word type) +{ + void *dst; + + dst = glue(find_shdr, SZ)(ehdr, fd, shdr, type); + if (!dst) + goto error; + + if (glue(read_section, SZ)(fd, shdr, dst)) + goto error; + return dst; + error: + qemu_free(dst); + return NULL; +} + +static void * glue(process_strtab, SZ)(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, struct elf_shdr *symtab) +{ + void *dst; + + dst = glue(find_strtab, SZ)(ehdr, fd, shdr, symtab); + if (!dst) + goto error; + + if (glue(read_section, SZ)(fd, shdr, dst)) + goto error; + return dst; + error: + qemu_free(dst); + return NULL; +} + +static void glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd) +{ + struct elf_shdr symtab, strtab; + struct elf_sym *syms; +#if (SZ == 64) + struct elf32_sym *syms32; +#endif + struct syminfo *s; + int nsyms, i; + char *str; + + /* Symbol table */ + syms = glue(process_section, SZ)(ehdr, fd, &symtab, SHT_SYMTAB); + if (!syms) + return; + + nsyms = symtab.sh_size / sizeof(struct elf_sym); +#if (SZ == 64) + syms32 = qemu_mallocz(nsyms * sizeof(struct elf32_sym)); +#endif + for (i = 0; i < nsyms; i++) { + glue(bswap_sym, SZ)(&syms[i]); +#if (SZ == 64) + syms32[i].st_name = syms[i].st_name; + syms32[i].st_info = syms[i].st_info; + syms32[i].st_other = syms[i].st_other; + syms32[i].st_shndx = syms[i].st_shndx; + syms32[i].st_value = syms[i].st_value & 0xffffffff; + syms32[i].st_size = syms[i].st_size & 0xffffffff; +#endif + } + /* String table */ + str = glue(process_strtab, SZ)(ehdr, fd, &strtab, &symtab); + if (!str) + goto error_freesyms; + + /* Commit */ + s = qemu_mallocz(sizeof(*s)); +#if (SZ == 64) + s->disas_symtab = syms32; + qemu_free(syms); +#else + s->disas_symtab = syms; +#endif + s->disas_num_syms = nsyms; + s->disas_strtab = str; + s->next = syminfos; + syminfos = s; + return; + error_freesyms: +#if (SZ == 64) + qemu_free(syms32); +#endif + qemu_free(syms); + return; +} diff --git a/qemu/hw/heathrow_pic.c b/qemu/hw/heathrow_pic.c new file mode 100644 index 0000000..d65da9a --- /dev/null +++ b/qemu/hw/heathrow_pic.c @@ -0,0 +1,168 @@ +/* + * Heathrow PIC support (standard PowerMac PIC) + * + * Copyright (c) 2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +//#define DEBUG + +typedef struct HeathrowPIC { + uint32_t events; + uint32_t mask; + uint32_t levels; + uint32_t level_triggered; +} HeathrowPIC; + +struct HeathrowPICS { + HeathrowPIC pics[2]; +}; + +static inline int check_irq(HeathrowPIC *pic) +{ + return (pic->events | (pic->levels & pic->level_triggered)) & pic->mask; +} + +/* update the CPU irq state */ +static void heathrow_pic_update(HeathrowPICS *s) +{ + if (check_irq(&s->pics[0]) || check_irq(&s->pics[1])) { + cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HARD); + } else { + cpu_reset_interrupt(cpu_single_env, CPU_INTERRUPT_HARD); + } +} + +static void pic_writel (void *opaque, target_phys_addr_t addr, uint32_t value) +{ + HeathrowPICS *s = opaque; + HeathrowPIC *pic; + unsigned int n; + + value = bswap32(value); +#ifdef DEBUG + printf("pic_writel: %08x: %08x\n", + addr, value); +#endif + n = ((addr & 0xfff) - 0x10) >> 4; + if (n >= 2) + return; + pic = &s->pics[n]; + switch(addr & 0xf) { + case 0x04: + pic->mask = value; + heathrow_pic_update(s); + break; + case 0x08: + /* do not reset level triggered IRQs */ + value &= ~pic->level_triggered; + pic->events &= ~value; + heathrow_pic_update(s); + break; + default: + break; + } +} + +static uint32_t pic_readl (void *opaque, target_phys_addr_t addr) +{ + HeathrowPICS *s = opaque; + HeathrowPIC *pic; + unsigned int n; + uint32_t value; + + n = ((addr & 0xfff) - 0x10) >> 4; + if (n >= 2) { + value = 0; + } else { + pic = &s->pics[n]; + switch(addr & 0xf) { + case 0x0: + value = pic->events; + break; + case 0x4: + value = pic->mask; + break; + case 0xc: + value = pic->levels; + break; + default: + value = 0; + break; + } + } +#ifdef DEBUG + printf("pic_readl: %08x: %08x\n", + addr, value); +#endif + value = bswap32(value); + return value; +} + +static CPUWriteMemoryFunc *pic_write[] = { + &pic_writel, + &pic_writel, + &pic_writel, +}; + +static CPUReadMemoryFunc *pic_read[] = { + &pic_readl, + &pic_readl, + &pic_readl, +}; + + +void heathrow_pic_set_irq(void *opaque, int num, int level) +{ + HeathrowPICS *s = opaque; + HeathrowPIC *pic; + unsigned int irq_bit; + +#if defined(DEBUG) + { + static int last_level[64]; + if (last_level[num] != level) { + printf("set_irq: num=0x%02x level=%d\n", num, level); + last_level[num] = level; + } + } +#endif + pic = &s->pics[1 - (num >> 5)]; + irq_bit = 1 << (num & 0x1f); + if (level) { + pic->events |= irq_bit & ~pic->level_triggered; + pic->levels |= irq_bit; + } else { + pic->levels &= ~irq_bit; + } + heathrow_pic_update(s); +} + +HeathrowPICS *heathrow_pic_init(int *pmem_index) +{ + HeathrowPICS *s; + + s = qemu_mallocz(sizeof(HeathrowPICS)); + s->pics[0].level_triggered = 0; + s->pics[1].level_triggered = 0x1ff00000; + *pmem_index = cpu_register_io_memory(0, pic_read, pic_write, s); + return s; +} diff --git a/qemu/hw/i8259.c b/qemu/hw/i8259.c index 8f1821d..00d5068 100644 --- a/qemu/hw/i8259.c +++ b/qemu/hw/i8259.c @@ -46,10 +46,19 @@ typedef struct PicState { uint8_t init4; /* true if 4 byte init */ uint8_t elcr; /* PIIX edge/trigger selection*/ uint8_t elcr_mask; + PicState2 *pics_state; } PicState; -/* 0 is master pic, 1 is slave pic */ -static PicState pics[2]; +struct PicState2 { + /* 0 is master pic, 1 is slave pic */ + /* XXX: better separation between the two pics */ + PicState pics[2]; + IRQRequestFunc *irq_request; + void *irq_request_opaque; + /* IOAPIC callback support */ + SetIRQFunc *alt_irq_func; + void *alt_irq_opaque; +}; #if defined(DEBUG_PIC) || defined (DEBUG_IRQ_COUNT) static int irq_level[16]; @@ -110,7 +119,7 @@ static int pic_get_irq(PicState *s) master, the IRQ coming from the slave is not taken into account for the priority computation. */ mask = s->isr; - if (s->special_fully_nested_mode && s == &pics[0]) + if (s->special_fully_nested_mode && s == &s->pics_state->pics[0]) mask &= ~(1 << 2); cur_priority = get_priority(s, mask); if (priority < cur_priority) { @@ -123,32 +132,34 @@ static int pic_get_irq(PicState *s) /* raise irq to CPU if necessary. must be called every time the active irq may change */ -static void pic_update_irq(void) +/* XXX: should not export it, but it is needed for an APIC kludge */ +void pic_update_irq(PicState2 *s) { int irq2, irq; /* first look at slave pic */ - irq2 = pic_get_irq(&pics[1]); + irq2 = pic_get_irq(&s->pics[1]); if (irq2 >= 0) { /* if irq request by slave pic, signal master PIC */ - pic_set_irq1(&pics[0], 2, 1); - pic_set_irq1(&pics[0], 2, 0); + pic_set_irq1(&s->pics[0], 2, 1); + pic_set_irq1(&s->pics[0], 2, 0); } /* look at requested irq */ - irq = pic_get_irq(&pics[0]); + irq = pic_get_irq(&s->pics[0]); if (irq >= 0) { #if defined(DEBUG_PIC) { int i; for(i = 0; i < 2; i++) { printf("pic%d: imr=%x irr=%x padd=%d\n", - i, pics[i].imr, pics[i].irr, pics[i].priority_add); + i, s->pics[i].imr, s->pics[i].irr, + s->pics[i].priority_add); } } printf("pic: cpu_interrupt\n"); #endif - cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HARD); + s->irq_request(s->irq_request_opaque, 1); } } @@ -156,8 +167,10 @@ static void pic_update_irq(void) int64_t irq_time[16]; #endif -void pic_set_irq(int irq, int level) +void pic_set_irq_new(void *opaque, int irq, int level) { + PicState2 *s = opaque; + #if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) if (level != irq_level[irq]) { #if defined(DEBUG_PIC) @@ -175,8 +188,17 @@ void pic_set_irq(int irq, int level) irq_time[irq] = qemu_get_clock(vm_clock); } #endif - pic_set_irq1(&pics[irq >> 3], irq & 7, level); - pic_update_irq(); + pic_set_irq1(&s->pics[irq >> 3], irq & 7, level); + /* used for IOAPIC irqs */ + if (s->alt_irq_func) + s->alt_irq_func(s->alt_irq_opaque, irq, level); + pic_update_irq(s); +} + +/* obsolete function */ +void pic_set_irq(int irq, int level) +{ + pic_set_irq_new(isa_pic, irq, level); } /* acknowledge interrupt 'irq' */ @@ -193,43 +215,32 @@ static inline void pic_intack(PicState *s, int irq) s->irr &= ~(1 << irq); } -int cpu_get_pic_interrupt(CPUState *env) +int pic_read_irq(PicState2 *s) { int irq, irq2, intno; -#ifdef TARGET_X86_64 - intno = apic_get_interrupt(env); - if (intno >= 0) { - /* set irq request if a PIC irq is still pending */ - /* XXX: improve that */ - pic_update_irq(); - return intno; - } -#endif - /* read the irq from the PIC */ - - irq = pic_get_irq(&pics[0]); + irq = pic_get_irq(&s->pics[0]); if (irq >= 0) { - pic_intack(&pics[0], irq); + pic_intack(&s->pics[0], irq); if (irq == 2) { - irq2 = pic_get_irq(&pics[1]); + irq2 = pic_get_irq(&s->pics[1]); if (irq2 >= 0) { - pic_intack(&pics[1], irq2); + pic_intack(&s->pics[1], irq2); } else { /* spurious IRQ on slave controller */ irq2 = 7; } - intno = pics[1].irq_base + irq2; + intno = s->pics[1].irq_base + irq2; irq = irq2 + 8; } else { - intno = pics[0].irq_base + irq; + intno = s->pics[0].irq_base + irq; } } else { /* spurious IRQ on host controller */ irq = 7; - intno = pics[0].irq_base + irq; + intno = s->pics[0].irq_base + irq; } - pic_update_irq(); + pic_update_irq(s); #ifdef DEBUG_IRQ_LATENCY printf("IRQ%d latency=%0.3fus\n", @@ -245,11 +256,22 @@ int cpu_get_pic_interrupt(CPUState *env) static void pic_reset(void *opaque) { PicState *s = opaque; - int tmp; - tmp = s->elcr_mask; - memset(s, 0, sizeof(PicState)); - s->elcr_mask = tmp; + s->last_irr = 0; + s->irr = 0; + s->imr = 0; + s->isr = 0; + s->priority_add = 0; + s->irq_base = 0; + s->read_reg_select = 0; + s->poll = 0; + s->special_mask = 0; + s->init_state = 0; + s->auto_eoi = 0; + s->rotate_on_auto_eoi = 0; + s->special_fully_nested_mode = 0; + s->init4 = 0; + s->elcr = 0; } static void pic_ioport_write(void *opaque, uint32_t addr, uint32_t val) @@ -266,8 +288,7 @@ static void pic_ioport_write(void *opaque, uint32_t addr, uint32_t val) /* init */ pic_reset(s); /* deassert a pending interrupt */ - cpu_reset_interrupt(cpu_single_env, CPU_INTERRUPT_HARD); - + s->pics_state->irq_request(s->pics_state->irq_request_opaque, 0); s->init_state = 1; s->init4 = val & 1; if (val & 0x02) @@ -296,23 +317,23 @@ static void pic_ioport_write(void *opaque, uint32_t addr, uint32_t val) s->isr &= ~(1 << irq); if (cmd == 5) s->priority_add = (irq + 1) & 7; - pic_update_irq(); + pic_update_irq(s->pics_state); } break; case 3: irq = val & 7; s->isr &= ~(1 << irq); - pic_update_irq(); + pic_update_irq(s->pics_state); break; case 6: s->priority_add = (val + 1) & 7; - pic_update_irq(); + pic_update_irq(s->pics_state); break; case 7: irq = val & 7; s->isr &= ~(1 << irq); s->priority_add = (irq + 1) & 7; - pic_update_irq(); + pic_update_irq(s->pics_state); break; default: /* no operation */ @@ -324,7 +345,7 @@ static void pic_ioport_write(void *opaque, uint32_t addr, uint32_t val) case 0: /* normal mode */ s->imr = val; - pic_update_irq(); + pic_update_irq(s->pics_state); break; case 1: s->irq_base = val & 0xf8; @@ -353,16 +374,16 @@ static uint32_t pic_poll_read (PicState *s, uint32_t addr1) ret = pic_get_irq(s); if (ret >= 0) { if (addr1 >> 7) { - pics[0].isr &= ~(1 << 2); - pics[0].irr &= ~(1 << 2); + s->pics_state->pics[0].isr &= ~(1 << 2); + s->pics_state->pics[0].irr &= ~(1 << 2); } s->irr &= ~(1 << ret); s->isr &= ~(1 << ret); if (addr1 >> 7 || ret != 2) - pic_update_irq(); + pic_update_irq(s->pics_state); } else { ret = 0x07; - pic_update_irq(); + pic_update_irq(s->pics_state); } return ret; @@ -396,15 +417,16 @@ static uint32_t pic_ioport_read(void *opaque, uint32_t addr1) } /* memory mapped interrupt status */ -uint32_t pic_intack_read(CPUState *env) +/* XXX: may be the same than pic_read_irq() */ +uint32_t pic_intack_read(PicState2 *s) { int ret; - ret = pic_poll_read(&pics[0], 0x00); + ret = pic_poll_read(&s->pics[0], 0x00); if (ret == 2) - ret = pic_poll_read(&pics[1], 0x80) + 8; + ret = pic_poll_read(&s->pics[1], 0x80) + 8; /* Prepare for ISR read */ - pics[0].read_reg_select = 1; + s->pics[0].read_reg_select = 1; return ret; } @@ -484,9 +506,12 @@ void pic_info(void) { int i; PicState *s; + + if (!isa_pic) + return; for(i=0;i<2;i++) { - s = &pics[i]; + s = &isa_pic->pics[i]; term_printf("pic%d: irr=%02x imr=%02x isr=%02x hprio=%d irq_base=%02x rr_sel=%d elcr=%02x fnm=%d\n", i, s->irr, s->imr, s->isr, s->priority_add, s->irq_base, s->read_reg_select, s->elcr, @@ -511,11 +536,26 @@ void irq_info(void) #endif } -void pic_init(void) +PicState2 *pic_init(IRQRequestFunc *irq_request, void *irq_request_opaque) { - pic_init1(0x20, 0x4d0, &pics[0]); - pic_init1(0xa0, 0x4d1, &pics[1]); - pics[0].elcr_mask = 0xf8; - pics[1].elcr_mask = 0xde; + PicState2 *s; + s = qemu_mallocz(sizeof(PicState2)); + if (!s) + return NULL; + pic_init1(0x20, 0x4d0, &s->pics[0]); + pic_init1(0xa0, 0x4d1, &s->pics[1]); + s->pics[0].elcr_mask = 0xf8; + s->pics[1].elcr_mask = 0xde; + s->irq_request = irq_request; + s->irq_request_opaque = irq_request_opaque; + s->pics[0].pics_state = s; + s->pics[1].pics_state = s; + return s; } +void pic_set_alt_irq_func(PicState2 *s, SetIRQFunc *alt_irq_func, + void *alt_irq_opaque) +{ + s->alt_irq_func = alt_irq_func; + s->alt_irq_opaque = alt_irq_opaque; +} diff --git a/qemu/hw/ide.c b/qemu/hw/ide.c index 3997a06..f5fd028 100644 --- a/qemu/hw/ide.c +++ b/qemu/hw/ide.c @@ -296,8 +296,9 @@ typedef struct IDEState { int cylinders, heads, sectors; int64_t nb_sectors; int mult_sectors; + SetIRQFunc *set_irq; + void *irq_opaque; int irq; - openpic_t *openpic; PCIDevice *pci_dev; struct BMDMAState *bmdma; int drive_serial; @@ -332,6 +333,7 @@ typedef struct IDEState { uint8_t *data_ptr; uint8_t *data_end; uint8_t io_buffer[MAX_MULT_SECTORS*512 + 4]; + QEMUTimer *sector_write_timer; /* only used for win2k instal hack */ } IDEState; #define BM_STATUS_DMAING 0x01 @@ -341,6 +343,18 @@ typedef struct IDEState { #define BM_CMD_START 0x01 #define BM_CMD_READ 0x08 +#define IDE_TYPE_PIIX3 0 +#define IDE_TYPE_CMD646 1 + +/* CMD646 specific */ +#define MRDMODE 0x71 +#define MRDMODE_INTR_CH0 0x04 +#define MRDMODE_INTR_CH1 0x08 +#define MRDMODE_BLK_CH0 0x10 +#define MRDMODE_BLK_CH1 0x20 +#define UDIDETCR0 0x73 +#define UDIDETCR1 0x7B + typedef int IDEDMAFunc(IDEState *s, target_phys_addr_t phys_addr, int transfer_size1); @@ -349,6 +363,8 @@ typedef struct BMDMAState { uint8_t cmd; uint8_t status; uint32_t addr; + + struct PCIIDEState *pci_dev; /* current transfer state */ IDEState *ide_if; IDEDMAFunc *dma_cb; @@ -358,6 +374,7 @@ typedef struct PCIIDEState { PCIDevice dev; IDEState ide_if[4]; BMDMAState bmdma[2]; + int type; /* see IDE_TYPE_xxx */ } PCIIDEState; static void ide_dma_start(IDEState *s, IDEDMAFunc *dma_cb); @@ -499,16 +516,12 @@ static inline void ide_abort_command(IDEState *s) static inline void ide_set_irq(IDEState *s) { + BMDMAState *bm = s->bmdma; if (!(s->cmd & IDE_CMD_DISABLE_IRQ)) { -#ifdef TARGET_PPC - if (s->openpic) - openpic_set_irq(s->openpic, s->irq, 1); - else -#endif - if (s->irq == 16) - pci_set_irq(s->pci_dev, 0, 1); - else - pic_set_irq(s->irq, 1); + if (bm) { + bm->status |= BM_STATUS_INT; + } + s->set_irq(s->irq_opaque, s->irq, 1); } } @@ -642,6 +655,12 @@ static void ide_sector_read_dma(IDEState *s) ide_dma_start(s, ide_read_dma_cb); } +static void ide_sector_write_timer_cb(void *opaque) +{ + IDEState *s = opaque; + ide_set_irq(s); +} + static void ide_sector_write(IDEState *s) { int64_t sector_num; @@ -667,7 +686,22 @@ static void ide_sector_write(IDEState *s) ide_transfer_start(s, s->io_buffer, 512 * n1, ide_sector_write); } ide_set_sector(s, sector_num + n); - ide_set_irq(s); + +#ifdef TARGET_I386 + if (win2k_install_hack) { + /* It seems there is a bug in the Windows 2000 installer HDD + IDE driver which fills the disk with empty logs when the + IDE write IRQ comes too early. This hack tries to correct + that at the expense of slower write performances. Use this + option _only_ to install Windows 2000. You must disable it + for normal use. */ + qemu_mod_timer(s->sector_write_timer, + qemu_get_clock(vm_clock) + (ticks_per_sec / 1000)); + } else +#endif + { + ide_set_irq(s); + } } static int ide_write_dma_cb(IDEState *s, @@ -789,7 +823,8 @@ static void cd_read_sector(BlockDriverState *bs, int lba, uint8_t *buf, case 2352: /* sync bytes */ buf[0] = 0x00; - memset(buf + 1, 0xff, 11); + memset(buf + 1, 0xff, 10); + buf[11] = 0x00; buf += 12; /* MSF */ lba_to_msf(buf, lba); @@ -917,6 +952,9 @@ static int ide_atapi_cmd_read_dma_cb(IDEState *s, transfer_size = transfer_size1; while (transfer_size > 0) { +#ifdef DEBUG_IDE_ATAPI + printf("transfer_size: %d phys_addr=%08x\n", transfer_size, phys_addr); +#endif if (s->packet_transfer_size <= 0) break; len = s->cd_sector_size - s->io_buffer_index; @@ -994,9 +1032,8 @@ static int cdrom_read_toc(IDEState *s, uint8_t *buf, int msf, int start_track) *q++ = 0; /* reserved */ if (msf) { *q++ = 0; /* reserved */ - *q++ = 0; /* minute */ - *q++ = 2; /* second */ - *q++ = 0; /* frame */ + lba_to_msf(q, 0); + q += 3; } else { /* sector 0 */ cpu_to_ube32(q, 0); @@ -1081,10 +1118,16 @@ static int cdrom_read_toc_raw(IDEState *s, uint8_t *buf, int msf, *q++ = 0; /* min */ *q++ = 0; /* sec */ *q++ = 0; /* frame */ - *q++ = 0; - *q++ = 0; - *q++ = 0; - *q++ = 0; + if (msf) { + *q++ = 0; + lba_to_msf(q, 0); + q += 3; + } else { + *q++ = 0; + *q++ = 0; + *q++ = 0; + *q++ = 0; + } len = q - buf; cpu_to_ube16(buf, len - 2); @@ -1571,6 +1614,7 @@ static void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val) break; case WIN_STANDBYNOW1: case WIN_IDLEIMMEDIATE: + case WIN_FLUSH_CACHE: s->status = READY_STAT; ide_set_irq(s); break; @@ -1672,15 +1716,7 @@ static uint32_t ide_ioport_read(void *opaque, uint32_t addr1) ret = 0; else ret = s->status; -#ifdef TARGET_PPC - if (s->openpic) - openpic_set_irq(s->openpic, s->irq, 0); - else -#endif - if (s->irq == 16) - pci_set_irq(s->pci_dev, 0, 0); - else - pic_set_irq(s->irq, 0); + s->set_irq(s->irq_opaque, s->irq, 0); break; } #ifdef DEBUG_IDE @@ -1873,8 +1909,9 @@ static int guess_disk_lchs(IDEState *s, return -1; } -static void ide_init2(IDEState *ide_state, int irq, - BlockDriverState *hd0, BlockDriverState *hd1) +static void ide_init2(IDEState *ide_state, + BlockDriverState *hd0, BlockDriverState *hd1, + SetIRQFunc *set_irq, void *irq_opaque, int irq) { IDEState *s; static int drive_serial = 1; @@ -1935,7 +1972,11 @@ static void ide_init2(IDEState *ide_state, int irq, } } s->drive_serial = drive_serial++; + s->set_irq = set_irq; + s->irq_opaque = irq_opaque; s->irq = irq; + s->sector_write_timer = qemu_new_timer(vm_clock, + ide_sector_write_timer_cb, s); ide_reset(s); } } @@ -1968,13 +2009,15 @@ void isa_ide_init(int iobase, int iobase2, int irq, if (!ide_state) return; - ide_init2(ide_state, irq, hd0, hd1); + ide_init2(ide_state, hd0, hd1, pic_set_irq_new, isa_pic, irq); ide_init_ioport(ide_state, iobase, iobase2); } /***********************************************************/ /* PCI IDE definitions */ +static void cmd646_update_irq(PCIIDEState *d); + static void ide_map(PCIDevice *pci_dev, int region_num, uint32_t addr, uint32_t size, int type) { @@ -2055,17 +2098,6 @@ static void ide_dma_start(IDEState *s, IDEDMAFunc *dma_cb) } } -static uint32_t bmdma_cmd_readb(void *opaque, uint32_t addr) -{ - BMDMAState *bm = opaque; - uint32_t val; - val = bm->cmd; -#ifdef DEBUG_IDE - printf("%s: 0x%08x\n", __func__, val); -#endif - return val; -} - static void bmdma_cmd_writeb(void *opaque, uint32_t addr, uint32_t val) { BMDMAState *bm = opaque; @@ -2085,24 +2117,77 @@ static void bmdma_cmd_writeb(void *opaque, uint32_t addr, uint32_t val) } } -static uint32_t bmdma_status_readb(void *opaque, uint32_t addr) +static uint32_t bmdma_readb(void *opaque, uint32_t addr) { BMDMAState *bm = opaque; + PCIIDEState *pci_dev; uint32_t val; - val = bm->status; + + switch(addr & 3) { + case 0: + val = bm->cmd; + break; + case 1: + pci_dev = bm->pci_dev; + if (pci_dev->type == IDE_TYPE_CMD646) { + val = pci_dev->dev.config[MRDMODE]; + } else { + val = 0xff; + } + break; + case 2: + val = bm->status; + break; + case 3: + pci_dev = bm->pci_dev; + if (pci_dev->type == IDE_TYPE_CMD646) { + if (bm == &pci_dev->bmdma[0]) + val = pci_dev->dev.config[UDIDETCR0]; + else + val = pci_dev->dev.config[UDIDETCR1]; + } else { + val = 0xff; + } + break; + default: + val = 0xff; + break; + } #ifdef DEBUG_IDE - printf("%s: 0x%08x\n", __func__, val); + printf("bmdma: readb 0x%02x : 0x%02x\n", addr, val); #endif return val; } -static void bmdma_status_writeb(void *opaque, uint32_t addr, uint32_t val) +static void bmdma_writeb(void *opaque, uint32_t addr, uint32_t val) { BMDMAState *bm = opaque; + PCIIDEState *pci_dev; #ifdef DEBUG_IDE - printf("%s: 0x%08x\n", __func__, val); + printf("bmdma: writeb 0x%02x : 0x%02x\n", addr, val); #endif - bm->status = (val & 0x60) | (bm->status & 1) | (bm->status & ~val & 0x06); + switch(addr & 3) { + case 1: + pci_dev = bm->pci_dev; + if (pci_dev->type == IDE_TYPE_CMD646) { + pci_dev->dev.config[MRDMODE] = + (pci_dev->dev.config[MRDMODE] & ~0x30) | (val & 0x30); + cmd646_update_irq(pci_dev); + } + break; + case 2: + bm->status = (val & 0x60) | (bm->status & 1) | (bm->status & ~val & 0x06); + break; + case 3: + pci_dev = bm->pci_dev; + if (pci_dev->type == IDE_TYPE_CMD646) { + if (bm == &pci_dev->bmdma[0]) + pci_dev->dev.config[UDIDETCR0] = val; + else + pci_dev->dev.config[UDIDETCR1] = val; + } + break; + } } static uint32_t bmdma_addr_readl(void *opaque, uint32_t addr) @@ -2135,12 +2220,12 @@ static void bmdma_map(PCIDevice *pci_dev, int region_num, BMDMAState *bm = &d->bmdma[i]; d->ide_if[2 * i].bmdma = bm; d->ide_if[2 * i + 1].bmdma = bm; - + bm->pci_dev = (PCIIDEState *)pci_dev; + register_ioport_write(addr, 1, 1, bmdma_cmd_writeb, bm); - register_ioport_read(addr, 1, 1, bmdma_cmd_readb, bm); - register_ioport_write(addr + 2, 1, 1, bmdma_status_writeb, bm); - register_ioport_read(addr + 2, 1, 1, bmdma_status_readb, bm); + register_ioport_write(addr + 1, 3, 1, bmdma_writeb, bm); + register_ioport_read(addr, 4, 1, bmdma_readb, bm); register_ioport_write(addr + 4, 4, 4, bmdma_addr_writel, bm); register_ioport_read(addr + 4, 4, 4, bmdma_addr_readl, bm); @@ -2148,29 +2233,62 @@ static void bmdma_map(PCIDevice *pci_dev, int region_num, } } -/* hd_table must contain 4 block drivers */ -void pci_ide_init(PCIBus *bus, BlockDriverState **hd_table) +/* XXX: call it also when the MRDMODE is changed from the PCI config + registers */ +static void cmd646_update_irq(PCIIDEState *d) +{ + int pci_level; + pci_level = ((d->dev.config[MRDMODE] & MRDMODE_INTR_CH0) && + !(d->dev.config[MRDMODE] & MRDMODE_BLK_CH0)) || + ((d->dev.config[MRDMODE] & MRDMODE_INTR_CH1) && + !(d->dev.config[MRDMODE] & MRDMODE_BLK_CH1)); + pci_set_irq((PCIDevice *)d, 0, pci_level); +} + +/* the PCI irq level is the logical OR of the two channels */ +static void cmd646_set_irq(void *opaque, int channel, int level) +{ + PCIIDEState *d = opaque; + int irq_mask; + + irq_mask = MRDMODE_INTR_CH0 << channel; + if (level) + d->dev.config[MRDMODE] |= irq_mask; + else + d->dev.config[MRDMODE] &= ~irq_mask; + cmd646_update_irq(d); +} + +/* CMD646 PCI IDE controller */ +void pci_cmd646_ide_init(PCIBus *bus, BlockDriverState **hd_table, + int secondary_ide_enabled) { PCIIDEState *d; uint8_t *pci_conf; int i; - d = (PCIIDEState *)pci_register_device(bus, "IDE", sizeof(PCIIDEState), + d = (PCIIDEState *)pci_register_device(bus, "CMD646 IDE", + sizeof(PCIIDEState), -1, NULL, NULL); + d->type = IDE_TYPE_CMD646; pci_conf = d->dev.config; - pci_conf[0x00] = 0x86; // Intel - pci_conf[0x01] = 0x80; - pci_conf[0x02] = 0x00; // fake - pci_conf[0x03] = 0x01; // fake + pci_conf[0x00] = 0x95; // CMD646 + pci_conf[0x01] = 0x10; + pci_conf[0x02] = 0x46; + pci_conf[0x03] = 0x06; + + pci_conf[0x08] = 0x07; // IDE controller revision + pci_conf[0x09] = 0x8f; + pci_conf[0x0a] = 0x01; // class_sub = PCI_IDE pci_conf[0x0b] = 0x01; // class_base = PCI_mass_storage - pci_conf[0x0e] = 0x80; // header_type = PCI_multifunction, generic - - pci_conf[0x2c] = 0x86; // subsys vendor - pci_conf[0x2d] = 0x80; // subsys vendor - pci_conf[0x2e] = 0x00; // fake - pci_conf[0x2f] = 0x01; // fake + pci_conf[0x0e] = 0x00; // header_type + + if (secondary_ide_enabled) { + /* XXX: if not enabled, really disable the seconday IDE controller */ + pci_conf[0x51] = 0x80; /* enable IDE1 */ + } pci_register_io_region((PCIDevice *)d, 0, 0x8, PCI_ADDRESS_SPACE_IO, ide_map); @@ -2184,11 +2302,13 @@ void pci_ide_init(PCIBus *bus, BlockDriverState **hd_table) PCI_ADDRESS_SPACE_IO, bmdma_map); pci_conf[0x3d] = 0x01; // interrupt on pin 1 - + for(i = 0; i < 4; i++) d->ide_if[i].pci_dev = (PCIDevice *)d; - ide_init2(&d->ide_if[0], 16, hd_table[0], hd_table[1]); - ide_init2(&d->ide_if[2], 16, hd_table[2], hd_table[3]); + ide_init2(&d->ide_if[0], hd_table[0], hd_table[1], + cmd646_set_irq, d, 0); + ide_init2(&d->ide_if[2], hd_table[2], hd_table[3], + cmd646_set_irq, d, 1); } /* hd_table must contain 4 block drivers */ @@ -2203,6 +2323,8 @@ void pci_piix3_ide_init(PCIBus *bus, BlockDriverState **hd_table) sizeof(PCIIDEState), ((PCIDevice *)piix3_state)->devfn + 1, NULL, NULL); + d->type = IDE_TYPE_PIIX3; + pci_conf = d->dev.config; pci_conf[0x00] = 0x86; // Intel pci_conf[0x01] = 0x80; @@ -2215,8 +2337,10 @@ void pci_piix3_ide_init(PCIBus *bus, BlockDriverState **hd_table) pci_register_io_region((PCIDevice *)d, 4, 0x10, PCI_ADDRESS_SPACE_IO, bmdma_map); - ide_init2(&d->ide_if[0], 14, hd_table[0], hd_table[1]); - ide_init2(&d->ide_if[2], 15, hd_table[2], hd_table[3]); + ide_init2(&d->ide_if[0], hd_table[0], hd_table[1], + pic_set_irq_new, isa_pic, 14); + ide_init2(&d->ide_if[2], hd_table[2], hd_table[3], + pic_set_irq_new, isa_pic, 15); ide_init_ioport(&d->ide_if[0], 0x1f0, 0x3f6); ide_init_ioport(&d->ide_if[2], 0x170, 0x376); } @@ -2334,15 +2458,14 @@ static CPUReadMemoryFunc *pmac_ide_read[] = { /* PowerMac uses memory mapped registers, not I/O. Return the memory I/O index to access the ide. */ int pmac_ide_init (BlockDriverState **hd_table, - openpic_t *openpic, int irq) + SetIRQFunc *set_irq, void *irq_opaque, int irq) { IDEState *ide_if; int pmac_ide_memory; ide_if = qemu_mallocz(sizeof(IDEState) * 2); - ide_init2(&ide_if[0], irq, hd_table[0], hd_table[1]); - ide_if[0].openpic = openpic; - ide_if[1].openpic = openpic; + ide_init2(&ide_if[0], hd_table[0], hd_table[1], + set_irq, irq_opaque, irq); pmac_ide_memory = cpu_register_io_memory(0, pmac_ide_read, pmac_ide_write, &ide_if[0]); diff --git a/qemu/hw/magic-load.c b/qemu/hw/magic-load.c index 713343a..d5c098f 100644 --- a/qemu/hw/magic-load.c +++ b/qemu/hw/magic-load.c @@ -56,213 +56,49 @@ static void bswap_ahdr(struct exec *e) #include "elf.h" -#ifdef BSWAP_NEEDED -static void bswap_ehdr(Elf32_Ehdr *ehdr) -{ - bswap16s(&ehdr->e_type); /* Object file type */ - bswap16s(&ehdr->e_machine); /* Architecture */ - bswap32s(&ehdr->e_version); /* Object file version */ - bswap32s(&ehdr->e_entry); /* Entry point virtual address */ - bswap32s(&ehdr->e_phoff); /* Program header table file offset */ - bswap32s(&ehdr->e_shoff); /* Section header table file offset */ - bswap32s(&ehdr->e_flags); /* Processor-specific flags */ - bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */ - bswap16s(&ehdr->e_phentsize); /* Program header table entry size */ - bswap16s(&ehdr->e_phnum); /* Program header table entry count */ - bswap16s(&ehdr->e_shentsize); /* Section header table entry size */ - bswap16s(&ehdr->e_shnum); /* Section header table entry count */ - bswap16s(&ehdr->e_shstrndx); /* Section header string table index */ -} - -static void bswap_phdr(Elf32_Phdr *phdr) -{ - bswap32s(&phdr->p_type); /* Segment type */ - bswap32s(&phdr->p_offset); /* Segment file offset */ - bswap32s(&phdr->p_vaddr); /* Segment virtual address */ - bswap32s(&phdr->p_paddr); /* Segment physical address */ - bswap32s(&phdr->p_filesz); /* Segment size in file */ - bswap32s(&phdr->p_memsz); /* Segment size in memory */ - bswap32s(&phdr->p_flags); /* Segment flags */ - bswap32s(&phdr->p_align); /* Segment alignment */ -} - -static void bswap_shdr(Elf32_Shdr *shdr) -{ - bswap32s(&shdr->sh_name); - bswap32s(&shdr->sh_type); - bswap32s(&shdr->sh_flags); - bswap32s(&shdr->sh_addr); - bswap32s(&shdr->sh_offset); - bswap32s(&shdr->sh_size); - bswap32s(&shdr->sh_link); - bswap32s(&shdr->sh_info); - bswap32s(&shdr->sh_addralign); - bswap32s(&shdr->sh_entsize); -} - -static void bswap_sym(Elf32_Sym *sym) -{ - bswap32s(&sym->st_name); - bswap32s(&sym->st_value); - bswap32s(&sym->st_size); - bswap16s(&sym->st_shndx); -} -#else -#define bswap_ehdr(e) do { } while (0) -#define bswap_phdr(e) do { } while (0) -#define bswap_shdr(e) do { } while (0) -#define bswap_sym(e) do { } while (0) +#ifndef BSWAP_NEEDED +#define bswap_ehdr32(e) do { } while (0) +#define bswap_phdr32(e) do { } while (0) +#define bswap_shdr32(e) do { } while (0) +#define bswap_sym32(e) do { } while (0) +#ifdef TARGET_SPARC64 +#define bswap_ehdr64(e) do { } while (0) +#define bswap_phdr64(e) do { } while (0) +#define bswap_shdr64(e) do { } while (0) +#define bswap_sym64(e) do { } while (0) +#endif #endif -static int find_phdr(struct elfhdr *ehdr, int fd, struct elf_phdr *phdr, uint32_t type) -{ - int i, retval; - - retval = lseek(fd, ehdr->e_phoff, SEEK_SET); - if (retval < 0) - return -1; - - for (i = 0; i < ehdr->e_phnum; i++) { - retval = read(fd, phdr, sizeof(*phdr)); - if (retval < 0) - return -1; - bswap_phdr(phdr); - if (phdr->p_type == type) - return 0; - } - return -1; -} - -static void *find_shdr(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, uint32_t type) -{ - int i, retval; - - retval = lseek(fd, ehdr->e_shoff, SEEK_SET); - if (retval < 0) - return NULL; - - for (i = 0; i < ehdr->e_shnum; i++) { - retval = read(fd, shdr, sizeof(*shdr)); - if (retval < 0) - return NULL; - bswap_shdr(shdr); - if (shdr->sh_type == type) - return qemu_malloc(shdr->sh_size); - } - return NULL; -} - -static void *find_strtab(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, struct elf_shdr *symtab) -{ - int retval; - - retval = lseek(fd, ehdr->e_shoff + sizeof(struct elf_shdr) * symtab->sh_link, SEEK_SET); - if (retval < 0) - return NULL; - - retval = read(fd, shdr, sizeof(*shdr)); - if (retval < 0) - return NULL; - bswap_shdr(shdr); - if (shdr->sh_type == SHT_STRTAB) - return qemu_malloc(shdr->sh_size);; - return NULL; -} - -static int read_program(int fd, struct elf_phdr *phdr, void *dst, uint32_t entry) -{ - int retval; - retval = lseek(fd, phdr->p_offset + entry - phdr->p_vaddr, SEEK_SET); - if (retval < 0) - return -1; - return read(fd, dst, phdr->p_filesz); -} - -static int read_section(int fd, struct elf_shdr *s, void *dst) -{ - int retval; - - retval = lseek(fd, s->sh_offset, SEEK_SET); - if (retval < 0) - return -1; - retval = read(fd, dst, s->sh_size); - if (retval < 0) - return -1; - return 0; -} - -static void *process_section(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, uint32_t type) -{ - void *dst; - - dst = find_shdr(ehdr, fd, shdr, type); - if (!dst) - goto error; - - if (read_section(fd, shdr, dst)) - goto error; - return dst; - error: - qemu_free(dst); - return NULL; -} - -static void *process_strtab(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, struct elf_shdr *symtab) -{ - void *dst; - - dst = find_strtab(ehdr, fd, shdr, symtab); - if (!dst) - goto error; - - if (read_section(fd, shdr, dst)) - goto error; - return dst; - error: - qemu_free(dst); - return NULL; -} - -static void load_symbols(struct elfhdr *ehdr, int fd) -{ - struct elf_shdr symtab, strtab; - struct elf_sym *syms; - struct syminfo *s; - int nsyms, i; - char *str; - - /* Symbol table */ - syms = process_section(ehdr, fd, &symtab, SHT_SYMTAB); - if (!syms) - return; - - nsyms = symtab.sh_size / sizeof(struct elf_sym); - for (i = 0; i < nsyms; i++) - bswap_sym(&syms[i]); - - /* String table */ - str = process_strtab(ehdr, fd, &strtab, &symtab); - if (!str) - goto error_freesyms; - - /* Commit */ - s = qemu_mallocz(sizeof(*s)); - s->disas_symtab = syms; - s->disas_num_syms = nsyms; - s->disas_strtab = str; - s->next = syminfos; - syminfos = s; - return; - error_freesyms: - qemu_free(syms); - return; -} +#define SZ 32 +#define elf_word uint32_t +#define bswapSZs bswap32s +#include "elf_ops.h" + +#ifdef TARGET_SPARC64 +#undef elfhdr +#undef elf_phdr +#undef elf_shdr +#undef elf_sym +#undef elf_note +#undef elf_word +#undef bswapSZs +#undef SZ +#define elfhdr elf64_hdr +#define elf_phdr elf64_phdr +#define elf_note elf64_note +#define elf_shdr elf64_shdr +#define elf_sym elf64_sym +#define elf_word uint64_t +#define bswapSZs bswap64s +#define SZ 64 +#include "elf_ops.h" +#endif int load_elf(const char *filename, uint8_t *addr) { - struct elfhdr ehdr; - struct elf_phdr phdr; + struct elf32_hdr ehdr; int retval, fd; + Elf32_Half machine; fd = open(filename, O_RDONLY | O_BINARY); if (fd < 0) @@ -272,21 +108,43 @@ int load_elf(const char *filename, uint8_t *addr) if (retval < 0) goto error; - bswap_ehdr(&ehdr); - if (ehdr.e_ident[0] != 0x7f || ehdr.e_ident[1] != 'E' - || ehdr.e_ident[2] != 'L' || ehdr.e_ident[3] != 'F' - || (ehdr.e_machine != EM_SPARC - && ehdr.e_machine != EM_SPARC32PLUS)) + || ehdr.e_ident[2] != 'L' || ehdr.e_ident[3] != 'F') goto error; + machine = tswap16(ehdr.e_machine); + if (machine == EM_SPARC || machine == EM_SPARC32PLUS) { + struct elf32_phdr phdr; - if (find_phdr(&ehdr, fd, &phdr, PT_LOAD)) - goto error; - retval = read_program(fd, &phdr, addr, ehdr.e_entry); - if (retval < 0) - goto error; + bswap_ehdr32(&ehdr); - load_symbols(&ehdr, fd); + if (find_phdr32(&ehdr, fd, &phdr, PT_LOAD)) + goto error; + retval = read_program32(fd, &phdr, addr, ehdr.e_entry); + if (retval < 0) + goto error; + load_symbols32(&ehdr, fd); + } +#ifdef TARGET_SPARC64 + else if (machine == EM_SPARCV9) { + struct elf64_hdr ehdr64; + struct elf64_phdr phdr; + + lseek(fd, 0, SEEK_SET); + + retval = read(fd, &ehdr64, sizeof(ehdr64)); + if (retval < 0) + goto error; + + bswap_ehdr64(&ehdr64); + + if (find_phdr64(&ehdr64, fd, &phdr, PT_LOAD)) + goto error; + retval = read_program64(fd, &phdr, phys_ram_base + ehdr64.e_entry, ehdr64.e_entry); + if (retval < 0) + goto error; + load_symbols64(&ehdr64, fd); + } +#endif close(fd); return retval; diff --git a/qemu/hw/mips_r4k.c b/qemu/hw/mips_r4k.c new file mode 100644 index 0000000..ca135ac --- /dev/null +++ b/qemu/hw/mips_r4k.c @@ -0,0 +1,254 @@ +#include "vl.h" + +#define BIOS_FILENAME "mips_bios.bin" +//#define BIOS_FILENAME "system.bin" +#define KERNEL_LOAD_ADDR 0x80010000 +#define INITRD_LOAD_ADDR 0x80800000 + +extern FILE *logfile; + +static void pic_irq_request(void *opaque, int level) +{ + if (level) { + cpu_single_env->CP0_Cause |= 0x00000400; + cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HARD); + } else { + cpu_single_env->CP0_Cause &= ~0x00000400; + cpu_reset_interrupt(cpu_single_env, CPU_INTERRUPT_HARD); + } +} + +void cpu_mips_irqctrl_init (void) +{ +} + +uint32_t cpu_mips_get_random (CPUState *env) +{ + uint32_t now = qemu_get_clock(vm_clock); + + return now % (MIPS_TLB_NB - env->CP0_Wired) + env->CP0_Wired; +} + +/* MIPS R4K timer */ +uint32_t cpu_mips_get_count (CPUState *env) +{ + return env->CP0_Count + + (uint32_t)muldiv64(qemu_get_clock(vm_clock), + 100 * 1000 * 1000, ticks_per_sec); +} + +static void cpu_mips_update_count (CPUState *env, uint32_t count, + uint32_t compare) +{ + uint64_t now, next; + uint32_t tmp; + + tmp = count; + if (count == compare) + tmp++; + now = qemu_get_clock(vm_clock); + next = now + muldiv64(compare - tmp, ticks_per_sec, 100 * 1000 * 1000); + if (next == now) + next++; +#if 1 + if (logfile) { + fprintf(logfile, "%s: 0x%08llx %08x %08x => 0x%08llx\n", + __func__, now, count, compare, next - now); + } +#endif + /* Store new count and compare registers */ + env->CP0_Compare = compare; + env->CP0_Count = + count - (uint32_t)muldiv64(now, 100 * 1000 * 1000, ticks_per_sec); + /* Adjust timer */ + qemu_mod_timer(env->timer, next); +} + +void cpu_mips_store_count (CPUState *env, uint32_t value) +{ + cpu_mips_update_count(env, value, env->CP0_Compare); +} + +void cpu_mips_store_compare (CPUState *env, uint32_t value) +{ + cpu_mips_update_count(env, cpu_mips_get_count(env), value); + pic_set_irq(5, 0); +} + +static void mips_timer_cb (void *opaque) +{ + CPUState *env; + + env = opaque; +#if 1 + if (logfile) { + fprintf(logfile, "%s\n", __func__); + } +#endif + cpu_mips_update_count(env, cpu_mips_get_count(env), env->CP0_Compare); + pic_set_irq(5, 1); +} + +void cpu_mips_clock_init (CPUState *env) +{ + env->timer = qemu_new_timer(vm_clock, &mips_timer_cb, env); + env->CP0_Compare = 0; + cpu_mips_update_count(env, 1, 0); +} + +static void io_writeb (void *opaque, target_phys_addr_t addr, uint32_t value) +{ + if (logfile) + fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, value); + cpu_outb(NULL, addr & 0xffff, value); +} + +static uint32_t io_readb (void *opaque, target_phys_addr_t addr) +{ + uint32_t ret = cpu_inb(NULL, addr & 0xffff); + if (logfile) + fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, ret); + return ret; +} + +static void io_writew (void *opaque, target_phys_addr_t addr, uint32_t value) +{ + if (logfile) + fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, value); +#ifdef TARGET_WORDS_BIGENDIAN + value = bswap16(value); +#endif + cpu_outw(NULL, addr & 0xffff, value); +} + +static uint32_t io_readw (void *opaque, target_phys_addr_t addr) +{ + uint32_t ret = cpu_inw(NULL, addr & 0xffff); +#ifdef TARGET_WORDS_BIGENDIAN + ret = bswap16(ret); +#endif + if (logfile) + fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, ret); + return ret; +} + +static void io_writel (void *opaque, target_phys_addr_t addr, uint32_t value) +{ + if (logfile) + fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, value); +#ifdef TARGET_WORDS_BIGENDIAN + value = bswap32(value); +#endif + cpu_outl(NULL, addr & 0xffff, value); +} + +static uint32_t io_readl (void *opaque, target_phys_addr_t addr) +{ + uint32_t ret = cpu_inl(NULL, addr & 0xffff); + +#ifdef TARGET_WORDS_BIGENDIAN + ret = bswap32(ret); +#endif + if (logfile) + fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, ret); + return ret; +} + +CPUWriteMemoryFunc *io_write[] = { + &io_writeb, + &io_writew, + &io_writel, +}; + +CPUReadMemoryFunc *io_read[] = { + &io_readb, + &io_readw, + &io_readl, +}; + +void mips_r4k_init (int ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, int snapshot, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename) +{ + char buf[1024]; + target_ulong kernel_base, kernel_size, initrd_base, initrd_size; + unsigned long bios_offset; + int io_memory; + int linux_boot; + int ret; + + printf("%s: start\n", __func__); + linux_boot = (kernel_filename != NULL); + /* allocate RAM */ + cpu_register_physical_memory(0, ram_size, IO_MEM_RAM); + bios_offset = ram_size + vga_ram_size; + snprintf(buf, sizeof(buf), "%s/%s", bios_dir, BIOS_FILENAME); + printf("%s: load BIOS '%s' size %d\n", __func__, buf, BIOS_SIZE); + ret = load_image(buf, phys_ram_base + bios_offset); + if (ret != BIOS_SIZE) { + fprintf(stderr, "qemu: could not load MIPS bios '%s'\n", buf); + exit(1); + } + cpu_register_physical_memory((uint32_t)(0x1fc00000), + BIOS_SIZE, bios_offset | IO_MEM_ROM); +#if 0 + memcpy(phys_ram_base + 0x10000, phys_ram_base + bios_offset, BIOS_SIZE); + cpu_single_env->PC = 0x80010004; +#else + cpu_single_env->PC = 0xBFC00004; +#endif + if (linux_boot) { + kernel_base = KERNEL_LOAD_ADDR; + /* now we can load the kernel */ + kernel_size = load_image(kernel_filename, + phys_ram_base + (kernel_base - 0x80000000)); + if (kernel_size == (target_ulong) -1) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + kernel_filename); + exit(1); + } + /* load initrd */ + if (initrd_filename) { + initrd_base = INITRD_LOAD_ADDR; + initrd_size = load_image(initrd_filename, + phys_ram_base + initrd_base); + if (initrd_size == (target_ulong) -1) { + fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", + initrd_filename); + exit(1); + } + } else { + initrd_base = 0; + initrd_size = 0; + } + cpu_single_env->PC = KERNEL_LOAD_ADDR; + } else { + kernel_base = 0; + kernel_size = 0; + initrd_base = 0; + initrd_size = 0; + } + + /* Init internal devices */ + cpu_mips_clock_init(cpu_single_env); + cpu_mips_irqctrl_init(); + + /* Register 64 KB of ISA IO space at 0x14000000 */ + io_memory = cpu_register_io_memory(0, io_read, io_write, NULL); + cpu_register_physical_memory(0x14000000, 0x00010000, io_memory); + isa_mem_base = 0x10000000; + + isa_pic = pic_init(pic_irq_request, cpu_single_env); + serial_init(0x3f8, 4, serial_hds[0]); + vga_initialize(NULL, ds, phys_ram_base + ram_size, ram_size, + vga_ram_size, 0, 0); + + isa_ne2000_init(0x300, 9, &nd_table[0]); +} + +QEMUMachine mips_machine = { + "mips", + "mips r4k platform", + mips_r4k_init, +}; diff --git a/qemu/hw/ne2000.c b/qemu/hw/ne2000.c index e1b656e..db61336 100644 --- a/qemu/hw/ne2000.c +++ b/qemu/hw/ne2000.c @@ -61,6 +61,9 @@ #define EN1_CURPAG 0x17 #define EN1_MULT 0x18 +#define EN2_STARTPG 0x21 /* Starting page of ring bfr RD */ +#define EN2_STOPPG 0x22 /* Ending page +1 of ring bfr RD */ + /* Register accessed at EN_CMD, the 8390 base addr. */ #define E8390_STOP 0x01 /* Stop and reset the chip */ #define E8390_START 0x02 /* Start the chip, clear reset */ @@ -150,7 +153,7 @@ static void ne2000_reset(NE2000State *s) static void ne2000_update_irq(NE2000State *s) { int isr; - isr = s->isr & s->imr; + isr = (s->isr & s->imr) & 0x7f; #if defined(DEBUG_NE2000) printf("NE2000: Set IRQ line %d to %d (%02x %02x)\n", s->irq, isr ? 1 : 0, s->isr, s->imr); @@ -255,7 +258,7 @@ static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val) if (addr == E8390_CMD) { /* control register */ s->cmd = val; - if (val & E8390_START) { + if (!(val & E8390_STOP)) { /* START bit makes no sense on RTL8029... */ s->isr &= ~ENISR_RESET; /* test specific case: zero length transfert */ if ((val & (E8390_RREAD | E8390_RWRITE)) && @@ -376,6 +379,12 @@ static uint32_t ne2000_ioport_read(void *opaque, uint32_t addr) case EN0_RSR: ret = s->rsr; break; + case EN2_STARTPG: + ret = s->start >> 8; + break; + case EN2_STOPPG: + ret = s->stop >> 8; + break; default: ret = 0x00; break; diff --git a/qemu/hw/openpic.c b/qemu/hw/openpic.c index e6bca32..08fb9bd 100644 --- a/qemu/hw/openpic.c +++ b/qemu/hw/openpic.c @@ -320,8 +320,9 @@ static void openpic_update_irq(openpic_t *opp, int n_IRQ) } } -void openpic_set_irq(openpic_t *opp, int n_IRQ, int level) +void openpic_set_irq(void *opaque, int n_IRQ, int level) { + openpic_t *opp = opaque; IRQ_src_t *src; src = &opp->src[n_IRQ]; diff --git a/qemu/hw/pc.c b/qemu/hw/pc.c index 5deb445..f0ae1b9 100644 --- a/qemu/hw/pc.c +++ b/qemu/hw/pc.c @@ -41,6 +41,7 @@ int dummy_refresh_clock; static fdctrl_t *floppy_controller; static RTCState *rtc_state; static PITState *pit; +static IOAPICState *ioapic; static void ioport80_write(void *opaque, uint32_t addr, uint32_t data) { @@ -65,6 +66,31 @@ uint64_t cpu_get_tsc(CPUX86State *env) return qemu_get_clock(vm_clock); } +/* IRQ handling */ +int cpu_get_pic_interrupt(CPUState *env) +{ + int intno; + + intno = apic_get_interrupt(env); + if (intno >= 0) { + /* set irq request if a PIC irq is still pending */ + /* XXX: improve that */ + pic_update_irq(isa_pic); + return intno; + } + /* read the irq from the PIC */ + intno = pic_read_irq(isa_pic); + return intno; +} + +static void pic_irq_request(void *opaque, int level) +{ + if (level) + cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HARD); + else + cpu_reset_interrupt(cpu_single_env, CPU_INTERRUPT_HARD); +} + /* PC cmos mappings */ #define REG_EQUIPMENT_BYTE 0x14 @@ -380,17 +406,17 @@ static int parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc }; static int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 }; /* PC hardware initialisation */ -void pc_init(int ram_size, int vga_ram_size, int boot_device, - DisplayState *ds, const char **fd_filename, int snapshot, - const char *kernel_filename, const char *kernel_cmdline, - const char *initrd_filename) +static void pc_init1(int ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, int snapshot, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename) { char buf[1024]; int ret, linux_boot, initrd_size, i, nb_nics1; unsigned long bios_offset, vga_bios_offset; int bios_size, isa_bios_size; PCIBus *pci_bus; - + linux_boot = (kernel_filename != NULL); /* allocate RAM */ @@ -520,7 +546,7 @@ void pc_init(int ram_size, int vga_ram_size, int boot_device, } } else { vga_initialize(pci_bus, ds, phys_ram_base + ram_size, ram_size, - vga_ram_size); + vga_ram_size, 0, 0); } rtc_state = rtc_init(0x70, 8); @@ -530,10 +556,15 @@ void pc_init(int ram_size, int vga_ram_size, int boot_device, register_ioport_read(0x92, 1, 1, ioport92_read, NULL); register_ioport_write(0x92, 1, 1, ioport92_write, NULL); - if (pci_enabled) + if (pci_enabled) { apic_init(cpu_single_env); - pic_init(); + ioapic = ioapic_init(); + } + isa_pic = pic_init(pic_irq_request, cpu_single_env); pit = pit_init(0x40, 0); + if (pci_enabled) { + pic_set_alt_irq_func(isa_pic, ioapic_set_irq, ioapic); + } for(i = 0; i < MAX_SERIAL_PORTS; i++) { if (serial_hds[i]) { @@ -595,3 +626,9 @@ void pc_init(int ram_size, int vga_ram_size, int boot_device, pci_bios_init(); } } + +QEMUMachine pc_machine = { + "pc", + "Standard PC", + pc_init1, +}; diff --git a/qemu/hw/pci.c b/qemu/hw/pci.c index cb7131a..efca2cd 100644 --- a/qemu/hw/pci.c +++ b/qemu/hw/pci.c @@ -45,7 +45,9 @@ struct PCIBus { int devfn_min; void (*set_irq)(PCIDevice *pci_dev, int irq_num, int level); uint32_t config_reg; /* XXX: suppress */ - openpic_t *openpic; /* XXX: suppress */ + /* low level pic */ + SetIRQFunc *low_set_irq; + void *irq_opaque; PCIDevice *devices[256]; }; @@ -498,6 +500,27 @@ static inline int pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num) return (irq_num + slot_addend) & 3; } +static inline int get_pci_irq_level(int irq_num) +{ + int pic_level; +#if (PCI_IRQ_WORDS == 2) + pic_level = ((pci_irq_levels[irq_num][0] | + pci_irq_levels[irq_num][1]) != 0); +#else + { + int i; + pic_level = 0; + for(i = 0; i < PCI_IRQ_WORDS; i++) { + if (pci_irq_levels[irq_num][i]) { + pic_level = 1; + break; + } + } + } +#endif + return pic_level; +} + static void piix3_set_irq(PCIDevice *pci_dev, int irq_num, int level) { int irq_index, shift, pic_irq, pic_level; @@ -510,26 +533,20 @@ static void piix3_set_irq(PCIDevice *pci_dev, int irq_num, int level) *p = (*p & ~(1 << shift)) | (level << shift); /* now we change the pic irq level according to the piix irq mappings */ + /* XXX: optimize */ pic_irq = piix3_state->dev.config[0x60 + irq_num]; if (pic_irq < 16) { /* the pic level is the logical OR of all the PCI irqs mapped to it */ pic_level = 0; -#if (PCI_IRQ_WORDS == 2) - pic_level = ((pci_irq_levels[irq_num][0] | - pci_irq_levels[irq_num][1]) != 0); -#else - { - int i; - pic_level = 0; - for(i = 0; i < PCI_IRQ_WORDS; i++) { - if (pci_irq_levels[irq_num][i]) { - pic_level = 1; - break; - } - } - } -#endif + if (pic_irq == piix3_state->dev.config[0x60]) + pic_level |= get_pci_irq_level(0); + if (pic_irq == piix3_state->dev.config[0x61]) + pic_level |= get_pci_irq_level(1); + if (pic_irq == piix3_state->dev.config[0x62]) + pic_level |= get_pci_irq_level(2); + if (pic_irq == piix3_state->dev.config[0x63]) + pic_level |= get_pci_irq_level(3); pic_set_irq(pic_irq, pic_level); } } @@ -708,25 +725,25 @@ PCIBus *pci_prep_init(void) PPC_PCIIO_write, s); cpu_register_physical_memory(0x80800000, 0x00400000, PPC_io_memory); - d = pci_register_device(s, "PREP PCI Bridge", sizeof(PCIDevice), 0, - NULL, NULL); - - /* XXX: put correct IDs */ - d->config[0x00] = 0x11; // vendor_id + /* PCI host bridge */ + d = pci_register_device(s, "PREP Host Bridge - Motorola Raven", + sizeof(PCIDevice), 0, NULL, NULL); + d->config[0x00] = 0x57; // vendor_id : Motorola d->config[0x01] = 0x10; - d->config[0x02] = 0x26; // device_id - d->config[0x03] = 0x00; - d->config[0x08] = 0x02; // revision - d->config[0x0a] = 0x04; // class_sub = pci2pci - d->config[0x0b] = 0x06; // class_base = PCI_bridge - d->config[0x0e] = 0x01; // header_type + d->config[0x02] = 0x01; // device_id : Raven + d->config[0x03] = 0x48; + d->config[0x08] = 0x00; // revision + d->config[0x0A] = 0x00; // class_sub = pci host + d->config[0x0B] = 0x06; // class_base = PCI_bridge + d->config[0x0C] = 0x08; // cache_line_size + d->config[0x0D] = 0x10; // latency_timer + d->config[0x0E] = 0x00; // header_type + d->config[0x34] = 0x00; // capabilities_pointer + return s; } -/* pmac pci init */ - -#if 0 /* Grackle PCI host */ static void pci_grackle_config_writel (void *opaque, target_phys_addr_t addr, uint32_t val) @@ -831,7 +848,93 @@ static CPUReadMemoryFunc *pci_grackle_read[] = { &pci_grackle_readw, &pci_grackle_readl, }; + +void pci_set_pic(PCIBus *bus, SetIRQFunc *set_irq, void *irq_opaque) +{ + bus->low_set_irq = set_irq; + bus->irq_opaque = irq_opaque; +} + +/* XXX: we do not simulate the hardware - we rely on the BIOS to + set correctly for irq line field */ +static void pci_set_irq_simple(PCIDevice *d, int irq_num, int level) +{ + PCIBus *s = d->bus; + s->low_set_irq(s->irq_opaque, d->config[PCI_INTERRUPT_LINE], level); +} + +PCIBus *pci_grackle_init(uint32_t base) +{ + PCIBus *s; + PCIDevice *d; + int pci_mem_config, pci_mem_data; + + s = pci_register_bus(); + s->set_irq = pci_set_irq_simple; + + pci_mem_config = cpu_register_io_memory(0, pci_grackle_config_read, + pci_grackle_config_write, s); + pci_mem_data = cpu_register_io_memory(0, pci_grackle_read, + pci_grackle_write, s); + cpu_register_physical_memory(base, 0x1000, pci_mem_config); + cpu_register_physical_memory(base + 0x00200000, 0x1000, pci_mem_data); + d = pci_register_device(s, "Grackle host bridge", sizeof(PCIDevice), + 0, NULL, NULL); + d->config[0x00] = 0x57; // vendor_id + d->config[0x01] = 0x10; + d->config[0x02] = 0x02; // device_id + d->config[0x03] = 0x00; + d->config[0x08] = 0x00; // revision + d->config[0x09] = 0x01; + d->config[0x0a] = 0x00; // class_sub = host + d->config[0x0b] = 0x06; // class_base = PCI_bridge + d->config[0x0e] = 0x00; // header_type + + d->config[0x18] = 0x00; // primary_bus + d->config[0x19] = 0x01; // secondary_bus + d->config[0x1a] = 0x00; // subordinate_bus + d->config[0x1c] = 0x00; + d->config[0x1d] = 0x00; + + d->config[0x20] = 0x00; // memory_base + d->config[0x21] = 0x00; + d->config[0x22] = 0x01; // memory_limit + d->config[0x23] = 0x00; + + d->config[0x24] = 0x00; // prefetchable_memory_base + d->config[0x25] = 0x00; + d->config[0x26] = 0x00; // prefetchable_memory_limit + d->config[0x27] = 0x00; + +#if 0 + /* PCI2PCI bridge same values as PearPC - check this */ + d->config[0x00] = 0x11; // vendor_id + d->config[0x01] = 0x10; + d->config[0x02] = 0x26; // device_id + d->config[0x03] = 0x00; + d->config[0x08] = 0x02; // revision + d->config[0x0a] = 0x04; // class_sub = pci2pci + d->config[0x0b] = 0x06; // class_base = PCI_bridge + d->config[0x0e] = 0x01; // header_type + + d->config[0x18] = 0x0; // primary_bus + d->config[0x19] = 0x1; // secondary_bus + d->config[0x1a] = 0x1; // subordinate_bus + d->config[0x1c] = 0x10; // io_base + d->config[0x1d] = 0x20; // io_limit + + d->config[0x20] = 0x80; // memory_base + d->config[0x21] = 0x80; + d->config[0x22] = 0x90; // memory_limit + d->config[0x23] = 0x80; + + d->config[0x24] = 0x00; // prefetchable_memory_base + d->config[0x25] = 0x84; + d->config[0x26] = 0x00; // prefetchable_memory_limit + d->config[0x27] = 0x85; #endif + return s; +} /* Uninorth PCI host (for all Mac99 and newer machines */ static void pci_unin_main_config_writel (void *opaque, target_phys_addr_t addr, @@ -1073,23 +1176,6 @@ static CPUReadMemoryFunc *pci_unin_read[] = { }; #endif -static void pmac_set_irq(PCIDevice *d, int irq_num, int level) -{ - openpic_t *openpic; - /* XXX: we do not simulate the hardware - we rely on the BIOS to - set correctly for irq line field */ - openpic = d->bus->openpic; -#ifdef TARGET_PPC - if (openpic) - openpic_set_irq(openpic, d->config[PCI_INTERRUPT_LINE], level); -#endif -} - -void pci_pmac_set_openpic(PCIBus *bus, openpic_t *openpic) -{ - bus->openpic = openpic; -} - PCIBus *pci_pmac_init(void) { PCIBus *s; @@ -1099,7 +1185,7 @@ PCIBus *pci_pmac_init(void) /* Use values found on a real PowerMac */ /* Uninorth main bus */ s = pci_register_bus(); - s->set_irq = pmac_set_irq; + s->set_irq = pci_set_irq_simple; pci_mem_config = cpu_register_io_memory(0, pci_unin_main_config_read, pci_unin_main_config_write, s); @@ -1202,34 +1288,253 @@ PCIBus *pci_pmac_init(void) d->config[0x0E] = 0x00; // header_type d->config[0x34] = 0x00; // capabilities_pointer #endif + return s; +} -#if 0 // Grackle ? - /* same values as PearPC - check this */ - d->config[0x00] = 0x11; // vendor_id - d->config[0x01] = 0x10; - d->config[0x02] = 0x26; // device_id - d->config[0x03] = 0x00; - d->config[0x08] = 0x02; // revision - d->config[0x0a] = 0x04; // class_sub = pci2pci - d->config[0x0b] = 0x06; // class_base = PCI_bridge - d->config[0x0e] = 0x01; // header_type +/* Ultrasparc APB PCI host */ +static void pci_apb_config_writel (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; + int i; - d->config[0x18] = 0x0; // primary_bus - d->config[0x19] = 0x1; // secondary_bus - d->config[0x1a] = 0x1; // subordinate_bus - d->config[0x1c] = 0x10; // io_base - d->config[0x1d] = 0x20; // io_limit - - d->config[0x20] = 0x80; // memory_base - d->config[0x21] = 0x80; - d->config[0x22] = 0x90; // memory_limit - d->config[0x23] = 0x80; - - d->config[0x24] = 0x00; // prefetchable_memory_base - d->config[0x25] = 0x84; - d->config[0x26] = 0x00; // prefetchable_memory_limit - d->config[0x27] = 0x85; -#endif + for (i = 11; i < 32; i++) { + if ((val & (1 << i)) != 0) + break; + } + s->config_reg = 0x80000000 | (1 << 16) | (val & 0x7FC) | (i << 11); +} + +static uint32_t pci_apb_config_readl (void *opaque, + target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + int devfn; + + devfn = (s->config_reg >> 8) & 0xFF; + val = (1 << (devfn >> 3)) | ((devfn & 0x07) << 8) | (s->config_reg & 0xFC); + return val; +} + +static CPUWriteMemoryFunc *pci_apb_config_write[] = { + &pci_apb_config_writel, + &pci_apb_config_writel, + &pci_apb_config_writel, +}; + +static CPUReadMemoryFunc *pci_apb_config_read[] = { + &pci_apb_config_readl, + &pci_apb_config_readl, + &pci_apb_config_readl, +}; + +static void apb_config_writel (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + //PCIBus *s = opaque; + + switch (addr & 0x3f) { + case 0x00: // Control/Status + case 0x10: // AFSR + case 0x18: // AFAR + case 0x20: // Diagnostic + case 0x28: // Target address space + // XXX + default: + break; + } +} + +static uint32_t apb_config_readl (void *opaque, + target_phys_addr_t addr) +{ + //PCIBus *s = opaque; + uint32_t val; + + switch (addr & 0x3f) { + case 0x00: // Control/Status + case 0x10: // AFSR + case 0x18: // AFAR + case 0x20: // Diagnostic + case 0x28: // Target address space + // XXX + default: + val = 0; + break; + } + return val; +} + +static CPUWriteMemoryFunc *apb_config_write[] = { + &apb_config_writel, + &apb_config_writel, + &apb_config_writel, +}; + +static CPUReadMemoryFunc *apb_config_read[] = { + &apb_config_readl, + &apb_config_readl, + &apb_config_readl, +}; + +static void pci_apb_writeb (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; + + pci_data_write(s, addr & 7, val, 1); +} + +static void pci_apb_writew (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; + + pci_data_write(s, addr & 7, val, 2); +} + +static void pci_apb_writel (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; + + pci_data_write(s, addr & 7, val, 4); +} + +static uint32_t pci_apb_readb (void *opaque, target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + + val = pci_data_read(s, addr & 7, 1); + return val; +} + +static uint32_t pci_apb_readw (void *opaque, target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + + val = pci_data_read(s, addr & 7, 2); + return val; +} + +static uint32_t pci_apb_readl (void *opaque, target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + + val = pci_data_read(s, addr, 4); + return val; +} + +static CPUWriteMemoryFunc *pci_apb_write[] = { + &pci_apb_writeb, + &pci_apb_writew, + &pci_apb_writel, +}; + +static CPUReadMemoryFunc *pci_apb_read[] = { + &pci_apb_readb, + &pci_apb_readw, + &pci_apb_readl, +}; + +static void pci_apb_iowriteb (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + cpu_outb(NULL, addr & 0xffff, val); +} + +static void pci_apb_iowritew (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + cpu_outw(NULL, addr & 0xffff, val); +} + +static void pci_apb_iowritel (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + cpu_outl(NULL, addr & 0xffff, val); +} + +static uint32_t pci_apb_ioreadb (void *opaque, target_phys_addr_t addr) +{ + uint32_t val; + + val = cpu_inb(NULL, addr & 0xffff); + return val; +} + +static uint32_t pci_apb_ioreadw (void *opaque, target_phys_addr_t addr) +{ + uint32_t val; + + val = cpu_inw(NULL, addr & 0xffff); + return val; +} + +static uint32_t pci_apb_ioreadl (void *opaque, target_phys_addr_t addr) +{ + uint32_t val; + + val = cpu_inl(NULL, addr & 0xffff); + return val; +} + +static CPUWriteMemoryFunc *pci_apb_iowrite[] = { + &pci_apb_iowriteb, + &pci_apb_iowritew, + &pci_apb_iowritel, +}; + +static CPUReadMemoryFunc *pci_apb_ioread[] = { + &pci_apb_ioreadb, + &pci_apb_ioreadw, + &pci_apb_ioreadl, +}; + +PCIBus *pci_apb_init(target_ulong special_base, target_ulong mem_base) +{ + PCIBus *s; + PCIDevice *d; + int pci_mem_config, pci_mem_data, apb_config, pci_ioport; + + /* Ultrasparc APB main bus */ + s = pci_register_bus(); + s->set_irq = pci_set_irq_simple; + + pci_mem_config = cpu_register_io_memory(0, pci_apb_config_read, + pci_apb_config_write, s); + apb_config = cpu_register_io_memory(0, apb_config_read, + apb_config_write, s); + pci_mem_data = cpu_register_io_memory(0, pci_apb_read, + pci_apb_write, s); + pci_ioport = cpu_register_io_memory(0, pci_apb_ioread, + pci_apb_iowrite, s); + + cpu_register_physical_memory(special_base + 0x2000ULL, 0x40, apb_config); + cpu_register_physical_memory(special_base + 0x1000000ULL, 0x10, pci_mem_config); + cpu_register_physical_memory(special_base + 0x2000000ULL, 0x10000, pci_ioport); + cpu_register_physical_memory(mem_base, 0x10000000, pci_mem_data); // XXX size should be 4G-prom + + d = pci_register_device(s, "Advanced PCI Bus", sizeof(PCIDevice), + -1, NULL, NULL); + d->config[0x00] = 0x8e; // vendor_id : Sun + d->config[0x01] = 0x10; + d->config[0x02] = 0x00; // device_id + d->config[0x03] = 0xa0; + d->config[0x04] = 0x06; // command = bus master, pci mem + d->config[0x05] = 0x00; + d->config[0x06] = 0xa0; // status = fast back-to-back, 66MHz, no error + d->config[0x07] = 0x03; // status = medium devsel + d->config[0x08] = 0x00; // revision + d->config[0x09] = 0x00; // programming i/f + d->config[0x0A] = 0x00; // class_sub = pci host + d->config[0x0B] = 0x06; // class_base = PCI_bridge + d->config[0x0D] = 0x10; // latency_timer + d->config[0x0E] = 0x00; // header_type return s; } diff --git a/qemu/hw/ppc.c b/qemu/hw/ppc.c index 5f99229..c460fec 100644 --- a/qemu/hw/ppc.c +++ b/qemu/hw/ppc.c @@ -107,13 +107,16 @@ uint32_t cpu_ppc_load_decr (CPUState *env) { ppc_tb_t *tb_env = env->tb_env; uint32_t decr; + int64_t diff; - decr = muldiv64(tb_env->decr_next - qemu_get_clock(vm_clock), - tb_env->tb_freq, ticks_per_sec); + diff = tb_env->decr_next - qemu_get_clock(vm_clock); + if (diff >= 0) + decr = muldiv64(diff, tb_env->tb_freq, ticks_per_sec); + else + decr = -muldiv64(-diff, tb_env->tb_freq, ticks_per_sec); #if defined(DEBUG_TB) printf("%s: 0x%08x\n", __func__, decr); #endif - return decr; } @@ -440,23 +443,4 @@ int PPC_NVRAM_set_params (m48t59_t *nvram, uint16_t NVRAM_size, NVRAM_set_word(nvram, 0xFC, crc); return 0; - } - -/*****************************************************************************/ -void ppc_init (int ram_size, int vga_ram_size, int boot_device, - DisplayState *ds, const char **fd_filename, int snapshot, - const char *kernel_filename, const char *kernel_cmdline, - const char *initrd_filename) -{ - if (prep_enabled) { - ppc_prep_init(ram_size, vga_ram_size, boot_device, ds, fd_filename, - snapshot, kernel_filename, kernel_cmdline, - initrd_filename); - } else { - ppc_chrp_init(ram_size, vga_ram_size, boot_device, ds, fd_filename, - snapshot, kernel_filename, kernel_cmdline, - initrd_filename); - } - /* Special port to get debug messages from Open-Firmware */ - register_ioport_write(0x0F00, 4, 1, &PPC_debug_write, NULL); } diff --git a/qemu/hw/ppc_chrp.c b/qemu/hw/ppc_chrp.c index f7c9b45..a76fc47 100644 --- a/qemu/hw/ppc_chrp.c +++ b/qemu/hw/ppc_chrp.c @@ -24,19 +24,22 @@ #include "vl.h" #define BIOS_FILENAME "ppc_rom.bin" +#define VGABIOS_FILENAME "video.x" #define NVRAM_SIZE 0x2000 #define KERNEL_LOAD_ADDR 0x01000000 #define INITRD_LOAD_ADDR 0x01800000 /* MacIO devices (mapped inside the MacIO address space): CUDA, DBDMA, - NVRAM (not implemented). */ + NVRAM */ static int dbdma_mem_index; static int cuda_mem_index; -static int ide0_mem_index; -static int ide1_mem_index; -static int openpic_mem_index; +static int ide0_mem_index = -1; +static int ide1_mem_index = -1; +static int openpic_mem_index = -1; +static int heathrow_pic_mem_index = -1; +static int macio_nvram_mem_index = -1; /* DBDMA: currently no op - should suffice right now */ @@ -81,17 +84,75 @@ static CPUReadMemoryFunc *dbdma_read[] = { &dbdma_readl, }; +/* macio style NVRAM device */ +typedef struct MacIONVRAMState { + uint8_t data[0x2000]; +} MacIONVRAMState; + +static void macio_nvram_writeb (void *opaque, target_phys_addr_t addr, uint32_t value) +{ + MacIONVRAMState *s = opaque; + addr = (addr >> 4) & 0x1fff; + s->data[addr] = value; + // printf("macio_nvram_writeb %04x = %02x\n", addr, value); +} + +static uint32_t macio_nvram_readb (void *opaque, target_phys_addr_t addr) +{ + MacIONVRAMState *s = opaque; + uint32_t value; + + addr = (addr >> 4) & 0x1fff; + value = s->data[addr]; + // printf("macio_nvram_readb %04x = %02x\n", addr, value); + return value; +} + +static CPUWriteMemoryFunc *macio_nvram_write[] = { + &macio_nvram_writeb, + &macio_nvram_writeb, + &macio_nvram_writeb, +}; + +static CPUReadMemoryFunc *macio_nvram_read[] = { + &macio_nvram_readb, + &macio_nvram_readb, + &macio_nvram_readb, +}; + +static MacIONVRAMState *macio_nvram_init(void) +{ + MacIONVRAMState *s; + s = qemu_mallocz(sizeof(MacIONVRAMState)); + if (!s) + return NULL; + macio_nvram_mem_index = cpu_register_io_memory(0, macio_nvram_read, + macio_nvram_write, s); + return s; +} + static void macio_map(PCIDevice *pci_dev, int region_num, uint32_t addr, uint32_t size, int type) { + if (heathrow_pic_mem_index >= 0) { + cpu_register_physical_memory(addr + 0x00000, 0x1000, + heathrow_pic_mem_index); + } cpu_register_physical_memory(addr + 0x08000, 0x1000, dbdma_mem_index); cpu_register_physical_memory(addr + 0x16000, 0x2000, cuda_mem_index); - cpu_register_physical_memory(addr + 0x1f000, 0x1000, ide0_mem_index); - cpu_register_physical_memory(addr + 0x20000, 0x1000, ide1_mem_index); - cpu_register_physical_memory(addr + 0x40000, 0x40000, openpic_mem_index); + if (ide0_mem_index >= 0) + cpu_register_physical_memory(addr + 0x1f000, 0x1000, ide0_mem_index); + if (ide1_mem_index >= 0) + cpu_register_physical_memory(addr + 0x20000, 0x1000, ide1_mem_index); + if (openpic_mem_index >= 0) { + cpu_register_physical_memory(addr + 0x40000, 0x40000, + openpic_mem_index); + } + if (macio_nvram_mem_index >= 0) + cpu_register_physical_memory(addr + 0x60000, 0x20000, macio_nvram_mem_index); } -static void macio_init(PCIBus *bus) +static void macio_init(PCIBus *bus, int device_id) { PCIDevice *d; @@ -101,8 +162,8 @@ static void macio_init(PCIBus *bus) in PearPC */ d->config[0x00] = 0x6b; // vendor_id d->config[0x01] = 0x10; - d->config[0x02] = 0x22; - d->config[0x03] = 0x00; + d->config[0x02] = device_id; + d->config[0x03] = device_id >> 8; d->config[0x0a] = 0x00; // class_sub = pci2pci d->config[0x0b] = 0xff; // class_base = bridge @@ -116,20 +177,141 @@ static void macio_init(PCIBus *bus) PCI_ADDRESS_SPACE_MEM, macio_map); } -/* PowerPC PREP hardware initialisation */ -void ppc_chrp_init(int ram_size, int vga_ram_size, int boot_device, - DisplayState *ds, const char **fd_filename, int snapshot, - const char *kernel_filename, const char *kernel_cmdline, - const char *initrd_filename) +/* UniN device */ +static void unin_writel (void *opaque, target_phys_addr_t addr, uint32_t value) +{ +} + +static uint32_t unin_readl (void *opaque, target_phys_addr_t addr) +{ + return 0; +} + +static CPUWriteMemoryFunc *unin_write[] = { + &unin_writel, + &unin_writel, + &unin_writel, +}; + +static CPUReadMemoryFunc *unin_read[] = { + &unin_readl, + &unin_readl, + &unin_readl, +}; + +/* temporary frame buffer OSI calls for the video.x driver. The right + solution is to modify the driver to use VGA PCI I/Os */ +static int vga_osi_call(CPUState *env) +{ + static int vga_vbl_enabled; + int linesize; + + // printf("osi_call R5=%d\n", env->gpr[5]); + + /* same handler as PearPC, coming from the original MOL video + driver. */ + switch(env->gpr[5]) { + case 4: + break; + case 28: /* set_vmode */ + if (env->gpr[6] != 1 || env->gpr[7] != 0) + env->gpr[3] = 1; + else + env->gpr[3] = 0; + break; + case 29: /* get_vmode_info */ + if (env->gpr[6] != 0) { + if (env->gpr[6] != 1 || env->gpr[7] != 0) { + env->gpr[3] = 1; + break; + } + } + env->gpr[3] = 0; + env->gpr[4] = (1 << 16) | 1; /* num_vmodes, cur_vmode */ + env->gpr[5] = (1 << 16) | 0; /* num_depths, cur_depth_mode */ + env->gpr[6] = (graphic_width << 16) | graphic_height; /* w, h */ + env->gpr[7] = 85 << 16; /* refresh rate */ + env->gpr[8] = (graphic_depth + 7) & ~7; /* depth (round to byte) */ + linesize = ((graphic_depth + 7) >> 3) * graphic_width; + linesize = (linesize + 3) & ~3; + env->gpr[9] = (linesize << 16) | 0; /* row_bytes, offset */ + break; + case 31: /* set_video power */ + env->gpr[3] = 0; + break; + case 39: /* video_ctrl */ + if (env->gpr[6] == 0 || env->gpr[6] == 1) + vga_vbl_enabled = env->gpr[6]; + env->gpr[3] = 0; + break; + case 47: + break; + case 59: /* set_color */ + /* R6 = index, R7 = RGB */ + env->gpr[3] = 0; + break; + case 64: /* get color */ + /* R6 = index */ + env->gpr[3] = 0; + break; + case 116: /* set hwcursor */ + /* R6 = x, R7 = y, R8 = visible, R9 = data */ + break; + default: + fprintf(stderr, "unsupported OSI call R5=%08x\n", env->gpr[5]); + break; + } + return 1; /* osi_call handled */ +} + +/* XXX: suppress that */ +static void pic_irq_request(void *opaque, int level) +{ +} + +static uint8_t nvram_chksum(const uint8_t *buf, int n) +{ + int sum, i; + sum = 0; + for(i = 0; i < n; i++) + sum += buf[i]; + return (sum & 0xff) + (sum >> 8); +} + +/* set a free Mac OS NVRAM partition */ +void pmac_format_nvram_partition(uint8_t *buf, int len) +{ + char partition_name[12] = "wwwwwwwwwwww"; + + buf[0] = 0x7f; /* free partition magic */ + buf[1] = 0; /* checksum */ + buf[2] = len >> 8; + buf[3] = len; + memcpy(buf + 4, partition_name, 12); + buf[1] = nvram_chksum(buf, 16); +} + +/* PowerPC CHRP hardware initialisation */ +static void ppc_chrp_init(int ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, + int snapshot, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + int is_heathrow) { char buf[1024]; - openpic_t *openpic; + SetIRQFunc *set_irq; + void *pic; m48t59_t *nvram; - int PPC_io_memory; - int ret, linux_boot, i; - unsigned long bios_offset; + int PPC_io_memory, unin_memory; + int linux_boot, i; + unsigned long bios_offset, vga_bios_offset; uint32_t kernel_base, kernel_size, initrd_base, initrd_size; + ppc_def_t *def; PCIBus *pci_bus; + const char *arch_name; + int vga_bios_size, bios_size; linux_boot = (kernel_filename != NULL); @@ -139,15 +321,36 @@ void ppc_chrp_init(int ram_size, int vga_ram_size, int boot_device, /* allocate and load BIOS */ bios_offset = ram_size + vga_ram_size; snprintf(buf, sizeof(buf), "%s/%s", bios_dir, BIOS_FILENAME); - ret = load_image(buf, phys_ram_base + bios_offset); - if (ret != BIOS_SIZE) { - fprintf(stderr, "qemu: could not load PPC PREP bios '%s'\n", buf); + bios_size = load_image(buf, phys_ram_base + bios_offset); + if (bios_size < 0 || bios_size > BIOS_SIZE) { + fprintf(stderr, "qemu: could not load PowerPC bios '%s'\n", buf); exit(1); } - cpu_register_physical_memory((uint32_t)(-BIOS_SIZE), - BIOS_SIZE, bios_offset | IO_MEM_ROM); - cpu_single_env->nip = 0xfffffffc; - + bios_size = (bios_size + 0xfff) & ~0xfff; + cpu_register_physical_memory((uint32_t)(-bios_size), + bios_size, bios_offset | IO_MEM_ROM); + + /* allocate and load VGA BIOS */ + vga_bios_offset = bios_offset + bios_size; + snprintf(buf, sizeof(buf), "%s/%s", bios_dir, VGABIOS_FILENAME); + vga_bios_size = load_image(buf, phys_ram_base + vga_bios_offset + 8); + if (vga_bios_size < 0) { + /* if no bios is present, we can still work */ + fprintf(stderr, "qemu: warning: could not load VGA bios '%s'\n", buf); + vga_bios_size = 0; + } else { + /* set a specific header (XXX: find real Apple format for NDRV + drivers) */ + phys_ram_base[vga_bios_offset] = 'N'; + phys_ram_base[vga_bios_offset + 1] = 'D'; + phys_ram_base[vga_bios_offset + 2] = 'R'; + phys_ram_base[vga_bios_offset + 3] = 'V'; + cpu_to_be32w((uint32_t *)(phys_ram_base + vga_bios_offset + 4), + vga_bios_size); + vga_bios_size += 8; + } + vga_bios_size = (vga_bios_size + 0xfff) & ~0xfff; + if (linux_boot) { kernel_base = KERNEL_LOAD_ADDR; /* now we can load the kernel */ @@ -179,50 +382,129 @@ void ppc_chrp_init(int ram_size, int vga_ram_size, int boot_device, initrd_size = 0; } /* Register CPU as a 74x/75x */ - cpu_ppc_register(cpu_single_env, 0x00080000); + /* XXX: CPU model (or PVR) should be provided on command line */ + // ppc_find_by_name("750gx", &def); // Linux boot OK + // ppc_find_by_name("750fx", &def); // Linux boot OK + /* Linux does not boot on 750cxe (and probably other 750cx based) + * because it assumes it has 8 IBAT & DBAT pairs as it only have 4. + */ + // ppc_find_by_name("750cxe", &def); + // ppc_find_by_name("750p", &def); + // ppc_find_by_name("740p", &def); + ppc_find_by_name("750", &def); + // ppc_find_by_name("740", &def); + // ppc_find_by_name("G3", &def); + // ppc_find_by_name("604r", &def); + // ppc_find_by_name("604e", &def); + // ppc_find_by_name("604", &def); + if (def == NULL) { + cpu_abort(cpu_single_env, "Unable to find PowerPC CPU definition\n"); + } + cpu_ppc_register(cpu_single_env, def); + /* Set time-base frequency to 100 Mhz */ cpu_ppc_tb_init(cpu_single_env, 100UL * 1000UL * 1000UL); - isa_mem_base = 0x80000000; - pci_bus = pci_pmac_init(); - - /* Register 8 MB of ISA IO space */ - PPC_io_memory = cpu_register_io_memory(0, PPC_io_read, PPC_io_write, NULL); - cpu_register_physical_memory(0xF2000000, 0x00800000, PPC_io_memory); - - /* init basic PC hardware */ - vga_initialize(pci_bus, ds, phys_ram_base + ram_size, ram_size, - vga_ram_size); - openpic = openpic_init(NULL, &openpic_mem_index, 1); - pci_pmac_set_openpic(pci_bus, openpic); - - /* XXX: suppress that */ - pic_init(); - - /* XXX: use Mac Serial port */ - serial_init(0x3f8, 4, serial_hds[0]); + cpu_single_env->osi_call = vga_osi_call; + + if (is_heathrow) { + isa_mem_base = 0x80000000; + pci_bus = pci_grackle_init(0xfec00000); + + /* Register 2 MB of ISA IO space */ + PPC_io_memory = cpu_register_io_memory(0, PPC_io_read, PPC_io_write, NULL); + cpu_register_physical_memory(0xfe000000, 0x00200000, PPC_io_memory); + + /* init basic PC hardware */ + vga_initialize(pci_bus, ds, phys_ram_base + ram_size, + ram_size, vga_ram_size, + vga_bios_offset, vga_bios_size); + pic = heathrow_pic_init(&heathrow_pic_mem_index); + set_irq = heathrow_pic_set_irq; + pci_set_pic(pci_bus, set_irq, pic); + + /* XXX: suppress that */ + isa_pic = pic_init(pic_irq_request, NULL); + + /* XXX: use Mac Serial port */ + serial_init(0x3f8, 4, serial_hds[0]); + + for(i = 0; i < nb_nics; i++) { + pci_ne2000_init(pci_bus, &nd_table[i]); + } + + pci_cmd646_ide_init(pci_bus, &bs_table[0], 0); + + /* cuda also initialize ADB */ + cuda_mem_index = cuda_init(set_irq, pic, 0x12); + + adb_kbd_init(&adb_bus); + adb_mouse_init(&adb_bus); + + { + MacIONVRAMState *nvr; + nvr = macio_nvram_init(); + pmac_format_nvram_partition(nvr->data, 0x2000); + } - for(i = 0; i < nb_nics; i++) { - pci_ne2000_init(pci_bus, &nd_table[i]); + macio_init(pci_bus, 0x0017); + + nvram = m48t59_init(8, 0xFFF04000, 0x0074, NVRAM_SIZE); + + arch_name = "HEATHROW"; + } else { + isa_mem_base = 0x80000000; + pci_bus = pci_pmac_init(); + + /* Register 8 MB of ISA IO space */ + PPC_io_memory = cpu_register_io_memory(0, PPC_io_read, PPC_io_write, NULL); + cpu_register_physical_memory(0xF2000000, 0x00800000, PPC_io_memory); + + /* UniN init */ + unin_memory = cpu_register_io_memory(0, unin_read, unin_write, NULL); + cpu_register_physical_memory(0xf8000000, 0x00001000, unin_memory); + + /* init basic PC hardware */ + vga_initialize(pci_bus, ds, phys_ram_base + ram_size, + ram_size, vga_ram_size, + vga_bios_offset, vga_bios_size); + pic = openpic_init(NULL, &openpic_mem_index, 1); + set_irq = openpic_set_irq; + pci_set_pic(pci_bus, set_irq, pic); + + /* XXX: suppress that */ + isa_pic = pic_init(pic_irq_request, NULL); + + /* XXX: use Mac Serial port */ + serial_init(0x3f8, 4, serial_hds[0]); + + for(i = 0; i < nb_nics; i++) { + pci_ne2000_init(pci_bus, &nd_table[i]); + } + +#if 1 + ide0_mem_index = pmac_ide_init(&bs_table[0], set_irq, pic, 0x13); + ide1_mem_index = pmac_ide_init(&bs_table[2], set_irq, pic, 0x14); +#else + pci_cmd646_ide_init(pci_bus, &bs_table[0], 0); +#endif + /* cuda also initialize ADB */ + cuda_mem_index = cuda_init(set_irq, pic, 0x19); + + adb_kbd_init(&adb_bus); + adb_mouse_init(&adb_bus); + + macio_init(pci_bus, 0x0022); + + nvram = m48t59_init(8, 0xFFF04000, 0x0074, NVRAM_SIZE); + + arch_name = "MAC99"; } - - ide0_mem_index = pmac_ide_init(&bs_table[0], openpic, 0x13); - ide1_mem_index = pmac_ide_init(&bs_table[2], openpic, 0x14); - - /* cuda also initialize ADB */ - cuda_mem_index = cuda_init(openpic, 0x19); - - adb_kbd_init(&adb_bus); - adb_mouse_init(&adb_bus); - - macio_init(pci_bus); - - nvram = m48t59_init(8, 0xFFF04000, 0x0074, NVRAM_SIZE); if (graphic_depth != 15 && graphic_depth != 32 && graphic_depth != 8) graphic_depth = 15; - PPC_NVRAM_set_params(nvram, NVRAM_SIZE, "CHRP", ram_size, boot_device, + PPC_NVRAM_set_params(nvram, NVRAM_SIZE, arch_name, ram_size, boot_device, kernel_base, kernel_size, kernel_cmdline, initrd_base, initrd_size, @@ -230,4 +512,45 @@ void ppc_chrp_init(int ram_size, int vga_ram_size, int boot_device, 0, graphic_width, graphic_height, graphic_depth); /* No PCI init: the BIOS will do it */ + + /* Special port to get debug messages from Open-Firmware */ + register_ioport_write(0x0F00, 4, 1, &PPC_debug_write, NULL); +} + +static void ppc_core99_init(int ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, + int snapshot, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename) +{ + ppc_chrp_init(ram_size, vga_ram_size, boot_device, + ds, fd_filename, snapshot, + kernel_filename, kernel_cmdline, + initrd_filename, 0); +} + +static void ppc_heathrow_init(int ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, + int snapshot, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename) +{ + ppc_chrp_init(ram_size, vga_ram_size, boot_device, + ds, fd_filename, snapshot, + kernel_filename, kernel_cmdline, + initrd_filename, 1); } + +QEMUMachine core99_machine = { + "mac99", + "Mac99 based PowerMAC", + ppc_core99_init, +}; + +QEMUMachine heathrow_machine = { + "g3bw", + "Heathrow based PowerMAC", + ppc_heathrow_init, +}; diff --git a/qemu/hw/ppc_prep.c b/qemu/hw/ppc_prep.c index 06951e5..148a08a 100644 --- a/qemu/hw/ppc_prep.c +++ b/qemu/hw/ppc_prep.c @@ -96,6 +96,14 @@ static uint32_t speaker_ioport_read(void *opaque, uint32_t addr) return 0; } +static void pic_irq_request(void *opaque, int level) +{ + if (level) + cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HARD); + else + cpu_reset_interrupt(cpu_single_env, CPU_INTERRUPT_HARD); +} + /* PCI intack register */ /* Read-only register (?) */ static void _PPC_intack_write (void *opaque, target_phys_addr_t addr, uint32_t value) @@ -108,7 +116,7 @@ static inline uint32_t _PPC_intack_read (target_phys_addr_t addr) uint32_t retval = 0; if (addr == 0xBFFFFFF0) - retval = pic_intack_read(NULL); + retval = pic_intack_read(isa_pic); // printf("%s: 0x%08x <= %d\n", __func__, addr, retval); return retval; @@ -250,6 +258,7 @@ typedef struct sysctrl_t { uint8_t syscontrol; uint8_t fake_io[2]; int contiguous_map; + int endian; } sysctrl_t; enum { @@ -289,8 +298,9 @@ static void PREP_io_800_writeb (void *opaque, uint32_t addr, uint32_t val) } /* Check LE mode */ if (val & 0x02) { - printf("Little Endian mode isn't supported (yet ?)\n"); - abort(); + sysctrl->endian = 1; + } else { + sysctrl->endian = 0; } break; case 0x0800: @@ -505,22 +515,21 @@ CPUReadMemoryFunc *PPC_prep_io_read[] = { &PPC_prep_io_readl, }; -extern CPUPPCState *global_env; - #define NVRAM_SIZE 0x2000 /* PowerPC PREP hardware initialisation */ -void ppc_prep_init(int ram_size, int vga_ram_size, int boot_device, - DisplayState *ds, const char **fd_filename, int snapshot, - const char *kernel_filename, const char *kernel_cmdline, - const char *initrd_filename) +static void ppc_prep_init(int ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, int snapshot, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename) { char buf[1024]; m48t59_t *nvram; int PPC_io_memory; - int ret, linux_boot, i, nb_nics1; + int linux_boot, i, nb_nics1, bios_size; unsigned long bios_offset; uint32_t kernel_base, kernel_size, initrd_base, initrd_size; + ppc_def_t *def; PCIBus *pci_bus; sysctrl = qemu_mallocz(sizeof(sysctrl_t)); @@ -535,14 +544,14 @@ void ppc_prep_init(int ram_size, int vga_ram_size, int boot_device, /* allocate and load BIOS */ bios_offset = ram_size + vga_ram_size; snprintf(buf, sizeof(buf), "%s/%s", bios_dir, BIOS_FILENAME); - ret = load_image(buf, phys_ram_base + bios_offset); - if (ret != BIOS_SIZE) { + bios_size = load_image(buf, phys_ram_base + bios_offset); + if (bios_size < 0 || bios_size > BIOS_SIZE) { fprintf(stderr, "qemu: could not load PPC PREP bios '%s'\n", buf); exit(1); } - cpu_register_physical_memory((uint32_t)(-BIOS_SIZE), - BIOS_SIZE, bios_offset | IO_MEM_ROM); - cpu_single_env->nip = 0xfffffffc; + bios_size = (bios_size + 0xfff) & ~0xfff; + cpu_register_physical_memory((uint32_t)(-bios_size), + bios_size, bios_offset | IO_MEM_ROM); if (linux_boot) { kernel_base = KERNEL_LOAD_ADDR; @@ -576,7 +585,14 @@ void ppc_prep_init(int ram_size, int vga_ram_size, int boot_device, } /* Register CPU as a 604 */ - cpu_ppc_register(cpu_single_env, 0x00040000); + /* XXX: CPU model (or PVR) should be provided on command line */ + // ppc_find_by_name("604r", &def); + // ppc_find_by_name("604e", &def); + ppc_find_by_name("604", &def); + if (def == NULL) { + cpu_abort(cpu_single_env, "Unable to find PowerPC CPU definition\n"); + } + cpu_ppc_register(cpu_single_env, def); /* Set time-base frequency to 100 Mhz */ cpu_ppc_tb_init(cpu_single_env, 100UL * 1000UL * 1000UL); @@ -590,11 +606,10 @@ void ppc_prep_init(int ram_size, int vga_ram_size, int boot_device, /* init basic PC hardware */ vga_initialize(pci_bus, ds, phys_ram_base + ram_size, ram_size, - vga_ram_size); + vga_ram_size, 0, 0); rtc_init(0x70, 8); // openpic = openpic_init(0x00000000, 0xF0000000, 1); - // pic_init(openpic); - pic_init(); + isa_pic = pic_init(pic_irq_request, cpu_single_env); // pit = pit_init(0x40, 0); serial_init(0x3f8, 4, serial_hds[0]); @@ -650,4 +665,13 @@ void ppc_prep_init(int ram_size, int vga_ram_size, int boot_device, /* XXX: need an option to load a NVRAM image */ 0, graphic_width, graphic_height, graphic_depth); + + /* Special port to get debug messages from Open-Firmware */ + register_ioport_write(0x0F00, 4, 1, &PPC_debug_write, NULL); } + +QEMUMachine prep_machine = { + "prep", + "PowerPC PREP platform", + ppc_prep_init, +}; diff --git a/qemu/hw/slavio_intctl.c b/qemu/hw/slavio_intctl.c index 9c8ddd0..8a5db5c 100644 --- a/qemu/hw/slavio_intctl.c +++ b/qemu/hw/slavio_intctl.c @@ -144,14 +144,14 @@ static void slavio_intctlm_mem_writel(void *opaque, target_phys_addr_t addr, uin switch (saddr) { case 2: // clear (enable) // Force clear unused bits - val &= ~0x7fb2007f; + val &= ~0x4fb2007f; s->intregm_disabled &= ~val; DPRINTF("Enabled master irq mask %x, curmask %x\n", val, s->intregm_disabled); slavio_check_interrupts(s); break; case 3: // set (disable, clear pending) // Force clear unused bits - val &= ~0x7fb2007f; + val &= ~0x4fb2007f; s->intregm_disabled |= val; s->intregm_pending &= ~val; DPRINTF("Disabled master irq mask %x, curmask %x\n", val, s->intregm_disabled); @@ -208,7 +208,7 @@ void slavio_irq_info(void *opaque) static const uint32_t intbit_to_level[32] = { 2, 3, 5, 7, 9, 11, 0, 14, 3, 5, 7, 9, 11, 13, 12, 12, - 6, 0, 4, 10, 8, 0, 11, 0, 0, 0, 0, 0, 15, 0, 0, 0, + 6, 0, 4, 10, 8, 0, 11, 0, 0, 0, 0, 0, 15, 0, 15, 0, }; static void slavio_check_interrupts(void *opaque) diff --git a/qemu/hw/slavio_misc.c b/qemu/hw/slavio_misc.c new file mode 100644 index 0000000..597a0cb --- /dev/null +++ b/qemu/hw/slavio_misc.c @@ -0,0 +1,240 @@ +/* + * QEMU Sparc SLAVIO aux io port emulation + * + * Copyright (c) 2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" +/* debug misc */ +//#define DEBUG_MISC + +/* + * This is the auxio port, chip control and system control part of + * chip STP2001 (Slave I/O), also produced as NCR89C105. See + * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt + * + * This also includes the PMC CPU idle controller. + */ + +#ifdef DEBUG_MISC +#define MISC_DPRINTF(fmt, args...) \ +do { printf("MISC: " fmt , ##args); } while (0) +#else +#define MISC_DPRINTF(fmt, args...) +#endif + +typedef struct MiscState { + int irq; + uint8_t config; + uint8_t aux1, aux2; + uint8_t diag, mctrl; +} MiscState; + +#define MISC_MAXADDR 1 + +static void slavio_misc_update_irq(void *opaque) +{ + MiscState *s = opaque; + + if ((s->aux2 & 0x4) && (s->config & 0x8)) { + pic_set_irq(s->irq, 1); + } else { + pic_set_irq(s->irq, 0); + } +} + +static void slavio_misc_reset(void *opaque) +{ + MiscState *s = opaque; + + // Diagnostic register not cleared in reset + s->config = s->aux1 = s->aux2 = s->mctrl = 0; +} + +void slavio_set_power_fail(void *opaque, int power_failing) +{ + MiscState *s = opaque; + + MISC_DPRINTF("Power fail: %d, config: %d\n", power_failing, s->config); + if (power_failing && (s->config & 0x8)) { + s->aux2 |= 0x4; + } else { + s->aux2 &= ~0x4; + } + slavio_misc_update_irq(s); +} + +static void slavio_misc_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + MiscState *s = opaque; + + switch (addr & 0xfff0000) { + case 0x1800000: + MISC_DPRINTF("Write config %2.2x\n", val & 0xff); + s->config = val & 0xff; + slavio_misc_update_irq(s); + break; + case 0x1900000: + MISC_DPRINTF("Write aux1 %2.2x\n", val & 0xff); + s->aux1 = val & 0xff; + break; + case 0x1910000: + val &= 0x3; + MISC_DPRINTF("Write aux2 %2.2x\n", val); + val |= s->aux2 & 0x4; + if (val & 0x2) // Clear Power Fail int + val &= 0x1; + s->aux2 = val; + if (val & 1) + qemu_system_shutdown_request(); + slavio_misc_update_irq(s); + break; + case 0x1a00000: + MISC_DPRINTF("Write diag %2.2x\n", val & 0xff); + s->diag = val & 0xff; + break; + case 0x1b00000: + MISC_DPRINTF("Write modem control %2.2x\n", val & 0xff); + s->mctrl = val & 0xff; + break; + case 0x1f00000: + MISC_DPRINTF("Write system control %2.2x\n", val & 0xff); + if (val & 1) + qemu_system_reset_request(); + break; + case 0xa000000: + MISC_DPRINTF("Write power management %2.2x\n", val & 0xff); +#if 0 + // XXX: halting CPU does not work + raise_exception(EXCP_HLT); + cpu_loop_exit(); +#endif + break; + } +} + +static uint32_t slavio_misc_mem_readb(void *opaque, target_phys_addr_t addr) +{ + MiscState *s = opaque; + uint32_t ret = 0; + + switch (addr & 0xfff0000) { + case 0x1800000: + ret = s->config; + MISC_DPRINTF("Read config %2.2x\n", ret); + break; + case 0x1900000: + ret = s->aux1; + MISC_DPRINTF("Read aux1 %2.2x\n", ret); + break; + case 0x1910000: + ret = s->aux2; + MISC_DPRINTF("Read aux2 %2.2x\n", ret); + break; + case 0x1a00000: + ret = s->diag; + MISC_DPRINTF("Read diag %2.2x\n", ret); + break; + case 0x1b00000: + ret = s->mctrl; + MISC_DPRINTF("Read modem control %2.2x\n", ret); + break; + case 0x1f00000: + MISC_DPRINTF("Read system control %2.2x\n", ret); + break; + case 0xa000000: + MISC_DPRINTF("Read power management %2.2x\n", ret); + break; + } + return ret; +} + +static CPUReadMemoryFunc *slavio_misc_mem_read[3] = { + slavio_misc_mem_readb, + slavio_misc_mem_readb, + slavio_misc_mem_readb, +}; + +static CPUWriteMemoryFunc *slavio_misc_mem_write[3] = { + slavio_misc_mem_writeb, + slavio_misc_mem_writeb, + slavio_misc_mem_writeb, +}; + +static void slavio_misc_save(QEMUFile *f, void *opaque) +{ + MiscState *s = opaque; + + qemu_put_be32s(f, &s->irq); + qemu_put_8s(f, &s->config); + qemu_put_8s(f, &s->aux1); + qemu_put_8s(f, &s->aux2); + qemu_put_8s(f, &s->diag); + qemu_put_8s(f, &s->mctrl); +} + +static int slavio_misc_load(QEMUFile *f, void *opaque, int version_id) +{ + MiscState *s = opaque; + + if (version_id != 1) + return -EINVAL; + + qemu_get_be32s(f, &s->irq); + qemu_get_8s(f, &s->config); + qemu_get_8s(f, &s->aux1); + qemu_get_8s(f, &s->aux2); + qemu_get_8s(f, &s->diag); + qemu_get_8s(f, &s->mctrl); + return 0; +} + +void *slavio_misc_init(uint32_t base, int irq) +{ + int slavio_misc_io_memory; + MiscState *s; + + s = qemu_mallocz(sizeof(MiscState)); + if (!s) + return NULL; + + slavio_misc_io_memory = cpu_register_io_memory(0, slavio_misc_mem_read, slavio_misc_mem_write, s); + // Slavio control + cpu_register_physical_memory(base + 0x1800000, MISC_MAXADDR, slavio_misc_io_memory); + // AUX 1 + cpu_register_physical_memory(base + 0x1900000, MISC_MAXADDR, slavio_misc_io_memory); + // AUX 2 + cpu_register_physical_memory(base + 0x1910000, MISC_MAXADDR, slavio_misc_io_memory); + // Diagnostics + cpu_register_physical_memory(base + 0x1a00000, MISC_MAXADDR, slavio_misc_io_memory); + // Modem control + cpu_register_physical_memory(base + 0x1b00000, MISC_MAXADDR, slavio_misc_io_memory); + // System control + cpu_register_physical_memory(base + 0x1f00000, MISC_MAXADDR, slavio_misc_io_memory); + // Power management + cpu_register_physical_memory(base + 0xa000000, MISC_MAXADDR, slavio_misc_io_memory); + + s->irq = irq; + + register_savevm("slavio_misc", base, 1, slavio_misc_save, slavio_misc_load, s); + qemu_register_reset(slavio_misc_reset, s); + slavio_misc_reset(s); + return s; +} diff --git a/qemu/hw/sun4m.c b/qemu/hw/sun4m.c index b911ba2..56b9069 100644 --- a/qemu/hw/sun4m.c +++ b/qemu/hw/sun4m.c @@ -37,6 +37,7 @@ // bits #define PHYS_JJ_IOMMU 0x10000000 /* I/O MMU */ #define PHYS_JJ_TCX_FB 0x50000000 /* TCX frame buffer */ +#define PHYS_JJ_SLAVIO 0x70000000 /* Slavio base */ #define PHYS_JJ_ESPDMA 0x78400000 /* ESP DMA controller */ #define PHYS_JJ_ESP 0x78800000 /* ESP SCSI */ #define PHYS_JJ_ESP_IRQ 18 @@ -55,6 +56,7 @@ #define PHYS_JJ_SER_IRQ 15 #define PHYS_JJ_FDC 0x71400000 /* Floppy */ #define PHYS_JJ_FLOPPY_IRQ 22 +#define PHYS_JJ_ME_IRQ 30 /* Module error, power fail */ /* TSC handling */ @@ -202,11 +204,18 @@ uint32_t iommu_translate(uint32_t addr) return iommu_translate_local(iommu, addr); } +static void *slavio_misc; + +void qemu_system_powerdown(void) +{ + slavio_set_power_fail(slavio_misc, 1); +} + /* Sun4m hardware initialisation */ -void sun4m_init(int ram_size, int vga_ram_size, int boot_device, - DisplayState *ds, const char **fd_filename, int snapshot, - const char *kernel_filename, const char *kernel_cmdline, - const char *initrd_filename) +static void sun4m_init(int ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, int snapshot, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename) { char buf[1024]; int ret, linux_boot; @@ -230,6 +239,7 @@ void sun4m_init(int ram_size, int vga_ram_size, int boot_device, slavio_serial_init(PHYS_JJ_SER, PHYS_JJ_SER_IRQ, serial_hds[1], serial_hds[0]); fdctrl_init(PHYS_JJ_FLOPPY_IRQ, 0, 1, PHYS_JJ_FDC, fd_table); esp_init(bs_table, PHYS_JJ_ESP_IRQ, PHYS_JJ_ESP, PHYS_JJ_ESPDMA); + slavio_misc = slavio_misc_init(PHYS_JJ_SLAVIO, PHYS_JJ_ME_IRQ); prom_offset = ram_size + vram_size; @@ -283,3 +293,9 @@ void sun4m_init(int ram_size, int vga_ram_size, int boot_device, } nvram_init(nvram, (uint8_t *)&nd_table[0].macaddr, kernel_cmdline, boot_device, ram_size, kernel_size, graphic_width, graphic_height, graphic_depth); } + +QEMUMachine sun4m_machine = { + "sun4m", + "Sun4m platform", + sun4m_init, +}; diff --git a/qemu/hw/sun4u.c b/qemu/hw/sun4u.c new file mode 100644 index 0000000..9c89453 --- /dev/null +++ b/qemu/hw/sun4u.c @@ -0,0 +1,375 @@ +/* + * QEMU Sun4u System Emulator + * + * Copyright (c) 2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" +#include "m48t59.h" + +#define KERNEL_LOAD_ADDR 0x00404000 +#define CMDLINE_ADDR 0x003ff000 +#define INITRD_LOAD_ADDR 0x00300000 +#define PROM_ADDR 0x1fff0000000ULL +#define APB_SPECIAL_BASE 0x1fe00000000ULL +#define APB_MEM_BASE 0x1ff00000000ULL +#define VGA_BASE (APB_MEM_BASE + 0x400000ULL) +#define PROM_FILENAMEB "proll-sparc64.bin" +#define PROM_FILENAMEE "proll-sparc64.elf" +#define NVRAM_SIZE 0x2000 + +/* TSC handling */ + +uint64_t cpu_get_tsc() +{ + return qemu_get_clock(vm_clock); +} + +int DMA_get_channel_mode (int nchan) +{ + return 0; +} +int DMA_read_memory (int nchan, void *buf, int pos, int size) +{ + return 0; +} +int DMA_write_memory (int nchan, void *buf, int pos, int size) +{ + return 0; +} +void DMA_hold_DREQ (int nchan) {} +void DMA_release_DREQ (int nchan) {} +void DMA_schedule(int nchan) {} +void DMA_run (void) {} +void DMA_init (int high_page_enable) {} +void DMA_register_channel (int nchan, + DMA_transfer_handler transfer_handler, + void *opaque) +{ +} + +/* NVRAM helpers */ +void NVRAM_set_byte (m48t59_t *nvram, uint32_t addr, uint8_t value) +{ + m48t59_set_addr(nvram, addr); + m48t59_write(nvram, value); +} + +uint8_t NVRAM_get_byte (m48t59_t *nvram, uint32_t addr) +{ + m48t59_set_addr(nvram, addr); + return m48t59_read(nvram); +} + +void NVRAM_set_word (m48t59_t *nvram, uint32_t addr, uint16_t value) +{ + m48t59_set_addr(nvram, addr); + m48t59_write(nvram, value >> 8); + m48t59_set_addr(nvram, addr + 1); + m48t59_write(nvram, value & 0xFF); +} + +uint16_t NVRAM_get_word (m48t59_t *nvram, uint32_t addr) +{ + uint16_t tmp; + + m48t59_set_addr(nvram, addr); + tmp = m48t59_read(nvram) << 8; + m48t59_set_addr(nvram, addr + 1); + tmp |= m48t59_read(nvram); + + return tmp; +} + +void NVRAM_set_lword (m48t59_t *nvram, uint32_t addr, uint32_t value) +{ + m48t59_set_addr(nvram, addr); + m48t59_write(nvram, value >> 24); + m48t59_set_addr(nvram, addr + 1); + m48t59_write(nvram, (value >> 16) & 0xFF); + m48t59_set_addr(nvram, addr + 2); + m48t59_write(nvram, (value >> 8) & 0xFF); + m48t59_set_addr(nvram, addr + 3); + m48t59_write(nvram, value & 0xFF); +} + +uint32_t NVRAM_get_lword (m48t59_t *nvram, uint32_t addr) +{ + uint32_t tmp; + + m48t59_set_addr(nvram, addr); + tmp = m48t59_read(nvram) << 24; + m48t59_set_addr(nvram, addr + 1); + tmp |= m48t59_read(nvram) << 16; + m48t59_set_addr(nvram, addr + 2); + tmp |= m48t59_read(nvram) << 8; + m48t59_set_addr(nvram, addr + 3); + tmp |= m48t59_read(nvram); + + return tmp; +} + +void NVRAM_set_string (m48t59_t *nvram, uint32_t addr, + const unsigned char *str, uint32_t max) +{ + int i; + + for (i = 0; i < max && str[i] != '\0'; i++) { + m48t59_set_addr(nvram, addr + i); + m48t59_write(nvram, str[i]); + } + m48t59_set_addr(nvram, addr + max - 1); + m48t59_write(nvram, '\0'); +} + +int NVRAM_get_string (m48t59_t *nvram, uint8_t *dst, uint16_t addr, int max) +{ + int i; + + memset(dst, 0, max); + for (i = 0; i < max; i++) { + dst[i] = NVRAM_get_byte(nvram, addr + i); + if (dst[i] == '\0') + break; + } + + return i; +} + +static uint16_t NVRAM_crc_update (uint16_t prev, uint16_t value) +{ + uint16_t tmp; + uint16_t pd, pd1, pd2; + + tmp = prev >> 8; + pd = prev ^ value; + pd1 = pd & 0x000F; + pd2 = ((pd >> 4) & 0x000F) ^ pd1; + tmp ^= (pd1 << 3) | (pd1 << 8); + tmp ^= pd2 | (pd2 << 7) | (pd2 << 12); + + return tmp; +} + +uint16_t NVRAM_compute_crc (m48t59_t *nvram, uint32_t start, uint32_t count) +{ + uint32_t i; + uint16_t crc = 0xFFFF; + int odd; + + odd = count & 1; + count &= ~1; + for (i = 0; i != count; i++) { + crc = NVRAM_crc_update(crc, NVRAM_get_word(nvram, start + i)); + } + if (odd) { + crc = NVRAM_crc_update(crc, NVRAM_get_byte(nvram, start + i) << 8); + } + + return crc; +} + +extern int nographic; + +int sun4u_NVRAM_set_params (m48t59_t *nvram, uint16_t NVRAM_size, + const unsigned char *arch, + uint32_t RAM_size, int boot_device, + uint32_t kernel_image, uint32_t kernel_size, + const char *cmdline, + uint32_t initrd_image, uint32_t initrd_size, + uint32_t NVRAM_image, + int width, int height, int depth) +{ + uint16_t crc; + + /* Set parameters for Open Hack'Ware BIOS */ + NVRAM_set_string(nvram, 0x00, "QEMU_BIOS", 16); + NVRAM_set_lword(nvram, 0x10, 0x00000002); /* structure v2 */ + NVRAM_set_word(nvram, 0x14, NVRAM_size); + NVRAM_set_string(nvram, 0x20, arch, 16); + NVRAM_set_byte(nvram, 0x2f, nographic & 0xff); + NVRAM_set_lword(nvram, 0x30, RAM_size); + NVRAM_set_byte(nvram, 0x34, boot_device); + NVRAM_set_lword(nvram, 0x38, kernel_image); + NVRAM_set_lword(nvram, 0x3C, kernel_size); + if (cmdline) { + /* XXX: put the cmdline in NVRAM too ? */ + strcpy(phys_ram_base + CMDLINE_ADDR, cmdline); + NVRAM_set_lword(nvram, 0x40, CMDLINE_ADDR); + NVRAM_set_lword(nvram, 0x44, strlen(cmdline)); + } else { + NVRAM_set_lword(nvram, 0x40, 0); + NVRAM_set_lword(nvram, 0x44, 0); + } + NVRAM_set_lword(nvram, 0x48, initrd_image); + NVRAM_set_lword(nvram, 0x4C, initrd_size); + NVRAM_set_lword(nvram, 0x50, NVRAM_image); + + NVRAM_set_word(nvram, 0x54, width); + NVRAM_set_word(nvram, 0x56, height); + NVRAM_set_word(nvram, 0x58, depth); + crc = NVRAM_compute_crc(nvram, 0x00, 0xF8); + NVRAM_set_word(nvram, 0xFC, crc); + + return 0; +} + +void pic_info() +{ +} + +void irq_info() +{ +} + +void pic_set_irq(int irq, int level) +{ +} + +void pic_set_irq_new(void *opaque, int irq, int level) +{ +} + +void qemu_system_powerdown(void) +{ +} + +static const int ide_iobase[2] = { 0x1f0, 0x170 }; +static const int ide_iobase2[2] = { 0x3f6, 0x376 }; +static const int ide_irq[2] = { 14, 15 }; + +static const int serial_io[MAX_SERIAL_PORTS] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; +static const int serial_irq[MAX_SERIAL_PORTS] = { 4, 3, 4, 3 }; + +static const int parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc }; +static const int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 }; + +static fdctrl_t *floppy_controller; + +/* Sun4u hardware initialisation */ +static void sun4u_init(int ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, int snapshot, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename) +{ + char buf[1024]; + m48t59_t *nvram; + int ret, linux_boot; + unsigned int i; + long prom_offset, initrd_size, kernel_size; + PCIBus *pci_bus; + + linux_boot = (kernel_filename != NULL); + + /* allocate RAM */ + cpu_register_physical_memory(0, ram_size, 0); + + prom_offset = ram_size + vga_ram_size; + + snprintf(buf, sizeof(buf), "%s/%s", bios_dir, PROM_FILENAMEE); + ret = load_elf(buf, phys_ram_base + prom_offset); + if (ret < 0) { + snprintf(buf, sizeof(buf), "%s/%s", bios_dir, PROM_FILENAMEB); + ret = load_image(buf, phys_ram_base + prom_offset); + } + if (ret < 0) { + fprintf(stderr, "qemu: could not load prom '%s'\n", + buf); + exit(1); + } + cpu_register_physical_memory(PROM_ADDR, (ret + TARGET_PAGE_SIZE) & TARGET_PAGE_MASK, + prom_offset | IO_MEM_ROM); + + kernel_size = 0; + initrd_size = 0; + if (linux_boot) { + kernel_size = load_elf(kernel_filename, phys_ram_base + KERNEL_LOAD_ADDR); + if (kernel_size < 0) + kernel_size = load_aout(kernel_filename, phys_ram_base + KERNEL_LOAD_ADDR); + if (kernel_size < 0) + kernel_size = load_image(kernel_filename, phys_ram_base + KERNEL_LOAD_ADDR); + if (kernel_size < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + kernel_filename); + exit(1); + } + + /* load initrd */ + if (initrd_filename) { + initrd_size = load_image(initrd_filename, phys_ram_base + INITRD_LOAD_ADDR); + if (initrd_size < 0) { + fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", + initrd_filename); + exit(1); + } + } + if (initrd_size > 0) { + for (i = 0; i < 64 * TARGET_PAGE_SIZE; i += TARGET_PAGE_SIZE) { + if (ldl_raw(phys_ram_base + KERNEL_LOAD_ADDR + i) + == 0x48647253) { // HdrS + stl_raw(phys_ram_base + KERNEL_LOAD_ADDR + i + 16, INITRD_LOAD_ADDR); + stl_raw(phys_ram_base + KERNEL_LOAD_ADDR + i + 20, initrd_size); + break; + } + } + } + } + pci_bus = pci_apb_init(APB_SPECIAL_BASE, APB_MEM_BASE); + isa_mem_base = VGA_BASE; + vga_initialize(pci_bus, ds, phys_ram_base + ram_size, ram_size, + vga_ram_size, 0, 0); + cpu_register_physical_memory(VGA_BASE, vga_ram_size, ram_size); + //pci_cirrus_vga_init(pci_bus, ds, phys_ram_base + ram_size, ram_size, vga_ram_size); + + for(i = 0; i < MAX_SERIAL_PORTS; i++) { + if (serial_hds[i]) { + serial_init(serial_io[i], serial_irq[i], serial_hds[i]); + } + } + + for(i = 0; i < MAX_PARALLEL_PORTS; i++) { + if (parallel_hds[i]) { + parallel_init(parallel_io[i], parallel_irq[i], parallel_hds[i]); + } + } + + for(i = 0; i < nb_nics; i++) { + pci_ne2000_init(pci_bus, &nd_table[i]); + } + + pci_cmd646_ide_init(pci_bus, bs_table, 1); + kbd_init(); + floppy_controller = fdctrl_init(6, 2, 0, 0x3f0, fd_table); + nvram = m48t59_init(8, 0, 0x0074, NVRAM_SIZE); + sun4u_NVRAM_set_params(nvram, NVRAM_SIZE, "Sun4u", ram_size, boot_device, + KERNEL_LOAD_ADDR, kernel_size, + kernel_cmdline, + INITRD_LOAD_ADDR, initrd_size, + /* XXX: need an option to load a NVRAM image */ + 0, + graphic_width, graphic_height, graphic_depth); + +} + +QEMUMachine sun4u_machine = { + "sun4u", + "Sun4u platform", + sun4u_init, +}; diff --git a/qemu/hw/vga.c b/qemu/hw/vga.c index 2c425ff..49e5b21 100644 --- a/qemu/hw/vga.c +++ b/qemu/hw/vga.c @@ -1654,8 +1654,11 @@ static void vga_map(PCIDevice *pci_dev, int region_num, uint32_t addr, uint32_t size, int type) { VGAState *s = vga_state; - - cpu_register_physical_memory(addr, s->vram_size, s->vram_offset); + if (region_num == PCI_ROM_SLOT) { + cpu_register_physical_memory(addr, s->bios_size, s->bios_offset); + } else { + cpu_register_physical_memory(addr, s->vram_size, s->vram_offset); + } } void vga_common_init(VGAState *s, DisplayState *ds, uint8_t *vga_ram_base, @@ -1701,7 +1704,8 @@ void vga_common_init(VGAState *s, DisplayState *ds, uint8_t *vga_ram_base, int vga_initialize(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base, - unsigned long vga_ram_offset, int vga_ram_size) + unsigned long vga_ram_offset, int vga_ram_size, + unsigned long vga_bios_offset, int vga_bios_size) { VGAState *s; @@ -1776,6 +1780,17 @@ int vga_initialize(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base, /* XXX: vga_ram_size must be a power of two */ pci_register_io_region(d, 0, vga_ram_size, PCI_ADDRESS_SPACE_MEM_PREFETCH, vga_map); + if (vga_bios_size != 0) { + unsigned int bios_total_size; + s->bios_offset = vga_bios_offset; + s->bios_size = vga_bios_size; + /* must be a power of two */ + bios_total_size = 1; + while (bios_total_size < vga_bios_size) + bios_total_size <<= 1; + pci_register_io_region(d, PCI_ROM_SLOT, bios_total_size, + PCI_ADDRESS_SPACE_MEM_PREFETCH, vga_map); + } } else { #ifdef CONFIG_BOCHS_VBE /* XXX: use optimized standard vga accesses */ diff --git a/qemu/hw/vga_int.h b/qemu/hw/vga_int.h index 2e7fb30..621268d 100644 --- a/qemu/hw/vga_int.h +++ b/qemu/hw/vga_int.h @@ -78,6 +78,8 @@ uint8_t *vram_ptr; \ unsigned long vram_offset; \ unsigned int vram_size; \ + unsigned long bios_offset; \ + unsigned int bios_size; \ uint32_t latch; \ uint8_t sr_index; \ uint8_t sr[256]; \ diff --git a/qemu/linux-user/arm-semi.c b/qemu/linux-user/arm-semi.c index ea014f6..6950daa 100644 --- a/qemu/linux-user/arm-semi.c +++ b/qemu/linux-user/arm-semi.c @@ -99,7 +99,7 @@ uint32_t do_arm_semihosting(CPUState *env) else return STDOUT_FILENO; } - return set_swi_errno(ts, open(s, open_modeflags[ARG(1)])); + return set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644)); case SYS_CLOSE: return set_swi_errno(ts, close(ARG(0))); case SYS_WRITEC: diff --git a/qemu/linux-user/main.c b/qemu/linux-user/main.c index d0f662c..622d06e 100644 --- a/qemu/linux-user/main.c +++ b/qemu/linux-user/main.c @@ -552,6 +552,7 @@ void cpu_loop (CPUSPARCState *env) env->pc = env->npc; env->npc = env->npc + 4; break; +#ifndef TARGET_SPARC64 case TT_WIN_OVF: /* window overflow */ save_window(env); break; @@ -569,6 +570,9 @@ void cpu_loop (CPUSPARCState *env) queue_signal(info.si_signo, &info); } break; +#else + // XXX +#endif case 0x100: // XXX, why do we get these? break; case EXCP_DEBUG: @@ -696,33 +700,28 @@ void cpu_loop(CPUPPCState *env) info._sifields._sigfault._addr = env->nip - 4; queue_signal(info.si_signo, &info); case EXCP_DSI: - fprintf(stderr, "Invalid data memory access: 0x%08x\n", env->spr[DAR]); + fprintf(stderr, "Invalid data memory access: 0x%08x\n", + env->spr[SPR_DAR]); if (loglevel) { fprintf(logfile, "Invalid data memory access: 0x%08x\n", - env->spr[DAR]); + env->spr[SPR_DAR]); } - switch (env->error_code & 0xF) { - case EXCP_DSI_TRANSLATE: + switch (env->error_code & 0xFF000000) { + case 0x40000000: info.si_signo = TARGET_SIGSEGV; info.si_errno = 0; info.si_code = TARGET_SEGV_MAPERR; break; - case EXCP_DSI_NOTSUP: - case EXCP_DSI_EXTERNAL: + case 0x04000000: info.si_signo = TARGET_SIGILL; info.si_errno = 0; info.si_code = TARGET_ILL_ILLADR; break; - case EXCP_DSI_PROT: + case 0x08000000: info.si_signo = TARGET_SIGSEGV; info.si_errno = 0; info.si_code = TARGET_SEGV_ACCERR; break; - case EXCP_DSI_DABR: - info.si_signo = TARGET_SIGTRAP; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - break; default: /* Let's send a regular segfault... */ fprintf(stderr, "Invalid segfault errno (%02x)\n", @@ -743,19 +742,14 @@ void cpu_loop(CPUPPCState *env) fprintf(stderr, "Invalid instruction fetch\n"); if (loglevel) fprintf(logfile, "Invalid instruction fetch\n"); - switch (env->error_code) { - case EXCP_ISI_TRANSLATE: + switch (env->error_code & 0xFF000000) { + case 0x40000000: info.si_signo = TARGET_SIGSEGV; info.si_errno = 0; info.si_code = TARGET_SEGV_MAPERR; break; - case EXCP_ISI_GUARD: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLADR; - break; - case EXCP_ISI_NOEXEC: - case EXCP_ISI_PROT: + case 0x10000000: + case 0x08000000: info.si_signo = TARGET_SIGSEGV; info.si_errno = 0; info.si_code = TARGET_SEGV_ACCERR; @@ -925,18 +919,6 @@ void cpu_loop(CPUPPCState *env) if (loglevel) fprintf(logfile, "Decrementer exception\n"); abort(); - case EXCP_RESA: /* Implementation specific */ - /* Should not happen ! */ - fprintf(stderr, "RESA exception should never happen !\n"); - if (loglevel) - fprintf(logfile, "RESA exception should never happen !\n"); - abort(); - case EXCP_RESB: /* Implementation specific */ - /* Should not happen ! */ - fprintf(stderr, "RESB exception should never happen !\n"); - if (loglevel) - fprintf(logfile, "RESB exception should never happen !\n"); - abort(); case EXCP_TRACE: /* Do nothing: we use this to trace execution */ break; @@ -958,12 +940,6 @@ void cpu_loop(CPUPPCState *env) case EXCP_BRANCH: /* We stopped because of a jump... */ break; - case EXCP_RFI: - /* Should not occur: we always are in user mode */ - fprintf(stderr, "Return from interrupt ?\n"); - if (loglevel) - fprintf(logfile, "Return from interrupt ?\n"); - abort(); case EXCP_INTERRUPT: /* Don't know why this should ever happen... */ break; @@ -997,7 +973,7 @@ void cpu_loop(CPUPPCState *env) void usage(void) { - printf("qemu-" TARGET_ARCH " version " QEMU_VERSION ", Copyright (c) 2003-2004 Fabrice Bellard\n" + printf("qemu-" TARGET_ARCH " version " QEMU_VERSION ", Copyright (c) 2003-2005 Fabrice Bellard\n" "usage: qemu-" TARGET_ARCH " [-h] [-g] [-d opts] [-L path] [-s size] program [arguments...]\n" "Linux CPU emulator (compiled for %s emulation)\n" "\n" @@ -1239,7 +1215,25 @@ int main(int argc, char **argv) } #elif defined(TARGET_PPC) { + ppc_def_t *def; int i; + + /* Choose and initialise CPU */ + /* XXX: CPU model (or PVR) should be provided on command line */ + // ppc_find_by_name("750gx", &def); + // ppc_find_by_name("750fx", &def); + // ppc_find_by_name("750p", &def); + ppc_find_by_name("750", &def); + // ppc_find_by_name("G3", &def); + // ppc_find_by_name("604r", &def); + // ppc_find_by_name("604e", &def); + // ppc_find_by_name("604", &def); + if (def == NULL) { + cpu_abort(cpu_single_env, + "Unable to find PowerPC CPU definition\n"); + } + cpu_ppc_register(cpu_single_env, def); + for (i = 0; i < 32; i++) { if (i != 12 && i != 6 && i != 13) env->msr[i] = (regs->msr >> i) & 1; diff --git a/qemu/linux-user/syscall.c b/qemu/linux-user/syscall.c index 0a4f07e..e51f513 100644 --- a/qemu/linux-user/syscall.c +++ b/qemu/linux-user/syscall.c @@ -547,7 +547,21 @@ static long do_setsockopt(int sockfd, int level, int optname, break; case SOL_IP: switch(optname) { + case IP_TOS: + case IP_TTL: case IP_HDRINCL: + case IP_ROUTER_ALERT: + case IP_RECVOPTS: + case IP_RETOPTS: + case IP_PKTINFO: + case IP_MTU_DISCOVER: + case IP_RECVERR: + case IP_RECVTOS: +#ifdef IP_FREEBIND + case IP_FREEBIND: +#endif + case IP_MULTICAST_TTL: + case IP_MULTICAST_LOOP: val = 0; if (optlen >= sizeof(uint32_t)) { if (get_user(val, (uint32_t *)optval)) @@ -619,6 +633,45 @@ static long do_getsockopt(int sockfd, int level, int optname, /* These don't just return a single integer */ goto unimplemented; default: + goto int_case; + } + break; + case SOL_TCP: + /* TCP options all take an 'int' value. */ + int_case: + if (get_user(len, optlen)) + return -EFAULT; + if (len < 0) + return -EINVAL; + lv = sizeof(int); + ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv)); + if (ret < 0) + return ret; + val = tswap32(val); + if (len > lv) + len = lv; + if (copy_to_user(optval, &val, len)) + return -EFAULT; + if (put_user(len, optlen)) + return -EFAULT; + break; + case SOL_IP: + switch(optname) { + case IP_TOS: + case IP_TTL: + case IP_HDRINCL: + case IP_ROUTER_ALERT: + case IP_RECVOPTS: + case IP_RETOPTS: + case IP_PKTINFO: + case IP_MTU_DISCOVER: + case IP_RECVERR: + case IP_RECVTOS: +#ifdef IP_FREEBIND + case IP_FREEBIND: +#endif + case IP_MULTICAST_TTL: + case IP_MULTICAST_LOOP: if (get_user(len, optlen)) return -EFAULT; if (len < 0) @@ -627,14 +680,25 @@ static long do_getsockopt(int sockfd, int level, int optname, ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv)); if (ret < 0) return ret; - val = tswap32(val); - if (len > lv) - len = lv; - if (copy_to_user(optval, &val, len)) - return -EFAULT; - if (put_user(len, optlen)) - return -EFAULT; + if (len < sizeof(int) && len > 0 && val >= 0 && val < 255) { + unsigned char ucval = val; + len = 1; + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval,&ucval,1)) + return -EFAULT; + } else { + val = tswap32(val); + if (len > sizeof(int)) + len = sizeof(int); + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, &val, len)) + return -EFAULT; + } break; + default: + goto unimplemented; } break; default: @@ -2756,11 +2820,13 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, #endif #ifdef TARGET_NR_truncate64 case TARGET_NR_truncate64: - goto unimplemented; + ret = get_errno(truncate64((const char *)arg1, arg2)); + break; #endif #ifdef TARGET_NR_ftruncate64 case TARGET_NR_ftruncate64: - goto unimplemented; + ret = get_errno(ftruncate64(arg1, arg2)); + break; #endif #ifdef TARGET_NR_stat64 case TARGET_NR_stat64: diff --git a/qemu/mips-dis.c b/qemu/mips-dis.c new file mode 100644 index 0000000..fbc64c7 --- /dev/null +++ b/qemu/mips-dis.c @@ -0,0 +1,3998 @@ +/* Print mips instructions for GDB, the GNU debugger, or for objdump. + Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, + 2000, 2001, 2002, 2003 + Free Software Foundation, Inc. + Contributed by Nobuyuki Hikichi(hikichi@sra.co.jp). + +This file is part of GDB, GAS, and the GNU binutils. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "dis-asm.h" + +/* mips.h. Mips opcode list for GDB, the GNU debugger. + Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 + Free Software Foundation, Inc. + Contributed by Ralph Campbell and OSF + Commented and modified by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +1, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +will be useful, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this file; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* mips.h. Mips opcode list for GDB, the GNU debugger. + Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 + Free Software Foundation, Inc. + Contributed by Ralph Campbell and OSF + Commented and modified by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +1, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +will be useful, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this file; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* These are bit masks and shift counts to use to access the various + fields of an instruction. To retrieve the X field of an + instruction, use the expression + (i >> OP_SH_X) & OP_MASK_X + To set the same field (to j), use + i = (i &~ (OP_MASK_X << OP_SH_X)) | (j << OP_SH_X) + + Make sure you use fields that are appropriate for the instruction, + of course. + + The 'i' format uses OP, RS, RT and IMMEDIATE. + + The 'j' format uses OP and TARGET. + + The 'r' format uses OP, RS, RT, RD, SHAMT and FUNCT. + + The 'b' format uses OP, RS, RT and DELTA. + + The floating point 'i' format uses OP, RS, RT and IMMEDIATE. + + The floating point 'r' format uses OP, FMT, FT, FS, FD and FUNCT. + + A breakpoint instruction uses OP, CODE and SPEC (10 bits of the + breakpoint instruction are not defined; Kane says the breakpoint + code field in BREAK is 20 bits; yet MIPS assemblers and debuggers + only use ten bits). An optional two-operand form of break/sdbbp + allows the lower ten bits to be set too, and MIPS32 and later + architectures allow 20 bits to be set with a signal operand + (using CODE20). + + The syscall instruction uses CODE20. + + The general coprocessor instructions use COPZ. */ + +#define OP_MASK_OP 0x3f +#define OP_SH_OP 26 +#define OP_MASK_RS 0x1f +#define OP_SH_RS 21 +#define OP_MASK_FR 0x1f +#define OP_SH_FR 21 +#define OP_MASK_FMT 0x1f +#define OP_SH_FMT 21 +#define OP_MASK_BCC 0x7 +#define OP_SH_BCC 18 +#define OP_MASK_CODE 0x3ff +#define OP_SH_CODE 16 +#define OP_MASK_CODE2 0x3ff +#define OP_SH_CODE2 6 +#define OP_MASK_RT 0x1f +#define OP_SH_RT 16 +#define OP_MASK_FT 0x1f +#define OP_SH_FT 16 +#define OP_MASK_CACHE 0x1f +#define OP_SH_CACHE 16 +#define OP_MASK_RD 0x1f +#define OP_SH_RD 11 +#define OP_MASK_FS 0x1f +#define OP_SH_FS 11 +#define OP_MASK_PREFX 0x1f +#define OP_SH_PREFX 11 +#define OP_MASK_CCC 0x7 +#define OP_SH_CCC 8 +#define OP_MASK_CODE20 0xfffff /* 20 bit syscall/breakpoint code. */ +#define OP_SH_CODE20 6 +#define OP_MASK_SHAMT 0x1f +#define OP_SH_SHAMT 6 +#define OP_MASK_FD 0x1f +#define OP_SH_FD 6 +#define OP_MASK_TARGET 0x3ffffff +#define OP_SH_TARGET 0 +#define OP_MASK_COPZ 0x1ffffff +#define OP_SH_COPZ 0 +#define OP_MASK_IMMEDIATE 0xffff +#define OP_SH_IMMEDIATE 0 +#define OP_MASK_DELTA 0xffff +#define OP_SH_DELTA 0 +#define OP_MASK_FUNCT 0x3f +#define OP_SH_FUNCT 0 +#define OP_MASK_SPEC 0x3f +#define OP_SH_SPEC 0 +#define OP_SH_LOCC 8 /* FP condition code. */ +#define OP_SH_HICC 18 /* FP condition code. */ +#define OP_MASK_CC 0x7 +#define OP_SH_COP1NORM 25 /* Normal COP1 encoding. */ +#define OP_MASK_COP1NORM 0x1 /* a single bit. */ +#define OP_SH_COP1SPEC 21 /* COP1 encodings. */ +#define OP_MASK_COP1SPEC 0xf +#define OP_MASK_COP1SCLR 0x4 +#define OP_MASK_COP1CMP 0x3 +#define OP_SH_COP1CMP 4 +#define OP_SH_FORMAT 21 /* FP short format field. */ +#define OP_MASK_FORMAT 0x7 +#define OP_SH_TRUE 16 +#define OP_MASK_TRUE 0x1 +#define OP_SH_GE 17 +#define OP_MASK_GE 0x01 +#define OP_SH_UNSIGNED 16 +#define OP_MASK_UNSIGNED 0x1 +#define OP_SH_HINT 16 +#define OP_MASK_HINT 0x1f +#define OP_SH_MMI 0 /* Multimedia (parallel) op. */ +#define OP_MASK_MMI 0x3f +#define OP_SH_MMISUB 6 +#define OP_MASK_MMISUB 0x1f +#define OP_MASK_PERFREG 0x1f /* Performance monitoring. */ +#define OP_SH_PERFREG 1 +#define OP_SH_SEL 0 /* Coprocessor select field. */ +#define OP_MASK_SEL 0x7 /* The sel field of mfcZ and mtcZ. */ +#define OP_SH_CODE19 6 /* 19 bit wait code. */ +#define OP_MASK_CODE19 0x7ffff +#define OP_SH_ALN 21 +#define OP_MASK_ALN 0x7 +#define OP_SH_VSEL 21 +#define OP_MASK_VSEL 0x1f +#define OP_MASK_VECBYTE 0x7 /* Selector field is really 4 bits, + but 0x8-0xf don't select bytes. */ +#define OP_SH_VECBYTE 22 +#define OP_MASK_VECALIGN 0x7 /* Vector byte-align (alni.ob) op. */ +#define OP_SH_VECALIGN 21 +#define OP_MASK_INSMSB 0x1f /* "ins" MSB. */ +#define OP_SH_INSMSB 11 +#define OP_MASK_EXTMSBD 0x1f /* "ext" MSBD. */ +#define OP_SH_EXTMSBD 11 + +#define OP_OP_COP0 0x10 +#define OP_OP_COP1 0x11 +#define OP_OP_COP2 0x12 +#define OP_OP_COP3 0x13 +#define OP_OP_LWC1 0x31 +#define OP_OP_LWC2 0x32 +#define OP_OP_LWC3 0x33 /* a.k.a. pref */ +#define OP_OP_LDC1 0x35 +#define OP_OP_LDC2 0x36 +#define OP_OP_LDC3 0x37 /* a.k.a. ld */ +#define OP_OP_SWC1 0x39 +#define OP_OP_SWC2 0x3a +#define OP_OP_SWC3 0x3b +#define OP_OP_SDC1 0x3d +#define OP_OP_SDC2 0x3e +#define OP_OP_SDC3 0x3f /* a.k.a. sd */ + +/* Values in the 'VSEL' field. */ +#define MDMX_FMTSEL_IMM_QH 0x1d +#define MDMX_FMTSEL_IMM_OB 0x1e +#define MDMX_FMTSEL_VEC_QH 0x15 +#define MDMX_FMTSEL_VEC_OB 0x16 + +/* This structure holds information for a particular instruction. */ + +struct mips_opcode +{ + /* The name of the instruction. */ + const char *name; + /* A string describing the arguments for this instruction. */ + const char *args; + /* The basic opcode for the instruction. When assembling, this + opcode is modified by the arguments to produce the actual opcode + that is used. If pinfo is INSN_MACRO, then this is 0. */ + unsigned long match; + /* If pinfo is not INSN_MACRO, then this is a bit mask for the + relevant portions of the opcode when disassembling. If the + actual opcode anded with the match field equals the opcode field, + then we have found the correct instruction. If pinfo is + INSN_MACRO, then this field is the macro identifier. */ + unsigned long mask; + /* For a macro, this is INSN_MACRO. Otherwise, it is a collection + of bits describing the instruction, notably any relevant hazard + information. */ + unsigned long pinfo; + /* A collection of bits describing the instruction sets of which this + instruction or macro is a member. */ + unsigned long membership; +}; + +/* These are the characters which may appear in the args field of an + instruction. They appear in the order in which the fields appear + when the instruction is used. Commas and parentheses in the args + string are ignored when assembling, and written into the output + when disassembling. + + Each of these characters corresponds to a mask field defined above. + + "<" 5 bit shift amount (OP_*_SHAMT) + ">" shift amount between 32 and 63, stored after subtracting 32 (OP_*_SHAMT) + "a" 26 bit target address (OP_*_TARGET) + "b" 5 bit base register (OP_*_RS) + "c" 10 bit breakpoint code (OP_*_CODE) + "d" 5 bit destination register specifier (OP_*_RD) + "h" 5 bit prefx hint (OP_*_PREFX) + "i" 16 bit unsigned immediate (OP_*_IMMEDIATE) + "j" 16 bit signed immediate (OP_*_DELTA) + "k" 5 bit cache opcode in target register position (OP_*_CACHE) + Also used for immediate operands in vr5400 vector insns. + "o" 16 bit signed offset (OP_*_DELTA) + "p" 16 bit PC relative branch target address (OP_*_DELTA) + "q" 10 bit extra breakpoint code (OP_*_CODE2) + "r" 5 bit same register used as both source and target (OP_*_RS) + "s" 5 bit source register specifier (OP_*_RS) + "t" 5 bit target register (OP_*_RT) + "u" 16 bit upper 16 bits of address (OP_*_IMMEDIATE) + "v" 5 bit same register used as both source and destination (OP_*_RS) + "w" 5 bit same register used as both target and destination (OP_*_RT) + "U" 5 bit same destination register in both OP_*_RD and OP_*_RT + (used by clo and clz) + "C" 25 bit coprocessor function code (OP_*_COPZ) + "B" 20 bit syscall/breakpoint function code (OP_*_CODE20) + "J" 19 bit wait function code (OP_*_CODE19) + "x" accept and ignore register name + "z" must be zero register + "K" 5 bit Hardware Register (rdhwr instruction) (OP_*_RD) + "+A" 5 bit ins/ext position, which becomes LSB (OP_*_SHAMT). + Enforces: 0 <= pos < 32. + "+B" 5 bit ins size, which becomes MSB (OP_*_INSMSB). + Requires that "+A" or "+E" occur first to set position. + Enforces: 0 < (pos+size) <= 32. + "+C" 5 bit ext size, which becomes MSBD (OP_*_EXTMSBD). + Requires that "+A" or "+E" occur first to set position. + Enforces: 0 < (pos+size) <= 32. + (Also used by "dext" w/ different limits, but limits for + that are checked by the M_DEXT macro.) + "+E" 5 bit dins/dext position, which becomes LSB-32 (OP_*_SHAMT). + Enforces: 32 <= pos < 64. + "+F" 5 bit "dinsm" size, which becomes MSB-32 (OP_*_INSMSB). + Requires that "+A" or "+E" occur first to set position. + Enforces: 32 < (pos+size) <= 64. + "+G" 5 bit "dextm" size, which becomes MSBD-32 (OP_*_EXTMSBD). + Requires that "+A" or "+E" occur first to set position. + Enforces: 32 < (pos+size) <= 64. + "+H" 5 bit "dextu" size, which becomes MSBD (OP_*_EXTMSBD). + Requires that "+A" or "+E" occur first to set position. + Enforces: 32 < (pos+size) <= 64. + + Floating point instructions: + "D" 5 bit destination register (OP_*_FD) + "M" 3 bit compare condition code (OP_*_CCC) (only used for mips4 and up) + "N" 3 bit branch condition code (OP_*_BCC) (only used for mips4 and up) + "S" 5 bit fs source 1 register (OP_*_FS) + "T" 5 bit ft source 2 register (OP_*_FT) + "R" 5 bit fr source 3 register (OP_*_FR) + "V" 5 bit same register used as floating source and destination (OP_*_FS) + "W" 5 bit same register used as floating target and destination (OP_*_FT) + + Coprocessor instructions: + "E" 5 bit target register (OP_*_RT) + "G" 5 bit destination register (OP_*_RD) + "H" 3 bit sel field for (d)mtc* and (d)mfc* (OP_*_SEL) + "P" 5 bit performance-monitor register (OP_*_PERFREG) + "e" 5 bit vector register byte specifier (OP_*_VECBYTE) + "%" 3 bit immediate vr5400 vector alignment operand (OP_*_VECALIGN) + see also "k" above + "+D" Combined destination register ("G") and sel ("H") for CP0 ops, + for pretty-printing in disassembly only. + + Macro instructions: + "A" General 32 bit expression + "I" 32 bit immediate (value placed in imm_expr). + "+I" 32 bit immediate (value placed in imm2_expr). + "F" 64 bit floating point constant in .rdata + "L" 64 bit floating point constant in .lit8 + "f" 32 bit floating point constant + "l" 32 bit floating point constant in .lit4 + + MDMX instruction operands (note that while these use the FP register + fields, they accept both $fN and $vN names for the registers): + "O" MDMX alignment offset (OP_*_ALN) + "Q" MDMX vector/scalar/immediate source (OP_*_VSEL and OP_*_FT) + "X" MDMX destination register (OP_*_FD) + "Y" MDMX source register (OP_*_FS) + "Z" MDMX source register (OP_*_FT) + + Other: + "()" parens surrounding optional value + "," separates operands + "[]" brackets around index for vector-op scalar operand specifier (vr5400) + "+" Start of extension sequence. + + Characters used so far, for quick reference when adding more: + "%[]<>(),+" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefhijklopqrstuvwxz" + + Extension character sequences used so far ("+" followed by the + following), for quick reference when adding more: + "ABCDEFGHI" +*/ + +/* These are the bits which may be set in the pinfo field of an + instructions, if it is not equal to INSN_MACRO. */ + +/* Modifies the general purpose register in OP_*_RD. */ +#define INSN_WRITE_GPR_D 0x00000001 +/* Modifies the general purpose register in OP_*_RT. */ +#define INSN_WRITE_GPR_T 0x00000002 +/* Modifies general purpose register 31. */ +#define INSN_WRITE_GPR_31 0x00000004 +/* Modifies the floating point register in OP_*_FD. */ +#define INSN_WRITE_FPR_D 0x00000008 +/* Modifies the floating point register in OP_*_FS. */ +#define INSN_WRITE_FPR_S 0x00000010 +/* Modifies the floating point register in OP_*_FT. */ +#define INSN_WRITE_FPR_T 0x00000020 +/* Reads the general purpose register in OP_*_RS. */ +#define INSN_READ_GPR_S 0x00000040 +/* Reads the general purpose register in OP_*_RT. */ +#define INSN_READ_GPR_T 0x00000080 +/* Reads the floating point register in OP_*_FS. */ +#define INSN_READ_FPR_S 0x00000100 +/* Reads the floating point register in OP_*_FT. */ +#define INSN_READ_FPR_T 0x00000200 +/* Reads the floating point register in OP_*_FR. */ +#define INSN_READ_FPR_R 0x00000400 +/* Modifies coprocessor condition code. */ +#define INSN_WRITE_COND_CODE 0x00000800 +/* Reads coprocessor condition code. */ +#define INSN_READ_COND_CODE 0x00001000 +/* TLB operation. */ +#define INSN_TLB 0x00002000 +/* Reads coprocessor register other than floating point register. */ +#define INSN_COP 0x00004000 +/* Instruction loads value from memory, requiring delay. */ +#define INSN_LOAD_MEMORY_DELAY 0x00008000 +/* Instruction loads value from coprocessor, requiring delay. */ +#define INSN_LOAD_COPROC_DELAY 0x00010000 +/* Instruction has unconditional branch delay slot. */ +#define INSN_UNCOND_BRANCH_DELAY 0x00020000 +/* Instruction has conditional branch delay slot. */ +#define INSN_COND_BRANCH_DELAY 0x00040000 +/* Conditional branch likely: if branch not taken, insn nullified. */ +#define INSN_COND_BRANCH_LIKELY 0x00080000 +/* Moves to coprocessor register, requiring delay. */ +#define INSN_COPROC_MOVE_DELAY 0x00100000 +/* Loads coprocessor register from memory, requiring delay. */ +#define INSN_COPROC_MEMORY_DELAY 0x00200000 +/* Reads the HI register. */ +#define INSN_READ_HI 0x00400000 +/* Reads the LO register. */ +#define INSN_READ_LO 0x00800000 +/* Modifies the HI register. */ +#define INSN_WRITE_HI 0x01000000 +/* Modifies the LO register. */ +#define INSN_WRITE_LO 0x02000000 +/* Takes a trap (easier to keep out of delay slot). */ +#define INSN_TRAP 0x04000000 +/* Instruction stores value into memory. */ +#define INSN_STORE_MEMORY 0x08000000 +/* Instruction uses single precision floating point. */ +#define FP_S 0x10000000 +/* Instruction uses double precision floating point. */ +#define FP_D 0x20000000 +/* Instruction is part of the tx39's integer multiply family. */ +#define INSN_MULT 0x40000000 +/* Instruction synchronize shared memory. */ +#define INSN_SYNC 0x80000000 +/* Instruction reads MDMX accumulator. XXX FIXME: No bits left! */ +#define INSN_READ_MDMX_ACC 0 +/* Instruction writes MDMX accumulator. XXX FIXME: No bits left! */ +#define INSN_WRITE_MDMX_ACC 0 + +/* Instruction is actually a macro. It should be ignored by the + disassembler, and requires special treatment by the assembler. */ +#define INSN_MACRO 0xffffffff + +/* Masks used to mark instructions to indicate which MIPS ISA level + they were introduced in. ISAs, as defined below, are logical + ORs of these bits, indicating that they support the instructions + defined at the given level. */ + +#define INSN_ISA_MASK 0x00000fff +#define INSN_ISA1 0x00000001 +#define INSN_ISA2 0x00000002 +#define INSN_ISA3 0x00000004 +#define INSN_ISA4 0x00000008 +#define INSN_ISA5 0x00000010 +#define INSN_ISA32 0x00000020 +#define INSN_ISA64 0x00000040 +#define INSN_ISA32R2 0x00000080 +#define INSN_ISA64R2 0x00000100 + +/* Masks used for MIPS-defined ASEs. */ +#define INSN_ASE_MASK 0x0000f000 + +/* MIPS 16 ASE */ +#define INSN_MIPS16 0x00002000 +/* MIPS-3D ASE */ +#define INSN_MIPS3D 0x00004000 +/* MDMX ASE */ +#define INSN_MDMX 0x00008000 + +/* Chip specific instructions. These are bitmasks. */ + +/* MIPS R4650 instruction. */ +#define INSN_4650 0x00010000 +/* LSI R4010 instruction. */ +#define INSN_4010 0x00020000 +/* NEC VR4100 instruction. */ +#define INSN_4100 0x00040000 +/* Toshiba R3900 instruction. */ +#define INSN_3900 0x00080000 +/* MIPS R10000 instruction. */ +#define INSN_10000 0x00100000 +/* Broadcom SB-1 instruction. */ +#define INSN_SB1 0x00200000 +/* NEC VR4111/VR4181 instruction. */ +#define INSN_4111 0x00400000 +/* NEC VR4120 instruction. */ +#define INSN_4120 0x00800000 +/* NEC VR5400 instruction. */ +#define INSN_5400 0x01000000 +/* NEC VR5500 instruction. */ +#define INSN_5500 0x02000000 + +/* MIPS ISA defines, use instead of hardcoding ISA level. */ + +#define ISA_UNKNOWN 0 /* Gas internal use. */ +#define ISA_MIPS1 (INSN_ISA1) +#define ISA_MIPS2 (ISA_MIPS1 | INSN_ISA2) +#define ISA_MIPS3 (ISA_MIPS2 | INSN_ISA3) +#define ISA_MIPS4 (ISA_MIPS3 | INSN_ISA4) +#define ISA_MIPS5 (ISA_MIPS4 | INSN_ISA5) + +#define ISA_MIPS32 (ISA_MIPS2 | INSN_ISA32) +#define ISA_MIPS64 (ISA_MIPS5 | INSN_ISA32 | INSN_ISA64) + +#define ISA_MIPS32R2 (ISA_MIPS32 | INSN_ISA32R2) +#define ISA_MIPS64R2 (ISA_MIPS64 | INSN_ISA32R2 | INSN_ISA64R2) + + +/* CPU defines, use instead of hardcoding processor number. Keep this + in sync with bfd/archures.c in order for machine selection to work. */ +#define CPU_UNKNOWN 0 /* Gas internal use. */ +#define CPU_R3000 3000 +#define CPU_R3900 3900 +#define CPU_R4000 4000 +#define CPU_R4010 4010 +#define CPU_VR4100 4100 +#define CPU_R4111 4111 +#define CPU_VR4120 4120 +#define CPU_R4300 4300 +#define CPU_R4400 4400 +#define CPU_R4600 4600 +#define CPU_R4650 4650 +#define CPU_R5000 5000 +#define CPU_VR5400 5400 +#define CPU_VR5500 5500 +#define CPU_R6000 6000 +#define CPU_RM7000 7000 +#define CPU_R8000 8000 +#define CPU_R10000 10000 +#define CPU_R12000 12000 +#define CPU_MIPS16 16 +#define CPU_MIPS32 32 +#define CPU_MIPS32R2 33 +#define CPU_MIPS5 5 +#define CPU_MIPS64 64 +#define CPU_MIPS64R2 65 +#define CPU_SB1 12310201 /* octal 'SB', 01. */ + +/* Test for membership in an ISA including chip specific ISAs. INSN + is pointer to an element of the opcode table; ISA is the specified + ISA/ASE bitmask to test against; and CPU is the CPU specific ISA to + test, or zero if no CPU specific ISA test is desired. */ + +#define OPCODE_IS_MEMBER(insn, isa, cpu) \ + (((insn)->membership & isa) != 0 \ + || (cpu == CPU_R4650 && ((insn)->membership & INSN_4650) != 0) \ + || (cpu == CPU_RM7000 && ((insn)->membership & INSN_4650) != 0) \ + || (cpu == CPU_R4010 && ((insn)->membership & INSN_4010) != 0) \ + || (cpu == CPU_VR4100 && ((insn)->membership & INSN_4100) != 0) \ + || (cpu == CPU_R3900 && ((insn)->membership & INSN_3900) != 0) \ + || ((cpu == CPU_R10000 || cpu == CPU_R12000) \ + && ((insn)->membership & INSN_10000) != 0) \ + || (cpu == CPU_SB1 && ((insn)->membership & INSN_SB1) != 0) \ + || (cpu == CPU_R4111 && ((insn)->membership & INSN_4111) != 0) \ + || (cpu == CPU_VR4120 && ((insn)->membership & INSN_4120) != 0) \ + || (cpu == CPU_VR5400 && ((insn)->membership & INSN_5400) != 0) \ + || (cpu == CPU_VR5500 && ((insn)->membership & INSN_5500) != 0) \ + || 0) /* Please keep this term for easier source merging. */ + +/* This is a list of macro expanded instructions. + + _I appended means immediate + _A appended means address + _AB appended means address with base register + _D appended means 64 bit floating point constant + _S appended means 32 bit floating point constant. */ + +enum +{ + M_ABS, + M_ADD_I, + M_ADDU_I, + M_AND_I, + M_BEQ, + M_BEQ_I, + M_BEQL_I, + M_BGE, + M_BGEL, + M_BGE_I, + M_BGEL_I, + M_BGEU, + M_BGEUL, + M_BGEU_I, + M_BGEUL_I, + M_BGT, + M_BGTL, + M_BGT_I, + M_BGTL_I, + M_BGTU, + M_BGTUL, + M_BGTU_I, + M_BGTUL_I, + M_BLE, + M_BLEL, + M_BLE_I, + M_BLEL_I, + M_BLEU, + M_BLEUL, + M_BLEU_I, + M_BLEUL_I, + M_BLT, + M_BLTL, + M_BLT_I, + M_BLTL_I, + M_BLTU, + M_BLTUL, + M_BLTU_I, + M_BLTUL_I, + M_BNE, + M_BNE_I, + M_BNEL_I, + M_DABS, + M_DADD_I, + M_DADDU_I, + M_DDIV_3, + M_DDIV_3I, + M_DDIVU_3, + M_DDIVU_3I, + M_DEXT, + M_DINS, + M_DIV_3, + M_DIV_3I, + M_DIVU_3, + M_DIVU_3I, + M_DLA_AB, + M_DLCA_AB, + M_DLI, + M_DMUL, + M_DMUL_I, + M_DMULO, + M_DMULO_I, + M_DMULOU, + M_DMULOU_I, + M_DREM_3, + M_DREM_3I, + M_DREMU_3, + M_DREMU_3I, + M_DSUB_I, + M_DSUBU_I, + M_DSUBU_I_2, + M_J_A, + M_JAL_1, + M_JAL_2, + M_JAL_A, + M_L_DOB, + M_L_DAB, + M_LA_AB, + M_LB_A, + M_LB_AB, + M_LBU_A, + M_LBU_AB, + M_LCA_AB, + M_LD_A, + M_LD_OB, + M_LD_AB, + M_LDC1_AB, + M_LDC2_AB, + M_LDC3_AB, + M_LDL_AB, + M_LDR_AB, + M_LH_A, + M_LH_AB, + M_LHU_A, + M_LHU_AB, + M_LI, + M_LI_D, + M_LI_DD, + M_LI_S, + M_LI_SS, + M_LL_AB, + M_LLD_AB, + M_LS_A, + M_LW_A, + M_LW_AB, + M_LWC0_A, + M_LWC0_AB, + M_LWC1_A, + M_LWC1_AB, + M_LWC2_A, + M_LWC2_AB, + M_LWC3_A, + M_LWC3_AB, + M_LWL_A, + M_LWL_AB, + M_LWR_A, + M_LWR_AB, + M_LWU_AB, + M_MOVE, + M_MUL, + M_MUL_I, + M_MULO, + M_MULO_I, + M_MULOU, + M_MULOU_I, + M_NOR_I, + M_OR_I, + M_REM_3, + M_REM_3I, + M_REMU_3, + M_REMU_3I, + M_DROL, + M_ROL, + M_DROL_I, + M_ROL_I, + M_DROR, + M_ROR, + M_DROR_I, + M_ROR_I, + M_S_DA, + M_S_DOB, + M_S_DAB, + M_S_S, + M_SC_AB, + M_SCD_AB, + M_SD_A, + M_SD_OB, + M_SD_AB, + M_SDC1_AB, + M_SDC2_AB, + M_SDC3_AB, + M_SDL_AB, + M_SDR_AB, + M_SEQ, + M_SEQ_I, + M_SGE, + M_SGE_I, + M_SGEU, + M_SGEU_I, + M_SGT, + M_SGT_I, + M_SGTU, + M_SGTU_I, + M_SLE, + M_SLE_I, + M_SLEU, + M_SLEU_I, + M_SLT_I, + M_SLTU_I, + M_SNE, + M_SNE_I, + M_SB_A, + M_SB_AB, + M_SH_A, + M_SH_AB, + M_SW_A, + M_SW_AB, + M_SWC0_A, + M_SWC0_AB, + M_SWC1_A, + M_SWC1_AB, + M_SWC2_A, + M_SWC2_AB, + M_SWC3_A, + M_SWC3_AB, + M_SWL_A, + M_SWL_AB, + M_SWR_A, + M_SWR_AB, + M_SUB_I, + M_SUBU_I, + M_SUBU_I_2, + M_TEQ_I, + M_TGE_I, + M_TGEU_I, + M_TLT_I, + M_TLTU_I, + M_TNE_I, + M_TRUNCWD, + M_TRUNCWS, + M_ULD, + M_ULD_A, + M_ULH, + M_ULH_A, + M_ULHU, + M_ULHU_A, + M_ULW, + M_ULW_A, + M_USH, + M_USH_A, + M_USW, + M_USW_A, + M_USD, + M_USD_A, + M_XOR_I, + M_COP0, + M_COP1, + M_COP2, + M_COP3, + M_NUM_MACROS +}; + + +/* The order of overloaded instructions matters. Label arguments and + register arguments look the same. Instructions that can have either + for arguments must apear in the correct order in this table for the + assembler to pick the right one. In other words, entries with + immediate operands must apear after the same instruction with + registers. + + Many instructions are short hand for other instructions (i.e., The + jal instruction is short for jalr ). */ + +extern const struct mips_opcode mips_builtin_opcodes[]; +extern const int bfd_mips_num_builtin_opcodes; +extern struct mips_opcode *mips_opcodes; +extern int bfd_mips_num_opcodes; +#define NUMOPCODES bfd_mips_num_opcodes + + +/* The rest of this file adds definitions for the mips16 TinyRISC + processor. */ + +/* These are the bitmasks and shift counts used for the different + fields in the instruction formats. Other than OP, no masks are + provided for the fixed portions of an instruction, since they are + not needed. + + The I format uses IMM11. + + The RI format uses RX and IMM8. + + The RR format uses RX, and RY. + + The RRI format uses RX, RY, and IMM5. + + The RRR format uses RX, RY, and RZ. + + The RRI_A format uses RX, RY, and IMM4. + + The SHIFT format uses RX, RY, and SHAMT. + + The I8 format uses IMM8. + + The I8_MOVR32 format uses RY and REGR32. + + The IR_MOV32R format uses REG32R and MOV32Z. + + The I64 format uses IMM8. + + The RI64 format uses RY and IMM5. + */ + +#define MIPS16OP_MASK_OP 0x1f +#define MIPS16OP_SH_OP 11 +#define MIPS16OP_MASK_IMM11 0x7ff +#define MIPS16OP_SH_IMM11 0 +#define MIPS16OP_MASK_RX 0x7 +#define MIPS16OP_SH_RX 8 +#define MIPS16OP_MASK_IMM8 0xff +#define MIPS16OP_SH_IMM8 0 +#define MIPS16OP_MASK_RY 0x7 +#define MIPS16OP_SH_RY 5 +#define MIPS16OP_MASK_IMM5 0x1f +#define MIPS16OP_SH_IMM5 0 +#define MIPS16OP_MASK_RZ 0x7 +#define MIPS16OP_SH_RZ 2 +#define MIPS16OP_MASK_IMM4 0xf +#define MIPS16OP_SH_IMM4 0 +#define MIPS16OP_MASK_REGR32 0x1f +#define MIPS16OP_SH_REGR32 0 +#define MIPS16OP_MASK_REG32R 0x1f +#define MIPS16OP_SH_REG32R 3 +#define MIPS16OP_EXTRACT_REG32R(i) ((((i) >> 5) & 7) | ((i) & 0x18)) +#define MIPS16OP_MASK_MOVE32Z 0x7 +#define MIPS16OP_SH_MOVE32Z 0 +#define MIPS16OP_MASK_IMM6 0x3f +#define MIPS16OP_SH_IMM6 5 + +/* These are the characters which may appears in the args field of an + instruction. They appear in the order in which the fields appear + when the instruction is used. Commas and parentheses in the args + string are ignored when assembling, and written into the output + when disassembling. + + "y" 3 bit register (MIPS16OP_*_RY) + "x" 3 bit register (MIPS16OP_*_RX) + "z" 3 bit register (MIPS16OP_*_RZ) + "Z" 3 bit register (MIPS16OP_*_MOVE32Z) + "v" 3 bit same register as source and destination (MIPS16OP_*_RX) + "w" 3 bit same register as source and destination (MIPS16OP_*_RY) + "0" zero register ($0) + "S" stack pointer ($sp or $29) + "P" program counter + "R" return address register ($ra or $31) + "X" 5 bit MIPS register (MIPS16OP_*_REGR32) + "Y" 5 bit MIPS register (MIPS16OP_*_REG32R) + "6" 6 bit unsigned break code (MIPS16OP_*_IMM6) + "a" 26 bit jump address + "e" 11 bit extension value + "l" register list for entry instruction + "L" register list for exit instruction + + The remaining codes may be extended. Except as otherwise noted, + the full extended operand is a 16 bit signed value. + "<" 3 bit unsigned shift count * 0 (MIPS16OP_*_RZ) (full 5 bit unsigned) + ">" 3 bit unsigned shift count * 0 (MIPS16OP_*_RX) (full 5 bit unsigned) + "[" 3 bit unsigned shift count * 0 (MIPS16OP_*_RZ) (full 6 bit unsigned) + "]" 3 bit unsigned shift count * 0 (MIPS16OP_*_RX) (full 6 bit unsigned) + "4" 4 bit signed immediate * 0 (MIPS16OP_*_IMM4) (full 15 bit signed) + "5" 5 bit unsigned immediate * 0 (MIPS16OP_*_IMM5) + "H" 5 bit unsigned immediate * 2 (MIPS16OP_*_IMM5) + "W" 5 bit unsigned immediate * 4 (MIPS16OP_*_IMM5) + "D" 5 bit unsigned immediate * 8 (MIPS16OP_*_IMM5) + "j" 5 bit signed immediate * 0 (MIPS16OP_*_IMM5) + "8" 8 bit unsigned immediate * 0 (MIPS16OP_*_IMM8) + "V" 8 bit unsigned immediate * 4 (MIPS16OP_*_IMM8) + "C" 8 bit unsigned immediate * 8 (MIPS16OP_*_IMM8) + "U" 8 bit unsigned immediate * 0 (MIPS16OP_*_IMM8) (full 16 bit unsigned) + "k" 8 bit signed immediate * 0 (MIPS16OP_*_IMM8) + "K" 8 bit signed immediate * 8 (MIPS16OP_*_IMM8) + "p" 8 bit conditional branch address (MIPS16OP_*_IMM8) + "q" 11 bit branch address (MIPS16OP_*_IMM11) + "A" 8 bit PC relative address * 4 (MIPS16OP_*_IMM8) + "B" 5 bit PC relative address * 8 (MIPS16OP_*_IMM5) + "E" 5 bit PC relative address * 4 (MIPS16OP_*_IMM5) + */ + +/* For the mips16, we use the same opcode table format and a few of + the same flags. However, most of the flags are different. */ + +/* Modifies the register in MIPS16OP_*_RX. */ +#define MIPS16_INSN_WRITE_X 0x00000001 +/* Modifies the register in MIPS16OP_*_RY. */ +#define MIPS16_INSN_WRITE_Y 0x00000002 +/* Modifies the register in MIPS16OP_*_RZ. */ +#define MIPS16_INSN_WRITE_Z 0x00000004 +/* Modifies the T ($24) register. */ +#define MIPS16_INSN_WRITE_T 0x00000008 +/* Modifies the SP ($29) register. */ +#define MIPS16_INSN_WRITE_SP 0x00000010 +/* Modifies the RA ($31) register. */ +#define MIPS16_INSN_WRITE_31 0x00000020 +/* Modifies the general purpose register in MIPS16OP_*_REG32R. */ +#define MIPS16_INSN_WRITE_GPR_Y 0x00000040 +/* Reads the register in MIPS16OP_*_RX. */ +#define MIPS16_INSN_READ_X 0x00000080 +/* Reads the register in MIPS16OP_*_RY. */ +#define MIPS16_INSN_READ_Y 0x00000100 +/* Reads the register in MIPS16OP_*_MOVE32Z. */ +#define MIPS16_INSN_READ_Z 0x00000200 +/* Reads the T ($24) register. */ +#define MIPS16_INSN_READ_T 0x00000400 +/* Reads the SP ($29) register. */ +#define MIPS16_INSN_READ_SP 0x00000800 +/* Reads the RA ($31) register. */ +#define MIPS16_INSN_READ_31 0x00001000 +/* Reads the program counter. */ +#define MIPS16_INSN_READ_PC 0x00002000 +/* Reads the general purpose register in MIPS16OP_*_REGR32. */ +#define MIPS16_INSN_READ_GPR_X 0x00004000 +/* Is a branch insn. */ +#define MIPS16_INSN_BRANCH 0x00010000 + +/* The following flags have the same value for the mips16 opcode + table: + INSN_UNCOND_BRANCH_DELAY + INSN_COND_BRANCH_DELAY + INSN_COND_BRANCH_LIKELY (never used) + INSN_READ_HI + INSN_READ_LO + INSN_WRITE_HI + INSN_WRITE_LO + INSN_TRAP + INSN_ISA3 + */ + +extern const struct mips_opcode mips16_opcodes[]; +extern const int bfd_mips16_num_opcodes; + +/* Short hand so the lines aren't too long. */ + +#define LDD INSN_LOAD_MEMORY_DELAY +#define LCD INSN_LOAD_COPROC_DELAY +#define UBD INSN_UNCOND_BRANCH_DELAY +#define CBD INSN_COND_BRANCH_DELAY +#define COD INSN_COPROC_MOVE_DELAY +#define CLD INSN_COPROC_MEMORY_DELAY +#define CBL INSN_COND_BRANCH_LIKELY +#define TRAP INSN_TRAP +#define SM INSN_STORE_MEMORY + +#define WR_d INSN_WRITE_GPR_D +#define WR_t INSN_WRITE_GPR_T +#define WR_31 INSN_WRITE_GPR_31 +#define WR_D INSN_WRITE_FPR_D +#define WR_T INSN_WRITE_FPR_T +#define WR_S INSN_WRITE_FPR_S +#define RD_s INSN_READ_GPR_S +#define RD_b INSN_READ_GPR_S +#define RD_t INSN_READ_GPR_T +#define RD_S INSN_READ_FPR_S +#define RD_T INSN_READ_FPR_T +#define RD_R INSN_READ_FPR_R +#define WR_CC INSN_WRITE_COND_CODE +#define RD_CC INSN_READ_COND_CODE +#define RD_C0 INSN_COP +#define RD_C1 INSN_COP +#define RD_C2 INSN_COP +#define RD_C3 INSN_COP +#define WR_C0 INSN_COP +#define WR_C1 INSN_COP +#define WR_C2 INSN_COP +#define WR_C3 INSN_COP + +#define WR_HI INSN_WRITE_HI +#define RD_HI INSN_READ_HI +#define MOD_HI WR_HI|RD_HI + +#define WR_LO INSN_WRITE_LO +#define RD_LO INSN_READ_LO +#define MOD_LO WR_LO|RD_LO + +#define WR_HILO WR_HI|WR_LO +#define RD_HILO RD_HI|RD_LO +#define MOD_HILO WR_HILO|RD_HILO + +#define IS_M INSN_MULT + +#define WR_MACC INSN_WRITE_MDMX_ACC +#define RD_MACC INSN_READ_MDMX_ACC + +#define I1 INSN_ISA1 +#define I2 INSN_ISA2 +#define I3 INSN_ISA3 +#define I4 INSN_ISA4 +#define I5 INSN_ISA5 +#define I32 INSN_ISA32 +#define I64 INSN_ISA64 +#define I33 INSN_ISA32R2 +#define I65 INSN_ISA64R2 + +/* MIPS64 MIPS-3D ASE support. */ +#define I16 INSN_MIPS16 + +/* MIPS64 MIPS-3D ASE support. */ +#define M3D INSN_MIPS3D + +/* MIPS64 MDMX ASE support. */ +#define MX INSN_MDMX + +#define P3 INSN_4650 +#define L1 INSN_4010 +#define V1 (INSN_4100 | INSN_4111 | INSN_4120) +#define T3 INSN_3900 +#define M1 INSN_10000 +#define SB1 INSN_SB1 +#define N411 INSN_4111 +#define N412 INSN_4120 +#define N5 (INSN_5400 | INSN_5500) +#define N54 INSN_5400 +#define N55 INSN_5500 + +#define G1 (T3 \ + ) + +#define G2 (T3 \ + ) + +#define G3 (I4 \ + ) + +/* The order of overloaded instructions matters. Label arguments and + register arguments look the same. Instructions that can have either + for arguments must apear in the correct order in this table for the + assembler to pick the right one. In other words, entries with + immediate operands must apear after the same instruction with + registers. + + Because of the lookup algorithm used, entries with the same opcode + name must be contiguous. + + Many instructions are short hand for other instructions (i.e., The + jal instruction is short for jalr ). */ + +const struct mips_opcode mips_builtin_opcodes[] = +{ +/* These instructions appear first so that the disassembler will find + them first. The assemblers uses a hash table based on the + instruction name anyhow. */ +/* name, args, match, mask, pinfo, membership */ +{"pref", "k,o(b)", 0xcc000000, 0xfc000000, RD_b, I4|I32|G3 }, +{"prefx", "h,t(b)", 0x4c00000f, 0xfc0007ff, RD_b|RD_t, I4 }, +{"nop", "", 0x00000000, 0xffffffff, 0, I1 }, /* sll */ +{"ssnop", "", 0x00000040, 0xffffffff, 0, I32|N55 }, /* sll */ +{"ehb", "", 0x000000c0, 0xffffffff, 0, I33 }, /* sll */ +{"li", "t,j", 0x24000000, 0xffe00000, WR_t, I1 }, /* addiu */ +{"li", "t,i", 0x34000000, 0xffe00000, WR_t, I1 }, /* ori */ +{"li", "t,I", 0, (int) M_LI, INSN_MACRO, I1 }, +{"move", "d,s", 0, (int) M_MOVE, INSN_MACRO, I1 }, +{"move", "d,s", 0x0000002d, 0xfc1f07ff, WR_d|RD_s, I3 },/* daddu */ +{"move", "d,s", 0x00000021, 0xfc1f07ff, WR_d|RD_s, I1 },/* addu */ +{"move", "d,s", 0x00000025, 0xfc1f07ff, WR_d|RD_s, I1 },/* or */ +{"b", "p", 0x10000000, 0xffff0000, UBD, I1 },/* beq 0,0 */ +{"b", "p", 0x04010000, 0xffff0000, UBD, I1 },/* bgez 0 */ +{"bal", "p", 0x04110000, 0xffff0000, UBD|WR_31, I1 },/* bgezal 0*/ + +{"abs", "d,v", 0, (int) M_ABS, INSN_MACRO, I1 }, +{"abs.s", "D,V", 0x46000005, 0xffff003f, WR_D|RD_S|FP_S, I1 }, +{"abs.d", "D,V", 0x46200005, 0xffff003f, WR_D|RD_S|FP_D, I1 }, +{"abs.ps", "D,V", 0x46c00005, 0xffff003f, WR_D|RD_S|FP_D, I5 }, +{"add", "d,v,t", 0x00000020, 0xfc0007ff, WR_d|RD_s|RD_t, I1 }, +{"add", "t,r,I", 0, (int) M_ADD_I, INSN_MACRO, I1 }, +{"add.s", "D,V,T", 0x46000000, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, I1 }, +{"add.d", "D,V,T", 0x46200000, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I1 }, +{"add.ob", "X,Y,Q", 0x7800000b, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 }, +{"add.ob", "D,S,T", 0x4ac0000b, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"add.ob", "D,S,T[e]", 0x4800000b, 0xfe20003f, WR_D|RD_S|RD_T, N54 }, +{"add.ob", "D,S,k", 0x4bc0000b, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"add.ps", "D,V,T", 0x46c00000, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 }, +{"add.qh", "X,Y,Q", 0x7820000b, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX }, +{"adda.ob", "Y,Q", 0x78000037, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 }, +{"adda.qh", "Y,Q", 0x78200037, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX }, +{"addi", "t,r,j", 0x20000000, 0xfc000000, WR_t|RD_s, I1 }, +{"addiu", "t,r,j", 0x24000000, 0xfc000000, WR_t|RD_s, I1 }, +{"addl.ob", "Y,Q", 0x78000437, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 }, +{"addl.qh", "Y,Q", 0x78200437, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX }, +{"addr.ps", "D,S,T", 0x46c00018, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, M3D }, +{"addu", "d,v,t", 0x00000021, 0xfc0007ff, WR_d|RD_s|RD_t, I1 }, +{"addu", "t,r,I", 0, (int) M_ADDU_I, INSN_MACRO, I1 }, +{"alni.ob", "X,Y,Z,O", 0x78000018, 0xff00003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 }, +{"alni.ob", "D,S,T,%", 0x48000018, 0xff00003f, WR_D|RD_S|RD_T, N54 }, +{"alni.qh", "X,Y,Z,O", 0x7800001a, 0xff00003f, WR_D|RD_S|RD_T|FP_D, MX }, +{"alnv.ps", "D,V,T,s", 0x4c00001e, 0xfc00003f, WR_D|RD_S|RD_T|FP_D, I5 }, +{"alnv.ob", "X,Y,Z,s", 0x78000019, 0xfc00003f, WR_D|RD_S|RD_T|RD_s|FP_D, MX|SB1 }, +{"alnv.qh", "X,Y,Z,s", 0x7800001b, 0xfc00003f, WR_D|RD_S|RD_T|RD_s|FP_D, MX }, +{"and", "d,v,t", 0x00000024, 0xfc0007ff, WR_d|RD_s|RD_t, I1 }, +{"and", "t,r,I", 0, (int) M_AND_I, INSN_MACRO, I1 }, +{"and.ob", "X,Y,Q", 0x7800000c, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 }, +{"and.ob", "D,S,T", 0x4ac0000c, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"and.ob", "D,S,T[e]", 0x4800000c, 0xfe20003f, WR_D|RD_S|RD_T, N54 }, +{"and.ob", "D,S,k", 0x4bc0000c, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"and.qh", "X,Y,Q", 0x7820000c, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX }, +{"andi", "t,r,i", 0x30000000, 0xfc000000, WR_t|RD_s, I1 }, +/* b is at the top of the table. */ +/* bal is at the top of the table. */ +{"bc0f", "p", 0x41000000, 0xffff0000, CBD|RD_CC, I1 }, +{"bc0fl", "p", 0x41020000, 0xffff0000, CBL|RD_CC, I2|T3 }, +{"bc0t", "p", 0x41010000, 0xffff0000, CBD|RD_CC, I1 }, +{"bc0tl", "p", 0x41030000, 0xffff0000, CBL|RD_CC, I2|T3 }, +{"bc1any2f", "N,p", 0x45200000, 0xffe30000, CBD|RD_CC|FP_S, M3D }, +{"bc1any2t", "N,p", 0x45210000, 0xffe30000, CBD|RD_CC|FP_S, M3D }, +{"bc1any4f", "N,p", 0x45400000, 0xffe30000, CBD|RD_CC|FP_S, M3D }, +{"bc1any4t", "N,p", 0x45410000, 0xffe30000, CBD|RD_CC|FP_S, M3D }, +{"bc1f", "p", 0x45000000, 0xffff0000, CBD|RD_CC|FP_S, I1 }, +{"bc1f", "N,p", 0x45000000, 0xffe30000, CBD|RD_CC|FP_S, I4|I32 }, +{"bc1fl", "p", 0x45020000, 0xffff0000, CBL|RD_CC|FP_S, I2|T3 }, +{"bc1fl", "N,p", 0x45020000, 0xffe30000, CBL|RD_CC|FP_S, I4|I32 }, +{"bc1t", "p", 0x45010000, 0xffff0000, CBD|RD_CC|FP_S, I1 }, +{"bc1t", "N,p", 0x45010000, 0xffe30000, CBD|RD_CC|FP_S, I4|I32 }, +{"bc1tl", "p", 0x45030000, 0xffff0000, CBL|RD_CC|FP_S, I2|T3 }, +{"bc1tl", "N,p", 0x45030000, 0xffe30000, CBL|RD_CC|FP_S, I4|I32 }, +/* bc2* are at the bottom of the table. */ +{"bc3f", "p", 0x4d000000, 0xffff0000, CBD|RD_CC, I1 }, +{"bc3fl", "p", 0x4d020000, 0xffff0000, CBL|RD_CC, I2|T3 }, +{"bc3t", "p", 0x4d010000, 0xffff0000, CBD|RD_CC, I1 }, +{"bc3tl", "p", 0x4d030000, 0xffff0000, CBL|RD_CC, I2|T3 }, +{"beqz", "s,p", 0x10000000, 0xfc1f0000, CBD|RD_s, I1 }, +{"beqzl", "s,p", 0x50000000, 0xfc1f0000, CBL|RD_s, I2|T3 }, +{"beq", "s,t,p", 0x10000000, 0xfc000000, CBD|RD_s|RD_t, I1 }, +{"beq", "s,I,p", 0, (int) M_BEQ_I, INSN_MACRO, I1 }, +{"beql", "s,t,p", 0x50000000, 0xfc000000, CBL|RD_s|RD_t, I2|T3 }, +{"beql", "s,I,p", 0, (int) M_BEQL_I, INSN_MACRO, I2|T3 }, +{"bge", "s,t,p", 0, (int) M_BGE, INSN_MACRO, I1 }, +{"bge", "s,I,p", 0, (int) M_BGE_I, INSN_MACRO, I1 }, +{"bgel", "s,t,p", 0, (int) M_BGEL, INSN_MACRO, I2|T3 }, +{"bgel", "s,I,p", 0, (int) M_BGEL_I, INSN_MACRO, I2|T3 }, +{"bgeu", "s,t,p", 0, (int) M_BGEU, INSN_MACRO, I1 }, +{"bgeu", "s,I,p", 0, (int) M_BGEU_I, INSN_MACRO, I1 }, +{"bgeul", "s,t,p", 0, (int) M_BGEUL, INSN_MACRO, I2|T3 }, +{"bgeul", "s,I,p", 0, (int) M_BGEUL_I, INSN_MACRO, I2|T3 }, +{"bgez", "s,p", 0x04010000, 0xfc1f0000, CBD|RD_s, I1 }, +{"bgezl", "s,p", 0x04030000, 0xfc1f0000, CBL|RD_s, I2|T3 }, +{"bgezal", "s,p", 0x04110000, 0xfc1f0000, CBD|RD_s|WR_31, I1 }, +{"bgezall", "s,p", 0x04130000, 0xfc1f0000, CBL|RD_s|WR_31, I2|T3 }, +{"bgt", "s,t,p", 0, (int) M_BGT, INSN_MACRO, I1 }, +{"bgt", "s,I,p", 0, (int) M_BGT_I, INSN_MACRO, I1 }, +{"bgtl", "s,t,p", 0, (int) M_BGTL, INSN_MACRO, I2|T3 }, +{"bgtl", "s,I,p", 0, (int) M_BGTL_I, INSN_MACRO, I2|T3 }, +{"bgtu", "s,t,p", 0, (int) M_BGTU, INSN_MACRO, I1 }, +{"bgtu", "s,I,p", 0, (int) M_BGTU_I, INSN_MACRO, I1 }, +{"bgtul", "s,t,p", 0, (int) M_BGTUL, INSN_MACRO, I2|T3 }, +{"bgtul", "s,I,p", 0, (int) M_BGTUL_I, INSN_MACRO, I2|T3 }, +{"bgtz", "s,p", 0x1c000000, 0xfc1f0000, CBD|RD_s, I1 }, +{"bgtzl", "s,p", 0x5c000000, 0xfc1f0000, CBL|RD_s, I2|T3 }, +{"ble", "s,t,p", 0, (int) M_BLE, INSN_MACRO, I1 }, +{"ble", "s,I,p", 0, (int) M_BLE_I, INSN_MACRO, I1 }, +{"blel", "s,t,p", 0, (int) M_BLEL, INSN_MACRO, I2|T3 }, +{"blel", "s,I,p", 0, (int) M_BLEL_I, INSN_MACRO, I2|T3 }, +{"bleu", "s,t,p", 0, (int) M_BLEU, INSN_MACRO, I1 }, +{"bleu", "s,I,p", 0, (int) M_BLEU_I, INSN_MACRO, I1 }, +{"bleul", "s,t,p", 0, (int) M_BLEUL, INSN_MACRO, I2|T3 }, +{"bleul", "s,I,p", 0, (int) M_BLEUL_I, INSN_MACRO, I2|T3 }, +{"blez", "s,p", 0x18000000, 0xfc1f0000, CBD|RD_s, I1 }, +{"blezl", "s,p", 0x58000000, 0xfc1f0000, CBL|RD_s, I2|T3 }, +{"blt", "s,t,p", 0, (int) M_BLT, INSN_MACRO, I1 }, +{"blt", "s,I,p", 0, (int) M_BLT_I, INSN_MACRO, I1 }, +{"bltl", "s,t,p", 0, (int) M_BLTL, INSN_MACRO, I2|T3 }, +{"bltl", "s,I,p", 0, (int) M_BLTL_I, INSN_MACRO, I2|T3 }, +{"bltu", "s,t,p", 0, (int) M_BLTU, INSN_MACRO, I1 }, +{"bltu", "s,I,p", 0, (int) M_BLTU_I, INSN_MACRO, I1 }, +{"bltul", "s,t,p", 0, (int) M_BLTUL, INSN_MACRO, I2|T3 }, +{"bltul", "s,I,p", 0, (int) M_BLTUL_I, INSN_MACRO, I2|T3 }, +{"bltz", "s,p", 0x04000000, 0xfc1f0000, CBD|RD_s, I1 }, +{"bltzl", "s,p", 0x04020000, 0xfc1f0000, CBL|RD_s, I2|T3 }, +{"bltzal", "s,p", 0x04100000, 0xfc1f0000, CBD|RD_s|WR_31, I1 }, +{"bltzall", "s,p", 0x04120000, 0xfc1f0000, CBL|RD_s|WR_31, I2|T3 }, +{"bnez", "s,p", 0x14000000, 0xfc1f0000, CBD|RD_s, I1 }, +{"bnezl", "s,p", 0x54000000, 0xfc1f0000, CBL|RD_s, I2|T3 }, +{"bne", "s,t,p", 0x14000000, 0xfc000000, CBD|RD_s|RD_t, I1 }, +{"bne", "s,I,p", 0, (int) M_BNE_I, INSN_MACRO, I1 }, +{"bnel", "s,t,p", 0x54000000, 0xfc000000, CBL|RD_s|RD_t, I2|T3 }, +{"bnel", "s,I,p", 0, (int) M_BNEL_I, INSN_MACRO, I2|T3 }, +{"break", "", 0x0000000d, 0xffffffff, TRAP, I1 }, +{"break", "c", 0x0000000d, 0xfc00ffff, TRAP, I1 }, +{"break", "c,q", 0x0000000d, 0xfc00003f, TRAP, I1 }, +{"c.f.d", "S,T", 0x46200030, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.f.d", "M,S,T", 0x46200030, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 }, +{"c.f.s", "S,T", 0x46000030, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.f.s", "M,S,T", 0x46000030, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 }, +{"c.f.ps", "S,T", 0x46c00030, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.f.ps", "M,S,T", 0x46c00030, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.un.d", "S,T", 0x46200031, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.un.d", "M,S,T", 0x46200031, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 }, +{"c.un.s", "S,T", 0x46000031, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.un.s", "M,S,T", 0x46000031, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 }, +{"c.un.ps", "S,T", 0x46c00031, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.un.ps", "M,S,T", 0x46c00031, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.eq.d", "S,T", 0x46200032, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.eq.d", "M,S,T", 0x46200032, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 }, +{"c.eq.s", "S,T", 0x46000032, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.eq.s", "M,S,T", 0x46000032, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 }, +{"c.eq.ob", "Y,Q", 0x78000001, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, MX|SB1 }, +{"c.eq.ob", "S,T", 0x4ac00001, 0xffe007ff, WR_CC|RD_S|RD_T, N54 }, +{"c.eq.ob", "S,T[e]", 0x48000001, 0xfe2007ff, WR_CC|RD_S|RD_T, N54 }, +{"c.eq.ob", "S,k", 0x4bc00001, 0xffe007ff, WR_CC|RD_S|RD_T, N54 }, +{"c.eq.ps", "S,T", 0x46c00032, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.eq.ps", "M,S,T", 0x46c00032, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.eq.qh", "Y,Q", 0x78200001, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, MX }, +{"c.ueq.d", "S,T", 0x46200033, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.ueq.d", "M,S,T", 0x46200033, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 }, +{"c.ueq.s", "S,T", 0x46000033, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.ueq.s", "M,S,T", 0x46000033, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 }, +{"c.ueq.ps","S,T", 0x46c00033, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.ueq.ps","M,S,T", 0x46c00033, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.olt.d", "S,T", 0x46200034, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.olt.d", "M,S,T", 0x46200034, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 }, +{"c.olt.s", "S,T", 0x46000034, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.olt.s", "M,S,T", 0x46000034, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 }, +{"c.olt.ps","S,T", 0x46c00034, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.olt.ps","M,S,T", 0x46c00034, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.ult.d", "S,T", 0x46200035, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.ult.d", "M,S,T", 0x46200035, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 }, +{"c.ult.s", "S,T", 0x46000035, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.ult.s", "M,S,T", 0x46000035, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 }, +{"c.ult.ps","S,T", 0x46c00035, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.ult.ps","M,S,T", 0x46c00035, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.ole.d", "S,T", 0x46200036, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.ole.d", "M,S,T", 0x46200036, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 }, +{"c.ole.s", "S,T", 0x46000036, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.ole.s", "M,S,T", 0x46000036, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 }, +{"c.ole.ps","S,T", 0x46c00036, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.ole.ps","M,S,T", 0x46c00036, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.ule.d", "S,T", 0x46200037, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.ule.d", "M,S,T", 0x46200037, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 }, +{"c.ule.s", "S,T", 0x46000037, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.ule.s", "M,S,T", 0x46000037, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 }, +{"c.ule.ps","S,T", 0x46c00037, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.ule.ps","M,S,T", 0x46c00037, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.sf.d", "S,T", 0x46200038, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.sf.d", "M,S,T", 0x46200038, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 }, +{"c.sf.s", "S,T", 0x46000038, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.sf.s", "M,S,T", 0x46000038, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 }, +{"c.sf.ps", "S,T", 0x46c00038, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.sf.ps", "M,S,T", 0x46c00038, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.ngle.d","S,T", 0x46200039, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.ngle.d","M,S,T", 0x46200039, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 }, +{"c.ngle.s","S,T", 0x46000039, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.ngle.s","M,S,T", 0x46000039, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 }, +{"c.ngle.ps","S,T", 0x46c00039, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.ngle.ps","M,S,T", 0x46c00039, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.seq.d", "S,T", 0x4620003a, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.seq.d", "M,S,T", 0x4620003a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 }, +{"c.seq.s", "S,T", 0x4600003a, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.seq.s", "M,S,T", 0x4600003a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 }, +{"c.seq.ps","S,T", 0x46c0003a, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.seq.ps","M,S,T", 0x46c0003a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.ngl.d", "S,T", 0x4620003b, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.ngl.d", "M,S,T", 0x4620003b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 }, +{"c.ngl.s", "S,T", 0x4600003b, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.ngl.s", "M,S,T", 0x4600003b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 }, +{"c.ngl.ps","S,T", 0x46c0003b, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.ngl.ps","M,S,T", 0x46c0003b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.lt.d", "S,T", 0x4620003c, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.lt.d", "M,S,T", 0x4620003c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 }, +{"c.lt.s", "S,T", 0x4600003c, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.lt.s", "M,S,T", 0x4600003c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 }, +{"c.lt.ob", "Y,Q", 0x78000004, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, MX|SB1 }, +{"c.lt.ob", "S,T", 0x4ac00004, 0xffe007ff, WR_CC|RD_S|RD_T, N54 }, +{"c.lt.ob", "S,T[e]", 0x48000004, 0xfe2007ff, WR_CC|RD_S|RD_T, N54 }, +{"c.lt.ob", "S,k", 0x4bc00004, 0xffe007ff, WR_CC|RD_S|RD_T, N54 }, +{"c.lt.ps", "S,T", 0x46c0003c, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.lt.ps", "M,S,T", 0x46c0003c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.lt.qh", "Y,Q", 0x78200004, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, MX }, +{"c.nge.d", "S,T", 0x4620003d, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.nge.d", "M,S,T", 0x4620003d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 }, +{"c.nge.s", "S,T", 0x4600003d, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.nge.s", "M,S,T", 0x4600003d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 }, +{"c.nge.ps","S,T", 0x46c0003d, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.nge.ps","M,S,T", 0x46c0003d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.le.d", "S,T", 0x4620003e, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.le.d", "M,S,T", 0x4620003e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 }, +{"c.le.s", "S,T", 0x4600003e, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.le.s", "M,S,T", 0x4600003e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 }, +{"c.le.ob", "Y,Q", 0x78000005, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, MX|SB1 }, +{"c.le.ob", "S,T", 0x4ac00005, 0xffe007ff, WR_CC|RD_S|RD_T, N54 }, +{"c.le.ob", "S,T[e]", 0x48000005, 0xfe2007ff, WR_CC|RD_S|RD_T, N54 }, +{"c.le.ob", "S,k", 0x4bc00005, 0xffe007ff, WR_CC|RD_S|RD_T, N54 }, +{"c.le.ps", "S,T", 0x46c0003e, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.le.ps", "M,S,T", 0x46c0003e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.le.qh", "Y,Q", 0x78200005, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, MX }, +{"c.ngt.d", "S,T", 0x4620003f, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 }, +{"c.ngt.d", "M,S,T", 0x4620003f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 }, +{"c.ngt.s", "S,T", 0x4600003f, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 }, +{"c.ngt.s", "M,S,T", 0x4600003f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 }, +{"c.ngt.ps","S,T", 0x46c0003f, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"c.ngt.ps","M,S,T", 0x46c0003f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 }, +{"cabs.eq.d", "M,S,T", 0x46200072, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.eq.ps", "M,S,T", 0x46c00072, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.eq.s", "M,S,T", 0x46000072, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D }, +{"cabs.f.d", "M,S,T", 0x46200070, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.f.ps", "M,S,T", 0x46c00070, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.f.s", "M,S,T", 0x46000070, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D }, +{"cabs.le.d", "M,S,T", 0x4620007e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.le.ps", "M,S,T", 0x46c0007e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.le.s", "M,S,T", 0x4600007e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D }, +{"cabs.lt.d", "M,S,T", 0x4620007c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.lt.ps", "M,S,T", 0x46c0007c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.lt.s", "M,S,T", 0x4600007c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D }, +{"cabs.nge.d", "M,S,T", 0x4620007d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.nge.ps","M,S,T", 0x46c0007d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.nge.s", "M,S,T", 0x4600007d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D }, +{"cabs.ngl.d", "M,S,T", 0x4620007b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.ngl.ps","M,S,T", 0x46c0007b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.ngl.s", "M,S,T", 0x4600007b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D }, +{"cabs.ngle.d","M,S,T", 0x46200079, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.ngle.ps","M,S,T",0x46c00079, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.ngle.s","M,S,T", 0x46000079, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D }, +{"cabs.ngt.d", "M,S,T", 0x4620007f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.ngt.ps","M,S,T", 0x46c0007f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.ngt.s", "M,S,T", 0x4600007f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D }, +{"cabs.ole.d", "M,S,T", 0x46200076, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.ole.ps","M,S,T", 0x46c00076, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.ole.s", "M,S,T", 0x46000076, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D }, +{"cabs.olt.d", "M,S,T", 0x46200074, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.olt.ps","M,S,T", 0x46c00074, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.olt.s", "M,S,T", 0x46000074, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D }, +{"cabs.seq.d", "M,S,T", 0x4620007a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.seq.ps","M,S,T", 0x46c0007a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.seq.s", "M,S,T", 0x4600007a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D }, +{"cabs.sf.d", "M,S,T", 0x46200078, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.sf.ps", "M,S,T", 0x46c00078, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.sf.s", "M,S,T", 0x46000078, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D }, +{"cabs.ueq.d", "M,S,T", 0x46200073, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.ueq.ps","M,S,T", 0x46c00073, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.ueq.s", "M,S,T", 0x46000073, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D }, +{"cabs.ule.d", "M,S,T", 0x46200077, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.ule.ps","M,S,T", 0x46c00077, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.ule.s", "M,S,T", 0x46000077, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D }, +{"cabs.ult.d", "M,S,T", 0x46200075, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.ult.ps","M,S,T", 0x46c00075, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.ult.s", "M,S,T", 0x46000075, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D }, +{"cabs.un.d", "M,S,T", 0x46200071, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.un.ps", "M,S,T", 0x46c00071, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D }, +{"cabs.un.s", "M,S,T", 0x46000071, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D }, +{"cache", "k,o(b)", 0xbc000000, 0xfc000000, RD_b, I3|I32|T3}, +{"ceil.l.d", "D,S", 0x4620000a, 0xffff003f, WR_D|RD_S|FP_D, I3 }, +{"ceil.l.s", "D,S", 0x4600000a, 0xffff003f, WR_D|RD_S|FP_S, I3 }, +{"ceil.w.d", "D,S", 0x4620000e, 0xffff003f, WR_D|RD_S|FP_D, I2 }, +{"ceil.w.s", "D,S", 0x4600000e, 0xffff003f, WR_D|RD_S|FP_S, I2 }, +{"cfc0", "t,G", 0x40400000, 0xffe007ff, LCD|WR_t|RD_C0, I1 }, +{"cfc1", "t,G", 0x44400000, 0xffe007ff, LCD|WR_t|RD_C1|FP_S, I1 }, +{"cfc1", "t,S", 0x44400000, 0xffe007ff, LCD|WR_t|RD_C1|FP_S, I1 }, +/* cfc2 is at the bottom of the table. */ +{"cfc3", "t,G", 0x4c400000, 0xffe007ff, LCD|WR_t|RD_C3, I1 }, +{"clo", "U,s", 0x70000021, 0xfc0007ff, WR_d|WR_t|RD_s, I32|N55 }, +{"clz", "U,s", 0x70000020, 0xfc0007ff, WR_d|WR_t|RD_s, I32|N55 }, +{"ctc0", "t,G", 0x40c00000, 0xffe007ff, COD|RD_t|WR_CC, I1 }, +{"ctc1", "t,G", 0x44c00000, 0xffe007ff, COD|RD_t|WR_CC|FP_S, I1 }, +{"ctc1", "t,S", 0x44c00000, 0xffe007ff, COD|RD_t|WR_CC|FP_S, I1 }, +/* ctc2 is at the bottom of the table. */ +{"ctc3", "t,G", 0x4cc00000, 0xffe007ff, COD|RD_t|WR_CC, I1 }, +{"cvt.d.l", "D,S", 0x46a00021, 0xffff003f, WR_D|RD_S|FP_D, I3 }, +{"cvt.d.s", "D,S", 0x46000021, 0xffff003f, WR_D|RD_S|FP_D|FP_S, I1 }, +{"cvt.d.w", "D,S", 0x46800021, 0xffff003f, WR_D|RD_S|FP_D, I1 }, +{"cvt.l.d", "D,S", 0x46200025, 0xffff003f, WR_D|RD_S|FP_D, I3 }, +{"cvt.l.s", "D,S", 0x46000025, 0xffff003f, WR_D|RD_S|FP_S, I3 }, +{"cvt.s.l", "D,S", 0x46a00020, 0xffff003f, WR_D|RD_S|FP_S, I3 }, +{"cvt.s.d", "D,S", 0x46200020, 0xffff003f, WR_D|RD_S|FP_S|FP_D, I1 }, +{"cvt.s.w", "D,S", 0x46800020, 0xffff003f, WR_D|RD_S|FP_S, I1 }, +{"cvt.s.pl","D,S", 0x46c00028, 0xffff003f, WR_D|RD_S|FP_S|FP_D, I5 }, +{"cvt.s.pu","D,S", 0x46c00020, 0xffff003f, WR_D|RD_S|FP_S|FP_D, I5 }, +{"cvt.w.d", "D,S", 0x46200024, 0xffff003f, WR_D|RD_S|FP_D, I1 }, +{"cvt.w.s", "D,S", 0x46000024, 0xffff003f, WR_D|RD_S|FP_S, I1 }, +{"cvt.ps.pw", "D,S", 0x46800026, 0xffff003f, WR_D|RD_S|FP_S|FP_D, M3D }, +{"cvt.ps.s","D,V,T", 0x46000026, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 }, +{"cvt.pw.ps", "D,S", 0x46c00024, 0xffff003f, WR_D|RD_S|FP_S|FP_D, M3D }, +{"dabs", "d,v", 0, (int) M_DABS, INSN_MACRO, I3 }, +{"dadd", "d,v,t", 0x0000002c, 0xfc0007ff, WR_d|RD_s|RD_t, I3 }, +{"dadd", "t,r,I", 0, (int) M_DADD_I, INSN_MACRO, I3 }, +{"daddi", "t,r,j", 0x60000000, 0xfc000000, WR_t|RD_s, I3 }, +{"daddiu", "t,r,j", 0x64000000, 0xfc000000, WR_t|RD_s, I3 }, +{"daddu", "d,v,t", 0x0000002d, 0xfc0007ff, WR_d|RD_s|RD_t, I3 }, +{"daddu", "t,r,I", 0, (int) M_DADDU_I, INSN_MACRO, I3 }, +{"dbreak", "", 0x7000003f, 0xffffffff, 0, N5 }, +{"dclo", "U,s", 0x70000025, 0xfc0007ff, RD_s|WR_d|WR_t, I64|N55 }, +{"dclz", "U,s", 0x70000024, 0xfc0007ff, RD_s|WR_d|WR_t, I64|N55 }, +/* dctr and dctw are used on the r5000. */ +{"dctr", "o(b)", 0xbc050000, 0xfc1f0000, RD_b, I3 }, +{"dctw", "o(b)", 0xbc090000, 0xfc1f0000, RD_b, I3 }, +{"deret", "", 0x4200001f, 0xffffffff, 0, I32|G2 }, +{"dext", "t,r,I,+I", 0, (int) M_DEXT, INSN_MACRO, I65 }, +{"dext", "t,r,+A,+C", 0x7c000003, 0xfc00003f, WR_t|RD_s, I65 }, +{"dextm", "t,r,+A,+G", 0x7c000001, 0xfc00003f, WR_t|RD_s, I65 }, +{"dextu", "t,r,+E,+H", 0x7c000002, 0xfc00003f, WR_t|RD_s, I65 }, +/* For ddiv, see the comments about div. */ +{"ddiv", "z,s,t", 0x0000001e, 0xfc00ffff, RD_s|RD_t|WR_HILO, I3 }, +{"ddiv", "d,v,t", 0, (int) M_DDIV_3, INSN_MACRO, I3 }, +{"ddiv", "d,v,I", 0, (int) M_DDIV_3I, INSN_MACRO, I3 }, +/* For ddivu, see the comments about div. */ +{"ddivu", "z,s,t", 0x0000001f, 0xfc00ffff, RD_s|RD_t|WR_HILO, I3 }, +{"ddivu", "d,v,t", 0, (int) M_DDIVU_3, INSN_MACRO, I3 }, +{"ddivu", "d,v,I", 0, (int) M_DDIVU_3I, INSN_MACRO, I3 }, +{"di", "", 0x41606000, 0xffffffff, WR_t|WR_C0, I33 }, +{"di", "t", 0x41606000, 0xffe0ffff, WR_t|WR_C0, I33 }, +{"dins", "t,r,I,+I", 0, (int) M_DINS, INSN_MACRO, I65 }, +{"dins", "t,r,+A,+B", 0x7c000007, 0xfc00003f, WR_t|RD_s, I65 }, +{"dinsm", "t,r,+A,+F", 0x7c000005, 0xfc00003f, WR_t|RD_s, I65 }, +{"dinsu", "t,r,+E,+F", 0x7c000006, 0xfc00003f, WR_t|RD_s, I65 }, +/* The MIPS assembler treats the div opcode with two operands as + though the first operand appeared twice (the first operand is both + a source and a destination). To get the div machine instruction, + you must use an explicit destination of $0. */ +{"div", "z,s,t", 0x0000001a, 0xfc00ffff, RD_s|RD_t|WR_HILO, I1 }, +{"div", "z,t", 0x0000001a, 0xffe0ffff, RD_s|RD_t|WR_HILO, I1 }, +{"div", "d,v,t", 0, (int) M_DIV_3, INSN_MACRO, I1 }, +{"div", "d,v,I", 0, (int) M_DIV_3I, INSN_MACRO, I1 }, +{"div.d", "D,V,T", 0x46200003, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I1 }, +{"div.s", "D,V,T", 0x46000003, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, I1 }, +{"div.ps", "D,V,T", 0x46c00003, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, SB1 }, +/* For divu, see the comments about div. */ +{"divu", "z,s,t", 0x0000001b, 0xfc00ffff, RD_s|RD_t|WR_HILO, I1 }, +{"divu", "z,t", 0x0000001b, 0xffe0ffff, RD_s|RD_t|WR_HILO, I1 }, +{"divu", "d,v,t", 0, (int) M_DIVU_3, INSN_MACRO, I1 }, +{"divu", "d,v,I", 0, (int) M_DIVU_3I, INSN_MACRO, I1 }, +{"dla", "t,A(b)", 0, (int) M_DLA_AB, INSN_MACRO, I3 }, +{"dlca", "t,A(b)", 0, (int) M_DLCA_AB, INSN_MACRO, I3 }, +{"dli", "t,j", 0x24000000, 0xffe00000, WR_t, I3 }, /* addiu */ +{"dli", "t,i", 0x34000000, 0xffe00000, WR_t, I3 }, /* ori */ +{"dli", "t,I", 0, (int) M_DLI, INSN_MACRO, I3 }, +{"dmacc", "d,s,t", 0x00000029, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, N412 }, +{"dmacchi", "d,s,t", 0x00000229, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, N412 }, +{"dmacchis", "d,s,t", 0x00000629, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, N412 }, +{"dmacchiu", "d,s,t", 0x00000269, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, N412 }, +{"dmacchius", "d,s,t", 0x00000669, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, N412 }, +{"dmaccs", "d,s,t", 0x00000429, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, N412 }, +{"dmaccu", "d,s,t", 0x00000069, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, N412 }, +{"dmaccus", "d,s,t", 0x00000469, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, N412 }, +{"dmadd16", "s,t", 0x00000029, 0xfc00ffff, RD_s|RD_t|MOD_LO, N411 }, +{"dmfc0", "t,G", 0x40200000, 0xffe007ff, LCD|WR_t|RD_C0, I3 }, +{"dmfc0", "t,+D", 0x40200000, 0xffe007f8, LCD|WR_t|RD_C0, I64 }, +{"dmfc0", "t,G,H", 0x40200000, 0xffe007f8, LCD|WR_t|RD_C0, I64 }, +{"dmtc0", "t,G", 0x40a00000, 0xffe007ff, COD|RD_t|WR_C0|WR_CC, I3 }, +{"dmtc0", "t,+D", 0x40a00000, 0xffe007f8, COD|RD_t|WR_C0|WR_CC, I64 }, +{"dmtc0", "t,G,H", 0x40a00000, 0xffe007f8, COD|RD_t|WR_C0|WR_CC, I64 }, +{"dmfc1", "t,S", 0x44200000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, I3 }, +{"dmfc1", "t,G", 0x44200000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, I3 }, +{"dmtc1", "t,S", 0x44a00000, 0xffe007ff, COD|RD_t|WR_S|FP_S, I3 }, +{"dmtc1", "t,G", 0x44a00000, 0xffe007ff, COD|RD_t|WR_S|FP_S, I3 }, +/* dmfc2 is at the bottom of the table. */ +/* dmtc2 is at the bottom of the table. */ +{"dmfc3", "t,G", 0x4c200000, 0xffe007ff, LCD|WR_t|RD_C3, I3 }, +{"dmfc3", "t,G,H", 0x4c200000, 0xffe007f8, LCD|WR_t|RD_C3, I64 }, +{"dmtc3", "t,G", 0x4ca00000, 0xffe007ff, COD|RD_t|WR_C3|WR_CC, I3 }, +{"dmtc3", "t,G,H", 0x4ca00000, 0xffe007f8, COD|RD_t|WR_C3|WR_CC, I64 }, +{"dmul", "d,v,t", 0, (int) M_DMUL, INSN_MACRO, I3 }, +{"dmul", "d,v,I", 0, (int) M_DMUL_I, INSN_MACRO, I3 }, +{"dmulo", "d,v,t", 0, (int) M_DMULO, INSN_MACRO, I3 }, +{"dmulo", "d,v,I", 0, (int) M_DMULO_I, INSN_MACRO, I3 }, +{"dmulou", "d,v,t", 0, (int) M_DMULOU, INSN_MACRO, I3 }, +{"dmulou", "d,v,I", 0, (int) M_DMULOU_I, INSN_MACRO, I3 }, +{"dmult", "s,t", 0x0000001c, 0xfc00ffff, RD_s|RD_t|WR_HILO, I3 }, +{"dmultu", "s,t", 0x0000001d, 0xfc00ffff, RD_s|RD_t|WR_HILO, I3 }, +{"dneg", "d,w", 0x0000002e, 0xffe007ff, WR_d|RD_t, I3 }, /* dsub 0 */ +{"dnegu", "d,w", 0x0000002f, 0xffe007ff, WR_d|RD_t, I3 }, /* dsubu 0*/ +{"drem", "z,s,t", 0x0000001e, 0xfc00ffff, RD_s|RD_t|WR_HILO, I3 }, +{"drem", "d,v,t", 3, (int) M_DREM_3, INSN_MACRO, I3 }, +{"drem", "d,v,I", 3, (int) M_DREM_3I, INSN_MACRO, I3 }, +{"dremu", "z,s,t", 0x0000001f, 0xfc00ffff, RD_s|RD_t|WR_HILO, I3 }, +{"dremu", "d,v,t", 3, (int) M_DREMU_3, INSN_MACRO, I3 }, +{"dremu", "d,v,I", 3, (int) M_DREMU_3I, INSN_MACRO, I3 }, +{"dret", "", 0x7000003e, 0xffffffff, 0, N5 }, +{"drol", "d,v,t", 0, (int) M_DROL, INSN_MACRO, I3 }, +{"drol", "d,v,I", 0, (int) M_DROL_I, INSN_MACRO, I3 }, +{"dror", "d,v,t", 0, (int) M_DROR, INSN_MACRO, I3 }, +{"dror", "d,v,I", 0, (int) M_DROR_I, INSN_MACRO, I3 }, +{"dror", "d,w,<", 0x0020003a, 0xffe0003f, WR_d|RD_t, N5|I65 }, +{"drorv", "d,t,s", 0x00000056, 0xfc0007ff, RD_t|RD_s|WR_d, N5|I65 }, +{"dror32", "d,w,<", 0x0020003e, 0xffe0003f, WR_d|RD_t, N5|I65 }, +{"drotl", "d,v,t", 0, (int) M_DROL, INSN_MACRO, I65 }, +{"drotl", "d,v,I", 0, (int) M_DROL_I, INSN_MACRO, I65 }, +{"drotr", "d,v,t", 0, (int) M_DROR, INSN_MACRO, I65 }, +{"drotr", "d,v,I", 0, (int) M_DROR_I, INSN_MACRO, I65 }, +{"drotrv", "d,t,s", 0x00000056, 0xfc0007ff, RD_t|RD_s|WR_d, I65 }, +{"drotr32", "d,w,<", 0x0020003e, 0xffe0003f, WR_d|RD_t, I65 }, +{"dsbh", "d,w", 0x7c0000a4, 0xffe007ff, WR_d|RD_t, I65 }, +{"dshd", "d,w", 0x7c000164, 0xffe007ff, WR_d|RD_t, I65 }, +{"dsllv", "d,t,s", 0x00000014, 0xfc0007ff, WR_d|RD_t|RD_s, I3 }, +{"dsll32", "d,w,<", 0x0000003c, 0xffe0003f, WR_d|RD_t, I3 }, +{"dsll", "d,w,s", 0x00000014, 0xfc0007ff, WR_d|RD_t|RD_s, I3 }, /* dsllv */ +{"dsll", "d,w,>", 0x0000003c, 0xffe0003f, WR_d|RD_t, I3 }, /* dsll32 */ +{"dsll", "d,w,<", 0x00000038, 0xffe0003f, WR_d|RD_t, I3 }, +{"dsrav", "d,t,s", 0x00000017, 0xfc0007ff, WR_d|RD_t|RD_s, I3 }, +{"dsra32", "d,w,<", 0x0000003f, 0xffe0003f, WR_d|RD_t, I3 }, +{"dsra", "d,w,s", 0x00000017, 0xfc0007ff, WR_d|RD_t|RD_s, I3 }, /* dsrav */ +{"dsra", "d,w,>", 0x0000003f, 0xffe0003f, WR_d|RD_t, I3 }, /* dsra32 */ +{"dsra", "d,w,<", 0x0000003b, 0xffe0003f, WR_d|RD_t, I3 }, +{"dsrlv", "d,t,s", 0x00000016, 0xfc0007ff, WR_d|RD_t|RD_s, I3 }, +{"dsrl32", "d,w,<", 0x0000003e, 0xffe0003f, WR_d|RD_t, I3 }, +{"dsrl", "d,w,s", 0x00000016, 0xfc0007ff, WR_d|RD_t|RD_s, I3 }, /* dsrlv */ +{"dsrl", "d,w,>", 0x0000003e, 0xffe0003f, WR_d|RD_t, I3 }, /* dsrl32 */ +{"dsrl", "d,w,<", 0x0000003a, 0xffe0003f, WR_d|RD_t, I3 }, +{"dsub", "d,v,t", 0x0000002e, 0xfc0007ff, WR_d|RD_s|RD_t, I3 }, +{"dsub", "d,v,I", 0, (int) M_DSUB_I, INSN_MACRO, I3 }, +{"dsubu", "d,v,t", 0x0000002f, 0xfc0007ff, WR_d|RD_s|RD_t, I3 }, +{"dsubu", "d,v,I", 0, (int) M_DSUBU_I, INSN_MACRO, I3 }, +{"ei", "", 0x41606020, 0xffffffff, WR_t|WR_C0, I33 }, +{"ei", "t", 0x41606020, 0xffe0ffff, WR_t|WR_C0, I33 }, +{"eret", "", 0x42000018, 0xffffffff, 0, I3|I32 }, +{"ext", "t,r,+A,+C", 0x7c000000, 0xfc00003f, WR_t|RD_s, I33 }, +{"floor.l.d", "D,S", 0x4620000b, 0xffff003f, WR_D|RD_S|FP_D, I3 }, +{"floor.l.s", "D,S", 0x4600000b, 0xffff003f, WR_D|RD_S|FP_S, I3 }, +{"floor.w.d", "D,S", 0x4620000f, 0xffff003f, WR_D|RD_S|FP_D, I2 }, +{"floor.w.s", "D,S", 0x4600000f, 0xffff003f, WR_D|RD_S|FP_S, I2 }, +{"flushi", "", 0xbc010000, 0xffffffff, 0, L1 }, +{"flushd", "", 0xbc020000, 0xffffffff, 0, L1 }, +{"flushid", "", 0xbc030000, 0xffffffff, 0, L1 }, +{"hibernate","", 0x42000023, 0xffffffff, 0, V1 }, +{"ins", "t,r,+A,+B", 0x7c000004, 0xfc00003f, WR_t|RD_s, I33 }, +{"jr", "s", 0x00000008, 0xfc1fffff, UBD|RD_s, I1 }, +{"jr.hb", "s", 0x00000408, 0xfc1fffff, UBD|RD_s, I33 }, +{"j", "s", 0x00000008, 0xfc1fffff, UBD|RD_s, I1 }, /* jr */ +/* SVR4 PIC code requires special handling for j, so it must be a + macro. */ +{"j", "a", 0, (int) M_J_A, INSN_MACRO, I1 }, +/* This form of j is used by the disassembler and internally by the + assembler, but will never match user input (because the line above + will match first). */ +{"j", "a", 0x08000000, 0xfc000000, UBD, I1 }, +{"jalr", "s", 0x0000f809, 0xfc1fffff, UBD|RD_s|WR_d, I1 }, +{"jalr", "d,s", 0x00000009, 0xfc1f07ff, UBD|RD_s|WR_d, I1 }, +{"jalr.hb", "s", 0x0000fc09, 0xfc1fffff, UBD|RD_s|WR_d, I33 }, +{"jalr.hb", "d,s", 0x00000409, 0xfc1f07ff, UBD|RD_s|WR_d, I33 }, +/* SVR4 PIC code requires special handling for jal, so it must be a + macro. */ +{"jal", "d,s", 0, (int) M_JAL_2, INSN_MACRO, I1 }, +{"jal", "s", 0, (int) M_JAL_1, INSN_MACRO, I1 }, +{"jal", "a", 0, (int) M_JAL_A, INSN_MACRO, I1 }, +/* This form of jal is used by the disassembler and internally by the + assembler, but will never match user input (because the line above + will match first). */ +{"jal", "a", 0x0c000000, 0xfc000000, UBD|WR_31, I1 }, +{"jalx", "a", 0x74000000, 0xfc000000, UBD|WR_31, I16 }, +{"la", "t,A(b)", 0, (int) M_LA_AB, INSN_MACRO, I1 }, +{"lb", "t,o(b)", 0x80000000, 0xfc000000, LDD|RD_b|WR_t, I1 }, +{"lb", "t,A(b)", 0, (int) M_LB_AB, INSN_MACRO, I1 }, +{"lbu", "t,o(b)", 0x90000000, 0xfc000000, LDD|RD_b|WR_t, I1 }, +{"lbu", "t,A(b)", 0, (int) M_LBU_AB, INSN_MACRO, I1 }, +{"lca", "t,A(b)", 0, (int) M_LCA_AB, INSN_MACRO, I1 }, +{"ld", "t,o(b)", 0xdc000000, 0xfc000000, WR_t|RD_b, I3 }, +{"ld", "t,o(b)", 0, (int) M_LD_OB, INSN_MACRO, I1 }, +{"ld", "t,A(b)", 0, (int) M_LD_AB, INSN_MACRO, I1 }, +{"ldc1", "T,o(b)", 0xd4000000, 0xfc000000, CLD|RD_b|WR_T|FP_D, I2 }, +{"ldc1", "E,o(b)", 0xd4000000, 0xfc000000, CLD|RD_b|WR_T|FP_D, I2 }, +{"ldc1", "T,A(b)", 0, (int) M_LDC1_AB, INSN_MACRO, I2 }, +{"ldc1", "E,A(b)", 0, (int) M_LDC1_AB, INSN_MACRO, I2 }, +{"l.d", "T,o(b)", 0xd4000000, 0xfc000000, CLD|RD_b|WR_T|FP_D, I2 }, /* ldc1 */ +{"l.d", "T,o(b)", 0, (int) M_L_DOB, INSN_MACRO, I1 }, +{"l.d", "T,A(b)", 0, (int) M_L_DAB, INSN_MACRO, I1 }, +{"ldc2", "E,o(b)", 0xd8000000, 0xfc000000, CLD|RD_b|WR_CC, I2 }, +{"ldc2", "E,A(b)", 0, (int) M_LDC2_AB, INSN_MACRO, I2 }, +{"ldc3", "E,o(b)", 0xdc000000, 0xfc000000, CLD|RD_b|WR_CC, I2 }, +{"ldc3", "E,A(b)", 0, (int) M_LDC3_AB, INSN_MACRO, I2 }, +{"ldl", "t,o(b)", 0x68000000, 0xfc000000, LDD|WR_t|RD_b, I3 }, +{"ldl", "t,A(b)", 0, (int) M_LDL_AB, INSN_MACRO, I3 }, +{"ldr", "t,o(b)", 0x6c000000, 0xfc000000, LDD|WR_t|RD_b, I3 }, +{"ldr", "t,A(b)", 0, (int) M_LDR_AB, INSN_MACRO, I3 }, +{"ldxc1", "D,t(b)", 0x4c000001, 0xfc00f83f, LDD|WR_D|RD_t|RD_b, I4 }, +{"lh", "t,o(b)", 0x84000000, 0xfc000000, LDD|RD_b|WR_t, I1 }, +{"lh", "t,A(b)", 0, (int) M_LH_AB, INSN_MACRO, I1 }, +{"lhu", "t,o(b)", 0x94000000, 0xfc000000, LDD|RD_b|WR_t, I1 }, +{"lhu", "t,A(b)", 0, (int) M_LHU_AB, INSN_MACRO, I1 }, +/* li is at the start of the table. */ +{"li.d", "t,F", 0, (int) M_LI_D, INSN_MACRO, I1 }, +{"li.d", "T,L", 0, (int) M_LI_DD, INSN_MACRO, I1 }, +{"li.s", "t,f", 0, (int) M_LI_S, INSN_MACRO, I1 }, +{"li.s", "T,l", 0, (int) M_LI_SS, INSN_MACRO, I1 }, +{"ll", "t,o(b)", 0xc0000000, 0xfc000000, LDD|RD_b|WR_t, I2 }, +{"ll", "t,A(b)", 0, (int) M_LL_AB, INSN_MACRO, I2 }, +{"lld", "t,o(b)", 0xd0000000, 0xfc000000, LDD|RD_b|WR_t, I3 }, +{"lld", "t,A(b)", 0, (int) M_LLD_AB, INSN_MACRO, I3 }, +{"lui", "t,u", 0x3c000000, 0xffe00000, WR_t, I1 }, +{"luxc1", "D,t(b)", 0x4c000005, 0xfc00f83f, LDD|WR_D|RD_t|RD_b, I5|N55 }, +{"lw", "t,o(b)", 0x8c000000, 0xfc000000, LDD|RD_b|WR_t, I1 }, +{"lw", "t,A(b)", 0, (int) M_LW_AB, INSN_MACRO, I1 }, +{"lwc0", "E,o(b)", 0xc0000000, 0xfc000000, CLD|RD_b|WR_CC, I1 }, +{"lwc0", "E,A(b)", 0, (int) M_LWC0_AB, INSN_MACRO, I1 }, +{"lwc1", "T,o(b)", 0xc4000000, 0xfc000000, CLD|RD_b|WR_T|FP_S, I1 }, +{"lwc1", "E,o(b)", 0xc4000000, 0xfc000000, CLD|RD_b|WR_T|FP_S, I1 }, +{"lwc1", "T,A(b)", 0, (int) M_LWC1_AB, INSN_MACRO, I1 }, +{"lwc1", "E,A(b)", 0, (int) M_LWC1_AB, INSN_MACRO, I1 }, +{"l.s", "T,o(b)", 0xc4000000, 0xfc000000, CLD|RD_b|WR_T|FP_S, I1 }, /* lwc1 */ +{"l.s", "T,A(b)", 0, (int) M_LWC1_AB, INSN_MACRO, I1 }, +{"lwc2", "E,o(b)", 0xc8000000, 0xfc000000, CLD|RD_b|WR_CC, I1 }, +{"lwc2", "E,A(b)", 0, (int) M_LWC2_AB, INSN_MACRO, I1 }, +{"lwc3", "E,o(b)", 0xcc000000, 0xfc000000, CLD|RD_b|WR_CC, I1 }, +{"lwc3", "E,A(b)", 0, (int) M_LWC3_AB, INSN_MACRO, I1 }, +{"lwl", "t,o(b)", 0x88000000, 0xfc000000, LDD|RD_b|WR_t, I1 }, +{"lwl", "t,A(b)", 0, (int) M_LWL_AB, INSN_MACRO, I1 }, +{"lcache", "t,o(b)", 0x88000000, 0xfc000000, LDD|RD_b|WR_t, I2 }, /* same */ +{"lcache", "t,A(b)", 0, (int) M_LWL_AB, INSN_MACRO, I2 }, /* as lwl */ +{"lwr", "t,o(b)", 0x98000000, 0xfc000000, LDD|RD_b|WR_t, I1 }, +{"lwr", "t,A(b)", 0, (int) M_LWR_AB, INSN_MACRO, I1 }, +{"flush", "t,o(b)", 0x98000000, 0xfc000000, LDD|RD_b|WR_t, I2 }, /* same */ +{"flush", "t,A(b)", 0, (int) M_LWR_AB, INSN_MACRO, I2 }, /* as lwr */ +{"lwu", "t,o(b)", 0x9c000000, 0xfc000000, LDD|RD_b|WR_t, I3 }, +{"lwu", "t,A(b)", 0, (int) M_LWU_AB, INSN_MACRO, I3 }, +{"lwxc1", "D,t(b)", 0x4c000000, 0xfc00f83f, LDD|WR_D|RD_t|RD_b, I4 }, +{"macc", "d,s,t", 0x00000028, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N412 }, +{"macc", "d,s,t", 0x00000158, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 }, +{"maccs", "d,s,t", 0x00000428, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N412 }, +{"macchi", "d,s,t", 0x00000228, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N412 }, +{"macchi", "d,s,t", 0x00000358, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 }, +{"macchis", "d,s,t", 0x00000628, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N412 }, +{"macchiu", "d,s,t", 0x00000268, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N412 }, +{"macchiu", "d,s,t", 0x00000359, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 }, +{"macchius","d,s,t", 0x00000668, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N412 }, +{"maccu", "d,s,t", 0x00000068, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N412 }, +{"maccu", "d,s,t", 0x00000159, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 }, +{"maccus", "d,s,t", 0x00000468, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N412 }, +{"mad", "s,t", 0x70000000, 0xfc00ffff, RD_s|RD_t|MOD_HILO, P3 }, +{"madu", "s,t", 0x70000001, 0xfc00ffff, RD_s|RD_t|MOD_HILO, P3 }, +{"madd.d", "D,R,S,T", 0x4c000021, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I4 }, +{"madd.s", "D,R,S,T", 0x4c000020, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_S, I4 }, +{"madd.ps", "D,R,S,T", 0x4c000026, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I5 }, +{"madd", "s,t", 0x0000001c, 0xfc00ffff, RD_s|RD_t|WR_HILO, L1 }, +{"madd", "s,t", 0x70000000, 0xfc00ffff, RD_s|RD_t|MOD_HILO, I32|N55}, +{"madd", "s,t", 0x70000000, 0xfc00ffff, RD_s|RD_t|WR_HILO|IS_M, G1 }, +{"madd", "d,s,t", 0x70000000, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d|IS_M, G1 }, +{"maddu", "s,t", 0x0000001d, 0xfc00ffff, RD_s|RD_t|WR_HILO, L1 }, +{"maddu", "s,t", 0x70000001, 0xfc00ffff, RD_s|RD_t|MOD_HILO, I32|N55}, +{"maddu", "s,t", 0x70000001, 0xfc00ffff, RD_s|RD_t|WR_HILO|IS_M, G1 }, +{"maddu", "d,s,t", 0x70000001, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d|IS_M, G1 }, +{"madd16", "s,t", 0x00000028, 0xfc00ffff, RD_s|RD_t|MOD_HILO, N411 }, +{"max.ob", "X,Y,Q", 0x78000007, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 }, +{"max.ob", "D,S,T", 0x4ac00007, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"max.ob", "D,S,T[e]", 0x48000007, 0xfe20003f, WR_D|RD_S|RD_T, N54 }, +{"max.ob", "D,S,k", 0x4bc00007, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"max.qh", "X,Y,Q", 0x78200007, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX }, +{"mfpc", "t,P", 0x4000c801, 0xffe0ffc1, LCD|WR_t|RD_C0, M1|N5 }, +{"mfps", "t,P", 0x4000c800, 0xffe0ffc1, LCD|WR_t|RD_C0, M1|N5 }, +{"mfc0", "t,G", 0x40000000, 0xffe007ff, LCD|WR_t|RD_C0, I1 }, +{"mfc0", "t,+D", 0x40000000, 0xffe007f8, LCD|WR_t|RD_C0, I32 }, +{"mfc0", "t,G,H", 0x40000000, 0xffe007f8, LCD|WR_t|RD_C0, I32 }, +{"mfc1", "t,S", 0x44000000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, I1 }, +{"mfc1", "t,G", 0x44000000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, I1 }, +{"mfhc1", "t,S", 0x44600000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, I33 }, +{"mfhc1", "t,G", 0x44600000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, I33 }, +/* mfc2 is at the bottom of the table. */ +/* mfhc2 is at the bottom of the table. */ +{"mfc3", "t,G", 0x4c000000, 0xffe007ff, LCD|WR_t|RD_C3, I1 }, +{"mfc3", "t,G,H", 0x4c000000, 0xffe007f8, LCD|WR_t|RD_C3, I32 }, +{"mfdr", "t,G", 0x7000003d, 0xffe007ff, LCD|WR_t|RD_C0, N5 }, +{"mfhi", "d", 0x00000010, 0xffff07ff, WR_d|RD_HI, I1 }, +{"mflo", "d", 0x00000012, 0xffff07ff, WR_d|RD_LO, I1 }, +{"min.ob", "X,Y,Q", 0x78000006, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 }, +{"min.ob", "D,S,T", 0x4ac00006, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"min.ob", "D,S,T[e]", 0x48000006, 0xfe20003f, WR_D|RD_S|RD_T, N54 }, +{"min.ob", "D,S,k", 0x4bc00006, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"min.qh", "X,Y,Q", 0x78200006, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX }, +{"mov.d", "D,S", 0x46200006, 0xffff003f, WR_D|RD_S|FP_D, I1 }, +{"mov.s", "D,S", 0x46000006, 0xffff003f, WR_D|RD_S|FP_S, I1 }, +{"mov.ps", "D,S", 0x46c00006, 0xffff003f, WR_D|RD_S|FP_D, I5 }, +{"movf", "d,s,N", 0x00000001, 0xfc0307ff, WR_d|RD_s|RD_CC|FP_D|FP_S, I4|I32}, +{"movf.d", "D,S,N", 0x46200011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, I4|I32 }, +{"movf.l", "D,S,N", 0x46a00011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, MX|SB1 }, +{"movf.l", "X,Y,N", 0x46a00011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, MX|SB1 }, +{"movf.s", "D,S,N", 0x46000011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_S, I4|I32 }, +{"movf.ps", "D,S,N", 0x46c00011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, I5 }, +{"movn", "d,v,t", 0x0000000b, 0xfc0007ff, WR_d|RD_s|RD_t, I4|I32 }, +{"ffc", "d,v", 0x0000000b, 0xfc1f07ff, WR_d|RD_s, L1 }, +{"movn.d", "D,S,t", 0x46200013, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, I4|I32 }, +{"movn.l", "D,S,t", 0x46a00013, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, MX|SB1 }, +{"movn.l", "X,Y,t", 0x46a00013, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, MX|SB1 }, +{"movn.s", "D,S,t", 0x46000013, 0xffe0003f, WR_D|RD_S|RD_t|FP_S, I4|I32 }, +{"movn.ps", "D,S,t", 0x46c00013, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, I5 }, +{"movt", "d,s,N", 0x00010001, 0xfc0307ff, WR_d|RD_s|RD_CC, I4|I32 }, +{"movt.d", "D,S,N", 0x46210011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, I4|I32 }, +{"movt.l", "D,S,N", 0x46a10011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, MX|SB1 }, +{"movt.l", "X,Y,N", 0x46a10011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, MX|SB1 }, +{"movt.s", "D,S,N", 0x46010011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_S, I4|I32 }, +{"movt.ps", "D,S,N", 0x46c10011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, I5 }, +{"movz", "d,v,t", 0x0000000a, 0xfc0007ff, WR_d|RD_s|RD_t, I4|I32 }, +{"ffs", "d,v", 0x0000000a, 0xfc1f07ff, WR_d|RD_s, L1 }, +{"movz.d", "D,S,t", 0x46200012, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, I4|I32 }, +{"movz.l", "D,S,t", 0x46a00012, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, MX|SB1 }, +{"movz.l", "X,Y,t", 0x46a00012, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, MX|SB1 }, +{"movz.s", "D,S,t", 0x46000012, 0xffe0003f, WR_D|RD_S|RD_t|FP_S, I4|I32 }, +{"movz.ps", "D,S,t", 0x46c00012, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, I5 }, +{"msac", "d,s,t", 0x000001d8, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 }, +{"msacu", "d,s,t", 0x000001d9, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 }, +{"msachi", "d,s,t", 0x000003d8, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 }, +{"msachiu", "d,s,t", 0x000003d9, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 }, +/* move is at the top of the table. */ +{"msgn.qh", "X,Y,Q", 0x78200000, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX }, +{"msub.d", "D,R,S,T", 0x4c000029, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I4 }, +{"msub.s", "D,R,S,T", 0x4c000028, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_S, I4 }, +{"msub.ps", "D,R,S,T", 0x4c00002e, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I5 }, +{"msub", "s,t", 0x0000001e, 0xfc00ffff, RD_s|RD_t|WR_HILO, L1 }, +{"msub", "s,t", 0x70000004, 0xfc00ffff, RD_s|RD_t|MOD_HILO, I32|N55 }, +{"msubu", "s,t", 0x0000001f, 0xfc00ffff, RD_s|RD_t|WR_HILO, L1 }, +{"msubu", "s,t", 0x70000005, 0xfc00ffff, RD_s|RD_t|MOD_HILO, I32|N55 }, +{"mtpc", "t,P", 0x4080c801, 0xffe0ffc1, COD|RD_t|WR_C0, M1|N5 }, +{"mtps", "t,P", 0x4080c800, 0xffe0ffc1, COD|RD_t|WR_C0, M1|N5 }, +{"mtc0", "t,G", 0x40800000, 0xffe007ff, COD|RD_t|WR_C0|WR_CC, I1 }, +{"mtc0", "t,+D", 0x40800000, 0xffe007f8, COD|RD_t|WR_C0|WR_CC, I32 }, +{"mtc0", "t,G,H", 0x40800000, 0xffe007f8, COD|RD_t|WR_C0|WR_CC, I32 }, +{"mtc1", "t,S", 0x44800000, 0xffe007ff, COD|RD_t|WR_S|FP_S, I1 }, +{"mtc1", "t,G", 0x44800000, 0xffe007ff, COD|RD_t|WR_S|FP_S, I1 }, +{"mthc1", "t,S", 0x44e00000, 0xffe007ff, COD|RD_t|WR_S|FP_S, I33 }, +{"mthc1", "t,G", 0x44e00000, 0xffe007ff, COD|RD_t|WR_S|FP_S, I33 }, +/* mtc2 is at the bottom of the table. */ +/* mthc2 is at the bottom of the table. */ +{"mtc3", "t,G", 0x4c800000, 0xffe007ff, COD|RD_t|WR_C3|WR_CC, I1 }, +{"mtc3", "t,G,H", 0x4c800000, 0xffe007f8, COD|RD_t|WR_C3|WR_CC, I32 }, +{"mtdr", "t,G", 0x7080003d, 0xffe007ff, COD|RD_t|WR_C0, N5 }, +{"mthi", "s", 0x00000011, 0xfc1fffff, RD_s|WR_HI, I1 }, +{"mtlo", "s", 0x00000013, 0xfc1fffff, RD_s|WR_LO, I1 }, +{"mul.d", "D,V,T", 0x46200002, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I1 }, +{"mul.s", "D,V,T", 0x46000002, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, I1 }, +{"mul.ob", "X,Y,Q", 0x78000030, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 }, +{"mul.ob", "D,S,T", 0x4ac00030, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"mul.ob", "D,S,T[e]", 0x48000030, 0xfe20003f, WR_D|RD_S|RD_T, N54 }, +{"mul.ob", "D,S,k", 0x4bc00030, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"mul.ps", "D,V,T", 0x46c00002, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 }, +{"mul.qh", "X,Y,Q", 0x78200030, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX }, +{"mul", "d,v,t", 0x70000002, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, I32|P3|N55}, +{"mul", "d,s,t", 0x00000058, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N54 }, +{"mul", "d,v,t", 0, (int) M_MUL, INSN_MACRO, I1 }, +{"mul", "d,v,I", 0, (int) M_MUL_I, INSN_MACRO, I1 }, +{"mula.ob", "Y,Q", 0x78000033, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 }, +{"mula.ob", "S,T", 0x4ac00033, 0xffe007ff, WR_CC|RD_S|RD_T, N54 }, +{"mula.ob", "S,T[e]", 0x48000033, 0xfe2007ff, WR_CC|RD_S|RD_T, N54 }, +{"mula.ob", "S,k", 0x4bc00033, 0xffe007ff, WR_CC|RD_S|RD_T, N54 }, +{"mula.qh", "Y,Q", 0x78200033, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX }, +{"mulhi", "d,s,t", 0x00000258, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 }, +{"mulhiu", "d,s,t", 0x00000259, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 }, +{"mull.ob", "Y,Q", 0x78000433, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 }, +{"mull.ob", "S,T", 0x4ac00433, 0xffe007ff, WR_CC|RD_S|RD_T, N54 }, +{"mull.ob", "S,T[e]", 0x48000433, 0xfe2007ff, WR_CC|RD_S|RD_T, N54 }, +{"mull.ob", "S,k", 0x4bc00433, 0xffe007ff, WR_CC|RD_S|RD_T, N54 }, +{"mull.qh", "Y,Q", 0x78200433, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX }, +{"mulo", "d,v,t", 0, (int) M_MULO, INSN_MACRO, I1 }, +{"mulo", "d,v,I", 0, (int) M_MULO_I, INSN_MACRO, I1 }, +{"mulou", "d,v,t", 0, (int) M_MULOU, INSN_MACRO, I1 }, +{"mulou", "d,v,I", 0, (int) M_MULOU_I, INSN_MACRO, I1 }, +{"mulr.ps", "D,S,T", 0x46c0001a, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, M3D }, +{"muls", "d,s,t", 0x000000d8, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 }, +{"mulsu", "d,s,t", 0x000000d9, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 }, +{"mulshi", "d,s,t", 0x000002d8, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 }, +{"mulshiu", "d,s,t", 0x000002d9, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 }, +{"muls.ob", "Y,Q", 0x78000032, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 }, +{"muls.ob", "S,T", 0x4ac00032, 0xffe007ff, WR_CC|RD_S|RD_T, N54 }, +{"muls.ob", "S,T[e]", 0x48000032, 0xfe2007ff, WR_CC|RD_S|RD_T, N54 }, +{"muls.ob", "S,k", 0x4bc00032, 0xffe007ff, WR_CC|RD_S|RD_T, N54 }, +{"muls.qh", "Y,Q", 0x78200032, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX }, +{"mulsl.ob", "Y,Q", 0x78000432, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 }, +{"mulsl.ob", "S,T", 0x4ac00432, 0xffe007ff, WR_CC|RD_S|RD_T, N54 }, +{"mulsl.ob", "S,T[e]", 0x48000432, 0xfe2007ff, WR_CC|RD_S|RD_T, N54 }, +{"mulsl.ob", "S,k", 0x4bc00432, 0xffe007ff, WR_CC|RD_S|RD_T, N54 }, +{"mulsl.qh", "Y,Q", 0x78200432, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX }, +{"mult", "s,t", 0x00000018, 0xfc00ffff, RD_s|RD_t|WR_HILO|IS_M, I1 }, +{"mult", "d,s,t", 0x00000018, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d|IS_M, G1 }, +{"multu", "s,t", 0x00000019, 0xfc00ffff, RD_s|RD_t|WR_HILO|IS_M, I1 }, +{"multu", "d,s,t", 0x00000019, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d|IS_M, G1 }, +{"mulu", "d,s,t", 0x00000059, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 }, +{"neg", "d,w", 0x00000022, 0xffe007ff, WR_d|RD_t, I1 }, /* sub 0 */ +{"negu", "d,w", 0x00000023, 0xffe007ff, WR_d|RD_t, I1 }, /* subu 0 */ +{"neg.d", "D,V", 0x46200007, 0xffff003f, WR_D|RD_S|FP_D, I1 }, +{"neg.s", "D,V", 0x46000007, 0xffff003f, WR_D|RD_S|FP_S, I1 }, +{"neg.ps", "D,V", 0x46c00007, 0xffff003f, WR_D|RD_S|FP_D, I5 }, +{"nmadd.d", "D,R,S,T", 0x4c000031, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I4 }, +{"nmadd.s", "D,R,S,T", 0x4c000030, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_S, I4 }, +{"nmadd.ps","D,R,S,T", 0x4c000036, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I5 }, +{"nmsub.d", "D,R,S,T", 0x4c000039, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I4 }, +{"nmsub.s", "D,R,S,T", 0x4c000038, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_S, I4 }, +{"nmsub.ps","D,R,S,T", 0x4c00003e, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I5 }, +/* nop is at the start of the table. */ +{"nor", "d,v,t", 0x00000027, 0xfc0007ff, WR_d|RD_s|RD_t, I1 }, +{"nor", "t,r,I", 0, (int) M_NOR_I, INSN_MACRO, I1 }, +{"nor.ob", "X,Y,Q", 0x7800000f, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 }, +{"nor.ob", "D,S,T", 0x4ac0000f, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"nor.ob", "D,S,T[e]", 0x4800000f, 0xfe20003f, WR_D|RD_S|RD_T, N54 }, +{"nor.ob", "D,S,k", 0x4bc0000f, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"nor.qh", "X,Y,Q", 0x7820000f, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX }, +{"not", "d,v", 0x00000027, 0xfc1f07ff, WR_d|RD_s|RD_t, I1 },/*nor d,s,0*/ +{"or", "d,v,t", 0x00000025, 0xfc0007ff, WR_d|RD_s|RD_t, I1 }, +{"or", "t,r,I", 0, (int) M_OR_I, INSN_MACRO, I1 }, +{"or.ob", "X,Y,Q", 0x7800000e, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 }, +{"or.ob", "D,S,T", 0x4ac0000e, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"or.ob", "D,S,T[e]", 0x4800000e, 0xfe20003f, WR_D|RD_S|RD_T, N54 }, +{"or.ob", "D,S,k", 0x4bc0000e, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"or.qh", "X,Y,Q", 0x7820000e, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX }, +{"ori", "t,r,i", 0x34000000, 0xfc000000, WR_t|RD_s, I1 }, +{"pabsdiff.ob", "X,Y,Q",0x78000009, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, SB1 }, +{"pabsdiffc.ob", "Y,Q", 0x78000035, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, SB1 }, +{"pavg.ob", "X,Y,Q", 0x78000008, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, SB1 }, +{"pickf.ob", "X,Y,Q", 0x78000002, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 }, +{"pickf.ob", "D,S,T", 0x4ac00002, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"pickf.ob", "D,S,T[e]",0x48000002, 0xfe20003f, WR_D|RD_S|RD_T, N54 }, +{"pickf.ob", "D,S,k", 0x4bc00002, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"pickf.qh", "X,Y,Q", 0x78200002, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX }, +{"pickt.ob", "X,Y,Q", 0x78000003, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 }, +{"pickt.ob", "D,S,T", 0x4ac00003, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"pickt.ob", "D,S,T[e]",0x48000003, 0xfe20003f, WR_D|RD_S|RD_T, N54 }, +{"pickt.ob", "D,S,k", 0x4bc00003, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"pickt.qh", "X,Y,Q", 0x78200003, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX }, +{"pll.ps", "D,V,T", 0x46c0002c, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 }, +{"plu.ps", "D,V,T", 0x46c0002d, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 }, + /* pref and prefx are at the start of the table. */ +{"pul.ps", "D,V,T", 0x46c0002e, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 }, +{"puu.ps", "D,V,T", 0x46c0002f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 }, +{"rach.ob", "X", 0x7a00003f, 0xfffff83f, WR_D|RD_MACC|FP_D, MX|SB1 }, +{"rach.ob", "D", 0x4a00003f, 0xfffff83f, WR_D, N54 }, +{"rach.qh", "X", 0x7a20003f, 0xfffff83f, WR_D|RD_MACC|FP_D, MX }, +{"racl.ob", "X", 0x7800003f, 0xfffff83f, WR_D|RD_MACC|FP_D, MX|SB1 }, +{"racl.ob", "D", 0x4800003f, 0xfffff83f, WR_D, N54 }, +{"racl.qh", "X", 0x7820003f, 0xfffff83f, WR_D|RD_MACC|FP_D, MX }, +{"racm.ob", "X", 0x7900003f, 0xfffff83f, WR_D|RD_MACC|FP_D, MX|SB1 }, +{"racm.ob", "D", 0x4900003f, 0xfffff83f, WR_D, N54 }, +{"racm.qh", "X", 0x7920003f, 0xfffff83f, WR_D|RD_MACC|FP_D, MX }, +{"recip.d", "D,S", 0x46200015, 0xffff003f, WR_D|RD_S|FP_D, I4 }, +{"recip.ps","D,S", 0x46c00015, 0xffff003f, WR_D|RD_S|FP_D, SB1 }, +{"recip.s", "D,S", 0x46000015, 0xffff003f, WR_D|RD_S|FP_S, I4 }, +{"recip1.d", "D,S", 0x4620001d, 0xffff003f, WR_D|RD_S|FP_D, M3D }, +{"recip1.ps", "D,S", 0x46c0001d, 0xffff003f, WR_D|RD_S|FP_S, M3D }, +{"recip1.s", "D,S", 0x4600001d, 0xffff003f, WR_D|RD_S|FP_S, M3D }, +{"recip2.d", "D,S,T", 0x4620001c, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, M3D }, +{"recip2.ps", "D,S,T", 0x46c0001c, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, M3D }, +{"recip2.s", "D,S,T", 0x4600001c, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, M3D }, +{"rem", "z,s,t", 0x0000001a, 0xfc00ffff, RD_s|RD_t|WR_HILO, I1 }, +{"rem", "d,v,t", 0, (int) M_REM_3, INSN_MACRO, I1 }, +{"rem", "d,v,I", 0, (int) M_REM_3I, INSN_MACRO, I1 }, +{"remu", "z,s,t", 0x0000001b, 0xfc00ffff, RD_s|RD_t|WR_HILO, I1 }, +{"remu", "d,v,t", 0, (int) M_REMU_3, INSN_MACRO, I1 }, +{"remu", "d,v,I", 0, (int) M_REMU_3I, INSN_MACRO, I1 }, +{"rdhwr", "t,K", 0x7c00003b, 0xffe007ff, WR_t, I33 }, +{"rdpgpr", "d,w", 0x41400000, 0xffe007ff, WR_d, I33 }, +{"rfe", "", 0x42000010, 0xffffffff, 0, I1|T3 }, +{"rnas.qh", "X,Q", 0x78200025, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX }, +{"rnau.ob", "X,Q", 0x78000021, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX|SB1 }, +{"rnau.qh", "X,Q", 0x78200021, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX }, +{"rnes.qh", "X,Q", 0x78200026, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX }, +{"rneu.ob", "X,Q", 0x78000022, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX|SB1 }, +{"rneu.qh", "X,Q", 0x78200022, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX }, +{"rol", "d,v,t", 0, (int) M_ROL, INSN_MACRO, I1 }, +{"rol", "d,v,I", 0, (int) M_ROL_I, INSN_MACRO, I1 }, +{"ror", "d,v,t", 0, (int) M_ROR, INSN_MACRO, I1 }, +{"ror", "d,v,I", 0, (int) M_ROR_I, INSN_MACRO, I1 }, +{"ror", "d,w,<", 0x00200002, 0xffe0003f, WR_d|RD_t, N5|I33 }, +{"rorv", "d,t,s", 0x00000046, 0xfc0007ff, RD_t|RD_s|WR_d, N5|I33 }, +{"rotl", "d,v,t", 0, (int) M_ROL, INSN_MACRO, I33 }, +{"rotl", "d,v,I", 0, (int) M_ROL_I, INSN_MACRO, I33 }, +{"rotr", "d,v,t", 0, (int) M_ROR, INSN_MACRO, I33 }, +{"rotr", "d,v,I", 0, (int) M_ROR_I, INSN_MACRO, I33 }, +{"rotrv", "d,t,s", 0x00000046, 0xfc0007ff, RD_t|RD_s|WR_d, I33 }, +{"round.l.d", "D,S", 0x46200008, 0xffff003f, WR_D|RD_S|FP_D, I3 }, +{"round.l.s", "D,S", 0x46000008, 0xffff003f, WR_D|RD_S|FP_S, I3 }, +{"round.w.d", "D,S", 0x4620000c, 0xffff003f, WR_D|RD_S|FP_D, I2 }, +{"round.w.s", "D,S", 0x4600000c, 0xffff003f, WR_D|RD_S|FP_S, I2 }, +{"rsqrt.d", "D,S", 0x46200016, 0xffff003f, WR_D|RD_S|FP_D, I4 }, +{"rsqrt.ps","D,S", 0x46c00016, 0xffff003f, WR_D|RD_S|FP_D, SB1 }, +{"rsqrt.s", "D,S", 0x46000016, 0xffff003f, WR_D|RD_S|FP_S, I4 }, +{"rsqrt1.d", "D,S", 0x4620001e, 0xffff003f, WR_D|RD_S|FP_D, M3D }, +{"rsqrt1.ps", "D,S", 0x46c0001e, 0xffff003f, WR_D|RD_S|FP_S, M3D }, +{"rsqrt1.s", "D,S", 0x4600001e, 0xffff003f, WR_D|RD_S|FP_S, M3D }, +{"rsqrt2.d", "D,S,T", 0x4620001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, M3D }, +{"rsqrt2.ps", "D,S,T", 0x46c0001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, M3D }, +{"rsqrt2.s", "D,S,T", 0x4600001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, M3D }, +{"rzs.qh", "X,Q", 0x78200024, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX }, +{"rzu.ob", "X,Q", 0x78000020, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX|SB1 }, +{"rzu.ob", "D,k", 0x4bc00020, 0xffe0f83f, WR_D|RD_S|RD_T, N54 }, +{"rzu.qh", "X,Q", 0x78200020, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX }, +{"sb", "t,o(b)", 0xa0000000, 0xfc000000, SM|RD_t|RD_b, I1 }, +{"sb", "t,A(b)", 0, (int) M_SB_AB, INSN_MACRO, I1 }, +{"sc", "t,o(b)", 0xe0000000, 0xfc000000, SM|RD_t|WR_t|RD_b, I2 }, +{"sc", "t,A(b)", 0, (int) M_SC_AB, INSN_MACRO, I2 }, +{"scd", "t,o(b)", 0xf0000000, 0xfc000000, SM|RD_t|WR_t|RD_b, I3 }, +{"scd", "t,A(b)", 0, (int) M_SCD_AB, INSN_MACRO, I3 }, +{"sd", "t,o(b)", 0xfc000000, 0xfc000000, SM|RD_t|RD_b, I3 }, +{"sd", "t,o(b)", 0, (int) M_SD_OB, INSN_MACRO, I1 }, +{"sd", "t,A(b)", 0, (int) M_SD_AB, INSN_MACRO, I1 }, +{"sdbbp", "", 0x0000000e, 0xffffffff, TRAP, G2 }, +{"sdbbp", "c", 0x0000000e, 0xfc00ffff, TRAP, G2 }, +{"sdbbp", "c,q", 0x0000000e, 0xfc00003f, TRAP, G2 }, +{"sdbbp", "", 0x7000003f, 0xffffffff, TRAP, I32 }, +{"sdbbp", "B", 0x7000003f, 0xfc00003f, TRAP, I32 }, +{"sdc1", "T,o(b)", 0xf4000000, 0xfc000000, SM|RD_T|RD_b|FP_D, I2 }, +{"sdc1", "E,o(b)", 0xf4000000, 0xfc000000, SM|RD_T|RD_b|FP_D, I2 }, +{"sdc1", "T,A(b)", 0, (int) M_SDC1_AB, INSN_MACRO, I2 }, +{"sdc1", "E,A(b)", 0, (int) M_SDC1_AB, INSN_MACRO, I2 }, +{"sdc2", "E,o(b)", 0xf8000000, 0xfc000000, SM|RD_C2|RD_b, I2 }, +{"sdc2", "E,A(b)", 0, (int) M_SDC2_AB, INSN_MACRO, I2 }, +{"sdc3", "E,o(b)", 0xfc000000, 0xfc000000, SM|RD_C3|RD_b, I2 }, +{"sdc3", "E,A(b)", 0, (int) M_SDC3_AB, INSN_MACRO, I2 }, +{"s.d", "T,o(b)", 0xf4000000, 0xfc000000, SM|RD_T|RD_b|FP_D, I2 }, +{"s.d", "T,o(b)", 0, (int) M_S_DOB, INSN_MACRO, I1 }, +{"s.d", "T,A(b)", 0, (int) M_S_DAB, INSN_MACRO, I1 }, +{"sdl", "t,o(b)", 0xb0000000, 0xfc000000, SM|RD_t|RD_b, I3 }, +{"sdl", "t,A(b)", 0, (int) M_SDL_AB, INSN_MACRO, I3 }, +{"sdr", "t,o(b)", 0xb4000000, 0xfc000000, SM|RD_t|RD_b, I3 }, +{"sdr", "t,A(b)", 0, (int) M_SDR_AB, INSN_MACRO, I3 }, +{"sdxc1", "S,t(b)", 0x4c000009, 0xfc0007ff, SM|RD_S|RD_t|RD_b, I4 }, +{"seb", "d,w", 0x7c000420, 0xffe007ff, WR_d|RD_t, I33 }, +{"seh", "d,w", 0x7c000620, 0xffe007ff, WR_d|RD_t, I33 }, +{"selsl", "d,v,t", 0x00000005, 0xfc0007ff, WR_d|RD_s|RD_t, L1 }, +{"selsr", "d,v,t", 0x00000001, 0xfc0007ff, WR_d|RD_s|RD_t, L1 }, +{"seq", "d,v,t", 0, (int) M_SEQ, INSN_MACRO, I1 }, +{"seq", "d,v,I", 0, (int) M_SEQ_I, INSN_MACRO, I1 }, +{"sge", "d,v,t", 0, (int) M_SGE, INSN_MACRO, I1 }, +{"sge", "d,v,I", 0, (int) M_SGE_I, INSN_MACRO, I1 }, +{"sgeu", "d,v,t", 0, (int) M_SGEU, INSN_MACRO, I1 }, +{"sgeu", "d,v,I", 0, (int) M_SGEU_I, INSN_MACRO, I1 }, +{"sgt", "d,v,t", 0, (int) M_SGT, INSN_MACRO, I1 }, +{"sgt", "d,v,I", 0, (int) M_SGT_I, INSN_MACRO, I1 }, +{"sgtu", "d,v,t", 0, (int) M_SGTU, INSN_MACRO, I1 }, +{"sgtu", "d,v,I", 0, (int) M_SGTU_I, INSN_MACRO, I1 }, +{"sh", "t,o(b)", 0xa4000000, 0xfc000000, SM|RD_t|RD_b, I1 }, +{"sh", "t,A(b)", 0, (int) M_SH_AB, INSN_MACRO, I1 }, +{"shfl.bfla.qh", "X,Y,Z", 0x7a20001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX }, +{"shfl.mixh.ob", "X,Y,Z", 0x7980001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 }, +{"shfl.mixh.ob", "D,S,T", 0x4980001f, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"shfl.mixh.qh", "X,Y,Z", 0x7820001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX }, +{"shfl.mixl.ob", "X,Y,Z", 0x79c0001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 }, +{"shfl.mixl.ob", "D,S,T", 0x49c0001f, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"shfl.mixl.qh", "X,Y,Z", 0x78a0001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX }, +{"shfl.pach.ob", "X,Y,Z", 0x7900001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 }, +{"shfl.pach.ob", "D,S,T", 0x4900001f, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"shfl.pach.qh", "X,Y,Z", 0x7920001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX }, +{"shfl.pacl.ob", "D,S,T", 0x4940001f, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"shfl.repa.qh", "X,Y,Z", 0x7b20001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX }, +{"shfl.repb.qh", "X,Y,Z", 0x7ba0001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX }, +{"shfl.upsl.ob", "X,Y,Z", 0x78c0001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 }, +{"sle", "d,v,t", 0, (int) M_SLE, INSN_MACRO, I1 }, +{"sle", "d,v,I", 0, (int) M_SLE_I, INSN_MACRO, I1 }, +{"sleu", "d,v,t", 0, (int) M_SLEU, INSN_MACRO, I1 }, +{"sleu", "d,v,I", 0, (int) M_SLEU_I, INSN_MACRO, I1 }, +{"sllv", "d,t,s", 0x00000004, 0xfc0007ff, WR_d|RD_t|RD_s, I1 }, +{"sll", "d,w,s", 0x00000004, 0xfc0007ff, WR_d|RD_t|RD_s, I1 }, /* sllv */ +{"sll", "d,w,<", 0x00000000, 0xffe0003f, WR_d|RD_t, I1 }, +{"sll.ob", "X,Y,Q", 0x78000010, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 }, +{"sll.ob", "D,S,T[e]", 0x48000010, 0xfe20003f, WR_D|RD_S|RD_T, N54 }, +{"sll.ob", "D,S,k", 0x4bc00010, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"sll.qh", "X,Y,Q", 0x78200010, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX }, +{"slt", "d,v,t", 0x0000002a, 0xfc0007ff, WR_d|RD_s|RD_t, I1 }, +{"slt", "d,v,I", 0, (int) M_SLT_I, INSN_MACRO, I1 }, +{"slti", "t,r,j", 0x28000000, 0xfc000000, WR_t|RD_s, I1 }, +{"sltiu", "t,r,j", 0x2c000000, 0xfc000000, WR_t|RD_s, I1 }, +{"sltu", "d,v,t", 0x0000002b, 0xfc0007ff, WR_d|RD_s|RD_t, I1 }, +{"sltu", "d,v,I", 0, (int) M_SLTU_I, INSN_MACRO, I1 }, +{"sne", "d,v,t", 0, (int) M_SNE, INSN_MACRO, I1 }, +{"sne", "d,v,I", 0, (int) M_SNE_I, INSN_MACRO, I1 }, +{"sqrt.d", "D,S", 0x46200004, 0xffff003f, WR_D|RD_S|FP_D, I2 }, +{"sqrt.s", "D,S", 0x46000004, 0xffff003f, WR_D|RD_S|FP_S, I2 }, +{"sqrt.ps", "D,S", 0x46c00004, 0xffff003f, WR_D|RD_S|FP_D, SB1 }, +{"srav", "d,t,s", 0x00000007, 0xfc0007ff, WR_d|RD_t|RD_s, I1 }, +{"sra", "d,w,s", 0x00000007, 0xfc0007ff, WR_d|RD_t|RD_s, I1 }, /* srav */ +{"sra", "d,w,<", 0x00000003, 0xffe0003f, WR_d|RD_t, I1 }, +{"sra.qh", "X,Y,Q", 0x78200013, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX }, +{"srlv", "d,t,s", 0x00000006, 0xfc0007ff, WR_d|RD_t|RD_s, I1 }, +{"srl", "d,w,s", 0x00000006, 0xfc0007ff, WR_d|RD_t|RD_s, I1 }, /* srlv */ +{"srl", "d,w,<", 0x00000002, 0xffe0003f, WR_d|RD_t, I1 }, +{"srl.ob", "X,Y,Q", 0x78000012, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 }, +{"srl.ob", "D,S,T[e]", 0x48000012, 0xfe20003f, WR_D|RD_S|RD_T, N54 }, +{"srl.ob", "D,S,k", 0x4bc00012, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"srl.qh", "X,Y,Q", 0x78200012, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX }, +/* ssnop is at the start of the table. */ +{"standby", "", 0x42000021, 0xffffffff, 0, V1 }, +{"sub", "d,v,t", 0x00000022, 0xfc0007ff, WR_d|RD_s|RD_t, I1 }, +{"sub", "d,v,I", 0, (int) M_SUB_I, INSN_MACRO, I1 }, +{"sub.d", "D,V,T", 0x46200001, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I1 }, +{"sub.s", "D,V,T", 0x46000001, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, I1 }, +{"sub.ob", "X,Y,Q", 0x7800000a, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 }, +{"sub.ob", "D,S,T", 0x4ac0000a, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"sub.ob", "D,S,T[e]", 0x4800000a, 0xfe20003f, WR_D|RD_S|RD_T, N54 }, +{"sub.ob", "D,S,k", 0x4bc0000a, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"sub.ps", "D,V,T", 0x46c00001, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 }, +{"sub.qh", "X,Y,Q", 0x7820000a, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX }, +{"suba.ob", "Y,Q", 0x78000036, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 }, +{"suba.qh", "Y,Q", 0x78200036, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX }, +{"subl.ob", "Y,Q", 0x78000436, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 }, +{"subl.qh", "Y,Q", 0x78200436, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX }, +{"subu", "d,v,t", 0x00000023, 0xfc0007ff, WR_d|RD_s|RD_t, I1 }, +{"subu", "d,v,I", 0, (int) M_SUBU_I, INSN_MACRO, I1 }, +{"suspend", "", 0x42000022, 0xffffffff, 0, V1 }, +{"suxc1", "S,t(b)", 0x4c00000d, 0xfc0007ff, SM|RD_S|RD_t|RD_b, I5|N55 }, +{"sw", "t,o(b)", 0xac000000, 0xfc000000, SM|RD_t|RD_b, I1 }, +{"sw", "t,A(b)", 0, (int) M_SW_AB, INSN_MACRO, I1 }, +{"swc0", "E,o(b)", 0xe0000000, 0xfc000000, SM|RD_C0|RD_b, I1 }, +{"swc0", "E,A(b)", 0, (int) M_SWC0_AB, INSN_MACRO, I1 }, +{"swc1", "T,o(b)", 0xe4000000, 0xfc000000, SM|RD_T|RD_b|FP_S, I1 }, +{"swc1", "E,o(b)", 0xe4000000, 0xfc000000, SM|RD_T|RD_b|FP_S, I1 }, +{"swc1", "T,A(b)", 0, (int) M_SWC1_AB, INSN_MACRO, I1 }, +{"swc1", "E,A(b)", 0, (int) M_SWC1_AB, INSN_MACRO, I1 }, +{"s.s", "T,o(b)", 0xe4000000, 0xfc000000, SM|RD_T|RD_b|FP_S, I1 }, /* swc1 */ +{"s.s", "T,A(b)", 0, (int) M_SWC1_AB, INSN_MACRO, I1 }, +{"swc2", "E,o(b)", 0xe8000000, 0xfc000000, SM|RD_C2|RD_b, I1 }, +{"swc2", "E,A(b)", 0, (int) M_SWC2_AB, INSN_MACRO, I1 }, +{"swc3", "E,o(b)", 0xec000000, 0xfc000000, SM|RD_C3|RD_b, I1 }, +{"swc3", "E,A(b)", 0, (int) M_SWC3_AB, INSN_MACRO, I1 }, +{"swl", "t,o(b)", 0xa8000000, 0xfc000000, SM|RD_t|RD_b, I1 }, +{"swl", "t,A(b)", 0, (int) M_SWL_AB, INSN_MACRO, I1 }, +{"scache", "t,o(b)", 0xa8000000, 0xfc000000, RD_t|RD_b, I2 }, /* same */ +{"scache", "t,A(b)", 0, (int) M_SWL_AB, INSN_MACRO, I2 }, /* as swl */ +{"swr", "t,o(b)", 0xb8000000, 0xfc000000, SM|RD_t|RD_b, I1 }, +{"swr", "t,A(b)", 0, (int) M_SWR_AB, INSN_MACRO, I1 }, +{"invalidate", "t,o(b)",0xb8000000, 0xfc000000, RD_t|RD_b, I2 }, /* same */ +{"invalidate", "t,A(b)",0, (int) M_SWR_AB, INSN_MACRO, I2 }, /* as swr */ +{"swxc1", "S,t(b)", 0x4c000008, 0xfc0007ff, SM|RD_S|RD_t|RD_b, I4 }, +{"sync", "", 0x0000000f, 0xffffffff, INSN_SYNC, I2|G1 }, +{"sync.p", "", 0x0000040f, 0xffffffff, INSN_SYNC, I2 }, +{"sync.l", "", 0x0000000f, 0xffffffff, INSN_SYNC, I2 }, +{"synci", "o(b)", 0x041f0000, 0xfc1f0000, SM|RD_b, I33 }, +{"syscall", "", 0x0000000c, 0xffffffff, TRAP, I1 }, +{"syscall", "B", 0x0000000c, 0xfc00003f, TRAP, I1 }, +{"teqi", "s,j", 0x040c0000, 0xfc1f0000, RD_s|TRAP, I2 }, +{"teq", "s,t", 0x00000034, 0xfc00ffff, RD_s|RD_t|TRAP, I2 }, +{"teq", "s,t,q", 0x00000034, 0xfc00003f, RD_s|RD_t|TRAP, I2 }, +{"teq", "s,j", 0x040c0000, 0xfc1f0000, RD_s|TRAP, I2 }, /* teqi */ +{"teq", "s,I", 0, (int) M_TEQ_I, INSN_MACRO, I2 }, +{"tgei", "s,j", 0x04080000, 0xfc1f0000, RD_s|TRAP, I2 }, +{"tge", "s,t", 0x00000030, 0xfc00ffff, RD_s|RD_t|TRAP, I2 }, +{"tge", "s,t,q", 0x00000030, 0xfc00003f, RD_s|RD_t|TRAP, I2 }, +{"tge", "s,j", 0x04080000, 0xfc1f0000, RD_s|TRAP, I2 }, /* tgei */ +{"tge", "s,I", 0, (int) M_TGE_I, INSN_MACRO, I2 }, +{"tgeiu", "s,j", 0x04090000, 0xfc1f0000, RD_s|TRAP, I2 }, +{"tgeu", "s,t", 0x00000031, 0xfc00ffff, RD_s|RD_t|TRAP, I2 }, +{"tgeu", "s,t,q", 0x00000031, 0xfc00003f, RD_s|RD_t|TRAP, I2 }, +{"tgeu", "s,j", 0x04090000, 0xfc1f0000, RD_s|TRAP, I2 }, /* tgeiu */ +{"tgeu", "s,I", 0, (int) M_TGEU_I, INSN_MACRO, I2 }, +{"tlbp", "", 0x42000008, 0xffffffff, INSN_TLB, I1 }, +{"tlbr", "", 0x42000001, 0xffffffff, INSN_TLB, I1 }, +{"tlbwi", "", 0x42000002, 0xffffffff, INSN_TLB, I1 }, +{"tlbwr", "", 0x42000006, 0xffffffff, INSN_TLB, I1 }, +{"tlti", "s,j", 0x040a0000, 0xfc1f0000, RD_s|TRAP, I2 }, +{"tlt", "s,t", 0x00000032, 0xfc00ffff, RD_s|RD_t|TRAP, I2 }, +{"tlt", "s,t,q", 0x00000032, 0xfc00003f, RD_s|RD_t|TRAP, I2 }, +{"tlt", "s,j", 0x040a0000, 0xfc1f0000, RD_s|TRAP, I2 }, /* tlti */ +{"tlt", "s,I", 0, (int) M_TLT_I, INSN_MACRO, I2 }, +{"tltiu", "s,j", 0x040b0000, 0xfc1f0000, RD_s|TRAP, I2 }, +{"tltu", "s,t", 0x00000033, 0xfc00ffff, RD_s|RD_t|TRAP, I2 }, +{"tltu", "s,t,q", 0x00000033, 0xfc00003f, RD_s|RD_t|TRAP, I2 }, +{"tltu", "s,j", 0x040b0000, 0xfc1f0000, RD_s|TRAP, I2 }, /* tltiu */ +{"tltu", "s,I", 0, (int) M_TLTU_I, INSN_MACRO, I2 }, +{"tnei", "s,j", 0x040e0000, 0xfc1f0000, RD_s|TRAP, I2 }, +{"tne", "s,t", 0x00000036, 0xfc00ffff, RD_s|RD_t|TRAP, I2 }, +{"tne", "s,t,q", 0x00000036, 0xfc00003f, RD_s|RD_t|TRAP, I2 }, +{"tne", "s,j", 0x040e0000, 0xfc1f0000, RD_s|TRAP, I2 }, /* tnei */ +{"tne", "s,I", 0, (int) M_TNE_I, INSN_MACRO, I2 }, +{"trunc.l.d", "D,S", 0x46200009, 0xffff003f, WR_D|RD_S|FP_D, I3 }, +{"trunc.l.s", "D,S", 0x46000009, 0xffff003f, WR_D|RD_S|FP_S, I3 }, +{"trunc.w.d", "D,S", 0x4620000d, 0xffff003f, WR_D|RD_S|FP_D, I2 }, +{"trunc.w.d", "D,S,x", 0x4620000d, 0xffff003f, WR_D|RD_S|FP_D, I2 }, +{"trunc.w.d", "D,S,t", 0, (int) M_TRUNCWD, INSN_MACRO, I1 }, +{"trunc.w.s", "D,S", 0x4600000d, 0xffff003f, WR_D|RD_S|FP_S, I2 }, +{"trunc.w.s", "D,S,x", 0x4600000d, 0xffff003f, WR_D|RD_S|FP_S, I2 }, +{"trunc.w.s", "D,S,t", 0, (int) M_TRUNCWS, INSN_MACRO, I1 }, +{"uld", "t,o(b)", 0, (int) M_ULD, INSN_MACRO, I3 }, +{"uld", "t,A(b)", 0, (int) M_ULD_A, INSN_MACRO, I3 }, +{"ulh", "t,o(b)", 0, (int) M_ULH, INSN_MACRO, I1 }, +{"ulh", "t,A(b)", 0, (int) M_ULH_A, INSN_MACRO, I1 }, +{"ulhu", "t,o(b)", 0, (int) M_ULHU, INSN_MACRO, I1 }, +{"ulhu", "t,A(b)", 0, (int) M_ULHU_A, INSN_MACRO, I1 }, +{"ulw", "t,o(b)", 0, (int) M_ULW, INSN_MACRO, I1 }, +{"ulw", "t,A(b)", 0, (int) M_ULW_A, INSN_MACRO, I1 }, +{"usd", "t,o(b)", 0, (int) M_USD, INSN_MACRO, I3 }, +{"usd", "t,A(b)", 0, (int) M_USD_A, INSN_MACRO, I3 }, +{"ush", "t,o(b)", 0, (int) M_USH, INSN_MACRO, I1 }, +{"ush", "t,A(b)", 0, (int) M_USH_A, INSN_MACRO, I1 }, +{"usw", "t,o(b)", 0, (int) M_USW, INSN_MACRO, I1 }, +{"usw", "t,A(b)", 0, (int) M_USW_A, INSN_MACRO, I1 }, +{"wach.ob", "Y", 0x7a00003e, 0xffff07ff, WR_MACC|RD_S|FP_D, MX|SB1 }, +{"wach.ob", "S", 0x4a00003e, 0xffff07ff, RD_S, N54 }, +{"wach.qh", "Y", 0x7a20003e, 0xffff07ff, WR_MACC|RD_S|FP_D, MX }, +{"wacl.ob", "Y,Z", 0x7800003e, 0xffe007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 }, +{"wacl.ob", "S,T", 0x4800003e, 0xffe007ff, RD_S|RD_T, N54 }, +{"wacl.qh", "Y,Z", 0x7820003e, 0xffe007ff, WR_MACC|RD_S|RD_T|FP_D, MX }, +{"wait", "", 0x42000020, 0xffffffff, TRAP, I3|I32 }, +{"wait", "J", 0x42000020, 0xfe00003f, TRAP, I32|N55 }, +{"waiti", "", 0x42000020, 0xffffffff, TRAP, L1 }, +{"wb", "o(b)", 0xbc040000, 0xfc1f0000, SM|RD_b, L1 }, +{"wrpgpr", "d,w", 0x41c00000, 0xffe007ff, RD_t, I33 }, +{"wsbh", "d,w", 0x7c0000a0, 0xffe007ff, WR_d|RD_t, I33 }, +{"xor", "d,v,t", 0x00000026, 0xfc0007ff, WR_d|RD_s|RD_t, I1 }, +{"xor", "t,r,I", 0, (int) M_XOR_I, INSN_MACRO, I1 }, +{"xor.ob", "X,Y,Q", 0x7800000d, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 }, +{"xor.ob", "D,S,T", 0x4ac0000d, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"xor.ob", "D,S,T[e]", 0x4800000d, 0xfe20003f, WR_D|RD_S|RD_T, N54 }, +{"xor.ob", "D,S,k", 0x4bc0000d, 0xffe0003f, WR_D|RD_S|RD_T, N54 }, +{"xor.qh", "X,Y,Q", 0x7820000d, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX }, +{"xori", "t,r,i", 0x38000000, 0xfc000000, WR_t|RD_s, I1 }, + +/* Coprocessor 2 move/branch operations overlap with VR5400 .ob format + instructions so they are here for the latters to take precedence. */ +{"bc2f", "p", 0x49000000, 0xffff0000, CBD|RD_CC, I1 }, +{"bc2fl", "p", 0x49020000, 0xffff0000, CBL|RD_CC, I2|T3 }, +{"bc2t", "p", 0x49010000, 0xffff0000, CBD|RD_CC, I1 }, +{"bc2tl", "p", 0x49030000, 0xffff0000, CBL|RD_CC, I2|T3 }, +{"cfc2", "t,G", 0x48400000, 0xffe007ff, LCD|WR_t|RD_C2, I1 }, +{"ctc2", "t,G", 0x48c00000, 0xffe007ff, COD|RD_t|WR_CC, I1 }, +{"dmfc2", "t,G", 0x48200000, 0xffe007ff, LCD|WR_t|RD_C2, I3 }, +{"dmfc2", "t,G,H", 0x48200000, 0xffe007f8, LCD|WR_t|RD_C2, I64 }, +{"dmtc2", "t,G", 0x48a00000, 0xffe007ff, COD|RD_t|WR_C2|WR_CC, I3 }, +{"dmtc2", "t,G,H", 0x48a00000, 0xffe007f8, COD|RD_t|WR_C2|WR_CC, I64 }, +{"mfc2", "t,G", 0x48000000, 0xffe007ff, LCD|WR_t|RD_C2, I1 }, +{"mfc2", "t,G,H", 0x48000000, 0xffe007f8, LCD|WR_t|RD_C2, I32 }, +{"mfhc2", "t,i", 0x48600000, 0xffe00000, LCD|WR_t|RD_C2, I33 }, +{"mtc2", "t,G", 0x48800000, 0xffe007ff, COD|RD_t|WR_C2|WR_CC, I1 }, +{"mtc2", "t,G,H", 0x48800000, 0xffe007f8, COD|RD_t|WR_C2|WR_CC, I32 }, +{"mthc2", "t,i", 0x48e00000, 0xffe00000, COD|RD_t|WR_C2|WR_CC, I33 }, + +/* No hazard protection on coprocessor instructions--they shouldn't + change the state of the processor and if they do it's up to the + user to put in nops as necessary. These are at the end so that the + disassembler recognizes more specific versions first. */ +{"c0", "C", 0x42000000, 0xfe000000, 0, I1 }, +{"c1", "C", 0x46000000, 0xfe000000, 0, I1 }, +{"c2", "C", 0x4a000000, 0xfe000000, 0, I1 }, +{"c3", "C", 0x4e000000, 0xfe000000, 0, I1 }, +{"cop0", "C", 0, (int) M_COP0, INSN_MACRO, I1 }, +{"cop1", "C", 0, (int) M_COP1, INSN_MACRO, I1 }, +{"cop2", "C", 0, (int) M_COP2, INSN_MACRO, I1 }, +{"cop3", "C", 0, (int) M_COP3, INSN_MACRO, I1 }, + + /* Conflicts with the 4650's "mul" instruction. Nobody's using the + 4010 any more, so move this insn out of the way. If the object + format gave us more info, we could do this right. */ +{"addciu", "t,r,j", 0x70000000, 0xfc000000, WR_t|RD_s, L1 }, +}; + +#define MIPS_NUM_OPCODES \ + ((sizeof mips_builtin_opcodes) / (sizeof (mips_builtin_opcodes[0]))) +const int bfd_mips_num_builtin_opcodes = MIPS_NUM_OPCODES; + +/* const removed from the following to allow for dynamic extensions to the + * built-in instruction set. */ +struct mips_opcode *mips_opcodes = + (struct mips_opcode *) mips_builtin_opcodes; +int bfd_mips_num_opcodes = MIPS_NUM_OPCODES; +#undef MIPS_NUM_OPCODES + +typedef int bfd_boolean; +#define TRUE (1) +#define FALSE (0) + +/* Mips instructions are at maximum this many bytes long. */ +#define INSNLEN 4 + +static void set_default_mips_dis_options + PARAMS ((struct disassemble_info *)); +static void parse_mips_dis_option + PARAMS ((const char *, unsigned int)); +static void parse_mips_dis_options + PARAMS ((const char *)); +static int _print_insn_mips + PARAMS ((bfd_vma, struct disassemble_info *, enum bfd_endian)); +static int print_insn_mips + PARAMS ((bfd_vma, unsigned long int, struct disassemble_info *)); +static void print_insn_args + PARAMS ((const char *, unsigned long, bfd_vma, struct disassemble_info *)); +#if 0 +static int print_insn_mips16 + PARAMS ((bfd_vma, struct disassemble_info *)); +#endif +#if 0 +static int is_newabi + PARAMS ((Elf32_Ehdr *)); +#endif +#if 0 +static void print_mips16_insn_arg + PARAMS ((int, const struct mips_opcode *, int, bfd_boolean, int, bfd_vma, + struct disassemble_info *)); +#endif + +/* FIXME: These should be shared with gdb somehow. */ + +struct mips_cp0sel_name { + unsigned int cp0reg; + unsigned int sel; + const char * const name; +}; + +/* The mips16 register names. */ +static const char * const mips16_reg_names[] = { + "s0", "s1", "v0", "v1", "a0", "a1", "a2", "a3" +}; + +static const char * const mips_gpr_names_numeric[32] = { + "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7", + "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", + "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23", + "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31" +}; + +static const char * const mips_gpr_names_oldabi[32] = { + "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra" +}; + +static const char * const mips_gpr_names_newabi[32] = { + "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", + "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra" +}; + +static const char * const mips_fpr_names_numeric[32] = { + "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", + "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15", + "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23", + "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31" +}; + +static const char * const mips_fpr_names_32[32] = { + "fv0", "fv0f", "fv1", "fv1f", "ft0", "ft0f", "ft1", "ft1f", + "ft2", "ft2f", "ft3", "ft3f", "fa0", "fa0f", "fa1", "fa1f", + "ft4", "ft4f", "ft5", "ft5f", "fs0", "fs0f", "fs1", "fs1f", + "fs2", "fs2f", "fs3", "fs3f", "fs4", "fs4f", "fs5", "fs5f" +}; + +static const char * const mips_fpr_names_n32[32] = { + "fv0", "ft14", "fv1", "ft15", "ft0", "ft1", "ft2", "ft3", + "ft4", "ft5", "ft6", "ft7", "fa0", "fa1", "fa2", "fa3", + "fa4", "fa5", "fa6", "fa7", "fs0", "ft8", "fs1", "ft9", + "fs2", "ft10", "fs3", "ft11", "fs4", "ft12", "fs5", "ft13" +}; + +static const char * const mips_fpr_names_64[32] = { + "fv0", "ft12", "fv1", "ft13", "ft0", "ft1", "ft2", "ft3", + "ft4", "ft5", "ft6", "ft7", "fa0", "fa1", "fa2", "fa3", + "fa4", "fa5", "fa6", "fa7", "ft8", "ft9", "ft10", "ft11", + "fs0", "fs1", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7" +}; + +static const char * const mips_cp0_names_numeric[32] = { + "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7", + "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", + "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23", + "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31" +}; + +static const char * const mips_cp0_names_mips3264[32] = { + "c0_index", "c0_random", "c0_entrylo0", "c0_entrylo1", + "c0_context", "c0_pagemask", "c0_wired", "$7", + "c0_badvaddr", "c0_count", "c0_entryhi", "c0_compare", + "c0_status", "c0_cause", "c0_epc", "c0_prid", + "c0_config", "c0_lladdr", "c0_watchlo", "c0_watchhi", + "c0_xcontext", "$21", "$22", "c0_debug", + "c0_depc", "c0_perfcnt", "c0_errctl", "c0_cacheerr", + "c0_taglo", "c0_taghi", "c0_errorepc", "c0_desave", +}; + +static const struct mips_cp0sel_name mips_cp0sel_names_mips3264[] = { + { 16, 1, "c0_config1" }, + { 16, 2, "c0_config2" }, + { 16, 3, "c0_config3" }, + { 18, 1, "c0_watchlo,1" }, + { 18, 2, "c0_watchlo,2" }, + { 18, 3, "c0_watchlo,3" }, + { 18, 4, "c0_watchlo,4" }, + { 18, 5, "c0_watchlo,5" }, + { 18, 6, "c0_watchlo,6" }, + { 18, 7, "c0_watchlo,7" }, + { 19, 1, "c0_watchhi,1" }, + { 19, 2, "c0_watchhi,2" }, + { 19, 3, "c0_watchhi,3" }, + { 19, 4, "c0_watchhi,4" }, + { 19, 5, "c0_watchhi,5" }, + { 19, 6, "c0_watchhi,6" }, + { 19, 7, "c0_watchhi,7" }, + { 25, 1, "c0_perfcnt,1" }, + { 25, 2, "c0_perfcnt,2" }, + { 25, 3, "c0_perfcnt,3" }, + { 25, 4, "c0_perfcnt,4" }, + { 25, 5, "c0_perfcnt,5" }, + { 25, 6, "c0_perfcnt,6" }, + { 25, 7, "c0_perfcnt,7" }, + { 27, 1, "c0_cacheerr,1" }, + { 27, 2, "c0_cacheerr,2" }, + { 27, 3, "c0_cacheerr,3" }, + { 28, 1, "c0_datalo" }, + { 29, 1, "c0_datahi" } +}; + +static const char * const mips_cp0_names_mips3264r2[32] = { + "c0_index", "c0_random", "c0_entrylo0", "c0_entrylo1", + "c0_context", "c0_pagemask", "c0_wired", "c0_hwrena", + "c0_badvaddr", "c0_count", "c0_entryhi", "c0_compare", + "c0_status", "c0_cause", "c0_epc", "c0_prid", + "c0_config", "c0_lladdr", "c0_watchlo", "c0_watchhi", + "c0_xcontext", "$21", "$22", "c0_debug", + "c0_depc", "c0_perfcnt", "c0_errctl", "c0_cacheerr", + "c0_taglo", "c0_taghi", "c0_errorepc", "c0_desave", +}; + +static const struct mips_cp0sel_name mips_cp0sel_names_mips3264r2[] = { + { 4, 1, "c0_contextconfig" }, + { 5, 1, "c0_pagegrain" }, + { 12, 1, "c0_intctl" }, + { 12, 2, "c0_srsctl" }, + { 12, 3, "c0_srsmap" }, + { 15, 1, "c0_ebase" }, + { 16, 1, "c0_config1" }, + { 16, 2, "c0_config2" }, + { 16, 3, "c0_config3" }, + { 18, 1, "c0_watchlo,1" }, + { 18, 2, "c0_watchlo,2" }, + { 18, 3, "c0_watchlo,3" }, + { 18, 4, "c0_watchlo,4" }, + { 18, 5, "c0_watchlo,5" }, + { 18, 6, "c0_watchlo,6" }, + { 18, 7, "c0_watchlo,7" }, + { 19, 1, "c0_watchhi,1" }, + { 19, 2, "c0_watchhi,2" }, + { 19, 3, "c0_watchhi,3" }, + { 19, 4, "c0_watchhi,4" }, + { 19, 5, "c0_watchhi,5" }, + { 19, 6, "c0_watchhi,6" }, + { 19, 7, "c0_watchhi,7" }, + { 23, 1, "c0_tracecontrol" }, + { 23, 2, "c0_tracecontrol2" }, + { 23, 3, "c0_usertracedata" }, + { 23, 4, "c0_tracebpc" }, + { 25, 1, "c0_perfcnt,1" }, + { 25, 2, "c0_perfcnt,2" }, + { 25, 3, "c0_perfcnt,3" }, + { 25, 4, "c0_perfcnt,4" }, + { 25, 5, "c0_perfcnt,5" }, + { 25, 6, "c0_perfcnt,6" }, + { 25, 7, "c0_perfcnt,7" }, + { 27, 1, "c0_cacheerr,1" }, + { 27, 2, "c0_cacheerr,2" }, + { 27, 3, "c0_cacheerr,3" }, + { 28, 1, "c0_datalo" }, + { 28, 2, "c0_taglo1" }, + { 28, 3, "c0_datalo1" }, + { 28, 4, "c0_taglo2" }, + { 28, 5, "c0_datalo2" }, + { 28, 6, "c0_taglo3" }, + { 28, 7, "c0_datalo3" }, + { 29, 1, "c0_datahi" }, + { 29, 2, "c0_taghi1" }, + { 29, 3, "c0_datahi1" }, + { 29, 4, "c0_taghi2" }, + { 29, 5, "c0_datahi2" }, + { 29, 6, "c0_taghi3" }, + { 29, 7, "c0_datahi3" }, +}; + +/* SB-1: MIPS64 (mips_cp0_names_mips3264) with minor mods. */ +static const char * const mips_cp0_names_sb1[32] = { + "c0_index", "c0_random", "c0_entrylo0", "c0_entrylo1", + "c0_context", "c0_pagemask", "c0_wired", "$7", + "c0_badvaddr", "c0_count", "c0_entryhi", "c0_compare", + "c0_status", "c0_cause", "c0_epc", "c0_prid", + "c0_config", "c0_lladdr", "c0_watchlo", "c0_watchhi", + "c0_xcontext", "$21", "$22", "c0_debug", + "c0_depc", "c0_perfcnt", "c0_errctl", "c0_cacheerr_i", + "c0_taglo_i", "c0_taghi_i", "c0_errorepc", "c0_desave", +}; + +static const struct mips_cp0sel_name mips_cp0sel_names_sb1[] = { + { 16, 1, "c0_config1" }, + { 18, 1, "c0_watchlo,1" }, + { 19, 1, "c0_watchhi,1" }, + { 22, 0, "c0_perftrace" }, + { 23, 3, "c0_edebug" }, + { 25, 1, "c0_perfcnt,1" }, + { 25, 2, "c0_perfcnt,2" }, + { 25, 3, "c0_perfcnt,3" }, + { 25, 4, "c0_perfcnt,4" }, + { 25, 5, "c0_perfcnt,5" }, + { 25, 6, "c0_perfcnt,6" }, + { 25, 7, "c0_perfcnt,7" }, + { 26, 1, "c0_buserr_pa" }, + { 27, 1, "c0_cacheerr_d" }, + { 27, 3, "c0_cacheerr_d_pa" }, + { 28, 1, "c0_datalo_i" }, + { 28, 2, "c0_taglo_d" }, + { 28, 3, "c0_datalo_d" }, + { 29, 1, "c0_datahi_i" }, + { 29, 2, "c0_taghi_d" }, + { 29, 3, "c0_datahi_d" }, +}; + +static const char * const mips_hwr_names_numeric[32] = { + "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7", + "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", + "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23", + "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31" +}; + +static const char * const mips_hwr_names_mips3264r2[32] = { + "hwr_cpunum", "hwr_synci_step", "hwr_cc", "hwr_ccres", + "$4", "$5", "$6", "$7", + "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", + "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23", + "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31" +}; + +struct mips_abi_choice { + const char *name; + const char * const *gpr_names; + const char * const *fpr_names; +}; + +struct mips_abi_choice mips_abi_choices[] = { + { "numeric", mips_gpr_names_numeric, mips_fpr_names_numeric }, + { "32", mips_gpr_names_oldabi, mips_fpr_names_32 }, + { "n32", mips_gpr_names_newabi, mips_fpr_names_n32 }, + { "64", mips_gpr_names_newabi, mips_fpr_names_64 }, +}; + +struct mips_arch_choice { + const char *name; + int bfd_mach_valid; + unsigned long bfd_mach; + int processor; + int isa; + const char * const *cp0_names; + const struct mips_cp0sel_name *cp0sel_names; + unsigned int cp0sel_names_len; + const char * const *hwr_names; +}; + +#define bfd_mach_mips3000 3000 +#define bfd_mach_mips3900 3900 +#define bfd_mach_mips4000 4000 +#define bfd_mach_mips4010 4010 +#define bfd_mach_mips4100 4100 +#define bfd_mach_mips4111 4111 +#define bfd_mach_mips4120 4120 +#define bfd_mach_mips4300 4300 +#define bfd_mach_mips4400 4400 +#define bfd_mach_mips4600 4600 +#define bfd_mach_mips4650 4650 +#define bfd_mach_mips5000 5000 +#define bfd_mach_mips5400 5400 +#define bfd_mach_mips5500 5500 +#define bfd_mach_mips6000 6000 +#define bfd_mach_mips7000 7000 +#define bfd_mach_mips8000 8000 +#define bfd_mach_mips10000 10000 +#define bfd_mach_mips12000 12000 +#define bfd_mach_mips16 16 +#define bfd_mach_mips5 5 +#define bfd_mach_mips_sb1 12310201 /* octal 'SB', 01 */ +#define bfd_mach_mipsisa32 32 +#define bfd_mach_mipsisa32r2 33 +#define bfd_mach_mipsisa64 64 +#define bfd_mach_mipsisa64r2 65 + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +const struct mips_arch_choice mips_arch_choices[] = { + { "numeric", 0, 0, 0, 0, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + + { "r3000", 1, bfd_mach_mips3000, CPU_R3000, ISA_MIPS1, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "r3900", 1, bfd_mach_mips3900, CPU_R3900, ISA_MIPS1, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "r4000", 1, bfd_mach_mips4000, CPU_R4000, ISA_MIPS3, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "r4010", 1, bfd_mach_mips4010, CPU_R4010, ISA_MIPS2, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "vr4100", 1, bfd_mach_mips4100, CPU_VR4100, ISA_MIPS3, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "vr4111", 1, bfd_mach_mips4111, CPU_R4111, ISA_MIPS3, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "vr4120", 1, bfd_mach_mips4120, CPU_VR4120, ISA_MIPS3, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "r4300", 1, bfd_mach_mips4300, CPU_R4300, ISA_MIPS3, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "r4400", 1, bfd_mach_mips4400, CPU_R4400, ISA_MIPS3, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "r4600", 1, bfd_mach_mips4600, CPU_R4600, ISA_MIPS3, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "r4650", 1, bfd_mach_mips4650, CPU_R4650, ISA_MIPS3, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "r5000", 1, bfd_mach_mips5000, CPU_R5000, ISA_MIPS4, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "vr5400", 1, bfd_mach_mips5400, CPU_VR5400, ISA_MIPS4, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "vr5500", 1, bfd_mach_mips5500, CPU_VR5500, ISA_MIPS4, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "r6000", 1, bfd_mach_mips6000, CPU_R6000, ISA_MIPS2, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "rm7000", 1, bfd_mach_mips7000, CPU_RM7000, ISA_MIPS4, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "rm9000", 1, bfd_mach_mips7000, CPU_RM7000, ISA_MIPS4, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "r8000", 1, bfd_mach_mips8000, CPU_R8000, ISA_MIPS4, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "r10000", 1, bfd_mach_mips10000, CPU_R10000, ISA_MIPS4, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "r12000", 1, bfd_mach_mips12000, CPU_R12000, ISA_MIPS4, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "mips5", 1, bfd_mach_mips5, CPU_MIPS5, ISA_MIPS5, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + + /* For stock MIPS32, disassemble all applicable MIPS-specified ASEs. + Note that MIPS-3D and MDMX are not applicable to MIPS32. (See + _MIPS32 Architecture For Programmers Volume I: Introduction to the + MIPS32 Architecture_ (MIPS Document Number MD00082, Revision 0.95), + page 1. */ + { "mips32", 1, bfd_mach_mipsisa32, CPU_MIPS32, + ISA_MIPS32 | INSN_MIPS16, + mips_cp0_names_mips3264, + mips_cp0sel_names_mips3264, ARRAY_SIZE (mips_cp0sel_names_mips3264), + mips_hwr_names_numeric }, + + { "mips32r2", 1, bfd_mach_mipsisa32r2, CPU_MIPS32R2, + ISA_MIPS32R2 | INSN_MIPS16, + mips_cp0_names_mips3264r2, + mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2), + mips_hwr_names_mips3264r2 }, + + /* For stock MIPS64, disassemble all applicable MIPS-specified ASEs. */ + { "mips64", 1, bfd_mach_mipsisa64, CPU_MIPS64, + ISA_MIPS64 | INSN_MIPS16 | INSN_MIPS3D | INSN_MDMX, + mips_cp0_names_mips3264, + mips_cp0sel_names_mips3264, ARRAY_SIZE (mips_cp0sel_names_mips3264), + mips_hwr_names_numeric }, + + { "mips64r2", 1, bfd_mach_mipsisa64r2, CPU_MIPS64R2, + ISA_MIPS64R2 | INSN_MIPS16 | INSN_MIPS3D | INSN_MDMX, + mips_cp0_names_mips3264r2, + mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2), + mips_hwr_names_mips3264r2 }, + + { "sb1", 1, bfd_mach_mips_sb1, CPU_SB1, + ISA_MIPS64 | INSN_MIPS3D | INSN_SB1, + mips_cp0_names_sb1, + mips_cp0sel_names_sb1, ARRAY_SIZE (mips_cp0sel_names_sb1), + mips_hwr_names_numeric }, + + /* This entry, mips16, is here only for ISA/processor selection; do + not print its name. */ + { "", 1, bfd_mach_mips16, CPU_MIPS16, ISA_MIPS3 | INSN_MIPS16, + mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, +}; + +/* ISA and processor type to disassemble for, and register names to use. + set_default_mips_dis_options and parse_mips_dis_options fill in these + values. */ +static int mips_processor; +static int mips_isa; +static const char * const *mips_gpr_names; +static const char * const *mips_fpr_names; +static const char * const *mips_cp0_names; +static const struct mips_cp0sel_name *mips_cp0sel_names; +static int mips_cp0sel_names_len; +static const char * const *mips_hwr_names; + +static const struct mips_abi_choice *choose_abi_by_name + PARAMS ((const char *, unsigned int)); +static const struct mips_arch_choice *choose_arch_by_name + PARAMS ((const char *, unsigned int)); +static const struct mips_arch_choice *choose_arch_by_number + PARAMS ((unsigned long)); +static const struct mips_cp0sel_name *lookup_mips_cp0sel_name + PARAMS ((const struct mips_cp0sel_name *, unsigned int, unsigned int, + unsigned int)); + +static const struct mips_abi_choice * +choose_abi_by_name (name, namelen) + const char *name; + unsigned int namelen; +{ + const struct mips_abi_choice *c; + unsigned int i; + + for (i = 0, c = NULL; i < ARRAY_SIZE (mips_abi_choices) && c == NULL; i++) + { + if (strncmp (mips_abi_choices[i].name, name, namelen) == 0 + && strlen (mips_abi_choices[i].name) == namelen) + c = &mips_abi_choices[i]; + } + return c; +} + +static const struct mips_arch_choice * +choose_arch_by_name (name, namelen) + const char *name; + unsigned int namelen; +{ + const struct mips_arch_choice *c = NULL; + unsigned int i; + + for (i = 0, c = NULL; i < ARRAY_SIZE (mips_arch_choices) && c == NULL; i++) + { + if (strncmp (mips_arch_choices[i].name, name, namelen) == 0 + && strlen (mips_arch_choices[i].name) == namelen) + c = &mips_arch_choices[i]; + } + return c; +} + +static const struct mips_arch_choice * +choose_arch_by_number (mach) + unsigned long mach; +{ + static unsigned long hint_bfd_mach; + static const struct mips_arch_choice *hint_arch_choice; + const struct mips_arch_choice *c; + unsigned int i; + + /* We optimize this because even if the user specifies no + flags, this will be done for every instruction! */ + if (hint_bfd_mach == mach + && hint_arch_choice != NULL + && hint_arch_choice->bfd_mach == hint_bfd_mach) + return hint_arch_choice; + + for (i = 0, c = NULL; i < ARRAY_SIZE (mips_arch_choices) && c == NULL; i++) + { + if (mips_arch_choices[i].bfd_mach_valid + && mips_arch_choices[i].bfd_mach == mach) + { + c = &mips_arch_choices[i]; + hint_bfd_mach = mach; + hint_arch_choice = c; + } + } + return c; +} + +void +set_default_mips_dis_options (info) + struct disassemble_info *info; +{ + const struct mips_arch_choice *chosen_arch; + + /* Defaults: mipsIII/r3000 (?!), (o)32-style ("oldabi") GPR names, + and numeric FPR, CP0 register, and HWR names. */ + mips_isa = ISA_MIPS3; + mips_processor = CPU_R3000; + mips_gpr_names = mips_gpr_names_oldabi; + mips_fpr_names = mips_fpr_names_numeric; + mips_cp0_names = mips_cp0_names_numeric; + mips_cp0sel_names = NULL; + mips_cp0sel_names_len = 0; + mips_hwr_names = mips_hwr_names_numeric; + + /* If an ELF "newabi" binary, use the n32/(n)64 GPR names. */ +#if 0 + if (info->flavour == bfd_target_elf_flavour && info->section != NULL) + { + Elf_Internal_Ehdr *header; + + header = elf_elfheader (info->section->owner); + if (is_newabi (header)) + mips_gpr_names = mips_gpr_names_newabi; + } +#endif + + /* Set ISA, architecture, and cp0 register names as best we can. */ +#if ! SYMTAB_AVAILABLE && 0 + /* This is running out on a target machine, not in a host tool. + FIXME: Where does mips_target_info come from? */ + target_processor = mips_target_info.processor; + mips_isa = mips_target_info.isa; +#else + chosen_arch = choose_arch_by_number (info->mach); + if (chosen_arch != NULL) + { + mips_processor = chosen_arch->processor; + mips_isa = chosen_arch->isa; + mips_cp0_names = chosen_arch->cp0_names; + mips_cp0sel_names = chosen_arch->cp0sel_names; + mips_cp0sel_names_len = chosen_arch->cp0sel_names_len; + mips_hwr_names = chosen_arch->hwr_names; + } +#endif +} + +void +parse_mips_dis_option (option, len) + const char *option; + unsigned int len; +{ + unsigned int i, optionlen, vallen; + const char *val; + const struct mips_abi_choice *chosen_abi; + const struct mips_arch_choice *chosen_arch; + + /* Look for the = that delimits the end of the option name. */ + for (i = 0; i < len; i++) + { + if (option[i] == '=') + break; + } + if (i == 0) /* Invalid option: no name before '='. */ + return; + if (i == len) /* Invalid option: no '='. */ + return; + if (i == (len - 1)) /* Invalid option: no value after '='. */ + return; + + optionlen = i; + val = option + (optionlen + 1); + vallen = len - (optionlen + 1); + + if (strncmp("gpr-names", option, optionlen) == 0 + && strlen("gpr-names") == optionlen) + { + chosen_abi = choose_abi_by_name (val, vallen); + if (chosen_abi != NULL) + mips_gpr_names = chosen_abi->gpr_names; + return; + } + + if (strncmp("fpr-names", option, optionlen) == 0 + && strlen("fpr-names") == optionlen) + { + chosen_abi = choose_abi_by_name (val, vallen); + if (chosen_abi != NULL) + mips_fpr_names = chosen_abi->fpr_names; + return; + } + + if (strncmp("cp0-names", option, optionlen) == 0 + && strlen("cp0-names") == optionlen) + { + chosen_arch = choose_arch_by_name (val, vallen); + if (chosen_arch != NULL) + { + mips_cp0_names = chosen_arch->cp0_names; + mips_cp0sel_names = chosen_arch->cp0sel_names; + mips_cp0sel_names_len = chosen_arch->cp0sel_names_len; + } + return; + } + + if (strncmp("hwr-names", option, optionlen) == 0 + && strlen("hwr-names") == optionlen) + { + chosen_arch = choose_arch_by_name (val, vallen); + if (chosen_arch != NULL) + mips_hwr_names = chosen_arch->hwr_names; + return; + } + + if (strncmp("reg-names", option, optionlen) == 0 + && strlen("reg-names") == optionlen) + { + /* We check both ABI and ARCH here unconditionally, so + that "numeric" will do the desirable thing: select + numeric register names for all registers. Other than + that, a given name probably won't match both. */ + chosen_abi = choose_abi_by_name (val, vallen); + if (chosen_abi != NULL) + { + mips_gpr_names = chosen_abi->gpr_names; + mips_fpr_names = chosen_abi->fpr_names; + } + chosen_arch = choose_arch_by_name (val, vallen); + if (chosen_arch != NULL) + { + mips_cp0_names = chosen_arch->cp0_names; + mips_cp0sel_names = chosen_arch->cp0sel_names; + mips_cp0sel_names_len = chosen_arch->cp0sel_names_len; + mips_hwr_names = chosen_arch->hwr_names; + } + return; + } + + /* Invalid option. */ +} + +void +parse_mips_dis_options (options) + const char *options; +{ + const char *option_end; + + if (options == NULL) + return; + + while (*options != '\0') + { + /* Skip empty options. */ + if (*options == ',') + { + options++; + continue; + } + + /* We know that *options is neither NUL or a comma. */ + option_end = options + 1; + while (*option_end != ',' && *option_end != '\0') + option_end++; + + parse_mips_dis_option (options, option_end - options); + + /* Go on to the next one. If option_end points to a comma, it + will be skipped above. */ + options = option_end; + } +} + +static const struct mips_cp0sel_name * +lookup_mips_cp0sel_name(names, len, cp0reg, sel) + const struct mips_cp0sel_name *names; + unsigned int len, cp0reg, sel; +{ + unsigned int i; + + for (i = 0; i < len; i++) + if (names[i].cp0reg == cp0reg && names[i].sel == sel) + return &names[i]; + return NULL; +} + +/* Print insn arguments for 32/64-bit code. */ + +static void +print_insn_args (d, l, pc, info) + const char *d; + register unsigned long int l; + bfd_vma pc; + struct disassemble_info *info; +{ + int op, delta; + unsigned int lsb, msb, msbd; + + lsb = 0; + + for (; *d != '\0'; d++) + { + switch (*d) + { + case ',': + case '(': + case ')': + case '[': + case ']': + (*info->fprintf_func) (info->stream, "%c", *d); + break; + + case '+': + /* Extension character; switch for second char. */ + d++; + switch (*d) + { + case '\0': + /* xgettext:c-format */ + (*info->fprintf_func) (info->stream, + _("# internal error, incomplete extension sequence (+)")); + return; + + case 'A': + lsb = (l >> OP_SH_SHAMT) & OP_MASK_SHAMT; + (*info->fprintf_func) (info->stream, "0x%x", lsb); + break; + + case 'B': + msb = (l >> OP_SH_INSMSB) & OP_MASK_INSMSB; + (*info->fprintf_func) (info->stream, "0x%x", msb - lsb + 1); + break; + + case 'C': + case 'H': + msbd = (l >> OP_SH_EXTMSBD) & OP_MASK_EXTMSBD; + (*info->fprintf_func) (info->stream, "0x%x", msbd + 1); + break; + + case 'D': + { + const struct mips_cp0sel_name *n; + unsigned int cp0reg, sel; + + cp0reg = (l >> OP_SH_RD) & OP_MASK_RD; + sel = (l >> OP_SH_SEL) & OP_MASK_SEL; + + /* CP0 register including 'sel' code for mtcN (et al.), to be + printed textually if known. If not known, print both + CP0 register name and sel numerically since CP0 register + with sel 0 may have a name unrelated to register being + printed. */ + n = lookup_mips_cp0sel_name(mips_cp0sel_names, + mips_cp0sel_names_len, cp0reg, sel); + if (n != NULL) + (*info->fprintf_func) (info->stream, "%s", n->name); + else + (*info->fprintf_func) (info->stream, "$%d,%d", cp0reg, sel); + break; + } + + case 'E': + lsb = ((l >> OP_SH_SHAMT) & OP_MASK_SHAMT) + 32; + (*info->fprintf_func) (info->stream, "0x%x", lsb); + break; + + case 'F': + msb = ((l >> OP_SH_INSMSB) & OP_MASK_INSMSB) + 32; + (*info->fprintf_func) (info->stream, "0x%x", msb - lsb + 1); + break; + + case 'G': + msbd = ((l >> OP_SH_EXTMSBD) & OP_MASK_EXTMSBD) + 32; + (*info->fprintf_func) (info->stream, "0x%x", msbd + 1); + break; + + default: + /* xgettext:c-format */ + (*info->fprintf_func) (info->stream, + _("# internal error, undefined extension sequence (+%c)"), + *d); + return; + } + break; + + case 's': + case 'b': + case 'r': + case 'v': + (*info->fprintf_func) (info->stream, "%s", + mips_gpr_names[(l >> OP_SH_RS) & OP_MASK_RS]); + break; + + case 't': + case 'w': + (*info->fprintf_func) (info->stream, "%s", + mips_gpr_names[(l >> OP_SH_RT) & OP_MASK_RT]); + break; + + case 'i': + case 'u': + (*info->fprintf_func) (info->stream, "0x%x", + (l >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE); + break; + + case 'j': /* Same as i, but sign-extended. */ + case 'o': + delta = (l >> OP_SH_DELTA) & OP_MASK_DELTA; + if (delta & 0x8000) + delta |= ~0xffff; + (*info->fprintf_func) (info->stream, "%d", + delta); + break; + + case 'h': + (*info->fprintf_func) (info->stream, "0x%x", + (unsigned int) ((l >> OP_SH_PREFX) + & OP_MASK_PREFX)); + break; + + case 'k': + (*info->fprintf_func) (info->stream, "0x%x", + (unsigned int) ((l >> OP_SH_CACHE) + & OP_MASK_CACHE)); + break; + + case 'a': + info->target = (((pc + 4) & ~(bfd_vma) 0x0fffffff) + | (((l >> OP_SH_TARGET) & OP_MASK_TARGET) << 2)); + (*info->print_address_func) (info->target, info); + break; + + case 'p': + /* Sign extend the displacement. */ + delta = (l >> OP_SH_DELTA) & OP_MASK_DELTA; + if (delta & 0x8000) + delta |= ~0xffff; + info->target = (delta << 2) + pc + INSNLEN; + (*info->print_address_func) (info->target, info); + break; + + case 'd': + (*info->fprintf_func) (info->stream, "%s", + mips_gpr_names[(l >> OP_SH_RD) & OP_MASK_RD]); + break; + + case 'U': + { + /* First check for both rd and rt being equal. */ + unsigned int reg = (l >> OP_SH_RD) & OP_MASK_RD; + if (reg == ((l >> OP_SH_RT) & OP_MASK_RT)) + (*info->fprintf_func) (info->stream, "%s", + mips_gpr_names[reg]); + else + { + /* If one is zero use the other. */ + if (reg == 0) + (*info->fprintf_func) (info->stream, "%s", + mips_gpr_names[(l >> OP_SH_RT) & OP_MASK_RT]); + else if (((l >> OP_SH_RT) & OP_MASK_RT) == 0) + (*info->fprintf_func) (info->stream, "%s", + mips_gpr_names[reg]); + else /* Bogus, result depends on processor. */ + (*info->fprintf_func) (info->stream, "%s or %s", + mips_gpr_names[reg], + mips_gpr_names[(l >> OP_SH_RT) & OP_MASK_RT]); + } + } + break; + + case 'z': + (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[0]); + break; + + case '<': + (*info->fprintf_func) (info->stream, "0x%x", + (l >> OP_SH_SHAMT) & OP_MASK_SHAMT); + break; + + case 'c': + (*info->fprintf_func) (info->stream, "0x%x", + (l >> OP_SH_CODE) & OP_MASK_CODE); + break; + + case 'q': + (*info->fprintf_func) (info->stream, "0x%x", + (l >> OP_SH_CODE2) & OP_MASK_CODE2); + break; + + case 'C': + (*info->fprintf_func) (info->stream, "0x%x", + (l >> OP_SH_COPZ) & OP_MASK_COPZ); + break; + + case 'B': + (*info->fprintf_func) (info->stream, "0x%x", + (l >> OP_SH_CODE20) & OP_MASK_CODE20); + break; + + case 'J': + (*info->fprintf_func) (info->stream, "0x%x", + (l >> OP_SH_CODE19) & OP_MASK_CODE19); + break; + + case 'S': + case 'V': + (*info->fprintf_func) (info->stream, "%s", + mips_fpr_names[(l >> OP_SH_FS) & OP_MASK_FS]); + break; + + case 'T': + case 'W': + (*info->fprintf_func) (info->stream, "%s", + mips_fpr_names[(l >> OP_SH_FT) & OP_MASK_FT]); + break; + + case 'D': + (*info->fprintf_func) (info->stream, "%s", + mips_fpr_names[(l >> OP_SH_FD) & OP_MASK_FD]); + break; + + case 'R': + (*info->fprintf_func) (info->stream, "%s", + mips_fpr_names[(l >> OP_SH_FR) & OP_MASK_FR]); + break; + + case 'E': + /* Coprocessor register for lwcN instructions, et al. + + Note that there is no load/store cp0 instructions, and + that FPU (cp1) instructions disassemble this field using + 'T' format. Therefore, until we gain understanding of + cp2 register names, we can simply print the register + numbers. */ + (*info->fprintf_func) (info->stream, "$%d", + (l >> OP_SH_RT) & OP_MASK_RT); + break; + + case 'G': + /* Coprocessor register for mtcN instructions, et al. Note + that FPU (cp1) instructions disassemble this field using + 'S' format. Therefore, we only need to worry about cp0, + cp2, and cp3. */ + op = (l >> OP_SH_OP) & OP_MASK_OP; + if (op == OP_OP_COP0) + (*info->fprintf_func) (info->stream, "%s", + mips_cp0_names[(l >> OP_SH_RD) & OP_MASK_RD]); + else + (*info->fprintf_func) (info->stream, "$%d", + (l >> OP_SH_RD) & OP_MASK_RD); + break; + + case 'K': + (*info->fprintf_func) (info->stream, "%s", + mips_hwr_names[(l >> OP_SH_RD) & OP_MASK_RD]); + break; + + case 'N': + (*info->fprintf_func) (info->stream, "$fcc%d", + (l >> OP_SH_BCC) & OP_MASK_BCC); + break; + + case 'M': + (*info->fprintf_func) (info->stream, "$fcc%d", + (l >> OP_SH_CCC) & OP_MASK_CCC); + break; + + case 'P': + (*info->fprintf_func) (info->stream, "%d", + (l >> OP_SH_PERFREG) & OP_MASK_PERFREG); + break; + + case 'e': + (*info->fprintf_func) (info->stream, "%d", + (l >> OP_SH_VECBYTE) & OP_MASK_VECBYTE); + break; + + case '%': + (*info->fprintf_func) (info->stream, "%d", + (l >> OP_SH_VECALIGN) & OP_MASK_VECALIGN); + break; + + case 'H': + (*info->fprintf_func) (info->stream, "%d", + (l >> OP_SH_SEL) & OP_MASK_SEL); + break; + + case 'O': + (*info->fprintf_func) (info->stream, "%d", + (l >> OP_SH_ALN) & OP_MASK_ALN); + break; + + case 'Q': + { + unsigned int vsel = (l >> OP_SH_VSEL) & OP_MASK_VSEL; + if ((vsel & 0x10) == 0) + { + int fmt; + vsel &= 0x0f; + for (fmt = 0; fmt < 3; fmt++, vsel >>= 1) + if ((vsel & 1) == 0) + break; + (*info->fprintf_func) (info->stream, "$v%d[%d]", + (l >> OP_SH_FT) & OP_MASK_FT, + vsel >> 1); + } + else if ((vsel & 0x08) == 0) + { + (*info->fprintf_func) (info->stream, "$v%d", + (l >> OP_SH_FT) & OP_MASK_FT); + } + else + { + (*info->fprintf_func) (info->stream, "0x%x", + (l >> OP_SH_FT) & OP_MASK_FT); + } + } + break; + + case 'X': + (*info->fprintf_func) (info->stream, "$v%d", + (l >> OP_SH_FD) & OP_MASK_FD); + break; + + case 'Y': + (*info->fprintf_func) (info->stream, "$v%d", + (l >> OP_SH_FS) & OP_MASK_FS); + break; + + case 'Z': + (*info->fprintf_func) (info->stream, "$v%d", + (l >> OP_SH_FT) & OP_MASK_FT); + break; + + default: + /* xgettext:c-format */ + (*info->fprintf_func) (info->stream, + _("# internal error, undefined modifier(%c)"), + *d); + return; + } + } +} + +/* Check if the object uses NewABI conventions. */ +#if 0 +static int +is_newabi (header) + Elf_Internal_Ehdr *header; +{ + /* There are no old-style ABIs which use 64-bit ELF. */ + if (header->e_ident[EI_CLASS] == ELFCLASS64) + return 1; + + /* If a 32-bit ELF file, n32 is a new-style ABI. */ + if ((header->e_flags & EF_MIPS_ABI2) != 0) + return 1; + + return 0; +} +#endif + +/* Print the mips instruction at address MEMADDR in debugged memory, + on using INFO. Returns length of the instruction, in bytes, which is + always INSNLEN. BIGENDIAN must be 1 if this is big-endian code, 0 if + this is little-endian code. */ + +static int +print_insn_mips (memaddr, word, info) + bfd_vma memaddr; + unsigned long int word; + struct disassemble_info *info; +{ + register const struct mips_opcode *op; + static bfd_boolean init = 0; + static const struct mips_opcode *mips_hash[OP_MASK_OP + 1]; + + /* Build a hash table to shorten the search time. */ + if (! init) + { + unsigned int i; + + for (i = 0; i <= OP_MASK_OP; i++) + { + for (op = mips_opcodes; op < &mips_opcodes[NUMOPCODES]; op++) + { + if (op->pinfo == INSN_MACRO) + continue; + if (i == ((op->match >> OP_SH_OP) & OP_MASK_OP)) + { + mips_hash[i] = op; + break; + } + } + } + + init = 1; + } + + info->bytes_per_chunk = INSNLEN; + info->display_endian = info->endian; + info->insn_info_valid = 1; + info->branch_delay_insns = 0; + info->data_size = 0; + info->insn_type = dis_nonbranch; + info->target = 0; + info->target2 = 0; + + op = mips_hash[(word >> OP_SH_OP) & OP_MASK_OP]; + if (op != NULL) + { + for (; op < &mips_opcodes[NUMOPCODES]; op++) + { + if (op->pinfo != INSN_MACRO && (word & op->mask) == op->match) + { + register const char *d; + + /* We always allow to disassemble the jalx instruction. */ + if (! OPCODE_IS_MEMBER (op, mips_isa, mips_processor) + && strcmp (op->name, "jalx")) + continue; + + /* Figure out instruction type and branch delay information. */ + if ((op->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0) + { + if ((info->insn_type & INSN_WRITE_GPR_31) != 0) + info->insn_type = dis_jsr; + else + info->insn_type = dis_branch; + info->branch_delay_insns = 1; + } + else if ((op->pinfo & (INSN_COND_BRANCH_DELAY + | INSN_COND_BRANCH_LIKELY)) != 0) + { + if ((info->insn_type & INSN_WRITE_GPR_31) != 0) + info->insn_type = dis_condjsr; + else + info->insn_type = dis_condbranch; + info->branch_delay_insns = 1; + } + else if ((op->pinfo & (INSN_STORE_MEMORY + | INSN_LOAD_MEMORY_DELAY)) != 0) + info->insn_type = dis_dref; + + (*info->fprintf_func) (info->stream, "%s", op->name); + + d = op->args; + if (d != NULL && *d != '\0') + { + (*info->fprintf_func) (info->stream, "\t"); + print_insn_args (d, word, memaddr, info); + } + + return INSNLEN; + } + } + } + + /* Handle undefined instructions. */ + info->insn_type = dis_noninsn; + (*info->fprintf_func) (info->stream, "0x%x", word); + return INSNLEN; +} + +/* In an environment where we do not know the symbol type of the + instruction we are forced to assume that the low order bit of the + instructions' address may mark it as a mips16 instruction. If we + are single stepping, or the pc is within the disassembled function, + this works. Otherwise, we need a clue. Sometimes. */ + +static int +_print_insn_mips (memaddr, info, endianness) + bfd_vma memaddr; + struct disassemble_info *info; + enum bfd_endian endianness; +{ + bfd_byte buffer[INSNLEN]; + int status; + + set_default_mips_dis_options (info); + parse_mips_dis_options (info->disassembler_options); + +#if 0 +#if 1 + /* FIXME: If odd address, this is CLEARLY a mips 16 instruction. */ + /* Only a few tools will work this way. */ + if (memaddr & 0x01) + return print_insn_mips16 (memaddr, info); +#endif + +#if SYMTAB_AVAILABLE + if (info->mach == bfd_mach_mips16 + || (info->flavour == bfd_target_elf_flavour + && info->symbols != NULL + && ((*(elf_symbol_type **) info->symbols)->internal_elf_sym.st_other + == STO_MIPS16))) + return print_insn_mips16 (memaddr, info); +#endif +#endif + + status = (*info->read_memory_func) (memaddr, buffer, INSNLEN, info); + if (status == 0) + { + unsigned long insn; + + if (endianness == BFD_ENDIAN_BIG) + insn = (unsigned long) bfd_getb32 (buffer); + else + insn = (unsigned long) bfd_getl32 (buffer); + + return print_insn_mips (memaddr, insn, info); + } + else + { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } +} + +int +print_insn_big_mips (memaddr, info) + bfd_vma memaddr; + struct disassemble_info *info; +{ + return _print_insn_mips (memaddr, info, BFD_ENDIAN_BIG); +} + +int +print_insn_little_mips (memaddr, info) + bfd_vma memaddr; + struct disassemble_info *info; +{ + return _print_insn_mips (memaddr, info, BFD_ENDIAN_LITTLE); +} + +/* Disassemble mips16 instructions. */ +#if 0 +static int +print_insn_mips16 (memaddr, info) + bfd_vma memaddr; + struct disassemble_info *info; +{ + int status; + bfd_byte buffer[2]; + int length; + int insn; + bfd_boolean use_extend; + int extend = 0; + const struct mips_opcode *op, *opend; + + info->bytes_per_chunk = 2; + info->display_endian = info->endian; + info->insn_info_valid = 1; + info->branch_delay_insns = 0; + info->data_size = 0; + info->insn_type = dis_nonbranch; + info->target = 0; + info->target2 = 0; + + status = (*info->read_memory_func) (memaddr, buffer, 2, info); + if (status != 0) + { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } + + length = 2; + + if (info->endian == BFD_ENDIAN_BIG) + insn = bfd_getb16 (buffer); + else + insn = bfd_getl16 (buffer); + + /* Handle the extend opcode specially. */ + use_extend = FALSE; + if ((insn & 0xf800) == 0xf000) + { + use_extend = TRUE; + extend = insn & 0x7ff; + + memaddr += 2; + + status = (*info->read_memory_func) (memaddr, buffer, 2, info); + if (status != 0) + { + (*info->fprintf_func) (info->stream, "extend 0x%x", + (unsigned int) extend); + (*info->memory_error_func) (status, memaddr, info); + return -1; + } + + if (info->endian == BFD_ENDIAN_BIG) + insn = bfd_getb16 (buffer); + else + insn = bfd_getl16 (buffer); + + /* Check for an extend opcode followed by an extend opcode. */ + if ((insn & 0xf800) == 0xf000) + { + (*info->fprintf_func) (info->stream, "extend 0x%x", + (unsigned int) extend); + info->insn_type = dis_noninsn; + return length; + } + + length += 2; + } + + /* FIXME: Should probably use a hash table on the major opcode here. */ + + opend = mips16_opcodes + bfd_mips16_num_opcodes; + for (op = mips16_opcodes; op < opend; op++) + { + if (op->pinfo != INSN_MACRO && (insn & op->mask) == op->match) + { + const char *s; + + if (strchr (op->args, 'a') != NULL) + { + if (use_extend) + { + (*info->fprintf_func) (info->stream, "extend 0x%x", + (unsigned int) extend); + info->insn_type = dis_noninsn; + return length - 2; + } + + use_extend = FALSE; + + memaddr += 2; + + status = (*info->read_memory_func) (memaddr, buffer, 2, + info); + if (status == 0) + { + use_extend = TRUE; + if (info->endian == BFD_ENDIAN_BIG) + extend = bfd_getb16 (buffer); + else + extend = bfd_getl16 (buffer); + length += 2; + } + } + + (*info->fprintf_func) (info->stream, "%s", op->name); + if (op->args[0] != '\0') + (*info->fprintf_func) (info->stream, "\t"); + + for (s = op->args; *s != '\0'; s++) + { + if (*s == ',' + && s[1] == 'w' + && (((insn >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX) + == ((insn >> MIPS16OP_SH_RY) & MIPS16OP_MASK_RY))) + { + /* Skip the register and the comma. */ + ++s; + continue; + } + if (*s == ',' + && s[1] == 'v' + && (((insn >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ) + == ((insn >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX))) + { + /* Skip the register and the comma. */ + ++s; + continue; + } + print_mips16_insn_arg (*s, op, insn, use_extend, extend, memaddr, + info); + } + + if ((op->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0) + { + info->branch_delay_insns = 1; + if (info->insn_type != dis_jsr) + info->insn_type = dis_branch; + } + + return length; + } + } + + if (use_extend) + (*info->fprintf_func) (info->stream, "0x%x", extend | 0xf000); + (*info->fprintf_func) (info->stream, "0x%x", insn); + info->insn_type = dis_noninsn; + + return length; +} + +/* Disassemble an operand for a mips16 instruction. */ + +static void +print_mips16_insn_arg (type, op, l, use_extend, extend, memaddr, info) + char type; + const struct mips_opcode *op; + int l; + bfd_boolean use_extend; + int extend; + bfd_vma memaddr; + struct disassemble_info *info; +{ + switch (type) + { + case ',': + case '(': + case ')': + (*info->fprintf_func) (info->stream, "%c", type); + break; + + case 'y': + case 'w': + (*info->fprintf_func) (info->stream, "%s", + mips16_reg_names[((l >> MIPS16OP_SH_RY) + & MIPS16OP_MASK_RY)]); + break; + + case 'x': + case 'v': + (*info->fprintf_func) (info->stream, "%s", + mips16_reg_names[((l >> MIPS16OP_SH_RX) + & MIPS16OP_MASK_RX)]); + break; + + case 'z': + (*info->fprintf_func) (info->stream, "%s", + mips16_reg_names[((l >> MIPS16OP_SH_RZ) + & MIPS16OP_MASK_RZ)]); + break; + + case 'Z': + (*info->fprintf_func) (info->stream, "%s", + mips16_reg_names[((l >> MIPS16OP_SH_MOVE32Z) + & MIPS16OP_MASK_MOVE32Z)]); + break; + + case '0': + (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[0]); + break; + + case 'S': + (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[29]); + break; + + case 'P': + (*info->fprintf_func) (info->stream, "$pc"); + break; + + case 'R': + (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[31]); + break; + + case 'X': + (*info->fprintf_func) (info->stream, "%s", + mips_gpr_names[((l >> MIPS16OP_SH_REGR32) + & MIPS16OP_MASK_REGR32)]); + break; + + case 'Y': + (*info->fprintf_func) (info->stream, "%s", + mips_gpr_names[MIPS16OP_EXTRACT_REG32R (l)]); + break; + + case '<': + case '>': + case '[': + case ']': + case '4': + case '5': + case 'H': + case 'W': + case 'D': + case 'j': + case '6': + case '8': + case 'V': + case 'C': + case 'U': + case 'k': + case 'K': + case 'p': + case 'q': + case 'A': + case 'B': + case 'E': + { + int immed, nbits, shift, signedp, extbits, pcrel, extu, branch; + + shift = 0; + signedp = 0; + extbits = 16; + pcrel = 0; + extu = 0; + branch = 0; + switch (type) + { + case '<': + nbits = 3; + immed = (l >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ; + extbits = 5; + extu = 1; + break; + case '>': + nbits = 3; + immed = (l >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX; + extbits = 5; + extu = 1; + break; + case '[': + nbits = 3; + immed = (l >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ; + extbits = 6; + extu = 1; + break; + case ']': + nbits = 3; + immed = (l >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX; + extbits = 6; + extu = 1; + break; + case '4': + nbits = 4; + immed = (l >> MIPS16OP_SH_IMM4) & MIPS16OP_MASK_IMM4; + signedp = 1; + extbits = 15; + break; + case '5': + nbits = 5; + immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5; + info->insn_type = dis_dref; + info->data_size = 1; + break; + case 'H': + nbits = 5; + shift = 1; + immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5; + info->insn_type = dis_dref; + info->data_size = 2; + break; + case 'W': + nbits = 5; + shift = 2; + immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5; + if ((op->pinfo & MIPS16_INSN_READ_PC) == 0 + && (op->pinfo & MIPS16_INSN_READ_SP) == 0) + { + info->insn_type = dis_dref; + info->data_size = 4; + } + break; + case 'D': + nbits = 5; + shift = 3; + immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5; + info->insn_type = dis_dref; + info->data_size = 8; + break; + case 'j': + nbits = 5; + immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5; + signedp = 1; + break; + case '6': + nbits = 6; + immed = (l >> MIPS16OP_SH_IMM6) & MIPS16OP_MASK_IMM6; + break; + case '8': + nbits = 8; + immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8; + break; + case 'V': + nbits = 8; + shift = 2; + immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8; + /* FIXME: This might be lw, or it might be addiu to $sp or + $pc. We assume it's load. */ + info->insn_type = dis_dref; + info->data_size = 4; + break; + case 'C': + nbits = 8; + shift = 3; + immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8; + info->insn_type = dis_dref; + info->data_size = 8; + break; + case 'U': + nbits = 8; + immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8; + extu = 1; + break; + case 'k': + nbits = 8; + immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8; + signedp = 1; + break; + case 'K': + nbits = 8; + shift = 3; + immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8; + signedp = 1; + break; + case 'p': + nbits = 8; + immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8; + signedp = 1; + pcrel = 1; + branch = 1; + info->insn_type = dis_condbranch; + break; + case 'q': + nbits = 11; + immed = (l >> MIPS16OP_SH_IMM11) & MIPS16OP_MASK_IMM11; + signedp = 1; + pcrel = 1; + branch = 1; + info->insn_type = dis_branch; + break; + case 'A': + nbits = 8; + shift = 2; + immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8; + pcrel = 1; + /* FIXME: This can be lw or la. We assume it is lw. */ + info->insn_type = dis_dref; + info->data_size = 4; + break; + case 'B': + nbits = 5; + shift = 3; + immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5; + pcrel = 1; + info->insn_type = dis_dref; + info->data_size = 8; + break; + case 'E': + nbits = 5; + shift = 2; + immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5; + pcrel = 1; + break; + default: + abort (); + } + + if (! use_extend) + { + if (signedp && immed >= (1 << (nbits - 1))) + immed -= 1 << nbits; + immed <<= shift; + if ((type == '<' || type == '>' || type == '[' || type == ']') + && immed == 0) + immed = 8; + } + else + { + if (extbits == 16) + immed |= ((extend & 0x1f) << 11) | (extend & 0x7e0); + else if (extbits == 15) + immed |= ((extend & 0xf) << 11) | (extend & 0x7f0); + else + immed = ((extend >> 6) & 0x1f) | (extend & 0x20); + immed &= (1 << extbits) - 1; + if (! extu && immed >= (1 << (extbits - 1))) + immed -= 1 << extbits; + } + + if (! pcrel) + (*info->fprintf_func) (info->stream, "%d", immed); + else + { + bfd_vma baseaddr; + + if (branch) + { + immed *= 2; + baseaddr = memaddr + 2; + } + else if (use_extend) + baseaddr = memaddr - 2; + else + { + int status; + bfd_byte buffer[2]; + + baseaddr = memaddr; + + /* If this instruction is in the delay slot of a jr + instruction, the base address is the address of the + jr instruction. If it is in the delay slot of jalr + instruction, the base address is the address of the + jalr instruction. This test is unreliable: we have + no way of knowing whether the previous word is + instruction or data. */ + status = (*info->read_memory_func) (memaddr - 4, buffer, 2, + info); + if (status == 0 + && (((info->endian == BFD_ENDIAN_BIG + ? bfd_getb16 (buffer) + : bfd_getl16 (buffer)) + & 0xf800) == 0x1800)) + baseaddr = memaddr - 4; + else + { + status = (*info->read_memory_func) (memaddr - 2, buffer, + 2, info); + if (status == 0 + && (((info->endian == BFD_ENDIAN_BIG + ? bfd_getb16 (buffer) + : bfd_getl16 (buffer)) + & 0xf81f) == 0xe800)) + baseaddr = memaddr - 2; + } + } + info->target = (baseaddr & ~((1 << shift) - 1)) + immed; + (*info->print_address_func) (info->target, info); + } + } + break; + + case 'a': + if (! use_extend) + extend = 0; + l = ((l & 0x1f) << 23) | ((l & 0x3e0) << 13) | (extend << 2); + info->target = ((memaddr + 4) & ~(bfd_vma) 0x0fffffff) | l; + (*info->print_address_func) (info->target, info); + info->insn_type = dis_jsr; + info->branch_delay_insns = 1; + break; + + case 'l': + case 'L': + { + int need_comma, amask, smask; + + need_comma = 0; + + l = (l >> MIPS16OP_SH_IMM6) & MIPS16OP_MASK_IMM6; + + amask = (l >> 3) & 7; + + if (amask > 0 && amask < 5) + { + (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[4]); + if (amask > 1) + (*info->fprintf_func) (info->stream, "-%s", + mips_gpr_names[amask + 3]); + need_comma = 1; + } + + smask = (l >> 1) & 3; + if (smask == 3) + { + (*info->fprintf_func) (info->stream, "%s??", + need_comma ? "," : ""); + need_comma = 1; + } + else if (smask > 0) + { + (*info->fprintf_func) (info->stream, "%s%s", + need_comma ? "," : "", + mips_gpr_names[16]); + if (smask > 1) + (*info->fprintf_func) (info->stream, "-%s", + mips_gpr_names[smask + 15]); + need_comma = 1; + } + + if (l & 1) + { + (*info->fprintf_func) (info->stream, "%s%s", + need_comma ? "," : "", + mips_gpr_names[31]); + need_comma = 1; + } + + if (amask == 5 || amask == 6) + { + (*info->fprintf_func) (info->stream, "%s$f0", + need_comma ? "," : ""); + if (amask == 6) + (*info->fprintf_func) (info->stream, "-$f1"); + } + } + break; + + default: + /* xgettext:c-format */ + (*info->fprintf_func) + (info->stream, + _("# internal disassembler error, unrecognised modifier (%c)"), + type); + abort (); + } +} +#endif + +void +print_mips_disassembler_options (stream) + FILE *stream; +{ + unsigned int i; + + fprintf (stream, _("\n\ +The following MIPS specific disassembler options are supported for use\n\ +with the -M switch (multiple options should be separated by commas):\n")); + + fprintf (stream, _("\n\ + gpr-names=ABI Print GPR names according to specified ABI.\n\ + Default: based on binary being disassembled.\n")); + + fprintf (stream, _("\n\ + fpr-names=ABI Print FPR names according to specified ABI.\n\ + Default: numeric.\n")); + + fprintf (stream, _("\n\ + cp0-names=ARCH Print CP0 register names according to\n\ + specified architecture.\n\ + Default: based on binary being disassembled.\n")); + + fprintf (stream, _("\n\ + hwr-names=ARCH Print HWR names according to specified \n\ + architecture.\n\ + Default: based on binary being disassembled.\n")); + + fprintf (stream, _("\n\ + reg-names=ABI Print GPR and FPR names according to\n\ + specified ABI.\n")); + + fprintf (stream, _("\n\ + reg-names=ARCH Print CP0 register and HWR names according to\n\ + specified architecture.\n")); + + fprintf (stream, _("\n\ + For the options above, the following values are supported for \"ABI\":\n\ + ")); + for (i = 0; i < ARRAY_SIZE (mips_abi_choices); i++) + fprintf (stream, " %s", mips_abi_choices[i].name); + fprintf (stream, _("\n")); + + fprintf (stream, _("\n\ + For the options above, The following values are supported for \"ARCH\":\n\ + ")); + for (i = 0; i < ARRAY_SIZE (mips_arch_choices); i++) + if (*mips_arch_choices[i].name != '\0') + fprintf (stream, " %s", mips_arch_choices[i].name); + fprintf (stream, _("\n")); + + fprintf (stream, _("\n")); +} diff --git a/qemu/monitor.c b/qemu/monitor.c index b9de029..297c0a4 100644 --- a/qemu/monitor.c +++ b/qemu/monitor.c @@ -222,7 +222,7 @@ static void do_info_registers(void) { #ifdef TARGET_I386 cpu_dump_state(cpu_single_env, NULL, monitor_fprintf, - X86_DUMP_FPU | X86_DUMP_CCOP); + X86_DUMP_FPU); #else cpu_dump_state(cpu_single_env, NULL, monitor_fprintf, 0); @@ -562,6 +562,22 @@ static void do_print(int count, int format, int size, unsigned int valh, unsigne term_printf("\n"); } +static void do_sum(uint32_t start, uint32_t size) +{ + uint32_t addr; + uint8_t buf[1]; + uint16_t sum; + + sum = 0; + for(addr = start; addr < (start + size); addr++) { + cpu_physical_memory_rw(addr, buf, 1, 0); + /* BSD sum algorithm ('sum' Unix command) */ + sum = (sum >> 1) | (sum << 15); + sum += buf[0]; + } + term_printf("%05d\n", sum); +} + typedef struct { int keycode; const char *name; @@ -751,6 +767,11 @@ static void do_system_reset(void) qemu_system_reset_request(); } +static void do_system_powerdown(void) +{ + qemu_system_powerdown_request(); +} + #if defined(TARGET_I386) static void print_pte(uint32_t addr, uint32_t pte, uint32_t mask) { @@ -864,6 +885,20 @@ static void mem_info(void) } #endif +static void do_info_kqemu(void) +{ +#ifdef USE_KQEMU + int val; + val = 0; + if (cpu_single_env) + val = cpu_single_env->kqemu_enabled; + term_printf("kqemu is %s\n", val ? "enabled" : "disabled"); +#else + term_printf("kqemu support is not compiled\n"); +#endif +} + + static term_cmd_t term_cmds[] = { { "help|?", "s?", do_help, "[cmd]", "show the help" }, @@ -906,6 +941,10 @@ static term_cmd_t term_cmds[] = { "keys", "send keys to the VM (e.g. 'sendkey ctrl-alt-f1')" }, { "system_reset", "", do_system_reset, "", "reset the system" }, + { "system_powerdown", "", do_system_powerdown, + "", "send system power down event" }, + { "sum", "ii", do_sum, + "addr size", "compute the checksum of a memory region" }, { NULL, NULL, }, }; @@ -934,6 +973,8 @@ static term_cmd_t info_cmds[] = { #endif { "jit", "", do_info_jit, "", "show dynamic compiler info", }, + { "kqemu", "", do_info_kqemu, + "", "show kqemu information", }, { NULL, NULL, }, }; diff --git a/qemu/pc-bios/README b/qemu/pc-bios/README index a10a9f0..5e61a28 100644 --- a/qemu/pc-bios/README +++ b/qemu/pc-bios/README @@ -5,9 +5,13 @@ project (http://www.nongnu.org/vgabios/). - The PowerPC Open Hack'Ware Open Firmware Compatible BIOS is - available at http://site.voila.fr/jmayer/OpenHackWare/index.htm. + available at http://perso.magic.fr/l_indien/OpenHackWare/index.htm. - Proll is a GPL'd boot PROM for Sparc JavaStations (http://people.redhat.com/zaitcev/linux/). Applying proll.patch allows circumventing some bugs and enables faster kernel load through a hack. + +- video.x is a PowerMac NDRV compatible driver for a VGA frame + buffer. It comes from the Mac-on-Linux project + (http://www.maconlinux.org/). diff --git a/qemu/pc-bios/ohw.diff b/qemu/pc-bios/ohw.diff new file mode 100644 index 0000000..4fb5422 --- /dev/null +++ b/qemu/pc-bios/ohw.diff @@ -0,0 +1,1843 @@ +diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/bios.h OpenHackWare-release-0.4/src/bios.h +--- OpenHackWare-release-0.4.org/src/bios.h 2005-04-06 23:20:22.000000000 +0200 ++++ OpenHackWare-release-0.4/src/bios.h 2005-07-07 01:10:20.000000000 +0200 +@@ -64,6 +64,7 @@ + ARCH_CHRP, + ARCH_MAC99, + ARCH_POP, ++ ARCH_HEATHROW, + }; + + /* Hardware definition(s) */ +@@ -174,6 +175,7 @@ + int bd_ioctl (bloc_device_t *bd, int func, void *args); + uint32_t bd_seclen (bloc_device_t *bd); + void bd_close (bloc_device_t *bd); ++void bd_reset_all(void); + uint32_t bd_seclen (bloc_device_t *bd); + uint32_t bd_maxbloc (bloc_device_t *bd); + void bd_sect2CHS (bloc_device_t *bd, uint32_t secnum, +@@ -183,12 +185,12 @@ + part_t *bd_probe (int boot_device); + bloc_device_t *bd_get (int device); + void bd_put (bloc_device_t *bd); +-void bd_set_boot_part (bloc_device_t *bd, part_t *partition); ++void bd_set_boot_part (bloc_device_t *bd, part_t *partition, int partnum); + part_t **_bd_parts (bloc_device_t *bd); + + void ide_pci_pc_register (uint32_t io_base0, uint32_t io_base1, + uint32_t io_base2, uint32_t io_base3, +- void *OF_private); ++ void *OF_private0, void *OF_private1); + void ide_pci_pmac_register (uint32_t io_base0, uint32_t io_base1, + void *OF_private); + +@@ -399,17 +401,23 @@ + uint16_t min_grant, uint16_t max_latency); + void OF_finalize_pci_host (void *dev, int first_bus, int nb_busses); + void OF_finalize_pci_device (void *dev, uint8_t bus, uint8_t devfn, +- uint32_t *regions, uint32_t *sizes); ++ uint32_t *regions, uint32_t *sizes, ++ int irq_line); + void OF_finalize_pci_macio (void *dev, uint32_t base_address, uint32_t size, + void *private_data); ++void OF_finalize_pci_ide (void *dev, ++ uint32_t io_base0, uint32_t io_base1, ++ uint32_t io_base2, uint32_t io_base3); + int OF_register_bus (const unsigned char *name, uint32_t address, + const unsigned char *type); + int OF_register_serial (const unsigned char *bus, const unsigned char *name, + uint32_t io_base, int irq); + int OF_register_stdio (const unsigned char *dev_in, + const unsigned char *dev_out); +-void OF_vga_register (const unsigned char *name, uint32_t address, +- int width, int height, int depth); ++void OF_vga_register (const unsigned char *name, unused uint32_t address, ++ int width, int height, int depth, ++ unsigned long vga_bios_addr, ++ unsigned long vga_bios_size); + void *OF_blockdev_register (void *parent, void *private, + const unsigned char *type, + const unsigned char *name, int devnum, +diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/bloc.c OpenHackWare-release-0.4/src/bloc.c +--- OpenHackWare-release-0.4.org/src/bloc.c 2005-04-06 23:21:00.000000000 +0200 ++++ OpenHackWare-release-0.4/src/bloc.c 2005-07-08 00:28:26.000000000 +0200 +@@ -55,6 +55,7 @@ + /* Partitions */ + part_t *parts, *bparts; + part_t *boot_part; ++ int bpartnum; + /* Chain */ + bloc_device_t *next; + }; +@@ -66,6 +67,7 @@ + + static int ide_initialize (bloc_device_t *bd, int device); + static int ide_read_sector (bloc_device_t *bd, void *buffer, int secnum); ++static int ide_reset (bloc_device_t *bd); + + static int mem_initialize (bloc_device_t *bd, int device); + static int mem_read_sector (bloc_device_t *bd, void *buffer, int secnum); +@@ -212,6 +214,17 @@ + { + } + ++void bd_reset_all(void) ++{ ++ bloc_device_t *bd; ++ for (bd = bd_list; bd != NULL; bd = bd->next) { ++ if (bd->init == &ide_initialize) { ++ /* reset IDE drive because Darwin wants all IDE devices to be reset */ ++ ide_reset(bd); ++ } ++ } ++} ++ + uint32_t bd_seclen (bloc_device_t *bd) + { + return bd->seclen; +@@ -223,10 +236,12 @@ + } + + /* XXX: to be suppressed */ +-void bd_set_boot_part (bloc_device_t *bd, part_t *partition) ++void bd_set_boot_part (bloc_device_t *bd, part_t *partition, int partnum) + { ++ dprintf("%s: part %p (%p) %d\n", __func__, partition, bd->boot_part, partnum); + if (bd->boot_part == NULL) { + bd->boot_part = partition; ++ bd->bpartnum = partnum; + } + } + +@@ -240,6 +255,13 @@ + return &bd->bparts; + } + ++void bd_set_boot_device (bloc_device_t *bd) ++{ ++#if defined (USE_OPENFIRMWARE) ++ OF_blockdev_set_boot_device(bd->OF_private, bd->bpartnum, "\\\\ofwboot"); ++#endif ++} ++ + part_t *bd_probe (int boot_device) + { + char devices[] = { /*'a', 'b',*/ 'c', 'd', 'e', 'f', 'm', '\0', }; +@@ -272,9 +294,7 @@ + tmp = part_probe(bd, force_raw); + if (boot_device == bd->device) { + boot_part = tmp; +-#if defined (USE_OPENFIRMWARE) +- OF_blockdev_set_boot_device(bd->OF_private, 2, "\\\\ofwboot"); +-#endif ++ bd_set_boot_device(bd); + } + } + +@@ -717,34 +737,29 @@ + /* IDE PCI access for pc */ + static uint8_t ide_pci_port_read (bloc_device_t *bd, int port) + { +- eieio(); +- +- return *(uint8_t *)(bd->io_base + port); ++ uint8_t value; ++ value = inb(bd->io_base + port); ++ return value; + } + + static void ide_pci_port_write (bloc_device_t *bd, int port, uint8_t value) + { +- *(uint8_t *)(bd->io_base + port) = value; +- eieio(); ++ outb(bd->io_base + port, value); + } + + static uint32_t ide_pci_data_readl (bloc_device_t *bd) + { +- eieio(); +- +- return *((uint32_t *)bd->io_base); ++ return inl(bd->io_base); + } + + static void ide_pci_data_writel (bloc_device_t *bd, uint32_t val) + { +- *(uint32_t *)(bd->io_base) = val; +- eieio(); ++ outl(bd->io_base, val); + } + + static void ide_pci_control_write (bloc_device_t *bd, uint32_t val) + { +- *((uint8_t *)bd->tmp) = val; +- eieio(); ++ outb(bd->tmp + 2, val); + } + + static ide_ops_t ide_pci_pc_ops = { +@@ -761,7 +776,7 @@ + + void ide_pci_pc_register (uint32_t io_base0, uint32_t io_base1, + uint32_t io_base2, uint32_t io_base3, +- unused void *OF_private) ++ void *OF_private0, void *OF_private1) + { + if (ide_pci_ops == NULL) { + ide_pci_ops = malloc(sizeof(ide_ops_t)); +@@ -770,19 +785,19 @@ + memcpy(ide_pci_ops, &ide_pci_pc_ops, sizeof(ide_ops_t)); + } + if ((io_base0 != 0 || io_base1 != 0) && +- ide_pci_ops->base[0] == 0 && ide_pci_ops->base[1] == 0) { ++ ide_pci_ops->base[0] == 0 && ide_pci_ops->base[2] == 0) { + ide_pci_ops->base[0] = io_base0; +- ide_pci_ops->base[1] = io_base1; ++ ide_pci_ops->base[2] = io_base1; + #ifdef USE_OPENFIRMWARE +- ide_pci_ops->OF_private[0] = OF_private; ++ ide_pci_ops->OF_private[0] = OF_private0; + #endif + } + if ((io_base2 != 0 || io_base3 != 0) && +- ide_pci_ops->base[2] == 0 && ide_pci_ops->base[3] == 0) { +- ide_pci_ops->base[2] = io_base2; ++ ide_pci_ops->base[1] == 0 && ide_pci_ops->base[3] == 0) { ++ ide_pci_ops->base[1] = io_base2; + ide_pci_ops->base[3] = io_base3; + #ifdef USE_OPENFIRMWARE +- ide_pci_ops->OF_private[1] = OF_private; ++ ide_pci_ops->OF_private[1] = OF_private1; + #endif + } + } +@@ -935,6 +950,8 @@ + } + + static void atapi_pad_req (void *buffer, int len); ++static void atapi_make_req (bloc_device_t *bd, uint32_t *buffer, ++ int maxlen); + static int atapi_read_sector (bloc_device_t *bd, void *buffer, int secnum); + + static int ide_initialize (bloc_device_t *bd, int device) +@@ -1035,9 +1052,7 @@ + DPRINTF("INQUIRY\n"); + len = spc_inquiry_req(&atapi_buffer, 36); + atapi_pad_req(&atapi_buffer, len); +- ide_port_write(bd, 0x07, 0xA0); +- for (i = 0; i < 3; i++) +- ide_data_writel(bd, ldswap32(&atapi_buffer[i])); ++ atapi_make_req(bd, atapi_buffer, 36); + status = ide_port_read(bd, 0x07); + if (status != 0x48) { + ERROR("ATAPI INQUIRY : status %0x != 0x48\n", status); +@@ -1053,9 +1068,7 @@ + DPRINTF("READ_CAPACITY\n"); + len = mmc_read_capacity_req(&atapi_buffer); + atapi_pad_req(&atapi_buffer, len); +- ide_port_write(bd, 0x07, 0xA0); +- for (i = 0; i < 3; i++) +- ide_data_writel(bd, ldswap32(&atapi_buffer[i])); ++ atapi_make_req(bd, atapi_buffer, 8); + status = ide_port_read(bd, 0x07); + if (status != 0x48) { + ERROR("ATAPI READ_CAPACITY : status %0x != 0x48\n", status); +@@ -1105,6 +1118,22 @@ + memset(p + len, 0, 12 - len); + } + ++static void atapi_make_req (bloc_device_t *bd, uint32_t *buffer, ++ int maxlen) ++{ ++ int i; ++ /* select drive */ ++ if (bd->drv == 0) ++ ide_port_write(bd, 0x06, 0x40); ++ else ++ ide_port_write(bd, 0x06, 0x50); ++ ide_port_write(bd, 0x04, maxlen & 0xff); ++ ide_port_write(bd, 0x05, (maxlen >> 8) & 0xff); ++ ide_port_write(bd, 0x07, 0xA0); ++ for (i = 0; i < 3; i++) ++ ide_data_writel(bd, ldswap32(&buffer[i])); ++} ++ + static int atapi_read_sector (bloc_device_t *bd, void *buffer, int secnum) + { + uint32_t atapi_buffer[4]; +@@ -1112,16 +1141,9 @@ + uint32_t status, value; + int i, len; + +- /* select drive */ +- if (bd->drv == 0) +- ide_port_write(bd, 0x06, 0x40); +- else +- ide_port_write(bd, 0x06, 0x50); + len = mmc_read12_req(atapi_buffer, secnum, 1); + atapi_pad_req(&atapi_buffer, len); +- ide_port_write(bd, 0x07, 0xA0); +- for (i = 0; i < 3; i++) +- ide_data_writel(bd, ldswap32(&atapi_buffer[i])); ++ atapi_make_req(bd, atapi_buffer, bd->seclen); + status = ide_port_read(bd, 0x07); + if (status != 0x48) { + ERROR("ATAPI READ12 : status %0x != 0x48\n", status); +diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/libpart/apple.c OpenHackWare-release-0.4/src/libpart/apple.c +--- OpenHackWare-release-0.4.org/src/libpart/apple.c 2005-03-31 09:23:33.000000000 +0200 ++++ OpenHackWare-release-0.4/src/libpart/apple.c 2005-07-03 16:17:41.000000000 +0200 +@@ -199,14 +199,18 @@ + if (len == 0) { + /* Place holder. Skip it */ + DPRINTF("%s placeholder part\t%d\n", __func__, i); ++ part->flags = PART_TYPE_APPLE | PART_FLAG_DUMMY; ++ part_register(bd, part, name, i); + } else if (strncmp("Apple_Void", type, 32) == 0) { + /* Void partition. Skip it */ + DPRINTF("%s Void part\t%d [%s]\n", __func__, i, type); ++ part->flags = PART_TYPE_APPLE | PART_FLAG_DUMMY; ++ part_register(bd, part, name, i); + } else if (strncmp("Apple_Free", type, 32) == 0) { + /* Free space. Skip it */ + DPRINTF("%s Free part (%d)\n", __func__, i); + part->flags = PART_TYPE_APPLE | PART_FLAG_DUMMY; +- part_register(bd, part, name); ++ part_register(bd, part, name, i); + } else if (strncmp("Apple_partition_map", type, 32) == 0 || + strncmp("Apple_Partition_Map", type, 32) == 0 + #if 0 // Is this really used or is it just a mistake ? +@@ -226,7 +230,7 @@ + */ + } + part->flags = PART_TYPE_APPLE | PART_FLAG_DUMMY; +- part_register(bd, part, name); ++ part_register(bd, part, name, i); + } else if (strncmp("Apple_Driver", type, 32) == 0 || + strncmp("Apple_Driver43", type, 32) == 0 || + strncmp("Apple_Driver43_CD", type, 32) == 0 || +@@ -236,8 +240,12 @@ + strncmp("Apple_Driver_IOKit", type, 32) == 0) { + /* Drivers. don't care for now */ + DPRINTF("%s Drivers part\t%d [%s]\n", __func__, i, type); ++ part->flags = PART_TYPE_APPLE | PART_FLAG_DRIVER; ++ part_register(bd, part, name, i); + } else if (strncmp("Apple_Patches", type, 32) == 0) { + /* Patches: don't care for now */ ++ part->flags = PART_TYPE_APPLE | PART_FLAG_PATCH; ++ part_register(bd, part, name, i); + DPRINTF("%s Patches part\t%d [%s]\n", __func__, i, type); + } else if (strncmp("Apple_HFS", type, 32) == 0 || + strncmp("Apple_MFS", type, 32) == 0 || +@@ -256,9 +264,8 @@ + count = partmap->bloc_cnt * HFS_BLOCSIZE; + if (partmap->boot_size == 0 || partmap->boot_load == 0) { + printf("Not a bootable partition %d %d (%p %p)\n", +- partmap->boot_size, partmap->boot_load,boot_part, part); +- if (boot_part == NULL) +- boot_part = part; ++ partmap->boot_size, partmap->boot_load, ++ boot_part, part); + part->flags = PART_TYPE_APPLE | PART_FLAG_FS; + } else { + part->boot_start.bloc = partmap->boot_start; +@@ -278,8 +285,8 @@ + boot_part = part; + part->flags = PART_TYPE_APPLE | PART_FLAG_FS | PART_FLAG_BOOT; + } +- printf("Partition: %d %s st %0x size %0x", +- i, name, partmap->start_bloc, partmap->bloc_cnt); ++ printf("Partition: %d '%s' '%s' st %0x size %0x", ++ i, name, type, partmap->start_bloc, partmap->bloc_cnt); + #ifndef DEBUG + printf("\n"); + #endif +@@ -290,11 +297,13 @@ + part->boot_load, part->boot_entry); + DPRINTF(" load %0x entry %0x %0x\n", + partmap->boot_load2, partmap->boot_entry2, HFS_BLOCSIZE); +- part_register(bd, part, name); ++ part_register(bd, part, name, i); + } else { + memcpy(tmp, type, 32); + tmp[32] = '\0'; + ERROR("Unknown partition type [%s]\n", tmp); ++ part->flags = PART_TYPE_APPLE | PART_FLAG_DUMMY; ++ part_register(bd, part, name, i); + } + } + error: +diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/libpart/core.c OpenHackWare-release-0.4/src/libpart/core.c +--- OpenHackWare-release-0.4.org/src/libpart/core.c 2005-03-31 09:23:33.000000000 +0200 ++++ OpenHackWare-release-0.4/src/libpart/core.c 2005-07-03 16:17:41.000000000 +0200 +@@ -126,7 +126,7 @@ + } + + int part_register (bloc_device_t *bd, part_t *partition, +- const unsigned char *name) ++ const unsigned char *name, int partnum) + { + part_t **cur; + +@@ -134,6 +134,7 @@ + partition->bd = bd; + partition->next = NULL; + partition->name = strdup(name); ++ partition->partnum = partnum; + for (cur = _bd_parts(bd); *cur != NULL; cur = &(*cur)->next) + continue; + *cur = partition; +@@ -141,29 +142,15 @@ + return 0; + } + +-static inline int set_boot_part (bloc_device_t *bd, int partnum) +-{ +- part_t *cur; +- +- cur = part_get(bd, partnum); +- if (cur == NULL) +- return -1; +- bd_set_boot_part(bd, cur); +- +- return 0; +-} +- + part_t *part_get (bloc_device_t *bd, int partnum) + { + part_t **listp, *cur; +- int i; + + listp = _bd_parts(bd); +- cur = *listp; +- for (i = 0; i != partnum; i++) { +- if (cur == NULL) ++ ++ for (cur = *listp; cur != NULL; cur = cur->next) { ++ if (cur->partnum == partnum) + break; +- cur = cur->next; + } + + return cur; +@@ -192,17 +179,20 @@ + part_set_blocsize(bd, part, 512); + part->bd = bd; + part->flags = PART_TYPE_RAW | PART_FLAG_BOOT; +- part_register(bd, part, "Raw"); ++ part_register(bd, part, "Raw", 0); + + return part; + } + ++bloc_device_t *part_get_bd (part_t *part) ++{ ++ return part->bd; ++} ++ + part_t *part_probe (bloc_device_t *bd, int set_raw) + { +- part_t *part0, *boot_part, **cur; ++ part_t *part0 = NULL, *boot_part, **cur; + +- /* Register the 0 partition: raw partition containing the whole disk */ +- part0 = part_get_raw(bd); + /* Try to find a valid boot partition */ + boot_part = Apple_probe_partitions(bd); + if (boot_part == NULL) { +@@ -210,10 +200,13 @@ + if (boot_part == NULL && arch == ARCH_PREP) + boot_part = PREP_find_partition(bd); + if (boot_part == NULL && set_raw != 0) { +- boot_part = part0; +- set_boot_part(bd, 0); ++ dprintf("Use bloc device as raw partition\n"); + } + } ++ if (_bd_parts(bd) == NULL) { ++ /* Register the 0 partition: raw partition containing the whole disk */ ++ part0 = part_get_raw(bd); ++ } + /* Probe filesystem on each found partition */ + for (cur = _bd_parts(bd); *cur != NULL; cur = &(*cur)->next) { + const unsigned char *map, *type; +@@ -248,23 +241,28 @@ + type = "unknown"; + break; + } +- DPRINTF("Probe filesystem on %s %s partition '%s' %s\n", ++ dprintf("Probe filesystem on %s %s partition '%s' %s %p\n", + type, map, (*cur)->name, +- ((*cur)->flags) & PART_FLAG_BOOT ? "(bootable)" : ""); ++ ((*cur)->flags) & PART_FLAG_BOOT ? "(bootable)" : "", *cur); + if (((*cur)->flags) & PART_FLAG_FS) { + if (((*cur)->flags) & PART_FLAG_BOOT) + (*cur)->fs = fs_probe(*cur, 1); + else + (*cur)->fs = fs_probe(*cur, 0); ++ } else if (((*cur)->flags) & PART_TYPE_RAW) { ++ (*cur)->fs = fs_probe(*cur, 2); + } else { + (*cur)->fs = fs_probe(*cur, 2); + } +- if (((*cur)->flags) & PART_FLAG_BOOT) { +- bd_set_boot_part(bd, *cur); + fs_get_bootfile((*cur)->fs); ++ if (((*cur)->flags) & PART_FLAG_BOOT) { ++ dprintf("Partition is bootable (%d)\n", (*cur)->partnum); ++ bd_set_boot_part(bd, *cur, (*cur)->partnum); ++ if (boot_part == NULL) ++ boot_part = *cur; + } + } +- DPRINTF("Boot partition: %p %p %p %p\n", boot_part, boot_part->fs, ++ dprintf("Boot partition: %p %p %p %p\n", boot_part, boot_part->fs, + part_fs(boot_part), part0); + + return boot_part; +@@ -279,6 +277,7 @@ + part->boot_size.offset = 0; + part->boot_load = 0; + part->boot_entry = 0; ++ part->flags |= PART_FLAG_BOOT; + + return 0; + } +diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/libpart/isofs.c OpenHackWare-release-0.4/src/libpart/isofs.c +--- OpenHackWare-release-0.4.org/src/libpart/isofs.c 2005-03-31 09:23:33.000000000 +0200 ++++ OpenHackWare-release-0.4/src/libpart/isofs.c 2005-07-03 16:17:41.000000000 +0200 +@@ -242,7 +242,7 @@ + part->boot_start.bloc, part->boot_size.bloc, + part->boot_load, part->boot_entry); + part->flags = PART_TYPE_ISO9660 | PART_FLAG_BOOT; +- part_register(bd, part, name); ++ part_register(bd, part, name, i + 1); + fs_raw_set_bootfile(part, part->boot_start.bloc, + part->boot_start.offset, + part->boot_size.bloc, +diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/libpart/libpart.h OpenHackWare-release-0.4/src/libpart/libpart.h +--- OpenHackWare-release-0.4.org/src/libpart/libpart.h 2005-03-31 09:23:33.000000000 +0200 ++++ OpenHackWare-release-0.4/src/libpart/libpart.h 2005-07-03 16:17:41.000000000 +0200 +@@ -30,6 +30,7 @@ + + struct part_t { + bloc_device_t *bd; ++ int partnum; + uint32_t start; /* Partition first bloc */ + uint32_t size; /* Partition size, in blocs */ + uint32_t spb; +@@ -54,7 +55,7 @@ + }; + + int part_register (bloc_device_t *bd, part_t *partition, +- const unsigned char *name); ++ const unsigned char *name, int partnum); + void part_set_blocsize (bloc_device_t *bd, part_t *part, uint32_t blocsize); + void part_private_set (part_t *part, void *private); + void *part_private_get (part_t *part); +diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/libpart/prep.c OpenHackWare-release-0.4/src/libpart/prep.c +--- OpenHackWare-release-0.4.org/src/libpart/prep.c 2005-03-31 09:23:33.000000000 +0200 ++++ OpenHackWare-release-0.4/src/libpart/prep.c 2005-07-03 16:17:41.000000000 +0200 +@@ -164,7 +164,7 @@ + part->boot_load = 0; + part->boot_entry = boot_offset - part->bloc_size; + part->flags = PART_TYPE_PREP | PART_FLAG_BOOT; +- part_register(bd, part, "PREP boot"); ++ part_register(bd, part, "PREP boot", i); + fs_raw_set_bootfile(part, part->boot_start.bloc, + part->boot_start.offset, + part->boot_size.bloc, +diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/main.c OpenHackWare-release-0.4/src/main.c +--- OpenHackWare-release-0.4.org/src/main.c 2005-03-31 09:23:33.000000000 +0200 ++++ OpenHackWare-release-0.4/src/main.c 2005-06-07 23:48:39.000000000 +0200 +@@ -364,20 +364,24 @@ + void *load_base, *load_entry, *last_alloc, *load_end; + uint32_t memsize, boot_image_size, cmdline_size, ramdisk_size; + uint32_t boot_base, boot_nb; +- int boot_device; ++ int boot_device, i; ++ static const uint32_t isa_base_tab[3] = { ++ 0x80000000, /* PREP */ ++ 0xFE000000, /* Grackle (Heathrow) */ ++ 0xF2000000, /* UniNorth (Mac99) */ ++ }; + + /* Retrieve NVRAM configuration */ +- nvram_retry: ++ for(i = 0; i < 3; i++) { ++ isa_io_base = isa_base_tab[i]; + nvram = NVRAM_get_config(&memsize, &boot_device, + &boot_image, &boot_image_size, + &cmdline, &cmdline_size, + &ramdisk, &ramdisk_size); +- if (nvram == NULL) { +- /* Retry with another isa_io_base */ +- if (isa_io_base == 0x80000000) { +- isa_io_base = 0xF2000000; +- goto nvram_retry; ++ if (nvram) ++ break; + } ++ if (i == 3) { + ERROR("Unable to load configuration from NVRAM. Aborting...\n"); + return -1; + } +@@ -402,7 +406,7 @@ + cpu_name = CPU_get_name(pvr); + OF_register_cpu(cpu_name, 0, pvr, + 200 * 1000 * 1000, 200 * 1000 * 1000, +- 100 * 1000 * 1000, 10 * 1000 * 1000, ++ 100 * 1000 * 1000, 100 * 1000 * 1000, + 0x0092); + } + OF_register_memory(memsize, 512 * 1024 /* TOFIX */); +@@ -433,9 +437,12 @@ + vga_puts(copyright); + vga_puts("\n"); + ++#if 0 + /* QEMU is quite incoherent: d is cdrom, not second drive */ ++ /* XXX: should probe CD-ROM position */ + if (boot_device == 'd') + boot_device = 'e'; ++#endif + /* Open boot device */ + boot_part = bd_probe(boot_device); + if (boot_device == 'm') { +diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/nvram.c OpenHackWare-release-0.4/src/nvram.c +--- OpenHackWare-release-0.4.org/src/nvram.c 2005-03-31 09:23:33.000000000 +0200 ++++ OpenHackWare-release-0.4/src/nvram.c 2005-06-04 23:44:03.000000000 +0200 +@@ -334,6 +334,7 @@ + ret = NVRAM_chrp_format(nvram); + break; + case ARCH_MAC99: ++ case ARCH_HEATHROW: /* XXX: may be incorrect */ + ret = NVRAM_mac99_format(nvram); + break; + case ARCH_POP: +@@ -409,13 +410,12 @@ + arch = ARCH_MAC99; + } else if (strcmp(sign, "POP") == 0) { + arch = ARCH_POP; ++ } else if (strcmp(sign, "HEATHROW") == 0) { ++ arch = ARCH_HEATHROW; + } else { + ERROR("Unknown PPC architecture: '%s'\n", sign); + return NULL; + } +- /* HACK */ +- if (arch == ARCH_CHRP) +- arch = ARCH_MAC99; + lword = NVRAM_get_lword(nvram, 0x30); + *RAM_size = lword; + byte = NVRAM_get_byte(nvram, 0x34); +diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/of.c OpenHackWare-release-0.4/src/of.c +--- OpenHackWare-release-0.4.org/src/of.c 2005-04-06 23:17:26.000000000 +0200 ++++ OpenHackWare-release-0.4/src/of.c 2005-07-07 23:30:08.000000000 +0200 +@@ -489,7 +489,7 @@ + ERROR("%s can't alloc new node '%s' name\n", __func__, name); + return NULL; + } +- new->prop_address = OF_prop_int_new(env, new, "address", address); ++ new->prop_address = OF_prop_int_new(env, new, "unit-address", address); + if (new->prop_address == NULL) { + free(new->prop_name->value); + free(new->prop_name); +@@ -1017,6 +1017,33 @@ + string, strlen(string) + 1); + } + ++/* convert '\1' char to '\0' */ ++static OF_prop_t *OF_prop_string_new1 (OF_env_t *env, OF_node_t *node, ++ const unsigned char *name, ++ const unsigned char *string) ++{ ++ int len, i; ++ OF_prop_t *ret; ++ unsigned char *str; ++ ++ if (strchr(string, '\1') == NULL) { ++ return OF_prop_string_new(env, node, name, string); ++ } else { ++ len = strlen(string) + 1; ++ str = malloc(len); ++ if (!str) ++ return NULL; ++ memcpy(str, string, len); ++ for(i = 0; i < len; i++) ++ if (str[i] == '\1') ++ str[i] = '\0'; ++ ret = OF_property_new(env, node, name, ++ str, len); ++ free(str); ++ return ret; ++ } ++} ++ + __attribute__ (( section (".OpenFirmware") )) + static OF_prop_t *OF_prop_int_new (OF_env_t *env, OF_node_t *node, + const unsigned char *name, uint32_t value) +@@ -1421,15 +1448,12 @@ + __attribute__ (( section (".OpenFirmware") )) + int OF_init (void) + { +- const unsigned char compat_str[] = + #if 0 + "PowerMac3,1\0MacRISC\0Power Macintosh\0"; + "PowerMac1,2\0MacRISC\0Power Macintosh\0"; + "AAPL,PowerMac G3\0PowerMac G3\0MacRISC\0Power Macintosh\0"; + "AAPL,PowerMac3,0\0MacRISC\0Power Macintosh\0"; + "AAPL,Gossamer\0MacRISC\0Power Macintosh\0"; +-#else +- "AAPL,PowerMac G3\0PowerMac G3\0MacRISC\0Power Macintosh\0"; + #endif + OF_env_t *OF_env; + OF_node_t *als, *opt, *chs, *pks; +@@ -1455,15 +1479,21 @@ + return -1; + } + OF_prop_string_new(OF_env, OF_node_root, "device_type", "bootrom"); +-#if 0 +- OF_prop_string_new(OF_env, OF_node_root, +- "model", "PPC Open Hack'Ware " BIOS_VERSION); +-#else ++ if (arch == ARCH_HEATHROW) { ++ const unsigned char compat_str[] = ++ "PowerMac1,1\0MacRISC\0Power Macintosh"; ++ OF_property_new(OF_env, OF_node_root, "compatible", ++ compat_str, sizeof(compat_str)); + OF_prop_string_new(OF_env, OF_node_root, +- "model", compat_str); +-#endif ++ "model", "Power Macintosh"); ++ } else { ++ const unsigned char compat_str[] = ++ "PowerMac3,1\0MacRISC\0Power Macintosh"; + OF_property_new(OF_env, OF_node_root, "compatible", + compat_str, sizeof(compat_str)); ++ OF_prop_string_new(OF_env, OF_node_root, ++ "model", "PowerMac3,1"); ++ } + #if 0 + OF_prop_string_new(OF_env, OF_node_root, "copyright", copyright); + #else +@@ -1561,14 +1591,15 @@ + range.size = 0x00800000; + OF_property_new(OF_env, rom, "ranges", &range, sizeof(OF_range_t)); + OF_prop_int_new(OF_env, rom, "#address-cells", 1); ++ + /* "/rom/boot-rom@fff00000" node */ +- brom = OF_node_new(OF_env, OF_node_root, "boot-rom", 0xfff00000); ++ brom = OF_node_new(OF_env, rom, "boot-rom", 0xfff00000); + if (brom == NULL) { + ERROR("Cannot create 'boot-rom'\n"); + return -1; + } + regs.address = 0xFFF00000; +- regs.size = 0x00010000; ++ regs.size = 0x00100000; + OF_property_new(OF_env, brom, "reg", ®s, sizeof(OF_regprop_t)); + OF_prop_string_new(OF_env, brom, "write-characteristic", "flash"); + OF_prop_string_new(OF_env, brom, "BootROM-build-date", +@@ -1577,7 +1608,7 @@ + OF_prop_string_new(OF_env, brom, "copyright", copyright); + OF_prop_string_new(OF_env, brom, "model", BIOS_str); + OF_prop_int_new(OF_env, brom, "result", 0); +-#if 0 ++#if 1 + { + /* Hack taken 'as-is' from PearPC */ + unsigned char info[] = { +@@ -1596,7 +1627,9 @@ + OF_node_put(OF_env, brom); + OF_node_put(OF_env, rom); + } ++#if 0 + /* From here, hardcoded hacks to get a Mac-like machine */ ++ /* XXX: Core99 does not seem to like this NVRAM tree */ + /* "/nvram@fff04000" node */ + { + OF_regprop_t regs; +@@ -1617,6 +1650,7 @@ + OF_prop_int_new(OF_env, chs, "nvram", OF_pack_handle(OF_env, nvr)); + OF_node_put(OF_env, nvr); + } ++#endif + /* "/pseudo-hid" : hid emulation as Apple does */ + { + OF_node_t *hid; +@@ -1663,7 +1697,27 @@ + } + OF_node_put(OF_env, hid); + } ++ if (arch == ARCH_MAC99) { ++ OF_node_t *unin; ++ OF_regprop_t regs; + ++ unin = OF_node_new(OF_env, OF_node_root, ++ "uni-n", 0xf8000000); ++ if (unin == NULL) { ++ ERROR("Cannot create 'uni-n'\n"); ++ return -1; ++ } ++ OF_prop_string_new(OF_env, unin, "device-type", "memory-controller"); ++ OF_prop_string_new(OF_env, unin, "model", "AAPL,UniNorth"); ++ OF_prop_string_new(OF_env, unin, "compatible", "uni-north"); ++ regs.address = 0xf8000000; ++ regs.size = 0x01000000; ++ OF_property_new(OF_env, unin, "reg", ®s, sizeof(regs)); ++ OF_prop_int_new(OF_env, unin, "#address-cells", 1); ++ OF_prop_int_new(OF_env, unin, "#size-cells", 1); ++ OF_prop_int_new(OF_env, unin, "device-rev", 3); ++ OF_node_put(OF_env, unin); ++ } + + #if 1 /* This is mandatory for claim to work + * but I don't know where it should really be (in cpu ?) +@@ -1693,7 +1747,9 @@ + + /* "/options/boot-args" node */ + { +- const unsigned char *args = "-v rootdev cdrom"; ++ // const unsigned char *args = "-v rootdev cdrom"; ++ //const unsigned char *args = "-v io=0xffffffff"; ++ const unsigned char *args = "-v"; + /* Ask MacOS X to print debug messages */ + // OF_prop_string_new(OF_env, chs, "machargs", args); + // OF_prop_string_new(OF_env, opt, "boot-command", args); +@@ -2013,17 +2069,17 @@ + OF_prop_int_new(OF_env, node, "min-grant", min_grant); + OF_prop_int_new(OF_env, node, "max-latency", max_latency); + if (dev->type != NULL) +- OF_prop_string_new(OF_env, node, "device_type", dev->type); ++ OF_prop_string_new1(OF_env, node, "device_type", dev->type); + if (dev->compat != NULL) +- OF_prop_string_new(OF_env, node, "compatible", dev->compat); ++ OF_prop_string_new1(OF_env, node, "compatible", dev->compat); + if (dev->model != NULL) +- OF_prop_string_new(OF_env, node, "model", dev->model); ++ OF_prop_string_new1(OF_env, node, "model", dev->model); + if (dev->acells != 0) + OF_prop_int_new(OF_env, node, "#address-cells", dev->acells); + if (dev->scells != 0) +- OF_prop_int_new(OF_env, node, "#interrupt-cells", dev->acells); ++ OF_prop_int_new(OF_env, node, "#size-cells", dev->scells); + if (dev->icells != 0) +- OF_prop_int_new(OF_env, node, "#size-cells", dev->acells); ++ OF_prop_int_new(OF_env, node, "#interrupt-cells", dev->icells); + dprintf("Done %p %p\n", parent, node); + + return node; +@@ -2040,8 +2096,9 @@ + OF_env_t *OF_env; + pci_range_t ranges[3]; + OF_regprop_t regs[1]; +- OF_node_t *pci_host; ++ OF_node_t *pci_host, *als; + int nranges; ++ unsigned char buffer[OF_NAMELEN_MAX]; + + OF_env = OF_env_main; + dprintf("register PCI host '%s' '%s' '%s' '%s'\n", +@@ -2052,6 +2109,17 @@ + ERROR("Cannot create pci host\n"); + return NULL; + } ++ ++ als = OF_node_get(OF_env, "aliases"); ++ if (als == NULL) { ++ ERROR("Cannot get 'aliases'\n"); ++ return NULL; ++ } ++ sprintf(buffer, "/%s", dev->name); ++ OF_prop_string_set(OF_env, als, "pci", buffer); ++ OF_node_put(OF_env, als); ++ ++ + regs[0].address = cfg_base; + regs[0].size = cfg_len; + OF_property_new(OF_env, pci_host, "reg", regs, sizeof(OF_regprop_t)); +@@ -2136,6 +2204,11 @@ + return pci_dev; + } + ++/* XXX: suppress that, used for interrupt map init */ ++OF_node_t *pci_host_node; ++uint32_t pci_host_interrupt_map[7 * 32]; ++int pci_host_interrupt_map_len = 0; ++ + void OF_finalize_pci_host (void *dev, int first_bus, int nb_busses) + { + OF_env_t *OF_env; +@@ -2145,10 +2218,12 @@ + regs[0].address = first_bus; + regs[0].size = nb_busses; + OF_property_new(OF_env, dev, "bus-range", regs, sizeof(OF_regprop_t)); ++ pci_host_node = dev; + } + + void OF_finalize_pci_device (void *dev, uint8_t bus, uint8_t devfn, +- uint32_t *regions, uint32_t *sizes) ++ uint32_t *regions, uint32_t *sizes, ++ int irq_line) + { + OF_env_t *OF_env; + pci_reg_prop_t pregs[6], rregs[6]; +@@ -2156,6 +2231,7 @@ + int i, j, k; + + OF_env = OF_env_main; ++ /* XXX: only useful for VGA card in fact */ + if (regions[0] != 0x00000000) + OF_prop_int_set(OF_env, dev, "address", regions[0] & ~0x0000000F); + for (i = 0, j = 0, k = 0; i < 6; i++) { +@@ -2222,7 +2298,22 @@ + } else { + OF_property_new(OF_env, dev, "assigned-addresses", NULL, 0); + } +-#if 0 ++ if (irq_line >= 0) { ++ int i; ++ OF_prop_int_new(OF_env, dev, "interrupts", 1); ++ i = pci_host_interrupt_map_len; ++ pci_host_interrupt_map[i++] = (devfn << 8) & 0xf800; ++ pci_host_interrupt_map[i++] = 0; ++ pci_host_interrupt_map[i++] = 0; ++ pci_host_interrupt_map[i++] = 0; ++ pci_host_interrupt_map[i++] = 0; /* pic handle will be patched later */ ++ pci_host_interrupt_map[i++] = irq_line; ++ if (arch != ARCH_HEATHROW) { ++ pci_host_interrupt_map[i++] = 1; ++ } ++ pci_host_interrupt_map_len = i; ++ } ++#if 1 + { + OF_prop_t *prop_name = ((OF_node_t *)dev)->prop_name; + +@@ -2390,6 +2481,54 @@ + return 0; + } + ++static void keylargo_ata(OF_node_t *mio, uint32_t base_address, ++ uint32_t base, int irq1, int irq2, ++ uint16_t pic_phandle) ++{ ++ OF_env_t *OF_env = OF_env_main; ++ OF_node_t *ata; ++ OF_regprop_t regs[2]; ++ ++ ata = OF_node_new(OF_env, mio, "ata-4", base); ++ if (ata == NULL) { ++ ERROR("Cannot create 'ata-4'\n"); ++ return; ++ } ++ OF_prop_string_new(OF_env, ata, "device_type", "ata"); ++#if 1 ++ OF_prop_string_new(OF_env, ata, "compatible", "key2largo-ata"); ++ OF_prop_string_new(OF_env, ata, "model", "ata-4"); ++ OF_prop_string_new(OF_env, ata, "cable-type", "80-conductor"); ++#else ++ OF_prop_string_new(OF_env, ata, "compatible", "cmd646-ata"); ++ OF_prop_string_new(OF_env, ata, "model", "ata-4"); ++#endif ++ OF_prop_int_new(OF_env, ata, "#address-cells", 1); ++ OF_prop_int_new(OF_env, ata, "#size-cells", 0); ++ regs[0].address = base; ++ regs[0].size = 0x00001000; ++#if 0 // HACK: Don't set up DMA registers ++ regs[1].address = 0x00008A00; ++ regs[1].size = 0x00001000; ++ OF_property_new(OF_env, ata, "reg", ++ regs, 2 * sizeof(OF_regprop_t)); ++#else ++ OF_property_new(OF_env, ata, "reg", ++ regs, sizeof(OF_regprop_t)); ++#endif ++ OF_prop_int_new(OF_env, ata, "interrupt-parent", pic_phandle); ++ regs[0].address = irq1; ++ regs[0].size = 0x00000001; ++ regs[1].address = irq2; ++ regs[1].size = 0x00000000; ++ OF_property_new(OF_env, ata, "interrupts", ++ regs, 2 * sizeof(OF_regprop_t)); ++ if (base == 0x1f000) ++ ide_pci_pmac_register(base_address + base, 0x00000000, ata); ++ else ++ ide_pci_pmac_register(0x00000000, base_address + base, ata); ++} ++ + void OF_finalize_pci_macio (void *dev, uint32_t base_address, uint32_t size, + void *private_data) + { +@@ -2398,6 +2537,8 @@ + pci_reg_prop_t pregs[2]; + OF_node_t *mio, *chs, *als; + uint16_t pic_phandle; ++ int rec_len; ++ OF_prop_t *mio_reg; + + OF_DPRINTF("mac-io: %p\n", dev); + OF_env = OF_env_main; +@@ -2416,10 +2557,14 @@ + mio = dev; + mio->private_data = private_data; + pregs[0].addr.hi = 0x00000000; +- pregs[0].addr.mid = 0x82013810; ++ pregs[0].addr.mid = 0x00000000; + pregs[0].addr.lo = 0x00000000; + pregs[0].size_hi = base_address; + pregs[0].size_lo = size; ++ mio_reg = OF_property_get(OF_env, mio, "reg"); ++ if (mio_reg && mio_reg->vlen >= 5 * 4) { ++ pregs[0].addr.mid = ((pci_reg_prop_t *)mio_reg->value)->addr.hi; ++ } + OF_property_new(OF_env, mio, "ranges", + &pregs, sizeof(pci_reg_prop_t)); + #if 0 +@@ -2431,8 +2576,32 @@ + OF_property_new(OF_env, mio, "assigned-addresses", + &pregs, sizeof(pci_reg_prop_t)); + #endif ++ ++ if (arch == ARCH_HEATHROW) { ++ /* Heathrow PIC */ ++ OF_regprop_t regs; ++ OF_node_t *mpic; ++ const char compat_str[] = "heathrow\0mac-risc"; ++ ++ mpic = OF_node_new(OF_env, mio, "interrupt-controller", 0x10); ++ if (mpic == NULL) { ++ ERROR("Cannot create 'mpic'\n"); ++ goto out; ++ } ++ OF_prop_string_new(OF_env, mpic, "device_type", "interrupt-controller"); ++ OF_property_new(OF_env, mpic, "compatible", compat_str, sizeof(compat_str)); ++ OF_prop_int_new(OF_env, mpic, "#interrupt-cells", 1); ++ regs.address = 0x10; ++ regs.size = 0x20; ++ OF_property_new(OF_env, mpic, "reg", ++ ®s, sizeof(regs)); ++ OF_property_new(OF_env, mpic, "interrupt-controller", NULL, 0); ++ pic_phandle = OF_pack_handle(OF_env, mpic); ++ OF_prop_int_new(OF_env, chs, "interrupt-controller", pic_phandle); ++ OF_node_put(OF_env, mpic); ++ rec_len = 6; ++ } else { + /* OpenPIC */ +- { + OF_regprop_t regs[4]; + OF_node_t *mpic; + mpic = OF_node_new(OF_env, mio, "interrupt-controller", 0x40000); +@@ -2455,8 +2624,37 @@ + pic_phandle = OF_pack_handle(OF_env, mpic); + OF_prop_int_new(OF_env, chs, "interrupt-controller", pic_phandle); + OF_node_put(OF_env, mpic); ++ rec_len = 7; + } +-#if 1 ++ ++ /* patch pci host table */ ++ /* XXX: do it after the PCI init */ ++ { ++ int i; ++ uint32_t tab[4]; ++ ++ for(i = 0; i < pci_host_interrupt_map_len; i += rec_len) ++ pci_host_interrupt_map[i + 4] = pic_phandle; ++#if 0 ++ dprintf("interrupt-map:\n"); ++ for(i = 0; i < pci_host_interrupt_map_len; i++) { ++ dprintf(" %08x", pci_host_interrupt_map[i]); ++ if ((i % rec_len) == (rec_len - 1)) ++ dprintf("\n"); ++ } ++ dprintf("\n"); ++#endif ++ OF_property_new(OF_env, pci_host_node, "interrupt-map", ++ pci_host_interrupt_map, ++ pci_host_interrupt_map_len * sizeof(uint32_t)); ++ tab[0] = 0xf800; ++ tab[1] = 0; ++ tab[2] = 0; ++ tab[3] = 0; ++ OF_property_new(OF_env, pci_host_node, "interrupt-map-mask", ++ tab, 4 * sizeof(uint32_t)); ++ } ++#if 0 + /* escc is usefull to get MacOS X debug messages */ + { + OF_regprop_t regs[8]; +@@ -2645,85 +2843,12 @@ + OF_node_put(OF_env, scc); + } + #endif +- /* IDE controller */ +- { +- OF_node_t *ata; +- OF_regprop_t regs[2]; +- ata = OF_node_new(OF_env, mio, "ata-4", 0x1f000); +- if (ata == NULL) { +- ERROR("Cannot create 'ata-4'\n"); +- goto out; +- } +- OF_prop_string_new(OF_env, ata, "device_type", "ata"); +-#if 1 +- OF_prop_string_new(OF_env, ata, "compatible", "keylargo-ata"); +- OF_prop_string_new(OF_env, ata, "model", "ata-4"); +-#else +- OF_prop_string_new(OF_env, ata, "compatible", "cmd646-ata"); +- OF_prop_string_new(OF_env, ata, "model", "ata-4"); +-#endif +- OF_prop_int_new(OF_env, ata, "#address-cells", 1); +- OF_prop_int_new(OF_env, ata, "#size-cells", 0); +- regs[0].address = 0x0001F000; +- regs[0].size = 0x00001000; +-#if 0 // HACK: Don't set up DMA registers +- regs[1].address = 0x00008A00; +- regs[1].size = 0x00001000; +- OF_property_new(OF_env, ata, "reg", +- regs, 2 * sizeof(OF_regprop_t)); +-#else +- OF_property_new(OF_env, ata, "reg", +- regs, sizeof(OF_regprop_t)); +-#endif +- OF_prop_int_new(OF_env, ata, "interrupt-parent", pic_phandle); +- regs[0].address = 0x00000013; +- regs[0].size = 0x00000001; +- regs[1].address = 0x0000000B; +- regs[1].size = 0x00000000; +- OF_property_new(OF_env, ata, "interrupts", +- regs, 2 * sizeof(OF_regprop_t)); +- ide_pci_pmac_register(base_address + 0x1f000, 0x00000000, ata); +- +- } +- { +- OF_node_t *ata; +- OF_regprop_t regs[2]; +- ata = OF_node_new(OF_env, mio, "ata-4", 0x20000); +- if (ata == NULL) { +- ERROR("Cannot create 'ata-4'\n"); +- goto out; +- } +- OF_prop_string_new(OF_env, ata, "device_type", "ata"); +-#if 1 +- OF_prop_string_new(OF_env, ata, "compatible", "keylargo-ata"); +- OF_prop_string_new(OF_env, ata, "model", "ata-4"); +-#else +- OF_prop_string_new(OF_env, ata, "compatible", "cmd646-ata"); +- OF_prop_string_new(OF_env, ata, "model", "ata-4"); +-#endif +- OF_prop_int_new(OF_env, ata, "#address-cells", 1); +- OF_prop_int_new(OF_env, ata, "#size-cells", 0); +- regs[0].address = 0x00020000; +- regs[0].size = 0x00001000; +-#if 0 // HACK: Don't set up DMA registers +- regs[1].address = 0x00008A00; +- regs[1].size = 0x00001000; +- OF_property_new(OF_env, ata, "reg", +- regs, 2 * sizeof(OF_regprop_t)); +-#else +- OF_property_new(OF_env, ata, "reg", +- regs, sizeof(OF_regprop_t)); +-#endif +- OF_prop_int_new(OF_env, ata, "interrupt-parent", pic_phandle); +- regs[0].address = 0x00000014; +- regs[0].size = 0x00000001; +- regs[1].address = 0x0000000B; +- regs[1].size = 0x00000000; +- OF_property_new(OF_env, ata, "interrupts", +- regs, 2 * sizeof(OF_regprop_t)); +- ide_pci_pmac_register(0x00000000, base_address + 0x20000, ata); +- ++ /* Keylargo IDE controller: need some work (DMA problem ?) */ ++ if (arch == ARCH_MAC99) { ++ keylargo_ata(mio, base_address, 0x1f000, 0x13, 0xb, pic_phandle); ++ keylargo_ata(mio, base_address, 0x20000, 0x14, 0xb, pic_phandle); + } ++#if 0 + /* Timer */ + { + OF_node_t *tmr; +@@ -2746,10 +2871,11 @@ + regs, sizeof(OF_regprop_t)); + OF_node_put(OF_env, tmr); + } ++#endif + /* VIA-PMU */ + { + /* Controls adb, RTC and power-mgt (forget it !) */ +- OF_node_t *via, *adb, *rtc; ++ OF_node_t *via, *adb; + OF_regprop_t regs[1]; + #if 0 // THIS IS A HACK AND IS COMPLETELY ABSURD ! + // (but needed has Qemu doesn't emulate via-pmu). +@@ -2773,14 +2899,21 @@ + regs[0].size = 0x00002000; + OF_property_new(OF_env, via, "reg", regs, sizeof(OF_regprop_t)); + OF_prop_int_new(OF_env, via, "interrupt-parent", pic_phandle); ++ if (arch == ARCH_HEATHROW) { ++ OF_prop_int_new(OF_env, via, "interrupts", 0x12); ++ } else { + regs[0].address = 0x00000019; + regs[0].size = 0x00000001; + OF_property_new(OF_env, via, "interrupts", + regs, sizeof(OF_regprop_t)); ++ } ++ /* force usage of OF bus speeds */ ++ OF_prop_int_new(OF_env, via, "BusSpeedCorrect", 1); + #if 0 + OF_prop_int_new(OF_env, via, "pmu-version", 0x00D0740C); + #endif +-#if 1 ++ { ++ OF_node_t *kbd, *mouse; + /* ADB pseudo-device */ + adb = OF_node_new(OF_env, via, "adb", OF_ADDRESS_NONE); + if (adb == NULL) { +@@ -2797,9 +2930,26 @@ + OF_prop_int_new(OF_env, adb, "#size-cells", 0); + OF_pack_get_path(OF_env, tmp, 512, adb); + OF_prop_string_new(OF_env, als, "adb", tmp); +- /* XXX: add "keyboard@2" and "mouse@3" */ +- OF_node_put(OF_env, adb); +-#endif ++ ++ kbd = OF_node_new(OF_env, adb, "keyboard", 2); ++ if (kbd == NULL) { ++ ERROR("Cannot create 'kbd'\n"); ++ goto out; ++ } ++ OF_prop_string_new(OF_env, kbd, "device_type", "keyboard"); ++ OF_prop_int_new(OF_env, kbd, "reg", 2); ++ ++ mouse = OF_node_new(OF_env, adb, "mouse", 3); ++ if (mouse == NULL) { ++ ERROR("Cannot create 'mouse'\n"); ++ goto out; ++ } ++ OF_prop_string_new(OF_env, mouse, "device_type", "mouse"); ++ OF_prop_int_new(OF_env, mouse, "reg", 3); ++ OF_prop_int_new(OF_env, mouse, "#buttons", 3); ++ } ++ { ++ OF_node_t *rtc; + + rtc = OF_node_new(OF_env, via, "rtc", OF_ADDRESS_NONE); + if (rtc == NULL) { +@@ -2813,14 +2963,68 @@ + OF_prop_string_new(OF_env, rtc, "compatible", "rtc"); + #endif + OF_node_put(OF_env, rtc); +- OF_node_put(OF_env, via); + } ++ // OF_node_put(OF_env, via); ++ } ++ { ++ OF_node_t *pmgt; ++ pmgt = OF_node_new(OF_env, mio, "power-mgt", OF_ADDRESS_NONE); ++ OF_prop_string_new(OF_env, pmgt, "device_type", "power-mgt"); ++ OF_prop_string_new(OF_env, pmgt, "compatible", "cuda"); ++ OF_prop_string_new(OF_env, pmgt, "mgt-kind", "min-consumption-pwm-led"); ++ OF_node_put(OF_env, pmgt); ++ } ++ ++ if (arch == ARCH_HEATHROW) { ++ /* NVRAM */ ++ OF_node_t *nvr; ++ OF_regprop_t regs; ++ nvr = OF_node_new(OF_env, mio, "nvram", 0x60000); ++ OF_prop_string_new(OF_env, nvr, "device_type", "nvram"); ++ regs.address = 0x60000; ++ regs.size = 0x00020000; ++ OF_property_new(OF_env, nvr, "reg", ®s, sizeof(regs)); ++ OF_prop_int_new(OF_env, nvr, "#bytes", 0x2000); ++ OF_node_put(OF_env, nvr); ++ } ++ + out: + // OF_node_put(OF_env, mio); + OF_node_put(OF_env, chs); + OF_node_put(OF_env, als); + } + ++void OF_finalize_pci_ide (void *dev, ++ uint32_t io_base0, uint32_t io_base1, ++ uint32_t io_base2, uint32_t io_base3) ++{ ++ OF_env_t *OF_env = OF_env_main; ++ OF_node_t *pci_ata = dev; ++ OF_node_t *ata, *atas[2]; ++ int i; ++ ++ OF_prop_int_new(OF_env, pci_ata, "#address-cells", 1); ++ OF_prop_int_new(OF_env, pci_ata, "#size-cells", 0); ++ ++ /* XXX: Darwin handles only one device */ ++ for(i = 0; i < 1; i++) { ++ ata = OF_node_new(OF_env, pci_ata, "ata-4", i); ++ if (ata == NULL) { ++ ERROR("Cannot create 'ata-4'\n"); ++ return; ++ } ++ OF_prop_string_new(OF_env, ata, "device_type", "ata"); ++ OF_prop_string_new(OF_env, ata, "compatible", "cmd646-ata"); ++ OF_prop_string_new(OF_env, ata, "model", "ata-4"); ++ OF_prop_int_new(OF_env, ata, "#address-cells", 1); ++ OF_prop_int_new(OF_env, ata, "#size-cells", 0); ++ OF_prop_int_new(OF_env, ata, "reg", i); ++ atas[i] = ata; ++ } ++ ide_pci_pc_register(io_base0, io_base1, io_base2, io_base3, ++ atas[0], atas[1]); ++} ++ + /*****************************************************************************/ + /* Fake package */ + static void OF_method_fake (OF_env_t *OF_env) +@@ -2862,11 +3066,11 @@ + /* As we get a 1:1 mapping, do nothing */ + ihandle = popd(OF_env); + args = (void *)popd(OF_env); +- address = popd(OF_env); +- virt = popd(OF_env); +- size = popd(OF_env); + popd(OF_env); +- OF_DPRINTF("Translate address %0x %0x %0x %0x\n", ihandle, address, ++ size = popd(OF_env); ++ virt = popd(OF_env); ++ address = popd(OF_env); ++ OF_DPRINTF("Map %0x %0x %0x %0x\n", ihandle, address, + virt, size); + pushd(OF_env, 0); + } +@@ -3270,7 +3474,7 @@ + OF_prop_string_new(OF_env, dsk, "device_type", "block"); + OF_prop_string_new(OF_env, dsk, "category", type); + OF_prop_int_new(OF_env, dsk, "device_id", devnum); +- OF_prop_int_new(OF_env, dsk, "reg", 0); ++ OF_prop_int_new(OF_env, dsk, "reg", devnum); + OF_method_new(OF_env, dsk, "open", &OF_blockdev_open); + OF_method_new(OF_env, dsk, "seek", &OF_blockdev_seek); + OF_method_new(OF_env, dsk, "read", &OF_blockdev_read); +@@ -3432,7 +3636,8 @@ + } + + void OF_vga_register (const unsigned char *name, unused uint32_t address, +- int width, int height, int depth) ++ int width, int height, int depth, ++ unsigned long vga_bios_addr, unsigned long vga_bios_size) + { + OF_env_t *OF_env; + unsigned char tmp[OF_NAMELEN_MAX]; +@@ -3504,6 +3709,18 @@ + OF_prop_string_new(OF_env, als, "display", tmp); + OF_node_put(OF_env, als); + /* XXX: may also need read-rectangle */ ++ ++ if (vga_bios_size >= 8) { ++ const uint8_t *p; ++ int size; ++ /* check the QEMU VGA BIOS header */ ++ p = (const uint8_t *)vga_bios_addr; ++ if (p[0] == 'N' && p[1] == 'D' && p[2] == 'R' && p[3] == 'V') { ++ size = *(uint32_t *)(p + 4); ++ OF_property_new(OF_env, disp, "driver,AAPL,MacOS,PowerPC", ++ p + 8, size); ++ } ++ } + out: + OF_node_put(OF_env, disp); + } +@@ -4451,7 +4668,10 @@ + break; + case 0x233441d3: /* MacOS X 10.2 and OpenDarwin 1.41 */ + /* Create "memory-map" pseudo device */ +- popd(OF_env); ++ { ++ OF_node_t *map; ++ uint32_t phandle; ++ + /* Find "/packages" */ + chs = OF_pack_find_by_name(OF_env, OF_node_root, "/chosen"); + if (chs == NULL) { +@@ -4459,10 +4679,6 @@ + ERROR("Cannot get '/chosen'\n"); + break; + } +- { +-#if 1 +- OF_node_t *map; +- uint32_t phandle; + map = OF_node_new(OF_env, chs, "memory-map", OF_ADDRESS_NONE); + if (map == NULL) { + pushd(OF_env, -1); +@@ -4473,11 +4689,8 @@ + OF_node_put(OF_env, map); + OF_node_put(OF_env, chs); + pushd(OF_env, phandle); +- } +-#else +- pushd(OF_env, 0); +-#endif + pushd(OF_env, 0); ++ } + break; + case 0x32a2d18e: /* MacOS X 10.2 and OpenDarwin 6.02 */ + /* Return screen ihandle */ +@@ -4540,9 +4753,10 @@ + case 0x4ad41f2d: + /* Yaboot: wait 10 ms: sure ! */ + break; ++ + default: + /* ERROR */ +- printf("Script:\n%s\n", FString); ++ printf("Script: len=%d\n%s\n", (int)strlen(FString), FString); + printf("Call %0x NOT IMPLEMENTED !\n", crc); + bug(); + break; +@@ -4581,6 +4795,7 @@ + { + OF_CHECK_NBARGS(OF_env, 0); + /* Should free all OF resources */ ++ bd_reset_all(); + #if defined (DEBUG_BIOS) + { + uint16_t loglevel = 0x02 | 0x10 | 0x80; +diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/pci.c OpenHackWare-release-0.4/src/pci.c +--- OpenHackWare-release-0.4.org/src/pci.c 2005-03-31 09:23:33.000000000 +0200 ++++ OpenHackWare-release-0.4/src/pci.c 2005-07-07 23:27:37.000000000 +0200 +@@ -99,8 +99,8 @@ + uint16_t min_grant; + uint16_t max_latency; + uint8_t irq_line; +- uint32_t regions[6]; +- uint32_t sizes[6]; ++ uint32_t regions[7]; /* the region 6 is the PCI ROM */ ++ uint32_t sizes[7]; + pci_device_t *next; + }; + +@@ -158,6 +158,7 @@ + + /* IRQ numbers assigned to PCI IRQs */ + static uint8_t prep_pci_irqs[4] = { 9, 11, 9, 11 }; ++static uint8_t heathrow_pci_irqs[4] = { 0x15, 0x16, 0x17, 0x18 }; + static uint8_t pmac_pci_irqs[4] = { 8, 9, 10, 11 }; + + /* PREP PCI host */ +@@ -399,6 +400,79 @@ + &uninorth_config_readl, &uninorth_config_writel, + }; + ++/* Grackle PCI host */ ++ ++static uint32_t grackle_cfg_address (pci_bridge_t *bridge, ++ uint8_t bus, uint8_t devfn, ++ uint8_t offset) ++{ ++ uint32_t addr; ++ addr = 0x80000000 | (bus << 16) | (devfn << 8) | (offset & 0xfc); ++ stswap32((uint32_t *)bridge->cfg_addr, addr); ++ return bridge->cfg_data + (offset & 3); ++} ++ ++static uint8_t grackle_config_readb (pci_bridge_t *bridge, ++ uint8_t bus, uint8_t devfn, ++ uint8_t offset) ++{ ++ uint32_t addr; ++ addr = grackle_cfg_address(bridge, bus, devfn, offset); ++ return *((uint8_t *)addr); ++} ++ ++static void grackle_config_writeb (pci_bridge_t *bridge, ++ uint8_t bus, uint8_t devfn, ++ uint8_t offset, uint8_t val) ++{ ++ uint32_t addr; ++ addr = grackle_cfg_address(bridge, bus, devfn, offset); ++ *((uint8_t *)addr) = val; ++} ++ ++static uint16_t grackle_config_readw (pci_bridge_t *bridge, ++ uint8_t bus, uint8_t devfn, ++ uint8_t offset) ++{ ++ uint32_t addr; ++ addr = grackle_cfg_address(bridge, bus, devfn, offset); ++ return ldswap16((uint16_t *)addr); ++} ++ ++static void grackle_config_writew (pci_bridge_t *bridge, ++ uint8_t bus, uint8_t devfn, ++ uint8_t offset, uint16_t val) ++{ ++ uint32_t addr; ++ addr = grackle_cfg_address(bridge, bus, devfn, offset); ++ stswap16((uint16_t *)addr, val); ++} ++ ++static uint32_t grackle_config_readl (pci_bridge_t *bridge, ++ uint8_t bus, uint8_t devfn, ++ uint8_t offset) ++{ ++ uint32_t addr; ++ addr = grackle_cfg_address(bridge, bus, devfn, offset); ++ return ldswap32((uint32_t *)addr); ++} ++ ++static void grackle_config_writel (pci_bridge_t *bridge, ++ uint8_t bus, uint8_t devfn, ++ uint8_t offset, uint32_t val) ++{ ++ uint32_t addr; ++ ++ addr = grackle_cfg_address(bridge, bus, devfn, offset); ++ stswap32((uint32_t *)addr, val); ++} ++ ++static pci_ops_t grackle_pci_ops = { ++ &grackle_config_readb, &grackle_config_writeb, ++ &grackle_config_readw, &grackle_config_writew, ++ &grackle_config_readl, &grackle_config_writel, ++}; ++ + static inline uint8_t pci_config_readb (pci_bridge_t *bridge, + uint8_t bus, uint8_t devfn, + uint8_t offset) +@@ -466,12 +540,22 @@ + }, + }; + ++static int ide_config_cb2 (pci_device_t *device) ++{ ++ OF_finalize_pci_ide(device->common.OF_private, ++ device->regions[0] & ~0x0000000F, ++ device->regions[1] & ~0x0000000F, ++ device->regions[2] & ~0x0000000F, ++ device->regions[3] & ~0x0000000F); ++ return 0; ++} ++ + static pci_dev_t ide_devices[] = { + { +- 0x8086, 0x0100, +- NULL, "Qemu IDE", "Qemu IDE", "ide", ++ 0x1095, 0x0646, /* CMD646 IDE controller */ ++ "pci-ide", "pci-ata", NULL, NULL, + 0, 0, 0, +- NULL, NULL, ++ ide_config_cb2, NULL, + }, + { + 0xFFFF, 0xFFFF, +@@ -481,7 +565,9 @@ + }, + }; + +-static int ide_config_cb (pci_device_t *device) ++#if 0 ++/* should base it on PCI ID, not on arch */ ++static int ide_config_cb (unused pci_device_t *device) + { + printf("Register IDE controller\n"); + switch (arch) { +@@ -491,14 +577,8 @@ + device->common.OF_private); + break; + default: +- ide_pci_pc_register(device->regions[0] & ~0x0000000F, +- device->regions[1] & ~0x0000000F, +- device->regions[2] & ~0x0000000F, +- device->regions[3] & ~0x0000000F, +- device->common.OF_private); + break; + } +- + return 0; + } + +@@ -512,16 +592,12 @@ + device->common.OF_private); + break; + default: +- ide_pci_pc_register(device->regions[0] & ~0x0000000F, +- device->regions[1] & ~0x0000000F, +- device->regions[2] & ~0x0000000F, +- device->regions[3] & ~0x0000000F, +- device->common.OF_private); + break; + } + + return 0; + } ++#endif + + static pci_subclass_t mass_subclass[] = { + { +@@ -530,7 +606,7 @@ + }, + { + 0x01, "IDE controller", "ide", ide_devices, NULL, +- &ide_config_cb, NULL, ++ NULL, NULL, + }, + { + 0x02, "Floppy disk controller", NULL, NULL, NULL, +@@ -546,7 +622,7 @@ + }, + { + 0x05, "ATA controller", "ata", NULL, NULL, +- &ata_config_cb, NULL, ++ NULL, NULL, + }, + { + 0x80, "misc mass-storage controller", NULL, NULL, NULL, +@@ -646,7 +722,9 @@ + /* VGA 640x480x16 */ + OF_vga_register(device->common.device->name, + device->regions[0] & ~0x0000000F, +- vga_width, vga_height, vga_depth); ++ vga_width, vga_height, vga_depth, ++ device->regions[6] & ~0x0000000F, ++ device->sizes[6]); + } + vga_console_register(); + +@@ -750,6 +828,13 @@ + NULL, &PREP_pci_ops, + }; + ++pci_dev_t grackle_fake_bridge = { ++ 0xFFFF, 0xFFFF, ++ "pci", "pci-bridge", "DEC,21154", "DEC,21154.pci-bridge", ++ -1, -1, -1, ++ NULL, &grackle_pci_ops, ++}; ++ + static pci_dev_t hbrg_devices[] = { + { + 0x106B, 0x0020, NULL, +@@ -758,8 +843,8 @@ + NULL, &uninorth_agp_fake_bridge, + }, + { +- 0x106B, 0x001F, +- NULL, "pci", "AAPL,UniNorth", "uni-north", ++ 0x106B, 0x001F, NULL, ++ "pci", "AAPL,UniNorth", "uni-north", + 3, 2, 1, + NULL, &uninorth_fake_bridge, + }, +@@ -770,10 +855,10 @@ + NULL, &uninorth_fake_bridge, + }, + { +- 0x1011, 0x0026, NULL, +- "pci-bridge", NULL, NULL, ++ 0x1057, 0x0002, "pci", ++ "pci", "MOT,MPC106", "grackle", + 3, 2, 1, +- NULL, &PREP_pci_ops, ++ NULL, &grackle_fake_bridge, + }, + { + 0x1057, 0x4801, NULL, +@@ -1443,7 +1528,14 @@ + } + + static const pci_dev_t misc_pci[] = { +- /* Apple Mac-io controller */ ++ /* Paddington Mac I/O */ ++ { ++ 0x106B, 0x0017, ++ "mac-io", "mac-io", "AAPL,343S1211", "paddington\1heathrow", ++ 1, 1, 1, ++ &macio_config_cb, NULL, ++ }, ++ /* KeyLargo Mac I/O */ + { + 0x106B, 0x0022, + "mac-io", "mac-io", "AAPL,Keylargo", "Keylargo", +@@ -1599,7 +1691,7 @@ + uint8_t min_grant, uint8_t max_latency, + int irq_line) + { +- uint32_t cmd; ++ uint32_t cmd, addr; + int i; + + device->min_grant = min_grant; +@@ -1611,22 +1703,28 @@ + printf("MAP PCI device %d:%d to IRQ %d\n", + device->bus, device->devfn, irq_line); + } +- for (i = 0; i < 6; i++) { ++ for (i = 0; i < 7; i++) { + if ((device->regions[i] & ~0xF) != 0x00000000 && + (device->regions[i] & ~0xF) != 0xFFFFFFF0) { + printf("Map PCI device %d:%d %d to %0x %0x (%s)\n", + device->bus, device->devfn, i, + device->regions[i], device->sizes[i], +- device->regions[i] & 0x00000001 ? "I/O" : "memory"); ++ (device->regions[i] & 0x00000001) && i != 6 ? "I/O" : ++ "memory"); ++ if (i != 6) { + cmd = pci_config_readl(bridge, device->bus, device->devfn, 0x04); + if (device->regions[i] & 0x00000001) + cmd |= 0x00000001; + else + cmd |= 0x00000002; + pci_config_writel(bridge, device->bus, device->devfn, 0x04, cmd); ++ } ++ if (i == 6) ++ addr = 0x30; /* PCI ROM */ ++ else ++ addr = 0x10 + (i * sizeof(uint32_t)); + pci_config_writel(bridge, device->bus, device->devfn, +- 0x10 + (i * sizeof(uint32_t)), +- device->regions[i]); ++ addr, device->regions[i]); + } + } + } +@@ -1900,7 +1998,7 @@ + goto out; + } + ret = (pci_u_t *)newd; +- max_areas = 6; ++ max_areas = 7; + /* register PCI device in OF tree */ + if (bridge->dev.common.type == PCI_FAKE_BRIDGE) { + newd->common.OF_private = +@@ -1927,6 +2025,9 @@ + /* Handle 64 bits memory mapping */ + continue; + } ++ if (i == 6) ++ addr = 0x30; /* PCI ROM */ ++ else + addr = 0x10 + (i * sizeof(uint32_t)); + /* Get region size + * Note: we assume it's always a power of 2 +@@ -1935,7 +2036,7 @@ + smask = pci_config_readl(bridge, bus, devfn, addr); + if (smask == 0x00000000 || smask == 0xFFFFFFFF) + continue; +- if (smask & 0x00000001) { ++ if ((smask & 0x00000001) != 0 && i != 6) { + /* I/O space */ + base = io_base; + /* Align to a minimum of 256 bytes (arbitrary) */ +@@ -1947,6 +2048,8 @@ + /* Align to a minimum of 64 kB (arbitrary) */ + min_align = 1 << 16; + amask = 0x0000000F; ++ if (i == 6) ++ smask |= 1; /* PCI ROM enable */ + } + omask = smask & amask; + smask &= ~amask; +@@ -1980,7 +2083,10 @@ + if (irq_pin > 0) { + /* assign the IRQ */ + irq_pin = ((devfn >> 3) + irq_pin - 1) & 3; +- if (arch == ARCH_PREP) { ++ /* XXX: should base it on the PCI bridge type, not the arch */ ++ switch(arch) { ++ case ARCH_PREP: ++ { + int elcr_port, val; + irq_line = prep_pci_irqs[irq_pin]; + /* set the IRQ to level-sensitive */ +@@ -1988,14 +2094,22 @@ + val = inb(elcr_port); + val |= 1 << (irq_line & 7); + outb(elcr_port, val); +- } else { ++ } ++ break; ++ case ARCH_MAC99: + irq_line = pmac_pci_irqs[irq_pin]; ++ break; ++ case ARCH_HEATHROW: ++ irq_line = heathrow_pci_irqs[irq_pin]; ++ break; ++ default: ++ break; + } + } + update_device: + pci_update_device(bridge, newd, min_grant, max_latency, irq_line); + OF_finalize_pci_device(newd->common.OF_private, bus, devfn, +- newd->regions, newd->sizes); ++ newd->regions, newd->sizes, irq_line); + /* Call special inits if needed */ + if (dev->config_cb != NULL) + (*dev->config_cb)(newd); +@@ -2049,6 +2163,32 @@ + case ARCH_CHRP: + /* TODO */ + break; ++ case ARCH_HEATHROW: ++ dev = pci_find_device(0x06, 0x00, 0xFF, checkv, checkp); ++ if (dev == NULL) ++ return -1; ++ fake_host = pci_add_host(hostp, dev, ++ (0x06 << 24) | (0x00 << 16) | (0xFF << 8)); ++ if (fake_host == NULL) ++ return -1; ++ fake_host->dev.common.type = PCI_FAKE_HOST; ++ dev = &grackle_fake_bridge; ++ if (dev == NULL) ++ goto free_fake_host; ++ fake_bridge = pci_add_bridge(fake_host, 0, 0, dev, ++ (0x06 << 24) | (0x04 << 16) | (0xFF << 8), ++ cfg_base, cfg_len, ++ cfg_base + 0x7ec00000, ++ cfg_base + 0x7ee00000, ++ mem_base, mem_len, ++ io_base, io_len, ++ rbase, rlen, ++ 0, ++ &grackle_pci_ops); ++ if (fake_bridge == NULL) ++ goto free_fake_host; ++ fake_bridge->dev.common.type = PCI_FAKE_BRIDGE; ++ break; + case ARCH_MAC99: + dev = pci_find_device(0x06, 0x00, 0xFF, checkv, checkp); + if (dev == NULL) +@@ -2167,6 +2307,30 @@ + case ARCH_CHRP: + /* TODO */ + break; ++ case ARCH_HEATHROW: ++ cfg_base = 0x80000000; ++ cfg_len = 0x7f000000; ++ mem_base = 0x80000000; ++ mem_len = 0x01000000; ++ io_base = 0xfe000000; ++ io_len = 0x00800000; ++#if 1 ++ rbase = 0xfd000000; ++ rlen = 0x01000000; ++#else ++ rbase = 0x00000000; ++ rlen = 0x01000000; ++#endif ++ if (pci_check_host(&pci_main, cfg_base, cfg_len, ++ mem_base, mem_len, io_base, io_len, rbase, rlen, ++ 0x1057, 0x0002) == 0) { ++ isa_io_base = io_base; ++ busnum++; ++ } ++ for (curh = pci_main; curh->next != NULL; curh = curh->next) ++ continue; ++ pci_check_devices(curh); ++ break; + case ARCH_MAC99: + /* We are supposed to have 3 host bridges: + * - the uninorth AGP bridge at 0xF0000000 diff --git a/qemu/pc-bios/ppc_rom.bin b/qemu/pc-bios/ppc_rom.bin index 3755ff01e74f404e5b0ab85c7cb0a29cca46cb80..f7cd8a82e926031297c3a5f5eb33d1fc55ca2550 100644 GIT binary patch literal 524288 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�o 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 z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pgz<&|~A;)pHw~V>J zX!DR$^a*Fy_P-o^|Ms|Z|B`)9cEgwc{nqXOqosHIXU5*Y%5j>O#2u&M_V>iM-!}IC z^${npIN~^=#-&ct?TK@6?=*Y~VI3HIf6Q7z+G9bo#|e literal 524288 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 z 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 K(%YJw3jF_`GVRR( diff --git a/qemu/pc-bios/video.x b/qemu/pc-bios/video.x new file mode 100644 index 0000000000000000000000000000000000000000..761aa0c9d47fc3648a64ccfe9b9747b2a3af9572 GIT binary patch literal 12192 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 C=6}@y literal 0 HcmV?d00001 diff --git a/qemu/ppc-dis.c b/qemu/ppc-dis.c index fcced17..f6fad88 100644 --- a/qemu/ppc-dis.c +++ b/qemu/ppc-dis.c @@ -3067,11 +3067,12 @@ const struct powerpc_macro powerpc_macros[] = { const int powerpc_num_macros = sizeof (powerpc_macros) / sizeof (powerpc_macros[0]); -static int print_insn_powerpc(FILE *, uint32_t insn, unsigned memaddr, int dialect); +static int +print_insn_powerpc (disassemble_info *info, uint32_t insn, unsigned memaddr, + int dialect); /* Print a big endian PowerPC instruction. For convenience, also disassemble instructions supported by the Motorola PowerPC 601. */ -#include "cpu.h" int print_insn_ppc (bfd_vma pc, disassemble_info *info) { @@ -3083,14 +3084,19 @@ int print_insn_ppc (bfd_vma pc, disassemble_info *info) opc = bfd_getb32(buf); else opc = bfd_getl32(buf); - return print_insn_powerpc (info->stream, opc, pc, - PPC | B32 | M601); + if (info->mach == bfd_mach_ppc64) { + return print_insn_powerpc (info, opc, pc, + PPC | B64); + } else { + return print_insn_powerpc (info, opc, pc, + PPC | B32 | M601); + } } /* Print a PowerPC or POWER instruction. */ static int -print_insn_powerpc (FILE *out, uint32_t insn, unsigned memaddr, +print_insn_powerpc (disassemble_info *info, uint32_t insn, unsigned memaddr, int dialect) { const struct powerpc_opcode *opcode; @@ -3136,9 +3142,9 @@ print_insn_powerpc (FILE *out, uint32_t insn, unsigned memaddr, continue; /* The instruction is valid. */ - fprintf(out, "%s", opcode->name); + (*info->fprintf_func)(info->stream, "%s", opcode->name); if (opcode->operands[0] != 0) - fprintf(out, "\t"); + (*info->fprintf_func)(info->stream, "\t"); /* Now extract and print the operands. */ need_comma = 0; @@ -3175,26 +3181,26 @@ print_insn_powerpc (FILE *out, uint32_t insn, unsigned memaddr, if (need_comma) { - fprintf(out, ","); + (*info->fprintf_func)(info->stream, ","); need_comma = 0; } /* Print the operand as directed by the flags. */ if ((operand->flags & PPC_OPERAND_GPR) != 0) - fprintf(out, "r%d", value); + (*info->fprintf_func)(info->stream, "r%d", value); else if ((operand->flags & PPC_OPERAND_FPR) != 0) - fprintf(out, "f%d", value); + (*info->fprintf_func)(info->stream, "f%d", value); else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0) - fprintf(out, "%08X", memaddr + value); + (*info->fprintf_func)(info->stream, "%08X", memaddr + value); else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0) - fprintf(out, "%08X", value & 0xffffffff); + (*info->fprintf_func)(info->stream, "%08X", value & 0xffffffff); else if ((operand->flags & PPC_OPERAND_CR) == 0 || (dialect & PPC_OPCODE_PPC) == 0) - fprintf(out, "%d", value); + (*info->fprintf_func)(info->stream, "%d", value); else { if (operand->bits == 3) - fprintf(out, "cr%d", value); + (*info->fprintf_func)(info->stream, "cr%d", value); else { static const char *cbnames[4] = { "lt", "gt", "eq", "so" }; @@ -3203,20 +3209,20 @@ print_insn_powerpc (FILE *out, uint32_t insn, unsigned memaddr, cr = value >> 2; if (cr != 0) - fprintf(out, "4*cr%d", cr); + (*info->fprintf_func)(info->stream, "4*cr%d", cr); cc = value & 3; if (cc != 0) { if (cr != 0) - fprintf(out, "+"); - fprintf(out, "%s", cbnames[cc]); + (*info->fprintf_func)(info->stream, "+"); + (*info->fprintf_func)(info->stream, "%s", cbnames[cc]); } } } if (need_paren) { - fprintf(out, ")"); + (*info->fprintf_func)(info->stream, ")"); need_paren = 0; } @@ -3224,7 +3230,7 @@ print_insn_powerpc (FILE *out, uint32_t insn, unsigned memaddr, need_comma = 1; else { - fprintf(out, "("); + (*info->fprintf_func)(info->stream, "("); need_paren = 1; } } @@ -3234,7 +3240,7 @@ print_insn_powerpc (FILE *out, uint32_t insn, unsigned memaddr, } /* We could not find a match. */ - fprintf(out, ".long 0x%x", insn); + (*info->fprintf_func)(info->stream, ".long 0x%x", insn); return 4; } diff --git a/qemu/ppc.ld b/qemu/ppc.ld index d95b93c..d0a0dcb 100644 --- a/qemu/ppc.ld +++ b/qemu/ppc.ld @@ -53,6 +53,16 @@ SECTIONS _etext = .; PROVIDE (etext = .); .fini : { *(.fini) } =0x47ff041f + . = ALIGN(32 / 8); + PROVIDE (__preinit_array_start = .); + .preinit_array : { *(.preinit_array) } + PROVIDE (__preinit_array_end = .); + PROVIDE (__init_array_start = .); + .init_array : { *(.init_array) } + PROVIDE (__init_array_end = .); + PROVIDE (__fini_array_start = .); + .fini_array : { *(.fini_array) } + PROVIDE (__fini_array_end = .); .rodata : { *(.rodata) *(.gnu.linkonce.r*) } .rodata1 : { *(.rodata1) } .reginfo : { *(.reginfo) } diff --git a/qemu/qemu-doc.html b/qemu/qemu-doc.html index 50e3990..c2c8346 100644 --- a/qemu/qemu-doc.html +++ b/qemu/qemu-doc.html @@ -1,6 +1,6 @@ - + QEMU CPU Emulator User Documentation @@ -51,7 +51,7 @@

  • 3.10.3 MS-DOS and FreeDOS @@ -61,23 +61,25 @@
  • 4. QEMU PowerPC System emulator invocation -
  • 5. Sparc System emulator invocation -
  • 6. QEMU User space emulator invocation +
  • 5. Sparc32 System emulator invocation +
  • 6. Sparc64 System emulator invocation +
  • 7. MIPS System emulator invocation +
  • 8. QEMU User space emulator invocation -
  • 7. Compilation from the sources +
  • 9. Compilation from the sources


    @@ -109,7 +111,7 @@ QEMU has two operating modes:

  • Full system emulation. In this mode, QEMU emulates a full system (for -example a PC), including a processor and various peripherials. It can +example a PC), including a processor and various peripherals. It can be used to launch different Operating Systems without rebooting the PC or to debug system code. @@ -131,18 +133,24 @@ performance. For system emulation, the following hardware targets are supported:
      -
    • PC (x86 processor) +
    • PC (x86 or x86_64 processor)
    • PREP (PowerPC processor) -
    • PowerMac (PowerPC processor, in progress) +
    • G3 BW PowerMac (PowerPC processor) -
    • Sun4m (Sparc processor, in progress) +
    • Mac99 PowerMac (PowerPC processor, in progress) + +
    • Sun4m (32-bit Sparc processor) + +
    • Sun4u (64-bit Sparc processor, in progress) + +
    • Malta board (32-bit MIPS processor, in progress)

    -For user emulation, x86, PowerPC, ARM, and SPARC CPUs are supported. +For user emulation, x86, PowerPC, ARM, and Sparc32/64 CPUs are supported. @@ -150,7 +158,7 @@ For user emulation, x86, PowerPC, ARM, and SPARC CPUs are supported.

    2. Installation

    -If you want to compile QEMU yourself, see section 7. Compilation from the sources. +If you want to compile QEMU yourself, see section 9. Compilation from the sources. @@ -159,7 +167,7 @@ If you want to compile QEMU yourself, see section

    If a precompiled package is available for your distribution - you just -have to install it. Otherwise, see section 7. Compilation from the sources. +have to install it. Otherwise, see section 9. Compilation from the sources. @@ -190,7 +198,7 @@ Download the experimental binary installer at

    The QEMU System emulator simulates the -following PC peripherials: +following PC peripherals: @@ -344,6 +352,12 @@ Start in full screen. Store the QEMU process PID in file. It is useful if you launch QEMU from a script. +

    `-win2k-hack' +
    +Use it when installing Windows 2000 to avoid a disk full bug. After +Windows 2000 is installed, you no longer need this option (this option +slows down the IDE transfers). +

    @@ -358,6 +372,10 @@ Set TUN/TAP network init script [default=/etc/qemu-ifup]. This script is launched to configure the host network interface (usually tun0) corresponding to the virtual NE2000 card. +

    `-nics n' +
    +Simulate n network cards (the default is 1). +
    `-macaddr addr'
    Set the mac address of the first interface (the format is @@ -912,8 +930,7 @@ image format in QEMU. It is supported only for compatibility with previous versions. It does not work on win32.
    vmdk
    -VMware 3 and 4 compatible image format. Currently only supported as -read-only. +VMware 3 and 4 compatible image format.
    cloop
    Linux Compressed Loop image, useful only to reuse directly compressed @@ -1354,19 +1371,14 @@ problem. Note that no such tool is needed for NT, 2000 or XP. -

    3.10.2.3 Windows 2000 disk full problems

    +

    3.10.2.3 Windows 2000 disk full problem

    -Currently (release 0.6.0) QEMU has a bug which gives a disk -full error during installation of some releases of Windows 2000. The -workaround is to stop QEMU as soon as you notice that your disk image -size is growing too fast (monitor it with ls -ls). Then -relaunch QEMU to continue the installation. If you still experience -the problem, relaunch QEMU again. - - -

    -Future QEMU releases are likely to correct this bug. +Windows 2000 has a bug which gives a disk full problem during its +installation. When installing it, use the `-win2k-hack' QEMU +option to enable a specific workaround. After Windows 2000 is +installed, you no longer need this option (this option slows down the +IDE transfers). @@ -1416,7 +1428,7 @@ or PowerMac PowerPC system.

    -QEMU emulates the following PowerMac peripherials: +QEMU emulates the following PowerMac peripherals: @@ -1442,7 +1454,7 @@ VIA-CUDA with ADB keyboard and mouse.

    -QEMU emulates the following PREP peripherials: +QEMU emulates the following PREP peripherals: @@ -1506,15 +1518,15 @@ More information is available at -

    5. Sparc System emulator invocation

    +

    5. Sparc32 System emulator invocation

    Use the executable `qemu-system-sparc' to simulate a JavaStation -(sun4m architecture). The emulation is far from complete. +(sun4m architecture). The emulation is somewhat complete.

    -QEMU emulates the following sun4m peripherials: +QEMU emulates the following sun4m peripherals: @@ -1533,26 +1545,89 @@ Lance (Am7990) Ethernet Non Volatile RAM M48T08

  • -Slave I/O: timers, interrupt controllers, Zilog serial ports +Slave I/O: timers, interrupt controllers, Zilog serial ports, keyboard +and power/reset logic +
  • + +ESP SCSI controller with hard disk and CD-ROM support +
  • + +Floppy drive

    +The number of peripherals is fixed in the architecture. + + +

    QEMU uses the Proll, a PROM replacement available at -http://people.redhat.com/zaitcev/linux/. +http://people.redhat.com/zaitcev/linux/. The required +QEMU-specific patches are included with the sources. + + +

    +A sample Linux 2.6 series kernel and ram disk image are available on +the QEMU web site. Please note that currently neither Linux 2.4 +series, NetBSD, nor OpenBSD kernels work. + + +

    +The following options are specific to the Sparc emulation: + + +

    + +
    `-g WxH' +
    +Set the initial TCX graphic mode. The default is 1024x768. + +
    + + + +

    6. Sparc64 System emulator invocation

    + +

    +Use the executable `qemu-system-sparc64' to simulate a Sun4u machine. +The emulator is not usable for anything yet. + + +

    +QEMU emulates the following sun4u peripherals: + + + +

      +
    • + +UltraSparc IIi APB PCI Bridge +
    • + +PCI VGA compatible card with VESA Bochs Extensions +
    • + +Non Volatile RAM M48T59 +
    • + +PC-compatible serial ports +
    + + +

    7. MIPS System emulator invocation

    -A sample Linux kernel and ram disk image are available on the QEMU web -site. +Use the executable `qemu-system-mips' to simulate a MIPS machine. +The emulator begins to launch a Linux kernel. -

    6. QEMU User space emulator invocation

    +

    8. QEMU User space emulator invocation

    -

    6.1 Quick Start

    +

    8.1 Quick Start

    In order to launch a Linux process, QEMU needs the process executable @@ -1614,7 +1689,7 @@ qemu-i386 /usr/local/qemu-i386/bin/qemu-i386 /usr/local/qemu-i386/bin/ls-i386 -

    6.2 Wine launch

    +

    8.2 Wine launch

      @@ -1649,7 +1724,7 @@ qemu-i386 /usr/local/qemu-i386/wine/bin/wine /usr/local/qemu-i386/wine/c/Program -

      6.3 Command line options

      +

      8.3 Command line options

      @@ -1685,15 +1760,15 @@ Act as if the host page size was 'pagesize' bytes
       
       
       
      -

      7. Compilation from the sources

      +

      9. Compilation from the sources

      -

      7.1 Linux/Unix

      +

      9.1 Linux/Unix

      -

      7.1.1 Compilation

      +

      9.1.1 Compilation

      First you must decompress the sources: @@ -1725,7 +1800,7 @@ to install QEMU in `/usr/local'. -

      7.1.2 Tested tool versions

      +

      9.1.2 Tested tool versions

      In order to compile QEMU succesfully, it is very important that you @@ -1764,7 +1839,7 @@ variables. You must use gcc 3.x on PowerPC. -

      7.2 Windows

      +

      9.2 Windows

        @@ -1801,7 +1876,7 @@ correct SDL directory when invoked. -

        7.3 Cross compilation for Windows with Linux

        +

        9.3 Cross compilation for Windows with Linux

          @@ -1843,7 +1918,7 @@ QEMU for Win32. -

          7.4 Mac OS X

          +

          9.4 Mac OS X

          The Mac OS X patches are not fully merged in QEMU, so you should look @@ -1852,7 +1927,7 @@ information.


          -This document was generated on 26 April 2005 using +This document was generated on 24 July 2005 using texi2html 1.56k. diff --git a/qemu/qemu-doc.texi b/qemu/qemu-doc.texi index 106ecf4..17072a7 100644 --- a/qemu/qemu-doc.texi +++ b/qemu/qemu-doc.texi @@ -22,7 +22,7 @@ QEMU has two operating modes: @item Full system emulation. In this mode, QEMU emulates a full system (for -example a PC), including a processor and various peripherials. It can +example a PC), including a processor and various peripherals. It can be used to launch different Operating Systems without rebooting the PC or to debug system code. @@ -39,13 +39,16 @@ performance. For system emulation, the following hardware targets are supported: @itemize -@item PC (x86 processor) +@item PC (x86 or x86_64 processor) @item PREP (PowerPC processor) -@item PowerMac (PowerPC processor, in progress) -@item Sun4m (Sparc processor, in progress) +@item G3 BW PowerMac (PowerPC processor) +@item Mac99 PowerMac (PowerPC processor, in progress) +@item Sun4m (32-bit Sparc processor) +@item Sun4u (64-bit Sparc processor, in progress) +@item Malta board (32-bit MIPS processor, in progress) @end itemize -For user emulation, x86, PowerPC, ARM, and SPARC CPUs are supported. +For user emulation, x86, PowerPC, ARM, and Sparc32/64 CPUs are supported. @chapter Installation @@ -73,7 +76,7 @@ Download the experimental binary installer at @c man begin DESCRIPTION The QEMU System emulator simulates the -following PC peripherials: +following PC peripherals: @itemize @minus @item @@ -192,6 +195,11 @@ Start in full screen. Store the QEMU process PID in @var{file}. It is useful if you launch QEMU from a script. +@item -win2k-hack +Use it when installing Windows 2000 to avoid a disk full bug. After +Windows 2000 is installed, you no longer need this option (this option +slows down the IDE transfers). + @end table Network options: @@ -203,6 +211,10 @@ Set TUN/TAP network init script [default=/etc/qemu-ifup]. This script is launched to configure the host network interface (usually tun0) corresponding to the virtual NE2000 card. +@item -nics n + +Simulate @var{n} network cards (the default is 1). + @item -macaddr addr Set the mac address of the first interface (the format is @@ -892,16 +904,13 @@ idle. You can install the utility from @url{http://www.user.cityline.ru/~maxamn/amnhltm.zip} to solve this problem. Note that no such tool is needed for NT, 2000 or XP. -@subsubsection Windows 2000 disk full problems +@subsubsection Windows 2000 disk full problem -Currently (release 0.6.0) QEMU has a bug which gives a @code{disk -full} error during installation of some releases of Windows 2000. The -workaround is to stop QEMU as soon as you notice that your disk image -size is growing too fast (monitor it with @code{ls -ls}). Then -relaunch QEMU to continue the installation. If you still experience -the problem, relaunch QEMU again. - -Future QEMU releases are likely to correct this bug. +Windows 2000 has a bug which gives a disk full problem during its +installation. When installing it, use the @option{-win2k-hack} QEMU +option to enable a specific workaround. After Windows 2000 is +installed, you no longer need this option (this option slows down the +IDE transfers). @subsubsection Windows XP security problems @@ -930,7 +939,7 @@ problem. Use the executable @file{qemu-system-ppc} to simulate a complete PREP or PowerMac PowerPC system. -QEMU emulates the following PowerMac peripherials: +QEMU emulates the following PowerMac peripherals: @itemize @minus @item @@ -947,7 +956,7 @@ Non Volatile RAM VIA-CUDA with ADB keyboard and mouse. @end itemize -QEMU emulates the following PREP peripherials: +QEMU emulates the following PREP peripherals: @itemize @minus @item @@ -995,15 +1004,15 @@ Set the initial VGA graphic mode. The default is 800x600x15. More information is available at @url{http://jocelyn.mayer.free.fr/qemu-ppc/}. -@chapter Sparc System emulator invocation +@chapter Sparc32 System emulator invocation Use the executable @file{qemu-system-sparc} to simulate a JavaStation -(sun4m architecture). The emulation is far from complete. +(sun4m architecture). The emulation is somewhat complete. -QEMU emulates the following sun4m peripherials: +QEMU emulates the following sun4m peripherals: @itemize @minus -@item +@item IOMMU @item TCX Frame buffer @@ -1012,14 +1021,60 @@ Lance (Am7990) Ethernet @item Non Volatile RAM M48T08 @item -Slave I/O: timers, interrupt controllers, Zilog serial ports +Slave I/O: timers, interrupt controllers, Zilog serial ports, keyboard +and power/reset logic +@item +ESP SCSI controller with hard disk and CD-ROM support +@item +Floppy drive @end itemize +The number of peripherals is fixed in the architecture. + QEMU uses the Proll, a PROM replacement available at -@url{http://people.redhat.com/zaitcev/linux/}. +@url{http://people.redhat.com/zaitcev/linux/}. The required +QEMU-specific patches are included with the sources. + +A sample Linux 2.6 series kernel and ram disk image are available on +the QEMU web site. Please note that currently neither Linux 2.4 +series, NetBSD, nor OpenBSD kernels work. + +@c man begin OPTIONS + +The following options are specific to the Sparc emulation: + +@table @option + +@item -g WxH + +Set the initial TCX graphic mode. The default is 1024x768. + +@end table + +@c man end + +@chapter Sparc64 System emulator invocation + +Use the executable @file{qemu-system-sparc64} to simulate a Sun4u machine. +The emulator is not usable for anything yet. + +QEMU emulates the following sun4u peripherals: + +@itemize @minus +@item +UltraSparc IIi APB PCI Bridge +@item +PCI VGA compatible card with VESA Bochs Extensions +@item +Non Volatile RAM M48T59 +@item +PC-compatible serial ports +@end itemize + +@chapter MIPS System emulator invocation -A sample Linux kernel and ram disk image are available on the QEMU web -site. +Use the executable @file{qemu-system-mips} to simulate a MIPS machine. +The emulator begins to launch a Linux kernel. @chapter QEMU User space emulator invocation diff --git a/qemu/qemu-img.1 b/qemu/qemu-img.1 index dffa1ce..51abc2c 100644 --- a/qemu/qemu-img.1 +++ b/qemu/qemu-img.1 @@ -129,7 +129,7 @@ .\" ======================================================================== .\" .IX Title "QEMU-IMG 1" -.TH QEMU-IMG 1 "2005-04-26" " " " " +.TH QEMU-IMG 1 "2005-07-24" " " " " .SH "NAME" qemu\-img \- QEMU disk image utility .SH "SYNOPSIS" @@ -187,8 +187,7 @@ previous versions. It does not work on win32. .ie n .IP """vmdk""" 4 .el .IP "\f(CWvmdk\fR" 4 .IX Item "vmdk" -VMware 3 and 4 compatible image format. Currently only supported as -read\-only. +VMware 3 and 4 compatible image format. .ie n .IP """cloop""" 4 .el .IP "\f(CWcloop\fR" 4 .IX Item "cloop" diff --git a/qemu/qemu-img.c b/qemu/qemu-img.c index 31f8776..3a18c93 100644 --- a/qemu/qemu-img.c +++ b/qemu/qemu-img.c @@ -127,7 +127,7 @@ static void format_print(void *opaque, const char *name) void help(void) { - printf("qemu-img version " QEMU_VERSION ", Copyright (c) 2004 Fabrice Bellard\n" + printf("qemu-img version " QEMU_VERSION ", Copyright (c) 2004-2005 Fabrice Bellard\n" "usage: qemu-img command [command options]\n" "QEMU disk image utility\n" "\n" @@ -658,9 +658,10 @@ static int img_info(int argc, char **argv) get_human_readable_size(size_buf, sizeof(size_buf), total_sectors * 512); allocated_size = get_allocated_file_size(filename); if (allocated_size < 0) - error("Could not get file size '%s'", filename); - get_human_readable_size(dsize_buf, sizeof(dsize_buf), - allocated_size); + sprintf(dsize_buf, "unavailable"); + else + get_human_readable_size(dsize_buf, sizeof(dsize_buf), + allocated_size); printf("image: %s\n" "file format: %s\n" "virtual size: %s (%lld bytes)\n" diff --git a/qemu/qemu-img.texi b/qemu/qemu-img.texi index 3afd320..ac7923f 100644 --- a/qemu/qemu-img.texi +++ b/qemu/qemu-img.texi @@ -44,8 +44,7 @@ User Mode Linux Copy On Write image format. Used to be the only growable image format in QEMU. It is supported only for compatibility with previous versions. It does not work on win32. @item vmdk -VMware 3 and 4 compatible image format. Currently only supported as -read-only. +VMware 3 and 4 compatible image format. @item cloop Linux Compressed Loop image, useful only to reuse directly compressed CD-ROM images present for example in the Knoppix CD-ROMs. diff --git a/qemu/qemu-tech.html b/qemu/qemu-tech.html index 6890410..b6c948b 100644 --- a/qemu/qemu-tech.html +++ b/qemu/qemu-tech.html @@ -1,6 +1,6 @@ - + QEMU Internals @@ -76,7 +76,7 @@ QEMU has two operating modes:

        • Full system emulation. In this mode, QEMU emulates a full system -(usually a PC), including a processor and various peripherials. It can +(usually a PC), including a processor and various peripherals. It can be used to launch an different Operating System without rebooting the PC or to debug system code. @@ -228,9 +228,36 @@ FPU and MMU.
        • Somewhat complete SPARC V8 emulation, including privileged -instructions, FPU and MMU. +instructions, FPU and MMU. SPARC V9 emulation includes most privileged +instructions, FPU and I/D MMU, but misses VIS instructions. -
        • Can run some SPARC Linux binaries. +
        • Can run some 32-bit SPARC Linux binaries. + +
        + +

        +Current QEMU limitations: + + + +

          + +
        • Tagged add/subtract instructions are not supported, but they are + +probably not used. + +
        • IPC syscalls are missing. + +
        • 128-bit floating point operations are not supported, though none of the + +real CPUs implement them either. FCMPE[SD] are not correctly +implemented. Floating point exception support is untested. + +
        • Alignment is not enforced at all. + +
        • Atomic instructions are not correctly implemented. + +
        • Sparc64 emulators are not usable for anything yet.
        @@ -746,7 +773,7 @@ and host CPUs.

        Example of usage of libqemu to emulate a user mode i386 CPU.


        -This document was generated on 26 April 2005 using +This document was generated on 24 July 2005 using texi2html 1.56k. diff --git a/qemu/qemu-tech.texi b/qemu/qemu-tech.texi index 987b3c3..95d1787 100644 --- a/qemu/qemu-tech.texi +++ b/qemu/qemu-tech.texi @@ -22,7 +22,7 @@ QEMU has two operating modes: @item Full system emulation. In this mode, QEMU emulates a full system -(usually a PC), including a processor and various peripherials. It can +(usually a PC), including a processor and various peripherals. It can be used to launch an different Operating System without rebooting the PC or to debug system code. @@ -138,9 +138,31 @@ FPU and MMU. @itemize @item Somewhat complete SPARC V8 emulation, including privileged -instructions, FPU and MMU. +instructions, FPU and MMU. SPARC V9 emulation includes most privileged +instructions, FPU and I/D MMU, but misses VIS instructions. -@item Can run some SPARC Linux binaries. +@item Can run some 32-bit SPARC Linux binaries. + +@end itemize + +Current QEMU limitations: + +@itemize + +@item Tagged add/subtract instructions are not supported, but they are +probably not used. + +@item IPC syscalls are missing. + +@item 128-bit floating point operations are not supported, though none of the +real CPUs implement them either. FCMPE[SD] are not correctly +implemented. Floating point exception support is untested. + +@item Alignment is not enforced at all. + +@item Atomic instructions are not correctly implemented. + +@item Sparc64 emulators are not usable for anything yet. @end itemize diff --git a/qemu/qemu.1 b/qemu/qemu.1 index 3b78553..dff6013 100644 --- a/qemu/qemu.1 +++ b/qemu/qemu.1 @@ -129,7 +129,7 @@ .\" ======================================================================== .\" .IX Title "QEMU 1" -.TH QEMU 1 "2005-04-26" " " " " +.TH QEMU 1 "2005-07-24" " " " " .SH "NAME" qemu \- QEMU System Emulator .SH "SYNOPSIS" @@ -138,7 +138,7 @@ usage: qemu [options] [disk_image] .SH "DESCRIPTION" .IX Header "DESCRIPTION" The \s-1QEMU\s0 System emulator simulates the -following \s-1PC\s0 peripherials: +following \s-1PC\s0 peripherals: .IP "\-" 4 i440FX host \s-1PCI\s0 bridge and \s-1PIIX3\s0 \s-1PCI\s0 to \s-1ISA\s0 bridge .IP "\-" 4 @@ -239,6 +239,11 @@ Start in full screen. .IX Item "-pidfile file" Store the \s-1QEMU\s0 process \s-1PID\s0 in \fIfile\fR. It is useful if you launch \s-1QEMU\s0 from a script. +.IP "\fB\-win2k\-hack\fR" 4 +.IX Item "-win2k-hack" +Use it when installing Windows 2000 to avoid a disk full bug. After +Windows 2000 is installed, you no longer need this option (this option +slows down the \s-1IDE\s0 transfers). .PP Network options: .IP "\fB\-n script\fR" 4 @@ -246,6 +251,9 @@ Network options: Set \s-1TUN/TAP\s0 network init script [default=/etc/qemu\-ifup]. This script is launched to configure the host network interface (usually tun0) corresponding to the virtual \s-1NE2000\s0 card. +.IP "\fB\-nics n\fR" 4 +.IX Item "-nics n" +Simulate \fIn\fR network cards (the default is 1). .IP "\fB\-macaddr addr\fR" 4 .IX Item "-macaddr addr" Set the mac address of the first interface (the format is @@ -457,6 +465,11 @@ Simulate a \s-1PREP\s0 system (default is PowerMAC) .IP "\fB\-g WxH[xDEPTH]\fR" 4 .IX Item "-g WxH[xDEPTH]" Set the initial \s-1VGA\s0 graphic mode. The default is 800x600x15. +.PP +The following options are specific to the Sparc emulation: +.IP "\fB\-g WxH\fR" 4 +.IX Item "-g WxH" +Set the initial \s-1TCX\s0 graphic mode. The default is 1024x768. .SH "SEE ALSO" .IX Header "SEE ALSO" The \s-1HTML\s0 documentation of \s-1QEMU\s0 for more precise information and Linux diff --git a/qemu/sdl.c b/qemu/sdl.c index 20e45d9..d43da88 100644 --- a/qemu/sdl.c +++ b/qemu/sdl.c @@ -53,7 +53,6 @@ static void sdl_resize(DisplayState *ds, int w, int h) // printf("resizing to %d %d\n", w, h); flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL; - flags |= SDL_RESIZABLE; if (gui_fullscreen) flags |= SDL_FULLSCREEN; screen = SDL_SetVideoMode(w, h, 0, flags); diff --git a/qemu/slirp/bootp.c b/qemu/slirp/bootp.c index 56caf70..9f0652f 100644 --- a/qemu/slirp/bootp.c +++ b/qemu/slirp/bootp.c @@ -238,7 +238,7 @@ static void bootp_reply(struct bootp_t *bp) void bootp_input(struct mbuf *m) { - struct bootp_t *bp = (struct bootp_t *)m->m_data; + struct bootp_t *bp = mtod(m, struct bootp_t *); if (bp->bp_op == BOOTP_REQUEST) { bootp_reply(bp); diff --git a/qemu/slirp/bootp.h b/qemu/slirp/bootp.h index d3b2baa..e48f53f 100644 --- a/qemu/slirp/bootp.h +++ b/qemu/slirp/bootp.h @@ -97,9 +97,9 @@ struct bootp_t { uint8_t bp_htype; uint8_t bp_hlen; uint8_t bp_hops; - unsigned long bp_xid; - unsigned short bp_secs; - unsigned short unused; + uint32_t bp_xid; + uint16_t bp_secs; + uint16_t unused; struct in_addr bp_ciaddr; struct in_addr bp_yiaddr; struct in_addr bp_siaddr; diff --git a/qemu/slirp/ip_icmp.h b/qemu/slirp/ip_icmp.h index 7ddaaf8..8c9b5a1 100644 --- a/qemu/slirp/ip_icmp.h +++ b/qemu/slirp/ip_icmp.h @@ -83,8 +83,8 @@ struct icmp { struct ip idi_ip; /* options and then 64 bits of data */ } id_ip; - u_long id_mask; - char id_data[1]; + uint32_t id_mask; + char id_data[1]; } icmp_dun; #define icmp_otime icmp_dun.id_ts.its_otime #define icmp_rtime icmp_dun.id_ts.its_rtime diff --git a/qemu/slirp/libslirp.h b/qemu/slirp/libslirp.h index 6a54eb1..cff159e 100644 --- a/qemu/slirp/libslirp.h +++ b/qemu/slirp/libslirp.h @@ -9,6 +9,10 @@ int inet_aton(const char *cp, struct in_addr *ia); #include #endif +#ifdef __cplusplus +extern "C" { +#endif + void slirp_init(void); void slirp_select_fill(int *pnfds, @@ -29,4 +33,8 @@ int slirp_add_exec(int do_pty, const char *args, int addr_low_byte, extern const char *tftp_prefix; +#ifdef __cplusplus +} +#endif + #endif diff --git a/qemu/slirp/slirp_config.h b/qemu/slirp/slirp_config.h index 856c315..a0795ef 100644 --- a/qemu/slirp/slirp_config.h +++ b/qemu/slirp/slirp_config.h @@ -149,8 +149,7 @@ #define SIZEOF_INT 4 /* Define to sizeof(char *) */ -/* XXX: patch it */ -#define SIZEOF_CHAR_P 4 +#define SIZEOF_CHAR_P (HOST_LONG_BITS / 8) /* Define if you have random() */ #undef HAVE_RANDOM diff --git a/qemu/slirp/udp.c b/qemu/slirp/udp.c index aefa0b7..be51af6 100644 --- a/qemu/slirp/udp.c +++ b/qemu/slirp/udp.c @@ -420,10 +420,16 @@ struct talk_request { #endif struct cu_header { - char dest[8]; - short family; - u_short port; - u_long addr; + uint16_t d_family; // destination family + uint16_t d_port; // destination port + uint32_t d_addr; // destination address + uint16_t s_family; // source family + uint16_t s_port; // source port + uint32_t so_addr; // source address + uint32_t seqn; // sequence number + uint16_t message; // message + uint16_t data_type; // data type + uint16_t pkt_len; // packet length } *cu_head; switch(so->so_emu) { @@ -610,8 +616,8 @@ struct cu_header { if (getsockname(so->s, (struct sockaddr *)&addr, &addrlen) < 0) return; cu_head = mtod(m, struct cu_header *); - cu_head->port = addr.sin_port; - cu_head->addr = (u_long) our_addr.s_addr; + cu_head->s_port = addr.sin_port; + cu_head->so_addr = our_addr.s_addr; } return; diff --git a/qemu/slirp/udp.h b/qemu/slirp/udp.h index 195b1bf..24c11bb 100644 --- a/qemu/slirp/udp.h +++ b/qemu/slirp/udp.h @@ -94,6 +94,7 @@ struct udpstat { extern struct udpstat udpstat; extern struct socket udb; +struct mbuf; void udp_init _P((void)); void udp_input _P((register struct mbuf *, int)); diff --git a/qemu/softmmu_header.h b/qemu/softmmu_header.h index f709f70..0798cf5 100644 --- a/qemu/softmmu_header.h +++ b/qemu/softmmu_header.h @@ -55,6 +55,8 @@ #define CPU_MEM_INDEX ((env->hflags & HF_CPL_MASK) == 3) #elif defined (TARGET_PPC) #define CPU_MEM_INDEX (msr_pr) +#elif defined (TARGET_MIPS) +#define CPU_MEM_INDEX ((env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM) #elif defined (TARGET_SPARC) #define CPU_MEM_INDEX ((env->psrs) == 0) #endif @@ -66,6 +68,8 @@ #define CPU_MEM_INDEX ((env->hflags & HF_CPL_MASK) == 3) #elif defined (TARGET_PPC) #define CPU_MEM_INDEX (msr_pr) +#elif defined (TARGET_MIPS) +#define CPU_MEM_INDEX ((env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM) #elif defined (TARGET_SPARC) #define CPU_MEM_INDEX ((env->psrs) == 0) #endif diff --git a/qemu/softmmu_template.h b/qemu/softmmu_template.h index 5076726..558bb8b 100644 --- a/qemu/softmmu_template.h +++ b/qemu/softmmu_template.h @@ -48,7 +48,7 @@ static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(target_ulong addr, int is_user, void *retaddr); -static inline DATA_TYPE glue(io_read, SUFFIX)(unsigned long physaddr, +static inline DATA_TYPE glue(io_read, SUFFIX)(target_phys_addr_t physaddr, target_ulong tlb_addr) { DATA_TYPE res; @@ -76,7 +76,7 @@ DATA_TYPE REGPARM(1) glue(glue(__ld, SUFFIX), MMUSUFFIX)(target_ulong addr, DATA_TYPE res; int index; target_ulong tlb_addr; - unsigned long physaddr; + target_phys_addr_t physaddr; void *retaddr; /* test if there is match for unaligned or IO access */ @@ -99,7 +99,7 @@ DATA_TYPE REGPARM(1) glue(glue(__ld, SUFFIX), MMUSUFFIX)(target_ulong addr, is_user, retaddr); } else { /* unaligned access in the same page */ - res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)physaddr); + res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)(long)physaddr); } } else { /* the page is not in the TLB : fill it */ @@ -117,7 +117,7 @@ static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(target_ulong addr, { DATA_TYPE res, res1, res2; int index, shift; - unsigned long physaddr; + target_phys_addr_t physaddr; target_ulong tlb_addr, addr1, addr2; index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); @@ -148,7 +148,7 @@ static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(target_ulong addr, res = (DATA_TYPE)res; } else { /* unaligned/aligned access in the same page */ - res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)physaddr); + res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)(long)physaddr); } } else { /* the page is not in the TLB : fill it */ @@ -165,7 +165,7 @@ static void glue(glue(slow_st, SUFFIX), MMUSUFFIX)(target_ulong addr, int is_user, void *retaddr); -static inline void glue(io_write, SUFFIX)(unsigned long physaddr, +static inline void glue(io_write, SUFFIX)(target_phys_addr_t physaddr, DATA_TYPE val, target_ulong tlb_addr, void *retaddr) @@ -192,7 +192,7 @@ void REGPARM(2) glue(glue(__st, SUFFIX), MMUSUFFIX)(target_ulong addr, DATA_TYPE val, int is_user) { - unsigned long physaddr; + target_phys_addr_t physaddr; target_ulong tlb_addr; void *retaddr; int index; @@ -215,7 +215,7 @@ void REGPARM(2) glue(glue(__st, SUFFIX), MMUSUFFIX)(target_ulong addr, is_user, retaddr); } else { /* aligned/unaligned access in the same page */ - glue(glue(st, SUFFIX), _raw)((uint8_t *)physaddr, val); + glue(glue(st, SUFFIX), _raw)((uint8_t *)(long)physaddr, val); } } else { /* the page is not in the TLB : fill it */ @@ -231,7 +231,7 @@ static void glue(glue(slow_st, SUFFIX), MMUSUFFIX)(target_ulong addr, int is_user, void *retaddr) { - unsigned long physaddr; + target_phys_addr_t physaddr; target_ulong tlb_addr; int index, i; @@ -259,7 +259,7 @@ static void glue(glue(slow_st, SUFFIX), MMUSUFFIX)(target_ulong addr, } } else { /* aligned/unaligned access in the same page */ - glue(glue(st, SUFFIX), _raw)((uint8_t *)physaddr, val); + glue(glue(st, SUFFIX), _raw)((uint8_t *)(long)physaddr, val); } } else { /* the page is not in the TLB : fill it */ diff --git a/qemu/target-arm/op.c b/qemu/target-arm/op.c index 0a3811e..8a82def 100644 --- a/qemu/target-arm/op.c +++ b/qemu/target-arm/op.c @@ -805,6 +805,23 @@ void OPPROTO op_subl_T0_T1_saturate(void) FORCE_RET(); } +void OPPROTO op_double_T1_saturate(void) +{ + int32_t val; + + val = T1; + if (val >= 0x40000000) { + T1 = 0x7fffffff; + env->QF = 1; + } else if (val <= (int32_t)0xc0000000) { + T1 = 0x80000000; + env->QF = 1; + } else { + T1 = val << 1; + } + FORCE_RET(); +} + /* thumb shift by immediate */ void OPPROTO op_shll_T0_im_thumb(void) { diff --git a/qemu/target-arm/translate.c b/qemu/target-arm/translate.c index 2965741..adbd561 100644 --- a/qemu/target-arm/translate.c +++ b/qemu/target-arm/translate.c @@ -1019,20 +1019,15 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) case 0x5: /* saturating add/subtract */ rd = (insn >> 12) & 0xf; rn = (insn >> 16) & 0xf; - gen_movl_T0_reg(s, rn); - if (op1 & 2) { - gen_movl_T1_reg(s, rn); - if (op1 & 1) - gen_op_subl_T0_T1_saturate(); - else - gen_op_addl_T0_T1_saturate(); - } - gen_movl_T1_reg(s, rm); + gen_movl_T0_reg(s, rm); + gen_movl_T1_reg(s, rn); + if (op1 & 2) + gen_op_double_T1_saturate(); if (op1 & 1) gen_op_subl_T0_T1_saturate(); else gen_op_addl_T0_T1_saturate(); - gen_movl_reg_T0(s, rn); + gen_movl_reg_T0(s, rd); break; case 0x8: /* signed multiply */ case 0xa: @@ -2196,7 +2191,7 @@ void cpu_dump_state(CPUState *env, FILE *f, int flags) { int i; - struct { + union { uint32_t i; float s; } s0, s1; diff --git a/qemu/target-i386/cpu.h b/qemu/target-i386/cpu.h index 3549511..f8373a1 100644 --- a/qemu/target-i386/cpu.h +++ b/qemu/target-i386/cpu.h @@ -214,6 +214,12 @@ #define MSR_IA32_SYSENTER_ESP 0x175 #define MSR_IA32_SYSENTER_EIP 0x176 +#define MSR_MCG_CAP 0x179 +#define MSR_MCG_STATUS 0x17a +#define MSR_MCG_CTL 0x17b + +#define MSR_PAT 0x277 + #define MSR_EFER 0xc0000080 #define MSR_EFER_SCE (1 << 0) @@ -246,6 +252,8 @@ #define CPUID_PGE (1 << 13) #define CPUID_MCA (1 << 14) #define CPUID_CMOV (1 << 15) +#define CPUID_PAT (1 << 16) +#define CPUID_CLFLUSH (1 << 19) /* ... */ #define CPUID_MMX (1 << 23) #define CPUID_FXSR (1 << 24) @@ -474,6 +482,8 @@ typedef struct CPUX86State { target_ulong kernelgsbase; #endif + uint64_t pat; + /* temporary data for USE_CODE_COPY mode */ #ifdef USE_CODE_COPY uint32_t tmp0; diff --git a/qemu/target-i386/exec.h b/qemu/target-i386/exec.h index e4b6251..b114501 100644 --- a/qemu/target-i386/exec.h +++ b/qemu/target-i386/exec.h @@ -157,11 +157,11 @@ void helper_lldt_T0(void); void helper_ltr_T0(void); void helper_movl_crN_T0(int reg); void helper_movl_drN_T0(int reg); -void helper_invlpg(unsigned int addr); +void helper_invlpg(target_ulong addr); void cpu_x86_update_cr0(CPUX86State *env, uint32_t new_cr0); void cpu_x86_update_cr3(CPUX86State *env, target_ulong new_cr3); void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4); -void cpu_x86_flush_tlb(CPUX86State *env, uint32_t addr); +void cpu_x86_flush_tlb(CPUX86State *env, target_ulong addr); int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, int is_write, int is_user, int is_softmmu); void tlb_fill(target_ulong addr, int is_write, int is_user, @@ -190,6 +190,7 @@ void helper_idivq_EAX_T0(void); void helper_cmpxchg8b(void); void helper_cpuid(void); void helper_enter_level(int level, int data32); +void helper_enter64_level(int level, int data64); void helper_sysenter(void); void helper_sysexit(void); void helper_syscall(int next_eip_addend); @@ -336,6 +337,7 @@ static inline void stfl(target_ulong ptr, float v) #define atan2 atan2l #define floor floorl #define ceil ceill +#define ldexp ldexpl #else #define floatx_to_int32 float64_to_int32 #define floatx_to_int64 float64_to_int64 diff --git a/qemu/target-i386/helper.c b/qemu/target-i386/helper.c index 0d2cd74..01b663f 100644 --- a/qemu/target-i386/helper.c +++ b/qemu/target-i386/helper.c @@ -1209,13 +1209,13 @@ void raise_exception(int exception_index) #ifdef BUGGY_GCC_DIV64 /* gcc 2.95.4 on PowerPC does not seem to like using __udivdi3, so we call it from another function */ -uint32_t div32(uint32_t *q_ptr, uint64_t num, uint32_t den) +uint32_t div32(uint64_t *q_ptr, uint64_t num, uint32_t den) { *q_ptr = num / den; return num % den; } -int32_t idiv32(int32_t *q_ptr, int64_t num, int32_t den) +int32_t idiv32(int64_t *q_ptr, int64_t num, int32_t den) { *q_ptr = num / den; return num % den; @@ -1224,8 +1224,8 @@ int32_t idiv32(int32_t *q_ptr, int64_t num, int32_t den) void helper_divl_EAX_T0(void) { - unsigned int den, q, r; - uint64_t num; + unsigned int den, r; + uint64_t num, q; num = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32); den = T0; @@ -1238,14 +1238,16 @@ void helper_divl_EAX_T0(void) q = (num / den); r = (num % den); #endif + if (q > 0xffffffff) + raise_exception(EXCP00_DIVZ); EAX = (uint32_t)q; EDX = (uint32_t)r; } void helper_idivl_EAX_T0(void) { - int den, q, r; - int64_t num; + int den, r; + int64_t num, q; num = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32); den = T0; @@ -1258,6 +1260,8 @@ void helper_idivl_EAX_T0(void) q = (num / den); r = (num % den); #endif + if (q != (int32_t)q) + raise_exception(EXCP00_DIVZ); EAX = (uint32_t)q; EDX = (uint32_t)r; } @@ -1334,6 +1338,20 @@ void helper_cpuid(void) ECX = env->cpuid_model[(index - 0x80000002) * 4 + 2]; EDX = env->cpuid_model[(index - 0x80000002) * 4 + 3]; break; + case 0x80000005: + /* cache info (L1 cache) */ + EAX = 0x01ff01ff; + EBX = 0x01ff01ff; + ECX = 0x40020140; + EDX = 0x40020140; + break; + case 0x80000006: + /* cache info (L2 cache) */ + EAX = 0; + EBX = 0x42004200; + ECX = 0x02008140; + EDX = 0; + break; case 0x80000008: /* virtual & phys address size in low 2 bytes. */ EAX = 0x00003028; @@ -1383,6 +1401,37 @@ void helper_enter_level(int level, int data32) } } +#ifdef TARGET_X86_64 +void helper_enter64_level(int level, int data64) +{ + target_ulong esp, ebp; + ebp = EBP; + esp = ESP; + + if (data64) { + /* 64 bit */ + esp -= 8; + while (--level) { + esp -= 8; + ebp -= 8; + stq(esp, ldq(ebp)); + } + esp -= 8; + stq(esp, T1); + } else { + /* 16 bit */ + esp -= 2; + while (--level) { + esp -= 2; + ebp -= 2; + stw(esp, lduw(ebp)); + } + esp -= 2; + stw(esp, T1); + } +} +#endif + void helper_lldt_T0(void) { int selector; @@ -1963,6 +2012,7 @@ static inline void helper_ret_protected(int shift, int is_iret, int addend) #endif sp_mask = get_sp_mask(env->segs[R_SS].flags); sp = ESP; + /* XXX: ssp is zero in 64 bit ? */ ssp = env->segs[R_SS].base; new_eflags = 0; /* avoid warning */ #ifdef TARGET_X86_64 @@ -2271,7 +2321,7 @@ void helper_movl_drN_T0(int reg) env->dr[reg] = T0; } -void helper_invlpg(unsigned int addr) +void helper_invlpg(target_ulong addr) { cpu_x86_flush_tlb(env, addr); } @@ -2332,6 +2382,9 @@ void helper_wrmsr(void) case MSR_STAR: env->star = val; break; + case MSR_PAT: + env->pat = val; + break; #ifdef TARGET_X86_64 case MSR_LSTAR: env->lstar = val; @@ -2380,6 +2433,9 @@ void helper_rdmsr(void) case MSR_STAR: val = env->star; break; + case MSR_PAT: + val = env->pat; + break; #ifdef TARGET_X86_64 case MSR_LSTAR: val = env->lstar; @@ -2832,11 +2888,7 @@ void helper_frndint(void) void helper_fscale(void) { - CPU86_LDouble fpsrcop, fptemp; - - fpsrcop = 2.0; - fptemp = pow(fpsrcop,ST1); - ST0 *= fptemp; + ST0 = ldexp (ST0, (int)(ST1)); } void helper_fsin(void) @@ -3202,8 +3254,8 @@ static void imul64(uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b) } } -/* XXX: overflow support */ -static void div64(uint64_t *plow, uint64_t *phigh, uint64_t b) +/* return TRUE if overflow */ +static int div64(uint64_t *plow, uint64_t *phigh, uint64_t b) { uint64_t q, r, a1, a0; int i, qb; @@ -3216,6 +3268,8 @@ static void div64(uint64_t *plow, uint64_t *phigh, uint64_t b) *plow = q; *phigh = r; } else { + if (a1 >= b) + return 1; /* XXX: use a better algorithm */ for(i = 0; i < 64; i++) { a1 = (a1 << 1) | (a0 >> 63); @@ -3234,9 +3288,11 @@ static void div64(uint64_t *plow, uint64_t *phigh, uint64_t b) *plow = a0; *phigh = a1; } + return 0; } -static void idiv64(uint64_t *plow, uint64_t *phigh, int64_t b) +/* return TRUE if overflow */ +static int idiv64(uint64_t *plow, uint64_t *phigh, int64_t b) { int sa, sb; sa = ((int64_t)*phigh < 0); @@ -3245,11 +3301,19 @@ static void idiv64(uint64_t *plow, uint64_t *phigh, int64_t b) sb = (b < 0); if (sb) b = -b; - div64(plow, phigh, b); - if (sa ^ sb) + if (div64(plow, phigh, b) != 0) + return 1; + if (sa ^ sb) { + if (*plow > (1ULL << 63)) + return 1; *plow = - *plow; + } else { + if (*plow >= (1ULL << 63)) + return 1; + } if (sa) *phigh = - *phigh; + return 0; } void helper_mulq_EAX_T0(void) @@ -3292,7 +3356,8 @@ void helper_divq_EAX_T0(void) } r0 = EAX; r1 = EDX; - div64(&r0, &r1, T0); + if (div64(&r0, &r1, T0)) + raise_exception(EXCP00_DIVZ); EAX = r0; EDX = r1; } @@ -3305,7 +3370,8 @@ void helper_idivq_EAX_T0(void) } r0 = EAX; r1 = EDX; - idiv64(&r0, &r1, T0); + if (idiv64(&r0, &r1, T0)) + raise_exception(EXCP00_DIVZ); EAX = r0; EDX = r1; } diff --git a/qemu/target-i386/helper2.c b/qemu/target-i386/helper2.c index 24cbcfc..6033590 100644 --- a/qemu/target-i386/helper2.c +++ b/qemu/target-i386/helper2.c @@ -106,7 +106,9 @@ CPUX86State *cpu_x86_init(void) env->cpuid_version = (family << 8) | (model << 4) | stepping; env->cpuid_features = (CPUID_FP87 | CPUID_DE | CPUID_PSE | CPUID_TSC | CPUID_MSR | CPUID_MCE | - CPUID_CX8 | CPUID_PGE | CPUID_CMOV); + CPUID_CX8 | CPUID_PGE | CPUID_CMOV | + CPUID_PAT); + env->pat = 0x0007040600070406ULL; env->cpuid_ext_features = 0; env->cpuid_features |= CPUID_FXSR | CPUID_MMX | CPUID_SSE | CPUID_SSE2 | CPUID_PAE | CPUID_SEP; env->cpuid_xlevel = 0; @@ -128,6 +130,9 @@ CPUX86State *cpu_x86_init(void) env->cpuid_ext2_features = (env->cpuid_features & 0x0183F3FF); env->cpuid_ext2_features |= CPUID_EXT2_LM | CPUID_EXT2_SYSCALL; env->cpuid_xlevel = 0x80000008; + + /* these features are needed for Win64 and aren't fully implemented */ + env->cpuid_features |= CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA; #endif } cpu_single_env = env; @@ -250,7 +255,7 @@ void cpu_dump_state(CPUState *env, FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...), int flags) { - int eflags, i; + int eflags, i, nb; char cc_op_name[32]; static const char *seg_name[6] = { "ES", "CS", "SS", "DS", "FS", "GS" }; @@ -398,16 +403,54 @@ void cpu_dump_state(CPUState *env, FILE *f, } } if (flags & X86_DUMP_FPU) { - cpu_fprintf(f, "ST0=%f ST1=%f ST2=%f ST3=%f\n", - (double)env->fpregs[0].d, - (double)env->fpregs[1].d, - (double)env->fpregs[2].d, - (double)env->fpregs[3].d); - cpu_fprintf(f, "ST4=%f ST5=%f ST6=%f ST7=%f\n", - (double)env->fpregs[4].d, - (double)env->fpregs[5].d, - (double)env->fpregs[7].d, - (double)env->fpregs[8].d); + int fptag; + fptag = 0; + for(i = 0; i < 8; i++) { + fptag |= ((!env->fptags[i]) << i); + } + cpu_fprintf(f, "FCW=%04x FSW=%04x [ST=%d] FTW=%02x MXCSR=%08x\n", + env->fpuc, + (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11, + env->fpstt, + fptag, + env->mxcsr); + for(i=0;i<8;i++) { +#if defined(USE_X86LDOUBLE) + union { + long double d; + struct { + uint64_t lower; + uint16_t upper; + } l; + } tmp; + tmp.d = env->fpregs[i].d; + cpu_fprintf(f, "FPR%d=%016llx %04x", + i, tmp.l.lower, tmp.l.upper); +#else + cpu_fprintf(f, "FPR%d=%016llx", + i, env->fpregs[i].mmx.q); +#endif + if ((i & 1) == 1) + cpu_fprintf(f, "\n"); + else + cpu_fprintf(f, " "); + } + if (env->hflags & HF_CS64_MASK) + nb = 16; + else + nb = 8; + for(i=0;ixmm_regs[i].XMM_L(3), + env->xmm_regs[i].XMM_L(2), + env->xmm_regs[i].XMM_L(1), + env->xmm_regs[i].XMM_L(0)); + if ((i & 1) == 1) + cpu_fprintf(f, "\n"); + else + cpu_fprintf(f, " "); + } } } @@ -508,7 +551,7 @@ void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4) } /* XXX: also flush 4MB pages */ -void cpu_x86_flush_tlb(CPUX86State *env, uint32_t addr) +void cpu_x86_flush_tlb(CPUX86State *env, target_ulong addr) { tlb_flush_page(env, addr); } diff --git a/qemu/target-i386/op.c b/qemu/target-i386/op.c index 2af4e8e..142b662 100644 --- a/qemu/target-i386/op.c +++ b/qemu/target-i386/op.c @@ -328,7 +328,6 @@ void OPPROTO op_imulq_T0_T1(void) #endif /* division, flags are undefined */ -/* XXX: add exceptions for overflow */ void OPPROTO op_divb_AL_T0(void) { @@ -339,7 +338,10 @@ void OPPROTO op_divb_AL_T0(void) if (den == 0) { raise_exception(EXCP00_DIVZ); } - q = (num / den) & 0xff; + q = (num / den); + if (q > 0xff) + raise_exception(EXCP00_DIVZ); + q &= 0xff; r = (num % den) & 0xff; EAX = (EAX & ~0xffff) | (r << 8) | q; } @@ -353,7 +355,10 @@ void OPPROTO op_idivb_AL_T0(void) if (den == 0) { raise_exception(EXCP00_DIVZ); } - q = (num / den) & 0xff; + q = (num / den); + if (q != (int8_t)q) + raise_exception(EXCP00_DIVZ); + q &= 0xff; r = (num % den) & 0xff; EAX = (EAX & ~0xffff) | (r << 8) | q; } @@ -367,7 +372,10 @@ void OPPROTO op_divw_AX_T0(void) if (den == 0) { raise_exception(EXCP00_DIVZ); } - q = (num / den) & 0xffff; + q = (num / den); + if (q > 0xffff) + raise_exception(EXCP00_DIVZ); + q &= 0xffff; r = (num % den) & 0xffff; EAX = (EAX & ~0xffff) | q; EDX = (EDX & ~0xffff) | r; @@ -382,7 +390,10 @@ void OPPROTO op_idivw_AX_T0(void) if (den == 0) { raise_exception(EXCP00_DIVZ); } - q = (num / den) & 0xffff; + q = (num / den); + if (q != (int16_t)q) + raise_exception(EXCP00_DIVZ); + q &= 0xffff; r = (num % den) & 0xffff; EAX = (EAX & ~0xffff) | q; EDX = (EDX & ~0xffff) | r; @@ -898,6 +909,11 @@ void op_addw_ESP_im(void) } #ifdef TARGET_X86_64 +void op_subq_A0_2(void) +{ + A0 -= 2; +} + void op_subq_A0_8(void) { A0 -= 8; @@ -929,6 +945,13 @@ void OPPROTO op_enter_level(void) helper_enter_level(PARAM1, PARAM2); } +#ifdef TARGET_X86_64 +void OPPROTO op_enter64_level(void) +{ + helper_enter64_level(PARAM1, PARAM2); +} +#endif + void OPPROTO op_sysenter(void) { helper_sysenter(); diff --git a/qemu/target-i386/translate.c b/qemu/target-i386/translate.c index c4584a0..619522a 100644 --- a/qemu/target-i386/translate.c +++ b/qemu/target-i386/translate.c @@ -1627,7 +1627,14 @@ static void gen_add_A0_ds_seg(DisasContext *s) override = R_DS; } if (must_add_seg) { - gen_op_addl_A0_seg(offsetof(CPUX86State,segs[override].base)); +#ifdef TARGET_X86_64 + if (CODE64(s)) { + gen_op_addq_A0_seg(offsetof(CPUX86State,segs[override].base)); + } else +#endif + { + gen_op_addl_A0_seg(offsetof(CPUX86State,segs[override].base)); + } } } @@ -1948,10 +1955,14 @@ static void gen_push_T0(DisasContext *s) { #ifdef TARGET_X86_64 if (CODE64(s)) { - /* XXX: check 16 bit behaviour */ gen_op_movq_A0_reg[R_ESP](); - gen_op_subq_A0_8(); - gen_op_st_T0_A0[OT_QUAD + s->mem_index](); + if (s->dflag) { + gen_op_subq_A0_8(); + gen_op_st_T0_A0[OT_QUAD + s->mem_index](); + } else { + gen_op_subq_A0_2(); + gen_op_st_T0_A0[OT_WORD + s->mem_index](); + } gen_op_movq_ESP_A0(); } else #endif @@ -1985,10 +1996,14 @@ static void gen_push_T1(DisasContext *s) { #ifdef TARGET_X86_64 if (CODE64(s)) { - /* XXX: check 16 bit behaviour */ gen_op_movq_A0_reg[R_ESP](); - gen_op_subq_A0_8(); - gen_op_st_T1_A0[OT_QUAD + s->mem_index](); + if (s->dflag) { + gen_op_subq_A0_8(); + gen_op_st_T1_A0[OT_QUAD + s->mem_index](); + } else { + gen_op_subq_A0_2(); + gen_op_st_T0_A0[OT_WORD + s->mem_index](); + } gen_op_movq_ESP_A0(); } else #endif @@ -2020,9 +2035,8 @@ static void gen_pop_T0(DisasContext *s) { #ifdef TARGET_X86_64 if (CODE64(s)) { - /* XXX: check 16 bit behaviour */ gen_op_movq_A0_reg[R_ESP](); - gen_op_ld_T0_A0[OT_QUAD + s->mem_index](); + gen_op_ld_T0_A0[(s->dflag ? OT_QUAD : OT_WORD) + s->mem_index](); } else #endif { @@ -2041,7 +2055,7 @@ static void gen_pop_T0(DisasContext *s) static void gen_pop_update(DisasContext *s) { #ifdef TARGET_X86_64 - if (CODE64(s)) { + if (CODE64(s) && s->dflag) { gen_stack_update(s, 8); } else #endif @@ -2105,26 +2119,48 @@ static void gen_enter(DisasContext *s, int esp_addend, int level) { int ot, opsize; - ot = s->dflag + OT_WORD; level &= 0x1f; - opsize = 2 << s->dflag; - - gen_op_movl_A0_ESP(); - gen_op_addl_A0_im(-opsize); - if (!s->ss32) - gen_op_andl_A0_ffff(); - gen_op_movl_T1_A0(); - if (s->addseg) - gen_op_addl_A0_seg(offsetof(CPUX86State,segs[R_SS].base)); - /* push bp */ - gen_op_mov_TN_reg[OT_LONG][0][R_EBP](); - gen_op_st_T0_A0[ot + s->mem_index](); - if (level) { - gen_op_enter_level(level, s->dflag); +#ifdef TARGET_X86_64 + if (CODE64(s)) { + ot = s->dflag ? OT_QUAD : OT_WORD; + opsize = 1 << ot; + + gen_op_movl_A0_ESP(); + gen_op_addq_A0_im(-opsize); + gen_op_movl_T1_A0(); + + /* push bp */ + gen_op_mov_TN_reg[OT_LONG][0][R_EBP](); + gen_op_st_T0_A0[ot + s->mem_index](); + if (level) { + gen_op_enter64_level(level, (ot == OT_QUAD)); + } + gen_op_mov_reg_T1[ot][R_EBP](); + gen_op_addl_T1_im( -esp_addend + (-opsize * level) ); + gen_op_mov_reg_T1[OT_QUAD][R_ESP](); + } else +#endif + { + ot = s->dflag + OT_WORD; + opsize = 2 << s->dflag; + + gen_op_movl_A0_ESP(); + gen_op_addl_A0_im(-opsize); + if (!s->ss32) + gen_op_andl_A0_ffff(); + gen_op_movl_T1_A0(); + if (s->addseg) + gen_op_addl_A0_seg(offsetof(CPUX86State,segs[R_SS].base)); + /* push bp */ + gen_op_mov_TN_reg[OT_LONG][0][R_EBP](); + gen_op_st_T0_A0[ot + s->mem_index](); + if (level) { + gen_op_enter_level(level, s->dflag); + } + gen_op_mov_reg_T1[ot][R_EBP](); + gen_op_addl_T1_im( -esp_addend + (-opsize * level) ); + gen_op_mov_reg_T1[OT_WORD + s->ss32][R_ESP](); } - gen_op_mov_reg_T1[ot][R_EBP](); - gen_op_addl_T1_im( -esp_addend + (-opsize * level) ); - gen_op_mov_reg_T1[OT_WORD + s->ss32][R_ESP](); } static void gen_exception(DisasContext *s, int trapno, target_ulong cur_eip) @@ -2901,7 +2937,7 @@ static void gen_sse(DisasContext *s, int b, target_ulong pc_start, int rex_r) if (mod != 3) goto illegal_op; #ifdef TARGET_X86_64 - if (CODE64(s)) { + if (s->aflag == 2) { gen_op_movq_A0_reg[R_EDI](); } else #endif @@ -3697,7 +3733,6 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) break; case 0xc8: /* enter */ { - /* XXX: long mode support */ int level; val = lduw_code(s->pc); s->pc += 2; @@ -3707,7 +3742,6 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) break; case 0xc9: /* leave */ /* XXX: exception not precise (ESP is updated before potential exception) */ - /* XXX: may be invalid for 16 bit in long mode */ if (CODE64(s)) { gen_op_mov_TN_reg[OT_QUAD][0][R_EBP](); gen_op_mov_reg_T0[OT_QUAD][R_ESP](); @@ -3926,7 +3960,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) else ot = dflag + OT_WORD; #ifdef TARGET_X86_64 - if (CODE64(s)) { + if (s->aflag == 2) { offset_addr = ldq_code(s->pc); s->pc += 8; if (offset_addr == (int32_t)offset_addr) @@ -3955,7 +3989,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) break; case 0xd7: /* xlat */ #ifdef TARGET_X86_64 - if (CODE64(s)) { + if (s->aflag == 2) { gen_op_movq_A0_reg[R_EBX](); gen_op_addq_A0_AL(); } else @@ -4779,6 +4813,8 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) val = ldsw_code(s->pc); s->pc += 2; gen_pop_T0(s); + if (CODE64(s) && s->dflag) + s->dflag = 2; gen_stack_update(s, val + (2 << s->dflag)); if (s->dflag == 0) gen_op_andl_T0_ffff(); @@ -5782,14 +5818,30 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) break; case 5: /* lfence */ case 6: /* mfence */ - case 7: /* sfence */ if ((modrm & 0xc7) != 0xc0 || !(s->cpuid_features & CPUID_SSE)) goto illegal_op; break; + case 7: /* sfence / clflush */ + if ((modrm & 0xc7) == 0xc0) { + /* sfence */ + if (!(s->cpuid_features & CPUID_SSE)) + goto illegal_op; + } else { + /* clflush */ + if (!(s->cpuid_features & CPUID_CLFLUSH)) + goto illegal_op; + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + } + break; default: goto illegal_op; } break; + case 0x10d: /* prefetch */ + modrm = ldub_code(s->pc++); + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + /* ignore for now */ + break; case 0x110 ... 0x117: case 0x128 ... 0x12f: case 0x150 ... 0x177: diff --git a/qemu/target-mips/cpu.h b/qemu/target-mips/cpu.h new file mode 100644 index 0000000..8ed09d5 --- /dev/null +++ b/qemu/target-mips/cpu.h @@ -0,0 +1,247 @@ +#if !defined (__MIPS_CPU_H__) +#define __MIPS_CPU_H__ + +#include "mips-defs.h" +#include "cpu-defs.h" +#include "config.h" +#include "softfloat.h" + +typedef union fpr_t fpr_t; +union fpr_t { + double d; + float f; + uint32_t u[2]; +}; + +#if defined(MIPS_USES_R4K_TLB) +typedef struct tlb_t tlb_t; +struct tlb_t { + target_ulong VPN; + target_ulong end; + uint8_t ASID; + uint8_t G; + uint8_t C[2]; + uint8_t V[2]; + uint8_t D[2]; + target_ulong PFN[2]; +}; +#endif + +typedef struct CPUMIPSState CPUMIPSState; +struct CPUMIPSState { + /* General integer registers */ + target_ulong gpr[32]; + /* Special registers */ + target_ulong PC; + uint32_t HI, LO; + uint32_t DCR; /* ? */ +#if defined(MIPS_USES_FPU) + /* Floating point registers */ + fpr_t fpr[16]; + /* Floating point special purpose registers */ + uint32_t fcr0; + uint32_t fcr25; + uint32_t fcr26; + uint32_t fcr28; + uint32_t fcsr; +#endif +#if defined(MIPS_USES_R4K_TLB) + tlb_t tlb[16]; +#endif + uint32_t CP0_index; + uint32_t CP0_random; + uint32_t CP0_EntryLo0; + uint32_t CP0_EntryLo1; + uint32_t CP0_Context; + uint32_t CP0_PageMask; + uint32_t CP0_Wired; + uint32_t CP0_BadVAddr; + uint32_t CP0_Count; + uint32_t CP0_EntryHi; + uint32_t CP0_Compare; + uint32_t CP0_Status; +#define CP0St_CU3 31 +#define CP0St_CU2 30 +#define CP0St_CU1 29 +#define CP0St_CU0 28 +#define CP0St_RP 27 +#define CP0St_RE 25 +#define CP0St_BEV 22 +#define CP0St_TS 21 +#define CP0St_SR 20 +#define CP0St_NMI 19 +#define CP0St_IM 8 +#define CP0St_UM 4 +#define CP0St_ERL 2 +#define CP0St_EXL 1 +#define CP0St_IE 0 + uint32_t CP0_Cause; +#define CP0Ca_IV 23 + uint32_t CP0_EPC; + uint32_t CP0_PRid; + uint32_t CP0_Config0; +#define CP0C0_M 31 +#define CP0C0_K23 28 +#define CP0C0_KU 25 +#define CP0C0_MDU 20 +#define CP0C0_MM 17 +#define CP0C0_BM 16 +#define CP0C0_BE 15 +#define CP0C0_AT 13 +#define CP0C0_AR 10 +#define CP0C0_MT 7 +#define CP0C0_K0 0 + uint32_t CP0_Config1; +#define CP0C1_MMU 25 +#define CP0C1_IS 22 +#define CP0C1_IL 19 +#define CP0C1_IA 16 +#define CP0C1_DS 13 +#define CP0C1_DL 10 +#define CP0C1_DA 7 +#define CP0C1_PC 4 +#define CP0C1_WR 3 +#define CP0C1_CA 2 +#define CP0C1_EP 1 +#define CP0C1_FP 0 + uint32_t CP0_LLAddr; + uint32_t CP0_WatchLo; + uint32_t CP0_WatchHi; + uint32_t CP0_Debug; +#define CPDB_DBD 31 +#define CP0DB_DM 30 +#define CP0DB_LSNM 28 +#define CP0DB_Doze 27 +#define CP0DB_Halt 26 +#define CP0DB_CNT 25 +#define CP0DB_IBEP 24 +#define CP0DB_DBEP 21 +#define CP0DB_IEXI 20 +#define CP0DB_VER 15 +#define CP0DB_DEC 10 +#define CP0DB_SSt 8 +#define CP0DB_DINT 5 +#define CP0DB_DIB 4 +#define CP0DB_DDBS 3 +#define CP0DB_DDBL 2 +#define CP0DB_DBp 1 +#define CP0DB_DSS 0 + uint32_t CP0_DEPC; + uint32_t CP0_TagLo; + uint32_t CP0_DataLo; + uint32_t CP0_ErrorEPC; + uint32_t CP0_DESAVE; + /* Qemu */ +#if defined (USE_HOST_FLOAT_REGS) && defined(MIPS_USES_FPU) + double ft0, ft1, ft2; +#endif + struct QEMUTimer *timer; /* Internal timer */ + int interrupt_request; + jmp_buf jmp_env; + int exception_index; + int error_code; + int user_mode_only; /* user mode only simulation */ + uint32_t hflags; /* CPU State */ + /* TMASK defines different execution modes */ +#define MIPS_HFLAGS_TMASK 0x00FF +#define MIPS_HFLAG_MODE 0x001F /* execution modes */ +#define MIPS_HFLAG_UM 0x0001 /* user mode */ +#define MIPS_HFLAG_ERL 0x0002 /* Error mode */ +#define MIPS_HFLAG_EXL 0x0004 /* Exception mode */ +#define MIPS_HFLAG_DM 0x0008 /* Debug mode */ +#define MIPS_HFLAG_SM 0x0010 /* Supervisor mode */ +#define MIPS_HFLAG_RE 0x0040 /* Reversed endianness */ +#define MIPS_HFLAG_DS 0x0080 /* In / out of delay slot */ + /* Those flags keep the branch state if the translation is interrupted + * between the branch instruction and the delay slot + */ +#define MIPS_HFLAG_BMASK 0x0F00 +#define MIPS_HFLAG_B 0x0100 /* Unconditional branch */ +#define MIPS_HFLAG_BC 0x0200 /* Conditional branch */ +#define MIPS_HFLAG_BL 0x0400 /* Likely branch */ +#define MIPS_HFLAG_BR 0x0800 /* branch to register (can't link TB) */ + target_ulong btarget; /* Jump / branch target */ + int bcond; /* Branch condition (if needed) */ + struct TranslationBlock *current_tb; /* currently executing TB */ + /* soft mmu support */ + /* in order to avoid passing too many arguments to the memory + write helpers, we store some rarely used information in the CPU + context) */ + target_ulong mem_write_pc; /* host pc at which the memory was + written */ + unsigned long mem_write_vaddr; /* target virtual addr at which the + memory was written */ + /* 0 = kernel, 1 = user (may have 2 = kernel code, 3 = user code ?) */ + CPUTLBEntry tlb_read[2][CPU_TLB_SIZE]; + CPUTLBEntry tlb_write[2][CPU_TLB_SIZE]; + /* ice debug support */ + target_ulong breakpoints[MAX_BREAKPOINTS]; + int nb_breakpoints; + int singlestep_enabled; /* XXX: should use CPU single step mode instead */ + /* user data */ + void *opaque; +}; + +#include "cpu-all.h" + +/* Memory access type : + * may be needed for precise access rights control and precise exceptions. + */ +enum { + /* 1 bit to define user level / supervisor access */ + ACCESS_USER = 0x00, + ACCESS_SUPER = 0x01, + /* 1 bit to indicate direction */ + ACCESS_STORE = 0x02, + /* Type of instruction that generated the access */ + ACCESS_CODE = 0x10, /* Code fetch access */ + ACCESS_INT = 0x20, /* Integer load/store access */ + ACCESS_FLOAT = 0x30, /* floating point load/store access */ +}; + +/* Exceptions */ +enum { + EXCP_NONE = -1, + EXCP_RESET = 0, + EXCP_SRESET, + EXCP_DSS, + EXCP_DINT, + EXCP_NMI, + EXCP_MCHECK, + EXCP_EXT_INTERRUPT, + EXCP_DFWATCH, + EXCP_DIB, /* 8 */ + EXCP_IWATCH, + EXCP_AdEL, + EXCP_AdES, + EXCP_TLBF, + EXCP_IBE, + EXCP_DBp, + EXCP_SYSCALL, + EXCP_BREAK, + EXCP_CpU, /* 16 */ + EXCP_RI, + EXCP_OVERFLOW, + EXCP_TRAP, + EXCP_DDBS, + EXCP_DWATCH, + EXCP_LAE, /* 22 */ + EXCP_SAE, + EXCP_LTLBL, + EXCP_TLBL, + EXCP_TLBS, + EXCP_DBE, + EXCP_DDBL, + EXCP_MTCP0 = 0x104, /* mtmsr instruction: */ + /* may change privilege level */ + EXCP_BRANCH = 0x108, /* branch instruction */ + EXCP_ERET = 0x10C, /* return from interrupt */ + EXCP_SYSCALL_USER = 0x110, /* System call in user mode only */ + EXCP_FLUSH = 0x109, +}; + +int cpu_mips_exec(CPUMIPSState *s); +CPUMIPSState *cpu_mips_init(void); +uint32_t cpu_mips_get_clock (void); + +#endif /* !defined (__MIPS_CPU_H__) */ diff --git a/qemu/target-mips/exec.h b/qemu/target-mips/exec.h new file mode 100644 index 0000000..77fce26 --- /dev/null +++ b/qemu/target-mips/exec.h @@ -0,0 +1,166 @@ +#if !defined(__QEMU_MIPS_EXEC_H__) +#define __QEMU_MIPS_EXEC_H__ + +#define DEBUG_OP + +#include "mips-defs.h" +#include "dyngen-exec.h" + +register struct CPUMIPSState *env asm(AREG0); + +#if defined (USE_64BITS_REGS) +typedef int64_t host_int_t; +typedef uint64_t host_uint_t; +#else +typedef int32_t host_int_t; +typedef uint32_t host_uint_t; +#endif + +register host_uint_t T0 asm(AREG1); +register host_uint_t T1 asm(AREG2); +register host_uint_t T2 asm(AREG3); + +#if defined (USE_HOST_FLOAT_REGS) +register double FT0 asm(FREG0); +register double FT1 asm(FREG1); +register double FT2 asm(FREG2); +#else +#define FT0 (env->ft0.d) +#define FT1 (env->ft1.d) +#define FT2 (env->ft2.d) +#endif + +#if defined (DEBUG_OP) +#define RETURN() __asm__ __volatile__("nop"); +#else +#define RETURN() __asm__ __volatile__(""); +#endif + +#include "cpu.h" +#include "exec-all.h" + +#if !defined(CONFIG_USER_ONLY) + +#define ldul_user ldl_user +#define ldul_kernel ldl_kernel + +#define ACCESS_TYPE 0 +#define MEMSUFFIX _kernel +#define DATA_SIZE 1 +#include "softmmu_header.h" + +#define DATA_SIZE 2 +#include "softmmu_header.h" + +#define DATA_SIZE 4 +#include "softmmu_header.h" + +#define DATA_SIZE 8 +#include "softmmu_header.h" +#undef ACCESS_TYPE +#undef MEMSUFFIX + +#define ACCESS_TYPE 1 +#define MEMSUFFIX _user +#define DATA_SIZE 1 +#include "softmmu_header.h" + +#define DATA_SIZE 2 +#include "softmmu_header.h" + +#define DATA_SIZE 4 +#include "softmmu_header.h" + +#define DATA_SIZE 8 +#include "softmmu_header.h" +#undef ACCESS_TYPE +#undef MEMSUFFIX + +/* these access are slower, they must be as rare as possible */ +#define ACCESS_TYPE 2 +#define MEMSUFFIX _data +#define DATA_SIZE 1 +#include "softmmu_header.h" + +#define DATA_SIZE 2 +#include "softmmu_header.h" + +#define DATA_SIZE 4 +#include "softmmu_header.h" + +#define DATA_SIZE 8 +#include "softmmu_header.h" +#undef ACCESS_TYPE +#undef MEMSUFFIX + +#define ldub(p) ldub_data(p) +#define ldsb(p) ldsb_data(p) +#define lduw(p) lduw_data(p) +#define ldsw(p) ldsw_data(p) +#define ldl(p) ldl_data(p) +#define ldq(p) ldq_data(p) + +#define stb(p, v) stb_data(p, v) +#define stw(p, v) stw_data(p, v) +#define stl(p, v) stl_data(p, v) +#define stq(p, v) stq_data(p, v) + +#endif /* !defined(CONFIG_USER_ONLY) */ + +static inline void env_to_regs(void) +{ +} + +static inline void regs_to_env(void) +{ +} + +#if (HOST_LONG_BITS == 32) +void do_mult (void); +void do_multu (void); +void do_madd (void); +void do_maddu (void); +void do_msub (void); +void do_msubu (void); +#endif +void do_mfc0(int reg, int sel); +void do_mtc0(int reg, int sel); +void do_tlbwi (void); +void do_tlbwr (void); +void do_tlbp (void); +void do_tlbr (void); +void do_lwl_raw (void); +void do_lwr_raw (void); +void do_swl_raw (void); +void do_swr_raw (void); +#if !defined(CONFIG_USER_ONLY) +void do_lwl_user (void); +void do_lwl_kernel (void); +void do_lwr_user (void); +void do_lwr_kernel (void); +void do_swl_user (void); +void do_swl_kernel (void); +void do_swr_user (void); +void do_swr_kernel (void); +#endif +void do_pmon (int function); + +int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw, + int is_user, int is_softmmu); +void do_interrupt (CPUState *env); + +void cpu_loop_exit(void); +void do_raise_exception_err (uint32_t exception, int error_code); +void do_raise_exception (uint32_t exception); + +void cpu_dump_state(CPUState *env, FILE *f, + int (*cpu_fprintf)(FILE *f, const char *fmt, ...), + int flags); +void cpu_mips_irqctrl_init (void); +uint32_t cpu_mips_get_random (CPUState *env); +uint32_t cpu_mips_get_count (CPUState *env); +void cpu_mips_store_count (CPUState *env, uint32_t value); +void cpu_mips_store_compare (CPUState *env, uint32_t value); +void cpu_mips_clock_init (CPUState *env); + +#endif /* !defined(__QEMU_MIPS_EXEC_H__) */ diff --git a/qemu/target-mips/helper.c b/qemu/target-mips/helper.c new file mode 100644 index 0000000..8d26175 --- /dev/null +++ b/qemu/target-mips/helper.c @@ -0,0 +1,422 @@ +/* + * MIPS emulation helpers for qemu. + * + * Copyright (c) 2004-2005 Jocelyn Mayer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include + +#include "cpu.h" +#include "exec-all.h" + +/* MIPS32 4K MMU emulation */ +#ifdef MIPS_USES_R4K_TLB +static int map_address (CPUState *env, target_ulong *physical, int *prot, + target_ulong address, int rw, int access_type) +{ + tlb_t *tlb; + target_ulong tag; + uint8_t ASID; + int i, n; + int ret; + + ret = -2; + tag = (address & 0xFFFFE000); + ASID = env->CP0_EntryHi & 0x000000FF; + for (i = 0; i < MIPS_TLB_NB; i++) { + tlb = &env->tlb[i]; + /* Check ASID, virtual page number & size */ + if ((tlb->G == 1 || tlb->ASID == ASID) && + tlb->VPN == tag && address < tlb->end) { + /* TLB match */ + n = (address >> 12) & 1; + /* Check access rights */ + if ((tlb->V[n] & 2) && (rw == 0 || (tlb->D[n] & 4))) { + *physical = tlb->PFN[n] | (address & 0xFFF); + *prot = PAGE_READ; + if (tlb->D[n]) + *prot |= PAGE_WRITE; + return 0; + } else if (!(tlb->V[n] & 2)) { + return -3; + } else { + return -4; + } + } + } + + return ret; +} +#endif + +int get_physical_address (CPUState *env, target_ulong *physical, int *prot, + target_ulong address, int rw, int access_type) +{ + int user_mode; + int ret; + + /* User mode can only access useg */ + user_mode = ((env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM) ? 1 : 0; +#if 0 + if (logfile) { + fprintf(logfile, "user mode %d h %08x\n", + user_mode, env->hflags); + } +#endif + if (user_mode && address > 0x7FFFFFFFUL) + return -1; + ret = 0; + if (address < 0x80000000UL) { + if (!(env->hflags & MIPS_HFLAG_ERL)) { +#ifdef MIPS_USES_R4K_TLB + ret = map_address(env, physical, prot, address, rw, access_type); +#else + *physical = address + 0x40000000UL; + *prot = PAGE_READ | PAGE_WRITE; +#endif + } else { + *physical = address; + *prot = PAGE_READ | PAGE_WRITE; + } + } else if (address < 0xA0000000UL) { + /* kseg0 */ + /* XXX: check supervisor mode */ + *physical = address - 0x80000000UL; + *prot = PAGE_READ | PAGE_WRITE; + } else if (address < 0xC0000000UL) { + /* kseg1 */ + /* XXX: check supervisor mode */ + *physical = address - 0xA0000000UL; + *prot = PAGE_READ | PAGE_WRITE; + } else if (address < 0xE0000000UL) { + /* kseg2 */ +#ifdef MIPS_USES_R4K_TLB + ret = map_address(env, physical, prot, address, rw, access_type); +#else + *physical = address; + *prot = PAGE_READ | PAGE_WRITE; +#endif + } else { + /* kseg3 */ + /* XXX: check supervisor mode */ + /* XXX: debug segment is not emulated */ +#ifdef MIPS_USES_R4K_TLB + ret = map_address(env, physical, prot, address, rw, access_type); +#else + *physical = address; + *prot = PAGE_READ | PAGE_WRITE; +#endif + } +#if 0 + if (logfile) { + fprintf(logfile, "%08x %d %d => %08x %d (%d)\n", address, rw, + access_type, *physical, *prot, ret); + } +#endif + + return ret; +} + +#if defined(CONFIG_USER_ONLY) +target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr) +{ + return addr; +} +#else +target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr) +{ + target_ulong phys_addr; + int prot; + + if (get_physical_address(env, &phys_addr, &prot, addr, 0, ACCESS_INT) != 0) + return -1; + return phys_addr; +} + +void cpu_mips_init_mmu (CPUState *env) +{ +} +#endif /* !defined(CONFIG_USER_ONLY) */ + +int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw, + int is_user, int is_softmmu) +{ + target_ulong physical; + int prot; + int exception = 0, error_code = 0; + int access_type; + int ret = 0; + + if (logfile) { + cpu_dump_state(env, logfile, fprintf, 0); + fprintf(logfile, "%s pc %08x ad %08x rw %d is_user %d smmu %d\n", + __func__, env->PC, address, rw, is_user, is_softmmu); + } + /* data access */ + /* XXX: put correct access by using cpu_restore_state() + correctly */ + access_type = ACCESS_INT; + if (env->user_mode_only) { + /* user mode only emulation */ + ret = -2; + goto do_fault; + } + ret = get_physical_address(env, &physical, &prot, + address, rw, access_type); + if (logfile) { + fprintf(logfile, "%s address=%08x ret %d physical %08x prot %d\n", + __func__, address, ret, physical, prot); + } + if (ret == 0) { + ret = tlb_set_page(env, address & ~0xFFF, physical & ~0xFFF, prot, + is_user, is_softmmu); + } else if (ret < 0) { + do_fault: + switch (ret) { + default: + case -1: + /* Reference to kernel address from user mode or supervisor mode */ + /* Reference to supervisor address from user mode */ + if (rw) + exception = EXCP_AdES; + else + exception = EXCP_AdEL; + break; + case -2: + /* No TLB match for a mapped address */ + if (rw) + exception = EXCP_TLBS; + else + exception = EXCP_TLBL; + error_code = 1; + break; + case -3: + /* TLB match with no valid bit */ + if (rw) + exception = EXCP_TLBS; + else + exception = EXCP_TLBL; + error_code = 0; + break; + case -4: + /* TLB match but 'D' bit is cleared */ + exception = EXCP_LTLBL; + break; + + } + /* Raise exception */ + env->CP0_BadVAddr = address; + env->CP0_Context = (env->CP0_Context & 0xff800000) | + ((address >> 8) & 0x007ffff0); + env->CP0_EntryHi = + (env->CP0_EntryHi & 0x000000FF) | (address & 0xFFFFF000); + env->exception_index = exception; + env->error_code = error_code; + ret = 1; + } + + return ret; +} + +void do_interrupt (CPUState *env) +{ + target_ulong pc, offset; + int cause = -1; + + if (logfile && env->exception_index != EXCP_EXT_INTERRUPT) { + fprintf(logfile, "%s enter: PC %08x EPC %08x cause %d excp %d\n", + __func__, env->PC, env->CP0_EPC, cause, env->exception_index); + } + if (env->exception_index == EXCP_EXT_INTERRUPT && + (env->hflags & MIPS_HFLAG_DM)) + env->exception_index = EXCP_DINT; + offset = 0x180; + switch (env->exception_index) { + case EXCP_DSS: + env->CP0_Debug |= 1 << CP0DB_DSS; + /* Debug single step cannot be raised inside a delay slot and + * resume will always occur on the next instruction + * (but we assume the pc has always been updated during + * code translation). + */ + env->CP0_DEPC = env->PC; + goto enter_debug_mode; + case EXCP_DINT: + env->CP0_Debug |= 1 << CP0DB_DINT; + goto set_DEPC; + case EXCP_DIB: + env->CP0_Debug |= 1 << CP0DB_DIB; + goto set_DEPC; + case EXCP_DBp: + env->CP0_Debug |= 1 << CP0DB_DBp; + goto set_DEPC; + case EXCP_DDBS: + env->CP0_Debug |= 1 << CP0DB_DDBS; + goto set_DEPC; + case EXCP_DDBL: + env->CP0_Debug |= 1 << CP0DB_DDBL; + goto set_DEPC; + set_DEPC: + if (env->hflags & MIPS_HFLAG_DS) { + /* If the exception was raised from a delay slot, + * come back to the jump + */ + env->CP0_DEPC = env->PC - 4; + } else { + env->CP0_DEPC = env->PC; + } + enter_debug_mode: + env->hflags |= MIPS_HFLAG_DM; + /* EJTAG probe trap enable is not implemented... */ + pc = 0xBFC00480; + break; + case EXCP_RESET: +#ifdef MIPS_USES_R4K_TLB + env->CP0_random = MIPS_TLB_NB - 1; +#endif + env->CP0_Wired = 0; + env->CP0_Config0 = MIPS_CONFIG0; +#if defined (MIPS_CONFIG1) + env->CP0_Config1 = MIPS_CONFIG1; +#endif +#if defined (MIPS_CONFIG2) + env->CP0_Config2 = MIPS_CONFIG2; +#endif +#if defined (MIPS_CONFIG3) + env->CP0_Config3 = MIPS_CONFIG3; +#endif + env->CP0_WatchLo = 0; + env->CP0_Status = (1 << CP0St_CU0) | (1 << CP0St_BEV); + goto set_error_EPC; + case EXCP_SRESET: + env->CP0_Status = (1 << CP0St_CU0) | (1 << CP0St_BEV) | + (1 << CP0St_SR); + env->CP0_WatchLo = 0; + goto set_error_EPC; + case EXCP_NMI: + env->CP0_Status = (1 << CP0St_CU0) | (1 << CP0St_BEV) | + (1 << CP0St_NMI); + set_error_EPC: + env->hflags = MIPS_HFLAG_ERL; + if (env->hflags & MIPS_HFLAG_DS) { + /* If the exception was raised from a delay slot, + * come back to the jump + */ + env->CP0_ErrorEPC = env->PC - 4; + } else { + env->CP0_ErrorEPC = env->PC; + } + pc = 0xBFC00000; + break; + case EXCP_MCHECK: + cause = 24; + goto set_EPC; + case EXCP_EXT_INTERRUPT: + cause = 0; + if (env->CP0_Cause & (1 << CP0Ca_IV)) + offset = 0x200; + goto set_EPC; + case EXCP_DWATCH: + cause = 23; + /* XXX: TODO: manage defered watch exceptions */ + goto set_EPC; + case EXCP_AdEL: + case EXCP_AdES: + cause = 4; + goto set_EPC; + case EXCP_TLBL: + case EXCP_TLBF: + cause = 2; + if (env->error_code == 1 && !(env->hflags & MIPS_HFLAG_EXL)) + offset = 0x000; + goto set_EPC; + case EXCP_IBE: + cause = 6; + goto set_EPC; + case EXCP_DBE: + cause = 7; + goto set_EPC; + case EXCP_SYSCALL: + cause = 8; + goto set_EPC; + case EXCP_BREAK: + cause = 9; + goto set_EPC; + case EXCP_RI: + cause = 10; + goto set_EPC; + case EXCP_CpU: + cause = 11; + /* XXX: fill in the faulty unit number */ + goto set_EPC; + case EXCP_OVERFLOW: + cause = 12; + goto set_EPC; + case EXCP_TRAP: + cause = 13; + goto set_EPC; + case EXCP_LTLBL: + cause = 1; + goto set_EPC; + case EXCP_TLBS: + cause = 3; + if (env->error_code == 1 && !(env->hflags & MIPS_HFLAG_EXL)) + offset = 0x000; + goto set_EPC; + set_EPC: + if (env->CP0_Status & (1 << CP0St_BEV)) { + pc = 0xBFC00200; + } else { + pc = 0x80000000; + } + env->hflags |= MIPS_HFLAG_EXL; + pc += offset; + env->CP0_Cause = (env->CP0_Cause & ~0x7C) | (cause << 2); + if (env->hflags & MIPS_HFLAG_DS) { + /* If the exception was raised from a delay slot, + * come back to the jump + */ + env->CP0_EPC = env->PC - 4; + env->CP0_Cause |= 0x80000000; + } else { + env->CP0_EPC = env->PC; + env->CP0_Cause &= ~0x80000000; + } + break; + default: + if (logfile) { + fprintf(logfile, "Invalid MIPS exception %d. Exiting\n", + env->exception_index); + } + printf("Invalid MIPS exception %d. Exiting\n", env->exception_index); + exit(1); + } + env->PC = pc; + if (logfile && env->exception_index != EXCP_EXT_INTERRUPT) { + fprintf(logfile, "%s: PC %08x EPC %08x cause %d excp %d\n" + " S %08x C %08x A %08x D %08x\n", + __func__, env->PC, env->CP0_EPC, cause, env->exception_index, + env->CP0_Status, env->CP0_Cause, env->CP0_BadVAddr, + env->CP0_DEPC); + } + env->exception_index = EXCP_NONE; +} diff --git a/qemu/target-mips/mips-defs.h b/qemu/target-mips/mips-defs.h new file mode 100644 index 0000000..6d28e9c --- /dev/null +++ b/qemu/target-mips/mips-defs.h @@ -0,0 +1,58 @@ +#if !defined (__QEMU_MIPS_DEFS_H__) +#define __QEMU_MIPS_DEFS_H__ + +/* If we want to use 64 bits host regs... */ +//#define USE_64BITS_REGS +/* If we want to use host float regs... */ +//#define USE_HOST_FLOAT_REGS + +enum { + MIPS_R4Kc = 0x00018000, + MIPS_R4Kp = 0x00018300, +}; + +/* Emulate MIPS R4Kc for now */ +#define MIPS_CPU MIPS_R4Kc + +#if (MIPS_CPU == MIPS_R4Kc) +/* 32 bits target */ +#define TARGET_LONG_BITS 32 +/* real pages are variable size... */ +#define TARGET_PAGE_BITS 12 +/* Uses MIPS R4Kx ehancements to MIPS32 architecture */ +#define MIPS_USES_R4K_EXT +/* Uses MIPS R4Kc TLB model */ +#define MIPS_USES_R4K_TLB +#define MIPS_TLB_NB 16 +/* Have config1, runs in big-endian mode, uses TLB */ +#define MIPS_CONFIG0 \ +((1 << CP0C0_M) | (0x000 << CP0C0_K23) | (0x000 << CP0C0_KU) | \ + (1 << CP0C0_BE) | (0x001 << CP0C0_MT) | (0x010 << CP0C0_K0)) +/* 16 TLBs, 64 sets Icache, 16 bytes Icache line, 2-way Icache, + * 64 sets Dcache, 16 bytes Dcache line, 2-way Dcache, + * no performance counters, watch registers present, no code compression, + * EJTAG present, no FPU + */ +#define MIPS_CONFIG1 \ +((15 << CP0C1_MMU) | \ + (0x000 << CP0C1_IS) | (0x3 << CP0C1_IL) | (0x01 << CP0C1_IA) | \ + (0x000 << CP0C1_DS) | (0x3 << CP0C1_DL) | (0x01 << CP0C1_DA) | \ + (0 << CP0C1_PC) | (1 << CP0C1_WR) | (0 << CP0C1_CA) | \ + (1 << CP0C1_EP) | (0 << CP0C1_FP)) +#elif defined (MIPS_CPU == MIPS_R4Kp) +/* 32 bits target */ +#define TARGET_LONG_BITS 32 +/* real pages are variable size... */ +#define TARGET_PAGE_BITS 12 +/* Uses MIPS R4Kx ehancements to MIPS32 architecture */ +#define MIPS_USES_R4K_EXT +/* Uses MIPS R4Km FPM MMU model */ +#define MIPS_USES_R4K_FPM +#else +#error "MIPS CPU not defined" +/* Remainder for other flags */ +//#define TARGET_MIPS64 +//define MIPS_USES_FPU +#endif + +#endif /* !defined (__QEMU_MIPS_DEFS_H__) */ diff --git a/qemu/target-mips/op.c b/qemu/target-mips/op.c new file mode 100644 index 0000000..32f7e4f --- /dev/null +++ b/qemu/target-mips/op.c @@ -0,0 +1,668 @@ +/* + * MIPS emulation micro-operations for qemu. + * + * Copyright (c) 2004-2005 Jocelyn Mayer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" +#include "exec.h" + +#ifndef CALL_FROM_TB0 +#define CALL_FROM_TB0(func) func(); +#endif +#ifndef CALL_FROM_TB1 +#define CALL_FROM_TB1(func, arg0) func(arg0); +#endif +#ifndef CALL_FROM_TB1_CONST16 +#define CALL_FROM_TB1_CONST16(func, arg0) CALL_FROM_TB1(func, arg0); +#endif +#ifndef CALL_FROM_TB2 +#define CALL_FROM_TB2(func, arg0, arg1) func(arg0, arg1); +#endif +#ifndef CALL_FROM_TB2_CONST16 +#define CALL_FROM_TB2_CONST16(func, arg0, arg1) \ +CALL_FROM_TB2(func, arg0, arg1); +#endif +#ifndef CALL_FROM_TB3 +#define CALL_FROM_TB3(func, arg0, arg1, arg2) func(arg0, arg1, arg2); +#endif +#ifndef CALL_FROM_TB4 +#define CALL_FROM_TB4(func, arg0, arg1, arg2, arg3) \ + func(arg0, arg1, arg2, arg3); +#endif + +#define REG 1 +#include "op_template.c" +#undef REG +#define REG 2 +#include "op_template.c" +#undef REG +#define REG 3 +#include "op_template.c" +#undef REG +#define REG 4 +#include "op_template.c" +#undef REG +#define REG 5 +#include "op_template.c" +#undef REG +#define REG 6 +#include "op_template.c" +#undef REG +#define REG 7 +#include "op_template.c" +#undef REG +#define REG 8 +#include "op_template.c" +#undef REG +#define REG 9 +#include "op_template.c" +#undef REG +#define REG 10 +#include "op_template.c" +#undef REG +#define REG 11 +#include "op_template.c" +#undef REG +#define REG 12 +#include "op_template.c" +#undef REG +#define REG 13 +#include "op_template.c" +#undef REG +#define REG 14 +#include "op_template.c" +#undef REG +#define REG 15 +#include "op_template.c" +#undef REG +#define REG 16 +#include "op_template.c" +#undef REG +#define REG 17 +#include "op_template.c" +#undef REG +#define REG 18 +#include "op_template.c" +#undef REG +#define REG 19 +#include "op_template.c" +#undef REG +#define REG 20 +#include "op_template.c" +#undef REG +#define REG 21 +#include "op_template.c" +#undef REG +#define REG 22 +#include "op_template.c" +#undef REG +#define REG 23 +#include "op_template.c" +#undef REG +#define REG 24 +#include "op_template.c" +#undef REG +#define REG 25 +#include "op_template.c" +#undef REG +#define REG 26 +#include "op_template.c" +#undef REG +#define REG 27 +#include "op_template.c" +#undef REG +#define REG 28 +#include "op_template.c" +#undef REG +#define REG 29 +#include "op_template.c" +#undef REG +#define REG 30 +#include "op_template.c" +#undef REG +#define REG 31 +#include "op_template.c" +#undef REG + +#define TN T0 +#include "op_template.c" +#undef TN +#define TN T1 +#include "op_template.c" +#undef TN +#define TN T2 +#include "op_template.c" +#undef TN + +void op_dup_T0 (void) +{ + T2 = T0; + RETURN(); +} + +void op_load_HI (void) +{ + T0 = env->HI; + RETURN(); +} + +void op_store_HI (void) +{ + env->HI = T0; + RETURN(); +} + +void op_load_LO (void) +{ + T0 = env->LO; + RETURN(); +} + +void op_store_LO (void) +{ + env->LO = T0; + RETURN(); +} + +/* Load and store */ +#define MEMSUFFIX _raw +#include "op_mem.c" +#undef MEMSUFFIX +#if !defined(CONFIG_USER_ONLY) +#define MEMSUFFIX _user +#include "op_mem.c" +#undef MEMSUFFIX + +#define MEMSUFFIX _kernel +#include "op_mem.c" +#undef MEMSUFFIX +#endif + +/* Arithmetic */ +void op_add (void) +{ + T0 += T1; + RETURN(); +} + +void op_addo (void) +{ + target_ulong tmp; + + tmp = T0; + T0 += T1; + if ((T0 >> 31) ^ (T1 >> 31) ^ (tmp >> 31)) { + CALL_FROM_TB1(do_raise_exception, EXCP_OVERFLOW); + } + RETURN(); +} + +void op_sub (void) +{ + T0 -= T1; + RETURN(); +} + +void op_subo (void) +{ + target_ulong tmp; + + tmp = T0; + T0 = (int32_t)T0 - (int32_t)T1; + if (!((T0 >> 31) ^ (T1 >> 31) ^ (tmp >> 31))) { + CALL_FROM_TB1(do_raise_exception, EXCP_OVERFLOW); + } + RETURN(); +} + +void op_mul (void) +{ + T0 = (int32_t)T0 * (int32_t)T1; + RETURN(); +} + +void op_div (void) +{ + if (T1 != 0) { + env->LO = (int32_t)T0 / (int32_t)T1; + env->HI = (int32_t)T0 % (int32_t)T1; + } + RETURN(); +} + +void op_divu (void) +{ + if (T1 != 0) { + env->LO = T0 / T1; + env->HI = T0 % T1; + } + RETURN(); +} + +/* Logical */ +void op_and (void) +{ + T0 &= T1; + RETURN(); +} + +void op_nor (void) +{ + T0 = ~(T0 | T1); + RETURN(); +} + +void op_or (void) +{ + T0 |= T1; + RETURN(); +} + +void op_xor (void) +{ + T0 ^= T1; + RETURN(); +} + +void op_sll (void) +{ + T0 = T0 << T1; + RETURN(); +} + +void op_sra (void) +{ + T0 = (int32_t)T0 >> T1; + RETURN(); +} + +void op_srl (void) +{ + T0 = T0 >> T1; + RETURN(); +} + +void op_sllv (void) +{ + T0 = T1 << (T0 & 0x1F); + RETURN(); +} + +void op_srav (void) +{ + T0 = (int32_t)T1 >> (T0 & 0x1F); + RETURN(); +} + +void op_srlv (void) +{ + T0 = T1 >> (T0 & 0x1F); + RETURN(); +} + +void op_clo (void) +{ + int n; + + if (T0 == (target_ulong)-1) { + T0 = 32; + } else { + for (n = 0; n < 32; n++) { + if (!(T0 & (1 << 31))) + break; + T0 = T0 << 1; + } + T0 = n; + } + RETURN(); +} + +void op_clz (void) +{ + int n; + + if (T0 == 0) { + T0 = 32; + } else { + for (n = 0; n < 32; n++) { + if (T0 & (1 << 31)) + break; + T0 = T0 << 1; + } + T0 = n; + } + RETURN(); +} + +/* 64 bits arithmetic */ +#if (HOST_LONG_BITS == 64) +static inline uint64_t get_HILO (void) +{ + return ((uint64_t)env->HI << 32) | (uint64_t)env->LO; +} + +static inline void set_HILO (uint64_t HILO) +{ + env->LO = HILO & 0xFFFFFFFF; + env->HI = HILO >> 32; +} + +void op_mult (void) +{ + set_HILO((int64_t)T0 * (int64_t)T1); + RETURN(); +} + +void op_multu (void) +{ + set_HILO((uint64_t)T0 * (uint64_t)T1); + RETURN(); +} + +void op_madd (void) +{ + int64_t tmp; + + tmp = ((int64_t)T0 * (int64_t)T1); + set_HILO((int64_t)get_HILO() + tmp); + RETURN(); +} + +void op_maddu (void) +{ + uint64_t tmp; + + tmp = ((uint64_t)T0 * (uint64_t)T1); + set_HILO(get_HILO() + tmp); + RETURN(); +} + +void op_msub (void) +{ + int64_t tmp; + + tmp = ((int64_t)T0 * (int64_t)T1); + set_HILO((int64_t)get_HILO() - tmp); + RETURN(); +} + +void op_msubu (void) +{ + uint64_t tmp; + + tmp = ((uint64_t)T0 * (uint64_t)T1); + set_HILO(get_HILO() - tmp); + RETURN(); +} +#else +void op_mult (void) +{ + CALL_FROM_TB0(do_mult); + RETURN(); +} + +void op_multu (void) +{ + CALL_FROM_TB0(do_multu); + RETURN(); +} + +void op_madd (void) +{ + CALL_FROM_TB0(do_madd); + RETURN(); +} + +void op_maddu (void) +{ + CALL_FROM_TB0(do_maddu); + RETURN(); +} + +void op_msub (void) +{ + CALL_FROM_TB0(do_msub); + RETURN(); +} + +void op_msubu (void) +{ + CALL_FROM_TB0(do_msubu); + RETURN(); +} +#endif + +/* Conditional moves */ +void op_movn (void) +{ + if (T1 != 0) + env->gpr[PARAM1] = T0; + RETURN(); +} + +void op_movz (void) +{ + if (T1 == 0) + env->gpr[PARAM1] = T0; + RETURN(); +} + +/* Tests */ +#define OP_COND(name, cond) \ +void glue(op_, name) (void) \ +{ \ + if (cond) { \ + T0 = 1; \ + } else { \ + T0 = 0; \ + } \ + RETURN(); \ +} + +OP_COND(eq, T0 == T1); +OP_COND(ne, T0 != T1); +OP_COND(ge, (int32_t)T0 >= (int32_t)T1); +OP_COND(geu, T0 >= T1); +OP_COND(lt, (int32_t)T0 < (int32_t)T1); +OP_COND(ltu, T0 < T1); +OP_COND(gez, (int32_t)T0 >= 0); +OP_COND(gtz, (int32_t)T0 > 0); +OP_COND(lez, (int32_t)T0 <= 0); +OP_COND(ltz, (int32_t)T0 < 0); + +/* Branchs */ +//#undef USE_DIRECT_JUMP +#define EIP env->PC + +/* Branch to register */ +void op_save_breg_target (void) +{ + env->btarget = T2; +} + +void op_restore_breg_target (void) +{ + T2 = env->btarget; +} + +void op_breg (void) +{ + env->PC = T2; + RETURN(); +} + +/* Unconditional branch */ +void op_branch (void) +{ + JUMP_TB(branch, PARAM1, 0, PARAM2); + RETURN(); +} + +void op_save_btarget (void) +{ + env->btarget = PARAM1; + RETURN(); +} + +/* Conditional branch */ +void op_set_bcond (void) +{ + T2 = T0; + RETURN(); +} + +void op_save_bcond (void) +{ + env->bcond = T2; + RETURN(); +} + +void op_restore_bcond (void) +{ + T2 = env->bcond; + RETURN(); +} + +void op_bcond (void) +{ + if (T2) { + JUMP_TB(bcond, PARAM1, 0, PARAM2); + } else { + JUMP_TB(bcond, PARAM1, 1, PARAM3); + } + RETURN(); +} + +/* Likely branch (used to skip the delay slot) */ +void op_blikely (void) +{ + /* If the test is false, skip the delay slot */ + if (T2 == 0) { + env->hflags = PARAM3; + JUMP_TB(blikely, PARAM1, 1, PARAM2); + } + RETURN(); +} + +/* CP0 functions */ +void op_mfc0 (void) +{ + CALL_FROM_TB2(do_mfc0, PARAM1, PARAM2); + RETURN(); +} + +void op_mtc0 (void) +{ + CALL_FROM_TB2(do_mtc0, PARAM1, PARAM2); + RETURN(); +} + +#if defined(MIPS_USES_R4K_TLB) +void op_tlbwi (void) +{ + CALL_FROM_TB0(do_tlbwi); + RETURN(); +} + +void op_tlbwr (void) +{ + CALL_FROM_TB0(do_tlbwr); + RETURN(); +} + +void op_tlbp (void) +{ + CALL_FROM_TB0(do_tlbp); + RETURN(); +} + +void op_tlbr (void) +{ + CALL_FROM_TB0(do_tlbr); + RETURN(); +} +#endif + +/* Specials */ +void op_pmon (void) +{ + CALL_FROM_TB1(do_pmon, PARAM1); +} + +void op_trap (void) +{ + if (T0) { + CALL_FROM_TB1(do_raise_exception, EXCP_TRAP); + } + RETURN(); +} + +void op_set_lladdr (void) +{ + env->CP0_LLAddr = T2; +} + +void debug_eret (void); +void op_eret (void) +{ + CALL_FROM_TB0(debug_eret); + if (env->hflags & MIPS_HFLAG_ERL) { + env->PC = env->CP0_ErrorEPC; + env->hflags &= ~MIPS_HFLAG_ERL; + } else { + env->PC = env->CP0_EPC; + env->hflags &= ~MIPS_HFLAG_EXL; + } + env->CP0_LLAddr = 1; +} + +void op_deret (void) +{ + CALL_FROM_TB0(debug_eret); + env->PC = env->CP0_DEPC; +} + +void op_save_state (void) +{ + env->hflags = PARAM1; + RETURN(); +} + +void op_save_pc (void) +{ + env->PC = PARAM1; + RETURN(); +} + +void op_raise_exception (void) +{ + CALL_FROM_TB1(do_raise_exception, PARAM1); + RETURN(); +} + +void op_raise_exception_err (void) +{ + CALL_FROM_TB2(do_raise_exception_err, PARAM1, PARAM2); + RETURN(); +} + +void op_exit_tb (void) +{ + EXIT_TB(); +} + diff --git a/qemu/target-mips/op_helper.c b/qemu/target-mips/op_helper.c new file mode 100644 index 0000000..b8397be --- /dev/null +++ b/qemu/target-mips/op_helper.c @@ -0,0 +1,677 @@ +/* + * MIPS emulation helpers for qemu. + * + * Copyright (c) 2004-2005 Jocelyn Mayer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include "exec.h" + +#define MIPS_DEBUG_DISAS + +/*****************************************************************************/ +/* Exceptions processing helpers */ +void cpu_loop_exit(void) +{ + longjmp(env->jmp_env, 1); +} + +void do_raise_exception_err (uint32_t exception, int error_code) +{ +#if 1 + if (logfile && exception < 0x100) + fprintf(logfile, "%s: %d %d\n", __func__, exception, error_code); +#endif + env->exception_index = exception; + env->error_code = error_code; + T0 = 0; + cpu_loop_exit(); +} + +void do_raise_exception (uint32_t exception) +{ + do_raise_exception_err(exception, 0); +} + +#define MEMSUFFIX _raw +#include "op_helper_mem.c" +#undef MEMSUFFIX +#if !defined(CONFIG_USER_ONLY) +#define MEMSUFFIX _user +#include "op_helper_mem.c" +#undef MEMSUFFIX +#define MEMSUFFIX _kernel +#include "op_helper_mem.c" +#undef MEMSUFFIX +#endif + +/* 64 bits arithmetic for 32 bits hosts */ +#if (HOST_LONG_BITS == 32) +static inline uint64_t get_HILO (void) +{ + return ((uint64_t)env->HI << 32) | (uint64_t)env->LO; +} + +static inline void set_HILO (uint64_t HILO) +{ + env->LO = HILO & 0xFFFFFFFF; + env->HI = HILO >> 32; +} + +void do_mult (void) +{ + set_HILO((int64_t)T0 * (int64_t)T1); +} + +void do_multu (void) +{ + set_HILO((uint64_t)T0 * (uint64_t)T1); +} + +void do_madd (void) +{ + int64_t tmp; + + tmp = ((int64_t)T0 * (int64_t)T1); + set_HILO((int64_t)get_HILO() + tmp); +} + +void do_maddu (void) +{ + uint64_t tmp; + + tmp = ((uint64_t)T0 * (uint64_t)T1); + set_HILO(get_HILO() + tmp); +} + +void do_msub (void) +{ + int64_t tmp; + + tmp = ((int64_t)T0 * (int64_t)T1); + set_HILO((int64_t)get_HILO() - tmp); +} + +void do_msubu (void) +{ + uint64_t tmp; + + tmp = ((uint64_t)T0 * (uint64_t)T1); + set_HILO(get_HILO() - tmp); +} +#endif + +/* CP0 helpers */ +void do_mfc0 (int reg, int sel) +{ + const unsigned char *rn; + + if (sel != 0 && reg != 16 && reg != 28) { + rn = "invalid"; + goto print; + } + switch (reg) { + case 0: + T0 = env->CP0_index; + rn = "Index"; + break; + case 1: + T0 = cpu_mips_get_random(env); + rn = "Random"; + break; + case 2: + T0 = env->CP0_EntryLo0; + rn = "EntryLo0"; + break; + case 3: + T0 = env->CP0_EntryLo1; + rn = "EntryLo1"; + break; + case 4: + T0 = env->CP0_Context; + rn = "Context"; + break; + case 5: + T0 = env->CP0_PageMask; + rn = "PageMask"; + break; + case 6: + T0 = env->CP0_Wired; + rn = "Wired"; + break; + case 8: + T0 = env->CP0_BadVAddr; + rn = "BadVaddr"; + break; + case 9: + T0 = cpu_mips_get_count(env); + rn = "Count"; + break; + case 10: + T0 = env->CP0_EntryHi; + rn = "EntryHi"; + break; + case 11: + T0 = env->CP0_Compare; + rn = "Compare"; + break; + case 12: + T0 = env->CP0_Status; + if (env->hflags & MIPS_HFLAG_UM) + T0 |= (1 << CP0St_UM); + if (env->hflags & MIPS_HFLAG_ERL) + T0 |= (1 << CP0St_ERL); + if (env->hflags & MIPS_HFLAG_EXL) + T0 |= (1 << CP0St_EXL); + rn = "Status"; + break; + case 13: + T0 = env->CP0_Cause; + rn = "Cause"; + break; + case 14: + T0 = env->CP0_EPC; + rn = "EPC"; + break; + case 15: + T0 = env->CP0_PRid; + rn = "PRid"; + break; + case 16: + switch (sel) { + case 0: + T0 = env->CP0_Config0; + rn = "Config"; + break; + case 1: + T0 = env->CP0_Config1; + rn = "Config1"; + break; + default: + rn = "Unknown config register"; + break; + } + break; + case 17: + T0 = env->CP0_LLAddr >> 4; + rn = "LLAddr"; + break; + case 18: + T0 = env->CP0_WatchLo; + rn = "WatchLo"; + break; + case 19: + T0 = env->CP0_WatchHi; + rn = "WatchHi"; + break; + case 23: + T0 = env->CP0_Debug; + if (env->hflags & MIPS_HFLAG_DM) + T0 |= 1 << CP0DB_DM; + rn = "Debug"; + break; + case 24: + T0 = env->CP0_DEPC; + rn = "DEPC"; + break; + case 28: + switch (sel) { + case 0: + T0 = env->CP0_TagLo; + rn = "TagLo"; + break; + case 1: + T0 = env->CP0_DataLo; + rn = "DataLo"; + break; + default: + rn = "unknown sel"; + break; + } + break; + case 30: + T0 = env->CP0_ErrorEPC; + rn = "ErrorEPC"; + break; + case 31: + T0 = env->CP0_DESAVE; + rn = "DESAVE"; + break; + default: + rn = "unknown"; + break; + } + print: +#if defined MIPS_DEBUG_DISAS + if (loglevel & CPU_LOG_TB_IN_ASM) { + fprintf(logfile, "%08x mfc0 %s => %08x (%d %d)\n", + env->PC, rn, T0, reg, sel); + } +#endif + return; +} + +void do_mtc0 (int reg, int sel) +{ + const unsigned char *rn; + uint32_t val, old, mask; + + if (sel != 0 && reg != 16 && reg != 28) { + val = -1; + old = -1; + rn = "invalid"; + goto print; + } + switch (reg) { + case 0: + val = (env->CP0_index & 0x80000000) | (T0 & 0x0000000F); + old = env->CP0_index; + env->CP0_index = val; + rn = "Index"; + break; + case 2: + val = T0 & 0x03FFFFFFF; + old = env->CP0_EntryLo0; + env->CP0_EntryLo0 = val; + rn = "EntryLo0"; + break; + case 3: + val = T0 & 0x03FFFFFFF; + old = env->CP0_EntryLo1; + env->CP0_EntryLo1 = val; + rn = "EntryLo1"; + break; + case 4: + val = (env->CP0_Context & 0xFF000000) | (T0 & 0x00FFFFF0); + old = env->CP0_Context; + env->CP0_Context = val; + rn = "Context"; + break; + case 5: + val = T0 & 0x01FFE000; + old = env->CP0_PageMask; + env->CP0_PageMask = val; + rn = "PageMask"; + break; + case 6: + val = T0 & 0x0000000F; + old = env->CP0_Wired; + env->CP0_Wired = val; + rn = "Wired"; + break; + case 9: + val = T0; + old = cpu_mips_get_count(env); + cpu_mips_store_count(env, val); + rn = "Count"; + break; + case 10: + val = T0 & 0xFFFFF0FF; + old = env->CP0_EntryHi; + env->CP0_EntryHi = val; + rn = "EntryHi"; + break; + case 11: + val = T0; + old = env->CP0_Compare; + cpu_mips_store_compare(env, val); + rn = "Compare"; + break; + case 12: + val = T0 & 0xFA78FF01; + if (T0 & (1 << CP0St_UM)) + env->hflags |= MIPS_HFLAG_UM; + else + env->hflags &= ~MIPS_HFLAG_UM; + if (T0 & (1 << CP0St_ERL)) + env->hflags |= MIPS_HFLAG_ERL; + else + env->hflags &= ~MIPS_HFLAG_ERL; + if (T0 & (1 << CP0St_EXL)) + env->hflags |= MIPS_HFLAG_EXL; + else + env->hflags &= ~MIPS_HFLAG_EXL; + old = env->CP0_Status; + env->CP0_Status = val; + /* If we unmasked an asserted IRQ, raise it */ + mask = 0x0000FF00; + if (loglevel & CPU_LOG_TB_IN_ASM) { + fprintf(logfile, "Status %08x => %08x Cause %08x (%08x %08x %08x)\n", + old, val, env->CP0_Cause, old & mask, val & mask, + env->CP0_Cause & mask); + } +#if 1 + if ((val & (1 << CP0St_IE)) && !(old & (1 << CP0St_IE)) && + !(env->hflags & MIPS_HFLAG_EXL) && + !(env->hflags & MIPS_HFLAG_ERL) && + !(env->hflags & MIPS_HFLAG_DM) && + (env->CP0_Status & env->CP0_Cause & mask)) { + if (logfile) + fprintf(logfile, "Raise pending IRQs\n"); + env->interrupt_request |= CPU_INTERRUPT_HARD; + do_raise_exception(EXCP_EXT_INTERRUPT); + } else if (!(val & 0x00000001) && (old & 0x00000001)) { + env->interrupt_request &= ~CPU_INTERRUPT_HARD; + } +#endif + rn = "Status"; + break; + case 13: + val = (env->CP0_Cause & 0xB000F87C) | (T0 & 0x000C00300); + old = env->CP0_Cause; + env->CP0_Cause = val; +#if 0 + { + int i; + /* Check if we ever asserted a software IRQ */ + for (i = 0; i < 2; i++) { + mask = 0x100 << i; + if ((val & mask) & !(old & mask)) + mips_set_irq(i); + } + } +#endif + rn = "Cause"; + break; + case 14: + val = T0; + old = env->CP0_EPC; + env->CP0_EPC = val; + rn = "EPC"; + break; + case 16: + switch (sel) { + case 0: +#if defined(MIPS_USES_R4K_TLB) + val = (env->CP0_Config0 & 0x8017FF80) | (T0 & 0x7E000001); +#else + val = (env->CP0_Config0 & 0xFE17FF80) | (T0 & 0x00000001); +#endif + old = env->CP0_Config0; + env->CP0_Config0 = val; + rn = "Config0"; + break; + default: + val = -1; + old = -1; + rn = "bad config selector"; + break; + } + break; + case 18: + val = T0; + old = env->CP0_WatchLo; + env->CP0_WatchLo = val; + rn = "WatchLo"; + break; + case 19: + val = T0 & 0x40FF0FF8; + old = env->CP0_WatchHi; + env->CP0_WatchHi = val; + rn = "WatchHi"; + break; + case 23: + val = (env->CP0_Debug & 0x8C03FC1F) | (T0 & 0x13300120); + if (T0 & (1 << CP0DB_DM)) + env->hflags |= MIPS_HFLAG_DM; + else + env->hflags &= ~MIPS_HFLAG_DM; + old = env->CP0_Debug; + env->CP0_Debug = val; + rn = "Debug"; + break; + case 24: + val = T0; + old = env->CP0_DEPC; + env->CP0_DEPC = val; + rn = "DEPC"; + break; + case 28: + switch (sel) { + case 0: + val = T0 & 0xFFFFFCF6; + old = env->CP0_TagLo; + env->CP0_TagLo = val; + rn = "TagLo"; + break; + default: + val = -1; + old = -1; + rn = "invalid sel"; + break; + } + break; + case 30: + val = T0; + old = env->CP0_ErrorEPC; + env->CP0_ErrorEPC = val; + rn = "EPC"; + break; + case 31: + val = T0; + old = env->CP0_DESAVE; + env->CP0_DESAVE = val; + rn = "DESAVE"; + break; + default: + val = -1; + old = -1; + rn = "unknown"; + break; + } + print: +#if defined MIPS_DEBUG_DISAS + if (loglevel & CPU_LOG_TB_IN_ASM) { + fprintf(logfile, "%08x mtc0 %s %08x => %08x (%d %d %08x)\n", + env->PC, rn, T0, val, reg, sel, old); + } +#endif + return; +} + +/* TLB management */ +#if defined(MIPS_USES_R4K_TLB) +static void invalidate_tb (int idx) +{ + tlb_t *tlb; + target_ulong addr, end; + + tlb = &env->tlb[idx]; + if (tlb->V[0]) { + addr = tlb->PFN[0]; + end = addr + (tlb->end - tlb->VPN); + tb_invalidate_page_range(addr, end); + } + if (tlb->V[1]) { + addr = tlb->PFN[1]; + end = addr + (tlb->end - tlb->VPN); + tb_invalidate_page_range(addr, end); + } +} + +static void fill_tb (int idx) +{ + tlb_t *tlb; + int size; + + /* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */ + tlb = &env->tlb[idx]; + tlb->VPN = env->CP0_EntryHi & 0xFFFFE000; + tlb->ASID = env->CP0_EntryHi & 0x000000FF; + size = env->CP0_PageMask >> 13; + size = 4 * (size + 1); + tlb->end = tlb->VPN + (1 << (8 + size)); + tlb->G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1; + tlb->V[0] = env->CP0_EntryLo0 & 2; + tlb->D[0] = env->CP0_EntryLo0 & 4; + tlb->C[0] = (env->CP0_EntryLo0 >> 3) & 0x7; + tlb->PFN[0] = (env->CP0_EntryLo0 >> 6) << 12; + tlb->V[1] = env->CP0_EntryLo1 & 2; + tlb->D[1] = env->CP0_EntryLo1 & 4; + tlb->C[1] = (env->CP0_EntryLo1 >> 3) & 0x7; + tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12; +} + +void do_tlbwi (void) +{ + /* Wildly undefined effects for CP0_index containing a too high value and + MIPS_TLB_NB not being a power of two. But so does real silicon. */ + invalidate_tb(env->CP0_index & (MIPS_TLB_NB - 1)); + fill_tb(env->CP0_index & (MIPS_TLB_NB - 1)); +} + +void do_tlbwr (void) +{ + int r = cpu_mips_get_random(env); + + invalidate_tb(r); + fill_tb(r); +} + +void do_tlbp (void) +{ + tlb_t *tlb; + target_ulong tag; + uint8_t ASID; + int i; + + tag = (env->CP0_EntryHi & 0xFFFFE000); + ASID = env->CP0_EntryHi & 0x000000FF; + for (i = 0; i < MIPS_TLB_NB; i++) { + tlb = &env->tlb[i]; + /* Check ASID, virtual page number & size */ + if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) { + /* TLB match */ + env->CP0_index = i; + break; + } + } + if (i == MIPS_TLB_NB) { + env->CP0_index |= 0x80000000; + } +} + +void do_tlbr (void) +{ + tlb_t *tlb; + int size; + + tlb = &env->tlb[env->CP0_index & (MIPS_TLB_NB - 1)]; + env->CP0_EntryHi = tlb->VPN | tlb->ASID; + size = (tlb->end - tlb->VPN) >> 12; + env->CP0_PageMask = (size - 1) << 13; + env->CP0_EntryLo0 = tlb->V[0] | tlb->D[0] | (tlb->C[0] << 3) | + (tlb->PFN[0] >> 6); + env->CP0_EntryLo1 = tlb->V[1] | tlb->D[1] | (tlb->C[1] << 3) | + (tlb->PFN[1] >> 6); +} +#endif + +void op_dump_ldst (const unsigned char *func) +{ + if (loglevel) + fprintf(logfile, "%s => %08x %08x\n", __func__, T0, T1); +} + +void dump_sc (void) +{ + if (loglevel) { + fprintf(logfile, "%s %08x at %08x (%08x)\n", __func__, + T1, T0, env->CP0_LLAddr); + } +} + +void debug_eret (void) +{ + if (loglevel) { + fprintf(logfile, "ERET: pc %08x EPC %08x ErrorEPC %08x (%d)\n", + env->PC, env->CP0_EPC, env->CP0_ErrorEPC, + env->hflags & MIPS_HFLAG_ERL ? 1 : 0); + } +} + +void do_pmon (int function) +{ + function /= 2; + switch (function) { + case 2: /* TODO: char inbyte(int waitflag); */ + if (env->gpr[4] == 0) + env->gpr[2] = -1; + /* Fall through */ + case 11: /* TODO: char inbyte (void); */ + env->gpr[2] = -1; + break; + case 3: + case 12: + printf("%c", env->gpr[4] & 0xFF); + break; + case 17: + break; + case 158: + { + unsigned char *fmt = (void *)env->gpr[4]; + printf("%s", fmt); + } + break; + } +} + +#if !defined(CONFIG_USER_ONLY) + +#define MMUSUFFIX _mmu +#define GETPC() (__builtin_return_address(0)) + +#define SHIFT 0 +#include "softmmu_template.h" + +#define SHIFT 1 +#include "softmmu_template.h" + +#define SHIFT 2 +#include "softmmu_template.h" + +#define SHIFT 3 +#include "softmmu_template.h" + +void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr) +{ + TranslationBlock *tb; + CPUState *saved_env; + unsigned long pc; + int ret; + + /* XXX: hack to restore env in all cases, even if not called from + generated code */ + saved_env = env; + env = cpu_single_env; + ret = cpu_mips_handle_mmu_fault(env, addr, is_write, is_user, 1); + if (ret) { + if (retaddr) { + /* now we have a real cpu fault */ + pc = (unsigned long)retaddr; + tb = tb_find_pc(pc); + if (tb) { + /* the PC is inside the translated code. It means that we have + a virtual CPU fault */ + cpu_restore_state(tb, env, pc, NULL); + } + } + do_raise_exception_err(env->exception_index, env->error_code); + } + env = saved_env; +} + +#endif diff --git a/qemu/target-mips/op_helper_mem.c b/qemu/target-mips/op_helper_mem.c new file mode 100644 index 0000000..69793b0 --- /dev/null +++ b/qemu/target-mips/op_helper_mem.c @@ -0,0 +1,143 @@ +void glue(do_lwl, MEMSUFFIX) (void) +{ +#if defined (DEBUG_OP) + target_ulong sav = T0; +#endif + uint32_t tmp; + + tmp = glue(ldl, MEMSUFFIX)(T0 & ~3); + /* XXX: this is valid only in big-endian mode + * should be reverted for little-endian... + */ + switch (T0 & 3) { + case 0: + T0 = tmp; + break; + case 1: + T0 = (tmp << 8) | (T1 & 0x000000FF); + break; + case 2: + T0 = (tmp << 16) | (T1 & 0x0000FFFF); + break; + case 3: + T0 = (tmp << 24) | (T1 & 0x00FFFFFF); + break; + } +#if defined (DEBUG_OP) + if (logfile) { + fprintf(logfile, "%s: %08x - %08x %08x => %08x\n", + __func__, sav, tmp, T1, T0); + } +#endif + RETURN(); +} + +void glue(do_lwr, MEMSUFFIX) (void) +{ +#if defined (DEBUG_OP) + target_ulong sav = T0; +#endif + uint32_t tmp; + + tmp = glue(ldl, MEMSUFFIX)(T0 & ~3); + /* XXX: this is valid only in big-endian mode + * should be reverted for little-endian... + */ + switch (T0 & 3) { + case 0: + T0 = (tmp >> 24) | (T1 & 0xFFFFFF00); + break; + case 1: + T0 = (tmp >> 16) | (T1 & 0xFFFF0000); + break; + case 2: + T0 = (tmp >> 8) | (T1 & 0xFF000000); + break; + case 3: + T0 = tmp; + break; + } +#if defined (DEBUG_OP) + if (logfile) { + fprintf(logfile, "%s: %08x - %08x %08x => %08x\n", + __func__, sav, tmp, T1, T0); + } +#endif + RETURN(); +} + +void glue(do_swl, MEMSUFFIX) (void) +{ +#if defined (DEBUG_OP) + target_ulong sav; +#endif + uint32_t tmp; + + tmp = glue(ldl, MEMSUFFIX)(T0 & ~3); +#if defined (DEBUG_OP) + sav = tmp; +#endif + /* XXX: this is valid only in big-endian mode + * should be reverted for little-endian... + */ + switch (T0 & 3) { + case 0: + tmp = T1; + break; + case 1: + tmp = (tmp & 0xFF000000) | (T1 >> 8); + break; + case 2: + tmp = (tmp & 0xFFFF0000) | (T1 >> 16); + break; + case 3: + tmp = (tmp & 0xFFFFFF00) | (T1 >> 24); + break; + } + glue(stl, MEMSUFFIX)(T0 & ~3, tmp); +#if defined (DEBUG_OP) + if (logfile) { + fprintf(logfile, "%s: %08x - %08x %08x => %08x\n", + __func__, T0, sav, T1, tmp); + } +#endif + RETURN(); +} + +void glue(do_swr, MEMSUFFIX) (void) +{ +#if defined (DEBUG_OP) + target_ulong sav; +#endif + uint32_t tmp; + + tmp = glue(ldl, MEMSUFFIX)(T0 & ~3); +#if defined (DEBUG_OP) + sav = tmp; +#endif + /* XXX: this is valid only in big-endian mode + * should be reverted for little-endian... + */ + switch (T0 & 3) { + case 0: + tmp = (tmp & 0x00FFFFFF) | (T1 << 24); + break; + case 1: + tmp = (tmp & 0x0000FFFF) | (T1 << 16); + break; + case 2: + tmp = (tmp & 0x000000FF) | (T1 << 8); + break; + case 3: + tmp = T1; + break; + } + glue(stl, MEMSUFFIX)(T0 & ~3, tmp); +#if defined (DEBUG_OP) + if (logfile) { + fprintf(logfile, "%s: %08x - %08x %08x => %08x\n", + __func__, T0, sav, T1, tmp); + } +#endif + RETURN(); +} diff --git a/qemu/target-mips/op_mem.c b/qemu/target-mips/op_mem.c new file mode 100644 index 0000000..bbb322d --- /dev/null +++ b/qemu/target-mips/op_mem.c @@ -0,0 +1,113 @@ +/* + * MIPS emulation memory micro-operations for qemu. + * + * Copyright (c) 2004-2005 Jocelyn Mayer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Standard loads and stores */ +void glue(op_lb, MEMSUFFIX) (void) +{ + T0 = glue(ldsb, MEMSUFFIX)(T0); + RETURN(); +} + +void glue(op_lbu, MEMSUFFIX) (void) +{ + T0 = glue(ldub, MEMSUFFIX)(T0); + RETURN(); +} + +void glue(op_sb, MEMSUFFIX) (void) +{ + glue(stb, MEMSUFFIX)(T0, T1); + RETURN(); +} + +void glue(op_lh, MEMSUFFIX) (void) +{ + T0 = glue(ldsw, MEMSUFFIX)(T0); + RETURN(); +} + +void glue(op_lhu, MEMSUFFIX) (void) +{ + T0 = glue(lduw, MEMSUFFIX)(T0); + RETURN(); +} + +void glue(op_sh, MEMSUFFIX) (void) +{ + glue(stw, MEMSUFFIX)(T0, T1); + RETURN(); +} + +void glue(op_lw, MEMSUFFIX) (void) +{ + T0 = glue(ldl, MEMSUFFIX)(T0); + RETURN(); +} + +void glue(op_sw, MEMSUFFIX) (void) +{ + glue(stl, MEMSUFFIX)(T0, T1); + RETURN(); +} + +/* "half" load and stores */ +void glue(op_lwl, MEMSUFFIX) (void) +{ + CALL_FROM_TB0(glue(do_lwl, MEMSUFFIX)); + RETURN(); +} + +void glue(op_lwr, MEMSUFFIX) (void) +{ + CALL_FROM_TB0(glue(do_lwr, MEMSUFFIX)); + RETURN(); +} + +void glue(op_swl, MEMSUFFIX) (void) +{ + CALL_FROM_TB0(glue(do_swl, MEMSUFFIX)); + RETURN(); +} + +void glue(op_swr, MEMSUFFIX) (void) +{ + CALL_FROM_TB0(glue(do_swr, MEMSUFFIX)); + RETURN(); +} + +void glue(op_ll, MEMSUFFIX) (void) +{ + T1 = T0; + T0 = glue(ldl, MEMSUFFIX)(T0); + env->CP0_LLAddr = T1; + RETURN(); +} + +void glue(op_sc, MEMSUFFIX) (void) +{ + CALL_FROM_TB0(dump_sc); + if (T0 == env->CP0_LLAddr) { + glue(stl, MEMSUFFIX)(T0, T1); + T0 = 1; + } else { + T0 = 0; + } + RETURN(); +} diff --git a/qemu/target-mips/op_template.c b/qemu/target-mips/op_template.c new file mode 100644 index 0000000..9314c95 --- /dev/null +++ b/qemu/target-mips/op_template.c @@ -0,0 +1,65 @@ +/* + * MIPS emulation micro-operations templates for reg load & store for qemu. + * + * Copyright (c) 2004-2005 Jocelyn Mayer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if defined(REG) +void glue(op_load_gpr_T0_gpr, REG) (void) +{ + T0 = env->gpr[REG]; + RETURN(); +} + +void glue(op_store_T0_gpr_gpr, REG) (void) +{ + env->gpr[REG] = T0; + RETURN(); +} + +void glue(op_load_gpr_T1_gpr, REG) (void) +{ + T1 = env->gpr[REG]; + RETURN(); +} + +void glue(op_store_T1_gpr_gpr, REG) (void) +{ + env->gpr[REG] = T1; + RETURN(); +} + +void glue(op_load_gpr_T2_gpr, REG) (void) +{ + T2 = env->gpr[REG]; + RETURN(); +} +#endif + +#if defined (TN) +void glue(op_set_, TN) (void) +{ + TN = PARAM1; + RETURN(); +} + +void glue (op_reset_, TN) (void) +{ + TN = 0; + RETURN(); +} +#endif diff --git a/qemu/target-mips/translate.c b/qemu/target-mips/translate.c new file mode 100644 index 0000000..7055775 --- /dev/null +++ b/qemu/target-mips/translate.c @@ -0,0 +1,1688 @@ +/* + * MIPS32 emulation for qemu: main translation routines. + * + * Copyright (c) 2004-2005 Jocelyn Mayer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include "cpu.h" +#include "exec-all.h" +#include "disas.h" + +#define MIPS_DEBUG_DISAS +//#define MIPS_SINGLE_STEP + +enum { +#define DEF(s, n, copy_size) INDEX_op_ ## s, +#include "opc.h" +#undef DEF + NB_OPS, +}; + +static uint16_t *gen_opc_ptr; +static uint32_t *gen_opparam_ptr; + +#include "gen-op.h" + +/* MIPS opcodes */ +#define EXT_SPECIAL 0x100 +#define EXT_SPECIAL2 0x200 +#define EXT_REGIMM 0x300 +#define EXT_CP0 0x400 +#define EXT_CP1 0x500 +#define EXT_CP2 0x600 +#define EXT_CP3 0x700 + +enum { + /* indirect opcode tables */ + OPC_SPECIAL = 0x00, + OPC_BREGIMM = 0x01, + OPC_CP0 = 0x10, + OPC_CP1 = 0x11, + OPC_CP2 = 0x12, + OPC_CP3 = 0x13, + OPC_SPECIAL2 = 0x1C, + /* arithmetic with immediate */ + OPC_ADDI = 0x08, + OPC_ADDIU = 0x09, + OPC_SLTI = 0x0A, + OPC_SLTIU = 0x0B, + OPC_ANDI = 0x0C, + OPC_ORI = 0x0D, + OPC_XORI = 0x0E, + OPC_LUI = 0x0F, + /* Jump and branches */ + OPC_J = 0x02, + OPC_JAL = 0x03, + OPC_BEQ = 0x04, /* Unconditional if rs = rt = 0 (B) */ + OPC_BEQL = 0x14, + OPC_BNE = 0x05, + OPC_BNEL = 0x15, + OPC_BLEZ = 0x06, + OPC_BLEZL = 0x16, + OPC_BGTZ = 0x07, + OPC_BGTZL = 0x17, + OPC_JALX = 0x1D, /* MIPS 16 only */ + /* Load and stores */ + OPC_LB = 0x20, + OPC_LH = 0x21, + OPC_LWL = 0x22, + OPC_LW = 0x23, + OPC_LBU = 0x24, + OPC_LHU = 0x25, + OPC_LWR = 0x26, + OPC_SB = 0x28, + OPC_SH = 0x29, + OPC_SWL = 0x2A, + OPC_SW = 0x2B, + OPC_SWR = 0x2E, + OPC_LL = 0x30, + OPC_SC = 0x38, + /* Floating point load/store */ + OPC_LWC1 = 0x31, + OPC_LWC2 = 0x32, + OPC_LDC1 = 0x35, + OPC_LDC2 = 0x36, + OPC_SWC1 = 0x39, + OPC_SWC2 = 0x3A, + OPC_SDC1 = 0x3D, + OPC_SDC2 = 0x3E, + /* Cache and prefetch */ + OPC_CACHE = 0x2F, + OPC_PREF = 0x33, +}; + +/* MIPS special opcodes */ +enum { + /* Shifts */ + OPC_SLL = 0x00 | EXT_SPECIAL, + /* NOP is SLL r0, r0, 0 */ + /* SSNOP is SLL r0, r0, 1 */ + OPC_SRL = 0x02 | EXT_SPECIAL, + OPC_SRA = 0x03 | EXT_SPECIAL, + OPC_SLLV = 0x04 | EXT_SPECIAL, + OPC_SRLV = 0x06 | EXT_SPECIAL, + OPC_SRAV = 0x07 | EXT_SPECIAL, + /* Multiplication / division */ + OPC_MULT = 0x18 | EXT_SPECIAL, + OPC_MULTU = 0x19 | EXT_SPECIAL, + OPC_DIV = 0x1A | EXT_SPECIAL, + OPC_DIVU = 0x1B | EXT_SPECIAL, + /* 2 registers arithmetic / logic */ + OPC_ADD = 0x20 | EXT_SPECIAL, + OPC_ADDU = 0x21 | EXT_SPECIAL, + OPC_SUB = 0x22 | EXT_SPECIAL, + OPC_SUBU = 0x23 | EXT_SPECIAL, + OPC_AND = 0x24 | EXT_SPECIAL, + OPC_OR = 0x25 | EXT_SPECIAL, + OPC_XOR = 0x26 | EXT_SPECIAL, + OPC_NOR = 0x27 | EXT_SPECIAL, + OPC_SLT = 0x2A | EXT_SPECIAL, + OPC_SLTU = 0x2B | EXT_SPECIAL, + /* Jumps */ + OPC_JR = 0x08 | EXT_SPECIAL, + OPC_JALR = 0x09 | EXT_SPECIAL, + /* Traps */ + OPC_TGE = 0x30 | EXT_SPECIAL, + OPC_TGEU = 0x31 | EXT_SPECIAL, + OPC_TLT = 0x32 | EXT_SPECIAL, + OPC_TLTU = 0x33 | EXT_SPECIAL, + OPC_TEQ = 0x34 | EXT_SPECIAL, + OPC_TNE = 0x36 | EXT_SPECIAL, + /* HI / LO registers load & stores */ + OPC_MFHI = 0x10 | EXT_SPECIAL, + OPC_MTHI = 0x11 | EXT_SPECIAL, + OPC_MFLO = 0x12 | EXT_SPECIAL, + OPC_MTLO = 0x13 | EXT_SPECIAL, + /* Conditional moves */ + OPC_MOVZ = 0x0A | EXT_SPECIAL, + OPC_MOVN = 0x0B | EXT_SPECIAL, + + OPC_MOVCI = 0x01 | EXT_SPECIAL, + + /* Special */ + OPC_PMON = 0x05 | EXT_SPECIAL, + OPC_SYSCALL = 0x0C | EXT_SPECIAL, + OPC_BREAK = 0x0D | EXT_SPECIAL, + OPC_SYNC = 0x0F | EXT_SPECIAL, +}; + +enum { + /* Mutiply & xxx operations */ + OPC_MADD = 0x00 | EXT_SPECIAL2, + OPC_MADDU = 0x01 | EXT_SPECIAL2, + OPC_MUL = 0x02 | EXT_SPECIAL2, + OPC_MSUB = 0x04 | EXT_SPECIAL2, + OPC_MSUBU = 0x05 | EXT_SPECIAL2, + /* Misc */ + OPC_CLZ = 0x20 | EXT_SPECIAL2, + OPC_CLO = 0x21 | EXT_SPECIAL2, + /* Special */ + OPC_SDBBP = 0x3F | EXT_SPECIAL2, +}; + +/* Branch REGIMM */ +enum { + OPC_BLTZ = 0x00 | EXT_REGIMM, + OPC_BLTZL = 0x02 | EXT_REGIMM, + OPC_BGEZ = 0x01 | EXT_REGIMM, + OPC_BGEZL = 0x03 | EXT_REGIMM, + OPC_BLTZAL = 0x10 | EXT_REGIMM, + OPC_BLTZALL = 0x12 | EXT_REGIMM, + OPC_BGEZAL = 0x11 | EXT_REGIMM, + OPC_BGEZALL = 0x13 | EXT_REGIMM, + OPC_TGEI = 0x08 | EXT_REGIMM, + OPC_TGEIU = 0x09 | EXT_REGIMM, + OPC_TLTI = 0x0A | EXT_REGIMM, + OPC_TLTIU = 0x0B | EXT_REGIMM, + OPC_TEQI = 0x0C | EXT_REGIMM, + OPC_TNEI = 0x0E | EXT_REGIMM, +}; + +enum { + /* Coprocessor 0 (MMU) */ + OPC_MFC0 = 0x00 | EXT_CP0, + OPC_MTC0 = 0x04 | EXT_CP0, + OPC_TLBR = 0x01 | EXT_CP0, + OPC_TLBWI = 0x02 | EXT_CP0, + OPC_TLBWR = 0x06 | EXT_CP0, + OPC_TLBP = 0x08 | EXT_CP0, + OPC_ERET = 0x18 | EXT_CP0, + OPC_DERET = 0x1F | EXT_CP0, + OPC_WAIT = 0x20 | EXT_CP0, +}; + +const unsigned char *regnames[] = + { "r0", "at", "v0", "v1", "a0", "a1", "a2", "a3", + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra", }; + +/* Warning: no function for r0 register (hard wired to zero) */ +#define GEN32(func, NAME) \ +static GenOpFunc *NAME ## _table [32] = { \ +NULL, NAME ## 1, NAME ## 2, NAME ## 3, \ +NAME ## 4, NAME ## 5, NAME ## 6, NAME ## 7, \ +NAME ## 8, NAME ## 9, NAME ## 10, NAME ## 11, \ +NAME ## 12, NAME ## 13, NAME ## 14, NAME ## 15, \ +NAME ## 16, NAME ## 17, NAME ## 18, NAME ## 19, \ +NAME ## 20, NAME ## 21, NAME ## 22, NAME ## 23, \ +NAME ## 24, NAME ## 25, NAME ## 26, NAME ## 27, \ +NAME ## 28, NAME ## 29, NAME ## 30, NAME ## 31, \ +}; \ +static inline void func(int n) \ +{ \ + NAME ## _table[n](); \ +} + +/* General purpose registers moves */ +GEN32(gen_op_load_gpr_T0, gen_op_load_gpr_T0_gpr); +GEN32(gen_op_load_gpr_T1, gen_op_load_gpr_T1_gpr); +GEN32(gen_op_load_gpr_T2, gen_op_load_gpr_T2_gpr); + +GEN32(gen_op_store_T0_gpr, gen_op_store_T0_gpr_gpr); +GEN32(gen_op_store_T1_gpr, gen_op_store_T1_gpr_gpr); + +typedef struct DisasContext { + struct TranslationBlock *tb; + target_ulong pc, saved_pc; + uint32_t opcode; + /* Routine used to access memory */ + int mem_idx; + uint32_t hflags, saved_hflags; + uint32_t CP0_Status; + int bstate; + target_ulong btarget; +} DisasContext; + +enum { + BS_NONE = 0, /* We go out of the TB without reaching a branch or an + * exception condition + */ + BS_STOP = 1, /* We want to stop translation for any reason */ + BS_BRANCH = 2, /* We reached a branch condition */ + BS_EXCP = 3, /* We reached an exception condition */ +}; + +#if defined MIPS_DEBUG_DISAS +#define MIPS_DEBUG(fmt, args...) \ +do { \ + if (loglevel & CPU_LOG_TB_IN_ASM) { \ + fprintf(logfile, "%08x: %08x " fmt "\n", \ + ctx->pc, ctx->opcode , ##args); \ + } \ +} while (0) +#else +#define MIPS_DEBUG(fmt, args...) do { } while(0) +#endif + +#define MIPS_INVAL(op) \ +do { \ + MIPS_DEBUG("Invalid %s %03x %03x %03x", op, ctx->opcode >> 26, \ + ctx->opcode & 0x3F, ((ctx->opcode >> 16) & 0x1F)); \ +} while (0) + +#define GEN_LOAD_REG_TN(Tn, Rn) \ +do { \ + if (Rn == 0) { \ + glue(gen_op_reset_, Tn)(); \ + } else { \ + glue(gen_op_load_gpr_, Tn)(Rn); \ + } \ +} while (0) + +#define GEN_LOAD_IMM_TN(Tn, Imm) \ +do { \ + if (Imm == 0) { \ + glue(gen_op_reset_, Tn)(); \ + } else { \ + glue(gen_op_set_, Tn)(Imm); \ + } \ +} while (0) + +#define GEN_STORE_TN_REG(Rn, Tn) \ +do { \ + if (Rn != 0) { \ + glue(glue(gen_op_store_, Tn),_gpr)(Rn); \ + } \ +} while (0) + +static inline void save_cpu_state (DisasContext *ctx, int do_save_pc) +{ +#if defined MIPS_DEBUG_DISAS + if (loglevel & CPU_LOG_TB_IN_ASM) { + fprintf(logfile, "hflags %08x saved %08x\n", + ctx->hflags, ctx->saved_hflags); + } +#endif + if (do_save_pc && ctx->pc != ctx->saved_pc) { + gen_op_save_pc(ctx->pc); + ctx->saved_pc = ctx->pc; + } + if (ctx->hflags != ctx->saved_hflags) { + gen_op_save_state(ctx->hflags); + ctx->saved_hflags = ctx->hflags; + if (ctx->hflags & MIPS_HFLAG_BR) { + gen_op_save_breg_target(); + } else if (ctx->hflags & MIPS_HFLAG_B) { + gen_op_save_btarget(ctx->btarget); + } else if (ctx->hflags & MIPS_HFLAG_BMASK) { + gen_op_save_bcond(); + gen_op_save_btarget(ctx->btarget); + } + } +} + +static inline void generate_exception (DisasContext *ctx, int excp) +{ +#if defined MIPS_DEBUG_DISAS + if (loglevel & CPU_LOG_TB_IN_ASM) + fprintf(logfile, "%s: raise exception %d\n", __func__, excp); +#endif + save_cpu_state(ctx, 1); + gen_op_raise_exception(excp); + ctx->bstate = BS_EXCP; +} + +#if defined(CONFIG_USER_ONLY) +#define op_ldst(name) gen_op_##name##_raw() +#define OP_LD_TABLE(width) +#define OP_ST_TABLE(width) +#else +#define op_ldst(name) (*gen_op_##name[ctx->mem_idx])() +#define OP_LD_TABLE(width) \ +static GenOpFunc *gen_op_l##width[] = { \ + &gen_op_l##width##_user, \ + &gen_op_l##width##_kernel, \ +} +#define OP_ST_TABLE(width) \ +static GenOpFunc *gen_op_s##width[] = { \ + &gen_op_s##width##_user, \ + &gen_op_s##width##_kernel, \ +} +#endif + +#ifdef TARGET_MIPS64 +OP_LD_TABLE(d); +OP_LD_TABLE(dl); +OP_LD_TABLE(dr); +OP_ST_TABLE(d); +OP_ST_TABLE(dl); +OP_ST_TABLE(dr); +#endif +OP_LD_TABLE(w); +OP_LD_TABLE(wl); +OP_LD_TABLE(wr); +OP_ST_TABLE(w); +OP_ST_TABLE(wl); +OP_ST_TABLE(wr); +OP_LD_TABLE(h); +OP_LD_TABLE(hu); +OP_ST_TABLE(h); +OP_LD_TABLE(b); +OP_LD_TABLE(bu); +OP_ST_TABLE(b); +OP_LD_TABLE(l); +OP_ST_TABLE(c); + +/* Load and store */ +static void gen_ldst (DisasContext *ctx, uint16_t opc, int rt, + int base, int16_t offset) +{ + const unsigned char *opn = "unk"; + + if (base == 0) { + GEN_LOAD_IMM_TN(T0, offset); + } else if (offset == 0) { + gen_op_load_gpr_T0(base); + } else { + gen_op_load_gpr_T0(base); + gen_op_set_T1(offset); + gen_op_add(); + } + /* Don't do NOP if destination is zero: we must perform the actual + * memory access + */ + switch (opc) { +#if defined(TARGET_MIPS64) + case OPC_LD: +#if defined (MIPS_HAS_UNALIGNED_LS) + case OPC_ULD: +#endif + op_ldst(ld); + GEN_STORE_TN_REG(rt, T0); + opn = "ld"; + break; + case OPC_SD: +#if defined (MIPS_HAS_UNALIGNED_LS) + case OPC_USD: +#endif + GEN_LOAD_REG_TN(T1, rt); + op_ldst(sd); + opn = "sd"; + break; + case OPC_LDL: + op_ldst(ldl); + GEN_STORE_TN_REG(rt, T0); + opn = "ldl"; + break; + case OPC_SDL: + GEN_LOAD_REG_TN(T1, rt); + op_ldst(sdl); + opn = "sdl"; + break; + case OPC_LDR: + op_ldst(ldr); + GEN_STORE_TN_REG(rt, T0); + opn = "ldr"; + break; + case OPC_SDR: + GEN_LOAD_REG_TN(T1, rt); + op_ldst(sdr); + opn = "sdr"; + break; +#endif + case OPC_LW: +#if defined (MIPS_HAS_UNALIGNED_LS) + case OPC_ULW: +#endif + op_ldst(lw); + GEN_STORE_TN_REG(rt, T0); + opn = "lw"; + break; + case OPC_SW: +#if defined (MIPS_HAS_UNALIGNED_LS) + case OPC_USW: +#endif + GEN_LOAD_REG_TN(T1, rt); + op_ldst(sw); + opn = "sw"; + break; + case OPC_LH: +#if defined (MIPS_HAS_UNALIGNED_LS) + case OPC_ULH: +#endif + op_ldst(lh); + GEN_STORE_TN_REG(rt, T0); + opn = "lh"; + break; + case OPC_SH: +#if defined (MIPS_HAS_UNALIGNED_LS) + case OPC_USH: +#endif + GEN_LOAD_REG_TN(T1, rt); + op_ldst(sh); + opn = "sh"; + break; + case OPC_LHU: +#if defined (MIPS_HAS_UNALIGNED_LS) + case OPC_ULHU: +#endif + op_ldst(lhu); + GEN_STORE_TN_REG(rt, T0); + opn = "lhu"; + break; + case OPC_LB: + op_ldst(lb); + GEN_STORE_TN_REG(rt, T0); + opn = "lb"; + break; + case OPC_SB: + GEN_LOAD_REG_TN(T1, rt); + op_ldst(sb); + opn = "sb"; + break; + case OPC_LBU: + op_ldst(lbu); + GEN_STORE_TN_REG(rt, T0); + opn = "lbu"; + break; + case OPC_LWL: + GEN_LOAD_REG_TN(T1, rt); + op_ldst(lwl); + GEN_STORE_TN_REG(rt, T0); + opn = "lwl"; + break; + case OPC_SWL: + GEN_LOAD_REG_TN(T1, rt); + op_ldst(swl); + opn = "swr"; + break; + case OPC_LWR: + GEN_LOAD_REG_TN(T1, rt); + op_ldst(lwr); + GEN_STORE_TN_REG(rt, T0); + opn = "lwr"; + break; + case OPC_SWR: + GEN_LOAD_REG_TN(T1, rt); + op_ldst(swr); + opn = "swr"; + break; + case OPC_LL: + op_ldst(ll); + GEN_STORE_TN_REG(rt, T0); + opn = "ll"; + break; + case OPC_SC: + GEN_LOAD_REG_TN(T1, rt); + op_ldst(sc); + GEN_STORE_TN_REG(rt, T0); + opn = "sc"; + break; + default: + MIPS_INVAL("load/store"); + generate_exception(ctx, EXCP_RI); + return; + } + MIPS_DEBUG("%s %s, %d(%s)", opn, regnames[rt], offset, regnames[base]); +} + +/* Arithmetic with immediate operand */ +static void gen_arith_imm (DisasContext *ctx, uint16_t opc, int rt, + int rs, int16_t imm) +{ + uint32_t uimm; + const unsigned char *opn = "unk"; + + if (rt == 0 && opc != OPC_ADDI) { + /* if no destination, treat it as a NOP + * For addi, we must generate the overflow exception when needed. + */ + MIPS_DEBUG("NOP"); + return; + } + if (opc == OPC_ADDI || opc == OPC_ADDIU || + opc == OPC_SLTI || opc == OPC_SLTIU) + uimm = (int32_t)imm; /* Sign extent to 32 bits */ + else + uimm = (uint16_t)imm; + if (opc != OPC_LUI) { + GEN_LOAD_REG_TN(T0, rs); + GEN_LOAD_IMM_TN(T1, uimm); + } else { + uimm = uimm << 16; + GEN_LOAD_IMM_TN(T0, uimm); + } + switch (opc) { + case OPC_ADDI: + save_cpu_state(ctx, 1); + gen_op_addo(); + opn = "addi"; + break; + case OPC_ADDIU: + gen_op_add(); + opn = "addiu"; + break; + case OPC_SLTI: + gen_op_lt(); + opn = "slti"; + break; + case OPC_SLTIU: + gen_op_ltu(); + opn = "sltiu"; + break; + case OPC_ANDI: + gen_op_and(); + opn = "andi"; + break; + case OPC_ORI: + gen_op_or(); + opn = "ori"; + break; + case OPC_XORI: + gen_op_xor(); + opn = "xori"; + break; + case OPC_LUI: + opn = "lui"; + break; + case OPC_SLL: + gen_op_sll(); + opn = "sll"; + break; + case OPC_SRA: + gen_op_sra(); + opn = "sra"; + break; + case OPC_SRL: + gen_op_srl(); + opn = "srl"; + break; + default: + MIPS_INVAL("imm arith"); + generate_exception(ctx, EXCP_RI); + return; + } + GEN_STORE_TN_REG(rt, T0); + MIPS_DEBUG("%s %s, %s, %x", opn, regnames[rt], regnames[rs], uimm); +} + +/* Arithmetic */ +static void gen_arith (DisasContext *ctx, uint16_t opc, + int rd, int rs, int rt) +{ + const unsigned char *opn = "unk"; + + if (rd == 0 && opc != OPC_ADD && opc != OPC_SUB) { + /* if no destination, treat it as a NOP + * For add & sub, we must generate the overflow exception when needed. + */ + MIPS_DEBUG("NOP"); + return; + } + GEN_LOAD_REG_TN(T0, rs); + GEN_LOAD_REG_TN(T1, rt); + switch (opc) { + case OPC_ADD: + save_cpu_state(ctx, 1); + gen_op_addo(); + opn = "add"; + break; + case OPC_ADDU: + gen_op_add(); + opn = "addu"; + break; + case OPC_SUB: + save_cpu_state(ctx, 1); + gen_op_subo(); + opn = "sub"; + break; + case OPC_SUBU: + gen_op_sub(); + opn = "subu"; + break; + case OPC_SLT: + gen_op_lt(); + opn = "slt"; + break; + case OPC_SLTU: + gen_op_ltu(); + opn = "sltu"; + break; + case OPC_AND: + gen_op_and(); + opn = "and"; + break; + case OPC_NOR: + gen_op_nor(); + opn = "nor"; + break; + case OPC_OR: + gen_op_or(); + opn = "or"; + break; + case OPC_XOR: + gen_op_xor(); + opn = "xor"; + break; + case OPC_MUL: + gen_op_mul(); + opn = "mul"; + break; + case OPC_MOVN: + gen_op_movn(rd); + opn = "movn"; + goto print; + case OPC_MOVZ: + gen_op_movz(rd); + opn = "movz"; + goto print; + case OPC_SLLV: + gen_op_sllv(); + opn = "sllv"; + break; + case OPC_SRAV: + gen_op_srav(); + opn = "srav"; + break; + case OPC_SRLV: + gen_op_srlv(); + opn = "srlv"; + break; + default: + MIPS_INVAL("arith"); + generate_exception(ctx, EXCP_RI); + return; + } + GEN_STORE_TN_REG(rd, T0); + print: + MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]); +} + +/* Arithmetic on HI/LO registers */ +static void gen_HILO (DisasContext *ctx, uint16_t opc, int reg) +{ + const unsigned char *opn = "unk"; + + if (reg == 0 && (opc == OPC_MFHI || opc == OPC_MFLO)) { + /* Treat as a NOP */ + MIPS_DEBUG("NOP"); + return; + } + switch (opc) { + case OPC_MFHI: + gen_op_load_HI(); + GEN_STORE_TN_REG(reg, T0); + opn = "mfhi"; + break; + case OPC_MFLO: + gen_op_load_LO(); + GEN_STORE_TN_REG(reg, T0); + opn = "mflo"; + break; + case OPC_MTHI: + GEN_LOAD_REG_TN(T0, reg); + gen_op_store_HI(); + opn = "mthi"; + break; + case OPC_MTLO: + GEN_LOAD_REG_TN(T0, reg); + gen_op_store_LO(); + opn = "mtlo"; + break; + default: + MIPS_INVAL("HILO"); + generate_exception(ctx, EXCP_RI); + return; + } + MIPS_DEBUG("%s %s", opn, regnames[reg]); +} + +static void gen_muldiv (DisasContext *ctx, uint16_t opc, + int rs, int rt) +{ + const unsigned char *opn = "unk"; + + GEN_LOAD_REG_TN(T0, rs); + GEN_LOAD_REG_TN(T1, rt); + switch (opc) { + case OPC_DIV: + gen_op_div(); + opn = "div"; + break; + case OPC_DIVU: + gen_op_divu(); + opn = "divu"; + break; + case OPC_MULT: + gen_op_mult(); + opn = "mult"; + break; + case OPC_MULTU: + gen_op_multu(); + opn = "multu"; + break; + case OPC_MADD: + gen_op_madd(); + opn = "madd"; + break; + case OPC_MADDU: + gen_op_maddu(); + opn = "maddu"; + break; + case OPC_MSUB: + gen_op_msub(); + opn = "msub"; + break; + case OPC_MSUBU: + gen_op_msubu(); + opn = "msubu"; + break; + default: + MIPS_INVAL("mul/div"); + generate_exception(ctx, EXCP_RI); + return; + } + MIPS_DEBUG("%s %s %s", opn, regnames[rs], regnames[rt]); +} + +static void gen_cl (DisasContext *ctx, uint16_t opc, + int rd, int rs) +{ + const unsigned char *opn = "unk"; + if (rd == 0) { + /* Treat as a NOP */ + MIPS_DEBUG("NOP"); + return; + } + GEN_LOAD_REG_TN(T0, rs); + switch (opc) { + case OPC_CLO: + /* CLO */ + gen_op_clo(); + opn = "clo"; + break; + case OPC_CLZ: + /* CLZ */ + gen_op_clz(); + opn = "clz"; + break; + default: + MIPS_INVAL("CLx"); + generate_exception(ctx, EXCP_RI); + return; + } + gen_op_store_T0_gpr(rd); + MIPS_DEBUG("%s %s, %s", opn, regnames[rd], regnames[rs]); +} + +/* Traps */ +static void gen_trap (DisasContext *ctx, uint16_t opc, + int rs, int rt, int16_t imm) +{ + int cond; + + cond = 0; + /* Load needed operands */ + switch (opc) { + case OPC_TEQ: + case OPC_TGE: + case OPC_TGEU: + case OPC_TLT: + case OPC_TLTU: + case OPC_TNE: + /* Compare two registers */ + if (rs != rt) { + GEN_LOAD_REG_TN(T0, rs); + GEN_LOAD_REG_TN(T1, rt); + cond = 1; + } + case OPC_TEQI: + case OPC_TGEI: + case OPC_TGEIU: + case OPC_TLTI: + case OPC_TLTIU: + case OPC_TNEI: + /* Compare register to immediate */ + if (rs != 0 || imm != 0) { + GEN_LOAD_REG_TN(T0, rs); + GEN_LOAD_IMM_TN(T1, (int32_t)imm); + cond = 1; + } + break; + } + if (cond == 0) { + switch (opc) { + case OPC_TEQ: /* rs == rs */ + case OPC_TEQI: /* r0 == 0 */ + case OPC_TGE: /* rs >= rs */ + case OPC_TGEI: /* r0 >= 0 */ + case OPC_TGEU: /* rs >= rs unsigned */ + case OPC_TGEIU: /* r0 >= 0 unsigned */ + /* Always trap */ + gen_op_set_T0(1); + break; + case OPC_TLT: /* rs < rs */ + case OPC_TLTI: /* r0 < 0 */ + case OPC_TLTU: /* rs < rs unsigned */ + case OPC_TLTIU: /* r0 < 0 unsigned */ + case OPC_TNE: /* rs != rs */ + case OPC_TNEI: /* r0 != 0 */ + /* Never trap: treat as NOP */ + return; + default: + MIPS_INVAL("TRAP"); + generate_exception(ctx, EXCP_RI); + return; + } + } else { + switch (opc) { + case OPC_TEQ: + case OPC_TEQI: + gen_op_eq(); + break; + case OPC_TGE: + case OPC_TGEI: + gen_op_ge(); + break; + case OPC_TGEU: + case OPC_TGEIU: + gen_op_geu(); + break; + case OPC_TLT: + case OPC_TLTI: + gen_op_lt(); + break; + case OPC_TLTU: + case OPC_TLTIU: + gen_op_ltu(); + break; + case OPC_TNE: + case OPC_TNEI: + gen_op_ne(); + break; + default: + MIPS_INVAL("TRAP"); + generate_exception(ctx, EXCP_RI); + return; + } + } + save_cpu_state(ctx, 1); + gen_op_trap(); + ctx->bstate = BS_STOP; +} + +/* Branches (before delay slot) */ +static void gen_compute_branch (DisasContext *ctx, uint16_t opc, + int rs, int rt, int32_t offset) +{ + target_ulong btarget; + int blink, bcond; + + btarget = -1; + blink = 0; + bcond = 0; + /* Load needed operands */ + switch (opc) { + case OPC_BEQ: + case OPC_BEQL: + case OPC_BNE: + case OPC_BNEL: + /* Compare two registers */ + if (rs != rt) { + GEN_LOAD_REG_TN(T0, rs); + GEN_LOAD_REG_TN(T1, rt); + bcond = 1; + } + btarget = ctx->pc + 4 + offset; + break; + case OPC_BGEZ: + case OPC_BGEZAL: + case OPC_BGEZALL: + case OPC_BGEZL: + case OPC_BGTZ: + case OPC_BGTZL: + case OPC_BLEZ: + case OPC_BLEZL: + case OPC_BLTZ: + case OPC_BLTZAL: + case OPC_BLTZALL: + case OPC_BLTZL: + /* Compare to zero */ + if (rs != 0) { + gen_op_load_gpr_T0(rs); + bcond = 1; + } + btarget = ctx->pc + 4 + offset; + break; + case OPC_J: + case OPC_JAL: + /* Jump to immediate */ + btarget = ((ctx->pc + 4) & 0xF0000000) | offset; + break; + case OPC_JR: + case OPC_JALR: + /* Jump to register */ + if (offset != 0) { + /* Only hint = 0 is valid */ + generate_exception(ctx, EXCP_RI); + return; + } + GEN_LOAD_REG_TN(T2, rs); + break; + default: + MIPS_INVAL("branch/jump"); + generate_exception(ctx, EXCP_RI); + return; + } + if (bcond == 0) { + /* No condition to be computed */ + switch (opc) { + case OPC_BEQ: /* rx == rx */ + case OPC_BEQL: /* rx == rx likely */ + case OPC_BGEZ: /* 0 >= 0 */ + case OPC_BGEZL: /* 0 >= 0 likely */ + case OPC_BLEZ: /* 0 <= 0 */ + case OPC_BLEZL: /* 0 <= 0 likely */ + /* Always take */ + ctx->hflags |= MIPS_HFLAG_DS | MIPS_HFLAG_B; + MIPS_DEBUG("balways"); + break; + case OPC_BGEZAL: /* 0 >= 0 */ + case OPC_BGEZALL: /* 0 >= 0 likely */ + /* Always take and link */ + blink = 31; + ctx->hflags |= MIPS_HFLAG_DS | MIPS_HFLAG_B; + MIPS_DEBUG("balways and link"); + break; + case OPC_BNE: /* rx != rx */ + case OPC_BGTZ: /* 0 > 0 */ + case OPC_BLTZ: /* 0 < 0 */ + case OPC_BLTZAL: /* 0 < 0 */ + /* Treated as NOP */ + MIPS_DEBUG("bnever (NOP)"); + return; + case OPC_BNEL: /* rx != rx likely */ + case OPC_BGTZL: /* 0 > 0 likely */ + case OPC_BLTZALL: /* 0 < 0 likely */ + case OPC_BLTZL: /* 0 < 0 likely */ + /* Skip the instruction in the delay slot */ + MIPS_DEBUG("bnever and skip"); + gen_op_branch((long)ctx->tb, ctx->pc + 4); + return; + case OPC_J: + ctx->hflags |= MIPS_HFLAG_DS | MIPS_HFLAG_B; + MIPS_DEBUG("j %08x", btarget); + break; + case OPC_JAL: + blink = 31; + ctx->hflags |= MIPS_HFLAG_DS | MIPS_HFLAG_B; + MIPS_DEBUG("jal %08x", btarget); + break; + case OPC_JR: + ctx->hflags |= MIPS_HFLAG_DS | MIPS_HFLAG_BR; + MIPS_DEBUG("jr %s", regnames[rs]); + break; + case OPC_JALR: + blink = rt; + ctx->hflags |= MIPS_HFLAG_DS | MIPS_HFLAG_BR; + MIPS_DEBUG("jalr %s, %s", regnames[rt], regnames[rs]); + break; + default: + MIPS_INVAL("branch/jump"); + generate_exception(ctx, EXCP_RI); + return; + } + } else { + switch (opc) { + case OPC_BEQ: + gen_op_eq(); + MIPS_DEBUG("beq %s, %s, %08x", + regnames[rs], regnames[rt], btarget); + goto not_likely; + case OPC_BEQL: + gen_op_eq(); + MIPS_DEBUG("beql %s, %s, %08x", + regnames[rs], regnames[rt], btarget); + goto likely; + case OPC_BNE: + gen_op_ne(); + MIPS_DEBUG("bne %s, %s, %08x", + regnames[rs], regnames[rt], btarget); + goto not_likely; + case OPC_BNEL: + gen_op_ne(); + MIPS_DEBUG("bnel %s, %s, %08x", + regnames[rs], regnames[rt], btarget); + goto likely; + case OPC_BGEZ: + gen_op_gez(); + MIPS_DEBUG("bgez %s, %08x", regnames[rs], btarget); + goto not_likely; + case OPC_BGEZL: + gen_op_gez(); + MIPS_DEBUG("bgezl %s, %08x", regnames[rs], btarget); + goto likely; + case OPC_BGEZAL: + gen_op_gez(); + MIPS_DEBUG("bgezal %s, %08x", regnames[rs], btarget); + blink = 31; + goto not_likely; + case OPC_BGEZALL: + gen_op_gez(); + blink = 31; + MIPS_DEBUG("bgezall %s, %08x", regnames[rs], btarget); + goto likely; + case OPC_BGTZ: + gen_op_gtz(); + MIPS_DEBUG("bgtz %s, %08x", regnames[rs], btarget); + goto not_likely; + case OPC_BGTZL: + gen_op_gtz(); + MIPS_DEBUG("bgtzl %s, %08x", regnames[rs], btarget); + goto likely; + case OPC_BLEZ: + gen_op_lez(); + MIPS_DEBUG("blez %s, %08x", regnames[rs], btarget); + goto not_likely; + case OPC_BLEZL: + gen_op_lez(); + MIPS_DEBUG("blezl %s, %08x", regnames[rs], btarget); + goto likely; + case OPC_BLTZ: + gen_op_ltz(); + MIPS_DEBUG("bltz %s, %08x", regnames[rs], btarget); + goto not_likely; + case OPC_BLTZL: + gen_op_ltz(); + MIPS_DEBUG("bltzl %s, %08x", regnames[rs], btarget); + goto likely; + case OPC_BLTZAL: + gen_op_ltz(); + blink = 31; + MIPS_DEBUG("bltzal %s, %08x", regnames[rs], btarget); + not_likely: + ctx->hflags |= MIPS_HFLAG_DS | MIPS_HFLAG_BC; + break; + case OPC_BLTZALL: + gen_op_ltz(); + blink = 31; + MIPS_DEBUG("bltzall %s, %08x", regnames[rs], btarget); + likely: + ctx->hflags |= MIPS_HFLAG_DS | MIPS_HFLAG_BL; + break; + } + gen_op_set_bcond(); + } + MIPS_DEBUG("enter ds: link %d cond %02x target %08x", + blink, ctx->hflags, btarget); + ctx->btarget = btarget; + if (blink > 0) { + gen_op_set_T0(ctx->pc + 8); + gen_op_store_T0_gpr(blink); + } + return; +} + +/* CP0 (MMU and control) */ +static void gen_cp0 (DisasContext *ctx, uint16_t opc, int rt, int rd) +{ + const unsigned char *opn = "unk"; + + if (!(ctx->CP0_Status & (1 << CP0St_CU0)) && + !(ctx->hflags & MIPS_HFLAG_UM) && + !(ctx->hflags & MIPS_HFLAG_ERL) && + !(ctx->hflags & MIPS_HFLAG_EXL)) { + if (loglevel & CPU_LOG_TB_IN_ASM) { + fprintf(logfile, "CP0 is not usable\n"); + } + gen_op_raise_exception_err(EXCP_CpU, 0); + return; + } + switch (opc) { + case OPC_MFC0: + if (rt == 0) { + /* Treat as NOP */ + return; + } + gen_op_mfc0(rd, ctx->opcode & 0x7); + gen_op_store_T0_gpr(rt); + opn = "mfc0"; + break; + case OPC_MTC0: + /* If we get an exception, we want to restart at next instruction */ + ctx->pc += 4; + save_cpu_state(ctx, 1); + ctx->pc -= 4; + GEN_LOAD_REG_TN(T0, rt); + gen_op_mtc0(rd, ctx->opcode & 0x7); + /* Stop translation as we may have switched the execution mode */ + ctx->bstate = BS_STOP; + opn = "mtc0"; + break; +#if defined(MIPS_USES_R4K_TLB) + case OPC_TLBWI: + gen_op_tlbwi(); + opn = "tlbwi"; + break; + case OPC_TLBWR: + gen_op_tlbwr(); + opn = "tlbwr"; + break; + case OPC_TLBP: + gen_op_tlbp(); + opn = "tlbp"; + break; + case OPC_TLBR: + gen_op_tlbr(); + opn = "tlbr"; + break; +#endif + case OPC_ERET: + opn = "eret"; + save_cpu_state(ctx, 0); + gen_op_eret(); + ctx->bstate = BS_EXCP; + break; + case OPC_DERET: + opn = "deret"; + if (!(ctx->hflags & MIPS_HFLAG_DM)) { + generate_exception(ctx, EXCP_RI); + } else { + save_cpu_state(ctx, 0); + gen_op_deret(); + ctx->bstate = BS_EXCP; + } + break; + /* XXX: TODO: WAIT */ + default: + if (loglevel & CPU_LOG_TB_IN_ASM) { + fprintf(logfile, "Invalid CP0 opcode: %08x %03x %03x %03x\n", + ctx->opcode, ctx->opcode >> 26, ctx->opcode & 0x3F, + ((ctx->opcode >> 16) & 0x1F)); + } + generate_exception(ctx, EXCP_RI); + return; + } + MIPS_DEBUG("%s %s %d", opn, regnames[rt], rd); +} + +/* Coprocessor 1 (FPU) */ + +/* ISA extensions */ +/* MIPS16 extension to MIPS32 */ +/* SmartMIPS extension to MIPS32 */ + +#ifdef TARGET_MIPS64 +static void gen_arith64 (DisasContext *ctx, uint16_t opc) +{ + if (func == 0x02 && rd == 0) { + /* NOP */ + return; + } + if (rs == 0 || rt == 0) { + gen_op_reset_T0(); + gen_op_save64(); + } else { + gen_op_load_gpr_T0(rs); + gen_op_load_gpr_T1(rt); + gen_op_save64(); + if (func & 0x01) + gen_op_mul64u(); + else + gen_op_mul64s(); + } + if (func & 0x02) + gen_op_add64(); + else + gen_op_sub64(); +} + +/* Coprocessor 3 (FPU) */ + +/* MDMX extension to MIPS64 */ +/* MIPS-3D extension to MIPS64 */ + +#endif + +static void decode_opc (DisasContext *ctx) +{ + int32_t offset; + int rs, rt, rd, sa; + uint16_t op, op1; + int16_t imm; + + if ((ctx->hflags & MIPS_HFLAG_DS) && + (ctx->hflags & MIPS_HFLAG_BL)) { + /* Handle blikely not taken case */ + MIPS_DEBUG("blikely condition (%08x)", ctx->pc + 4); + gen_op_blikely((long)ctx->tb, ctx->pc + 4, + ctx->hflags & ~(MIPS_HFLAG_BMASK | MIPS_HFLAG_DS)); + } + op = ctx->opcode >> 26; + rs = ((ctx->opcode >> 21) & 0x1F); + rt = ((ctx->opcode >> 16) & 0x1F); + rd = ((ctx->opcode >> 11) & 0x1F); + sa = ((ctx->opcode >> 6) & 0x1F); + imm = (int16_t)ctx->opcode; + switch (op) { + case 0x00: /* Special opcode */ + op1 = ctx->opcode & 0x3F; + switch (op1) { + case 0x00: /* Arithmetic with immediate */ + case 0x02 ... 0x03: + gen_arith_imm(ctx, op1 | EXT_SPECIAL, rd, rt, sa); + break; + case 0x04: /* Arithmetic */ + case 0x06 ... 0x07: + case 0x0A ... 0x0B: + case 0x20 ... 0x27: + case 0x2A ... 0x2B: + gen_arith(ctx, op1 | EXT_SPECIAL, rd, rs, rt); + break; + case 0x18 ... 0x1B: /* MULT / DIV */ + gen_muldiv(ctx, op1 | EXT_SPECIAL, rs, rt); + break; + case 0x08 ... 0x09: /* Jumps */ + gen_compute_branch(ctx, op1 | EXT_SPECIAL, rs, rd, sa); + return; + case 0x30 ... 0x34: /* Traps */ + case 0x36: + gen_trap(ctx, op1 | EXT_SPECIAL, rs, rt, -1); + break; + case 0x10: /* Move from HI/LO */ + case 0x12: + gen_HILO(ctx, op1 | EXT_SPECIAL, rd); + break; + case 0x11: + case 0x13: /* Move to HI/LO */ + gen_HILO(ctx, op1 | EXT_SPECIAL, rs); + break; + case 0x0C: /* SYSCALL */ + generate_exception(ctx, EXCP_SYSCALL); + break; + case 0x0D: /* BREAK */ + generate_exception(ctx, EXCP_BREAK); + break; + case 0x0F: /* SYNC */ + /* Treat as a noop */ + break; + case 0x05: /* Pmon entry point */ + gen_op_pmon((ctx->opcode >> 6) & 0x1F); + break; +#if defined (MIPS_HAS_MOVCI) + case 0x01: /* MOVCI */ +#endif +#if defined (TARGET_MIPS64) + case 0x14: /* MIPS64 specific opcodes */ + case 0x16: + case 0x17: + case 0x1C ... 0x1F: + case 0x2C ... 0x2F: + case 0x37: + case 0x39 ... 0x3B: + case 0x3E ... 0x3F: +#endif + default: /* Invalid */ + MIPS_INVAL("special"); + generate_exception(ctx, EXCP_RI); + break; + } + break; + case 0x1C: /* Special2 opcode */ + op1 = ctx->opcode & 0x3F; + switch (op1) { +#if defined (MIPS_USES_R4K_EXT) + /* Those instructions are not part of MIPS32 core */ + case 0x00 ... 0x01: /* Multiply and add/sub */ + case 0x04 ... 0x05: + gen_muldiv(ctx, op1 | EXT_SPECIAL2, rs, rt); + break; + case 0x02: /* MUL */ + gen_arith(ctx, op1 | EXT_SPECIAL2, rd, rs, rt); + break; + case 0x20 ... 0x21: /* CLO / CLZ */ + gen_cl(ctx, op1 | EXT_SPECIAL2, rd, rs); + break; +#endif + case 0x3F: /* SDBBP */ + /* XXX: not clear which exception should be raised + * when in debug mode... + */ + if (!(ctx->hflags & MIPS_HFLAG_DM)) { + generate_exception(ctx, EXCP_DBp); + } else { + generate_exception(ctx, EXCP_DBp); + } + /* Treat as a noop */ + break; + default: /* Invalid */ + MIPS_INVAL("special2"); + generate_exception(ctx, EXCP_RI); + break; + } + break; + case 0x01: /* B REGIMM opcode */ + op1 = ((ctx->opcode >> 16) & 0x1F); + switch (op1) { + case 0x00 ... 0x03: /* REGIMM branches */ + case 0x10 ... 0x13: + gen_compute_branch(ctx, op1 | EXT_REGIMM, rs, -1, imm << 2); + return; + case 0x08 ... 0x0C: /* Traps */ + case 0x0E: + gen_trap(ctx, op1 | EXT_REGIMM, rs, -1, imm); + break; + default: /* Invalid */ + MIPS_INVAL("REGIMM"); + generate_exception(ctx, EXCP_RI); + break; + } + break; + case 0x10: /* CP0 opcode */ + op1 = ((ctx->opcode >> 21) & 0x1F); + switch (op1) { + case 0x00: + case 0x04: + gen_cp0(ctx, op1 | EXT_CP0, rt, rd); + break; + default: + gen_cp0(ctx, (ctx->opcode & 0x1F) | EXT_CP0, rt, rd); + break; + } + break; + case 0x08 ... 0x0F: /* Arithmetic with immediate opcode */ + gen_arith_imm(ctx, op, rt, rs, imm); + break; + case 0x02 ... 0x03: /* Jump */ + offset = (int32_t)(ctx->opcode & 0x03FFFFFF) << 2; + gen_compute_branch(ctx, op, rs, rt, offset); + return; + case 0x04 ... 0x07: /* Branch */ + case 0x14 ... 0x17: + gen_compute_branch(ctx, op, rs, rt, imm << 2); + return; + case 0x20 ... 0x26: /* Load and stores */ + case 0x28 ... 0x2E: + case 0x30: + case 0x38: + gen_ldst(ctx, op, rt, rs, imm); + break; + case 0x2F: /* Cache operation */ + /* Treat as a noop */ + break; + case 0x33: /* Prefetch */ + /* Treat as a noop */ + break; + case 0x3F: /* HACK */ + break; +#if defined(MIPS_USES_FPU) + case 0x31 ... 0x32: /* Floating point load/store */ + case 0x35 ... 0x36: + case 0x3A ... 0x3B: + case 0x3D ... 0x3E: + /* Not implemented */ + /* XXX: not correct */ +#endif + case 0x11: /* CP1 opcode */ + /* Not implemented */ + /* XXX: not correct */ + case 0x12: /* CP2 opcode */ + /* Not implemented */ + /* XXX: not correct */ + case 0x13: /* CP3 opcode */ + /* Not implemented */ + /* XXX: not correct */ +#if defined (TARGET_MIPS64) + case 0x18 ... 0x1B: + case 0x27: + case 0x34: + case 0x37: + /* MIPS64 opcodes */ +#endif +#if defined (MIPS_HAS_JALX) + case 0x1D: + /* JALX: not implemented */ +#endif + case 0x1E: + /* ASE specific */ +#if defined (MIPS_HAS_LSC) + case 0x31: /* LWC1 */ + case 0x32: /* LWC2 */ + case 0x35: /* SDC1 */ + case 0x36: /* SDC2 */ +#endif + default: /* Invalid */ + MIPS_INVAL(""); + generate_exception(ctx, EXCP_RI); + break; + } + if (ctx->hflags & MIPS_HFLAG_DS) { + int hflags = ctx->hflags; + /* Branches completion */ + ctx->hflags &= ~(MIPS_HFLAG_BMASK | MIPS_HFLAG_DS); + ctx->bstate = BS_BRANCH; + save_cpu_state(ctx, 0); + switch (hflags & MIPS_HFLAG_BMASK) { + case MIPS_HFLAG_B: + /* unconditional branch */ + MIPS_DEBUG("unconditional branch"); + gen_op_branch((long)ctx->tb, ctx->btarget); + break; + case MIPS_HFLAG_BL: + /* blikely taken case */ + MIPS_DEBUG("blikely branch taken"); + gen_op_branch((long)ctx->tb, ctx->btarget); + break; + case MIPS_HFLAG_BC: + /* Conditional branch */ + MIPS_DEBUG("conditional branch"); + gen_op_bcond((long)ctx->tb, ctx->btarget, ctx->pc + 4); + break; + case MIPS_HFLAG_BR: + /* unconditional branch to register */ + MIPS_DEBUG("branch to register"); + gen_op_breg(); + break; + default: + MIPS_DEBUG("unknown branch"); + break; + } + } +} + +int gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb, + int search_pc) +{ + DisasContext ctx, *ctxp = &ctx; + target_ulong pc_start; + uint16_t *gen_opc_end; + int j, lj = -1; + + pc_start = tb->pc; + gen_opc_ptr = gen_opc_buf; + gen_opc_end = gen_opc_buf + OPC_MAX_SIZE; + gen_opparam_ptr = gen_opparam_buf; + ctx.pc = pc_start; + ctx.tb = tb; + ctx.bstate = BS_NONE; + /* Restore delay slot state */ + ctx.hflags = env->hflags; + ctx.saved_hflags = ctx.hflags; + if (ctx.hflags & MIPS_HFLAG_BR) { + gen_op_restore_breg_target(); + } else if (ctx.hflags & MIPS_HFLAG_B) { + ctx.btarget = env->btarget; + } else if (ctx.hflags & MIPS_HFLAG_BMASK) { + /* If we are in the delay slot of a conditional branch, + * restore the branch condition from env->bcond to T2 + */ + ctx.btarget = env->btarget; + gen_op_restore_bcond(); + } +#if defined(CONFIG_USER_ONLY) + ctx.mem_idx = 0; +#else + ctx.mem_idx = (ctx.hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM ? 0 : 1; +#endif + ctx.CP0_Status = env->CP0_Status; +#ifdef DEBUG_DISAS + if (loglevel & CPU_LOG_TB_CPU) { + fprintf(logfile, "------------------------------------------------\n"); + cpu_dump_state(env, logfile, fprintf, 0); + } +#endif +#if defined MIPS_DEBUG_DISAS + if (loglevel & CPU_LOG_TB_IN_ASM) + fprintf(logfile, "\ntb %p super %d cond %04x %04x\n", + tb, ctx.mem_idx, ctx.hflags, env->hflags); +#endif + while (ctx.bstate == BS_NONE && gen_opc_ptr < gen_opc_end) { + if (search_pc) { + j = gen_opc_ptr - gen_opc_buf; + save_cpu_state(ctxp, 1); + if (lj < j) { + lj++; + while (lj < j) + gen_opc_instr_start[lj++] = 0; + gen_opc_pc[lj] = ctx.pc; + gen_opc_instr_start[lj] = 1; + } + } + ctx.opcode = ldl_code(ctx.pc); + decode_opc(&ctx); + ctx.pc += 4; + if ((ctx.pc & (TARGET_PAGE_SIZE - 1)) == 0) + break; +#if defined (MIPS_SINGLE_STEP) + break; +#endif + } + if (ctx.bstate != BS_BRANCH && ctx.bstate != BS_EXCP) { + save_cpu_state(ctxp, 0); + gen_op_branch((long)ctx.tb, ctx.pc); + } + gen_op_reset_T0(); + /* Generate the return instruction */ + gen_op_exit_tb(); + *gen_opc_ptr = INDEX_op_end; + if (search_pc) { + j = gen_opc_ptr - gen_opc_buf; + lj++; + while (lj <= j) + gen_opc_instr_start[lj++] = 0; + tb->size = 0; + } else { + tb->size = ctx.pc - pc_start; + } +#ifdef DEBUG_DISAS +#if defined MIPS_DEBUG_DISAS + if (loglevel & CPU_LOG_TB_IN_ASM) + fprintf(logfile, "\n"); +#endif + if (loglevel & CPU_LOG_TB_IN_ASM) { + fprintf(logfile, "IN: %s\n", lookup_symbol(pc_start)); + target_disas(logfile, pc_start, ctx.pc - pc_start, 0); + fprintf(logfile, "\n"); + } + if (loglevel & CPU_LOG_TB_OP) { + fprintf(logfile, "OP:\n"); + dump_ops(gen_opc_buf, gen_opparam_buf); + fprintf(logfile, "\n"); + } + if (loglevel & CPU_LOG_TB_CPU) { + fprintf(logfile, "---------------- %d %08x\n", ctx.bstate, ctx.hflags); + } +#endif + + return 0; +} + +int gen_intermediate_code (CPUState *env, struct TranslationBlock *tb) +{ + return gen_intermediate_code_internal(env, tb, 0); +} + +int gen_intermediate_code_pc (CPUState *env, struct TranslationBlock *tb) +{ + return gen_intermediate_code_internal(env, tb, 1); +} + +void cpu_dump_state (CPUState *env, FILE *f, + int (*cpu_fprintf)(FILE *f, const char *fmt, ...), + int flags) +{ + uint32_t c0_status; + int i; + + cpu_fprintf(f, "pc=0x%08x HI=0x%08x LO=0x%08x ds %04x %08x %d\n", + env->PC, env->HI, env->LO, env->hflags, env->btarget, env->bcond); + for (i = 0; i < 32; i++) { + if ((i & 3) == 0) + cpu_fprintf(f, "GPR%02d:", i); + cpu_fprintf(f, " %s %08x", regnames[i], env->gpr[i]); + if ((i & 3) == 3) + cpu_fprintf(f, "\n"); + } + + c0_status = env->CP0_Status; + if (env->hflags & MIPS_HFLAG_UM) + c0_status |= (1 << CP0St_UM); + if (env->hflags & MIPS_HFLAG_ERL) + c0_status |= (1 << CP0St_ERL); + if (env->hflags & MIPS_HFLAG_EXL) + c0_status |= (1 << CP0St_EXL); + + cpu_fprintf(f, "CP0 Status 0x%08x Cause 0x%08x EPC 0x%08x\n", + c0_status, env->CP0_Cause, env->CP0_EPC); + cpu_fprintf(f, " Config0 0x%08x Config1 0x%08x LLAddr 0x%08x\n", + env->CP0_Config0, env->CP0_Config1, env->CP0_LLAddr); +} + +CPUMIPSState *cpu_mips_init (void) +{ + CPUMIPSState *env; + + cpu_exec_init(); + env = qemu_mallocz(sizeof(CPUMIPSState)); + if (!env) + return NULL; + tlb_flush(env, 1); + /* Minimal init */ + env->PC = 0xBFC00000; +#if defined (MIPS_USES_R4K_TLB) + env->CP0_random = MIPS_TLB_NB - 1; +#endif + env->CP0_Wired = 0; + env->CP0_Config0 = MIPS_CONFIG0; +#if defined (MIPS_CONFIG1) + env->CP0_Config1 = MIPS_CONFIG1; +#endif +#if defined (MIPS_CONFIG2) + env->CP0_Config2 = MIPS_CONFIG2; +#endif +#if defined (MIPS_CONFIG3) + env->CP0_Config3 = MIPS_CONFIG3; +#endif + env->CP0_Status = (1 << CP0St_CU0) | (1 << CP0St_BEV); + env->CP0_WatchLo = 0; + env->hflags = MIPS_HFLAG_ERL; + /* Count register increments in debug mode, EJTAG version 1 */ + env->CP0_Debug = (1 << CP0DB_CNT) | (0x1 << CP0DB_VER); + env->CP0_PRid = MIPS_CPU; + env->exception_index = EXCP_NONE; + + cpu_single_env = env; + + return env; +} diff --git a/qemu/target-ppc/cpu.h b/qemu/target-ppc/cpu.h index 12f7c17..8dd9cc1 100644 --- a/qemu/target-ppc/cpu.h +++ b/qemu/target-ppc/cpu.h @@ -1,7 +1,7 @@ /* - * PPC emulation cpu definitions for qemu. + * PowerPC emulation cpu definitions for qemu. * - * Copyright (c) 2003 Jocelyn Mayer + * Copyright (c) 2003-2005 Jocelyn Mayer * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,64 +20,412 @@ #if !defined (__CPU_PPC_H__) #define __CPU_PPC_H__ +#include "config.h" + #define TARGET_LONG_BITS 32 #include "cpu-defs.h" -#include "config.h" #include #include "softfloat.h" #define TARGET_HAS_ICE 1 +/* XXX: this should be tunable: PowerPC 601 & 64 bits PowerPC + * have different cache line sizes + */ +#define ICACHE_LINE_SIZE 32 +#define DCACHE_LINE_SIZE 32 + +/* XXX: put this in a common place */ +#define likely(x) __builtin_expect(!!(x), 1) + +/*****************************************************************************/ +/* PVR definitions for most known PowerPC */ +enum { + /* PowerPC 401 cores */ + CPU_PPC_401A1 = 0x00210000, + CPU_PPC_401B2 = 0x00220000, + CPU_PPC_401C2 = 0x00230000, + CPU_PPC_401D2 = 0x00240000, + CPU_PPC_401E2 = 0x00250000, + CPU_PPC_401F2 = 0x00260000, + CPU_PPC_401G2 = 0x00270000, + CPU_PPC_IOP480 = 0x40100000, + /* PowerPC 403 cores */ + CPU_PPC_403GA = 0x00200000, + CPU_PPC_403GB = 0x00200100, + CPU_PPC_403GC = 0x00200200, + CPU_PPC_403GCX = 0x00201400, + /* PowerPC 405 cores */ + CPU_PPC_405 = 0x40110000, + CPU_PPC_405EP = 0x51210000, + CPU_PPC_405GPR = 0x50910000, + CPU_PPC_405D2 = 0x20010000, + CPU_PPC_405D4 = 0x41810000, + CPU_PPC_NPE405H = 0x41410000, + CPU_PPC_NPE405L = 0x41610000, +#if 0 + CPU_PPC_STB02 = xxx, +#endif + CPU_PPC_STB03 = 0x40310000, +#if 0 + CPU_PPC_STB04 = xxx, +#endif + CPU_PPC_STB25 = 0x51510000, +#if 0 + CPU_PPC_STB130 = xxx, +#endif + /* PowerPC 440 cores */ + CPU_PPC_440EP = 0x42220000, + CPU_PPC_440GP = 0x40120400, + CPU_PPC_440GX = 0x51B20000, + /* PowerPC MPC 8xx cores */ + CPU_PPC_8540 = 0x80200000, + CPU_PPC_8xx = 0x00500000, + CPU_PPC_8240 = 0x00810100, + CPU_PPC_8245 = 0x00811014, + /* PowerPC 6xx cores */ + CPU_PPC_601 = 0x00010000, + CPU_PPC_602 = 0x00050000, + CPU_PPC_603 = 0x00030000, + CPU_PPC_603E = 0x00060000, + CPU_PPC_603EV = 0x00070000, + CPU_PPC_603R = 0x00071000, + CPU_PPC_G2 = 0x80810000, + CPU_PPC_G2LE = 0x80820000, + CPU_PPC_604 = 0x00040000, + CPU_PPC_604E = 0x00090000, + CPU_PPC_604R = 0x000a0000, + /* PowerPC 74x/75x cores (aka G3) */ + CPU_PPC_74x = 0x00080000, + CPU_PPC_755 = 0x00083000, + CPU_PPC_74xP = 0x10080000, + CPU_PPC_750CXE22 = 0x00082202, + CPU_PPC_750CXE24 = 0x00082214, + CPU_PPC_750CXE24b = 0x00083214, + CPU_PPC_750CXE31 = 0x00083211, + CPU_PPC_750CXE31b = 0x00083311, +#define CPU_PPC_750CXE CPU_PPC_750CXE31b + CPU_PPC_750FX = 0x70000000, + CPU_PPC_750GX = 0x70020000, + /* PowerPC 74xx cores (aka G4) */ + CPU_PPC_7400 = 0x000C0000, + CPU_PPC_7410 = 0x800C0000, + CPU_PPC_7441 = 0x80000200, + CPU_PPC_7450 = 0x80000000, + CPU_PPC_7451 = 0x80000203, + CPU_PPC_7455 = 0x80010000, + CPU_PPC_7457 = 0x80020000, + CPU_PPC_7457A = 0x80030000, + /* 64 bits PowerPC */ + CPU_PPC_620 = 0x00140000, + CPU_PPC_630 = 0x00400000, + CPU_PPC_631 = 0x00410000, + CPU_PPC_POWER4 = 0x00350000, + CPU_PPC_POWER4P = 0x00380000, + CPU_PPC_POWER5 = 0x003A0000, + CPU_PPC_POWER5P = 0x003B0000, + CPU_PPC_970 = 0x00390000, + CPU_PPC_970FX = 0x003C0000, + CPU_PPC_RS64 = 0x00330000, + CPU_PPC_RS64II = 0x00340000, + CPU_PPC_RS64III = 0x00360000, + CPU_PPC_RS64IV = 0x00370000, + /* Original POWER */ + /* XXX: should be POWER (RIOS), RSC3308, RSC4608, + * POWER2 (RIOS2) & RSC2 (P2SC) here + */ +#if 0 + CPU_POWER = xxx, +#endif +#if 0 + CPU_POWER2 = xxx, +#endif +}; + +/* System version register (used on MPC 8xx) */ +enum { + PPC_SVR_8540 = 0x80300000, + PPC_SVR_8541E = 0x807A0000, + PPC_SVR_8555E = 0x80790000, + PPC_SVR_8560 = 0x80700000, +}; + +/*****************************************************************************/ /* Instruction types */ enum { - PPC_NONE = 0x0000, - PPC_INTEGER = 0x0001, /* CPU has integer operations instructions */ - PPC_FLOAT = 0x0002, /* CPU has floating point operations instructions */ - PPC_FLOW = 0x0004, /* CPU has flow control instructions */ - PPC_MEM = 0x0008, /* CPU has virtual memory instructions */ - PPC_RES = 0x0010, /* CPU has ld/st with reservation instructions */ - PPC_CACHE = 0x0020, /* CPU has cache control instructions */ - PPC_MISC = 0x0040, /* CPU has spr/msr access instructions */ - PPC_EXTERN = 0x0080, /* CPU has external control instructions */ - PPC_SEGMENT = 0x0100, /* CPU has memory segment instructions */ - PPC_CACHE_OPT= 0x0200, - PPC_FLOAT_OPT= 0x0400, - PPC_MEM_OPT = 0x0800, + PPC_NONE = 0x00000000, + /* integer operations instructions */ + /* flow control instructions */ + /* virtual memory instructions */ + /* ld/st with reservation instructions */ + /* cache control instructions */ + /* spr/msr access instructions */ + PPC_INSNS_BASE = 0x00000001, +#define PPC_INTEGER PPC_INSNS_BASE +#define PPC_FLOW PPC_INSNS_BASE +#define PPC_MEM PPC_INSNS_BASE +#define PPC_RES PPC_INSNS_BASE +#define PPC_CACHE PPC_INSNS_BASE +#define PPC_MISC PPC_INSNS_BASE + /* floating point operations instructions */ + PPC_FLOAT = 0x00000002, + /* more floating point operations instructions */ + PPC_FLOAT_EXT = 0x00000004, + /* external control instructions */ + PPC_EXTERN = 0x00000008, + /* segment register access instructions */ + PPC_SEGMENT = 0x00000010, + /* Optional cache control instructions */ + PPC_CACHE_OPT = 0x00000020, + /* Optional floating point op instructions */ + PPC_FLOAT_OPT = 0x00000040, + /* Optional memory control instructions */ + PPC_MEM_TLBIA = 0x00000080, + PPC_MEM_TLBIE = 0x00000100, + PPC_MEM_TLBSYNC = 0x00000200, + /* eieio & sync */ + PPC_MEM_SYNC = 0x00000400, + /* PowerPC 6xx TLB management instructions */ + PPC_6xx_TLB = 0x00000800, + /* Altivec support */ + PPC_ALTIVEC = 0x00001000, + /* Time base support */ + PPC_TB = 0x00002000, + /* Embedded PowerPC dedicated instructions */ + PPC_4xx_COMMON = 0x00004000, + /* PowerPC 40x exception model */ + PPC_40x_EXCP = 0x00008000, + /* PowerPC 40x specific instructions */ + PPC_40x_SPEC = 0x00010000, + /* PowerPC 405 Mac instructions */ + PPC_405_MAC = 0x00020000, + /* PowerPC 440 specific instructions */ + PPC_440_SPEC = 0x00040000, + /* Specific extensions */ + /* Power-to-PowerPC bridge (601) */ + PPC_POWER_BR = 0x00080000, + /* PowerPC 602 specific */ + PPC_602_SPEC = 0x00100000, + /* Deprecated instructions */ + /* Original POWER instruction set */ + PPC_POWER = 0x00200000, + /* POWER2 instruction set extension */ + PPC_POWER2 = 0x00400000, + /* Power RTC support */ + PPC_POWER_RTC = 0x00800000, + /* 64 bits PowerPC instructions */ + /* 64 bits PowerPC instruction set */ + PPC_64B = 0x01000000, + /* 64 bits hypervisor extensions */ + PPC_64H = 0x02000000, + /* 64 bits PowerPC "bridge" features */ + PPC_64_BRIDGE = 0x04000000, }; -#define PPC_COMMON (PPC_INTEGER | PPC_FLOAT | PPC_FLOW | PPC_MEM | \ - PPC_RES | PPC_CACHE | PPC_MISC | PPC_SEGMENT) -/* PPC 604 */ -#define PPC_604 (PPC_INTEGER | PPC_FLOAT | PPC_FLOW | PPC_MEM | \ - PPC_RES | PPC_CACHE | PPC_MISC | PPC_EXTERN | PPC_SEGMENT \ - PPC_MEM_OPT) -/* PPC 740/745/750/755 (aka G3) has external access instructions */ -#define PPC_750 (PPC_INTEGER | PPC_FLOAT | PPC_FLOW | PPC_MEM | \ - PPC_RES | PPC_CACHE | PPC_MISC | PPC_EXTERN | PPC_SEGMENT) +/* CPU run-time flags (MMU and exception model) */ +enum { + /* MMU model */ +#define PPC_FLAGS_MMU_MASK (0x0000000F) + /* Standard 32 bits PowerPC MMU */ + PPC_FLAGS_MMU_32B = 0x00000000, + /* Standard 64 bits PowerPC MMU */ + PPC_FLAGS_MMU_64B = 0x00000001, + /* PowerPC 601 MMU */ + PPC_FLAGS_MMU_601 = 0x00000002, + /* PowerPC 6xx MMU with software TLB */ + PPC_FLAGS_MMU_SOFT_6xx = 0x00000003, + /* PowerPC 4xx MMU with software TLB */ + PPC_FLAGS_MMU_SOFT_4xx = 0x00000004, + /* PowerPC 403 MMU */ + PPC_FLAGS_MMU_403 = 0x00000005, + /* Exception model */ +#define PPC_FLAGS_EXCP_MASK (0x000000F0) + /* Standard PowerPC exception model */ + PPC_FLAGS_EXCP_STD = 0x00000000, + /* PowerPC 40x exception model */ + PPC_FLAGS_EXCP_40x = 0x00000010, + /* PowerPC 601 exception model */ + PPC_FLAGS_EXCP_601 = 0x00000020, + /* PowerPC 602 exception model */ + PPC_FLAGS_EXCP_602 = 0x00000030, + /* PowerPC 603 exception model */ + PPC_FLAGS_EXCP_603 = 0x00000040, + /* PowerPC 604 exception model */ + PPC_FLAGS_EXCP_604 = 0x00000050, + /* PowerPC 7x0 exception model */ + PPC_FLAGS_EXCP_7x0 = 0x00000060, + /* PowerPC 7x5 exception model */ + PPC_FLAGS_EXCP_7x5 = 0x00000070, + /* PowerPC 74xx exception model */ + PPC_FLAGS_EXCP_74xx = 0x00000080, + /* PowerPC 970 exception model */ + PPC_FLAGS_EXCP_970 = 0x00000090, +}; + +#define PPC_MMU(env) (env->flags & PPC_FLAGS_MMU_MASK) +#define PPC_EXCP(env) (env->flags & PPC_FLAGS_EXCP_MASK) + +/*****************************************************************************/ +/* Supported instruction set definitions */ +/* This generates an empty opcode table... */ +#define PPC_INSNS_TODO (PPC_NONE) +#define PPC_FLAGS_TODO (0x00000000) + +/* PowerPC 40x instruction set */ +#define PPC_INSNS_4xx (PPC_INSNS_BASE | PPC_MEM_TLBSYNC | PPC_4xx_COMMON) +/* PowerPC 401 */ +#define PPC_INSNS_401 (PPC_INSNS_TODO) +#define PPC_FLAGS_401 (PPC_FLAGS_TODO) +/* PowerPC 403 */ +#define PPC_INSNS_403 (PPC_INSNS_4xx | PPC_MEM_SYNC | PPC_MEM_TLBIA | \ + PPC_40x_EXCP | PPC_40x_SPEC) +#define PPC_FLAGS_403 (PPC_FLAGS_MMU_403 | PPC_FLAGS_EXCP_40x) +/* PowerPC 405 */ +#define PPC_INSNS_405 (PPC_INSNS_4xx | PPC_MEM_SYNC | PPC_CACHE_OPT | \ + PPC_MEM_TLBIA | PPC_TB | PPC_40x_SPEC | PPC_40x_EXCP | \ + PPC_405_MAC) +#define PPC_FLAGS_405 (PPC_FLAGS_MMU_SOFT_4xx | PPC_FLAGS_EXCP_40x) +/* PowerPC 440 */ +#define PPC_INSNS_440 (PPC_INSNS_4xx | PPC_CACHE_OPT | PPC_405_MAC | \ + PPC_440_SPEC) +#define PPC_FLAGS_440 (PPC_FLAGS_TODO) +/* Non-embedded PowerPC */ +#define PPC_INSNS_COMMON (PPC_INSNS_BASE | PPC_FLOAT | PPC_MEM_SYNC | \ + PPC_SEGMENT | PPC_MEM_TLBIE) +/* PowerPC 601 */ +#define PPC_INSNS_601 (PPC_INSNS_COMMON | PPC_EXTERN | PPC_POWER_BR) +#define PPC_FLAGS_601 (PPC_FLAGS_MMU_601 | PPC_FLAGS_EXCP_601) +/* PowerPC 602 */ +#define PPC_INSNS_602 (PPC_INSNS_COMMON | PPC_FLOAT_EXT | PPC_6xx_TLB | \ + PPC_MEM_TLBSYNC | PPC_TB) +#define PPC_FLAGS_602 (PPC_FLAGS_MMU_SOFT_6xx | PPC_FLAGS_EXCP_602) +/* PowerPC 603 */ +#define PPC_INSNS_603 (PPC_INSNS_COMMON | PPC_FLOAT_EXT | PPC_6xx_TLB | \ + PPC_MEM_TLBSYNC | PPC_EXTERN | PPC_TB) +#define PPC_FLAGS_603 (PPC_FLAGS_MMU_SOFT_6xx | PPC_FLAGS_EXCP_603) +/* PowerPC G2 */ +#define PPC_INSNS_G2 (PPC_INSNS_COMMON | PPC_FLOAT_EXT | PPC_6xx_TLB | \ + PPC_MEM_TLBSYNC | PPC_EXTERN | PPC_TB) +#define PPC_FLAGS_G2 (PPC_FLAGS_MMU_SOFT_6xx | PPC_FLAGS_EXCP_603) +/* PowerPC 604 */ +#define PPC_INSNS_604 (PPC_INSNS_COMMON | PPC_FLOAT_EXT | PPC_EXTERN | \ + PPC_MEM_TLBSYNC | PPC_TB) +#define PPC_FLAGS_604 (PPC_FLAGS_MMU_32B | PPC_FLAGS_EXCP_604) +/* PowerPC 740/750 (aka G3) */ +#define PPC_INSNS_7x0 (PPC_INSNS_COMMON | PPC_FLOAT_EXT | PPC_EXTERN | \ + PPC_MEM_TLBSYNC | PPC_TB) +#define PPC_FLAGS_7x0 (PPC_FLAGS_MMU_32B | PPC_FLAGS_EXCP_7x0) +/* PowerPC 745/755 */ +#define PPC_INSNS_7x5 (PPC_INSNS_COMMON | PPC_FLOAT_EXT | PPC_EXTERN | \ + PPC_MEM_TLBSYNC | PPC_TB | PPC_6xx_TLB) +#define PPC_FLAGS_7x5 (PPC_FLAGS_MMU_SOFT_6xx | PPC_FLAGS_EXCP_7x5) +/* PowerPC 74xx (aka G4) */ +#define PPC_INSNS_74xx (PPC_INSNS_COMMON | PPC_FLOAT_EXT | PPC_ALTIVEC | \ + PPC_MEM_TLBSYNC | PPC_TB) +#define PPC_FLAGS_74xx (PPC_FLAGS_MMU_32B | PPC_FLAGS_EXCP_74xx) +/* Default PowerPC will be 604/970 */ +#define PPC_INSNS_PPC32 PPC_INSNS_604 +#define PPC_FLAGS_PPC32 PPC_FLAGS_604 +#if 0 +#define PPC_INSNS_PPC64 PPC_INSNS_970 +#define PPC_FLAGS_PPC64 PPC_FLAGS_970 +#endif +#define PPC_INSNS_DEFAULT PPC_INSNS_604 +#define PPC_FLAGS_DEFAULT PPC_FLAGS_604 +typedef struct ppc_def_t ppc_def_t; + +/*****************************************************************************/ +/* Types used to describe some PowerPC registers */ +typedef struct CPUPPCState CPUPPCState; +typedef struct opc_handler_t opc_handler_t; typedef struct ppc_tb_t ppc_tb_t; +typedef struct ppc_spr_t ppc_spr_t; +typedef struct ppc_dcr_t ppc_dcr_t; +typedef struct ppc_avr_t ppc_avr_t; + +/* SPR access micro-ops generations callbacks */ +struct ppc_spr_t { + void (*uea_read)(void *opaque, int spr_num); + void (*uea_write)(void *opaque, int spr_num); + void (*oea_read)(void *opaque, int spr_num); + void (*oea_write)(void *opaque, int spr_num); + const unsigned char *name; +}; + +/* Altivec registers (128 bits) */ +struct ppc_avr_t { + uint32_t u[4]; +}; + +/* Software TLB cache */ +typedef struct ppc_tlb_t ppc_tlb_t; +struct ppc_tlb_t { + /* Physical page number */ + target_phys_addr_t RPN; + /* Virtual page number */ + target_ulong VPN; + /* Page size */ + target_ulong size; + /* Protection bits */ + int prot; + int is_user; + uint32_t private; + uint32_t flags; +}; -/* Supervisor mode registers */ -/* Machine state register */ -#define MSR_POW 18 -#define MSR_ILE 16 -#define MSR_EE 15 -#define MSR_PR 14 -#define MSR_FP 13 -#define MSR_ME 12 -#define MSR_FE0 11 -#define MSR_SE 10 -#define MSR_BE 9 -#define MSR_FE1 8 -#define MSR_IP 6 -#define MSR_IR 5 -#define MSR_DR 4 -#define MSR_RI 1 -#define MSR_LE 0 +/*****************************************************************************/ +/* Machine state register bits definition */ +#define MSR_SF 63 /* Sixty-four-bit mode */ +#define MSR_ISF 61 /* Sixty-four-bit interrupt mode on 630 */ +#define MSR_HV 60 /* hypervisor state */ +#define MSR_VR 25 /* altivec available */ +#define MSR_AP 23 /* Access privilege state on 602 */ +#define MSR_SA 22 /* Supervisor access mode on 602 */ +#define MSR_KEY 19 /* key bit on 603e */ +#define MSR_POW 18 /* Power management */ +#define MSR_WE 18 /* Wait state enable on embedded PowerPC */ +#define MSR_TGPR 17 /* TGPR usage on 602/603 */ +#define MSR_TLB 17 /* TLB on ? */ +#define MSR_CE 17 /* Critical interrupt enable on embedded PowerPC */ +#define MSR_ILE 16 /* Interrupt little-endian mode */ +#define MSR_EE 15 /* External interrupt enable */ +#define MSR_PR 14 /* Problem state */ +#define MSR_FP 13 /* Floating point available */ +#define MSR_ME 12 /* Machine check interrupt enable */ +#define MSR_FE0 11 /* Floating point exception mode 0 */ +#define MSR_SE 10 /* Single-step trace enable */ +#define MSR_DWE 10 /* Debug wait enable on 405 */ +#define MSR_BE 9 /* Branch trace enable */ +#define MSR_DE 9 /* Debug interrupts enable on embedded PowerPC */ +#define MSR_FE1 8 /* Floating point exception mode 1 */ +#define MSR_AL 7 /* AL bit on POWER */ +#define MSR_IP 6 /* Interrupt prefix */ +#define MSR_IR 5 /* Instruction relocate */ +#define MSR_IS 5 /* Instruction address space on embedded PowerPC */ +#define MSR_DR 4 /* Data relocate */ +#define MSR_DS 4 /* Data address space on embedded PowerPC */ +#define MSR_PE 3 /* Protection enable on 403 */ +#define MSR_EP 3 /* Exception prefix on 601 */ +#define MSR_PX 2 /* Protection exclusive on 403 */ +#define MSR_PMM 2 /* Performance monitor mark on POWER */ +#define MSR_RI 1 /* Recoverable interrupt */ +#define MSR_LE 0 /* Little-endian mode */ +#define msr_sf env->msr[MSR_SF] +#define msr_isf env->msr[MSR_ISF] +#define msr_hv env->msr[MSR_HV] +#define msr_vr env->msr[MSR_VR] +#define msr_ap env->msr[MSR_AP] +#define msr_sa env->msr[MSR_SA] +#define msr_key env->msr[MSR_KEY] #define msr_pow env->msr[MSR_POW] +#define msr_we env->msr[MSR_WE] +#define msr_tgpr env->msr[MSR_TGPR] +#define msr_tlb env->msr[MSR_TLB] +#define msr_ce env->msr[MSR_CE] #define msr_ile env->msr[MSR_ILE] #define msr_ee env->msr[MSR_EE] #define msr_pr env->msr[MSR_PR] @@ -85,58 +433,72 @@ typedef struct ppc_tb_t ppc_tb_t; #define msr_me env->msr[MSR_ME] #define msr_fe0 env->msr[MSR_FE0] #define msr_se env->msr[MSR_SE] +#define msr_dwe env->msr[MSR_DWE] #define msr_be env->msr[MSR_BE] +#define msr_de env->msr[MSR_DE] #define msr_fe1 env->msr[MSR_FE1] +#define msr_al env->msr[MSR_AL] #define msr_ip env->msr[MSR_IP] #define msr_ir env->msr[MSR_IR] +#define msr_is env->msr[MSR_IS] #define msr_dr env->msr[MSR_DR] +#define msr_ds env->msr[MSR_DS] +#define msr_pe env->msr[MSR_PE] +#define msr_ep env->msr[MSR_EP] +#define msr_px env->msr[MSR_PX] +#define msr_pmm env->msr[MSR_PMM] #define msr_ri env->msr[MSR_RI] #define msr_le env->msr[MSR_LE] -/* Segment registers */ -typedef struct CPUPPCState { +/*****************************************************************************/ +/* The whole PowerPC CPU context */ +struct CPUPPCState { + /* First are the most commonly used resources + * during translated code execution + */ +#if TARGET_LONG_BITS > HOST_LONG_BITS + /* temporary fixed-point registers + * used to emulate 64 bits target on 32 bits hosts + */ + target_ulong t0, t1, t2; +#endif /* general purpose registers */ - uint32_t gpr[32]; - /* floating point registers */ - float64 fpr[32]; - /* segment registers */ - uint32_t sdr1; - uint32_t sr[16]; + target_ulong gpr[32]; + /* LR */ + target_ulong lr; + /* CTR */ + target_ulong ctr; + /* condition register */ + uint8_t crf[8]; /* XER */ - uint8_t xer[4]; + /* XXX: We use only 5 fields, but we want to keep the structure aligned */ + uint8_t xer[8]; /* Reservation address */ - uint32_t reserve; + target_ulong reserve; + + /* Those ones are used in supervisor mode only */ /* machine state register */ - uint8_t msr[32]; - /* condition register */ - uint8_t crf[8]; - /* floating point status and control register */ - uint8_t fpscr[8]; - uint32_t nip; - /* special purpose registers */ - uint32_t lr; - uint32_t ctr; - /* BATs */ - uint32_t DBAT[2][8]; - uint32_t IBAT[2][8]; - /* all others */ - uint32_t spr[1024]; - /* qemu dedicated */ + uint8_t msr[64]; + /* temporary general purpose registers */ + target_ulong tgpr[4]; /* Used to speed-up TLB assist handlers */ + + /* Floating point execution context */ /* temporary float registers */ float64 ft0; float64 ft1; float64 ft2; float_status fp_status; + /* floating point registers */ + float64 fpr[32]; + /* floating point status and control register */ + uint8_t fpscr[8]; - int interrupt_request; - jmp_buf jmp_env; - int exception_index; - int error_code; + /* soft mmu support */ + /* 0 = kernel, 1 = user (may have 2 = kernel code, 3 = user code ?) */ + CPUTLBEntry tlb_read[2][CPU_TLB_SIZE]; + CPUTLBEntry tlb_write[2][CPU_TLB_SIZE]; int access_type; /* when a memory exception occurs, the access type is stored here */ - int user_mode_only; /* user mode only simulation */ - struct TranslationBlock *current_tb; /* currently executing TB */ - /* soft mmu support */ /* in order to avoid passing too many arguments to the memory write helpers, we store some rarely used information in the CPU context) */ @@ -144,25 +506,83 @@ typedef struct CPUPPCState { written */ unsigned long mem_write_vaddr; /* target virtual addr at which the memory was written */ - /* 0 = kernel, 1 = user (may have 2 = kernel code, 3 = user code ?) */ - CPUTLBEntry tlb_read[2][CPU_TLB_SIZE]; - CPUTLBEntry tlb_write[2][CPU_TLB_SIZE]; - /* ice debug support */ - uint32_t breakpoints[MAX_BREAKPOINTS]; - int nb_breakpoints; - int singlestep_enabled; /* XXX: should use CPU single step mode instead */ + /* MMU context */ + /* Address space register */ + target_ulong asr; + /* segment registers */ + target_ulong sdr1; + target_ulong sr[16]; + /* BATs */ + int nb_BATs; + target_ulong DBAT[2][8]; + target_ulong IBAT[2][8]; + + /* Other registers */ + /* Special purpose registers */ + target_ulong spr[1024]; + /* Altivec registers */ + ppc_avr_t avr[32]; + uint32_t vscr; + /* Internal devices resources */ /* Time base and decrementer */ ppc_tb_t *tb_env; + /* Device control registers */ + int (*dcr_read)(ppc_dcr_t *dcr_env, int dcr_num, target_ulong *val); + int (*dcr_write)(ppc_dcr_t *dcr_env, int dcr_num, target_ulong val); + ppc_dcr_t *dcr_env; + + /* PowerPC TLB registers (for 4xx and 60x software driven TLBs) */ + int nb_tlb; + int nb_ways, last_way; + ppc_tlb_t tlb[128]; + /* Callbacks for specific checks on some implementations */ + int (*tlb_check_more)(CPUPPCState *env, struct ppc_tlb_t *tlb, int *prot, + target_ulong vaddr, int rw, int acc_type, + int is_user); + /* 403 dedicated access protection registers */ + target_ulong pb[4]; + + /* Those resources are used during exception processing */ + /* CPU model definition */ + uint64_t msr_mask; + uint32_t flags; + + int exception_index; + int error_code; + int interrupt_request; + + /* Those resources are used only during code translation */ + /* Next instruction pointer */ + target_ulong nip; + /* SPR translation callbacks */ + ppc_spr_t spr_cb[1024]; + /* opcode handlers */ + opc_handler_t *opcodes[0x40]; + + /* Those resources are used only in Qemu core */ + jmp_buf jmp_env; + int user_mode_only; /* user mode only simulation */ + struct TranslationBlock *current_tb; /* currently executing TB */ + uint32_t hflags; + + /* ice debug support */ + target_ulong breakpoints[MAX_BREAKPOINTS]; + int nb_breakpoints; + int singlestep_enabled; /* XXX: should use CPU single step mode instead */ /* Power management */ int power_mode; + /* temporary hack to handle OSI calls (only used if non NULL) */ + int (*osi_call)(struct CPUPPCState *env); + /* user data */ void *opaque; -} CPUPPCState; +}; +/*****************************************************************************/ CPUPPCState *cpu_ppc_init(void); int cpu_ppc_exec(CPUPPCState *s); void cpu_ppc_close(CPUPPCState *s); @@ -178,12 +598,38 @@ void cpu_loop_exit(void); void dump_stack (CPUPPCState *env); -uint32_t _load_xer (CPUPPCState *env); -void _store_xer (CPUPPCState *env, uint32_t value); -uint32_t _load_msr (CPUPPCState *env); -void _store_msr (CPUPPCState *env, uint32_t value); +target_ulong do_load_ibatu (CPUPPCState *env, int nr); +target_ulong do_load_ibatl (CPUPPCState *env, int nr); +void do_store_ibatu (CPUPPCState *env, int nr, target_ulong value); +void do_store_ibatl (CPUPPCState *env, int nr, target_ulong value); +target_ulong do_load_dbatu (CPUPPCState *env, int nr); +target_ulong do_load_dbatl (CPUPPCState *env, int nr); +void do_store_dbatu (CPUPPCState *env, int nr, target_ulong value); +void do_store_dbatl (CPUPPCState *env, int nr, target_ulong value); + +target_ulong do_load_nip (CPUPPCState *env); +void do_store_nip (CPUPPCState *env, target_ulong value); +target_ulong do_load_sdr1 (CPUPPCState *env); +void do_store_sdr1 (CPUPPCState *env, target_ulong value); +target_ulong do_load_asr (CPUPPCState *env); +void do_store_asr (CPUPPCState *env, target_ulong value); +target_ulong do_load_sr (CPUPPCState *env, int srnum); +void do_store_sr (CPUPPCState *env, int srnum, target_ulong value); +uint32_t do_load_cr (CPUPPCState *env); +void do_store_cr (CPUPPCState *env, uint32_t value, uint32_t mask); +uint32_t do_load_xer (CPUPPCState *env); +void do_store_xer (CPUPPCState *env, uint32_t value); +target_ulong do_load_msr (CPUPPCState *env); +void do_store_msr (CPUPPCState *env, target_ulong value); +float64 do_load_fpscr (CPUPPCState *env); +void do_store_fpscr (CPUPPCState *env, float64 f, uint32_t mask); -int cpu_ppc_register (CPUPPCState *env, uint32_t pvr); +void do_compute_hflags (CPUPPCState *env); + +int ppc_find_by_name (const unsigned char *name, ppc_def_t **def); +int ppc_find_by_pvr (uint32_t apvr, ppc_def_t **def); +void ppc_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)); +int cpu_ppc_register (CPUPPCState *env, ppc_def_t *def); /* Time-base and decrementer management */ #ifndef NO_CPU_IO_DEFS @@ -198,132 +644,276 @@ void cpu_ppc_store_decr (CPUPPCState *env, uint32_t value); #define TARGET_PAGE_BITS 12 #include "cpu-all.h" +/*****************************************************************************/ +/* Registers definitions */ #define ugpr(n) (env->gpr[n]) -#define fprd(n) (env->fpr[n]) -#define fprs(n) ((float)env->fpr[n]) -#define fpru(n) ((uint32_t)env->fpr[n]) -#define fpri(n) ((int32_t)env->fpr[n]) - -#define SPR_ENCODE(sprn) \ -(((sprn) >> 5) | (((sprn) & 0x1F) << 5)) -/* User mode SPR */ -#define spr(n) env->spr[n] #define XER_SO 31 #define XER_OV 30 #define XER_CA 29 +#define XER_CMP 8 #define XER_BC 0 -#define xer_so env->xer[3] -#define xer_ov env->xer[2] -#define xer_ca env->xer[1] +#define xer_so env->xer[4] +#define xer_ov env->xer[6] +#define xer_ca env->xer[2] +#define xer_cmp env->xer[1] #define xer_bc env->xer[0] -#define MQ SPR_ENCODE(0) -#define XER SPR_ENCODE(1) -#define RTCUR SPR_ENCODE(4) -#define RTCLR SPR_ENCODE(5) -#define LR SPR_ENCODE(8) -#define CTR SPR_ENCODE(9) -/* VEA mode SPR */ -#define V_TBL SPR_ENCODE(268) -#define V_TBU SPR_ENCODE(269) -/* supervisor mode SPR */ -#define DSISR SPR_ENCODE(18) -#define DAR SPR_ENCODE(19) -#define RTCUW SPR_ENCODE(20) -#define RTCLW SPR_ENCODE(21) -#define DECR SPR_ENCODE(22) -#define SDR1 SPR_ENCODE(25) -#define SRR0 SPR_ENCODE(26) -#define SRR1 SPR_ENCODE(27) -#define SPRG0 SPR_ENCODE(272) -#define SPRG1 SPR_ENCODE(273) -#define SPRG2 SPR_ENCODE(274) -#define SPRG3 SPR_ENCODE(275) -#define SPRG4 SPR_ENCODE(276) -#define SPRG5 SPR_ENCODE(277) -#define SPRG6 SPR_ENCODE(278) -#define SPRG7 SPR_ENCODE(279) -#define ASR SPR_ENCODE(280) -#define EAR SPR_ENCODE(282) -#define O_TBL SPR_ENCODE(284) -#define O_TBU SPR_ENCODE(285) -#define PVR SPR_ENCODE(287) -#define IBAT0U SPR_ENCODE(528) -#define IBAT0L SPR_ENCODE(529) -#define IBAT1U SPR_ENCODE(530) -#define IBAT1L SPR_ENCODE(531) -#define IBAT2U SPR_ENCODE(532) -#define IBAT2L SPR_ENCODE(533) -#define IBAT3U SPR_ENCODE(534) -#define IBAT3L SPR_ENCODE(535) -#define DBAT0U SPR_ENCODE(536) -#define DBAT0L SPR_ENCODE(537) -#define DBAT1U SPR_ENCODE(538) -#define DBAT1L SPR_ENCODE(539) -#define DBAT2U SPR_ENCODE(540) -#define DBAT2L SPR_ENCODE(541) -#define DBAT3U SPR_ENCODE(542) -#define DBAT3L SPR_ENCODE(543) -#define IBAT4U SPR_ENCODE(560) -#define IBAT4L SPR_ENCODE(561) -#define IBAT5U SPR_ENCODE(562) -#define IBAT5L SPR_ENCODE(563) -#define IBAT6U SPR_ENCODE(564) -#define IBAT6L SPR_ENCODE(565) -#define IBAT7U SPR_ENCODE(566) -#define IBAT7L SPR_ENCODE(567) -#define DBAT4U SPR_ENCODE(568) -#define DBAT4L SPR_ENCODE(569) -#define DBAT5U SPR_ENCODE(570) -#define DBAT5L SPR_ENCODE(571) -#define DBAT6U SPR_ENCODE(572) -#define DBAT6L SPR_ENCODE(573) -#define DBAT7U SPR_ENCODE(574) -#define DBAT7L SPR_ENCODE(575) -#define UMMCR0 SPR_ENCODE(936) -#define UPMC1 SPR_ENCODE(937) -#define UPMC2 SPR_ENCODE(938) -#define USIA SPR_ENCODE(939) -#define UMMCR1 SPR_ENCODE(940) -#define UPMC3 SPR_ENCODE(941) -#define UPMC4 SPR_ENCODE(942) -#define MMCR0 SPR_ENCODE(952) -#define PMC1 SPR_ENCODE(953) -#define PMC2 SPR_ENCODE(954) -#define SIA SPR_ENCODE(955) -#define MMCR1 SPR_ENCODE(956) -#define PMC3 SPR_ENCODE(957) -#define PMC4 SPR_ENCODE(958) -#define SDA SPR_ENCODE(959) -#define DMISS SPR_ENCODE(976) -#define DCMP SPR_ENCODE(977) -#define DHASH1 SPR_ENCODE(978) -#define DHASH2 SPR_ENCODE(979) -#define IMISS SPR_ENCODE(980) -#define ICMP SPR_ENCODE(981) -#define RPA SPR_ENCODE(982) -#define TCR SPR_ENCODE(984) -#define IBR SPR_ENCODE(986) -#define ESASRR SPR_ENCODE(987) -#define SEBR SPR_ENCODE(990) -#define SER SPR_ENCODE(991) -#define HID0 SPR_ENCODE(1008) -#define HID1 SPR_ENCODE(1009) -#define IABR SPR_ENCODE(1010) -#define HID2 SPR_ENCODE(1011) -#define DABR SPR_ENCODE(1013) -#define L2PM SPR_ENCODE(1016) -#define L2CR SPR_ENCODE(1017) -#define ICTC SPR_ENCODE(1019) -#define THRM1 SPR_ENCODE(1020) -#define THRM2 SPR_ENCODE(1021) -#define THRM3 SPR_ENCODE(1022) -#define SP SPR_ENCODE(1021) -#define SPR_LP SPR_ENCODE(1022) -#define DABR_MASK 0xFFFFFFF8 -#define FPECR SPR_ENCODE(1022) -#define PIR SPR_ENCODE(1023) +/* SPR definitions */ +#define SPR_MQ (0x000) +#define SPR_XER (0x001) +#define SPR_601_VRTCU (0x004) +#define SPR_601_VRTCL (0x005) +#define SPR_601_UDECR (0x006) +#define SPR_LR (0x008) +#define SPR_CTR (0x009) +#define SPR_DSISR (0x012) +#define SPR_DAR (0x013) +#define SPR_601_RTCU (0x014) +#define SPR_601_RTCL (0x015) +#define SPR_DECR (0x016) +#define SPR_SDR1 (0x019) +#define SPR_SRR0 (0x01A) +#define SPR_SRR1 (0x01B) +#define SPR_440_PID (0x030) +#define SPR_440_DECAR (0x036) +#define SPR_CSRR0 (0x03A) +#define SPR_CSRR1 (0x03B) +#define SPR_440_DEAR (0x03D) +#define SPR_440_ESR (0x03E) +#define SPR_440_IVPR (0x03F) +#define SPR_8xx_EIE (0x050) +#define SPR_8xx_EID (0x051) +#define SPR_8xx_NRE (0x052) +#define SPR_58x_CMPA (0x090) +#define SPR_58x_CMPB (0x091) +#define SPR_58x_CMPC (0x092) +#define SPR_58x_CMPD (0x093) +#define SPR_58x_ICR (0x094) +#define SPR_58x_DER (0x094) +#define SPR_58x_COUNTA (0x096) +#define SPR_58x_COUNTB (0x097) +#define SPR_58x_CMPE (0x098) +#define SPR_58x_CMPF (0x099) +#define SPR_58x_CMPG (0x09A) +#define SPR_58x_CMPH (0x09B) +#define SPR_58x_LCTRL1 (0x09C) +#define SPR_58x_LCTRL2 (0x09D) +#define SPR_58x_ICTRL (0x09E) +#define SPR_58x_BAR (0x09F) +#define SPR_VRSAVE (0x100) +#define SPR_USPRG0 (0x100) +#define SPR_USPRG4 (0x104) +#define SPR_USPRG5 (0x105) +#define SPR_USPRG6 (0x106) +#define SPR_USPRG7 (0x107) +#define SPR_VTBL (0x10C) +#define SPR_VTBU (0x10D) +#define SPR_SPRG0 (0x110) +#define SPR_SPRG1 (0x111) +#define SPR_SPRG2 (0x112) +#define SPR_SPRG3 (0x113) +#define SPR_SPRG4 (0x114) +#define SPR_SCOMC (0x114) +#define SPR_SPRG5 (0x115) +#define SPR_SCOMD (0x115) +#define SPR_SPRG6 (0x116) +#define SPR_SPRG7 (0x117) +#define SPR_ASR (0x118) +#define SPR_EAR (0x11A) +#define SPR_TBL (0x11C) +#define SPR_TBU (0x11D) +#define SPR_SVR (0x11E) +#define SPR_440_PIR (0x11E) +#define SPR_PVR (0x11F) +#define SPR_HSPRG0 (0x130) +#define SPR_440_DBSR (0x130) +#define SPR_HSPRG1 (0x131) +#define SPR_440_DBCR0 (0x134) +#define SPR_IBCR (0x135) +#define SPR_440_DBCR1 (0x135) +#define SPR_DBCR (0x136) +#define SPR_HDEC (0x136) +#define SPR_440_DBCR2 (0x136) +#define SPR_HIOR (0x137) +#define SPR_MBAR (0x137) +#define SPR_RMOR (0x138) +#define SPR_440_IAC1 (0x138) +#define SPR_HRMOR (0x139) +#define SPR_440_IAC2 (0x139) +#define SPR_HSSR0 (0x13A) +#define SPR_440_IAC3 (0x13A) +#define SPR_HSSR1 (0x13B) +#define SPR_440_IAC4 (0x13B) +#define SPR_LPCR (0x13C) +#define SPR_440_DAC1 (0x13C) +#define SPR_LPIDR (0x13D) +#define SPR_DABR2 (0x13D) +#define SPR_440_DAC2 (0x13D) +#define SPR_440_DVC1 (0x13E) +#define SPR_440_DVC2 (0x13F) +#define SPR_440_TSR (0x150) +#define SPR_440_TCR (0x154) +#define SPR_440_IVOR0 (0x190) +#define SPR_440_IVOR1 (0x191) +#define SPR_440_IVOR2 (0x192) +#define SPR_440_IVOR3 (0x193) +#define SPR_440_IVOR4 (0x194) +#define SPR_440_IVOR5 (0x195) +#define SPR_440_IVOR6 (0x196) +#define SPR_440_IVOR7 (0x197) +#define SPR_440_IVOR8 (0x198) +#define SPR_440_IVOR9 (0x199) +#define SPR_440_IVOR10 (0x19A) +#define SPR_440_IVOR11 (0x19B) +#define SPR_440_IVOR12 (0x19C) +#define SPR_440_IVOR13 (0x19D) +#define SPR_440_IVOR14 (0x19E) +#define SPR_440_IVOR15 (0x19F) +#define SPR_IBAT0U (0x210) +#define SPR_IBAT0L (0x211) +#define SPR_IBAT1U (0x212) +#define SPR_IBAT1L (0x213) +#define SPR_IBAT2U (0x214) +#define SPR_IBAT2L (0x215) +#define SPR_IBAT3U (0x216) +#define SPR_IBAT3L (0x217) +#define SPR_DBAT0U (0x218) +#define SPR_DBAT0L (0x219) +#define SPR_DBAT1U (0x21A) +#define SPR_DBAT1L (0x21B) +#define SPR_DBAT2U (0x21C) +#define SPR_DBAT2L (0x21D) +#define SPR_DBAT3U (0x21E) +#define SPR_DBAT3L (0x21F) +#define SPR_IBAT4U (0x230) +#define SPR_IBAT4L (0x231) +#define SPR_IBAT5U (0x232) +#define SPR_IBAT5L (0x233) +#define SPR_IBAT6U (0x234) +#define SPR_IBAT6L (0x235) +#define SPR_IBAT7U (0x236) +#define SPR_IBAT7L (0x237) +#define SPR_DBAT4U (0x238) +#define SPR_DBAT4L (0x239) +#define SPR_DBAT5U (0x23A) +#define SPR_DBAT5L (0x23B) +#define SPR_DBAT6U (0x23C) +#define SPR_DBAT6L (0x23D) +#define SPR_DBAT7U (0x23E) +#define SPR_DBAT7L (0x23F) +#define SPR_440_INV0 (0x370) +#define SPR_440_INV1 (0x371) +#define SPR_440_INV2 (0x372) +#define SPR_440_INV3 (0x373) +#define SPR_440_IVT0 (0x374) +#define SPR_440_IVT1 (0x375) +#define SPR_440_IVT2 (0x376) +#define SPR_440_IVT3 (0x377) +#define SPR_440_DNV0 (0x390) +#define SPR_440_DNV1 (0x391) +#define SPR_440_DNV2 (0x392) +#define SPR_440_DNV3 (0x393) +#define SPR_440_DVT0 (0x394) +#define SPR_440_DVT1 (0x395) +#define SPR_440_DVT2 (0x396) +#define SPR_440_DVT3 (0x397) +#define SPR_440_DVLIM (0x398) +#define SPR_440_IVLIM (0x399) +#define SPR_440_RSTCFG (0x39B) +#define SPR_440_DCBTRL (0x39C) +#define SPR_440_DCBTRH (0x39D) +#define SPR_440_ICBTRL (0x39E) +#define SPR_440_ICBTRH (0x39F) +#define SPR_UMMCR0 (0x3A8) +#define SPR_UPMC1 (0x3A9) +#define SPR_UPMC2 (0x3AA) +#define SPR_USIA (0x3AB) +#define SPR_UMMCR1 (0x3AC) +#define SPR_UPMC3 (0x3AD) +#define SPR_UPMC4 (0x3AE) +#define SPR_USDA (0x3AF) +#define SPR_40x_ZPR (0x3B0) +#define SPR_40x_PID (0x3B1) +#define SPR_440_MMUCR (0x3B2) +#define SPR_4xx_CCR0 (0x3B3) +#define SPR_405_IAC3 (0x3B4) +#define SPR_405_IAC4 (0x3B5) +#define SPR_405_DVC1 (0x3B6) +#define SPR_405_DVC2 (0x3B7) +#define SPR_MMCR0 (0x3B8) +#define SPR_PMC1 (0x3B9) +#define SPR_40x_SGR (0x3B9) +#define SPR_PMC2 (0x3BA) +#define SPR_40x_DCWR (0x3BA) +#define SPR_SIA (0x3BB) +#define SPR_405_SLER (0x3BB) +#define SPR_MMCR1 (0x3BC) +#define SPR_405_SU0R (0x3BC) +#define SPR_PMC3 (0x3BD) +#define SPR_405_DBCR1 (0x3BD) +#define SPR_PMC4 (0x3BE) +#define SPR_SDA (0x3BF) +#define SPR_403_VTBL (0x3CC) +#define SPR_403_VTBU (0x3CD) +#define SPR_DMISS (0x3D0) +#define SPR_DCMP (0x3D1) +#define SPR_DHASH1 (0x3D2) +#define SPR_DHASH2 (0x3D3) +#define SPR_4xx_ICDBDR (0x3D3) +#define SPR_IMISS (0x3D4) +#define SPR_40x_ESR (0x3D4) +#define SPR_ICMP (0x3D5) +#define SPR_40x_DEAR (0x3D5) +#define SPR_RPA (0x3D6) +#define SPR_40x_EVPR (0x3D6) +#define SPR_403_CDBCR (0x3D7) +#define SPR_TCR (0x3D8) +#define SPR_40x_TSR (0x3D8) +#define SPR_IBR (0x3DA) +#define SPR_40x_TCR (0x3DA) +#define SPR_ESASR (0x3DB) +#define SPR_40x_PIT (0x3DB) +#define SPR_403_TBL (0x3DC) +#define SPR_403_TBU (0x3DD) +#define SPR_SEBR (0x3DE) +#define SPR_40x_SRR2 (0x3DE) +#define SPR_SER (0x3DF) +#define SPR_40x_SRR3 (0x3DF) +#define SPR_HID0 (0x3F0) +#define SPR_40x_DBSR (0x3F0) +#define SPR_HID1 (0x3F1) +#define SPR_IABR (0x3F2) +#define SPR_40x_DBCR0 (0x3F2) +#define SPR_601_HID2 (0x3F2) +#define SPR_HID2 (0x3F3) +#define SPR_440_DBDR (0x3F3) +#define SPR_40x_IAC1 (0x3F4) +#define SPR_DABR (0x3F5) +#define DABR_MASK (~(target_ulong)0x7) +#define SPR_40x_IAC2 (0x3F5) +#define SPR_601_HID5 (0x3F5) +#define SPR_40x_DAC1 (0x3F6) +#define SPR_40x_DAC2 (0x3F7) +#define SPR_L2PM (0x3F8) +#define SPR_750_HID2 (0x3F8) +#define SPR_L2CR (0x3F9) +#define SPR_IABR2 (0x3FA) +#define SPR_40x_DCCR (0x3FA) +#define SPR_ICTC (0x3FB) +#define SPR_40x_ICCR (0x3FB) +#define SPR_THRM1 (0x3FC) +#define SPR_403_PBL1 (0x3FC) +#define SPR_SP (0x3FD) +#define SPR_THRM2 (0x3FD) +#define SPR_403_PBU1 (0x3FD) +#define SPR_LT (0x3FE) +#define SPR_THRM3 (0x3FE) +#define SPR_FPECR (0x3FE) +#define SPR_403_PBL2 (0x3FE) +#define SPR_PIR (0x3FF) +#define SPR_403_PBU2 (0x3FF) +#define SPR_601_HID15 (0x3FF) /* Memory access type : * may be needed for precise access rights control and precise exceptions. @@ -343,62 +933,72 @@ enum { /*****************************************************************************/ /* Exceptions */ -enum { - EXCP_NONE = -1, - /* PPC hardware exceptions : exception vector / 0x100 */ - EXCP_RESET = 0x01, /* System reset */ - EXCP_MACHINE_CHECK = 0x02, /* Machine check exception */ - EXCP_DSI = 0x03, /* Impossible memory access */ - EXCP_ISI = 0x04, /* Impossible instruction fetch */ - EXCP_EXTERNAL = 0x05, /* External interruption */ - EXCP_ALIGN = 0x06, /* Alignment exception */ - EXCP_PROGRAM = 0x07, /* Program exception */ - EXCP_NO_FP = 0x08, /* No floating point */ - EXCP_DECR = 0x09, /* Decrementer exception */ - EXCP_RESA = 0x0A, /* Implementation specific */ - EXCP_RESB = 0x0B, /* Implementation specific */ - EXCP_SYSCALL = 0x0C, /* System call */ - EXCP_TRACE = 0x0D, /* Trace exception (optional) */ - EXCP_FP_ASSIST = 0x0E, /* Floating-point assist (optional) */ - /* MPC740/745/750 & IBM 750 */ - EXCP_PERF = 0x0F, /* Performance monitor */ - EXCP_IABR = 0x13, /* Instruction address breakpoint */ - EXCP_SMI = 0x14, /* System management interrupt */ - EXCP_THRM = 0x15, /* Thermal management interrupt */ - /* MPC755 */ - EXCP_TLBMISS = 0x10, /* Instruction TLB miss */ - EXCP_TLBMISS_DL = 0x11, /* Data TLB miss for load */ - EXCP_TLBMISS_DS = 0x12, /* Data TLB miss for store */ - EXCP_PPC_MAX = 0x16, - /* Qemu exception */ - EXCP_OFCALL = 0x20, /* Call open-firmware emulator */ - EXCP_RTASCALL = 0x21, /* Call RTAS emulator */ - /* Special cases where we want to stop translation */ - EXCP_MTMSR = 0x104, /* mtmsr instruction: */ +#define EXCP_NONE -1 +/* PowerPC hardware exceptions : exception vectors defined in PowerPC book 3 */ +#define EXCP_RESET 0x0100 /* System reset */ +#define EXCP_MACHINE_CHECK 0x0200 /* Machine check exception */ +#define EXCP_DSI 0x0300 /* Data storage exception */ +#define EXCP_DSEG 0x0380 /* Data segment exception */ +#define EXCP_ISI 0x0400 /* Instruction storage exception */ +#define EXCP_ISEG 0x0480 /* Instruction segment exception */ +#define EXCP_EXTERNAL 0x0500 /* External interruption */ +#define EXCP_ALIGN 0x0600 /* Alignment exception */ +#define EXCP_PROGRAM 0x0700 /* Program exception */ +#define EXCP_NO_FP 0x0800 /* Floating point unavailable exception */ +#define EXCP_DECR 0x0900 /* Decrementer exception */ +#define EXCP_HDECR 0x0980 /* Hypervisor decrementer exception */ +#define EXCP_SYSCALL 0x0C00 /* System call */ +#define EXCP_TRACE 0x0D00 /* Trace exception */ +#define EXCP_PERF 0x0F00 /* Performance monitor exception */ +/* Exceptions defined in PowerPC 32 bits programming environment manual */ +#define EXCP_FP_ASSIST 0x0E00 /* Floating-point assist */ +/* Implementation specific exceptions */ +/* 40x exceptions */ +#define EXCP_40x_PIT 0x1000 /* Programmable interval timer interrupt */ +#define EXCP_40x_FIT 0x1010 /* Fixed interval timer interrupt */ +#define EXCP_40x_WATCHDOG 0x1020 /* Watchdog timer exception */ +#define EXCP_40x_DTLBMISS 0x1100 /* Data TLB miss exception */ +#define EXCP_40x_ITLBMISS 0x1200 /* Instruction TLB miss exception */ +#define EXCP_40x_DEBUG 0x2000 /* Debug exception */ +/* 405 specific exceptions */ +#define EXCP_405_APU 0x0F20 /* APU unavailable exception */ +/* TLB assist exceptions (602/603) */ +#define EXCP_I_TLBMISS 0x1000 /* Instruction TLB miss */ +#define EXCP_DL_TLBMISS 0x1100 /* Data load TLB miss */ +#define EXCP_DS_TLBMISS 0x1200 /* Data store TLB miss */ +/* Breakpoint exceptions (602/603/604/620/740/745/750/755...) */ +#define EXCP_IABR 0x1300 /* Instruction address breakpoint */ +#define EXCP_SMI 0x1400 /* System management interrupt */ +/* Altivec related exceptions */ +#define EXCP_VPU 0x0F20 /* VPU unavailable exception */ +/* 601 specific exceptions */ +#define EXCP_601_IO 0x0600 /* IO error exception */ +#define EXCP_601_RUNM 0x2000 /* Run mode exception */ +/* 602 specific exceptions */ +#define EXCP_602_WATCHDOG 0x1500 /* Watchdog exception */ +#define EXCP_602_EMUL 0x1600 /* Emulation trap exception */ +/* G2 specific exceptions */ +#define EXCP_G2_CRIT 0x0A00 /* Critical interrupt */ +/* MPC740/745/750 & IBM 750 specific exceptions */ +#define EXCP_THRM 0x1700 /* Thermal management interrupt */ +/* 74xx specific exceptions */ +#define EXCP_74xx_VPUA 0x1600 /* VPU assist exception */ +/* 970FX specific exceptions */ +#define EXCP_970_SOFTP 0x1500 /* Soft patch exception */ +#define EXCP_970_MAINT 0x1600 /* Maintenance exception */ +#define EXCP_970_THRM 0x1800 /* Thermal exception */ +#define EXCP_970_VPUA 0x1700 /* VPU assist exception */ +/* End of exception vectors area */ +#define EXCP_PPC_MAX 0x4000 +/* Qemu exceptions: special cases we want to stop translation */ +#define EXCP_MTMSR 0x11000 /* mtmsr instruction: */ /* may change privilege level */ - EXCP_BRANCH = 0x108, /* branch instruction */ - EXCP_RFI = 0x10C, /* return from interrupt */ - EXCP_SYSCALL_USER = 0x110, /* System call in user mode only */ -}; +#define EXCP_BRANCH 0x11001 /* branch instruction */ +#define EXCP_SYSCALL_USER 0x12000 /* System call in user mode only */ +#define EXCP_INTERRUPT_CRITICAL 0x13000 /* critical IRQ */ + /* Error codes */ enum { - /* Exception subtypes for EXCP_DSI */ - EXCP_DSI_TRANSLATE = 0x01, /* Data address can't be translated */ - EXCP_DSI_NOTSUP = 0x02, /* Access type not supported */ - EXCP_DSI_PROT = 0x03, /* Memory protection violation */ - EXCP_DSI_EXTERNAL = 0x04, /* External access disabled */ - EXCP_DSI_DABR = 0x05, /* Data address breakpoint */ - /* flags for EXCP_DSI */ - EXCP_DSI_DIRECT = 0x10, - EXCP_DSI_STORE = 0x20, - EXCP_DSI_ECXW = 0x40, - /* Exception subtypes for EXCP_ISI */ - EXCP_ISI_TRANSLATE = 0x01, /* Code address can't be translated */ - EXCP_ISI_NOEXEC = 0x02, /* Try to fetch from a data segment */ - EXCP_ISI_GUARD = 0x03, /* Fetch from guarded memory */ - EXCP_ISI_PROT = 0x04, /* Memory protection violation */ - EXCP_ISI_DIRECT = 0x05, /* Trying to fetch from * - * a direct store segment */ /* Exception subtypes for EXCP_ALIGN */ EXCP_ALIGN_FP = 0x01, /* FP alignment exception */ EXCP_ALIGN_LST = 0x02, /* Unaligned mult/extern load/store */ diff --git a/qemu/target-ppc/exec.h b/qemu/target-ppc/exec.h index ad21c56..9e89880 100644 --- a/qemu/target-ppc/exec.h +++ b/qemu/target-ppc/exec.h @@ -1,7 +1,7 @@ /* - * PPC emulation definitions for qemu. + * PowerPC emulation definitions for qemu. * - * Copyright (c) 2003 Jocelyn Mayer + * Copyright (c) 2003-2005 Jocelyn Mayer * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,8 +20,12 @@ #if !defined (__PPC_H__) #define __PPC_H__ +#include "config.h" + #include "dyngen-exec.h" +#define TARGET_LONG_BITS 32 + register struct CPUPPCState *env asm(AREG0); register uint32_t T0 asm(AREG1); register uint32_t T1 asm(AREG2); @@ -119,15 +123,6 @@ static inline uint32_t rotl (uint32_t i, int n) void do_raise_exception_err (uint32_t exception, int error_code); void do_raise_exception (uint32_t exception); -void do_load_cr (void); -void do_store_cr (uint32_t mask); -void do_load_xer (void); -void do_store_xer (void); -void do_load_msr (void); -void do_store_msr (void); -void do_load_fpscr (void); -void do_store_fpscr (uint32_t mask); - void do_sraw(void); void do_fctiw (void); @@ -143,20 +138,9 @@ void do_fcmpo (void); void do_check_reservation (void); void do_icbi (void); -void do_store_sr (uint32_t srnum); -void do_store_ibat (int ul, int nr); -void do_store_dbat (int ul, int nr); void do_tlbia (void); void do_tlbie (void); -void dump_state (void); -void dump_rfi (void); -void dump_store_sr (int srnum); -void dump_store_ibat (int ul, int nr); -void dump_store_dbat (int ul, int nr); -void dump_store_tb (int ul); -void dump_update_tb(uint32_t param); - static inline void env_to_regs(void) { } diff --git a/qemu/target-ppc/helper.c b/qemu/target-ppc/helper.c index b54b5d2..c9693c8 100644 --- a/qemu/target-ppc/helper.c +++ b/qemu/target-ppc/helper.c @@ -1,7 +1,7 @@ /* - * PPC emulation helpers for qemu. + * PowerPC emulation helpers for qemu. * - * Copyright (c) 2003 Jocelyn Mayer + * Copyright (c) 2003-2005 Jocelyn Mayer * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -17,14 +17,24 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "exec.h" +#include +#include +#include +#include +#include +#include +#include + +#include "cpu.h" +#include "exec-all.h" //#define DEBUG_MMU //#define DEBUG_BATS //#define DEBUG_EXCEPTIONS +//#define FLUSH_ALL_TLBS /*****************************************************************************/ -/* PPC MMU emulation */ +/* PowerPC MMU emulation */ /* Perform BAT hit & translation */ static int get_bat (CPUState *env, uint32_t *real, int *prot, @@ -128,7 +138,7 @@ static int find_pte (uint32_t *RPN, int *prot, uint32_t base, uint32_t va, pte0 = ldl_phys(base + (i * 8)); pte1 = ldl_phys(base + (i * 8) + 4); #if defined (DEBUG_MMU) - if (loglevel > 0) { + if (loglevel > 0) { fprintf(logfile, "Load pte from 0x%08x => 0x%08x 0x%08x " "%d %d %d 0x%08x\n", base + (i * 8), pte0, pte1, pte0 >> 31, h, (pte0 >> 6) & 1, va); @@ -175,17 +185,17 @@ static int find_pte (uint32_t *RPN, int *prot, uint32_t base, uint32_t va, if (loglevel > 0) fprintf(logfile, "PTE access granted !\n"); #endif - good = i; - keep = pte1; - ret = 0; + good = i; + keep = pte1; + ret = 0; } else { /* Access right violation */ - ret = -2; + ret = -2; #if defined (DEBUG_MMU) if (loglevel > 0) fprintf(logfile, "PTE access rejected\n"); #endif - } + } *prot = access; } } @@ -194,7 +204,7 @@ static int find_pte (uint32_t *RPN, int *prot, uint32_t base, uint32_t va, if (good != -1) { *RPN = keep & 0xFFFFF000; #if defined (DEBUG_MMU) - if (loglevel > 0) { + if (loglevel > 0) { fprintf(logfile, "found PTE at addr 0x%08x prot=0x%01x ret=%d\n", *RPN, *prot, ret); } @@ -205,7 +215,7 @@ static int find_pte (uint32_t *RPN, int *prot, uint32_t base, uint32_t va, keep |= 0x00000100; store = 1; } - if (!(keep & 0x00000080)) { + if (!(keep & 0x00000080)) { if (rw && ret == 0) { /* Change flag */ keep |= 0x00000080; @@ -251,7 +261,7 @@ static int get_segment (CPUState *env, uint32_t *real, int *prot, ((sr & 0x40000000) && msr_pr == 0)) ? 1 : 0; if ((sr & 0x80000000) == 0) { #if defined (DEBUG_MMU) - if (loglevel > 0) + if (loglevel > 0) fprintf(logfile, "pte segment: key=%d n=0x%08x\n", key, sr & 0x10000000); #endif @@ -394,92 +404,6 @@ target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr) } #endif -#if !defined(CONFIG_USER_ONLY) - -#define MMUSUFFIX _mmu -#define GETPC() (__builtin_return_address(0)) - -#define SHIFT 0 -#include "softmmu_template.h" - -#define SHIFT 1 -#include "softmmu_template.h" - -#define SHIFT 2 -#include "softmmu_template.h" - -#define SHIFT 3 -#include "softmmu_template.h" - -/* try to fill the TLB and return an exception if error. If retaddr is - NULL, it means that the function was called in C code (i.e. not - from generated code or from helper.c) */ -/* XXX: fix it to restore all registers */ -void tlb_fill(target_ulong addr, int is_write, int is_user, void *retaddr) -{ - TranslationBlock *tb; - CPUState *saved_env; - unsigned long pc; - int ret; - - /* XXX: hack to restore env in all cases, even if not called from - generated code */ - saved_env = env; - env = cpu_single_env; -#if 0 - { - unsigned long tlb_addrr, tlb_addrw; - int index; - index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - tlb_addrr = env->tlb_read[is_user][index].address; - tlb_addrw = env->tlb_write[is_user][index].address; - if (loglevel) { - fprintf(logfile, - "%s 1 %p %p idx=%d addr=0x%08lx tbl_addr=0x%08lx 0x%08lx " - "(0x%08lx 0x%08lx)\n", __func__, env, - &env->tlb_read[is_user][index], index, addr, - tlb_addrr, tlb_addrw, addr & TARGET_PAGE_MASK, - tlb_addrr & (TARGET_PAGE_MASK | TLB_INVALID_MASK)); - } - } -#endif - ret = cpu_ppc_handle_mmu_fault(env, addr, is_write, is_user, 1); - if (ret) { - if (retaddr) { - /* now we have a real cpu fault */ - pc = (unsigned long)retaddr; - tb = tb_find_pc(pc); - if (tb) { - /* the PC is inside the translated code. It means that we have - a virtual CPU fault */ - cpu_restore_state(tb, env, pc, NULL); - } - } - do_raise_exception_err(env->exception_index, env->error_code); - } -#if 0 - { - unsigned long tlb_addrr, tlb_addrw; - int index; - index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - tlb_addrr = env->tlb_read[is_user][index].address; - tlb_addrw = env->tlb_write[is_user][index].address; - printf("%s 2 %p %p idx=%d addr=0x%08lx tbl_addr=0x%08lx 0x%08lx " - "(0x%08lx 0x%08lx)\n", __func__, env, - &env->tlb_read[is_user][index], index, addr, - tlb_addrr, tlb_addrw, addr & TARGET_PAGE_MASK, - tlb_addrr & (TARGET_PAGE_MASK | TLB_INVALID_MASK)); - } -#endif - env = saved_env; -} - -void cpu_ppc_init_mmu(CPUState *env) -{ - /* Nothing to do: all translation are disabled */ -} -#endif - /* Perform address translation */ int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, int is_user, int is_softmmu) @@ -522,20 +446,25 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, switch (ret) { case -1: /* No matches in page tables */ - error_code = EXCP_ISI_TRANSLATE; + error_code = 0x40000000; break; case -2: /* Access rights violation */ - error_code = EXCP_ISI_PROT; + error_code = 0x08000000; break; case -3: /* No execute protection violation */ - error_code = EXCP_ISI_NOEXEC; + error_code = 0x10000000; break; case -4: /* Direct store exception */ /* No code fetch is allowed in direct-store areas */ - error_code = EXCP_ISI_DIRECT; + error_code = 0x10000000; + break; + case -5: + /* No match in segment table */ + exception = EXCP_ISEG; + error_code = 0; break; } } else { @@ -543,11 +472,11 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, switch (ret) { case -1: /* No matches in page tables */ - error_code = EXCP_DSI_TRANSLATE; + error_code = 0x40000000; break; case -2: /* Access rights violation */ - error_code = EXCP_DSI_PROT; + error_code = 0x08000000; break; case -4: /* Direct store exception */ @@ -559,14 +488,11 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, break; case ACCESS_RES: /* lwarx, ldarx or srwcx. */ - exception = EXCP_DSI; - error_code = EXCP_DSI_NOTSUP | EXCP_DSI_DIRECT; + error_code = 0x04000000; break; case ACCESS_EXT: /* eciwx or ecowx */ - exception = EXCP_DSI; - error_code = EXCP_DSI_NOTSUP | EXCP_DSI_DIRECT | - EXCP_DSI_ECXW; + error_code = 0x04100000; break; default: printf("DSI: invalid exception (%d)\n", ret); @@ -574,11 +500,18 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, error_code = EXCP_INVAL | EXCP_INVAL_INVAL; break; } + break; + case -5: + /* No match in segment table */ + exception = EXCP_DSEG; + error_code = 0; + break; } - if (rw) - error_code |= EXCP_DSI_STORE; + if (exception == EXCP_DSI && rw == 1) + error_code |= 0x02000000; /* Store fault address */ - env->spr[DAR] = address; + env->spr[SPR_DAR] = address; + env->spr[SPR_DSISR] = error_code; } #if 0 printf("%s: set exception to %d %02x\n", @@ -591,25 +524,239 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, return ret; } -uint32_t _load_xer (CPUState *env) +/*****************************************************************************/ +/* BATs management */ +#if !defined(FLUSH_ALL_TLBS) +static inline void do_invalidate_BAT (CPUPPCState *env, + target_ulong BATu, target_ulong mask) +{ + target_ulong base, end, page; + base = BATu & ~0x0001FFFF; + end = base + mask + 0x00020000; +#if defined (DEBUG_BATS) + if (loglevel != 0) + fprintf(logfile, "Flush BAT from %08x to %08x (%08x)\n", base, end, mask); +#endif + for (page = base; page != end; page += TARGET_PAGE_SIZE) + tlb_flush_page(env, page); +#if defined (DEBUG_BATS) + if (loglevel != 0) + fprintf(logfile, "Flush done\n"); +#endif +} +#endif + +static inline void dump_store_bat (CPUPPCState *env, char ID, int ul, int nr, + target_ulong value) +{ +#if defined (DEBUG_BATS) + if (loglevel != 0) { + fprintf(logfile, "Set %cBAT%d%c to 0x%08lx (0x%08lx)\n", + ID, nr, ul == 0 ? 'u' : 'l', (unsigned long)value, + (unsigned long)env->nip); + } +#endif +} + +target_ulong do_load_ibatu (CPUPPCState *env, int nr) +{ + return env->IBAT[0][nr]; +} + +target_ulong do_load_ibatl (CPUPPCState *env, int nr) +{ + return env->IBAT[1][nr]; +} + +void do_store_ibatu (CPUPPCState *env, int nr, target_ulong value) +{ + target_ulong mask; + + dump_store_bat(env, 'I', 0, nr, value); + if (env->IBAT[0][nr] != value) { + mask = (value << 15) & 0x0FFE0000UL; +#if !defined(FLUSH_ALL_TLBS) + do_invalidate_BAT(env, env->IBAT[0][nr], mask); +#endif + /* When storing valid upper BAT, mask BEPI and BRPN + * and invalidate all TLBs covered by this BAT + */ + mask = (value << 15) & 0x0FFE0000UL; + env->IBAT[0][nr] = (value & 0x00001FFFUL) | + (value & ~0x0001FFFFUL & ~mask); + env->IBAT[1][nr] = (env->IBAT[1][nr] & 0x0000007B) | + (env->IBAT[1][nr] & ~0x0001FFFF & ~mask); +#if !defined(FLUSH_ALL_TLBS) + do_invalidate_BAT(env, env->IBAT[0][nr], mask); +#endif +#if defined(FLUSH_ALL_TLBS) + tlb_flush(env, 1); +#endif + } +} + +void do_store_ibatl (CPUPPCState *env, int nr, target_ulong value) +{ + dump_store_bat(env, 'I', 1, nr, value); + env->IBAT[1][nr] = value; +} + +target_ulong do_load_dbatu (CPUPPCState *env, int nr) +{ + return env->DBAT[0][nr]; +} + +target_ulong do_load_dbatl (CPUPPCState *env, int nr) +{ + return env->DBAT[1][nr]; +} + +void do_store_dbatu (CPUPPCState *env, int nr, target_ulong value) +{ + target_ulong mask; + + dump_store_bat(env, 'D', 0, nr, value); + if (env->DBAT[0][nr] != value) { + /* When storing valid upper BAT, mask BEPI and BRPN + * and invalidate all TLBs covered by this BAT + */ + mask = (value << 15) & 0x0FFE0000UL; +#if !defined(FLUSH_ALL_TLBS) + do_invalidate_BAT(env, env->DBAT[0][nr], mask); +#endif + mask = (value << 15) & 0x0FFE0000UL; + env->DBAT[0][nr] = (value & 0x00001FFFUL) | + (value & ~0x0001FFFFUL & ~mask); + env->DBAT[1][nr] = (env->DBAT[1][nr] & 0x0000007B) | + (env->DBAT[1][nr] & ~0x0001FFFF & ~mask); +#if !defined(FLUSH_ALL_TLBS) + do_invalidate_BAT(env, env->DBAT[0][nr], mask); +#else + tlb_flush(env, 1); +#endif + } +} + +void do_store_dbatl (CPUPPCState *env, int nr, target_ulong value) +{ + dump_store_bat(env, 'D', 1, nr, value); + env->DBAT[1][nr] = value; +} + +static inline void invalidate_all_tlbs (CPUPPCState *env) +{ + /* XXX: this needs to be completed for sotware driven TLB support */ + tlb_flush(env, 1); +} + +/*****************************************************************************/ +/* Special registers manipulation */ +target_ulong do_load_nip (CPUPPCState *env) +{ + return env->nip; +} + +void do_store_nip (CPUPPCState *env, target_ulong value) +{ + env->nip = value; +} + +target_ulong do_load_sdr1 (CPUPPCState *env) +{ + return env->sdr1; +} + +void do_store_sdr1 (CPUPPCState *env, target_ulong value) +{ +#if defined (DEBUG_MMU) + if (loglevel != 0) { + fprintf(logfile, "%s: 0x%08lx\n", __func__, (unsigned long)value); + } +#endif + if (env->sdr1 != value) { + env->sdr1 = value; + invalidate_all_tlbs(env); + } +} + +target_ulong do_load_sr (CPUPPCState *env, int srnum) +{ + return env->sr[srnum]; +} + +void do_store_sr (CPUPPCState *env, int srnum, target_ulong value) +{ +#if defined (DEBUG_MMU) + if (loglevel != 0) { + fprintf(logfile, "%s: reg=%d 0x%08lx %08lx\n", + __func__, srnum, (unsigned long)value, env->sr[srnum]); + } +#endif + if (env->sr[srnum] != value) { + env->sr[srnum] = value; +#if !defined(FLUSH_ALL_TLBS) && 0 + { + target_ulong page, end; + /* Invalidate 256 MB of virtual memory */ + page = (16 << 20) * srnum; + end = page + (16 << 20); + for (; page != end; page += TARGET_PAGE_SIZE) + tlb_flush_page(env, page); + } +#else + invalidate_all_tlbs(env); +#endif + } +} + +uint32_t do_load_cr (CPUPPCState *env) +{ + return (env->crf[0] << 28) | + (env->crf[1] << 24) | + (env->crf[2] << 20) | + (env->crf[3] << 16) | + (env->crf[4] << 12) | + (env->crf[5] << 8) | + (env->crf[6] << 4) | + (env->crf[7] << 0); +} + +void do_store_cr (CPUPPCState *env, uint32_t value, uint32_t mask) +{ + int i, sh; + + for (i = 0, sh = 7; i < 8; i++, sh --) { + if (mask & (1 << sh)) + env->crf[i] = (value >> (sh * 4)) & 0xFUL; + } +} + +uint32_t do_load_xer (CPUPPCState *env) { return (xer_so << XER_SO) | (xer_ov << XER_OV) | (xer_ca << XER_CA) | - (xer_bc << XER_BC); + (xer_bc << XER_BC) | + (xer_cmp << XER_CMP); } -void _store_xer (CPUState *env, uint32_t value) +void do_store_xer (CPUPPCState *env, uint32_t value) { xer_so = (value >> XER_SO) & 0x01; xer_ov = (value >> XER_OV) & 0x01; xer_ca = (value >> XER_CA) & 0x01; - xer_bc = (value >> XER_BC) & 0x1f; + xer_cmp = (value >> XER_CMP) & 0xFF; + xer_bc = (value >> XER_BC) & 0x3F; } -uint32_t _load_msr (CPUState *env) +target_ulong do_load_msr (CPUPPCState *env) { - return (msr_pow << MSR_POW) | + return (msr_vr << MSR_VR) | + (msr_ap << MSR_AP) | + (msr_sa << MSR_SA) | + (msr_key << MSR_KEY) | + (msr_pow << MSR_POW) | + (msr_tlb << MSR_TLB) | (msr_ile << MSR_ILE) | (msr_ee << MSR_EE) | (msr_pr << MSR_PR) | @@ -619,162 +766,257 @@ uint32_t _load_msr (CPUState *env) (msr_se << MSR_SE) | (msr_be << MSR_BE) | (msr_fe1 << MSR_FE1) | + (msr_al << MSR_AL) | (msr_ip << MSR_IP) | (msr_ir << MSR_IR) | (msr_dr << MSR_DR) | + (msr_pe << MSR_PE) | + (msr_px << MSR_PX) | (msr_ri << MSR_RI) | (msr_le << MSR_LE); } -void _store_msr (CPUState *env, uint32_t value) +void do_compute_hflags (CPUPPCState *env) { -#if 0 // TRY - if (((value >> MSR_IR) & 0x01) != msr_ir || - ((value >> MSR_DR) & 0x01) != msr_dr) + /* Compute current hflags */ + env->hflags = (msr_pr << MSR_PR) | (msr_le << MSR_LE) | + (msr_fp << MSR_FP) | (msr_fe0 << MSR_FE0) | (msr_fe1 << MSR_FE1) | + (msr_vr << MSR_VR) | (msr_ap << MSR_AP) | (msr_sa << MSR_SA) | + (msr_se << MSR_SE) | (msr_be << MSR_BE); +} + +void do_store_msr (CPUPPCState *env, target_ulong value) { - /* Flush all tlb when changing translation mode or privilege level */ - tlb_flush(env, 1); - } -#endif - msr_pow = (value >> MSR_POW) & 0x03; - msr_ile = (value >> MSR_ILE) & 0x01; - msr_ee = (value >> MSR_EE) & 0x01; - msr_pr = (value >> MSR_PR) & 0x01; - msr_fp = (value >> MSR_FP) & 0x01; - msr_me = (value >> MSR_ME) & 0x01; - msr_fe0 = (value >> MSR_FE0) & 0x01; - msr_se = (value >> MSR_SE) & 0x01; - msr_be = (value >> MSR_BE) & 0x01; - msr_fe1 = (value >> MSR_FE1) & 0x01; - msr_ip = (value >> MSR_IP) & 0x01; - msr_ir = (value >> MSR_IR) & 0x01; - msr_dr = (value >> MSR_DR) & 0x01; - msr_ri = (value >> MSR_RI) & 0x01; - msr_le = (value >> MSR_LE) & 0x01; - /* XXX: should enter PM state if msr_pow has been set */ + value &= env->msr_mask; + if (((value >> MSR_IR) & 1) != msr_ir || + ((value >> MSR_DR) & 1) != msr_dr) { + /* Flush all tlb when changing translation mode + * When using software driven TLB, we may also need to reload + * all defined TLBs + */ + tlb_flush(env, 1); + env->interrupt_request |= CPU_INTERRUPT_EXITTB; + } +#if 0 + if (loglevel != 0) { + fprintf(logfile, "%s: T0 %08lx\n", __func__, value); + } +#endif + msr_vr = (value >> MSR_VR) & 1; + msr_ap = (value >> MSR_AP) & 1; + msr_sa = (value >> MSR_SA) & 1; + msr_key = (value >> MSR_KEY) & 1; + msr_pow = (value >> MSR_POW) & 1; + msr_tlb = (value >> MSR_TLB) & 1; + msr_ile = (value >> MSR_ILE) & 1; + msr_ee = (value >> MSR_EE) & 1; + msr_pr = (value >> MSR_PR) & 1; + msr_fp = (value >> MSR_FP) & 1; + msr_me = (value >> MSR_ME) & 1; + msr_fe0 = (value >> MSR_FE0) & 1; + msr_se = (value >> MSR_SE) & 1; + msr_be = (value >> MSR_BE) & 1; + msr_fe1 = (value >> MSR_FE1) & 1; + msr_al = (value >> MSR_AL) & 1; + msr_ip = (value >> MSR_IP) & 1; + msr_ir = (value >> MSR_IR) & 1; + msr_dr = (value >> MSR_DR) & 1; + msr_pe = (value >> MSR_PE) & 1; + msr_px = (value >> MSR_PX) & 1; + msr_ri = (value >> MSR_RI) & 1; + msr_le = (value >> MSR_LE) & 1; + do_compute_hflags(env); } +float64 do_load_fpscr (CPUPPCState *env) +{ + /* The 32 MSB of the target fpr are undefined. + * They'll be zero... + */ + union { + float64 d; + struct { + uint32_t u[2]; + } s; + } u; + int i; + +#ifdef WORDS_BIGENDIAN +#define WORD0 0 +#define WORD1 1 +#else +#define WORD0 1 +#define WORD1 0 +#endif + u.s.u[WORD0] = 0; + u.s.u[WORD1] = 0; + for (i = 0; i < 8; i++) + u.s.u[WORD1] |= env->fpscr[i] << (4 * i); + return u.d; +} + +void do_store_fpscr (CPUPPCState *env, float64 f, uint32_t mask) +{ + /* + * We use only the 32 LSB of the incoming fpr + */ + union { + double d; + struct { + uint32_t u[2]; + } s; + } u; + int i, rnd_type; + + u.d = f; + if (mask & 0x80) + env->fpscr[0] = (env->fpscr[0] & 0x9) | ((u.s.u[WORD1] >> 28) & ~0x9); + for (i = 1; i < 7; i++) { + if (mask & (1 << (7 - i))) + env->fpscr[i] = (u.s.u[WORD1] >> (4 * (7 - i))) & 0xF; + } + /* TODO: update FEX & VX */ + /* Set rounding mode */ + switch (env->fpscr[0] & 0x3) { + case 0: + /* Best approximation (round to nearest) */ + rnd_type = float_round_nearest_even; + break; + case 1: + /* Smaller magnitude (round toward zero) */ + rnd_type = float_round_to_zero; + break; + case 2: + /* Round toward +infinite */ + rnd_type = float_round_up; + break; + default: + case 3: + /* Round toward -infinite */ + rnd_type = float_round_down; + break; + } + set_float_rounding_mode(rnd_type, &env->fp_status); +} + +/*****************************************************************************/ +/* Exception processing */ #if defined (CONFIG_USER_ONLY) void do_interrupt (CPUState *env) { env->exception_index = -1; } #else +static void dump_syscall(CPUState *env) +{ + fprintf(logfile, "syscall r0=0x%08x r3=0x%08x r4=0x%08x r5=0x%08x r6=0x%08x nip=0x%08x\n", + env->gpr[0], env->gpr[3], env->gpr[4], + env->gpr[5], env->gpr[6], env->nip); +} + void do_interrupt (CPUState *env) { - uint32_t msr; + target_ulong msr, *srr_0, *srr_1, tmp; int excp; excp = env->exception_index; - msr = _load_msr(env); + msr = do_load_msr(env); + /* The default is to use SRR0 & SRR1 to save the exception context */ + srr_0 = &env->spr[SPR_SRR0]; + srr_1 = &env->spr[SPR_SRR1]; #if defined (DEBUG_EXCEPTIONS) - if ((excp == EXCP_PROGRAM || excp == EXCP_DSI) && msr_pr == 1) - { - if (loglevel > 0) { - fprintf(logfile, "Raise exception at 0x%08x => 0x%08x (%02x)\n", - env->nip, excp << 8, env->error_code); + if ((excp == EXCP_PROGRAM || excp == EXCP_DSI) && msr_pr == 1) { + if (loglevel != 0) { + fprintf(logfile, "Raise exception at 0x%08lx => 0x%08x (%02x)\n", + (unsigned long)env->nip, excp, env->error_code); + cpu_dump_state(env, logfile, fprintf, 0); } - if (loglevel > 0) - cpu_dump_state(env, logfile, fprintf, 0); } #endif if (loglevel & CPU_LOG_INT) { - fprintf(logfile, "Raise exception at 0x%08x => 0x%08x (%02x)\n", - env->nip, excp << 8, env->error_code); + fprintf(logfile, "Raise exception at 0x%08lx => 0x%08x (%02x)\n", + (unsigned long)env->nip, excp, env->error_code); } + msr_pow = 0; /* Generate informations in save/restore registers */ switch (excp) { - case EXCP_NONE: - /* Do nothing */ -#if defined (DEBUG_EXCEPTIONS) - printf("%s: escape EXCP_NONE\n", __func__); -#endif - return; - case EXCP_RESET: - if (msr_ip) - excp += 0xFFC00; + /* Generic PowerPC exceptions */ + case EXCP_RESET: /* 0x0100 */ + if (PPC_EXCP(env) != PPC_FLAGS_EXCP_40x) { + if (msr_ip) + excp += 0xFFC00; + excp |= 0xFFC00000; + } else { + srr_0 = &env->spr[SPR_40x_SRR2]; + srr_1 = &env->spr[SPR_40x_SRR3]; + } goto store_next; - case EXCP_MACHINE_CHECK: + case EXCP_MACHINE_CHECK: /* 0x0200 */ if (msr_me == 0) { cpu_abort(env, "Machine check exception while not allowed\n"); } + if (PPC_EXCP(env) == PPC_FLAGS_EXCP_40x) { + srr_0 = &env->spr[SPR_40x_SRR2]; + srr_1 = &env->spr[SPR_40x_SRR3]; + } msr_me = 0; break; - case EXCP_DSI: + case EXCP_DSI: /* 0x0300 */ /* Store exception cause */ /* data location address has been stored * when the fault has been detected - */ + */ msr &= ~0xFFFF0000; - env->spr[DSISR] = 0; - if (env->error_code & EXCP_DSI_TRANSLATE) - env->spr[DSISR] |= 0x40000000; - else if (env->error_code & EXCP_DSI_PROT) - env->spr[DSISR] |= 0x08000000; - else if (env->error_code & EXCP_DSI_NOTSUP) { - env->spr[DSISR] |= 0x80000000; - if (env->error_code & EXCP_DSI_DIRECT) - env->spr[DSISR] |= 0x04000000; - } - if (env->error_code & EXCP_DSI_STORE) - env->spr[DSISR] |= 0x02000000; - if ((env->error_code & 0xF) == EXCP_DSI_DABR) - env->spr[DSISR] |= 0x00400000; - if (env->error_code & EXCP_DSI_ECXW) - env->spr[DSISR] |= 0x00100000; #if defined (DEBUG_EXCEPTIONS) if (loglevel) { fprintf(logfile, "DSI exception: DSISR=0x%08x, DAR=0x%08x\n", - env->spr[DSISR], env->spr[DAR]); + env->spr[SPR_DSISR], env->spr[SPR_DAR]); } else { - printf("DSI exception: DSISR=0x%08x, DAR=0x%08x nip=0x%08x\n", - env->spr[DSISR], env->spr[DAR], env->nip); + printf("DSI exception: DSISR=0x%08x, DAR=0x%08x\n", + env->spr[SPR_DSISR], env->spr[SPR_DAR]); } #endif goto store_next; - case EXCP_ISI: + case EXCP_ISI: /* 0x0400 */ /* Store exception cause */ msr &= ~0xFFFF0000; - if (env->error_code == EXCP_ISI_TRANSLATE) - msr |= 0x40000000; - else if (env->error_code == EXCP_ISI_NOEXEC || - env->error_code == EXCP_ISI_GUARD || - env->error_code == EXCP_ISI_DIRECT) - msr |= 0x10000000; - else - msr |= 0x08000000; + msr |= env->error_code; #if defined (DEBUG_EXCEPTIONS) - if (loglevel) { + if (loglevel != 0) { fprintf(logfile, "ISI exception: msr=0x%08x, nip=0x%08x\n", msr, env->nip); - } else { - printf("ISI exception: msr=0x%08x, nip=0x%08x tbl:0x%08x\n", - msr, env->nip, env->spr[V_TBL]); } #endif goto store_next; - case EXCP_EXTERNAL: + case EXCP_EXTERNAL: /* 0x0500 */ if (msr_ee == 0) { #if defined (DEBUG_EXCEPTIONS) if (loglevel > 0) { fprintf(logfile, "Skipping hardware interrupt\n"); - } + } #endif /* Requeue it */ - do_raise_exception(EXCP_EXTERNAL); + env->interrupt_request |= CPU_INTERRUPT_HARD; return; - } + } goto store_next; - case EXCP_ALIGN: - /* Store exception cause */ - /* Get rS/rD and rA from faulting opcode */ - env->spr[DSISR] |= - (ldl_code((env->nip - 4)) & 0x03FF0000) >> 16; - /* data location address has been stored - * when the fault has been detected - */ + case EXCP_ALIGN: /* 0x0600 */ + if (PPC_EXCP(env) != PPC_FLAGS_EXCP_601) { + /* Store exception cause */ + /* Get rS/rD and rA from faulting opcode */ + env->spr[SPR_DSISR] |= + (ldl_code((env->nip - 4)) & 0x03FF0000) >> 16; + /* data location address has been stored + * when the fault has been detected + */ + } else { + /* IO error exception on PowerPC 601 */ + /* XXX: TODO */ + cpu_abort(env, + "601 IO error exception is not implemented yet !\n"); + } goto store_current; - case EXCP_PROGRAM: + case EXCP_PROGRAM: /* 0x0700 */ msr &= ~0xFFFF0000; switch (env->error_code & ~0xF) { case EXCP_FP: @@ -808,70 +1050,356 @@ void do_interrupt (CPUState *env) } msr |= 0x00010000; goto store_current; - case EXCP_NO_FP: + case EXCP_NO_FP: /* 0x0800 */ msr &= ~0xFFFF0000; goto store_current; case EXCP_DECR: if (msr_ee == 0) { +#if 1 /* Requeue it */ - do_raise_exception(EXCP_DECR); + env->interrupt_request |= CPU_INTERRUPT_TIMER; +#endif return; } goto store_next; - case EXCP_SYSCALL: + case EXCP_SYSCALL: /* 0x0C00 */ + /* NOTE: this is a temporary hack to support graphics OSI + calls from the MOL driver */ + if (env->gpr[3] == 0x113724fa && env->gpr[4] == 0x77810f9b && + env->osi_call) { + if (env->osi_call(env) != 0) + return; + } if (loglevel & CPU_LOG_INT) { - fprintf(logfile, "syscall %d 0x%08x 0x%08x 0x%08x 0x%08x\n", - env->gpr[0], env->gpr[3], env->gpr[4], - env->gpr[5], env->gpr[6]); - if (env->gpr[0] == 4 && env->gpr[3] == 1) { - int len, addr, i; - uint8_t c; - - fprintf(logfile, "write: "); - addr = env->gpr[4]; - len = env->gpr[5]; - if (len > 64) - len = 64; - for(i = 0; i < len; i++) { - c = 0; - cpu_memory_rw_debug(env, addr + i, &c, 1, 0); - if (c < 32 || c > 126) - c = '.'; - fprintf(logfile, "%c", c); - } - fprintf(logfile, "\n"); - } + dump_syscall(env); } goto store_next; - case EXCP_TRACE: + case EXCP_TRACE: /* 0x0D00 */ + /* XXX: TODO */ + cpu_abort(env, "Trace exception is not implemented yet !\n"); + goto store_next; + case EXCP_PERF: /* 0x0F00 */ + /* XXX: TODO */ + cpu_abort(env, + "Performance counter exception is not implemented yet !\n"); + goto store_next; + /* 32 bits PowerPC specific exceptions */ + case EXCP_FP_ASSIST: /* 0x0E00 */ + /* XXX: TODO */ + cpu_abort(env, "Floating point assist exception " + "is not implemented yet !\n"); goto store_next; - case EXCP_FP_ASSIST: + /* 64 bits PowerPC exceptions */ + case EXCP_DSEG: /* 0x0380 */ + /* XXX: TODO */ + cpu_abort(env, "Data segment exception is not implemented yet !\n"); + goto store_next; + case EXCP_ISEG: /* 0x0480 */ + /* XXX: TODO */ + cpu_abort(env, + "Instruction segment exception is not implemented yet !\n"); + goto store_next; + case EXCP_HDECR: /* 0x0980 */ + if (msr_ee == 0) { +#if 1 + /* Requeue it */ + env->interrupt_request |= CPU_INTERRUPT_TIMER; +#endif + return; + } + cpu_abort(env, + "Hypervisor decrementer exception is not implemented yet !\n"); goto store_next; - case EXCP_MTMSR: - /* Nothing to do */ + /* Implementation specific exceptions */ + case 0x0A00: + if (PPC_EXCP(env) != PPC_FLAGS_EXCP_602) { + /* Critical interrupt on G2 */ + /* XXX: TODO */ + cpu_abort(env, "G2 critical interrupt is not implemented yet !\n"); + goto store_next; + } else { + cpu_abort(env, "Invalid exception 0x0A00 !\n"); + } return; - case EXCP_BRANCH: - /* Nothing to do */ + case 0x0F20: + switch (PPC_EXCP(env)) { + case PPC_FLAGS_EXCP_40x: + /* APU unavailable on 405 */ + /* XXX: TODO */ + cpu_abort(env, + "APU unavailable exception is not implemented yet !\n"); + goto store_next; + case PPC_FLAGS_EXCP_74xx: + /* Altivec unavailable */ + /* XXX: TODO */ + cpu_abort(env, "Altivec unavailable exception " + "is not implemented yet !\n"); + goto store_next; + default: + cpu_abort(env, "Invalid exception 0x0F20 !\n"); + break; + } return; - case EXCP_RFI: - /* Restore user-mode state */ -#if defined (DEBUG_EXCEPTIONS) - if (msr_pr == 1) - printf("Return from exception => 0x%08x\n", (uint32_t)env->nip); + case 0x1000: + switch (PPC_EXCP(env)) { + case PPC_FLAGS_EXCP_40x: + /* PIT on 4xx */ + /* XXX: TODO */ + cpu_abort(env, "40x PIT exception is not implemented yet !\n"); + goto store_next; + case PPC_FLAGS_EXCP_602: + case PPC_FLAGS_EXCP_603: + /* ITLBMISS on 602/603 */ + msr &= ~0xF00F0000; + msr_tgpr = 1; + goto store_gprs; + default: + cpu_abort(env, "Invalid exception 0x1000 !\n"); + break; + } + return; + case 0x1010: + switch (PPC_EXCP(env)) { + case PPC_FLAGS_EXCP_40x: + /* FIT on 4xx */ + cpu_abort(env, "40x FIT exception is not implemented yet !\n"); + /* XXX: TODO */ + goto store_next; + default: + cpu_abort(env, "Invalid exception 0x1010 !\n"); + break; + } + return; + case 0x1020: + switch (PPC_EXCP(env)) { + case PPC_FLAGS_EXCP_40x: + /* Watchdog on 4xx */ + /* XXX: TODO */ + cpu_abort(env, + "40x watchdog exception is not implemented yet !\n"); + goto store_next; + default: + cpu_abort(env, "Invalid exception 0x1020 !\n"); + break; + } + return; + case 0x1100: + switch (PPC_EXCP(env)) { + case PPC_FLAGS_EXCP_40x: + /* DTLBMISS on 4xx */ + /* XXX: TODO */ + cpu_abort(env, + "40x DTLBMISS exception is not implemented yet !\n"); + goto store_next; + case PPC_FLAGS_EXCP_602: + case PPC_FLAGS_EXCP_603: + /* DLTLBMISS on 602/603 */ + msr &= ~0xF00F0000; + msr_tgpr = 1; + goto store_gprs; + default: + cpu_abort(env, "Invalid exception 0x1100 !\n"); + break; + } + return; + case 0x1200: + switch (PPC_EXCP(env)) { + case PPC_FLAGS_EXCP_40x: + /* ITLBMISS on 4xx */ + /* XXX: TODO */ + cpu_abort(env, + "40x ITLBMISS exception is not implemented yet !\n"); + goto store_next; + case PPC_FLAGS_EXCP_602: + case PPC_FLAGS_EXCP_603: + /* DSTLBMISS on 602/603 */ + msr &= ~0xF00F0000; + msr_tgpr = 1; + store_gprs: +#if defined (DEBUG_SOFTWARE_TLB) + if (loglevel != 0) { + fprintf(logfile, "6xx %sTLB miss: IM %08x DM %08x IC %08x " + "DC %08x H1 %08x H2 %08x %08x\n", + excp == 0x1000 ? "I" : excp == 0x1100 ? "DL" : "DS", + env->spr[SPR_IMISS], env->spr[SPR_DMISS], + env->spr[SPR_ICMP], env->spr[SPR_DCMP], + env->spr[SPR_DHASH1], env->spr[SPR_DHASH2], + env->error_code); + } #endif + /* Swap temporary saved registers with GPRs */ + tmp = env->gpr[0]; + env->gpr[0] = env->tgpr[0]; + env->tgpr[0] = tmp; + tmp = env->gpr[1]; + env->gpr[1] = env->tgpr[1]; + env->tgpr[1] = tmp; + tmp = env->gpr[2]; + env->gpr[2] = env->tgpr[2]; + env->tgpr[2] = tmp; + tmp = env->gpr[3]; + env->gpr[3] = env->tgpr[3]; + env->tgpr[3] = tmp; + msr |= env->crf[0] << 28; + msr |= env->error_code; /* key, D/I, S/L bits */ + /* Set way using a LRU mechanism */ + msr |= (env->last_way ^ 1) << 17; + goto store_next; + default: + cpu_abort(env, "Invalid exception 0x1200 !\n"); + break; + } + return; + case 0x1300: + switch (PPC_EXCP(env)) { + case PPC_FLAGS_EXCP_601: + case PPC_FLAGS_EXCP_602: + case PPC_FLAGS_EXCP_603: + case PPC_FLAGS_EXCP_604: + case PPC_FLAGS_EXCP_7x0: + case PPC_FLAGS_EXCP_7x5: + /* IABR on 6xx/7xx */ + /* XXX: TODO */ + cpu_abort(env, "IABR exception is not implemented yet !\n"); + goto store_next; + default: + cpu_abort(env, "Invalid exception 0x1300 !\n"); + break; + } + return; + case 0x1400: + switch (PPC_EXCP(env)) { + case PPC_FLAGS_EXCP_601: + case PPC_FLAGS_EXCP_602: + case PPC_FLAGS_EXCP_603: + case PPC_FLAGS_EXCP_604: + case PPC_FLAGS_EXCP_7x0: + case PPC_FLAGS_EXCP_7x5: + /* SMI on 6xx/7xx */ + /* XXX: TODO */ + cpu_abort(env, "SMI exception is not implemented yet !\n"); + goto store_next; + default: + cpu_abort(env, "Invalid exception 0x1400 !\n"); + break; + } + return; + case 0x1500: + switch (PPC_EXCP(env)) { + case PPC_FLAGS_EXCP_602: + /* Watchdog on 602 */ + cpu_abort(env, + "602 watchdog exception is not implemented yet !\n"); + goto store_next; + case PPC_FLAGS_EXCP_970: + /* Soft patch exception on 970 */ + /* XXX: TODO */ + cpu_abort(env, + "970 soft-patch exception is not implemented yet !\n"); + goto store_next; + case PPC_FLAGS_EXCP_74xx: + /* VPU assist on 74xx */ + /* XXX: TODO */ + cpu_abort(env, "VPU assist exception is not implemented yet !\n"); + goto store_next; + default: + cpu_abort(env, "Invalid exception 0x1500 !\n"); + break; + } + return; + case 0x1600: + switch (PPC_EXCP(env)) { + case PPC_FLAGS_EXCP_602: + /* Emulation trap on 602 */ + /* XXX: TODO */ + cpu_abort(env, "602 emulation trap exception " + "is not implemented yet !\n"); + goto store_next; + case PPC_FLAGS_EXCP_970: + /* Maintenance exception on 970 */ + /* XXX: TODO */ + cpu_abort(env, + "970 maintenance exception is not implemented yet !\n"); + goto store_next; + default: + cpu_abort(env, "Invalid exception 0x1600 !\n"); + break; + } + return; + case 0x1700: + switch (PPC_EXCP(env)) { + case PPC_FLAGS_EXCP_7x0: + case PPC_FLAGS_EXCP_7x5: + /* Thermal management interrupt on G3 */ + /* XXX: TODO */ + cpu_abort(env, "G3 thermal management exception " + "is not implemented yet !\n"); + goto store_next; + case PPC_FLAGS_EXCP_970: + /* VPU assist on 970 */ + /* XXX: TODO */ + cpu_abort(env, + "970 VPU assist exception is not implemented yet !\n"); + goto store_next; + default: + cpu_abort(env, "Invalid exception 0x1700 !\n"); + break; + } + return; + case 0x1800: + switch (PPC_EXCP(env)) { + case PPC_FLAGS_EXCP_970: + /* Thermal exception on 970 */ + /* XXX: TODO */ + cpu_abort(env, "970 thermal management exception " + "is not implemented yet !\n"); + goto store_next; + default: + cpu_abort(env, "Invalid exception 0x1800 !\n"); + break; + } + return; + case 0x2000: + switch (PPC_EXCP(env)) { + case PPC_FLAGS_EXCP_40x: + /* DEBUG on 4xx */ + /* XXX: TODO */ + cpu_abort(env, "40x debug exception is not implemented yet !\n"); + goto store_next; + case PPC_FLAGS_EXCP_601: + /* Run mode exception on 601 */ + /* XXX: TODO */ + cpu_abort(env, + "601 run mode exception is not implemented yet !\n"); + goto store_next; + default: + cpu_abort(env, "Invalid exception 0x1800 !\n"); + break; + } + return; + /* Other exceptions */ + /* Qemu internal exceptions: + * we should never come here with those values: abort execution + */ + default: + cpu_abort(env, "Invalid exception: code %d (%04x)\n", excp, excp); return; store_current: - /* SRR0 is set to current instruction */ - env->spr[SRR0] = (uint32_t)env->nip - 4; + /* save current instruction location */ + *srr_0 = (env->nip - 4) & 0xFFFFFFFFULL; break; store_next: - /* SRR0 is set to next instruction */ - env->spr[SRR0] = (uint32_t)env->nip; + /* save next instruction location */ + *srr_0 = env->nip & 0xFFFFFFFFULL; break; } - env->spr[SRR1] = msr; + /* Save msr */ + *srr_1 = msr; + /* If we disactivated any translation, flush TLBs */ + if (msr_ir || msr_dr) { + tlb_flush(env, 1); + } /* reload MSR with correct bits */ - msr_pow = 0; msr_ee = 0; msr_pr = 0; msr_fp = 0; @@ -883,10 +1411,12 @@ void do_interrupt (CPUState *env) msr_dr = 0; msr_ri = 0; msr_le = msr_ile; + msr_sf = msr_isf; + do_compute_hflags(env); /* Jump to handler */ - env->nip = excp << 8; + env->nip = excp; env->exception_index = EXCP_NONE; - /* Invalidate all TLB as we may have changed translation mode */ +#if 0 /* ensure that no TB jump will be modified as the program flow was changed */ #ifdef __sparc__ @@ -894,6 +1424,7 @@ void do_interrupt (CPUState *env) #else T0 = 0; #endif - env->exception_index = -1; +#endif + env->interrupt_request |= CPU_INTERRUPT_EXITTB; } #endif /* !CONFIG_USER_ONLY */ diff --git a/qemu/target-ppc/op.c b/qemu/target-ppc/op.c index 6ae7a52..0e37119 100644 --- a/qemu/target-ppc/op.c +++ b/qemu/target-ppc/op.c @@ -1,7 +1,7 @@ /* - * PPC emulation micro-operations for qemu. + * PowerPC emulation micro-operations for qemu. * - * Copyright (c) 2003 Jocelyn Mayer + * Copyright (c) 2003-2005 Jocelyn Mayer * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -130,7 +130,7 @@ #define REG 31 #include "op_template.h" -/* PPC state maintenance operations */ +/* PowerPC state maintenance operations */ /* set_Rc0 */ PPC_OP(set_Rc0) { @@ -204,16 +204,6 @@ PPC_OP(update_nip) env->nip = PARAM(1); } -PPC_OP(debug) -{ - env->nip = PARAM(1); -#if defined (DEBUG_OP) - dump_state(); -#endif - do_raise_exception(EXCP_DEBUG); - RETURN(); -} - /* Segment registers load and store with immediate index */ PPC_OP(load_srin) { @@ -223,7 +213,7 @@ PPC_OP(load_srin) PPC_OP(store_srin) { - do_store_sr(T1 >> 28); + do_store_sr(env, ((uint32_t)T1 >> 28), T0); RETURN(); } @@ -235,7 +225,7 @@ PPC_OP(load_sdr1) PPC_OP(store_sdr1) { - regs->sdr1 = T0; + do_store_sdr1(env, T0); RETURN(); } @@ -247,13 +237,13 @@ PPC_OP(exit_tb) /* Load/store special registers */ PPC_OP(load_cr) { - do_load_cr(); + T0 = do_load_cr(env); RETURN(); } PPC_OP(store_cr) { - do_store_cr(PARAM(1)); + do_store_cr(env, T0, PARAM(1)); RETURN(); } @@ -279,25 +269,25 @@ PPC_OP(load_xer_bc) PPC_OP(load_xer) { - do_load_xer(); + T0 = do_load_xer(env); RETURN(); } PPC_OP(store_xer) { - do_store_xer(); + do_store_xer(env, T0); RETURN(); } PPC_OP(load_msr) { - do_load_msr(); + T0 = do_load_msr(env); RETURN(); } PPC_OP(store_msr) { - do_store_msr(); + do_store_msr(env, T0); RETURN(); } @@ -378,9 +368,20 @@ PPC_OP(load_ibat) T0 = regs->IBAT[PARAM(1)][PARAM(2)]; } -PPC_OP(store_ibat) +void op_store_ibatu (void) { - do_store_ibat(PARAM(1), PARAM(2)); + do_store_ibatu(env, PARAM1, T0); + RETURN(); +} + +void op_store_ibatl (void) +{ +#if 1 + env->IBAT[1][PARAM1] = T0; +#else + do_store_ibatl(env, PARAM1, T0); +#endif + RETURN(); } PPC_OP(load_dbat) @@ -388,21 +389,32 @@ PPC_OP(load_dbat) T0 = regs->DBAT[PARAM(1)][PARAM(2)]; } -PPC_OP(store_dbat) +void op_store_dbatu (void) +{ + do_store_dbatu(env, PARAM1, T0); + RETURN(); +} + +void op_store_dbatl (void) { - do_store_dbat(PARAM(1), PARAM(2)); +#if 1 + env->DBAT[1][PARAM1] = T0; +#else + do_store_dbatl(env, PARAM1, T0); +#endif + RETURN(); } /* FPSCR */ PPC_OP(load_fpscr) { - do_load_fpscr(); + FT0 = do_load_fpscr(env); RETURN(); } PPC_OP(store_fpscr) { - do_store_fpscr(PARAM(1)); + do_store_fpscr(env, FT0, PARAM1); RETURN(); } @@ -446,7 +458,7 @@ PPC_OP(b) PPC_OP(b_T1) { - regs->nip = T1; + regs->nip = T1 & ~3; } PPC_OP(btest) @@ -536,16 +548,10 @@ PPC_OP(add) RETURN(); } -PPC_OP(addo) +void do_addo (void); +void op_addo (void) { - T2 = T0; - T0 += T1; - if ((T2 ^ T1 ^ (-1)) & (T2 ^ T0) & (1 << 31)) { - xer_so = 1; - xer_ov = 1; - } else { - xer_ov = 0; - } + do_addo(); RETURN(); } @@ -562,53 +568,24 @@ PPC_OP(addc) RETURN(); } -PPC_OP(addco) +void do_addco (void); +void op_addco (void) { - T2 = T0; - T0 += T1; - if (T0 < T2) { - xer_ca = 1; - } else { - xer_ca = 0; - } - if ((T2 ^ T1 ^ (-1)) & (T2 ^ T0) & (1 << 31)) { - xer_so = 1; - xer_ov = 1; - } else { - xer_ov = 0; - } + do_addco(); RETURN(); } /* add extended */ -/* candidate for helper (too long) */ -PPC_OP(adde) +void do_adde (void); +void op_adde (void) { - T2 = T0; - T0 += T1 + xer_ca; - if (T0 < T2 || (xer_ca == 1 && T0 == T2)) { - xer_ca = 1; - } else { - xer_ca = 0; - } - RETURN(); + do_adde(); } +void do_addeo (void); PPC_OP(addeo) { - T2 = T0; - T0 += T1 + xer_ca; - if (T0 < T2 || (xer_ca == 1 && T0 == T2)) { - xer_ca = 1; - } else { - xer_ca = 0; - } - if ((T2 ^ T1 ^ (-1)) & (T2 ^ T0) & (1 << 31)) { - xer_so = 1; - xer_ov = 1; - } else { - xer_ov = 0; - } + do_addeo(); RETURN(); } @@ -642,18 +619,10 @@ PPC_OP(addme) RETURN(); } -PPC_OP(addmeo) +void do_addmeo (void); +void op_addmeo (void) { - T1 = T0; - T0 += xer_ca + (-1); - if (T1 & (T1 ^ T0) & (1 << 31)) { - xer_so = 1; - xer_ov = 1; - } else { - xer_ov = 0; - } - if (T1 != 0) - xer_ca = 1; + do_addmeo(); RETURN(); } @@ -670,26 +639,14 @@ PPC_OP(addze) RETURN(); } -PPC_OP(addzeo) +void do_addzeo (void); +void op_addzeo (void) { - T1 = T0; - T0 += xer_ca; - if ((T1 ^ (-1)) & (T1 ^ T0) & (1 << 31)) { - xer_so = 1; - xer_ov = 1; - } else { - xer_ov = 0; - } - if (T0 < T1) { - xer_ca = 1; - } else { - xer_ca = 0; - } + do_addzeo(); RETURN(); } /* divide word */ -/* candidate for helper (too long) */ PPC_OP(divw) { if ((Ts0 == INT32_MIN && Ts1 == -1) || Ts1 == 0) { @@ -700,16 +657,10 @@ PPC_OP(divw) RETURN(); } -PPC_OP(divwo) +void do_divwo (void); +void op_divwo (void) { - if ((Ts0 == INT32_MIN && Ts1 == -1) || Ts1 == 0) { - xer_so = 1; - xer_ov = 1; - T0 = (-1) * (T0 >> 31); - } else { - xer_ov = 0; - T0 = (Ts0 / Ts1); - } + do_divwo(); RETURN(); } @@ -724,16 +675,10 @@ PPC_OP(divwu) RETURN(); } -PPC_OP(divwuo) +void do_divwuo (void); +void op_divwuo (void) { - if (T1 == 0) { - xer_so = 1; - xer_ov = 1; - T0 = 0; - } else { - xer_ov = 0; - T0 /= T1; - } + do_divwuo(); RETURN(); } @@ -765,17 +710,10 @@ PPC_OP(mullw) RETURN(); } -PPC_OP(mullwo) +void do_mullwo (void); +void op_mullwo (void) { - int64_t res = (int64_t)Ts0 * (int64_t)Ts1; - - if ((int32_t)res != res) { - xer_ov = 1; - xer_so = 1; - } else { - xer_ov = 0; - } - T0 = (int32_t)res; + do_mullwo(); RETURN(); } @@ -788,15 +726,10 @@ PPC_OP(neg) RETURN(); } -PPC_OP(nego) +void do_nego (void); +void op_nego (void) { - if (T0 == 0x80000000) { - xer_ov = 1; - xer_so = 1; - } else { - xer_ov = 0; - T0 = -Ts0; - } + do_nego(); RETURN(); } @@ -807,16 +740,10 @@ PPC_OP(subf) RETURN(); } -PPC_OP(subfo) +void do_subfo (void); +void op_subfo (void) { - T2 = T0; - T0 = T1 - T0; - if (((~T2) ^ T1 ^ (-1)) & ((~T2) ^ T0) & (1 << 31)) { - xer_so = 1; - xer_ov = 1; - } else { - xer_ov = 0; - } + do_subfo(); RETURN(); } @@ -832,52 +759,25 @@ PPC_OP(subfc) RETURN(); } -PPC_OP(subfco) +void do_subfco (void); +void op_subfco (void) { - T2 = T0; - T0 = T1 - T0; - if (T0 <= T1) { - xer_ca = 1; - } else { - xer_ca = 0; - } - if (((~T2) ^ T1 ^ (-1)) & ((~T2) ^ T0) & (1 << 31)) { - xer_so = 1; - xer_ov = 1; - } else { - xer_ov = 0; - } + do_subfco(); RETURN(); } /* substract from extended */ -/* candidate for helper (too long) */ -PPC_OP(subfe) +void do_subfe (void); +void op_subfe (void) { - T0 = T1 + ~T0 + xer_ca; - if (T0 < T1 || (xer_ca == 1 && T0 == T1)) { - xer_ca = 1; - } else { - xer_ca = 0; - } + do_subfe(); RETURN(); } +void do_subfeo (void); PPC_OP(subfeo) { - T2 = T0; - T0 = T1 + ~T0 + xer_ca; - if ((~T2 ^ T1 ^ (-1)) & (~T2 ^ T0) & (1 << 31)) { - xer_so = 1; - xer_ov = 1; - } else { - xer_ov = 0; - } - if (T0 < T1 || (xer_ca == 1 && T0 == T1)) { - xer_ca = 1; - } else { - xer_ca = 0; - } + do_subfeo(); RETURN(); } @@ -903,18 +803,10 @@ PPC_OP(subfme) RETURN(); } -PPC_OP(subfmeo) +void do_subfmeo (void); +void op_subfmeo (void) { - T1 = T0; - T0 = ~T0 + xer_ca - 1; - if (~T1 & (~T1 ^ T0) & (1 << 31)) { - xer_so = 1; - xer_ov = 1; - } else { - xer_ov = 0; - } - if (T1 != -1) - xer_ca = 1; + do_subfmeo(); RETURN(); } @@ -931,21 +823,10 @@ PPC_OP(subfze) RETURN(); } -PPC_OP(subfzeo) +void do_subfzeo (void); +void op_subfzeo (void) { - T1 = T0; - T0 = ~T0 + xer_ca; - if ((~T1 ^ (-1)) & ((~T1) ^ T0) & (1 << 31)) { - xer_ov = 1; - xer_so = 1; - } else { - xer_ov = 0; - } - if (T0 < ~T1) { - xer_ca = 1; - } else { - xer_ca = 0; - } + do_subfzeo(); RETURN(); } @@ -1162,7 +1043,7 @@ PPC_OP(slw) } /* shift right algebraic word */ -PPC_OP(sraw) +void op_sraw (void) { do_sraw(); RETURN(); @@ -1215,10 +1096,9 @@ PPC_OP(fmul) } /* fdiv - fdiv. */ -void do_fdiv (void); PPC_OP(fdiv) { - do_fdiv(); + FT0 = float64_div(FT0, FT1, &env->fp_status); RETURN(); } @@ -1319,25 +1199,24 @@ PPC_OP(fcmpo) /*** Floating-point move ***/ /* fabs */ -void do_fabs (void); PPC_OP(fabs) { - do_fabs(); + FT0 = float64_abs(FT0); RETURN(); } /* fnabs */ -void do_fnabs (void); PPC_OP(fnabs) { - do_fnabs(); + FT0 = float64_abs(FT0); + FT0 = float64_chs(FT0); RETURN(); } /* fneg */ PPC_OP(fneg) { - FT0 = -FT0; + FT0 = float64_chs(FT0); RETURN(); } @@ -1355,48 +1234,30 @@ PPC_OP(fneg) /* Special op to check and maybe clear reservation */ PPC_OP(check_reservation) { - do_check_reservation(); + if ((uint32_t)env->reserve == (uint32_t)(T0 & ~0x00000003)) + env->reserve = -1; RETURN(); } /* Return from interrupt */ -PPC_OP(rfi) +void do_rfi (void); +void op_rfi (void) { - regs->nip = regs->spr[SRR0] & ~0x00000003; -#if 1 // TRY - T0 = regs->spr[SRR1] & ~0xFFF00000; -#else - T0 = regs->spr[SRR1] & ~0xFFFF0000; -#endif - do_store_msr(); -#if defined (DEBUG_OP) - dump_rfi(); -#endif - // do_tlbia(); - do_raise_exception(EXCP_RFI); + do_rfi(); RETURN(); } /* Trap word */ -PPC_OP(tw) +void do_tw (uint32_t cmp, int flags); +void op_tw (void) { - if ((Ts0 < Ts1 && (PARAM(1) & 0x10)) || - (Ts0 > Ts1 && (PARAM(1) & 0x08)) || - (Ts0 == Ts1 && (PARAM(1) & 0x04)) || - (T0 < T1 && (PARAM(1) & 0x02)) || - (T0 > T1 && (PARAM(1) & 0x01))) - do_raise_exception_err(EXCP_PROGRAM, EXCP_TRAP); + do_tw(T1, PARAM(1)); RETURN(); } -PPC_OP(twi) +void op_twi (void) { - if ((Ts0 < SPARAM(1) && (PARAM(2) & 0x10)) || - (Ts0 > SPARAM(1) && (PARAM(2) & 0x08)) || - (Ts0 == SPARAM(1) && (PARAM(2) & 0x04)) || - (T0 < (uint32_t)SPARAM(1) && (PARAM(2) & 0x02)) || - (T0 > (uint32_t)SPARAM(1) && (PARAM(2) & 0x01))) - do_raise_exception_err(EXCP_PROGRAM, EXCP_TRAP); + do_tw(PARAM(1), PARAM(2)); RETURN(); } @@ -1420,3 +1281,9 @@ PPC_OP(tlbie) do_tlbie(); RETURN(); } + +void op_store_pir (void) +{ + env->spr[SPR_PIR] = T0 & 0x0000000FUL; + RETURN(); +} diff --git a/qemu/target-ppc/op_helper.c b/qemu/target-ppc/op_helper.c index 102249d..06fa6d4 100644 --- a/qemu/target-ppc/op_helper.c +++ b/qemu/target-ppc/op_helper.c @@ -1,7 +1,7 @@ /* - * PPC emulation helpers for qemu. + * PowerPC emulation helpers for qemu. * - * Copyright (c) 2003 Jocelyn Mayer + * Copyright (c) 2003-2005 Jocelyn Mayer * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -29,6 +29,14 @@ #include "op_helper_mem.h" #endif +//#define DEBUG_OP +//#define DEBUG_EXCEPTIONS +//#define FLUSH_ALL_TLBS + +#define Ts0 (long)((target_long)T0) +#define Ts1 (long)((target_long)T1) +#define Ts2 (long)((target_long)T2) + /*****************************************************************************/ /* Exceptions processing helpers */ void cpu_loop_exit(void) @@ -42,12 +50,6 @@ void do_raise_exception_err (uint32_t exception, int error_code) printf("Raise exception %3x code : %d\n", exception, error_code); #endif switch (exception) { - case EXCP_EXTERNAL: - case EXCP_DECR: - printf("DECREMENTER & EXTERNAL exceptions should be hard interrupts !\n"); - if (msr_ee == 0) - return; - break; case EXCP_PROGRAM: if (error_code == EXCP_FP && msr_fe0 == 0 && msr_fe1 == 0) return; @@ -66,90 +68,229 @@ void do_raise_exception (uint32_t exception) } /*****************************************************************************/ -/* Helpers for "fat" micro operations */ -/* Special registers load and store */ -void do_load_cr (void) +/* Fixed point operations helpers */ +void do_addo (void) { - T0 = (env->crf[0] << 28) | - (env->crf[1] << 24) | - (env->crf[2] << 20) | - (env->crf[3] << 16) | - (env->crf[4] << 12) | - (env->crf[5] << 8) | - (env->crf[6] << 4) | - (env->crf[7] << 0); + T2 = T0; + T0 += T1; + if (likely(!((T2 ^ T1 ^ (-1)) & (T2 ^ T0) & (1 << 31)))) { + xer_ov = 0; + } else { + xer_so = 1; + xer_ov = 1; + } } -void do_store_cr (uint32_t mask) +void do_addco (void) { - int i, sh; + T2 = T0; + T0 += T1; + if (likely(T0 >= T2)) { + xer_ca = 0; + } else { + xer_ca = 1; + } + if (likely(!((T2 ^ T1 ^ (-1)) & (T2 ^ T0) & (1 << 31)))) { + xer_ov = 0; + } else { + xer_so = 1; + xer_ov = 1; + } +} - for (i = 0, sh = 7; i < 8; i++, sh --) { - if (mask & (1 << sh)) - env->crf[i] = (T0 >> (sh * 4)) & 0xF; +void do_adde (void) +{ + T2 = T0; + T0 += T1 + xer_ca; + if (likely(!(T0 < T2 || (xer_ca == 1 && T0 == T2)))) { + xer_ca = 0; + } else { + xer_ca = 1; } } -void do_load_xer (void) +void do_addeo (void) { - T0 = (xer_so << XER_SO) | - (xer_ov << XER_OV) | - (xer_ca << XER_CA) | - (xer_bc << XER_BC); + T2 = T0; + T0 += T1 + xer_ca; + if (likely(!(T0 < T2 || (xer_ca == 1 && T0 == T2)))) { + xer_ca = 0; + } else { + xer_ca = 1; + } + if (likely(!((T2 ^ T1 ^ (-1)) & (T2 ^ T0) & (1 << 31)))) { + xer_ov = 0; + } else { + xer_so = 1; + xer_ov = 1; + } } -void do_store_xer (void) +void do_addmeo (void) { - xer_so = (T0 >> XER_SO) & 0x01; - xer_ov = (T0 >> XER_OV) & 0x01; - xer_ca = (T0 >> XER_CA) & 0x01; - xer_bc = (T0 >> XER_BC) & 0x1f; + T1 = T0; + T0 += xer_ca + (-1); + if (likely(!(T1 & (T1 ^ T0) & (1 << 31)))) { + xer_ov = 0; + } else { + xer_so = 1; + xer_ov = 1; + } + if (likely(T1 != 0)) + xer_ca = 1; } -void do_load_msr (void) +void do_addzeo (void) { - T0 = (msr_pow << MSR_POW) | - (msr_ile << MSR_ILE) | - (msr_ee << MSR_EE) | - (msr_pr << MSR_PR) | - (msr_fp << MSR_FP) | - (msr_me << MSR_ME) | - (msr_fe0 << MSR_FE0) | - (msr_se << MSR_SE) | - (msr_be << MSR_BE) | - (msr_fe1 << MSR_FE1) | - (msr_ip << MSR_IP) | - (msr_ir << MSR_IR) | - (msr_dr << MSR_DR) | - (msr_ri << MSR_RI) | - (msr_le << MSR_LE); + T1 = T0; + T0 += xer_ca; + if (likely(!((T1 ^ (-1)) & (T1 ^ T0) & (1 << 31)))) { + xer_ov = 0; + } else { + xer_so = 1; + xer_ov = 1; + } + if (likely(T0 >= T1)) { + xer_ca = 0; + } else { + xer_ca = 1; + } } -void do_store_msr (void) +void do_divwo (void) { -#if 1 // TRY - if (((T0 >> MSR_IR) & 0x01) != msr_ir || - ((T0 >> MSR_DR) & 0x01) != msr_dr || - ((T0 >> MSR_PR) & 0x01) != msr_pr) - { - do_tlbia(); + if (likely(!((Ts0 == INT32_MIN && Ts1 == -1) || Ts1 == 0))) { + xer_ov = 0; + T0 = (Ts0 / Ts1); + } else { + xer_so = 1; + xer_ov = 1; + T0 = (-1) * ((uint32_t)T0 >> 31); + } +} + +void do_divwuo (void) +{ + if (likely((uint32_t)T1 != 0)) { + xer_ov = 0; + T0 = (uint32_t)T0 / (uint32_t)T1; + } else { + xer_so = 1; + xer_ov = 1; + T0 = 0; + } +} + +void do_mullwo (void) +{ + int64_t res = (int64_t)Ts0 * (int64_t)Ts1; + + if (likely((int32_t)res == res)) { + xer_ov = 0; + } else { + xer_ov = 1; + xer_so = 1; + } + T0 = (int32_t)res; +} + +void do_nego (void) +{ + if (likely(T0 != INT32_MIN)) { + xer_ov = 0; + T0 = -Ts0; + } else { + xer_ov = 1; + xer_so = 1; + } +} + +void do_subfo (void) +{ + T2 = T0; + T0 = T1 - T0; + if (likely(!(((~T2) ^ T1 ^ (-1)) & ((~T2) ^ T0) & (1 << 31)))) { + xer_ov = 0; + } else { + xer_so = 1; + xer_ov = 1; + } + RETURN(); +} + +void do_subfco (void) +{ + T2 = T0; + T0 = T1 - T0; + if (likely(T0 > T1)) { + xer_ca = 0; + } else { + xer_ca = 1; + } + if (likely(!(((~T2) ^ T1 ^ (-1)) & ((~T2) ^ T0) & (1 << 31)))) { + xer_ov = 0; + } else { + xer_so = 1; + xer_ov = 1; + } +} + +void do_subfe (void) +{ + T0 = T1 + ~T0 + xer_ca; + if (likely(T0 >= T1 && (xer_ca == 0 || T0 != T1))) { + xer_ca = 0; + } else { + xer_ca = 1; + } +} + +void do_subfeo (void) +{ + T2 = T0; + T0 = T1 + ~T0 + xer_ca; + if (likely(!((~T2 ^ T1 ^ (-1)) & (~T2 ^ T0) & (1 << 31)))) { + xer_ov = 0; + } else { + xer_so = 1; + xer_ov = 1; + } + if (likely(T0 >= T1 && (xer_ca == 0 || T0 != T1))) { + xer_ca = 0; + } else { + xer_ca = 1; + } +} + +void do_subfmeo (void) +{ + T1 = T0; + T0 = ~T0 + xer_ca - 1; + if (likely(!(~T1 & (~T1 ^ T0) & (1 << 31)))) { + xer_ov = 0; + } else { + xer_so = 1; + xer_ov = 1; + } + if (likely(T1 != -1)) + xer_ca = 1; +} + +void do_subfzeo (void) +{ + T1 = T0; + T0 = ~T0 + xer_ca; + if (likely(!((~T1 ^ (-1)) & ((~T1) ^ T0) & (1 << 31)))) { + xer_ov = 0; + } else { + xer_ov = 1; + xer_so = 1; + } + if (likely(T0 >= ~T1)) { + xer_ca = 0; + } else { + xer_ca = 1; } -#endif - msr_pow = (T0 >> MSR_POW) & 0x03; - msr_ile = (T0 >> MSR_ILE) & 0x01; - msr_ee = (T0 >> MSR_EE) & 0x01; - msr_pr = (T0 >> MSR_PR) & 0x01; - msr_fp = (T0 >> MSR_FP) & 0x01; - msr_me = (T0 >> MSR_ME) & 0x01; - msr_fe0 = (T0 >> MSR_FE0) & 0x01; - msr_se = (T0 >> MSR_SE) & 0x01; - msr_be = (T0 >> MSR_BE) & 0x01; - msr_fe1 = (T0 >> MSR_FE1) & 0x01; - msr_ip = (T0 >> MSR_IP) & 0x01; - msr_ir = (T0 >> MSR_IR) & 0x01; - msr_dr = (T0 >> MSR_DR) & 0x01; - msr_ri = (T0 >> MSR_RI) & 0x01; - msr_le = (T0 >> MSR_LE) & 0x01; } /* shift right arithmetic helper */ @@ -157,95 +298,31 @@ void do_sraw (void) { int32_t ret; + if (likely(!(T1 & 0x20UL))) { + if (likely(T1 != 0)) { + ret = (int32_t)T0 >> (T1 & 0x1fUL); + if (likely(ret >= 0 || ((int32_t)T0 & ((1 << T1) - 1)) == 0)) { xer_ca = 0; - if (T1 & 0x20) { - ret = (-1) * (T0 >> 31); - if (ret < 0 && (T0 & ~0x80000000) != 0) + } else { xer_ca = 1; -#if 1 // TRY - } else if (T1 == 0) { + } + } else { ret = T0; -#endif + xer_ca = 0; + } + } else { + ret = (-1) * ((uint32_t)T0 >> 31); + if (likely(ret >= 0 || ((uint32_t)T0 & ~0x80000000UL) == 0)) { + xer_ca = 0; } else { - ret = (int32_t)T0 >> (T1 & 0x1f); - if (ret < 0 && ((int32_t)T0 & ((1 << T1) - 1)) != 0) xer_ca = 1; } + } T0 = ret; } +/*****************************************************************************/ /* Floating point operations helpers */ -void do_load_fpscr (void) -{ - /* The 32 MSB of the target fpr are undefined. - * They'll be zero... - */ - union { - double d; - struct { - uint32_t u[2]; - } s; - } u; - int i; - -#ifdef WORDS_BIGENDIAN -#define WORD0 0 -#define WORD1 1 -#else -#define WORD0 1 -#define WORD1 0 -#endif - u.s.u[WORD0] = 0; - u.s.u[WORD1] = 0; - for (i = 0; i < 8; i++) - u.s.u[WORD1] |= env->fpscr[i] << (4 * i); - FT0 = u.d; -} - -void do_store_fpscr (uint32_t mask) -{ - /* - * We use only the 32 LSB of the incoming fpr - */ - union { - double d; - struct { - uint32_t u[2]; - } s; - } u; - int i, rnd_type; - - u.d = FT0; - if (mask & 0x80) - env->fpscr[0] = (env->fpscr[0] & 0x9) | ((u.s.u[WORD1] >> 28) & ~0x9); - for (i = 1; i < 7; i++) { - if (mask & (1 << (7 - i))) - env->fpscr[i] = (u.s.u[WORD1] >> (4 * (7 - i))) & 0xF; - } - /* TODO: update FEX & VX */ - /* Set rounding mode */ - switch (env->fpscr[0] & 0x3) { - case 0: - /* Best approximation (round to nearest) */ - rnd_type = float_round_nearest_even; - break; - case 1: - /* Smaller magnitude (round toward zero) */ - rnd_type = float_round_to_zero; - break; - case 2: - /* Round toward +infinite */ - rnd_type = float_round_up; - break; - default: - case 3: - /* Round toward -infinite */ - rnd_type = float_round_down; - break; - } - set_float_rounding_mode(rnd_type, &env->fp_status); -} - void do_fctiw (void) { union { @@ -254,7 +331,7 @@ void do_fctiw (void) } p; /* XXX: higher bits are not supposed to be significant. - * to make tests easier, return the same as a real PPC 750 (aka G3) + * to make tests easier, return the same as a real PowerPC 750 (aka G3) */ p.i = float64_to_int32(FT0, &env->fp_status); p.i |= 0xFFF80000ULL << 32; @@ -269,7 +346,7 @@ void do_fctiwz (void) } p; /* XXX: higher bits are not supposed to be significant. - * to make tests easier, return the same as a real PPC 750 (aka G3) + * to make tests easier, return the same as a real PowerPC 750 (aka G3) */ p.i = float64_to_int32_round_to_zero(FT0, &env->fp_status); p.i |= 0xFFF80000ULL << 32; @@ -278,29 +355,23 @@ void do_fctiwz (void) void do_fnmadd (void) { - FT0 = (FT0 * FT1) + FT2; - if (!isnan(FT0)) - FT0 = -FT0; + FT0 = float64_mul(FT0, FT1, &env->fp_status); + FT0 = float64_add(FT0, FT2, &env->fp_status); + if (likely(!isnan(FT0))) + FT0 = float64_chs(FT0); } void do_fnmsub (void) { - FT0 = (FT0 * FT1) - FT2; - if (!isnan(FT0)) - FT0 = -FT0; -} - -void do_fdiv (void) -{ - if (FT0 == -0.0 && FT1 == -0.0) - FT0 = 0.0 / 0.0; - else - FT0 /= FT1; + FT0 = float64_mul(FT0, FT1, &env->fp_status); + FT0 = float64_sub(FT0, FT2, &env->fp_status); + if (likely(!isnan(FT0))) + FT0 = float64_chs(FT0); } void do_fsqrt (void) { - FT0 = sqrt(FT0); + FT0 = float64_sqrt(FT0, &env->fp_status); } void do_fres (void) @@ -310,7 +381,7 @@ void do_fres (void) uint64_t i; } p; - if (isnormal(FT0)) { + if (likely(isnormal(FT0))) { FT0 = (float)(1.0 / FT0); } else { p.d = FT0; @@ -336,8 +407,9 @@ void do_frsqrte (void) uint64_t i; } p; - if (isnormal(FT0) && FT0 > 0.0) { - FT0 = (float)(1.0 / sqrt(FT0)); + if (likely(isnormal(FT0) && FT0 > 0.0)) { + FT0 = float64_sqrt(FT0, &env->fp_status); + FT0 = float32_div(1.0, FT0, &env->fp_status); } else { p.d = FT0; if (p.i == 0x8000000000000000ULL) { @@ -366,16 +438,18 @@ void do_fsel (void) void do_fcmpu (void) { - if (isnan(FT0) || isnan(FT1)) { - T0 = 0x01; + if (likely(!isnan(FT0) && !isnan(FT1))) { + if (float64_lt(FT0, FT1, &env->fp_status)) { + T0 = 0x08UL; + } else if (!float64_le(FT0, FT1, &env->fp_status)) { + T0 = 0x04UL; + } else { + T0 = 0x02UL; + } + } else { + T0 = 0x01UL; env->fpscr[4] |= 0x1; env->fpscr[6] |= 0x1; - } else if (FT0 < FT1) { - T0 = 0x08; - } else if (FT0 > FT1) { - T0 = 0x04; - } else { - T0 = 0x02; } env->fpscr[3] = T0; } @@ -383,8 +457,16 @@ void do_fcmpu (void) void do_fcmpo (void) { env->fpscr[4] &= ~0x1; - if (isnan(FT0) || isnan(FT1)) { - T0 = 0x01; + if (likely(!isnan(FT0) && !isnan(FT1))) { + if (float64_lt(FT0, FT1, &env->fp_status)) { + T0 = 0x08UL; + } else if (!float64_le(FT0, FT1, &env->fp_status)) { + T0 = 0x04UL; + } else { + T0 = 0x02UL; + } + } else { + T0 = 0x01UL; env->fpscr[4] |= 0x1; /* I don't know how to test "quiet" nan... */ if (0 /* || ! quiet_nan(...) */) { @@ -394,56 +476,51 @@ void do_fcmpo (void) } else { env->fpscr[4] |= 0x8; } - } else if (FT0 < FT1) { - T0 = 0x08; - } else if (FT0 > FT1) { - T0 = 0x04; - } else { - T0 = 0x02; } env->fpscr[3] = T0; } -void do_fabs (void) +void do_rfi (void) { - union { - double d; - uint64_t i; - } p; - - p.d = FT0; - p.i &= ~0x8000000000000000ULL; - FT0 = p.d; + env->nip = env->spr[SPR_SRR0] & ~0x00000003; + T0 = env->spr[SPR_SRR1] & ~0xFFFF0000UL; + do_store_msr(env, T0); +#if defined (DEBUG_OP) + dump_rfi(); +#endif + env->interrupt_request |= CPU_INTERRUPT_EXITTB; } -void do_fnabs (void) +void do_tw (uint32_t cmp, int flags) { - union { - double d; - uint64_t i; - } p; - - p.d = FT0; - p.i |= 0x8000000000000000ULL; - FT0 = p.d; + if (!likely(!((Ts0 < (int32_t)cmp && (flags & 0x10)) || + (Ts0 > (int32_t)cmp && (flags & 0x08)) || + (Ts0 == (int32_t)cmp && (flags & 0x04)) || + (T0 < cmp && (flags & 0x02)) || + (T0 > cmp && (flags & 0x01))))) + do_raise_exception_err(EXCP_PROGRAM, EXCP_TRAP); } /* Instruction cache invalidation helper */ -#define ICACHE_LINE_SIZE 32 - -void do_check_reservation (void) -{ - if ((env->reserve & ~0x03) == T0) - env->reserve = -1; -} - void do_icbi (void) { - /* Invalidate one cache line */ + uint32_t tmp; + /* Invalidate one cache line : + * PowerPC specification says this is to be treated like a load + * (not a fetch) by the MMU. To be sure it will be so, + * do the load "by hand". + */ +#if defined(TARGET_PPC64) + if (!msr_sf) + T0 &= 0xFFFFFFFFULL; +#endif + tmp = ldl_kernel(T0); T0 &= ~(ICACHE_LINE_SIZE - 1); tb_invalidate_page_range(T0, T0 + ICACHE_LINE_SIZE); } +/*****************************************************************************/ +/* MMU related helpers */ /* TLB invalidation helpers */ void do_tlbia (void) { @@ -452,119 +529,62 @@ void do_tlbia (void) void do_tlbie (void) { +#if !defined(FLUSH_ALL_TLBS) tlb_flush_page(env, T0); -} - -void do_store_sr (uint32_t srnum) -{ -#if defined (DEBUG_OP) - dump_store_sr(srnum); -#endif -#if 0 // TRY - { - uint32_t base, page; - - base = srnum << 28; - for (page = base; page != base + 0x100000000; page += 0x1000) - tlb_flush_page(env, page); - } -#else - tlb_flush(env, 1); -#endif - env->sr[srnum] = T0; -} - -/* For BATs, we may not invalidate any TLBs if the change is only on - * protection bits for user mode. - */ -void do_store_ibat (int ul, int nr) -{ -#if defined (DEBUG_OP) - dump_store_ibat(ul, nr); -#endif -#if 0 // TRY - { - uint32_t base, length, page; - - base = env->IBAT[0][nr]; - length = (((base >> 2) & 0x000007FF) + 1) << 17; - base &= 0xFFFC0000; - for (page = base; page != base + length; page += 0x1000) - tlb_flush_page(env, page); - } -#else - tlb_flush(env, 1); -#endif - env->IBAT[ul][nr] = T0; -} - -void do_store_dbat (int ul, int nr) -{ -#if defined (DEBUG_OP) - dump_store_dbat(ul, nr); -#endif -#if 0 // TRY - { - uint32_t base, length, page; - base = env->DBAT[0][nr]; - length = (((base >> 2) & 0x000007FF) + 1) << 17; - base &= 0xFFFC0000; - for (page = base; page != base + length; page += 0x1000) - tlb_flush_page(env, page); - } #else - tlb_flush(env, 1); + do_tlbia(); #endif - env->DBAT[ul][nr] = T0; } /*****************************************************************************/ -/* Special helpers for debug */ -void dump_state (void) -{ - // cpu_dump_state(env, stdout, fprintf, 0); -} - -void dump_rfi (void) -{ -#if 0 - printf("Return from interrupt => 0x%08x\n", env->nip); - // cpu_dump_state(env, stdout, fprintf, 0); -#endif +/* Softmmu support */ +#if !defined (CONFIG_USER_ONLY) + +#define MMUSUFFIX _mmu +#define GETPC() (__builtin_return_address(0)) + +#define SHIFT 0 +#include "softmmu_template.h" + +#define SHIFT 1 +#include "softmmu_template.h" + +#define SHIFT 2 +#include "softmmu_template.h" + +#define SHIFT 3 +#include "softmmu_template.h" + +/* try to fill the TLB and return an exception if error. If retaddr is + NULL, it means that the function was called in C code (i.e. not + from generated code or from helper.c) */ +/* XXX: fix it to restore all registers */ +void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr) +{ + TranslationBlock *tb; + CPUState *saved_env; + target_phys_addr_t pc; + int ret; + + /* XXX: hack to restore env in all cases, even if not called from + generated code */ + saved_env = env; + env = cpu_single_env; + ret = cpu_ppc_handle_mmu_fault(env, addr, is_write, is_user, 1); + if (!likely(ret == 0)) { + if (likely(retaddr)) { + /* now we have a real cpu fault */ + pc = (target_phys_addr_t)retaddr; + tb = tb_find_pc(pc); + if (likely(tb)) { + /* the PC is inside the translated code. It means that we have + a virtual CPU fault */ + cpu_restore_state(tb, env, pc, NULL); } - -void dump_store_sr (int srnum) -{ -#if 0 - printf("%s: reg=%d 0x%08x\n", __func__, srnum, T0); -#endif -} - -static void _dump_store_bat (char ID, int ul, int nr) -{ - printf("Set %cBAT%d%c to 0x%08x (0x%08x)\n", - ID, nr, ul == 0 ? 'u' : 'l', T0, env->nip); -} - -void dump_store_ibat (int ul, int nr) -{ - _dump_store_bat('I', ul, nr); -} - -void dump_store_dbat (int ul, int nr) -{ - _dump_store_bat('D', ul, nr); -} - -void dump_store_tb (int ul) -{ - printf("Set TB%c to 0x%08x\n", ul == 0 ? 'L' : 'U', T0); -} - -void dump_update_tb(uint32_t param) -{ -#if 0 - printf("Update TB: 0x%08x + %d => 0x%08x\n", T1, param, T0); -#endif + } + do_raise_exception_err(env->exception_index, env->error_code); + } + env = saved_env; } +#endif /* !CONFIG_USER_ONLY */ diff --git a/qemu/target-ppc/op_helper_mem.h b/qemu/target-ppc/op_helper_mem.h index fa7f076..fb90691 100644 --- a/qemu/target-ppc/op_helper_mem.h +++ b/qemu/target-ppc/op_helper_mem.h @@ -3,10 +3,12 @@ void glue(do_lsw, MEMSUFFIX) (int dst) uint32_t tmp; int sh; +#if 0 if (loglevel > 0) { fprintf(logfile, "%s: addr=0x%08x count=%d reg=%d\n", __func__, T0, T1, dst); } +#endif for (; T1 > 3; T1 -= 4, T0 += 4) { ugpr(dst++) = glue(ldl, MEMSUFFIX)(T0); if (dst == 32) @@ -25,10 +27,12 @@ void glue(do_stsw, MEMSUFFIX) (int src) { int sh; +#if 0 if (loglevel > 0) { fprintf(logfile, "%s: addr=0x%08x count=%d reg=%d\n", __func__, T0, T1, src); } +#endif for (; T1 > 3; T1 -= 4, T0 += 4) { glue(stl, MEMSUFFIX)(T0, ugpr(src++)); if (src == 32) @@ -45,10 +49,12 @@ void glue(do_lsw_le, MEMSUFFIX) (int dst) uint32_t tmp; int sh; +#if 0 if (loglevel > 0) { fprintf(logfile, "%s: addr=0x%08x count=%d reg=%d\n", __func__, T0, T1, dst); } +#endif for (; T1 > 3; T1 -= 4, T0 += 4) { tmp = glue(ldl, MEMSUFFIX)(T0); ugpr(dst++) = ((tmp & 0xFF000000) >> 24) | ((tmp & 0x00FF0000) >> 8) | @@ -70,10 +76,12 @@ void glue(do_stsw_le, MEMSUFFIX) (int src) uint32_t tmp; int sh; +#if 0 if (loglevel > 0) { fprintf(logfile, "%s: addr=0x%08x count=%d reg=%d\n", __func__, T0, T1, src); } +#endif for (; T1 > 3; T1 -= 4, T0 += 4) { tmp = ((ugpr(src++) & 0xFF000000) >> 24); tmp |= ((ugpr(src++) & 0x00FF0000) >> 8); diff --git a/qemu/target-ppc/op_template.h b/qemu/target-ppc/op_template.h index 338b7aa..1be640d 100644 --- a/qemu/target-ppc/op_template.h +++ b/qemu/target-ppc/op_template.h @@ -1,7 +1,7 @@ /* - * PPC emulation micro-operations for qemu. + * PowerPC emulation micro-operations for qemu. * - * Copyright (c) 2003 Jocelyn Mayer + * Copyright (c) 2003-2005 Jocelyn Mayer * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -175,7 +175,7 @@ void OPPROTO glue(op_load_sr, REG)(void) void OPPROTO glue(op_store_sr, REG)(void) { - do_store_sr(REG); + do_store_sr(env, REG, T0); RETURN(); } #endif diff --git a/qemu/target-ppc/translate.c b/qemu/target-ppc/translate.c index 7d2e62b..70f8863 100644 --- a/qemu/target-ppc/translate.c +++ b/qemu/target-ppc/translate.c @@ -1,7 +1,7 @@ /* - * PPC emulation for qemu: main translation routines. + * PowerPC emulation for qemu: main translation routines. * - * Copyright (c) 2003 Jocelyn Mayer + * Copyright (c) 2003-2005 Jocelyn Mayer * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -141,16 +141,17 @@ typedef struct DisasContext { int supervisor; #endif int fpu_enabled; + ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */ } DisasContext; -typedef struct opc_handler_t { +struct opc_handler_t { /* invalid bits */ uint32_t inval; /* instruction type */ uint32_t type; /* handler */ void (*handler)(DisasContext *ctx); -} opc_handler_t; +}; #define RET_EXCP(ctx, excp, error) \ do { \ @@ -173,6 +174,17 @@ RET_EXCP((ctx), EXCP_PROGRAM, EXCP_INVAL | EXCP_PRIV_REG) #define RET_MTMSR(ctx) \ RET_EXCP((ctx), EXCP_MTMSR, 0) +static inline void RET_STOP (DisasContext *ctx) +{ + RET_EXCP(ctx, EXCP_MTMSR, 0); +} + +static inline void RET_CHG_FLOW (DisasContext *ctx) +{ + gen_op_raise_exception_err(EXCP_MTMSR, 0); + ctx->exception = EXCP_MTMSR; +} + #define GEN_HANDLER(name, opc1, opc2, opc3, inval, type) \ static void gen_##name (DisasContext *ctx); \ GEN_OPCODE(name, opc1, opc2, opc3, inval, type); \ @@ -186,6 +198,7 @@ typedef struct opcode_t { unsigned char pad[1]; #endif opc_handler_t handler; + const unsigned char *oname; } opcode_t; /*** Instruction decoding ***/ @@ -226,7 +239,13 @@ EXTRACT_HELPER(crbD, 21, 5); EXTRACT_HELPER(crbA, 16, 5); EXTRACT_HELPER(crbB, 11, 5); /* SPR / TBL */ -EXTRACT_HELPER(SPR, 11, 10); +EXTRACT_HELPER(_SPR, 11, 10); +static inline uint32_t SPR (uint32_t opcode) +{ + uint32_t sprn = _SPR(opcode); + + return ((sprn >> 5) & 0x1F) | ((sprn & 0x1F) << 5); +} /*** Get constants ***/ EXTRACT_HELPER(IMM, 12, 8); /* 16 bits signed immediate value */ @@ -282,12 +301,17 @@ static inline uint32_t MASK (uint32_t start, uint32_t end) return ret; } +#if HOST_LONG_BITS == 64 +#define OPC_ALIGN 8 +#else +#define OPC_ALIGN 4 +#endif #if defined(__APPLE__) #define OPCODES_SECTION \ - __attribute__ ((section("__TEXT,__opcodes"), unused, aligned (8) )) + __attribute__ ((section("__TEXT,__opcodes"), unused, aligned (OPC_ALIGN) )) #else #define OPCODES_SECTION \ - __attribute__ ((section(".opcodes"), unused, aligned (8) )) + __attribute__ ((section(".opcodes"), unused, aligned (OPC_ALIGN) )) #endif #define GEN_OPCODE(name, op1, op2, op3, invl, _typ) \ @@ -301,6 +325,7 @@ OPCODES_SECTION opcode_t opc_##name = { \ .type = _typ, \ .handler = &gen_##name, \ }, \ + .oname = stringify(name), \ } #define GEN_OPCODE_MARK(name) \ @@ -314,6 +339,7 @@ OPCODES_SECTION opcode_t opc_##name = { \ .type = 0x00, \ .handler = NULL, \ }, \ + .oname = stringify(name), \ } /* Start opcode list */ @@ -1344,7 +1370,7 @@ static GenOpFunc1 *gen_op_stsw[] = { #endif /* lswi */ -/* PPC32 specification says we must generate an exception if +/* PowerPC32 specification says we must generate an exception if * rA is in the range of registers to be loaded. * In an other hand, IBM says this is valid, but rA won't be loaded. * For now, I'll follow the spec... @@ -1370,6 +1396,8 @@ GEN_HANDLER(lswi, 0x1F, 0x15, 0x12, 0x00000001, PPC_INTEGER) gen_op_load_gpr_T0(ra); } gen_op_set_T1(nb); + /* NIP cannot be restored if the memory exception comes from an helper */ + gen_op_update_nip((ctx)->nip - 4); op_ldsts(lswi, start); } @@ -1388,6 +1416,8 @@ GEN_HANDLER(lswx, 0x1F, 0x15, 0x10, 0x00000001, PPC_INTEGER) gen_op_add(); } gen_op_load_xer_bc(); + /* NIP cannot be restored if the memory exception comes from an helper */ + gen_op_update_nip((ctx)->nip - 4); op_ldstsx(lswx, rD(ctx->opcode), ra, rb); } @@ -1404,6 +1434,8 @@ GEN_HANDLER(stswi, 0x1F, 0x15, 0x16, 0x00000001, PPC_INTEGER) if (nb == 0) nb = 32; gen_op_set_T1(nb); + /* NIP cannot be restored if the memory exception comes from an helper */ + gen_op_update_nip((ctx)->nip - 4); op_ldsts(stsw, rS(ctx->opcode)); } @@ -1421,6 +1453,8 @@ GEN_HANDLER(stswx, 0x1F, 0x15, 0x14, 0x00000001, PPC_INTEGER) gen_op_add(); } gen_op_load_xer_bc(); + /* NIP cannot be restored if the memory exception comes from an helper */ + gen_op_update_nip((ctx)->nip - 4); op_ldsts(stsw, rS(ctx->opcode)); } @@ -1867,7 +1901,7 @@ GEN_HANDLER(rfi, 0x13, 0x12, 0xFF, 0x03FF8001, PPC_FLOW) return; } gen_op_rfi(); - RET_EXCP(ctx, EXCP_RFI, 0); + RET_CHG_FLOW(ctx); #endif } @@ -1957,173 +1991,59 @@ GEN_HANDLER(mfmsr, 0x1F, 0x13, 0x02, 0x001FF801, PPC_MISC) #endif } +#if 0 +#define SPR_NOACCESS ((void *)(-1)) +#else +static void spr_noaccess (void *opaque, int sprn) +{ + sprn = ((sprn >> 5) & 0x1F) | ((sprn & 0x1F) << 5); + printf("ERROR: try to access SPR %d !\n", sprn); +} +#define SPR_NOACCESS (&spr_noaccess) +#endif + /* mfspr */ -GEN_HANDLER(mfspr, 0x1F, 0x13, 0x0A, 0x00000001, PPC_MISC) +static inline void gen_op_mfspr (DisasContext *ctx) { + void (*read_cb)(void *opaque, int sprn); uint32_t sprn = SPR(ctx->opcode); -#if defined(CONFIG_USER_ONLY) - switch (check_spr_access(sprn, 0, 0)) -#else - switch (check_spr_access(sprn, 0, ctx->supervisor)) +#if !defined(CONFIG_USER_ONLY) + if (ctx->supervisor) + read_cb = ctx->spr_cb[sprn].oea_read; + else #endif - { - case -1: - RET_EXCP(ctx, EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_SPR); - return; - case 0: + read_cb = ctx->spr_cb[sprn].uea_read; + if (read_cb != NULL) { + if (read_cb != SPR_NOACCESS) { + (*read_cb)(ctx, sprn); + gen_op_store_T0_gpr(rD(ctx->opcode)); + } else { + /* Privilege exception */ + printf("Trying to read priviledged spr %d %03x\n", sprn, sprn); RET_PRIVREG(ctx); - return; - default: - break; } - switch (sprn) { - case XER: - gen_op_load_xer(); - break; - case LR: - gen_op_load_lr(); - break; - case CTR: - gen_op_load_ctr(); - break; - case IBAT0U: - gen_op_load_ibat(0, 0); - break; - case IBAT1U: - gen_op_load_ibat(0, 1); - break; - case IBAT2U: - gen_op_load_ibat(0, 2); - break; - case IBAT3U: - gen_op_load_ibat(0, 3); - break; - case IBAT4U: - gen_op_load_ibat(0, 4); - break; - case IBAT5U: - gen_op_load_ibat(0, 5); - break; - case IBAT6U: - gen_op_load_ibat(0, 6); - break; - case IBAT7U: - gen_op_load_ibat(0, 7); - break; - case IBAT0L: - gen_op_load_ibat(1, 0); - break; - case IBAT1L: - gen_op_load_ibat(1, 1); - break; - case IBAT2L: - gen_op_load_ibat(1, 2); - break; - case IBAT3L: - gen_op_load_ibat(1, 3); - break; - case IBAT4L: - gen_op_load_ibat(1, 4); - break; - case IBAT5L: - gen_op_load_ibat(1, 5); - break; - case IBAT6L: - gen_op_load_ibat(1, 6); - break; - case IBAT7L: - gen_op_load_ibat(1, 7); - break; - case DBAT0U: - gen_op_load_dbat(0, 0); - break; - case DBAT1U: - gen_op_load_dbat(0, 1); - break; - case DBAT2U: - gen_op_load_dbat(0, 2); - break; - case DBAT3U: - gen_op_load_dbat(0, 3); - break; - case DBAT4U: - gen_op_load_dbat(0, 4); - break; - case DBAT5U: - gen_op_load_dbat(0, 5); - break; - case DBAT6U: - gen_op_load_dbat(0, 6); - break; - case DBAT7U: - gen_op_load_dbat(0, 7); - break; - case DBAT0L: - gen_op_load_dbat(1, 0); - break; - case DBAT1L: - gen_op_load_dbat(1, 1); - break; - case DBAT2L: - gen_op_load_dbat(1, 2); - break; - case DBAT3L: - gen_op_load_dbat(1, 3); - break; - case DBAT4L: - gen_op_load_dbat(1, 4); - break; - case DBAT5L: - gen_op_load_dbat(1, 5); - break; - case DBAT6L: - gen_op_load_dbat(1, 6); - break; - case DBAT7L: - gen_op_load_dbat(1, 7); - break; - case SDR1: - gen_op_load_sdr1(); - break; - case V_TBL: - gen_op_load_tbl(); - break; - case V_TBU: - gen_op_load_tbu(); - break; - case DECR: - gen_op_load_decr(); - break; - default: - gen_op_load_spr(sprn); - break; + } else { + /* Not defined */ + printf("Trying to read invalid spr %d %03x\n", sprn, sprn); + RET_EXCP(ctx, EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_SPR); } - gen_op_store_T0_gpr(rD(ctx->opcode)); } -/* mftb */ -GEN_HANDLER(mftb, 0x1F, 0x13, 0x0B, 0x00000001, PPC_MISC) +GEN_HANDLER(mfspr, 0x1F, 0x13, 0x0A, 0x00000001, PPC_MISC) { - uint32_t sprn = SPR(ctx->opcode); - - /* We need to update the time base before reading it */ - switch (sprn) { - case V_TBL: - gen_op_load_tbl(); - break; - case V_TBU: - gen_op_load_tbu(); - break; - default: - RET_INVAL(ctx); - return; + gen_op_mfspr(ctx); } - gen_op_store_T0_gpr(rD(ctx->opcode)); + +/* mftb */ +GEN_HANDLER(mftb, 0x1F, 0x13, 0x0B, 0x00000001, PPC_TB) +{ + gen_op_mfspr(ctx); } /* mtcrf */ -GEN_HANDLER(mtcrf, 0x1F, 0x10, 0x04, 0x00100801, PPC_MISC) +/* The mask should be 0x00100801, but Mac OS X 10.4 use an alternate form */ +GEN_HANDLER(mtcrf, 0x1F, 0x10, 0x04, 0x00000801, PPC_MISC) { gen_op_load_gpr_T0(rS(ctx->opcode)); gen_op_store_cr(CRM(ctx->opcode)); @@ -2149,184 +2069,28 @@ GEN_HANDLER(mtmsr, 0x1F, 0x12, 0x04, 0x001FF801, PPC_MISC) /* mtspr */ GEN_HANDLER(mtspr, 0x1F, 0x13, 0x0E, 0x00000001, PPC_MISC) { + void (*write_cb)(void *opaque, int sprn); uint32_t sprn = SPR(ctx->opcode); -#if 0 - if (loglevel > 0) { - fprintf(logfile, "MTSPR %d src=%d (%d)\n", SPR_ENCODE(sprn), - rS(ctx->opcode), sprn); - } -#endif -#if defined(CONFIG_USER_ONLY) - switch (check_spr_access(sprn, 1, 0)) -#else - switch (check_spr_access(sprn, 1, ctx->supervisor)) +#if !defined(CONFIG_USER_ONLY) + if (ctx->supervisor) + write_cb = ctx->spr_cb[sprn].oea_write; + else #endif - { - case -1: - RET_EXCP(ctx, EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_SPR); - break; - case 0: + write_cb = ctx->spr_cb[sprn].uea_write; + if (write_cb != NULL) { + if (write_cb != SPR_NOACCESS) { + gen_op_load_gpr_T0(rS(ctx->opcode)); + (*write_cb)(ctx, sprn); + } else { + /* Privilege exception */ + printf("Trying to write priviledged spr %d %03x\n", sprn, sprn); RET_PRIVREG(ctx); - break; - default: - break; } - gen_op_load_gpr_T0(rS(ctx->opcode)); - switch (sprn) { - case XER: - gen_op_store_xer(); - break; - case LR: - gen_op_store_lr(); - break; - case CTR: - gen_op_store_ctr(); - break; - case IBAT0U: - gen_op_store_ibat(0, 0); - RET_MTMSR(ctx); - break; - case IBAT1U: - gen_op_store_ibat(0, 1); - RET_MTMSR(ctx); - break; - case IBAT2U: - gen_op_store_ibat(0, 2); - RET_MTMSR(ctx); - break; - case IBAT3U: - gen_op_store_ibat(0, 3); - RET_MTMSR(ctx); - break; - case IBAT4U: - gen_op_store_ibat(0, 4); - RET_MTMSR(ctx); - break; - case IBAT5U: - gen_op_store_ibat(0, 5); - RET_MTMSR(ctx); - break; - case IBAT6U: - gen_op_store_ibat(0, 6); - RET_MTMSR(ctx); - break; - case IBAT7U: - gen_op_store_ibat(0, 7); - RET_MTMSR(ctx); - break; - case IBAT0L: - gen_op_store_ibat(1, 0); - RET_MTMSR(ctx); - break; - case IBAT1L: - gen_op_store_ibat(1, 1); - RET_MTMSR(ctx); - break; - case IBAT2L: - gen_op_store_ibat(1, 2); - RET_MTMSR(ctx); - break; - case IBAT3L: - gen_op_store_ibat(1, 3); - RET_MTMSR(ctx); - break; - case IBAT4L: - gen_op_store_ibat(1, 4); - RET_MTMSR(ctx); - break; - case IBAT5L: - gen_op_store_ibat(1, 5); - RET_MTMSR(ctx); - break; - case IBAT6L: - gen_op_store_ibat(1, 6); - RET_MTMSR(ctx); - break; - case IBAT7L: - gen_op_store_ibat(1, 7); - RET_MTMSR(ctx); - break; - case DBAT0U: - gen_op_store_dbat(0, 0); - RET_MTMSR(ctx); - break; - case DBAT1U: - gen_op_store_dbat(0, 1); - RET_MTMSR(ctx); - break; - case DBAT2U: - gen_op_store_dbat(0, 2); - RET_MTMSR(ctx); - break; - case DBAT3U: - gen_op_store_dbat(0, 3); - RET_MTMSR(ctx); - break; - case DBAT4U: - gen_op_store_dbat(0, 4); - RET_MTMSR(ctx); - break; - case DBAT5U: - gen_op_store_dbat(0, 5); - RET_MTMSR(ctx); - break; - case DBAT6U: - gen_op_store_dbat(0, 6); - RET_MTMSR(ctx); - break; - case DBAT7U: - gen_op_store_dbat(0, 7); - RET_MTMSR(ctx); - break; - case DBAT0L: - gen_op_store_dbat(1, 0); - RET_MTMSR(ctx); - break; - case DBAT1L: - gen_op_store_dbat(1, 1); - RET_MTMSR(ctx); - break; - case DBAT2L: - gen_op_store_dbat(1, 2); - RET_MTMSR(ctx); - break; - case DBAT3L: - gen_op_store_dbat(1, 3); - RET_MTMSR(ctx); - break; - case DBAT4L: - gen_op_store_dbat(1, 4); - RET_MTMSR(ctx); - break; - case DBAT5L: - gen_op_store_dbat(1, 5); - RET_MTMSR(ctx); - break; - case DBAT6L: - gen_op_store_dbat(1, 6); - RET_MTMSR(ctx); - break; - case DBAT7L: - gen_op_store_dbat(1, 7); - RET_MTMSR(ctx); - break; - case SDR1: - gen_op_store_sdr1(); - RET_MTMSR(ctx); - break; - case O_TBL: - gen_op_store_tbl(); - break; - case O_TBU: - gen_op_store_tbu(); - break; - case DECR: - gen_op_store_decr(); - break; - default: - gen_op_store_spr(sprn); - break; + } else { + /* Not defined */ + printf("Trying to write invalid spr %d %03x\n", sprn, sprn); + RET_EXCP(ctx, EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_SPR); } } @@ -2400,6 +2164,8 @@ GEN_HANDLER(dcbtst, 0x1F, 0x16, 0x07, 0x03E00001, PPC_CACHE) #define op_dcbz() (*gen_op_dcbz[ctx->mem_idx])() static GenOpFunc *gen_op_dcbz[] = { &gen_op_dcbz_user, + &gen_op_dcbz_user, + &gen_op_dcbz_kernel, &gen_op_dcbz_kernel, }; #endif @@ -2503,7 +2269,7 @@ GEN_HANDLER(mtsrin, 0x1F, 0x12, 0x07, 0x001F0001, PPC_SEGMENT) /*** Lookaside buffer management ***/ /* Optional & supervisor only: */ /* tlbia */ -GEN_HANDLER(tlbia, 0x1F, 0x12, 0x0B, 0x03FFFC01, PPC_MEM_OPT) +GEN_HANDLER(tlbia, 0x1F, 0x12, 0x0B, 0x03FFFC01, PPC_MEM_TLBIA) { #if defined(CONFIG_USER_ONLY) RET_PRIVOPC(ctx); @@ -2613,510 +2379,41 @@ GEN_HANDLER(ecowx, 0x1F, 0x16, 0x09, 0x00000001, PPC_EXTERN) /* End opcode list */ GEN_OPCODE_MARK(end); -/*****************************************************************************/ -#include -#include - -int fflush (FILE *stream); - -/* Main ppc opcodes table: - * at init, all opcodes are invalids - */ -static opc_handler_t *ppc_opcodes[0x40]; - -/* Opcode types */ -enum { - PPC_DIRECT = 0, /* Opcode routine */ - PPC_INDIRECT = 1, /* Indirect opcode table */ -}; - -static inline int is_indirect_opcode (void *handler) -{ - return ((unsigned long)handler & 0x03) == PPC_INDIRECT; -} - -static inline opc_handler_t **ind_table(void *handler) -{ - return (opc_handler_t **)((unsigned long)handler & ~3); -} - -/* Instruction table creation */ -/* Opcodes tables creation */ -static void fill_new_table (opc_handler_t **table, int len) -{ - int i; - - for (i = 0; i < len; i++) - table[i] = &invalid_handler; -} - -static int create_new_table (opc_handler_t **table, unsigned char idx) -{ - opc_handler_t **tmp; - - tmp = malloc(0x20 * sizeof(opc_handler_t)); - if (tmp == NULL) - return -1; - fill_new_table(tmp, 0x20); - table[idx] = (opc_handler_t *)((unsigned long)tmp | PPC_INDIRECT); - - return 0; -} - -static int insert_in_table (opc_handler_t **table, unsigned char idx, - opc_handler_t *handler) -{ - if (table[idx] != &invalid_handler) - return -1; - table[idx] = handler; - - return 0; -} - -static int register_direct_insn (opc_handler_t **ppc_opcodes, - unsigned char idx, opc_handler_t *handler) -{ - if (insert_in_table(ppc_opcodes, idx, handler) < 0) { - printf("*** ERROR: opcode %02x already assigned in main " - "opcode table\n", idx); - return -1; - } - - return 0; -} - -static int register_ind_in_table (opc_handler_t **table, - unsigned char idx1, unsigned char idx2, - opc_handler_t *handler) -{ - if (table[idx1] == &invalid_handler) { - if (create_new_table(table, idx1) < 0) { - printf("*** ERROR: unable to create indirect table " - "idx=%02x\n", idx1); - return -1; - } - } else { - if (!is_indirect_opcode(table[idx1])) { - printf("*** ERROR: idx %02x already assigned to a direct " - "opcode\n", idx1); - return -1; - } - } - if (handler != NULL && - insert_in_table(ind_table(table[idx1]), idx2, handler) < 0) { - printf("*** ERROR: opcode %02x already assigned in " - "opcode table %02x\n", idx2, idx1); - return -1; - } - - return 0; -} - -static int register_ind_insn (opc_handler_t **ppc_opcodes, - unsigned char idx1, unsigned char idx2, - opc_handler_t *handler) -{ - int ret; - - ret = register_ind_in_table(ppc_opcodes, idx1, idx2, handler); - - return ret; -} - -static int register_dblind_insn (opc_handler_t **ppc_opcodes, - unsigned char idx1, unsigned char idx2, - unsigned char idx3, opc_handler_t *handler) -{ - if (register_ind_in_table(ppc_opcodes, idx1, idx2, NULL) < 0) { - printf("*** ERROR: unable to join indirect table idx " - "[%02x-%02x]\n", idx1, idx2); - return -1; - } - if (register_ind_in_table(ind_table(ppc_opcodes[idx1]), idx2, idx3, - handler) < 0) { - printf("*** ERROR: unable to insert opcode " - "[%02x-%02x-%02x]\n", idx1, idx2, idx3); - return -1; - } - - return 0; -} - -static int register_insn (opc_handler_t **ppc_opcodes, opcode_t *insn) -{ - if (insn->opc2 != 0xFF) { - if (insn->opc3 != 0xFF) { - if (register_dblind_insn(ppc_opcodes, insn->opc1, insn->opc2, - insn->opc3, &insn->handler) < 0) - return -1; - } else { - if (register_ind_insn(ppc_opcodes, insn->opc1, - insn->opc2, &insn->handler) < 0) - return -1; - } - } else { - if (register_direct_insn(ppc_opcodes, insn->opc1, &insn->handler) < 0) - return -1; - } - - return 0; -} - -static int test_opcode_table (opc_handler_t **table, int len) -{ - int i, count, tmp; - - for (i = 0, count = 0; i < len; i++) { - /* Consistency fixup */ - if (table[i] == NULL) - table[i] = &invalid_handler; - if (table[i] != &invalid_handler) { - if (is_indirect_opcode(table[i])) { - tmp = test_opcode_table(ind_table(table[i]), 0x20); - if (tmp == 0) { - free(table[i]); - table[i] = &invalid_handler; - } else { - count++; - } - } else { - count++; - } - } - } - - return count; -} - -static void fix_opcode_tables (opc_handler_t **ppc_opcodes) -{ - if (test_opcode_table(ppc_opcodes, 0x40) == 0) - printf("*** WARNING: no opcode defined !\n"); -} - -#define SPR_RIGHTS(rw, priv) (1 << ((2 * (priv)) + (rw))) -#define SPR_UR SPR_RIGHTS(0, 0) -#define SPR_UW SPR_RIGHTS(1, 0) -#define SPR_SR SPR_RIGHTS(0, 1) -#define SPR_SW SPR_RIGHTS(1, 1) - -#define spr_set_rights(spr, rights) \ -do { \ - spr_access[(spr) >> 1] |= ((rights) << (4 * ((spr) & 1))); \ -} while (0) - -static void init_spr_rights (uint32_t pvr) -{ - /* XER (SPR 1) */ - spr_set_rights(XER, SPR_UR | SPR_UW | SPR_SR | SPR_SW); - /* LR (SPR 8) */ - spr_set_rights(LR, SPR_UR | SPR_UW | SPR_SR | SPR_SW); - /* CTR (SPR 9) */ - spr_set_rights(CTR, SPR_UR | SPR_UW | SPR_SR | SPR_SW); - /* TBL (SPR 268) */ - spr_set_rights(V_TBL, SPR_UR | SPR_SR); - /* TBU (SPR 269) */ - spr_set_rights(V_TBU, SPR_UR | SPR_SR); - /* DSISR (SPR 18) */ - spr_set_rights(DSISR, SPR_SR | SPR_SW); - /* DAR (SPR 19) */ - spr_set_rights(DAR, SPR_SR | SPR_SW); - /* DEC (SPR 22) */ - spr_set_rights(DECR, SPR_SR | SPR_SW); - /* SDR1 (SPR 25) */ - spr_set_rights(SDR1, SPR_SR | SPR_SW); - /* SRR0 (SPR 26) */ - spr_set_rights(SRR0, SPR_SR | SPR_SW); - /* SRR1 (SPR 27) */ - spr_set_rights(SRR1, SPR_SR | SPR_SW); - /* SPRG0 (SPR 272) */ - spr_set_rights(SPRG0, SPR_SR | SPR_SW); - /* SPRG1 (SPR 273) */ - spr_set_rights(SPRG1, SPR_SR | SPR_SW); - /* SPRG2 (SPR 274) */ - spr_set_rights(SPRG2, SPR_SR | SPR_SW); - /* SPRG3 (SPR 275) */ - spr_set_rights(SPRG3, SPR_SR | SPR_SW); - /* ASR (SPR 280) */ - spr_set_rights(ASR, SPR_SR | SPR_SW); - /* EAR (SPR 282) */ - spr_set_rights(EAR, SPR_SR | SPR_SW); - /* TBL (SPR 284) */ - spr_set_rights(O_TBL, SPR_SW); - /* TBU (SPR 285) */ - spr_set_rights(O_TBU, SPR_SW); - /* PVR (SPR 287) */ - spr_set_rights(PVR, SPR_SR); - /* IBAT0U (SPR 528) */ - spr_set_rights(IBAT0U, SPR_SR | SPR_SW); - /* IBAT0L (SPR 529) */ - spr_set_rights(IBAT0L, SPR_SR | SPR_SW); - /* IBAT1U (SPR 530) */ - spr_set_rights(IBAT1U, SPR_SR | SPR_SW); - /* IBAT1L (SPR 531) */ - spr_set_rights(IBAT1L, SPR_SR | SPR_SW); - /* IBAT2U (SPR 532) */ - spr_set_rights(IBAT2U, SPR_SR | SPR_SW); - /* IBAT2L (SPR 533) */ - spr_set_rights(IBAT2L, SPR_SR | SPR_SW); - /* IBAT3U (SPR 534) */ - spr_set_rights(IBAT3U, SPR_SR | SPR_SW); - /* IBAT3L (SPR 535) */ - spr_set_rights(IBAT3L, SPR_SR | SPR_SW); - /* DBAT0U (SPR 536) */ - spr_set_rights(DBAT0U, SPR_SR | SPR_SW); - /* DBAT0L (SPR 537) */ - spr_set_rights(DBAT0L, SPR_SR | SPR_SW); - /* DBAT1U (SPR 538) */ - spr_set_rights(DBAT1U, SPR_SR | SPR_SW); - /* DBAT1L (SPR 539) */ - spr_set_rights(DBAT1L, SPR_SR | SPR_SW); - /* DBAT2U (SPR 540) */ - spr_set_rights(DBAT2U, SPR_SR | SPR_SW); - /* DBAT2L (SPR 541) */ - spr_set_rights(DBAT2L, SPR_SR | SPR_SW); - /* DBAT3U (SPR 542) */ - spr_set_rights(DBAT3U, SPR_SR | SPR_SW); - /* DBAT3L (SPR 543) */ - spr_set_rights(DBAT3L, SPR_SR | SPR_SW); - /* FPECR (SPR 1022) */ - spr_set_rights(FPECR, SPR_SR | SPR_SW); - /* Special registers for PPC 604 */ - if ((pvr & 0xFFFF0000) == 0x00040000) { - /* IABR */ - spr_set_rights(IABR , SPR_SR | SPR_SW); - /* DABR (SPR 1013) */ - spr_set_rights(DABR, SPR_SR | SPR_SW); - /* HID0 */ - spr_set_rights(HID0, SPR_SR | SPR_SW); - /* PIR */ - spr_set_rights(PIR, SPR_SR | SPR_SW); - /* PMC1 */ - spr_set_rights(PMC1, SPR_SR | SPR_SW); - /* PMC2 */ - spr_set_rights(PMC2, SPR_SR | SPR_SW); - /* MMCR0 */ - spr_set_rights(MMCR0, SPR_SR | SPR_SW); - /* SIA */ - spr_set_rights(SIA, SPR_SR | SPR_SW); - /* SDA */ - spr_set_rights(SDA, SPR_SR | SPR_SW); - } - /* Special registers for MPC740/745/750/755 (aka G3) & IBM 750 */ - if ((pvr & 0xFFFF0000) == 0x00080000 || - (pvr & 0xFFFF0000) == 0x70000000) { - /* HID0 */ - spr_set_rights(HID0, SPR_SR | SPR_SW); - /* HID1 */ - spr_set_rights(HID1, SPR_SR | SPR_SW); - /* IABR */ - spr_set_rights(IABR, SPR_SR | SPR_SW); - /* ICTC */ - spr_set_rights(ICTC, SPR_SR | SPR_SW); - /* L2CR */ - spr_set_rights(L2CR, SPR_SR | SPR_SW); - /* MMCR0 */ - spr_set_rights(MMCR0, SPR_SR | SPR_SW); - /* MMCR1 */ - spr_set_rights(MMCR1, SPR_SR | SPR_SW); - /* PMC1 */ - spr_set_rights(PMC1, SPR_SR | SPR_SW); - /* PMC2 */ - spr_set_rights(PMC2, SPR_SR | SPR_SW); - /* PMC3 */ - spr_set_rights(PMC3, SPR_SR | SPR_SW); - /* PMC4 */ - spr_set_rights(PMC4, SPR_SR | SPR_SW); - /* SIA */ - spr_set_rights(SIA, SPR_SR | SPR_SW); - /* SDA */ - spr_set_rights(SDA, SPR_SR | SPR_SW); - /* THRM1 */ - spr_set_rights(THRM1, SPR_SR | SPR_SW); - /* THRM2 */ - spr_set_rights(THRM2, SPR_SR | SPR_SW); - /* THRM3 */ - spr_set_rights(THRM3, SPR_SR | SPR_SW); - /* UMMCR0 */ - spr_set_rights(UMMCR0, SPR_UR | SPR_UW); - /* UMMCR1 */ - spr_set_rights(UMMCR1, SPR_UR | SPR_UW); - /* UPMC1 */ - spr_set_rights(UPMC1, SPR_UR | SPR_UW); - /* UPMC2 */ - spr_set_rights(UPMC2, SPR_UR | SPR_UW); - /* UPMC3 */ - spr_set_rights(UPMC3, SPR_UR | SPR_UW); - /* UPMC4 */ - spr_set_rights(UPMC4, SPR_UR | SPR_UW); - /* USIA */ - spr_set_rights(USIA, SPR_UR | SPR_UW); - } - /* MPC755 has special registers */ - if (pvr == 0x00083100) { - /* SPRG4 */ - spr_set_rights(SPRG4, SPR_SR | SPR_SW); - /* SPRG5 */ - spr_set_rights(SPRG5, SPR_SR | SPR_SW); - /* SPRG6 */ - spr_set_rights(SPRG6, SPR_SR | SPR_SW); - /* SPRG7 */ - spr_set_rights(SPRG7, SPR_SR | SPR_SW); - /* IBAT4U */ - spr_set_rights(IBAT4U, SPR_SR | SPR_SW); - /* IBAT4L */ - spr_set_rights(IBAT4L, SPR_SR | SPR_SW); - /* IBAT5U */ - spr_set_rights(IBAT5U, SPR_SR | SPR_SW); - /* IBAT5L */ - spr_set_rights(IBAT5L, SPR_SR | SPR_SW); - /* IBAT6U */ - spr_set_rights(IBAT6U, SPR_SR | SPR_SW); - /* IBAT6L */ - spr_set_rights(IBAT6L, SPR_SR | SPR_SW); - /* IBAT7U */ - spr_set_rights(IBAT7U, SPR_SR | SPR_SW); - /* IBAT7L */ - spr_set_rights(IBAT7L, SPR_SR | SPR_SW); - /* DBAT4U */ - spr_set_rights(DBAT4U, SPR_SR | SPR_SW); - /* DBAT4L */ - spr_set_rights(DBAT4L, SPR_SR | SPR_SW); - /* DBAT5U */ - spr_set_rights(DBAT5U, SPR_SR | SPR_SW); - /* DBAT5L */ - spr_set_rights(DBAT5L, SPR_SR | SPR_SW); - /* DBAT6U */ - spr_set_rights(DBAT6U, SPR_SR | SPR_SW); - /* DBAT6L */ - spr_set_rights(DBAT6L, SPR_SR | SPR_SW); - /* DBAT7U */ - spr_set_rights(DBAT7U, SPR_SR | SPR_SW); - /* DBAT7L */ - spr_set_rights(DBAT7L, SPR_SR | SPR_SW); - /* DMISS */ - spr_set_rights(DMISS, SPR_SR | SPR_SW); - /* DCMP */ - spr_set_rights(DCMP, SPR_SR | SPR_SW); - /* DHASH1 */ - spr_set_rights(DHASH1, SPR_SR | SPR_SW); - /* DHASH2 */ - spr_set_rights(DHASH2, SPR_SR | SPR_SW); - /* IMISS */ - spr_set_rights(IMISS, SPR_SR | SPR_SW); - /* ICMP */ - spr_set_rights(ICMP, SPR_SR | SPR_SW); - /* RPA */ - spr_set_rights(RPA, SPR_SR | SPR_SW); - /* HID2 */ - spr_set_rights(HID2, SPR_SR | SPR_SW); - /* L2PM */ - spr_set_rights(L2PM, SPR_SR | SPR_SW); - } -} +#include "translate_init.c" /*****************************************************************************/ -/* PPC "main stream" common instructions (no optional ones) */ - -typedef struct ppc_proc_t { - int flags; - void *specific; -} ppc_proc_t; - -typedef struct ppc_def_t { - unsigned long pvr; - unsigned long pvr_mask; - ppc_proc_t *proc; -} ppc_def_t; - -static ppc_proc_t ppc_proc_common = { - .flags = PPC_COMMON, - .specific = NULL, -}; - -static ppc_proc_t ppc_proc_G3 = { - .flags = PPC_750, - .specific = NULL, -}; - -static ppc_def_t ppc_defs[] = -{ - /* MPC740/745/750/755 (G3) */ - { - .pvr = 0x00080000, - .pvr_mask = 0xFFFF0000, - .proc = &ppc_proc_G3, - }, - /* IBM 750FX (G3 embedded) */ - { - .pvr = 0x70000000, - .pvr_mask = 0xFFFF0000, - .proc = &ppc_proc_G3, - }, - /* Fallback (generic PPC) */ - { - .pvr = 0x00000000, - .pvr_mask = 0x00000000, - .proc = &ppc_proc_common, - }, -}; - -static int create_ppc_proc (opc_handler_t **ppc_opcodes, unsigned long pvr) -{ - opcode_t *opc, *start, *end; - int i, flags; - - fill_new_table(ppc_opcodes, 0x40); - for (i = 0; ; i++) { - if ((ppc_defs[i].pvr & ppc_defs[i].pvr_mask) == - (pvr & ppc_defs[i].pvr_mask)) { - flags = ppc_defs[i].proc->flags; - break; - } - } - - if (&opc_start < &opc_end) { - start = &opc_start; - end = &opc_end; - } else { - start = &opc_end; - end = &opc_start; - } - for (opc = start + 1; opc != end; opc++) { - if ((opc->handler.type & flags) != 0) - if (register_insn(ppc_opcodes, opc) < 0) { - printf("*** ERROR initializing PPC instruction " - "0x%02x 0x%02x 0x%02x\n", opc->opc1, opc->opc2, - opc->opc3); - return -1; - } - } - fix_opcode_tables(ppc_opcodes); - - return 0; -} - - -/*****************************************************************************/ -/* Misc PPC helpers */ - +/* Misc PowerPC helpers */ void cpu_dump_state(CPUState *env, FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...), int flags) { +#if defined(TARGET_PPC64) || 1 +#define FILL "" +#define REGX "%016llx" +#define RGPL 4 +#define RFPL 4 +#else +#define FILL " " +#define REGX "%08llx" +#define RGPL 8 +#define RFPL 4 +#endif + int i; - cpu_fprintf(f, "nip=0x%08x LR=0x%08x CTR=0x%08x XER=0x%08x " - "MSR=0x%08x\n", env->nip, env->lr, env->ctr, - _load_xer(env), _load_msr(env)); + cpu_fprintf(f, "NIP " REGX " LR " REGX " CTR " REGX "\n", + env->nip, env->lr, env->ctr); + cpu_fprintf(f, "MSR " REGX FILL " XER %08x TB %08x %08x DECR %08x\n", + do_load_msr(env), do_load_xer(env), cpu_ppc_load_tbu(env), + cpu_ppc_load_tbl(env), cpu_ppc_load_decr(env)); for (i = 0; i < 32; i++) { - if ((i & 7) == 0) - cpu_fprintf(f, "GPR%02d:", i); - cpu_fprintf(f, " %08x", env->gpr[i]); - if ((i & 7) == 7) + if ((i & (RGPL - 1)) == 0) + cpu_fprintf(f, "GPR%02d", i); + cpu_fprintf(f, " " REGX, env->gpr[i]); + if ((i & (RGPL - 1)) == (RGPL - 1)) cpu_fprintf(f, "\n"); } - cpu_fprintf(f, "CR: 0x"); + cpu_fprintf(f, "CR "); for (i = 0; i < 8; i++) cpu_fprintf(f, "%01x", env->crf[i]); cpu_fprintf(f, " ["); @@ -3130,65 +2427,22 @@ void cpu_dump_state(CPUState *env, FILE *f, a = 'E'; cpu_fprintf(f, " %c%c", a, env->crf[i] & 0x01 ? 'O' : ' '); } - cpu_fprintf(f, " ] "); - cpu_fprintf(f, "TB: 0x%08x %08x\n", cpu_ppc_load_tbu(env), - cpu_ppc_load_tbl(env)); - for (i = 0; i < 16; i++) { - if ((i & 3) == 0) - cpu_fprintf(f, "FPR%02d:", i); + cpu_fprintf(f, " ] " FILL "RES " REGX "\n", env->reserve); + for (i = 0; i < 32; i++) { + if ((i & (RFPL - 1)) == 0) + cpu_fprintf(f, "FPR%02d", i); cpu_fprintf(f, " %016llx", *((uint64_t *)&env->fpr[i])); - if ((i & 3) == 3) + if ((i & (RFPL - 1)) == (RFPL - 1)) cpu_fprintf(f, "\n"); } - cpu_fprintf(f, "SRR0 0x%08x SRR1 0x%08x DECR=0x%08x\n", - env->spr[SRR0], env->spr[SRR1], cpu_ppc_load_decr(env)); - cpu_fprintf(f, "reservation 0x%08x\n", env->reserve); -} - -CPUPPCState *cpu_ppc_init(void) -{ - CPUPPCState *env; - - cpu_exec_init(); - - env = qemu_mallocz(sizeof(CPUPPCState)); - if (!env) - return NULL; -// env->spr[PVR] = 0; /* Basic PPC */ - env->spr[PVR] = 0x00080100; /* G3 CPU */ -// env->spr[PVR] = 0x00083100; /* MPC755 (G3 embedded) */ -// env->spr[PVR] = 0x00070100; /* IBM 750FX */ - tlb_flush(env, 1); -#if defined (DO_SINGLE_STEP) - /* Single step trace mode */ - msr_se = 1; -#endif - msr_fp = 1; /* Allow floating point exceptions */ - msr_me = 1; /* Allow machine check exceptions */ -#if defined(CONFIG_USER_ONLY) - msr_pr = 1; - cpu_ppc_register(env, 0x00080000); -#else - env->nip = 0xFFFFFFFC; -#endif - cpu_single_env = env; - return env; -} - -int cpu_ppc_register (CPUPPCState *env, uint32_t pvr) -{ - env->spr[PVR] = pvr; - if (create_ppc_proc(ppc_opcodes, env->spr[PVR]) < 0) - return -1; - init_spr_rights(env->spr[PVR]); + cpu_fprintf(f, "SRR0 " REGX " SRR1 " REGX " " FILL FILL FILL + "SDR1 " REGX "\n", + env->spr[SPR_SRR0], env->spr[SPR_SRR1], env->sdr1); - return 0; -} - -void cpu_ppc_close(CPUPPCState *env) -{ - /* Should also remove all opcode tables... */ - free(env); +#undef REGX +#undef RGPL +#undef RFPL +#undef FILL } /*****************************************************************************/ @@ -3208,6 +2462,7 @@ int gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb, ctx.nip = pc_start; ctx.tb = tb; ctx.exception = EXCP_NONE; + ctx.spr_cb = env->spr_cb; #if defined(CONFIG_USER_ONLY) ctx.mem_idx = msr_le; #else @@ -3215,7 +2470,7 @@ int gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb, ctx.mem_idx = ((1 - msr_pr) << 1) | msr_le; #endif ctx.fpu_enabled = msr_fp; -#if defined (DO_SINGLE_STEP) +#if defined (DO_SINGLE_STEP) && 0 /* Single step trace mode */ msr_se = 1; #endif @@ -3253,7 +2508,7 @@ int gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb, } #endif ctx.nip += 4; - table = ppc_opcodes; + table = env->opcodes; handler = table[opc1(ctx.opcode)]; if (is_indirect_opcode(handler)) { table = ind_table(handler); @@ -3306,14 +2561,18 @@ int gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb, (msr_se && (ctx.nip < 0x100 || ctx.nip > 0xF00 || (ctx.nip & 0xFC) != 0x04) && - ctx.exception != EXCP_SYSCALL && ctx.exception != EXCP_RFI && + ctx.exception != EXCP_SYSCALL && + ctx.exception != EXCP_SYSCALL_USER && ctx.exception != EXCP_TRAP)) { RET_EXCP(ctxp, EXCP_TRACE, 0); } /* if we reach a page boundary, stop generation */ if ((ctx.nip & (TARGET_PAGE_SIZE - 1)) == 0) { - RET_EXCP(ctxp, EXCP_BRANCH, 0); + break; } +#if defined (DO_SINGLE_STEP) + break; +#endif } if (ctx.exception == EXCP_NONE) { gen_op_b((unsigned long)ctx.tb, ctx.nip); diff --git a/qemu/target-ppc/translate_init.c b/qemu/target-ppc/translate_init.c new file mode 100644 index 0000000..624527d --- /dev/null +++ b/qemu/target-ppc/translate_init.c @@ -0,0 +1,2069 @@ +/* + * PowerPC CPU initialization for qemu. + * + * Copyright (c) 2003-2005 Jocelyn Mayer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* A lot of PowerPC definition have been included here. + * Most of them are not usable for now but have been kept + * inside "#if defined(TODO) ... #endif" statements to make tests easier. + */ + +//#define PPC_DUMP_CPU +//#define PPC_DEBUG_SPR + +struct ppc_def_t { + const unsigned char *name; + uint32_t pvr; + uint32_t pvr_mask; + uint32_t insns_flags; + uint32_t flags; + uint64_t msr_mask; +}; + +/* Generic callbacks: + * do nothing but store/retrieve spr value + */ +static void spr_read_generic (void *opaque, int sprn) +{ + gen_op_load_spr(sprn); +} + +static void spr_write_generic (void *opaque, int sprn) +{ + gen_op_store_spr(sprn); +} + +/* SPR common to all PPC */ +/* XER */ +static void spr_read_xer (void *opaque, int sprn) +{ + gen_op_load_xer(); +} + +static void spr_write_xer (void *opaque, int sprn) +{ + gen_op_store_xer(); +} + +/* LR */ +static void spr_read_lr (void *opaque, int sprn) +{ + gen_op_load_lr(); +} + +static void spr_write_lr (void *opaque, int sprn) +{ + gen_op_store_lr(); +} + +/* CTR */ +static void spr_read_ctr (void *opaque, int sprn) +{ + gen_op_load_ctr(); +} + +static void spr_write_ctr (void *opaque, int sprn) +{ + gen_op_store_ctr(); +} + +/* User read access to SPR */ +/* USPRx */ +/* UMMCRx */ +/* UPMCx */ +/* USIA */ +/* UDECR */ +static void spr_read_ureg (void *opaque, int sprn) +{ + gen_op_load_spr(sprn + 0x10); +} + +/* SPR common to all non-embedded PPC (ie not 4xx) */ +/* DECR */ +static void spr_read_decr (void *opaque, int sprn) +{ + gen_op_load_decr(); +} + +static void spr_write_decr (void *opaque, int sprn) +{ + gen_op_store_decr(); +} + +/* SPR common to all non-embedded PPC, except 601 */ +/* Time base */ +static void spr_read_tbl (void *opaque, int sprn) +{ + gen_op_load_tbl(); +} + +static void spr_write_tbl (void *opaque, int sprn) +{ + gen_op_store_tbl(); +} + +static void spr_read_tbu (void *opaque, int sprn) +{ + gen_op_load_tbu(); +} + +static void spr_write_tbu (void *opaque, int sprn) +{ + gen_op_store_tbu(); +} + +/* IBAT0U...IBAT0U */ +/* IBAT0L...IBAT7L */ +static void spr_read_ibat (void *opaque, int sprn) +{ + gen_op_load_ibat(sprn & 1, (sprn - SPR_IBAT0U) / 2); +} + +static void spr_read_ibat_h (void *opaque, int sprn) +{ + gen_op_load_ibat(sprn & 1, (sprn - SPR_IBAT4U) / 2); +} + +static void spr_write_ibatu (void *opaque, int sprn) +{ + DisasContext *ctx = opaque; + + gen_op_store_ibatu((sprn - SPR_IBAT0U) / 2); + RET_STOP(ctx); +} + +static void spr_write_ibatu_h (void *opaque, int sprn) +{ + DisasContext *ctx = opaque; + + gen_op_store_ibatu((sprn - SPR_IBAT4U) / 2); + RET_STOP(ctx); +} + +static void spr_write_ibatl (void *opaque, int sprn) +{ + DisasContext *ctx = opaque; + + gen_op_store_ibatl((sprn - SPR_IBAT0L) / 2); + RET_STOP(ctx); +} + +static void spr_write_ibatl_h (void *opaque, int sprn) +{ + DisasContext *ctx = opaque; + + gen_op_store_ibatl((sprn - SPR_IBAT4L) / 2); + RET_STOP(ctx); +} + +/* DBAT0U...DBAT7U */ +/* DBAT0L...DBAT7L */ +static void spr_read_dbat (void *opaque, int sprn) +{ + gen_op_load_dbat(sprn & 1, (sprn - SPR_DBAT0U) / 2); +} + +static void spr_read_dbat_h (void *opaque, int sprn) +{ + gen_op_load_dbat(sprn & 1, (sprn - SPR_DBAT4U) / 2); +} + +static void spr_write_dbatu (void *opaque, int sprn) +{ + DisasContext *ctx = opaque; + + gen_op_store_dbatu((sprn - SPR_DBAT0U) / 2); + RET_STOP(ctx); +} + +static void spr_write_dbatu_h (void *opaque, int sprn) +{ + DisasContext *ctx = opaque; + + gen_op_store_dbatu((sprn - SPR_DBAT4U) / 2); + RET_STOP(ctx); +} + +static void spr_write_dbatl (void *opaque, int sprn) +{ + DisasContext *ctx = opaque; + + gen_op_store_dbatl((sprn - SPR_DBAT0L) / 2); + RET_STOP(ctx); +} + +static void spr_write_dbatl_h (void *opaque, int sprn) +{ + DisasContext *ctx = opaque; + + gen_op_store_dbatl((sprn - SPR_DBAT4L) / 2); + RET_STOP(ctx); +} + +/* SDR1 */ +static void spr_read_sdr1 (void *opaque, int sprn) +{ + gen_op_load_sdr1(); +} + +static void spr_write_sdr1 (void *opaque, int sprn) +{ + DisasContext *ctx = opaque; + + gen_op_store_sdr1(); + RET_STOP(ctx); +} + +static void spr_write_pir (void *opaque, int sprn) +{ + gen_op_store_pir(); +} + +static inline void spr_register (CPUPPCState *env, int num, + const unsigned char *name, + void (*uea_read)(void *opaque, int sprn), + void (*uea_write)(void *opaque, int sprn), + void (*oea_read)(void *opaque, int sprn), + void (*oea_write)(void *opaque, int sprn), + target_ulong initial_value) +{ + ppc_spr_t *spr; + + spr = &env->spr_cb[num]; + if (spr->name != NULL ||env-> spr[num] != 0x00000000 || + spr->uea_read != NULL || spr->uea_write != NULL || + spr->oea_read != NULL || spr->oea_write != NULL) { + printf("Error: Trying to register SPR %d (%03x) twice !\n", num, num); + exit(1); + } +#if defined(PPC_DEBUG_SPR) + printf("*** register spr %d (%03x) %s val %08llx\n", num, num, name, + (unsigned long long)initial_value); +#endif + spr->name = name; + spr->uea_read = uea_read; + spr->uea_write = uea_write; + spr->oea_read = oea_read; + spr->oea_write = oea_write; + env->spr[num] = initial_value; +} + +/* Generic PowerPC SPRs */ +static void gen_spr_generic (CPUPPCState *env) +{ + /* Integer processing */ + spr_register(env, SPR_XER, "XER", + &spr_read_xer, &spr_write_xer, + &spr_read_xer, &spr_write_xer, + 0x00000000); + /* Branch contol */ + spr_register(env, SPR_LR, "LR", + &spr_read_lr, &spr_write_lr, + &spr_read_lr, &spr_write_lr, + 0x00000000); + spr_register(env, SPR_CTR, "CTR", + &spr_read_ctr, &spr_write_ctr, + &spr_read_ctr, &spr_write_ctr, + 0x00000000); + /* Interrupt processing */ + spr_register(env, SPR_SRR0, "SRR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_SRR1, "SRR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Processor control */ + spr_register(env, SPR_SPRG0, "SPRG0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_SPRG1, "SPRG1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_SPRG2, "SPRG2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_SPRG3, "SPRG3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +/* SPR common to all non-embedded PowerPC, including 601 */ +static void gen_spr_ne_601 (CPUPPCState *env) +{ + /* Exception processing */ + spr_register(env, SPR_DSISR, "DSISR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_DAR, "DAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Timer */ + spr_register(env, SPR_DECR, "DECR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_decr, &spr_write_decr, + 0x00000000); + /* Memory management */ + spr_register(env, SPR_SDR1, "SDR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_sdr1, &spr_write_sdr1, + 0x00000000); +} + +/* BATs 0-3 */ +static void gen_low_BATs (CPUPPCState *env) +{ + spr_register(env, SPR_IBAT0U, "IBAT0U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat, &spr_write_ibatu, + 0x00000000); + spr_register(env, SPR_IBAT0L, "IBAT0L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat, &spr_write_ibatl, + 0x00000000); + spr_register(env, SPR_IBAT1U, "IBAT1U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat, &spr_write_ibatu, + 0x00000000); + spr_register(env, SPR_IBAT1L, "IBAT1L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat, &spr_write_ibatl, + 0x00000000); + spr_register(env, SPR_IBAT2U, "IBAT2U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat, &spr_write_ibatu, + 0x00000000); + spr_register(env, SPR_IBAT2L, "IBAT2L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat, &spr_write_ibatl, + 0x00000000); + spr_register(env, SPR_IBAT3U, "IBAT3U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat, &spr_write_ibatu, + 0x00000000); + spr_register(env, SPR_IBAT3L, "IBAT3L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat, &spr_write_ibatl, + 0x00000000); + spr_register(env, SPR_DBAT0U, "DBAT0U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat, &spr_write_dbatu, + 0x00000000); + spr_register(env, SPR_DBAT0L, "DBAT0L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat, &spr_write_dbatl, + 0x00000000); + spr_register(env, SPR_DBAT1U, "DBAT1U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat, &spr_write_dbatu, + 0x00000000); + spr_register(env, SPR_DBAT1L, "DBAT1L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat, &spr_write_dbatl, + 0x00000000); + spr_register(env, SPR_DBAT2U, "DBAT2U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat, &spr_write_dbatu, + 0x00000000); + spr_register(env, SPR_DBAT2L, "DBAT2L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat, &spr_write_dbatl, + 0x00000000); + spr_register(env, SPR_DBAT3U, "DBAT3U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat, &spr_write_dbatu, + 0x00000000); + spr_register(env, SPR_DBAT3L, "DBAT3L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat, &spr_write_dbatl, + 0x00000000); + env->nb_BATs = 4; +} + +/* BATs 4-7 */ +static void gen_high_BATs (CPUPPCState *env) +{ + spr_register(env, SPR_IBAT4U, "IBAT4U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat_h, &spr_write_ibatu_h, + 0x00000000); + spr_register(env, SPR_IBAT4L, "IBAT4L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat_h, &spr_write_ibatl_h, + 0x00000000); + spr_register(env, SPR_IBAT5U, "IBAT5U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat_h, &spr_write_ibatu_h, + 0x00000000); + spr_register(env, SPR_IBAT5L, "IBAT5L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat_h, &spr_write_ibatl_h, + 0x00000000); + spr_register(env, SPR_IBAT6U, "IBAT6U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat_h, &spr_write_ibatu_h, + 0x00000000); + spr_register(env, SPR_IBAT6L, "IBAT6L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat_h, &spr_write_ibatl_h, + 0x00000000); + spr_register(env, SPR_IBAT7U, "IBAT7U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat_h, &spr_write_ibatu_h, + 0x00000000); + spr_register(env, SPR_IBAT7L, "IBAT7L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_ibat_h, &spr_write_ibatl_h, + 0x00000000); + spr_register(env, SPR_DBAT4U, "DBAT4U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat_h, &spr_write_dbatu_h, + 0x00000000); + spr_register(env, SPR_DBAT4L, "DBAT4L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat_h, &spr_write_dbatl_h, + 0x00000000); + spr_register(env, SPR_DBAT5U, "DBAT5U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat_h, &spr_write_dbatu_h, + 0x00000000); + spr_register(env, SPR_DBAT5L, "DBAT5L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat_h, &spr_write_dbatl_h, + 0x00000000); + spr_register(env, SPR_DBAT6U, "DBAT6U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat_h, &spr_write_dbatu_h, + 0x00000000); + spr_register(env, SPR_DBAT6L, "DBAT6L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat_h, &spr_write_dbatl_h, + 0x00000000); + spr_register(env, SPR_DBAT7U, "DBAT7U", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat_h, &spr_write_dbatu_h, + 0x00000000); + spr_register(env, SPR_DBAT7L, "DBAT7L", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dbat_h, &spr_write_dbatl_h, + 0x00000000); + env->nb_BATs = 8; +} + +/* Generic PowerPC time base */ +static void gen_tbl (CPUPPCState *env) +{ + spr_register(env, SPR_VTBL, "TBL", + &spr_read_tbl, SPR_NOACCESS, + &spr_read_tbl, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_TBL, "TBL", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, &spr_write_tbl, + 0x00000000); + spr_register(env, SPR_VTBU, "TBU", + &spr_read_tbu, SPR_NOACCESS, + &spr_read_tbu, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_TBU, "TBU", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, &spr_write_tbu, + 0x00000000); +} + +/* SPR common to all 7xx PowerPC implementations */ +static void gen_spr_7xx (CPUPPCState *env) +{ + /* Breakpoints */ + /* XXX : not implemented */ + spr_register(env, SPR_DABR, "DABR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_IABR, "IABR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Cache management */ + /* XXX : not implemented */ + spr_register(env, SPR_ICTC, "ICTC", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Performance monitors */ + /* XXX : not implemented */ + spr_register(env, SPR_MMCR0, "MMCR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MMCR1, "MMCR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_PMC1, "PMC1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_PMC2, "PMC2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_PMC3, "PMC3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_PMC4, "PMC4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_SIA, "SIA", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_UMMCR0, "UMMCR0", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_UMMCR1, "UMMCR1", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_UPMC1, "UPMC1", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_UPMC2, "UPMC2", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_UPMC3, "UPMC3", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_UPMC4, "UPMC4", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_USIA, "USIA", + &spr_read_ureg, SPR_NOACCESS, + &spr_read_ureg, SPR_NOACCESS, + 0x00000000); + /* Thermal management */ + /* XXX : not implemented */ + spr_register(env, SPR_THRM1, "THRM1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_THRM2, "THRM2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_THRM3, "THRM3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* External access control */ + /* XXX : not implemented */ + spr_register(env, SPR_EAR, "EAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +/* SPR specific to PowerPC 604 implementation */ +static void gen_spr_604 (CPUPPCState *env) +{ + /* Processor identification */ + spr_register(env, SPR_PIR, "PIR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_pir, + 0x00000000); + /* Breakpoints */ + /* XXX : not implemented */ + spr_register(env, SPR_IABR, "IABR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_DABR, "DABR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Performance counters */ + /* XXX : not implemented */ + spr_register(env, SPR_MMCR0, "MMCR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MMCR1, "MMCR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_PMC1, "PMC1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_PMC2, "PMC2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_PMC3, "PMC3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_PMC4, "PMC4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_SIA, "SIA", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_SDA, "SDA", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + 0x00000000); + /* External access control */ + /* XXX : not implemented */ + spr_register(env, SPR_EAR, "EAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); +} + +// XXX: TODO (64 bits PPC sprs) +/* + * ASR => SPR 280 (64 bits) + * FPECR => SPR 1022 (?) + * VRSAVE => SPR 256 (Altivec) + * SCOMC => SPR 276 (64 bits ?) + * SCOMD => SPR 277 (64 bits ?) + * HSPRG0 => SPR 304 (hypervisor) + * HSPRG1 => SPR 305 (hypervisor) + * HDEC => SPR 310 (hypervisor) + * HIOR => SPR 311 (hypervisor) + * RMOR => SPR 312 (970) + * HRMOR => SPR 313 (hypervisor) + * HSRR0 => SPR 314 (hypervisor) + * HSRR1 => SPR 315 (hypervisor) + * LPCR => SPR 316 (970) + * LPIDR => SPR 317 (970) + * ... and more (thermal management, performance counters, ...) + */ + +static void init_ppc_proc (CPUPPCState *env, ppc_def_t *def) +{ + /* Default MMU definitions */ + env->nb_BATs = -1; + env->nb_tlb = 0; + env->nb_ways = 0; + /* XXX: missing: + * 32 bits PPC: + * - MPC5xx(x) + * - MPC8xx(x) + * - RCPU (MPC5xx) + */ + spr_register(env, SPR_PVR, "PVR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + def->pvr); + switch (def->pvr & def->pvr_mask) { + case CPU_PPC_604: /* PPC 604 */ + case CPU_PPC_604E: /* PPC 604e */ + case CPU_PPC_604R: /* PPC 604r */ + gen_spr_generic(env); + gen_spr_ne_601(env); + /* Memory management */ + gen_low_BATs(env); + /* Time base */ + gen_tbl(env); + gen_spr_604(env); + /* Hardware implementation registers */ + /* XXX : not implemented */ + spr_register(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID1, "HID1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + break; + + case CPU_PPC_74x: /* PPC 740 / 750 */ + case CPU_PPC_74xP: /* PPC 740P / 750P */ + case CPU_PPC_750CXE: /* IBM PPC 750cxe */ + gen_spr_generic(env); + gen_spr_ne_601(env); + /* Memory management */ + gen_low_BATs(env); + /* Time base */ + gen_tbl(env); + gen_spr_7xx(env); + /* XXX : not implemented */ + spr_register(env, SPR_L2CR, "L2CR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Hardware implementation registers */ + /* XXX : not implemented */ + spr_register(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID1, "HID1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + break; + + case CPU_PPC_750FX: /* IBM PPC 750 FX */ + case CPU_PPC_750GX: /* IBM PPC 750 GX */ + gen_spr_generic(env); + gen_spr_ne_601(env); + /* Memory management */ + gen_low_BATs(env); + /* PowerPC 750fx & 750gx has 8 DBATs and 8 IBATs */ + gen_high_BATs(env); + /* Time base */ + gen_tbl(env); + gen_spr_7xx(env); + /* XXX : not implemented */ + spr_register(env, SPR_L2CR, "L2CR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* Hardware implementation registers */ + /* XXX : not implemented */ + spr_register(env, SPR_HID0, "HID0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_HID1, "HID1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_750_HID2, "HID2", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + break; + + default: + gen_spr_generic(env); + break; + } + if (env->nb_BATs == -1) + env->nb_BATs = 4; +} + +#if defined(PPC_DUMP_CPU) +static void dump_sprs (CPUPPCState *env) +{ + ppc_spr_t *spr; + uint32_t pvr = env->spr[SPR_PVR]; + uint32_t sr, sw, ur, uw; + int i, j, n; + + printf("* SPRs for PVR=%08x\n", pvr); + for (i = 0; i < 32; i++) { + for (j = 0; j < 32; j++) { + n = (i << 5) | j; + spr = &env->spr_cb[n]; + sw = spr->oea_write != NULL && spr->oea_write != SPR_NOACCESS; + sr = spr->oea_read != NULL && spr->oea_read != SPR_NOACCESS; + uw = spr->uea_write != NULL && spr->uea_write != SPR_NOACCESS; + ur = spr->uea_read != NULL && spr->uea_read != SPR_NOACCESS; + if (sw || sr || uw || ur) { + printf("%4d (%03x) %8s s%c%c u%c%c\n", + (i << 5) | j, (i << 5) | j, spr->name, + sw ? 'w' : '-', sr ? 'r' : '-', + uw ? 'w' : '-', ur ? 'r' : '-'); + } + } + } + fflush(stdout); + fflush(stderr); +} +#endif + +/*****************************************************************************/ +#include +#include + +int fflush (FILE *stream); + +/* Opcode types */ +enum { + PPC_DIRECT = 0, /* Opcode routine */ + PPC_INDIRECT = 1, /* Indirect opcode table */ +}; + +static inline int is_indirect_opcode (void *handler) +{ + return ((unsigned long)handler & 0x03) == PPC_INDIRECT; +} + +static inline opc_handler_t **ind_table(void *handler) +{ + return (opc_handler_t **)((unsigned long)handler & ~3); +} + +/* Instruction table creation */ +/* Opcodes tables creation */ +static void fill_new_table (opc_handler_t **table, int len) +{ + int i; + + for (i = 0; i < len; i++) + table[i] = &invalid_handler; +} + +static int create_new_table (opc_handler_t **table, unsigned char idx) +{ + opc_handler_t **tmp; + + tmp = malloc(0x20 * sizeof(opc_handler_t)); + if (tmp == NULL) + return -1; + fill_new_table(tmp, 0x20); + table[idx] = (opc_handler_t *)((unsigned long)tmp | PPC_INDIRECT); + + return 0; +} + +static int insert_in_table (opc_handler_t **table, unsigned char idx, + opc_handler_t *handler) +{ + if (table[idx] != &invalid_handler) + return -1; + table[idx] = handler; + + return 0; +} + +static int register_direct_insn (opc_handler_t **ppc_opcodes, + unsigned char idx, opc_handler_t *handler) +{ + if (insert_in_table(ppc_opcodes, idx, handler) < 0) { + printf("*** ERROR: opcode %02x already assigned in main " + "opcode table\n", idx); + return -1; + } + + return 0; +} + +static int register_ind_in_table (opc_handler_t **table, + unsigned char idx1, unsigned char idx2, + opc_handler_t *handler) +{ + if (table[idx1] == &invalid_handler) { + if (create_new_table(table, idx1) < 0) { + printf("*** ERROR: unable to create indirect table " + "idx=%02x\n", idx1); + return -1; + } + } else { + if (!is_indirect_opcode(table[idx1])) { + printf("*** ERROR: idx %02x already assigned to a direct " + "opcode\n", idx1); + return -1; + } + } + if (handler != NULL && + insert_in_table(ind_table(table[idx1]), idx2, handler) < 0) { + printf("*** ERROR: opcode %02x already assigned in " + "opcode table %02x\n", idx2, idx1); + return -1; + } + + return 0; +} + +static int register_ind_insn (opc_handler_t **ppc_opcodes, + unsigned char idx1, unsigned char idx2, + opc_handler_t *handler) +{ + int ret; + + ret = register_ind_in_table(ppc_opcodes, idx1, idx2, handler); + + return ret; +} + +static int register_dblind_insn (opc_handler_t **ppc_opcodes, + unsigned char idx1, unsigned char idx2, + unsigned char idx3, opc_handler_t *handler) +{ + if (register_ind_in_table(ppc_opcodes, idx1, idx2, NULL) < 0) { + printf("*** ERROR: unable to join indirect table idx " + "[%02x-%02x]\n", idx1, idx2); + return -1; + } + if (register_ind_in_table(ind_table(ppc_opcodes[idx1]), idx2, idx3, + handler) < 0) { + printf("*** ERROR: unable to insert opcode " + "[%02x-%02x-%02x]\n", idx1, idx2, idx3); + return -1; + } + + return 0; +} + +static int register_insn (opc_handler_t **ppc_opcodes, opcode_t *insn) +{ + if (insn->opc2 != 0xFF) { + if (insn->opc3 != 0xFF) { + if (register_dblind_insn(ppc_opcodes, insn->opc1, insn->opc2, + insn->opc3, &insn->handler) < 0) + return -1; + } else { + if (register_ind_insn(ppc_opcodes, insn->opc1, + insn->opc2, &insn->handler) < 0) + return -1; + } + } else { + if (register_direct_insn(ppc_opcodes, insn->opc1, &insn->handler) < 0) + return -1; + } + + return 0; +} + +static int test_opcode_table (opc_handler_t **table, int len) +{ + int i, count, tmp; + + for (i = 0, count = 0; i < len; i++) { + /* Consistency fixup */ + if (table[i] == NULL) + table[i] = &invalid_handler; + if (table[i] != &invalid_handler) { + if (is_indirect_opcode(table[i])) { + tmp = test_opcode_table(ind_table(table[i]), 0x20); + if (tmp == 0) { + free(table[i]); + table[i] = &invalid_handler; + } else { + count++; + } + } else { + count++; + } + } + } + + return count; +} + +static void fix_opcode_tables (opc_handler_t **ppc_opcodes) +{ + if (test_opcode_table(ppc_opcodes, 0x40) == 0) + printf("*** WARNING: no opcode defined !\n"); +} + +/*****************************************************************************/ +static int create_ppc_opcodes (CPUPPCState *env, ppc_def_t *def) +{ + opcode_t *opc, *start, *end; + + fill_new_table(env->opcodes, 0x40); +#if defined(PPC_DUMP_CPU) + printf("* PPC instructions for PVR %08x: %s\n", def->pvr, def->name); +#endif + if (&opc_start < &opc_end) { + start = &opc_start; + end = &opc_end; + } else { + start = &opc_end; + end = &opc_start; + } + for (opc = start + 1; opc != end; opc++) { + if ((opc->handler.type & def->insns_flags) != 0) { + if (register_insn(env->opcodes, opc) < 0) { + printf("*** ERROR initializing PPC instruction " + "0x%02x 0x%02x 0x%02x\n", opc->opc1, opc->opc2, + opc->opc3); + return -1; + } +#if defined(PPC_DUMP_CPU) + if (opc1 != 0x00) { + if (opc->opc3 == 0xFF) { + if (opc->opc2 == 0xFF) { + printf(" %02x -- -- (%2d ----) : %s\n", + opc->opc1, opc->opc1, opc->oname); + } else { + printf(" %02x %02x -- (%2d %4d) : %s\n", + opc->opc1, opc->opc2, opc->opc1, opc->opc2, + opc->oname); + } + } else { + printf(" %02x %02x %02x (%2d %4d) : %s\n", + opc->opc1, opc->opc2, opc->opc3, + opc->opc1, (opc->opc3 << 5) | opc->opc2, + opc->oname); + } + } +#endif + } + } + fix_opcode_tables(env->opcodes); + fflush(stdout); + fflush(stderr); + + return 0; +} + +int cpu_ppc_register (CPUPPCState *env, ppc_def_t *def) +{ + env->msr_mask = def->msr_mask; + env->flags = def->flags; + if (create_ppc_opcodes(env, def) < 0) { + printf("Error creating opcodes table\n"); + fflush(stdout); + fflush(stderr); + return -1; + } + init_ppc_proc(env, def); +#if defined(PPC_DUMP_CPU) + dump_sprs(env); +#endif + fflush(stdout); + fflush(stderr); + + return 0; +} + +CPUPPCState *cpu_ppc_init(void) +{ + CPUPPCState *env; + + cpu_exec_init(); + + env = qemu_mallocz(sizeof(CPUPPCState)); + if (!env) + return NULL; + tlb_flush(env, 1); +#if defined (DO_SINGLE_STEP) && 0 + /* Single step trace mode */ + msr_se = 1; + msr_be = 1; +#endif + msr_fp = 1; /* Allow floating point exceptions */ + msr_me = 1; /* Allow machine check exceptions */ +#if defined(CONFIG_USER_ONLY) + msr_pr = 1; +#else + env->nip = 0xFFFFFFFC; +#endif + do_compute_hflags(env); + env->reserve = -1; + cpu_single_env = env; + return env; +} + +void cpu_ppc_close(CPUPPCState *env) +{ + /* Should also remove all opcode tables... */ + free(env); +} + +/*****************************************************************************/ +/* PowerPC CPU definitions */ +static ppc_def_t ppc_defs[] = +{ + /* Embedded PPC */ +#if defined (TODO) + /* PPC 401 */ + { + .name = "401", + .pvr = CPU_PPC_401, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_401, + .flags = PPC_FLAGS_401, + .msr_mask = xxx, + }, +#endif +#if defined (TODO) + /* IOP480 (401 microcontroler) */ + { + .name = "iop480", + .pvr = CPU_PPC_IOP480, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_401, + .flags = PPC_FLAGS_401, + .msr_mask = xxx, + }, +#endif +#if defined (TODO) + /* PPC 403 GA */ + { + .name = "403ga", + .pvr = CPU_PPC_403GA, + .pvr_mask = 0xFFFFFF00, + .insns_flags = PPC_INSNS_403, + .flags = PPC_FLAGS_403, + .msr_mask = 0x000000000007D23D, + }, +#endif +#if defined (TODO) + /* PPC 403 GB */ + { + .name = "403gb", + .pvr = CPU_PPC_403GB, + .pvr_mask = 0xFFFFFF00, + .insns_flags = PPC_INSNS_403, + .flags = PPC_FLAGS_403, + .msr_mask = 0x000000000007D23D, + }, +#endif +#if defined (TODO) + /* PPC 403 GC */ + { + .name = "403gc", + .pvr = CPU_PPC_403GC, + .pvr_mask = 0xFFFFFF00, + .insns_flags = PPC_INSNS_403, + .flags = PPC_FLAGS_403, + .msr_mask = 0x000000000007D23D, + }, +#endif +#if defined (TODO) + /* PPC 403 GCX */ + { + .name = "403gcx", + .pvr = CPU_PPC_403GCX, + .pvr_mask = 0xFFFFFF00, + .insns_flags = PPC_INSNS_403, + .flags = PPC_FLAGS_403, + .msr_mask = 0x000000000007D23D, + }, +#endif +#if defined (TODO) + /* PPC 405 CR */ + { + .name = "405cr", + .pvr = CPU_PPC_405, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_405, + .flags = PPC_FLAGS_405, + .msr_mask = 0x00000000020EFF30, + }, +#endif +#if defined (TODO) + /* PPC 405 GP */ + { + .name = "405gp", + .pvr = CPU_PPC_405, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_405, + .flags = PPC_FLAGS_405, + .msr_mask = 0x00000000020EFF30, + }, +#endif +#if defined (TODO) + /* PPC 405 EP */ + { + .name = "405ep", + .pvr = CPU_PPC_405EP, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_405, + .flags = PPC_FLAGS_405, + .msr_mask = 0x00000000020EFF30, + }, +#endif +#if defined (TODO) + /* PPC 405 GPR */ + { + .name = "405gpr", + .pvr = CPU_PPC_405GPR, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_405, + .flags = PPC_FLAGS_405, + .msr_mask = 0x00000000020EFF30, + }, +#endif +#if defined (TODO) + /* PPC 405 D2 */ + { + .name = "405d2", + .pvr = CPU_PPC_405D2, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_405, + .flags = PPC_FLAGS_405, + .msr_mask = 0x00000000020EFF30, + }, +#endif +#if defined (TODO) + /* PPC 405 D4 */ + { + .name = "405d4", + .pvr = CPU_PPC_405D4, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_405, + .flags = PPC_FLAGS_405, + .msr_mask = 0x00000000020EFF30, + }, +#endif +#if defined (TODO) + /* Npe405 H */ + { + .name = "Npe405H", + .pvr = CPU_PPC_NPE405H, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_405, + .flags = PPC_FLAGS_405, + .msr_mask = 0x00000000020EFF30, + }, +#endif +#if defined (TODO) + /* Npe405 L */ + { + .name = "Npe405L", + .pvr = CPU_PPC_NPE405L, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_405, + .flags = PPC_FLAGS_405, + .msr_mask = 0x00000000020EFF30, + }, +#endif +#if defined (TODO) + /* STB03xx */ + { + .name = "STB03", + .pvr = CPU_PPC_STB03, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_405, + .flags = PPC_FLAGS_405, + .msr_mask = 0x00000000020EFF30, + }, +#endif +#if defined (TODO) + /* STB04xx */ + { + .name = "STB04", + .pvr = CPU_PPC_STB04, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_405, + .flags = PPC_FLAGS_405, + .msr_mask = 0x00000000020EFF30, + }, +#endif +#if defined (TODO) + /* STB25xx */ + { + .name = "STB25", + .pvr = CPU_PPC_STB25, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_405, + .flags = PPC_FLAGS_405, + .msr_mask = 0x00000000020EFF30, + }, +#endif +#if defined (TODO) + /* PPC 440 EP */ + { + .name = "440ep", + .pvr = CPU_PPC_440EP, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_440, + .flags = PPC_FLAGS_440, + .msr_mask = 0x000000000006D630, + }, +#endif +#if defined (TODO) + /* PPC 440 GP */ + { + .name = "440gp", + .pvr = CPU_PPC_440GP, + .pvr_mask = 0xFFFFFF00, + .insns_flags = PPC_INSNS_440, + .flags = PPC_FLAGS_440, + .msr_mask = 0x000000000006D630, + }, +#endif +#if defined (TODO) + /* PPC 440 GX */ + { + .name = "440gx", + .pvr = CPU_PPC_440GX, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_405, + .flags = PPC_FLAGS_440, + .msr_mask = 0x000000000006D630, + }, +#endif + + /* 32 bits "classic" powerpc */ +#if defined (TODO) + /* PPC 601 */ + { + .name = "601", + .pvr = CPU_PPC_601, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_601, + .flags = PPC_FLAGS_601, + .msr_mask = 0x000000000000FD70, + }, +#endif +#if defined (TODO) + /* PPC 602 */ + { + .name = "602", + .pvr = CPU_PPC_602, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_602, + .flags = PPC_FLAGS_602, + .msr_mask = 0x0000000000C7FF73, + }, +#endif +#if defined (TODO) + /* PPC 603 */ + { + .name = "603", + .pvr = CPU_PPC_603, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_603, + .flags = PPC_FLAGS_603, + .msr_mask = 0x000000000007FF73, + }, +#endif +#if defined (TODO) + /* PPC 603e */ + { + .name = "603e", + .pvr = CPU_PPC_603E, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_603, + .flags = PPC_FLAGS_603, + .msr_mask = 0x000000000007FF73, + }, + { + .name = "Stretch", + .pvr = CPU_PPC_603E, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_603, + .flags = PPC_FLAGS_603, + .msr_mask = 0x000000000007FF73, + }, +#endif +#if defined (TODO) + /* PPC 603ev */ + { + .name = "603ev", + .pvr = CPU_PPC_603EV, + .pvr_mask = 0xFFFFF000, + .insns_flags = PPC_INSNS_603, + .flags = PPC_FLAGS_603, + .msr_mask = 0x000000000007FF73, + }, +#endif +#if defined (TODO) + /* PPC 603r */ + { + .name = "603r", + .pvr = CPU_PPC_603R, + .pvr_mask = 0xFFFFF000, + .insns_flags = PPC_INSNS_603, + .flags = PPC_FLAGS_603, + .msr_mask = 0x000000000007FF73, + }, + { + .name = "Goldeneye", + .pvr = CPU_PPC_603R, + .pvr_mask = 0xFFFFF000, + .insns_flags = PPC_INSNS_603, + .flags = PPC_FLAGS_603, + .msr_mask = 0x000000000007FF73, + }, +#endif +#if defined (TODO) + /* XXX: TODO: according to Motorola UM, this is a derivative to 603e */ + { + .name = "G2", + .pvr = CPU_PPC_G2, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_G2, + .flags = PPC_FLAGS_G2, + .msr_mask = 0x000000000006FFF2, + }, + { /* Same as G2, with LE mode support */ + .name = "G2le", + .pvr = CPU_PPC_G2LE, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_G2, + .flags = PPC_FLAGS_G2, + .msr_mask = 0x000000000007FFF3, + }, +#endif + /* PPC 604 */ + { + .name = "604", + .pvr = CPU_PPC_604, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_604, + .flags = PPC_FLAGS_604, + .msr_mask = 0x000000000005FF77, + }, + /* PPC 604e */ + { + .name = "604e", + .pvr = CPU_PPC_604E, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_604, + .flags = PPC_FLAGS_604, + .msr_mask = 0x000000000005FF77, + }, + /* PPC 604r */ + { + .name = "604r", + .pvr = CPU_PPC_604R, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_604, + .flags = PPC_FLAGS_604, + .msr_mask = 0x000000000005FF77, + }, + /* generic G3 */ + { + .name = "G3", + .pvr = CPU_PPC_74x, + .pvr_mask = 0xFFFFF000, + .insns_flags = PPC_INSNS_7x0, + .flags = PPC_FLAGS_7x0, + .msr_mask = 0x000000000007FF77, + }, +#if defined (TODO) + /* MPC740 (G3) */ + { + .name = "740", + .pvr = CPU_PPC_74x, + .pvr_mask = 0xFFFFF000, + .insns_flags = PPC_INSNS_7x0, + .flags = PPC_FLAGS_7x0, + .msr_mask = 0x000000000007FF77, + }, + { + .name = "Arthur", + .pvr = CPU_PPC_74x, + .pvr_mask = 0xFFFFF000, + .insns_flags = PPC_INSNS_7x0, + .flags = PPC_FLAGS_7x0, + .msr_mask = 0x000000000007FF77, + }, +#endif +#if defined (TODO) + /* MPC745 (G3) */ + { + .name = "745", + .pvr = CPU_PPC_74x, + .pvr_mask = 0xFFFFF000, + .insns_flags = PPC_INSNS_7x5, + .flags = PPC_FLAGS_7x5, + .msr_mask = 0x000000000007FF77, + }, + { + .name = "Goldfinger", + .pvr = CPU_PPC_74x, + .pvr_mask = 0xFFFFF000, + .insns_flags = PPC_INSNS_7x5, + .flags = PPC_FLAGS_7x5, + .msr_mask = 0x000000000007FF77, + }, +#endif + /* MPC750 (G3) */ + { + .name = "750", + .pvr = CPU_PPC_74x, + .pvr_mask = 0xFFFFF000, + .insns_flags = PPC_INSNS_7x0, + .flags = PPC_FLAGS_7x0, + .msr_mask = 0x000000000007FF77, + }, +#if defined (TODO) + /* MPC755 (G3) */ + { + .name = "755", + .pvr = CPU_PPC_755, + .pvr_mask = 0xFFFFF000, + .insns_flags = PPC_INSNS_7x5, + .flags = PPC_FLAGS_7x5, + .msr_mask = 0x000000000007FF77, + }, +#endif +#if defined (TODO) + /* MPC740P (G3) */ + { + .name = "740p", + .pvr = CPU_PPC_74xP, + .pvr_mask = 0xFFFFF000, + .insns_flags = PPC_INSNS_7x0, + .flags = PPC_FLAGS_7x0, + .msr_mask = 0x000000000007FF77, + }, + { + .name = "Conan/Doyle", + .pvr = CPU_PPC_74xP, + .pvr_mask = 0xFFFFF000, + .insns_flags = PPC_INSNS_7x0, + .flags = PPC_FLAGS_7x0, + .msr_mask = 0x000000000007FF77, + }, +#endif +#if defined (TODO) + /* MPC745P (G3) */ + { + .name = "745p", + .pvr = CPU_PPC_74xP, + .pvr_mask = 0xFFFFF000, + .insns_flags = PPC_INSNS_7x5, + .flags = PPC_FLAGS_7x5, + .msr_mask = 0x000000000007FF77, + }, +#endif + /* MPC750P (G3) */ + { + .name = "750p", + .pvr = CPU_PPC_74xP, + .pvr_mask = 0xFFFFF000, + .insns_flags = PPC_INSNS_7x0, + .flags = PPC_FLAGS_7x0, + .msr_mask = 0x000000000007FF77, + }, +#if defined (TODO) + /* MPC755P (G3) */ + { + .name = "755p", + .pvr = CPU_PPC_74xP, + .pvr_mask = 0xFFFFF000, + .insns_flags = PPC_INSNS_7x5, + .flags = PPC_FLAGS_7x5, + .msr_mask = 0x000000000007FF77, + }, +#endif + /* IBM 750CXe (G3 embedded) */ + { + .name = "750cxe", + .pvr = CPU_PPC_750CXE, + .pvr_mask = 0xFFFFF000, + .insns_flags = PPC_INSNS_7x0, + .flags = PPC_FLAGS_7x0, + .msr_mask = 0x000000000007FF77, + }, + /* IBM 750FX (G3 embedded) */ + { + .name = "750fx", + .pvr = CPU_PPC_750FX, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_7x0, + .flags = PPC_FLAGS_7x0, + .msr_mask = 0x000000000007FF77, + }, + /* IBM 750GX (G3 embedded) */ + { + .name = "750gx", + .pvr = CPU_PPC_750GX, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_7x0, + .flags = PPC_FLAGS_7x0, + .msr_mask = 0x000000000007FF77, + }, +#if defined (TODO) + /* generic G4 */ + { + .name = "G4", + .pvr = CPU_PPC_7400, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_74xx, + .flags = PPC_FLAGS_74xx, + .msr_mask = 0x000000000205FF77, + }, +#endif +#if defined (TODO) + /* PPC 7400 (G4) */ + { + .name = "7400", + .pvr = CPU_PPC_7400, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_74xx, + .flags = PPC_FLAGS_74xx, + .msr_mask = 0x000000000205FF77, + }, + { + .name = "Max", + .pvr = CPU_PPC_7400, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_74xx, + .flags = PPC_FLAGS_74xx, + .msr_mask = 0x000000000205FF77, + }, +#endif +#if defined (TODO) + /* PPC 7410 (G4) */ + { + .name = "7410", + .pvr = CPU_PPC_7410, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_74xx, + .flags = PPC_FLAGS_74xx, + .msr_mask = 0x000000000205FF77, + }, + { + .name = "Nitro", + .pvr = CPU_PPC_7410, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_74xx, + .flags = PPC_FLAGS_74xx, + .msr_mask = 0x000000000205FF77, + }, +#endif + /* XXX: 7441 */ + /* XXX: 7445 */ + /* XXX: 7447 */ + /* XXX: 7447A */ +#if defined (TODO) + /* PPC 7450 (G4) */ + { + .name = "7450", + .pvr = CPU_PPC_7450, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_74xx, + .flags = PPC_FLAGS_74xx, + .msr_mask = 0x000000000205FF77, + }, + { + .name = "Vger", + .pvr = CPU_PPC_7450, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_74xx, + .flags = PPC_FLAGS_74xx, + .msr_mask = 0x000000000205FF77, + }, +#endif + /* XXX: 7451 */ +#if defined (TODO) + /* PPC 7455 (G4) */ + { + .name = "7455", + .pvr = CPU_PPC_7455, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_74xx, + .flags = PPC_FLAGS_74xx, + .msr_mask = 0x000000000205FF77, + }, + { + .name = "Apollo 6", + .pvr = CPU_PPC_7455, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_74xx, + .flags = PPC_FLAGS_74xx, + .msr_mask = 0x000000000205FF77, + }, +#endif +#if defined (TODO) + /* PPC 7457 (G4) */ + { + .name = "7457", + .pvr = CPU_PPC_7457, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_74xx, + .flags = PPC_FLAGS_74xx, + .msr_mask = 0x000000000205FF77, + }, + { + .name = "Apollo 7", + .pvr = CPU_PPC_7457, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_74xx, + .flags = PPC_FLAGS_74xx, + .msr_mask = 0x000000000205FF77, + }, +#endif +#if defined (TODO) + /* PPC 7457A (G4) */ + { + .name = "7457A", + .pvr = CPU_PPC_7457A, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_74xx, + .flags = PPC_FLAGS_74xx, + .msr_mask = 0x000000000205FF77, + }, + { + .name = "Apollo 7 PM", + .pvr = CPU_PPC_7457A, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_74xx, + .flags = PPC_FLAGS_74xx, + .msr_mask = 0x000000000205FF77, + }, +#endif + /* 64 bits PPC */ +#if defined (TODO) + /* PPC 620 */ + { + .name = "620", + .pvr = CPU_PPC_620, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_620, + .flags = PPC_FLAGS_620, + .msr_mask = 0x800000000005FF73, + }, +#endif +#if defined (TODO) + /* PPC 630 (POWER3) */ + { + .name = "630", + .pvr = CPU_PPC_630, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_630, + .flags = PPC_FLAGS_630, + .msr_mask = xxx, + } + { + .name = "POWER3", + .pvr = CPU_PPC_630, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_630, + .flags = PPC_FLAGS_630, + .msr_mask = xxx, + } +#endif +#if defined (TODO) + /* PPC 631 (Power 3+)*/ + { + .name = "631", + .pvr = CPU_PPC_631, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_631, + .flags = PPC_FLAGS_631, + .msr_mask = xxx, + }, + { + .name = "POWER3+", + .pvr = CPU_PPC_631, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_631, + .flags = PPC_FLAGS_631, + .msr_mask = xxx, + }, +#endif +#if defined (TODO) + /* POWER4 */ + { + .name = "POWER4", + .pvr = CPU_PPC_POWER4, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_POWER4, + .flags = PPC_FLAGS_POWER4, + .msr_mask = xxx, + }, +#endif +#if defined (TODO) + /* POWER4p */ + { + .name = "POWER4+", + .pvr = CPU_PPC_POWER4P, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_POWER4, + .flags = PPC_FLAGS_POWER4, + .msr_mask = xxx, + }, +#endif +#if defined (TODO) + /* POWER5 */ + { + .name = "POWER5", + .pvr = CPU_PPC_POWER5, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_POWER5, + .flags = PPC_FLAGS_POWER5, + .msr_mask = xxx, + }, +#endif +#if defined (TODO) + /* POWER5+ */ + { + .name = "POWER5+", + .pvr = CPU_PPC_POWER5P, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_POWER5, + .flags = PPC_FLAGS_POWER5, + .msr_mask = xxx, + }, +#endif +#if defined (TODO) + /* PPC 970 */ + { + .name = "970", + .pvr = CPU_PPC_970, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_970, + .flags = PPC_FLAGS_970, + .msr_mask = 0x900000000204FF36, + }, +#endif +#if defined (TODO) + /* PPC 970FX (G5) */ + { + .name = "970fx", + .pvr = CPU_PPC_970FX, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_970FX, + .flags = PPC_FLAGS_970FX, + .msr_mask = 0x800000000204FF36, + }, +#endif +#if defined (TODO) + /* RS64 (Apache/A35) */ + /* This one seems to support the whole POWER2 instruction set + * and the PowerPC 64 one. + */ + { + .name = "RS64", + .pvr = CPU_PPC_RS64, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_RS64, + .flags = PPC_FLAGS_RS64, + .msr_mask = xxx, + }, + { + .name = "Apache", + .pvr = CPU_PPC_RS64, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_RS64, + .flags = PPC_FLAGS_RS64, + .msr_mask = xxx, + }, + { + .name = "A35", + .pvr = CPU_PPC_RS64, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_RS64, + .flags = PPC_FLAGS_RS64, + .msr_mask = xxx, + }, +#endif +#if defined (TODO) + /* RS64-II (NorthStar/A50) */ + { + .name = "RS64-II", + .pvr = CPU_PPC_RS64II, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_RS64, + .flags = PPC_FLAGS_RS64, + .msr_mask = xxx, + }, + { + .name = "NortStar", + .pvr = CPU_PPC_RS64II, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_RS64, + .flags = PPC_FLAGS_RS64, + .msr_mask = xxx, + }, + { + .name = "A50", + .pvr = CPU_PPC_RS64II, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_RS64, + .flags = PPC_FLAGS_RS64, + .msr_mask = xxx, + }, +#endif +#if defined (TODO) + /* RS64-III (Pulsar) */ + { + .name = "RS64-III", + .pvr = CPU_PPC_RS64III, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_RS64, + .flags = PPC_FLAGS_RS64, + .msr_mask = xxx, + }, + { + .name = "Pulsar", + .pvr = CPU_PPC_RS64III, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_RS64, + .flags = PPC_FLAGS_RS64, + .msr_mask = xxx, + }, +#endif +#if defined (TODO) + /* RS64-IV (IceStar/IStar/SStar) */ + { + .name = "RS64-IV", + .pvr = CPU_PPC_RS64IV, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_RS64, + .flags = PPC_FLAGS_RS64, + .msr_mask = xxx, + }, + { + .name = "IceStar", + .pvr = CPU_PPC_RS64IV, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_RS64, + .flags = PPC_FLAGS_RS64, + .msr_mask = xxx, + }, + { + .name = "IStar", + .pvr = CPU_PPC_RS64IV, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_RS64, + .flags = PPC_FLAGS_RS64, + .msr_mask = xxx, + }, + { + .name = "SStar", + .pvr = CPU_PPC_RS64IV, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_RS64, + .flags = PPC_FLAGS_RS64, + .msr_mask = xxx, + }, +#endif + /* POWER */ +#if defined (TODO) + /* Original POWER */ + { + .name = "POWER", + .pvr = CPU_POWER, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_POWER, + .flags = PPC_FLAGS_POWER, + .msr_mask = xxx, + }, +#endif +#if defined (TODO) + /* POWER2 */ + { + .name = "POWER2", + .pvr = CPU_POWER2, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_POWER, + .flags = PPC_FLAGS_POWER, + .msr_mask = xxx, + }, +#endif + /* Generic PowerPCs */ +#if defined (TODO) + { + .name = "ppc64", + .pvr = CPU_PPC_970, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_PPC64, + .flags = PPC_FLAGS_PPC64, + .msr_mask = 0xA00000000204FF36, + }, +#endif + { + .name = "ppc32", + .pvr = CPU_PPC_604, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_PPC32, + .flags = PPC_FLAGS_PPC32, + .msr_mask = 0x000000000005FF77, + }, + /* Fallback */ + { + .name = "ppc", + .pvr = CPU_PPC_604, + .pvr_mask = 0xFFFF0000, + .insns_flags = PPC_INSNS_PPC32, + .flags = PPC_FLAGS_PPC32, + .msr_mask = 0x000000000005FF77, + }, +}; + +int ppc_find_by_name (const unsigned char *name, ppc_def_t **def) +{ + int i, ret; + + ret = -1; + *def = NULL; + for (i = 0; strcmp(ppc_defs[i].name, "ppc") != 0; i++) { + if (strcasecmp(name, ppc_defs[i].name) == 0) { + *def = &ppc_defs[i]; + ret = 0; + break; + } + } + + return ret; +} + +int ppc_find_by_pvr (uint32_t pvr, ppc_def_t **def) +{ + int i, ret; + + ret = -1; + *def = NULL; + for (i = 0; ppc_defs[i].name != NULL; i++) { + if ((pvr & ppc_defs[i].pvr_mask) == + (ppc_defs[i].pvr & ppc_defs[i].pvr_mask)) { + *def = &ppc_defs[i]; + ret = 0; + break; + } + } + + return ret; +} + +void ppc_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) +{ + int i; + + for (i = 0; ; i++) { + (*cpu_fprintf)(f, "PowerPC '%s' PVR %08x mask %08x\n", + ppc_defs[i].name, + ppc_defs[i].pvr, ppc_defs[i].pvr_mask); + if (strcmp(ppc_defs[i].name, "ppc") == 0) + break; + } +} diff --git a/qemu/target-sparc/cpu.h b/qemu/target-sparc/cpu.h index b556e23..999d5d7 100644 --- a/qemu/target-sparc/cpu.h +++ b/qemu/target-sparc/cpu.h @@ -6,12 +6,13 @@ #if !defined(TARGET_SPARC64) #define TARGET_LONG_BITS 32 #define TARGET_FPREGS 32 -#define TARGET_FPREG_T float +#define TARGET_PAGE_BITS 12 /* 4k */ #else #define TARGET_LONG_BITS 64 #define TARGET_FPREGS 64 -#define TARGET_FPREG_T double +#define TARGET_PAGE_BITS 12 /* XXX */ #endif +#define TARGET_FPREG_T float #include "cpu-defs.h" @@ -22,6 +23,7 @@ /*#define EXCP_INTERRUPT 0x100*/ /* trap definitions */ +#ifndef TARGET_SPARC64 #define TT_TFAULT 0x01 #define TT_ILL_INSN 0x02 #define TT_PRIV_INSN 0x03 @@ -33,6 +35,25 @@ #define TT_EXTINT 0x10 #define TT_DIV_ZERO 0x2a #define TT_TRAP 0x80 +#else +#define TT_TFAULT 0x08 +#define TT_TMISS 0x09 +#define TT_ILL_INSN 0x10 +#define TT_PRIV_INSN 0x11 +#define TT_NFPU_INSN 0x20 +#define TT_FP_EXCP 0x21 +#define TT_CLRWIN 0x24 +#define TT_DIV_ZERO 0x28 +#define TT_DFAULT 0x30 +#define TT_DMISS 0x31 +#define TT_DPROT 0x32 +#define TT_PRIV_ACT 0x37 +#define TT_EXTINT 0x40 +#define TT_SPILL 0x80 +#define TT_FILL 0xc0 +#define TT_WOTHER 0x10 +#define TT_TRAP 0x100 +#endif #define PSR_NEG (1<<23) #define PSR_ZERO (1<<22) @@ -49,6 +70,17 @@ /* Trap base register */ #define TBR_BASE_MASK 0xfffff000 +#if defined(TARGET_SPARC64) +#define PS_IG (1<<11) +#define PS_MG (1<<10) +#define PS_RED (1<<5) +#define PS_PEF (1<<4) +#define PS_AM (1<<3) +#define PS_PRIV (1<<2) +#define PS_IE (1<<1) +#define PS_AG (1<<0) +#endif + /* Fcc */ #define FSR_RD1 (1<<31) #define FSR_RD0 (1<<30) @@ -119,15 +151,15 @@ typedef struct CPUSPARCState { target_ulong npc; /* next program counter */ target_ulong y; /* multiply/divide register */ uint32_t psr; /* processor state register */ - uint32_t fsr; /* FPU state register */ + target_ulong fsr; /* FPU state register */ uint32_t cwp; /* index of current register window (extracted from PSR) */ uint32_t wim; /* window invalid mask */ - uint32_t tbr; /* trap base register */ + target_ulong tbr; /* trap base register */ int psrs; /* supervisor mode (extracted from PSR) */ int psrps; /* previous supervisor mode */ int psret; /* enable traps */ - int psrpil; /* interrupt level */ + uint32_t psrpil; /* interrupt level */ int psref; /* enable fpu */ jmp_buf jmp_env; int user_mode_only; @@ -144,19 +176,51 @@ typedef struct CPUSPARCState { context) */ unsigned long mem_write_pc; /* host pc at which the memory was written */ - unsigned long mem_write_vaddr; /* target virtual addr at which the + target_ulong mem_write_vaddr; /* target virtual addr at which the memory was written */ /* 0 = kernel, 1 = user (may have 2 = kernel code, 3 = user code ?) */ CPUTLBEntry tlb_read[2][CPU_TLB_SIZE]; CPUTLBEntry tlb_write[2][CPU_TLB_SIZE]; /* MMU regs */ +#if defined(TARGET_SPARC64) + uint64_t lsu; +#define DMMU_E 0x8 +#define IMMU_E 0x4 + uint64_t immuregs[16]; + uint64_t dmmuregs[16]; + uint64_t itlb_tag[64]; + uint64_t itlb_tte[64]; + uint64_t dtlb_tag[64]; + uint64_t dtlb_tte[64]; +#else uint32_t mmuregs[16]; +#endif /* temporary float registers */ - float ft0, ft1, ft2; - double dt0, dt1, dt2; + float ft0, ft1; + double dt0, dt1; float_status fp_status; #if defined(TARGET_SPARC64) - target_ulong t0, t1, t2; +#define MAXTL 4 + uint64_t t0, t1, t2; + uint64_t tpc[MAXTL]; + uint64_t tnpc[MAXTL]; + uint64_t tstate[MAXTL]; + uint32_t tt[MAXTL]; + uint32_t xcc; /* Extended integer condition codes */ + uint32_t asi; + uint32_t pstate; + uint32_t tl; + uint32_t cansave, canrestore, otherwin, wstate, cleanwin; + uint64_t agregs[8]; /* alternate general registers */ + uint64_t bgregs[8]; /* backup for normal global registers */ + uint64_t igregs[8]; /* interrupt general registers */ + uint64_t mgregs[8]; /* mmu general registers */ + uint64_t version; + uint64_t fprs; + uint64_t tick_cmpr, stick_cmpr; +#endif +#if !defined(TARGET_SPARC64) && !defined(reg_T2) + target_ulong t2; #endif /* ice debug support */ @@ -165,6 +229,24 @@ typedef struct CPUSPARCState { int singlestep_enabled; /* XXX: should use CPU single step mode instead */ } CPUSPARCState; +#if defined(TARGET_SPARC64) +#define GET_FSR32(env) (env->fsr & 0xcfc1ffff) +#define PUT_FSR32(env, val) do { uint32_t _tmp = val; \ + env->fsr = (_tmp & 0xcfc1c3ff) | (env->fsr & 0x3f00000000ULL); \ + } while (0) +#define GET_FSR64(env) (env->fsr & 0x3fcfc1ffffULL) +#define PUT_FSR64(env, val) do { uint64_t _tmp = val; \ + env->fsr = _tmp & 0x3fcfc1c3ffULL; \ + } while (0) +// Manuf 0x17, version 0x11, mask 0 (UltraSparc-II) +#define GET_VER(env) ((0x17ULL << 48) | (0x11ULL << 32) | \ + (0 << 24) | (MAXTL << 8) | (NWINDOWS - 1)) +#else +#define GET_FSR32(env) (env->fsr) +#define PUT_FSR32(env, val) do { uint32_t _tmp = val; \ + env->fsr = _tmp & 0xcfc1ffff; \ + } while (0) +#endif CPUSPARCState *cpu_sparc_init(void); int cpu_sparc_exec(CPUSPARCState *s); @@ -194,10 +276,17 @@ void cpu_set_cwp(CPUSPARCState *env1, int new_cwp); cpu_set_cwp(env, _tmp & PSR_CWP & (NWINDOWS - 1)); \ } while (0) +#ifdef TARGET_SPARC64 +#define GET_CCR(env) ((env->xcc << 4) | (env->psr & PSR_ICC)) +#define PUT_CCR(env, val) do { int _tmp = val; \ + env->xcc = _tmp >> 4; \ + env->psr = (_tmp & 0xf) << 20; \ + } while (0) +#endif + struct siginfo; int cpu_sparc_signal_handler(int hostsignum, struct siginfo *info, void *puc); -#define TARGET_PAGE_BITS 12 /* 4k */ #include "cpu-all.h" #endif diff --git a/qemu/target-sparc/exec.h b/qemu/target-sparc/exec.h index 5e6c062..942b811 100644 --- a/qemu/target-sparc/exec.h +++ b/qemu/target-sparc/exec.h @@ -1,23 +1,41 @@ #ifndef EXEC_SPARC_H #define EXEC_SPARC_H 1 #include "dyngen-exec.h" +#include "config.h" register struct CPUSPARCState *env asm(AREG0); #ifdef TARGET_SPARC64 #define T0 (env->t0) #define T1 (env->t1) #define T2 (env->t2) +#define REGWPTR env->regwptr #else register uint32_t T0 asm(AREG1); register uint32_t T1 asm(AREG2); + +#undef REG_REGWPTR // Broken +#ifdef REG_REGWPTR +register uint32_t *REGWPTR asm(AREG3); +#define reg_REGWPTR + +#ifdef AREG4 +register uint32_t T2 asm(AREG4); +#define reg_T2 +#else +#define T2 (env->t2) +#endif + +#else +#define REGWPTR env->regwptr register uint32_t T2 asm(AREG3); +#define reg_T2 +#endif #endif + #define FT0 (env->ft0) #define FT1 (env->ft1) -#define FT2 (env->ft2) #define DT0 (env->dt0) #define DT1 (env->dt1) -#define DT2 (env->dt2) #include "cpu.h" #include "exec-all.h" @@ -38,14 +56,27 @@ void do_fsqrts(void); void do_fsqrtd(void); void do_fcmps(void); void do_fcmpd(void); +#ifdef TARGET_SPARC64 +void do_fabsd(void); +void do_fcmps_fcc1(void); +void do_fcmpd_fcc1(void); +void do_fcmps_fcc2(void); +void do_fcmpd_fcc2(void); +void do_fcmps_fcc3(void); +void do_fcmpd_fcc3(void); +void do_popc(); +void do_wrpstate(); +void do_done(); +void do_retry(); +#endif void do_ldd_kernel(target_ulong addr); void do_ldd_user(target_ulong addr); void do_ldd_raw(target_ulong addr); void do_interrupt(int intno); void raise_exception(int tt); void memcpy32(target_ulong *dst, const target_ulong *src); -target_ulong mmu_probe(target_ulong address, int mmulev); -void dump_mmu(void); +target_ulong mmu_probe(CPUState *env, target_ulong address, int mmulev); +void dump_mmu(CPUState *env); void helper_debug(); void do_wrpsr(); void do_rdpsr(); diff --git a/qemu/target-sparc/fbranch_template.h b/qemu/target-sparc/fbranch_template.h new file mode 100644 index 0000000..e6bf9a2 --- /dev/null +++ b/qemu/target-sparc/fbranch_template.h @@ -0,0 +1,89 @@ +/* FCC1:FCC0: 0 =, 1 <, 2 >, 3 u */ + +void OPPROTO glue(op_eval_fbne, FCC)(void) +{ +// !0 + T2 = FFLAG_SET(FSR_FCC0) | FFLAG_SET(FSR_FCC1); /* L or G or U */ +} + +void OPPROTO glue(op_eval_fblg, FCC)(void) +{ +// 1 or 2 + T2 = FFLAG_SET(FSR_FCC0) ^ FFLAG_SET(FSR_FCC1); +} + +void OPPROTO glue(op_eval_fbul, FCC)(void) +{ +// 1 or 3 + T2 = FFLAG_SET(FSR_FCC0); +} + +void OPPROTO glue(op_eval_fbl, FCC)(void) +{ +// 1 + T2 = FFLAG_SET(FSR_FCC0) & !FFLAG_SET(FSR_FCC1); +} + +void OPPROTO glue(op_eval_fbug, FCC)(void) +{ +// 2 or 3 + T2 = FFLAG_SET(FSR_FCC1); +} + +void OPPROTO glue(op_eval_fbg, FCC)(void) +{ +// 2 + T2 = !FFLAG_SET(FSR_FCC0) & FFLAG_SET(FSR_FCC1); +} + +void OPPROTO glue(op_eval_fbu, FCC)(void) +{ +// 3 + T2 = FFLAG_SET(FSR_FCC0) & FFLAG_SET(FSR_FCC1); +} + +void OPPROTO glue(op_eval_fbe, FCC)(void) +{ +// 0 + T2 = !FFLAG_SET(FSR_FCC0) & !FFLAG_SET(FSR_FCC1); +} + +void OPPROTO glue(op_eval_fbue, FCC)(void) +{ +// 0 or 3 + T2 = !(FFLAG_SET(FSR_FCC1) ^ FFLAG_SET(FSR_FCC0)); + FORCE_RET(); +} + +void OPPROTO glue(op_eval_fbge, FCC)(void) +{ +// 0 or 2 + T2 = !FFLAG_SET(FSR_FCC0); +} + +void OPPROTO glue(op_eval_fbuge, FCC)(void) +{ +// !1 + T2 = !(FFLAG_SET(FSR_FCC0) & !FFLAG_SET(FSR_FCC1)); +} + +void OPPROTO glue(op_eval_fble, FCC)(void) +{ +// 0 or 1 + T2 = !FFLAG_SET(FSR_FCC1); +} + +void OPPROTO glue(op_eval_fbule, FCC)(void) +{ +// !2 + T2 = !(!FFLAG_SET(FSR_FCC0) & FFLAG_SET(FSR_FCC1)); +} + +void OPPROTO glue(op_eval_fbo, FCC)(void) +{ +// !3 + T2 = !(FFLAG_SET(FSR_FCC0) & FFLAG_SET(FSR_FCC1)); +} + +#undef FCC +#undef FFLAG_SET diff --git a/qemu/target-sparc/fop_template.h b/qemu/target-sparc/fop_template.h index eb1e1e3..74988f7 100644 --- a/qemu/target-sparc/fop_template.h +++ b/qemu/target-sparc/fop_template.h @@ -40,16 +40,6 @@ void OPPROTO glue(op_store_FT1_fpr_fpr, REGNAME)(void) REG = FT1; } -void OPPROTO glue(op_load_fpr_FT2_fpr, REGNAME)(void) -{ - FT2 = REG; -} - -void OPPROTO glue(op_store_FT2_fpr_fpr, REGNAME)(void) -{ - REG = FT2; -} - /* double floating point registers moves */ void OPPROTO glue(op_load_fpr_DT0_fpr, REGNAME)(void) { @@ -87,23 +77,5 @@ void OPPROTO glue(op_store_DT1_fpr_fpr, REGNAME)(void) *p = u.l.upper; } -void OPPROTO glue(op_load_fpr_DT2_fpr, REGNAME)(void) -{ - CPU_DoubleU u; - uint32_t *p = (uint32_t *)® - u.l.lower = *(p +1); - u.l.upper = *p; - DT2 = u.d; -} - -void OPPROTO glue(op_store_DT2_fpr_fpr, REGNAME)(void) -{ - CPU_DoubleU u; - uint32_t *p = (uint32_t *)® - u.d = DT2; - *(p +1) = u.l.lower; - *p = u.l.upper; -} - #undef REG #undef REGNAME diff --git a/qemu/target-sparc/helper.c b/qemu/target-sparc/helper.c index 9fd5f21..78a033b 100644 --- a/qemu/target-sparc/helper.c +++ b/qemu/target-sparc/helper.c @@ -1,7 +1,7 @@ /* * sparc helpers * - * Copyright (c) 2003 Fabrice Bellard + * Copyright (c) 2003-2005 Fabrice Bellard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -17,9 +17,17 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "exec.h" +#include +#include +#include +#include +#include +#include +#include + +#include "cpu.h" +#include "exec-all.h" -//#define DEBUG_PCALL //#define DEBUG_MMU /* Sparc MMU emulation */ @@ -43,7 +51,6 @@ void cpu_unlock(void) int cpu_sparc_handle_mmu_fault(CPUState *env, target_ulong address, int rw, int is_user, int is_softmmu) { - env->mmuregs[4] = address; if (rw & 2) env->exception_index = TT_TFAULT; else @@ -53,55 +60,10 @@ int cpu_sparc_handle_mmu_fault(CPUState *env, target_ulong address, int rw, #else -#define MMUSUFFIX _mmu -#define GETPC() (__builtin_return_address(0)) - -#define SHIFT 0 -#include "softmmu_template.h" - -#define SHIFT 1 -#include "softmmu_template.h" - -#define SHIFT 2 -#include "softmmu_template.h" - -#define SHIFT 3 -#include "softmmu_template.h" - - -/* try to fill the TLB and return an exception if error. If retaddr is - NULL, it means that the function was called in C code (i.e. not - from generated code or from helper.c) */ -/* XXX: fix it to restore all registers */ -void tlb_fill(target_ulong addr, int is_write, int is_user, void *retaddr) -{ - TranslationBlock *tb; - int ret; - unsigned long pc; - CPUState *saved_env; - - /* XXX: hack to restore env in all cases, even if not called from - generated code */ - saved_env = env; - env = cpu_single_env; - - ret = cpu_sparc_handle_mmu_fault(env, addr, is_write, is_user, 1); - if (ret) { - if (retaddr) { - /* now we have a real cpu fault */ - pc = (unsigned long)retaddr; - tb = tb_find_pc(pc); - if (tb) { - /* the PC is inside the translated code. It means that we have - a virtual CPU fault */ - cpu_restore_state(tb, env, pc, (void *)T2); - } - } - cpu_loop_exit(); - } - env = saved_env; -} - +#ifndef TARGET_SPARC64 +/* + * Sparc V8 Reference MMU (SRMMU) + */ static const int access_table[8][8] = { { 0, 0, 0, 0, 2, 0, 3, 3 }, { 0, 0, 0, 0, 2, 0, 0, 0 }, @@ -268,89 +230,175 @@ int cpu_sparc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, return 1; } } -#endif - -void memcpy32(target_ulong *dst, const target_ulong *src) +#else +/* + * UltraSparc IIi I/DMMUs + */ +static int get_physical_address_data(CPUState *env, target_phys_addr_t *physical, int *prot, + int *access_index, target_ulong address, int rw, + int is_user) { - dst[0] = src[0]; - dst[1] = src[1]; - dst[2] = src[2]; - dst[3] = src[3]; - dst[4] = src[4]; - dst[5] = src[5]; - dst[6] = src[6]; - dst[7] = src[7]; + target_ulong mask; + unsigned int i; + + if ((env->lsu & DMMU_E) == 0) { /* DMMU disabled */ + *physical = address; + *prot = PAGE_READ | PAGE_WRITE; + return 0; + } + + for (i = 0; i < 64; i++) { + switch ((env->dtlb_tte[i] >> 61) & 3) { + default: + case 0x0: // 8k + mask = 0xffffffffffffe000ULL; + break; + case 0x1: // 64k + mask = 0xffffffffffff0000ULL; + break; + case 0x2: // 512k + mask = 0xfffffffffff80000ULL; + break; + case 0x3: // 4M + mask = 0xffffffffffc00000ULL; + break; + } + // ctx match, vaddr match? + if (env->dmmuregs[1] == (env->dtlb_tag[i] & 0x1fff) && + (address & mask) == (env->dtlb_tag[i] & ~0x1fffULL)) { + // valid, access ok? + if ((env->dtlb_tte[i] & 0x8000000000000000ULL) == 0 || + ((env->dtlb_tte[i] & 0x4) && is_user) || + (!(env->dtlb_tte[i] & 0x2) && (rw == 1))) { + if (env->dmmuregs[3]) /* Fault status register */ + env->dmmuregs[3] = 2; /* overflow (not read before another fault) */ + env->dmmuregs[3] |= (is_user << 3) | ((rw == 1) << 2) | 1; + env->dmmuregs[4] = address; /* Fault address register */ + env->exception_index = TT_DFAULT; +#ifdef DEBUG_MMU + printf("DFAULT at 0x%llx\n", address); +#endif + return 1; + } + *physical = (env->dtlb_tte[i] & mask & 0x1fffffff000ULL) + (address & ~mask & 0x1fffffff000ULL); + *prot = PAGE_READ; + if (env->dtlb_tte[i] & 0x2) + *prot |= PAGE_WRITE; + return 0; + } + } +#ifdef DEBUG_MMU + printf("DMISS at 0x%llx\n", address); +#endif + env->exception_index = TT_DMISS; + return 1; } -void set_cwp(int new_cwp) +static int get_physical_address_code(CPUState *env, target_phys_addr_t *physical, int *prot, + int *access_index, target_ulong address, int rw, + int is_user) { - /* put the modified wrap registers at their proper location */ - if (env->cwp == (NWINDOWS - 1)) - memcpy32(env->regbase, env->regbase + NWINDOWS * 16); - env->cwp = new_cwp; - /* put the wrap registers at their temporary location */ - if (new_cwp == (NWINDOWS - 1)) - memcpy32(env->regbase + NWINDOWS * 16, env->regbase); - env->regwptr = env->regbase + (new_cwp * 16); + target_ulong mask; + unsigned int i; + + if ((env->lsu & IMMU_E) == 0) { /* IMMU disabled */ + *physical = address; + *prot = PAGE_READ; + return 0; + } + + for (i = 0; i < 64; i++) { + switch ((env->itlb_tte[i] >> 61) & 3) { + default: + case 0x0: // 8k + mask = 0xffffffffffffe000ULL; + break; + case 0x1: // 64k + mask = 0xffffffffffff0000ULL; + break; + case 0x2: // 512k + mask = 0xfffffffffff80000ULL; + break; + case 0x3: // 4M + mask = 0xffffffffffc00000ULL; + break; + } + // ctx match, vaddr match? + if (env->dmmuregs[1] == (env->itlb_tag[i] & 0x1fff) && + (address & mask) == (env->itlb_tag[i] & ~0x1fffULL)) { + // valid, access ok? + if ((env->itlb_tte[i] & 0x8000000000000000ULL) == 0 || + ((env->itlb_tte[i] & 0x4) && is_user)) { + if (env->immuregs[3]) /* Fault status register */ + env->immuregs[3] = 2; /* overflow (not read before another fault) */ + env->immuregs[3] |= (is_user << 3) | 1; + env->exception_index = TT_TFAULT; +#ifdef DEBUG_MMU + printf("TFAULT at 0x%llx\n", address); +#endif + return 1; + } + *physical = (env->itlb_tte[i] & mask & 0x1fffffff000ULL) + (address & ~mask & 0x1fffffff000ULL); + *prot = PAGE_READ; + return 0; + } + } +#ifdef DEBUG_MMU + printf("TMISS at 0x%llx\n", address); +#endif + env->exception_index = TT_TMISS; + return 1; } -void cpu_set_cwp(CPUState *env1, int new_cwp) +int get_physical_address(CPUState *env, target_phys_addr_t *physical, int *prot, + int *access_index, target_ulong address, int rw, + int is_user) { - CPUState *saved_env; - saved_env = env; - env = env1; - set_cwp(new_cwp); - env = saved_env; + if (rw == 2) + return get_physical_address_code(env, physical, prot, access_index, address, rw, is_user); + else + return get_physical_address_data(env, physical, prot, access_index, address, rw, is_user); } -void do_interrupt(int intno) +/* Perform address translation */ +int cpu_sparc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, + int is_user, int is_softmmu) { - int cwp; - -#ifdef DEBUG_PCALL - if (loglevel & CPU_LOG_INT) { - static int count; - fprintf(logfile, "%6d: v=%02x pc=%08x npc=%08x SP=%08x\n", - count, intno, - env->pc, - env->npc, env->regwptr[6]); - cpu_dump_state(env, logfile, fprintf, 0); -#if 0 - { - int i; - uint8_t *ptr; - - fprintf(logfile, " code="); - ptr = (uint8_t *)env->pc; - for(i = 0; i < 16; i++) { - fprintf(logfile, " %02x", ldub(ptr + i)); - } - fprintf(logfile, "\n"); - } + target_ulong virt_addr, vaddr; + target_phys_addr_t paddr; + int error_code = 0, prot, ret = 0, access_index; + + error_code = get_physical_address(env, &paddr, &prot, &access_index, address, rw, is_user); + if (error_code == 0) { + virt_addr = address & TARGET_PAGE_MASK; + vaddr = virt_addr + ((address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1)); +#ifdef DEBUG_MMU + printf("Translate at 0x%llx -> 0x%llx, vaddr 0x%llx\n", address, paddr, vaddr); #endif - count++; + ret = tlb_set_page(env, vaddr, paddr, prot, is_user, is_softmmu); + return ret; } + // XXX + return 1; +} + #endif -#if !defined(CONFIG_USER_ONLY) - if (env->psret == 0) { - cpu_abort(cpu_single_env, "Trap 0x%02x while interrupts disabled, Error state", env->exception_index); - return; - } #endif - env->psret = 0; - cwp = (env->cwp - 1) & (NWINDOWS - 1); - set_cwp(cwp); - env->regwptr[9] = env->pc; - env->regwptr[10] = env->npc; - env->psrps = env->psrs; - env->psrs = 1; - env->tbr = (env->tbr & TBR_BASE_MASK) | (intno << 4); - env->pc = env->tbr; - env->npc = env->pc + 4; - env->exception_index = 0; + +void memcpy32(target_ulong *dst, const target_ulong *src) +{ + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + dst[4] = src[4]; + dst[5] = src[5]; + dst[6] = src[6]; + dst[7] = src[7]; } -target_ulong mmu_probe(target_ulong address, int mmulev) +#if !defined(TARGET_SPARC64) +target_ulong mmu_probe(CPUState *env, target_ulong address, int mmulev) { target_phys_addr_t pde_ptr; uint32_t pde; @@ -413,7 +461,7 @@ target_ulong mmu_probe(target_ulong address, int mmulev) } #ifdef DEBUG_MMU -void dump_mmu(void) +void dump_mmu(CPUState *env) { target_ulong va, va1, va2; unsigned int n, m, o; @@ -425,17 +473,17 @@ void dump_mmu(void) pde = ldl_phys(pde_ptr); printf("Root ptr: " TARGET_FMT_lx ", ctx: %d\n", env->mmuregs[1] << 4, env->mmuregs[2]); for (n = 0, va = 0; n < 256; n++, va += 16 * 1024 * 1024) { - pde_ptr = mmu_probe(va, 2); + pde_ptr = mmu_probe(env, va, 2); if (pde_ptr) { pa = cpu_get_phys_page_debug(env, va); printf("VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx " PDE: " TARGET_FMT_lx "\n", va, pa, pde_ptr); for (m = 0, va1 = va; m < 64; m++, va1 += 256 * 1024) { - pde_ptr = mmu_probe(va1, 1); + pde_ptr = mmu_probe(env, va1, 1); if (pde_ptr) { pa = cpu_get_phys_page_debug(env, va1); printf(" VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx " PDE: " TARGET_FMT_lx "\n", va1, pa, pde_ptr); for (o = 0, va2 = va1; o < 64; o++, va2 += 4 * 1024) { - pde_ptr = mmu_probe(va2, 0); + pde_ptr = mmu_probe(env, va2, 0); if (pde_ptr) { pa = cpu_get_phys_page_debug(env, va2); printf(" VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx " PTE: " TARGET_FMT_lx "\n", va2, pa, pde_ptr); @@ -448,3 +496,77 @@ void dump_mmu(void) printf("MMU dump ends\n"); } #endif +#else +#ifdef DEBUG_MMU +void dump_mmu(CPUState *env) +{ + unsigned int i; + const char *mask; + + printf("MMU contexts: Primary: %lld, Secondary: %lld\n", env->dmmuregs[1], env->dmmuregs[2]); + if ((env->lsu & DMMU_E) == 0) { + printf("DMMU disabled\n"); + } else { + printf("DMMU dump:\n"); + for (i = 0; i < 64; i++) { + switch ((env->dtlb_tte[i] >> 61) & 3) { + default: + case 0x0: + mask = " 8k"; + break; + case 0x1: + mask = " 64k"; + break; + case 0x2: + mask = "512k"; + break; + case 0x3: + mask = " 4M"; + break; + } + if ((env->dtlb_tte[i] & 0x8000000000000000ULL) != 0) { + printf("VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx ", %s, %s, %s, %s, ctx %lld\n", + env->dtlb_tag[i] & ~0x1fffULL, + env->dtlb_tte[i] & 0x1ffffffe000ULL, + mask, + env->dtlb_tte[i] & 0x4? "priv": "user", + env->dtlb_tte[i] & 0x2? "RW": "RO", + env->dtlb_tte[i] & 0x40? "locked": "unlocked", + env->dtlb_tag[i] & 0x1fffULL); + } + } + } + if ((env->lsu & IMMU_E) == 0) { + printf("IMMU disabled\n"); + } else { + printf("IMMU dump:\n"); + for (i = 0; i < 64; i++) { + switch ((env->itlb_tte[i] >> 61) & 3) { + default: + case 0x0: + mask = " 8k"; + break; + case 0x1: + mask = " 64k"; + break; + case 0x2: + mask = "512k"; + break; + case 0x3: + mask = " 4M"; + break; + } + if ((env->itlb_tte[i] & 0x8000000000000000ULL) != 0) { + printf("VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_lx ", %s, %s, %s, ctx %lld\n", + env->itlb_tag[i] & ~0x1fffULL, + env->itlb_tte[i] & 0x1ffffffe000ULL, + mask, + env->itlb_tte[i] & 0x4? "priv": "user", + env->itlb_tte[i] & 0x40? "locked": "unlocked", + env->itlb_tag[i] & 0x1fffULL); + } + } + } +} +#endif +#endif diff --git a/qemu/target-sparc/op.c b/qemu/target-sparc/op.c index 281917a..f8b491a 100644 --- a/qemu/target-sparc/op.c +++ b/qemu/target-sparc/op.c @@ -46,76 +46,76 @@ #define REG (env->gregs[7]) #include "op_template.h" #define REGNAME i0 -#define REG (env->regwptr[16]) +#define REG (REGWPTR[16]) #include "op_template.h" #define REGNAME i1 -#define REG (env->regwptr[17]) +#define REG (REGWPTR[17]) #include "op_template.h" #define REGNAME i2 -#define REG (env->regwptr[18]) +#define REG (REGWPTR[18]) #include "op_template.h" #define REGNAME i3 -#define REG (env->regwptr[19]) +#define REG (REGWPTR[19]) #include "op_template.h" #define REGNAME i4 -#define REG (env->regwptr[20]) +#define REG (REGWPTR[20]) #include "op_template.h" #define REGNAME i5 -#define REG (env->regwptr[21]) +#define REG (REGWPTR[21]) #include "op_template.h" #define REGNAME i6 -#define REG (env->regwptr[22]) +#define REG (REGWPTR[22]) #include "op_template.h" #define REGNAME i7 -#define REG (env->regwptr[23]) +#define REG (REGWPTR[23]) #include "op_template.h" #define REGNAME l0 -#define REG (env->regwptr[8]) +#define REG (REGWPTR[8]) #include "op_template.h" #define REGNAME l1 -#define REG (env->regwptr[9]) +#define REG (REGWPTR[9]) #include "op_template.h" #define REGNAME l2 -#define REG (env->regwptr[10]) +#define REG (REGWPTR[10]) #include "op_template.h" #define REGNAME l3 -#define REG (env->regwptr[11]) +#define REG (REGWPTR[11]) #include "op_template.h" #define REGNAME l4 -#define REG (env->regwptr[12]) +#define REG (REGWPTR[12]) #include "op_template.h" #define REGNAME l5 -#define REG (env->regwptr[13]) +#define REG (REGWPTR[13]) #include "op_template.h" #define REGNAME l6 -#define REG (env->regwptr[14]) +#define REG (REGWPTR[14]) #include "op_template.h" #define REGNAME l7 -#define REG (env->regwptr[15]) +#define REG (REGWPTR[15]) #include "op_template.h" #define REGNAME o0 -#define REG (env->regwptr[0]) +#define REG (REGWPTR[0]) #include "op_template.h" #define REGNAME o1 -#define REG (env->regwptr[1]) +#define REG (REGWPTR[1]) #include "op_template.h" #define REGNAME o2 -#define REG (env->regwptr[2]) +#define REG (REGWPTR[2]) #include "op_template.h" #define REGNAME o3 -#define REG (env->regwptr[3]) +#define REG (REGWPTR[3]) #include "op_template.h" #define REGNAME o4 -#define REG (env->regwptr[4]) +#define REG (REGWPTR[4]) #include "op_template.h" #define REGNAME o5 -#define REG (env->regwptr[5]) +#define REG (REGWPTR[5]) #include "op_template.h" #define REGNAME o6 -#define REG (env->regwptr[6]) +#define REG (REGWPTR[6]) #include "op_template.h" #define REGNAME o7 -#define REG (env->regwptr[7]) +#define REG (REGWPTR[7]) #include "op_template.h" #define REGNAME f0 @@ -215,10 +215,97 @@ #define REG (env->fpr[31]) #include "fop_template.h" +#ifdef TARGET_SPARC64 +#define REGNAME f32 +#define REG (env->fpr[32]) +#include "fop_template.h" +#define REGNAME f34 +#define REG (env->fpr[34]) +#include "fop_template.h" +#define REGNAME f36 +#define REG (env->fpr[36]) +#include "fop_template.h" +#define REGNAME f38 +#define REG (env->fpr[38]) +#include "fop_template.h" +#define REGNAME f40 +#define REG (env->fpr[40]) +#include "fop_template.h" +#define REGNAME f42 +#define REG (env->fpr[42]) +#include "fop_template.h" +#define REGNAME f44 +#define REG (env->fpr[44]) +#include "fop_template.h" +#define REGNAME f46 +#define REG (env->fpr[46]) +#include "fop_template.h" +#define REGNAME f48 +#define REG (env->fpr[47]) +#include "fop_template.h" +#define REGNAME f50 +#define REG (env->fpr[50]) +#include "fop_template.h" +#define REGNAME f52 +#define REG (env->fpr[52]) +#include "fop_template.h" +#define REGNAME f54 +#define REG (env->fpr[54]) +#include "fop_template.h" +#define REGNAME f56 +#define REG (env->fpr[56]) +#include "fop_template.h" +#define REGNAME f58 +#define REG (env->fpr[58]) +#include "fop_template.h" +#define REGNAME f60 +#define REG (env->fpr[60]) +#include "fop_template.h" +#define REGNAME f62 +#define REG (env->fpr[62]) +#include "fop_template.h" +#endif + +#ifdef TARGET_SPARC64 +#ifdef WORDS_BIGENDIAN +typedef union UREG64 { + struct { uint16_t v3, v2, v1, v0; } w; + struct { uint32_t v1, v0; } l; + uint64_t q; +} UREG64; +#else +typedef union UREG64 { + struct { uint16_t v0, v1, v2, v3; } w; + struct { uint32_t v0, v1; } l; + uint64_t q; +} UREG64; +#endif + +#define PARAMQ1 \ +({\ + UREG64 __p;\ + __p.l.v1 = PARAM1;\ + __p.l.v0 = PARAM2;\ + __p.q;\ +}) + +void OPPROTO op_movq_T0_im64(void) +{ + T0 = PARAMQ1; +} + +void OPPROTO op_movq_T1_im64(void) +{ + T1 = PARAMQ1; +} + +#define XFLAG_SET(x) ((env->xcc&x)?1:0) + +#else #define EIP (env->pc) +#endif #define FLAG_SET(x) ((env->psr&x)?1:0) -#define FFLAG_SET(x) ((env->fsr&x)?1:0) void OPPROTO op_movl_T0_0(void) { @@ -227,17 +314,52 @@ void OPPROTO op_movl_T0_0(void) void OPPROTO op_movl_T0_im(void) { - T0 = PARAM1; + T0 = (uint32_t)PARAM1; } void OPPROTO op_movl_T1_im(void) { - T1 = PARAM1; + T1 = (uint32_t)PARAM1; } void OPPROTO op_movl_T2_im(void) { - T2 = PARAM1; + T2 = (uint32_t)PARAM1; +} + +void OPPROTO op_movl_T0_sim(void) +{ + T0 = (int32_t)PARAM1; +} + +void OPPROTO op_movl_T1_sim(void) +{ + T1 = (int32_t)PARAM1; +} + +void OPPROTO op_movl_T2_sim(void) +{ + T2 = (int32_t)PARAM1; +} + +void OPPROTO op_movl_T0_env(void) +{ + T0 = *(uint32_t *)((char *)env + PARAM1); +} + +void OPPROTO op_movl_env_T0(void) +{ + *(uint32_t *)((char *)env + PARAM1) = T0; +} + +void OPPROTO op_movtl_T0_env(void) +{ + T0 = *(target_ulong *)((char *)env + PARAM1); +} + +void OPPROTO op_movtl_env_T0(void) +{ + *(target_ulong *)((char *)env + PARAM1) = T0; } void OPPROTO op_add_T1_T0(void) @@ -252,6 +374,27 @@ void OPPROTO op_add_T1_T0_cc(void) src1 = T0; T0 += T1; env->psr = 0; +#ifdef TARGET_SPARC64 + if (!(T0 & 0xffffffff)) + env->psr |= PSR_ZERO; + if ((int32_t) T0 < 0) + env->psr |= PSR_NEG; + if ((src1 & 0xffffffff) < (T1 & 0xffffffff)) + env->psr |= PSR_CARRY; + if ((((src1 & 0xffffffff) ^ (T1 & 0xffffffff) ^ -1) & + ((src1 & 0xffffffff) ^ (T0 & 0xffffffff))) & (1 << 31)) + env->psr |= PSR_OVF; + + env->xcc = 0; + if (!T0) + env->xcc |= PSR_ZERO; + if ((int64_t) T0 < 0) + env->xcc |= PSR_NEG; + if (T0 < src1) + env->xcc |= PSR_CARRY; + if (((src1 ^ T1 ^ -1) & (src1 ^ T0)) & (1ULL << 63)) + env->xcc |= PSR_OVF; +#else if (!T0) env->psr |= PSR_ZERO; if ((int32_t) T0 < 0) @@ -260,7 +403,7 @@ void OPPROTO op_add_T1_T0_cc(void) env->psr |= PSR_CARRY; if (((src1 ^ T1 ^ -1) & (src1 ^ T0)) & (1 << 31)) env->psr |= PSR_OVF; - /* V9 xcc */ +#endif FORCE_RET(); } @@ -276,6 +419,27 @@ void OPPROTO op_addx_T1_T0_cc(void) src1 = T0; T0 += T1 + FLAG_SET(PSR_CARRY); env->psr = 0; +#ifdef TARGET_SPARC64 + if (!(T0 & 0xffffffff)) + env->psr |= PSR_ZERO; + if ((int32_t) T0 < 0) + env->psr |= PSR_NEG; + if ((src1 & 0xffffffff) < (T1 & 0xffffffff)) + env->psr |= PSR_CARRY; + if ((((src1 & 0xffffffff) ^ (T1 & 0xffffffff) ^ -1) & + ((src1 & 0xffffffff) ^ (T0 & 0xffffffff))) & (1 << 31)) + env->psr |= PSR_OVF; + + env->xcc = 0; + if (!T0) + env->xcc |= PSR_ZERO; + if ((int64_t) T0 < 0) + env->xcc |= PSR_NEG; + if (T0 < src1) + env->xcc |= PSR_CARRY; + if (((src1 ^ T1 ^ -1) & (src1 ^ T0)) & (1ULL << 63)) + env->xcc |= PSR_OVF; +#else if (!T0) env->psr |= PSR_ZERO; if ((int32_t) T0 < 0) @@ -284,7 +448,7 @@ void OPPROTO op_addx_T1_T0_cc(void) env->psr |= PSR_CARRY; if (((src1 ^ T1 ^ -1) & (src1 ^ T0)) & (1 << 31)) env->psr |= PSR_OVF; - /* V9 xcc */ +#endif FORCE_RET(); } @@ -300,6 +464,27 @@ void OPPROTO op_sub_T1_T0_cc(void) src1 = T0; T0 -= T1; env->psr = 0; +#ifdef TARGET_SPARC64 + if (!(T0 & 0xffffffff)) + env->psr |= PSR_ZERO; + if ((int32_t) T0 < 0) + env->psr |= PSR_NEG; + if ((src1 & 0xffffffff) < (T1 & 0xffffffff)) + env->psr |= PSR_CARRY; + if ((((src1 & 0xffffffff) ^ (T1 & 0xffffffff)) & + ((src1 & 0xffffffff) ^ (T0 & 0xffffffff))) & (1 << 31)) + env->psr |= PSR_OVF; + + env->xcc = 0; + if (!T0) + env->xcc |= PSR_ZERO; + if ((int64_t) T0 < 0) + env->xcc |= PSR_NEG; + if (T0 < src1) + env->xcc |= PSR_CARRY; + if (((src1 ^ T1) & (src1 ^ T0)) & (1ULL << 63)) + env->xcc |= PSR_OVF; +#else if (!T0) env->psr |= PSR_ZERO; if ((int32_t) T0 < 0) @@ -308,7 +493,7 @@ void OPPROTO op_sub_T1_T0_cc(void) env->psr |= PSR_CARRY; if (((src1 ^ T1) & (src1 ^ T0)) & (1 << 31)) env->psr |= PSR_OVF; - /* V9 xcc */ +#endif FORCE_RET(); } @@ -324,6 +509,27 @@ void OPPROTO op_subx_T1_T0_cc(void) src1 = T0; T0 -= T1 + FLAG_SET(PSR_CARRY); env->psr = 0; +#ifdef TARGET_SPARC64 + if (!(T0 & 0xffffffff)) + env->psr |= PSR_ZERO; + if ((int32_t) T0 < 0) + env->psr |= PSR_NEG; + if ((src1 & 0xffffffff) < (T1 & 0xffffffff)) + env->psr |= PSR_CARRY; + if ((((src1 & 0xffffffff) ^ (T1 & 0xffffffff)) & + ((src1 & 0xffffffff) ^ (T0 & 0xffffffff))) & (1 << 31)) + env->psr |= PSR_OVF; + + env->xcc = 0; + if (!T0) + env->xcc |= PSR_ZERO; + if ((int64_t) T0 < 0) + env->xcc |= PSR_NEG; + if (T0 < src1) + env->xcc |= PSR_CARRY; + if (((src1 ^ T1) & (src1 ^ T0)) & (1ULL << 63)) + env->xcc |= PSR_OVF; +#else if (!T0) env->psr |= PSR_ZERO; if ((int32_t) T0 < 0) @@ -332,7 +538,7 @@ void OPPROTO op_subx_T1_T0_cc(void) env->psr |= PSR_CARRY; if (((src1 ^ T1) & (src1 ^ T0)) & (1 << 31)) env->psr |= PSR_OVF; - /* V9 xcc */ +#endif FORCE_RET(); } @@ -370,7 +576,11 @@ void OPPROTO op_umul_T1_T0(void) { uint64_t res; res = (uint64_t) T0 * (uint64_t) T1; +#ifdef TARGET_SPARC64 + T0 = res; +#else T0 = res & 0xffffffff; +#endif env->y = res >> 32; } @@ -378,7 +588,11 @@ void OPPROTO op_smul_T1_T0(void) { uint64_t res; res = (int64_t) ((int32_t) T0) * (int64_t) ((int32_t) T1); +#ifdef TARGET_SPARC64 + T0 = res; +#else T0 = res & 0xffffffff; +#endif env->y = res >> 32; } @@ -449,24 +663,73 @@ void OPPROTO op_sdiv_T1_T0(void) void OPPROTO op_div_cc(void) { env->psr = 0; +#ifdef TARGET_SPARC64 if (!T0) env->psr |= PSR_ZERO; if ((int32_t) T0 < 0) env->psr |= PSR_NEG; if (T1) env->psr |= PSR_OVF; - /* V9 xcc */ + + env->xcc = 0; + if (!T0) + env->xcc |= PSR_ZERO; + if ((int64_t) T0 < 0) + env->xcc |= PSR_NEG; +#else + if (!T0) + env->psr |= PSR_ZERO; + if ((int32_t) T0 < 0) + env->psr |= PSR_NEG; + if (T1) + env->psr |= PSR_OVF; +#endif FORCE_RET(); } +#ifdef TARGET_SPARC64 +void OPPROTO op_mulx_T1_T0(void) +{ + T0 *= T1; + FORCE_RET(); +} + +void OPPROTO op_udivx_T1_T0(void) +{ + T0 /= T1; + FORCE_RET(); +} + +void OPPROTO op_sdivx_T1_T0(void) +{ + if (T0 == INT64_MIN && T1 == -1) + T0 = INT64_MIN; + else + T0 /= (target_long) T1; + FORCE_RET(); +} +#endif + void OPPROTO op_logic_T0_cc(void) { env->psr = 0; +#ifdef TARGET_SPARC64 + if (!(T0 & 0xffffffff)) + env->psr |= PSR_ZERO; + if ((int32_t) T0 < 0) + env->psr |= PSR_NEG; + + env->xcc = 0; + if (!T0) + env->xcc |= PSR_ZERO; + if ((int64_t) T0 < 0) + env->xcc |= PSR_NEG; +#else if (!T0) env->psr |= PSR_ZERO; if ((int32_t) T0 < 0) env->psr |= PSR_NEG; - /* V9 xcc */ +#endif FORCE_RET(); } @@ -475,6 +738,27 @@ void OPPROTO op_sll(void) T0 <<= T1; } +#ifdef TARGET_SPARC64 +void OPPROTO op_srl(void) +{ + T0 = (T0 & 0xffffffff) >> T1; +} + +void OPPROTO op_srlx(void) +{ + T0 >>= T1; +} + +void OPPROTO op_sra(void) +{ + T0 = ((int32_t) (T0 & 0xffffffff)) >> T1; +} + +void OPPROTO op_srax(void) +{ + T0 = ((int64_t) T0) >> T1; +} +#else void OPPROTO op_srl(void) { T0 >>= T1; @@ -484,6 +768,7 @@ void OPPROTO op_sra(void) { T0 = ((int32_t) T0) >> T1; } +#endif /* Load and store */ #define MEMSUFFIX _raw @@ -498,62 +783,137 @@ void OPPROTO op_sra(void) void OPPROTO op_ldfsr(void) { - env->fsr = *((uint32_t *) &FT0); + PUT_FSR32(env, *((uint32_t *) &FT0)); helper_ldfsr(); } void OPPROTO op_stfsr(void) { - *((uint32_t *) &FT0) = env->fsr; + *((uint32_t *) &FT0) = GET_FSR32(env); } -void OPPROTO op_wry(void) +#ifndef TARGET_SPARC64 +void OPPROTO op_rdpsr(void) { - env->y = T0; + do_rdpsr(); } -void OPPROTO op_rdy(void) +void OPPROTO op_wrpsr(void) { - T0 = env->y; + do_wrpsr(); + FORCE_RET(); } -void OPPROTO op_rdwim(void) +void OPPROTO op_rett(void) { - T0 = env->wim; + helper_rett(); + FORCE_RET(); } -void OPPROTO op_wrwim(void) +/* XXX: use another pointer for %iN registers to avoid slow wrapping + handling ? */ +void OPPROTO op_save(void) { - env->wim = T0; + uint32_t cwp; + cwp = (env->cwp - 1) & (NWINDOWS - 1); + if (env->wim & (1 << cwp)) { + raise_exception(TT_WIN_OVF); + } + set_cwp(cwp); FORCE_RET(); } -void OPPROTO op_rdpsr(void) +void OPPROTO op_restore(void) { - do_rdpsr(); + uint32_t cwp; + cwp = (env->cwp + 1) & (NWINDOWS - 1); + if (env->wim & (1 << cwp)) { + raise_exception(TT_WIN_UNF); + } + set_cwp(cwp); + FORCE_RET(); +} +#else +void OPPROTO op_rdccr(void) +{ + T0 = GET_CCR(env); } -void OPPROTO op_wrpsr(void) +void OPPROTO op_wrccr(void) { - do_wrpsr(); - FORCE_RET(); + PUT_CCR(env, T0); } -void OPPROTO op_rdtbr(void) +void OPPROTO op_rdtick(void) { - T0 = env->tbr; + T0 = 0; // XXX read cycle counter and bit 31 } -void OPPROTO op_wrtbr(void) +void OPPROTO op_wrtick(void) { - env->tbr = T0; - FORCE_RET(); + // XXX write cycle counter and bit 31 } -void OPPROTO op_rett(void) +void OPPROTO op_rdtpc(void) { - helper_rett(); - FORCE_RET(); + T0 = env->tpc[env->tl]; +} + +void OPPROTO op_wrtpc(void) +{ + env->tpc[env->tl] = T0; +} + +void OPPROTO op_rdtnpc(void) +{ + T0 = env->tnpc[env->tl]; +} + +void OPPROTO op_wrtnpc(void) +{ + env->tnpc[env->tl] = T0; +} + +void OPPROTO op_rdtstate(void) +{ + T0 = env->tstate[env->tl]; +} + +void OPPROTO op_wrtstate(void) +{ + env->tstate[env->tl] = T0; +} + +void OPPROTO op_rdtt(void) +{ + T0 = env->tt[env->tl]; +} + +void OPPROTO op_wrtt(void) +{ + env->tt[env->tl] = T0; +} + +void OPPROTO op_rdpstate(void) +{ + T0 = env->pstate; +} + +void OPPROTO op_wrpstate(void) +{ + do_wrpstate(); +} + +// CWP handling is reversed in V9, but we still use the V8 register +// order. +void OPPROTO op_rdcwp(void) +{ + T0 = NWINDOWS - 1 - env->cwp; +} + +void OPPROTO op_wrcwp(void) +{ + env->cwp = NWINDOWS - 1 - T0; } /* XXX: use another pointer for %iN registers to avoid slow wrapping @@ -562,10 +922,20 @@ void OPPROTO op_save(void) { uint32_t cwp; cwp = (env->cwp - 1) & (NWINDOWS - 1); - if (env->wim & (1 << cwp)) { - raise_exception(TT_WIN_OVF); + if (env->cansave == 0) { + raise_exception(TT_SPILL | (env->otherwin != 0 ? + (TT_WOTHER | ((env->wstate & 0x38) >> 1)): + ((env->wstate & 0x7) << 2))); + } else { + if (env->cleanwin - env->canrestore == 0) { + // XXX Clean windows without trap + raise_exception(TT_CLRWIN); + } else { + env->cansave--; + env->canrestore++; + set_cwp(cwp); + } } - set_cwp(cwp); FORCE_RET(); } @@ -573,12 +943,18 @@ void OPPROTO op_restore(void) { uint32_t cwp; cwp = (env->cwp + 1) & (NWINDOWS - 1); - if (env->wim & (1 << cwp)) { - raise_exception(TT_WIN_UNF); + if (env->canrestore == 0) { + raise_exception(TT_FILL | (env->otherwin != 0 ? + (TT_WOTHER | ((env->wstate & 0x38) >> 1)): + ((env->wstate & 0x7) << 2))); + } else { + env->cansave++; + env->canrestore--; + set_cwp(cwp); } - set_cwp(cwp); FORCE_RET(); } +#endif void OPPROTO op_exception(void) { @@ -629,6 +1005,11 @@ void OPPROTO op_exit_tb(void) EXIT_TB(); } +void OPPROTO op_eval_ba(void) +{ + T2 = 1; +} + void OPPROTO op_eval_be(void) { T2 = FLAG_SET(PSR_ZERO); @@ -665,6 +1046,11 @@ void OPPROTO op_eval_bvs(void) T2 = FLAG_SET(PSR_OVF); } +void OPPROTO op_eval_bn(void) +{ + T2 = 0; +} + void OPPROTO op_eval_bneg(void) { T2 = FLAG_SET(PSR_NEG); @@ -711,101 +1097,156 @@ void OPPROTO op_eval_bvc(void) T2 = !FLAG_SET(PSR_OVF); } -/* FCC1:FCC0: 0 =, 1 <, 2 >, 3 u */ +#ifdef TARGET_SPARC64 +void OPPROTO op_eval_xbe(void) +{ + T2 = XFLAG_SET(PSR_ZERO); +} -void OPPROTO op_eval_fbne(void) +void OPPROTO op_eval_xble(void) { -// !0 - T2 = (env->fsr & (FSR_FCC1 | FSR_FCC0)); /* L or G or U */ + target_ulong Z = XFLAG_SET(PSR_ZERO), N = XFLAG_SET(PSR_NEG), V = XFLAG_SET(PSR_OVF); + + T2 = Z | (N ^ V); } -void OPPROTO op_eval_fblg(void) +void OPPROTO op_eval_xbl(void) { -// 1 or 2 - T2 = FFLAG_SET(FSR_FCC0) ^ FFLAG_SET(FSR_FCC1); + target_ulong N = XFLAG_SET(PSR_NEG), V = XFLAG_SET(PSR_OVF); + + T2 = N ^ V; } -void OPPROTO op_eval_fbul(void) +void OPPROTO op_eval_xbleu(void) { -// 1 or 3 - T2 = FFLAG_SET(FSR_FCC0); + target_ulong Z = XFLAG_SET(PSR_ZERO), C = XFLAG_SET(PSR_CARRY); + + T2 = C | Z; } -void OPPROTO op_eval_fbl(void) +void OPPROTO op_eval_xbcs(void) { -// 1 - T2 = FFLAG_SET(FSR_FCC0) & !FFLAG_SET(FSR_FCC1); + T2 = XFLAG_SET(PSR_CARRY); } -void OPPROTO op_eval_fbug(void) +void OPPROTO op_eval_xbvs(void) { -// 2 or 3 - T2 = FFLAG_SET(FSR_FCC1); + T2 = XFLAG_SET(PSR_OVF); } -void OPPROTO op_eval_fbg(void) +void OPPROTO op_eval_xbneg(void) { -// 2 - T2 = !FFLAG_SET(FSR_FCC0) & FFLAG_SET(FSR_FCC1); + T2 = XFLAG_SET(PSR_NEG); } -void OPPROTO op_eval_fbu(void) +void OPPROTO op_eval_xbne(void) { -// 3 - T2 = FFLAG_SET(FSR_FCC0) & FFLAG_SET(FSR_FCC1); + T2 = !XFLAG_SET(PSR_ZERO); } -void OPPROTO op_eval_fbe(void) +void OPPROTO op_eval_xbg(void) { -// 0 - T2 = !FFLAG_SET(FSR_FCC0) & !FFLAG_SET(FSR_FCC1); + target_ulong Z = XFLAG_SET(PSR_ZERO), N = XFLAG_SET(PSR_NEG), V = XFLAG_SET(PSR_OVF); + + T2 = !(Z | (N ^ V)); } -void OPPROTO op_eval_fbue(void) +void OPPROTO op_eval_xbge(void) { -// 0 or 3 - T2 = !(FFLAG_SET(FSR_FCC1) ^ FFLAG_SET(FSR_FCC0)); - FORCE_RET(); + target_ulong N = XFLAG_SET(PSR_NEG), V = XFLAG_SET(PSR_OVF); + + T2 = !(N ^ V); } -void OPPROTO op_eval_fbge(void) +void OPPROTO op_eval_xbgu(void) { -// 0 or 2 - T2 = !FFLAG_SET(FSR_FCC0); + target_ulong Z = XFLAG_SET(PSR_ZERO), C = XFLAG_SET(PSR_CARRY); + + T2 = !(C | Z); } -void OPPROTO op_eval_fbuge(void) +void OPPROTO op_eval_xbcc(void) { -// !1 - T2 = !(FFLAG_SET(FSR_FCC0) & !FFLAG_SET(FSR_FCC1)); + T2 = !XFLAG_SET(PSR_CARRY); } -void OPPROTO op_eval_fble(void) +void OPPROTO op_eval_xbpos(void) { -// 0 or 1 - T2 = !FFLAG_SET(FSR_FCC1); + T2 = !XFLAG_SET(PSR_NEG); } -void OPPROTO op_eval_fbule(void) +void OPPROTO op_eval_xbvc(void) { -// !2 - T2 = !(!FFLAG_SET(FSR_FCC0) & FFLAG_SET(FSR_FCC1)); + T2 = !XFLAG_SET(PSR_OVF); } +#endif + +#define FCC +#define FFLAG_SET(x) (env->fsr & x? 1: 0) +#include "fbranch_template.h" + +#ifdef TARGET_SPARC64 +#define FCC _fcc1 +#define FFLAG_SET(x) ((env->fsr & ((uint64_t)x >> 32))? 1: 0) +#include "fbranch_template.h" +#define FCC _fcc2 +#define FFLAG_SET(x) ((env->fsr & ((uint64_t)x >> 34))? 1: 0) +#include "fbranch_template.h" +#define FCC _fcc3 +#define FFLAG_SET(x) ((env->fsr & ((uint64_t)x >> 36))? 1: 0) +#include "fbranch_template.h" +#endif -void OPPROTO op_eval_fbo(void) +#ifdef TARGET_SPARC64 +void OPPROTO op_eval_brz(void) { -// !3 - T2 = !(FFLAG_SET(FSR_FCC0) & FFLAG_SET(FSR_FCC1)); + T2 = (T0 == 0); } +void OPPROTO op_eval_brnz(void) +{ + T2 = (T0 != 0); +} + +void OPPROTO op_eval_brlz(void) +{ + T2 = ((int64_t)T0 < 0); +} + +void OPPROTO op_eval_brlez(void) +{ + T2 = ((int64_t)T0 <= 0); +} + +void OPPROTO op_eval_brgz(void) +{ + T2 = ((int64_t)T0 > 0); +} + +void OPPROTO op_eval_brgez(void) +{ + T2 = ((int64_t)T0 >= 0); +} + +void OPPROTO op_jmp_im64(void) +{ + env->pc = PARAMQ1; +} + +void OPPROTO op_movq_npc_im64(void) +{ + env->npc = PARAMQ1; +} +#endif + void OPPROTO op_jmp_im(void) { - env->pc = PARAM1; + env->pc = (uint32_t)PARAM1; } void OPPROTO op_movl_npc_im(void) { - env->npc = PARAM1; + env->npc = (uint32_t)PARAM1; } void OPPROTO op_movl_npc_T0(void) @@ -824,43 +1265,32 @@ void OPPROTO op_next_insn(void) env->npc = env->npc + 4; } -void OPPROTO op_branch(void) +void OPPROTO op_goto_tb0(void) { - env->npc = PARAM3; /* XXX: optimize */ - JUMP_TB(op_branch, PARAM1, 0, PARAM2); + GOTO_TB(op_goto_tb0, PARAM1, 0); } -void OPPROTO op_branch2(void) +void OPPROTO op_goto_tb1(void) { - if (T2) { - env->npc = PARAM2 + 4; - JUMP_TB(op_branch2, PARAM1, 0, PARAM2); - } else { - env->npc = PARAM3 + 4; - JUMP_TB(op_branch2, PARAM1, 1, PARAM3); - } - FORCE_RET(); + GOTO_TB(op_goto_tb1, PARAM1, 1); } -void OPPROTO op_branch_a(void) +void OPPROTO op_jmp_label(void) { - if (T2) { - env->npc = PARAM2; /* XXX: optimize */ - JUMP_TB(op_branch_a, PARAM1, 0, PARAM3); - } else { - env->npc = PARAM3 + 8; /* XXX: optimize */ - JUMP_TB(op_branch_a, PARAM1, 1, PARAM3 + 4); - } + GOTO_LABEL_PARAM(1); +} + +void OPPROTO op_jnz_T2_label(void) +{ + if (T2) + GOTO_LABEL_PARAM(1); FORCE_RET(); } -void OPPROTO op_generic_branch(void) +void OPPROTO op_jz_T2_label(void) { - if (T2) { - env->npc = PARAM1; - } else { - env->npc = PARAM2; - } + if (!T2) + GOTO_LABEL_PARAM(1); FORCE_RET(); } @@ -879,6 +1309,18 @@ void OPPROTO op_fabss(void) do_fabss(); } +#ifdef TARGET_SPARC64 +void OPPROTO op_fnegd(void) +{ + DT0 = -DT1; +} + +void OPPROTO op_fabsd(void) +{ + do_fabsd(); +} +#endif + void OPPROTO op_fsqrts(void) { do_fsqrts(); @@ -944,6 +1386,38 @@ void OPPROTO op_fcmpd(void) do_fcmpd(); } +#ifdef TARGET_SPARC64 +void OPPROTO op_fcmps_fcc1(void) +{ + do_fcmps_fcc1(); +} + +void OPPROTO op_fcmpd_fcc1(void) +{ + do_fcmpd_fcc1(); +} + +void OPPROTO op_fcmps_fcc2(void) +{ + do_fcmps_fcc2(); +} + +void OPPROTO op_fcmpd_fcc2(void) +{ + do_fcmpd_fcc2(); +} + +void OPPROTO op_fcmps_fcc3(void) +{ + do_fcmps_fcc3(); +} + +void OPPROTO op_fcmpd_fcc3(void) +{ + do_fcmpd_fcc3(); +} +#endif + #ifdef USE_INT_TO_FLOAT_HELPERS void OPPROTO op_fitos(void) { @@ -964,6 +1438,18 @@ void OPPROTO op_fitod(void) { DT0 = (double) *((int32_t *)&FT1); } + +#ifdef TARGET_SPARC64 +void OPPROTO op_fxtos(void) +{ + FT0 = (float) *((int64_t *)&DT1); +} + +void OPPROTO op_fxtod(void) +{ + DT0 = (double) *((int64_t *)&DT1); +} +#endif #endif void OPPROTO op_fdtos(void) @@ -986,6 +1472,96 @@ void OPPROTO op_fdtoi(void) *((int32_t *)&FT0) = (int32_t) DT1; } +#ifdef TARGET_SPARC64 +void OPPROTO op_fstox(void) +{ + *((int64_t *)&DT0) = (int64_t) FT1; +} + +void OPPROTO op_fdtox(void) +{ + *((int64_t *)&DT0) = (int64_t) DT1; +} + +void OPPROTO op_fmovs_cc(void) +{ + if (T2) + FT0 = FT1; +} + +void OPPROTO op_fmovd_cc(void) +{ + if (T2) + DT0 = DT1; +} + +void OPPROTO op_mov_cc(void) +{ + if (T2) + T0 = T1; +} + +void OPPROTO op_flushw(void) +{ + if (env->cansave != NWINDOWS - 2) { + raise_exception(TT_SPILL | (env->otherwin != 0 ? + (TT_WOTHER | ((env->wstate & 0x38) >> 1)): + ((env->wstate & 0x7) << 2))); + } +} + +void OPPROTO op_saved(void) +{ + env->cansave++; + if (env->otherwin == 0) + env->canrestore--; +} + +void OPPROTO op_restored(void) +{ + env->canrestore++; + if (env->cleanwin < NWINDOWS - 1) + env->cleanwin++; + if (env->otherwin == 0) + env->cansave--; + else + env->otherwin--; +} + +void OPPROTO op_popc(void) +{ + do_popc(); +} + +void OPPROTO op_done(void) +{ + do_done(); +} + +void OPPROTO op_retry(void) +{ + do_retry(); +} + +void OPPROTO op_sir(void) +{ + // XXX + +} + +void OPPROTO op_ld_asi_reg() +{ + T0 += PARAM1; + helper_ld_asi(env->asi, PARAM2, PARAM3); +} + +void OPPROTO op_st_asi_reg() +{ + T0 += PARAM1; + helper_st_asi(env->asi, PARAM2, PARAM3); +} +#endif + void OPPROTO op_ld_asi() { helper_ld_asi(PARAM1, PARAM2, PARAM3); diff --git a/qemu/target-sparc/op_helper.c b/qemu/target-sparc/op_helper.c index 143cc16..468bbb6 100644 --- a/qemu/target-sparc/op_helper.c +++ b/qemu/target-sparc/op_helper.c @@ -1,5 +1,6 @@ #include "exec.h" +//#define DEBUG_PCALL //#define DEBUG_MMU void raise_exception(int tt) @@ -25,6 +26,13 @@ void do_fabss(void) FT0 = float32_abs(FT1); } +#ifdef TARGET_SPARC64 +void do_fabsd(void) +{ + DT0 = float64_abs(DT1); +} +#endif + void do_fsqrts(void) { FT0 = float32_sqrt(FT1, &env->fp_status); @@ -35,51 +43,188 @@ void do_fsqrtd(void) DT0 = float64_sqrt(DT1, &env->fp_status); } +#define FS 0 void do_fcmps (void) { + env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); if (isnan(FT0) || isnan(FT1)) { - T0 = FSR_FCC1 | FSR_FCC0; - env->fsr &= ~(FSR_FCC1 | FSR_FCC0); - env->fsr |= T0; + T0 = (FSR_FCC1 | FSR_FCC0) << FS; if (env->fsr & FSR_NVM) { + env->fsr |= T0; raise_exception(TT_FP_EXCP); } else { env->fsr |= FSR_NVA; } } else if (FT0 < FT1) { - T0 = FSR_FCC0; + T0 = FSR_FCC0 << FS; } else if (FT0 > FT1) { - T0 = FSR_FCC1; + T0 = FSR_FCC1 << FS; } else { T0 = 0; } - env->fsr = T0; + env->fsr |= T0; } void do_fcmpd (void) { + env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); + if (isnan(DT0) || isnan(DT1)) { + T0 = (FSR_FCC1 | FSR_FCC0) << FS; + if (env->fsr & FSR_NVM) { + env->fsr |= T0; + raise_exception(TT_FP_EXCP); + } else { + env->fsr |= FSR_NVA; + } + } else if (DT0 < DT1) { + T0 = FSR_FCC0 << FS; + } else if (DT0 > DT1) { + T0 = FSR_FCC1 << FS; + } else { + T0 = 0; + } + env->fsr |= T0; +} + +#ifdef TARGET_SPARC64 +#undef FS +#define FS 22 +void do_fcmps_fcc1 (void) +{ + env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); + if (isnan(FT0) || isnan(FT1)) { + T0 = (FSR_FCC1 | FSR_FCC0) << FS; + if (env->fsr & FSR_NVM) { + env->fsr |= T0; + raise_exception(TT_FP_EXCP); + } else { + env->fsr |= FSR_NVA; + } + } else if (FT0 < FT1) { + T0 = FSR_FCC0 << FS; + } else if (FT0 > FT1) { + T0 = FSR_FCC1 << FS; + } else { + T0 = 0; + } + env->fsr |= T0; +} + +void do_fcmpd_fcc1 (void) +{ + env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); + if (isnan(DT0) || isnan(DT1)) { + T0 = (FSR_FCC1 | FSR_FCC0) << FS; + if (env->fsr & FSR_NVM) { + env->fsr |= T0; + raise_exception(TT_FP_EXCP); + } else { + env->fsr |= FSR_NVA; + } + } else if (DT0 < DT1) { + T0 = FSR_FCC0 << FS; + } else if (DT0 > DT1) { + T0 = FSR_FCC1 << FS; + } else { + T0 = 0; + } + env->fsr |= T0; +} + +#undef FS +#define FS 24 +void do_fcmps_fcc2 (void) +{ + env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); + if (isnan(FT0) || isnan(FT1)) { + T0 = (FSR_FCC1 | FSR_FCC0) << FS; + if (env->fsr & FSR_NVM) { + env->fsr |= T0; + raise_exception(TT_FP_EXCP); + } else { + env->fsr |= FSR_NVA; + } + } else if (FT0 < FT1) { + T0 = FSR_FCC0 << FS; + } else if (FT0 > FT1) { + T0 = FSR_FCC1 << FS; + } else { + T0 = 0; + } + env->fsr |= T0; +} + +void do_fcmpd_fcc2 (void) +{ + env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); if (isnan(DT0) || isnan(DT1)) { - T0 = FSR_FCC1 | FSR_FCC0; - env->fsr &= ~(FSR_FCC1 | FSR_FCC0); - env->fsr |= T0; + T0 = (FSR_FCC1 | FSR_FCC0) << FS; if (env->fsr & FSR_NVM) { + env->fsr |= T0; raise_exception(TT_FP_EXCP); } else { env->fsr |= FSR_NVA; } } else if (DT0 < DT1) { - T0 = FSR_FCC0; + T0 = FSR_FCC0 << FS; } else if (DT0 > DT1) { - T0 = FSR_FCC1; + T0 = FSR_FCC1 << FS; } else { T0 = 0; } - env->fsr = T0; + env->fsr |= T0; } +#undef FS +#define FS 26 +void do_fcmps_fcc3 (void) +{ + env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); + if (isnan(FT0) || isnan(FT1)) { + T0 = (FSR_FCC1 | FSR_FCC0) << FS; + if (env->fsr & FSR_NVM) { + env->fsr |= T0; + raise_exception(TT_FP_EXCP); + } else { + env->fsr |= FSR_NVA; + } + } else if (FT0 < FT1) { + T0 = FSR_FCC0 << FS; + } else if (FT0 > FT1) { + T0 = FSR_FCC1 << FS; + } else { + T0 = 0; + } + env->fsr |= T0; +} + +void do_fcmpd_fcc3 (void) +{ + env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); + if (isnan(DT0) || isnan(DT1)) { + T0 = (FSR_FCC1 | FSR_FCC0) << FS; + if (env->fsr & FSR_NVM) { + env->fsr |= T0; + raise_exception(TT_FP_EXCP); + } else { + env->fsr |= FSR_NVA; + } + } else if (DT0 < DT1) { + T0 = FSR_FCC0 << FS; + } else if (DT0 > DT1) { + T0 = FSR_FCC1 << FS; + } else { + T0 = 0; + } + env->fsr |= T0; +} +#undef FS +#endif + +#ifndef TARGET_SPARC64 void helper_ld_asi(int asi, int size, int sign) { - uint32_t ret; + uint32_t ret = 0; switch (asi) { case 3: /* MMU probe */ @@ -90,7 +235,7 @@ void helper_ld_asi(int asi, int size, int sign) if (mmulev > 4) ret = 0; else { - ret = mmu_probe(T0, mmulev); + ret = mmu_probe(env, T0, mmulev); //bswap32s(&ret); } #ifdef DEBUG_MMU @@ -149,13 +294,14 @@ void helper_st_asi(int asi, int size, int sign) break; } #ifdef DEBUG_MMU - dump_mmu(); + dump_mmu(env); #endif return; } case 4: /* write MMU regs */ { - int reg = (T0 >> 8) & 0xf, oldreg; + int reg = (T0 >> 8) & 0xf; + uint32_t oldreg; oldreg = env->mmuregs[reg]; switch(reg) { @@ -186,7 +332,7 @@ void helper_st_asi(int asi, int size, int sign) if (oldreg != env->mmuregs[reg]) { printf("mmu change reg[%d]: 0x%08x -> 0x%08x\n", reg, oldreg, env->mmuregs[reg]); } - dump_mmu(); + dump_mmu(env); #endif return; } @@ -195,7 +341,7 @@ void helper_st_asi(int asi, int size, int sign) // value (T1) = src // address (T0) = dst // copy 32 bytes - int src = T1, dst = T0; + uint32_t src = T1, dst = T0; uint8_t temp[32]; tswap32s(&src); @@ -209,7 +355,8 @@ void helper_st_asi(int asi, int size, int sign) // value (T1, T2) // address (T0) = dst // fill 32 bytes - int i, dst = T0; + int i; + uint32_t dst = T0; uint64_t val; val = (((uint64_t)T1) << 32) | T2; @@ -222,7 +369,7 @@ void helper_st_asi(int asi, int size, int sign) return; case 0x20 ... 0x2f: /* MMU passthrough */ { - int temp = T1; + uint32_t temp = T1; if (size == 4) tswap32s(&temp); else if (size == 2) @@ -235,6 +382,327 @@ void helper_st_asi(int asi, int size, int sign) } } +#else + +void helper_ld_asi(int asi, int size, int sign) +{ + uint64_t ret = 0; + + if (asi < 0x80 && (env->pstate & PS_PRIV) == 0) + raise_exception(TT_PRIV_ACT); + + switch (asi) { + case 0x14: // Bypass + case 0x15: // Bypass, non-cacheable + { + cpu_physical_memory_read(T0, (void *) &ret, size); + if (size == 8) + tswap64s(&ret); + if (size == 4) + tswap32s((uint32_t *)&ret); + else if (size == 2) + tswap16s((uint16_t *)&ret); + break; + } + case 0x04: // Nucleus + case 0x0c: // Nucleus Little Endian (LE) + case 0x10: // As if user primary + case 0x11: // As if user secondary + case 0x18: // As if user primary LE + case 0x19: // As if user secondary LE + case 0x1c: // Bypass LE + case 0x1d: // Bypass, non-cacheable LE + case 0x24: // Nucleus quad LDD 128 bit atomic + case 0x2c: // Nucleus quad LDD 128 bit atomic + case 0x4a: // UPA config + case 0x82: // Primary no-fault + case 0x83: // Secondary no-fault + case 0x88: // Primary LE + case 0x89: // Secondary LE + case 0x8a: // Primary no-fault LE + case 0x8b: // Secondary no-fault LE + // XXX + break; + case 0x45: // LSU + ret = env->lsu; + break; + case 0x50: // I-MMU regs + { + int reg = (T0 >> 3) & 0xf; + + ret = env->immuregs[reg]; + break; + } + case 0x51: // I-MMU 8k TSB pointer + case 0x52: // I-MMU 64k TSB pointer + case 0x55: // I-MMU data access + // XXX + break; + case 0x56: // I-MMU tag read + { + unsigned int i; + + for (i = 0; i < 64; i++) { + // Valid, ctx match, vaddr match + if ((env->itlb_tte[i] & 0x8000000000000000ULL) != 0 && + env->itlb_tag[i] == T0) { + ret = env->itlb_tag[i]; + break; + } + } + break; + } + case 0x58: // D-MMU regs + { + int reg = (T0 >> 3) & 0xf; + + ret = env->dmmuregs[reg]; + break; + } + case 0x5e: // D-MMU tag read + { + unsigned int i; + + for (i = 0; i < 64; i++) { + // Valid, ctx match, vaddr match + if ((env->dtlb_tte[i] & 0x8000000000000000ULL) != 0 && + env->dtlb_tag[i] == T0) { + ret = env->dtlb_tag[i]; + break; + } + } + break; + } + case 0x59: // D-MMU 8k TSB pointer + case 0x5a: // D-MMU 64k TSB pointer + case 0x5b: // D-MMU data pointer + case 0x5d: // D-MMU data access + case 0x48: // Interrupt dispatch, RO + case 0x49: // Interrupt data receive + case 0x7f: // Incoming interrupt vector, RO + // XXX + break; + case 0x54: // I-MMU data in, WO + case 0x57: // I-MMU demap, WO + case 0x5c: // D-MMU data in, WO + case 0x5f: // D-MMU demap, WO + case 0x77: // Interrupt vector, WO + default: + ret = 0; + break; + } + T1 = ret; +} + +void helper_st_asi(int asi, int size, int sign) +{ + if (asi < 0x80 && (env->pstate & PS_PRIV) == 0) + raise_exception(TT_PRIV_ACT); + + switch(asi) { + case 0x14: // Bypass + case 0x15: // Bypass, non-cacheable + { + target_ulong temp = T1; + if (size == 8) + tswap64s(&temp); + else if (size == 4) + tswap32s((uint32_t *)&temp); + else if (size == 2) + tswap16s((uint16_t *)&temp); + cpu_physical_memory_write(T0, (void *) &temp, size); + } + return; + case 0x04: // Nucleus + case 0x0c: // Nucleus Little Endian (LE) + case 0x10: // As if user primary + case 0x11: // As if user secondary + case 0x18: // As if user primary LE + case 0x19: // As if user secondary LE + case 0x1c: // Bypass LE + case 0x1d: // Bypass, non-cacheable LE + case 0x24: // Nucleus quad LDD 128 bit atomic + case 0x2c: // Nucleus quad LDD 128 bit atomic + case 0x4a: // UPA config + case 0x88: // Primary LE + case 0x89: // Secondary LE + // XXX + return; + case 0x45: // LSU + { + uint64_t oldreg; + + oldreg = env->lsu; + env->lsu = T1 & (DMMU_E | IMMU_E); + // Mappings generated during D/I MMU disabled mode are + // invalid in normal mode + if (oldreg != env->lsu) { +#ifdef DEBUG_MMU + printf("LSU change: 0x%llx -> 0x%llx\n", oldreg, env->lsu); + dump_mmu(env); +#endif + tlb_flush(env, 1); + } + return; + } + case 0x50: // I-MMU regs + { + int reg = (T0 >> 3) & 0xf; + uint64_t oldreg; + + oldreg = env->immuregs[reg]; + switch(reg) { + case 0: // RO + case 4: + return; + case 1: // Not in I-MMU + case 2: + case 7: + case 8: + return; + case 3: // SFSR + if ((T1 & 1) == 0) + T1 = 0; // Clear SFSR + break; + case 5: // TSB access + case 6: // Tag access + default: + break; + } + env->immuregs[reg] = T1; +#ifdef DEBUG_MMU + if (oldreg != env->immuregs[reg]) { + printf("mmu change reg[%d]: 0x%08llx -> 0x%08llx\n", reg, oldreg, env->immuregs[reg]); + } + dump_mmu(env); +#endif + return; + } + case 0x54: // I-MMU data in + { + unsigned int i; + + // Try finding an invalid entry + for (i = 0; i < 64; i++) { + if ((env->itlb_tte[i] & 0x8000000000000000ULL) == 0) { + env->itlb_tag[i] = env->immuregs[6]; + env->itlb_tte[i] = T1; + return; + } + } + // Try finding an unlocked entry + for (i = 0; i < 64; i++) { + if ((env->itlb_tte[i] & 0x40) == 0) { + env->itlb_tag[i] = env->immuregs[6]; + env->itlb_tte[i] = T1; + return; + } + } + // error state? + return; + } + case 0x55: // I-MMU data access + { + unsigned int i = (T0 >> 3) & 0x3f; + + env->itlb_tag[i] = env->immuregs[6]; + env->itlb_tte[i] = T1; + return; + } + case 0x57: // I-MMU demap + // XXX + return; + case 0x58: // D-MMU regs + { + int reg = (T0 >> 3) & 0xf; + uint64_t oldreg; + + oldreg = env->dmmuregs[reg]; + switch(reg) { + case 0: // RO + case 4: + return; + case 3: // SFSR + if ((T1 & 1) == 0) { + T1 = 0; // Clear SFSR, Fault address + env->dmmuregs[4] = 0; + } + env->dmmuregs[reg] = T1; + break; + case 1: // Primary context + case 2: // Secondary context + case 5: // TSB access + case 6: // Tag access + case 7: // Virtual Watchpoint + case 8: // Physical Watchpoint + default: + break; + } + env->dmmuregs[reg] = T1; +#ifdef DEBUG_MMU + if (oldreg != env->dmmuregs[reg]) { + printf("mmu change reg[%d]: 0x%08llx -> 0x%08llx\n", reg, oldreg, env->dmmuregs[reg]); + } + dump_mmu(env); +#endif + return; + } + case 0x5c: // D-MMU data in + { + unsigned int i; + + // Try finding an invalid entry + for (i = 0; i < 64; i++) { + if ((env->dtlb_tte[i] & 0x8000000000000000ULL) == 0) { + env->dtlb_tag[i] = env->dmmuregs[6]; + env->dtlb_tte[i] = T1; + return; + } + } + // Try finding an unlocked entry + for (i = 0; i < 64; i++) { + if ((env->dtlb_tte[i] & 0x40) == 0) { + env->dtlb_tag[i] = env->dmmuregs[6]; + env->dtlb_tte[i] = T1; + return; + } + } + // error state? + return; + } + case 0x5d: // D-MMU data access + { + unsigned int i = (T0 >> 3) & 0x3f; + + env->dtlb_tag[i] = env->dmmuregs[6]; + env->dtlb_tte[i] = T1; + return; + } + case 0x5f: // D-MMU demap + case 0x49: // Interrupt data receive + // XXX + return; + case 0x51: // I-MMU 8k TSB pointer, RO + case 0x52: // I-MMU 64k TSB pointer, RO + case 0x56: // I-MMU tag read, RO + case 0x59: // D-MMU 8k TSB pointer, RO + case 0x5a: // D-MMU 64k TSB pointer, RO + case 0x5b: // D-MMU data pointer, RO + case 0x5e: // D-MMU tag read, RO + case 0x48: // Interrupt dispatch, RO + case 0x7f: // Incoming interrupt vector, RO + case 0x82: // Primary no-fault, RO + case 0x83: // Secondary no-fault, RO + case 0x8a: // Primary no-fault LE, RO + case 0x8b: // Secondary no-fault LE, RO + default: + return; + } +} + +#endif + +#ifndef TARGET_SPARC64 void helper_rett() { unsigned int cwp; @@ -247,6 +715,7 @@ void helper_rett() set_cwp(cwp); env->psrs = env->psrps; } +#endif void helper_ldfsr(void) { @@ -288,6 +757,7 @@ void helper_debug() cpu_loop_exit(); } +#ifndef TARGET_SPARC64 void do_wrpsr() { PUT_PSR(env, T0); @@ -297,3 +767,257 @@ void do_rdpsr() { T0 = GET_PSR(env); } + +#else + +void do_popc() +{ + T0 = (T1 & 0x5555555555555555ULL) + ((T1 >> 1) & 0x5555555555555555ULL); + T0 = (T0 & 0x3333333333333333ULL) + ((T0 >> 2) & 0x3333333333333333ULL); + T0 = (T0 & 0x0f0f0f0f0f0f0f0fULL) + ((T0 >> 4) & 0x0f0f0f0f0f0f0f0fULL); + T0 = (T0 & 0x00ff00ff00ff00ffULL) + ((T0 >> 8) & 0x00ff00ff00ff00ffULL); + T0 = (T0 & 0x0000ffff0000ffffULL) + ((T0 >> 16) & 0x0000ffff0000ffffULL); + T0 = (T0 & 0x00000000ffffffffULL) + ((T0 >> 32) & 0x00000000ffffffffULL); +} + +static inline uint64_t *get_gregset(uint64_t pstate) +{ + switch (pstate) { + default: + case 0: + return env->bgregs; + case PS_AG: + return env->agregs; + case PS_MG: + return env->mgregs; + case PS_IG: + return env->igregs; + } +} + +void do_wrpstate() +{ + uint64_t new_pstate, pstate_regs, new_pstate_regs; + uint64_t *src, *dst; + + new_pstate = T0 & 0xf3f; + pstate_regs = env->pstate & 0xc01; + new_pstate_regs = new_pstate & 0xc01; + if (new_pstate_regs != pstate_regs) { + // Switch global register bank + src = get_gregset(new_pstate_regs); + dst = get_gregset(pstate_regs); + memcpy32(dst, env->gregs); + memcpy32(env->gregs, src); + } + env->pstate = new_pstate; +} + +void do_done(void) +{ + env->tl--; + env->pc = env->tnpc[env->tl]; + env->npc = env->tnpc[env->tl] + 4; + PUT_CCR(env, env->tstate[env->tl] >> 32); + env->asi = (env->tstate[env->tl] >> 24) & 0xff; + env->pstate = (env->tstate[env->tl] >> 8) & 0xfff; + set_cwp(env->tstate[env->tl] & 0xff); +} + +void do_retry(void) +{ + env->tl--; + env->pc = env->tpc[env->tl]; + env->npc = env->tnpc[env->tl]; + PUT_CCR(env, env->tstate[env->tl] >> 32); + env->asi = (env->tstate[env->tl] >> 24) & 0xff; + env->pstate = (env->tstate[env->tl] >> 8) & 0xfff; + set_cwp(env->tstate[env->tl] & 0xff); +} +#endif + +void set_cwp(int new_cwp) +{ + /* put the modified wrap registers at their proper location */ + if (env->cwp == (NWINDOWS - 1)) + memcpy32(env->regbase, env->regbase + NWINDOWS * 16); + env->cwp = new_cwp; + /* put the wrap registers at their temporary location */ + if (new_cwp == (NWINDOWS - 1)) + memcpy32(env->regbase + NWINDOWS * 16, env->regbase); + env->regwptr = env->regbase + (new_cwp * 16); + REGWPTR = env->regwptr; +} + +void cpu_set_cwp(CPUState *env1, int new_cwp) +{ + CPUState *saved_env; +#ifdef reg_REGWPTR + target_ulong *saved_regwptr; +#endif + + saved_env = env; +#ifdef reg_REGWPTR + saved_regwptr = REGWPTR; +#endif + env = env1; + set_cwp(new_cwp); + env = saved_env; +#ifdef reg_REGWPTR + REGWPTR = saved_regwptr; +#endif +} + +#ifdef TARGET_SPARC64 +void do_interrupt(int intno) +{ +#ifdef DEBUG_PCALL + if (loglevel & CPU_LOG_INT) { + static int count; + fprintf(logfile, "%6d: v=%04x pc=%016llx npc=%016llx SP=%016llx\n", + count, intno, + env->pc, + env->npc, env->regwptr[6]); + cpu_dump_state(env, logfile, fprintf, 0); +#if 0 + { + int i; + uint8_t *ptr; + + fprintf(logfile, " code="); + ptr = (uint8_t *)env->pc; + for(i = 0; i < 16; i++) { + fprintf(logfile, " %02x", ldub(ptr + i)); + } + fprintf(logfile, "\n"); + } +#endif + count++; + } +#endif +#if !defined(CONFIG_USER_ONLY) + if (env->tl == MAXTL) { + cpu_abort(cpu_single_env, "Trap 0x%04x while trap level is MAXTL, Error state", env->exception_index); + return; + } +#endif + env->tstate[env->tl] = ((uint64_t)GET_CCR(env) << 32) | ((env->asi & 0xff) << 24) | + ((env->pstate & 0xfff) << 8) | (env->cwp & 0xff); + env->tpc[env->tl] = env->pc; + env->tnpc[env->tl] = env->npc; + env->tt[env->tl] = intno; + env->pstate = PS_PEF | PS_PRIV | PS_AG; + env->tbr &= ~0x7fffULL; + env->tbr |= ((env->tl > 1) ? 1 << 14 : 0) | (intno << 5); + if (env->tl < MAXTL - 1) { + env->tl++; + } else { + env->pstate |= PS_RED; + if (env->tl != MAXTL) + env->tl++; + } + env->pc = env->tbr; + env->npc = env->pc + 4; + env->exception_index = 0; +} +#else +void do_interrupt(int intno) +{ + int cwp; + +#ifdef DEBUG_PCALL + if (loglevel & CPU_LOG_INT) { + static int count; + fprintf(logfile, "%6d: v=%02x pc=%08x npc=%08x SP=%08x\n", + count, intno, + env->pc, + env->npc, env->regwptr[6]); + cpu_dump_state(env, logfile, fprintf, 0); +#if 0 + { + int i; + uint8_t *ptr; + + fprintf(logfile, " code="); + ptr = (uint8_t *)env->pc; + for(i = 0; i < 16; i++) { + fprintf(logfile, " %02x", ldub(ptr + i)); + } + fprintf(logfile, "\n"); + } +#endif + count++; + } +#endif +#if !defined(CONFIG_USER_ONLY) + if (env->psret == 0) { + cpu_abort(cpu_single_env, "Trap 0x%02x while interrupts disabled, Error state", env->exception_index); + return; + } +#endif + env->psret = 0; + cwp = (env->cwp - 1) & (NWINDOWS - 1); + set_cwp(cwp); + env->regwptr[9] = env->pc; + env->regwptr[10] = env->npc; + env->psrps = env->psrs; + env->psrs = 1; + env->tbr = (env->tbr & TBR_BASE_MASK) | (intno << 4); + env->pc = env->tbr; + env->npc = env->pc + 4; + env->exception_index = 0; +} +#endif + +#if !defined(CONFIG_USER_ONLY) + +#define MMUSUFFIX _mmu +#define GETPC() (__builtin_return_address(0)) + +#define SHIFT 0 +#include "softmmu_template.h" + +#define SHIFT 1 +#include "softmmu_template.h" + +#define SHIFT 2 +#include "softmmu_template.h" + +#define SHIFT 3 +#include "softmmu_template.h" + + +/* try to fill the TLB and return an exception if error. If retaddr is + NULL, it means that the function was called in C code (i.e. not + from generated code or from helper.c) */ +/* XXX: fix it to restore all registers */ +void tlb_fill(target_ulong addr, int is_write, int is_user, void *retaddr) +{ + TranslationBlock *tb; + int ret; + unsigned long pc; + CPUState *saved_env; + + /* XXX: hack to restore env in all cases, even if not called from + generated code */ + saved_env = env; + env = cpu_single_env; + + ret = cpu_sparc_handle_mmu_fault(env, addr, is_write, is_user, 1); + if (ret) { + if (retaddr) { + /* now we have a real cpu fault */ + pc = (unsigned long)retaddr; + tb = tb_find_pc(pc); + if (tb) { + /* the PC is inside the translated code. It means that we have + a virtual CPU fault */ + cpu_restore_state(tb, env, pc, (void *)T2); + } + } + cpu_loop_exit(); + } + env = saved_env; +} + +#endif diff --git a/qemu/target-sparc/op_mem.h b/qemu/target-sparc/op_mem.h index 9f6ecef..f5dbd26 100644 --- a/qemu/target-sparc/op_mem.h +++ b/qemu/target-sparc/op_mem.h @@ -2,9 +2,15 @@ #define SPARC_LD_OP(name, qp) \ void OPPROTO glue(glue(op_, name), MEMSUFFIX)(void) \ { \ - T1 = glue(qp, MEMSUFFIX)(T0); \ + T1 = (target_ulong)glue(qp, MEMSUFFIX)(T0); \ } +#define SPARC_LD_OP_S(name, qp) \ + void OPPROTO glue(glue(op_, name), MEMSUFFIX)(void) \ + { \ + T1 = (target_long)glue(qp, MEMSUFFIX)(T0); \ + } + #define SPARC_ST_OP(name, op) \ void OPPROTO glue(glue(op_, name), MEMSUFFIX)(void) \ { \ @@ -14,8 +20,8 @@ void OPPROTO glue(glue(op_, name), MEMSUFFIX)(void) \ SPARC_LD_OP(ld, ldl); SPARC_LD_OP(ldub, ldub); SPARC_LD_OP(lduh, lduw); -SPARC_LD_OP(ldsb, ldsb); -SPARC_LD_OP(ldsh, ldsw); +SPARC_LD_OP_S(ldsb, ldsb); +SPARC_LD_OP_S(ldsh, ldsw); /*** Integer store ***/ SPARC_ST_OP(st, stl); @@ -68,4 +74,41 @@ void OPPROTO glue(op_lddf, MEMSUFFIX) (void) { DT0 = glue(ldfq, MEMSUFFIX)(T0); } + +#ifdef TARGET_SPARC64 +/* XXX: Should be Atomically */ +/* XXX: There are no cas[x] instructions, only cas[x]a */ +void OPPROTO glue(op_cas, MEMSUFFIX)(void) +{ + uint32_t tmp; + + tmp = glue(ldl, MEMSUFFIX)(T0); + T2 &= 0xffffffffULL; + if (tmp == (T1 & 0xffffffffULL)) { + glue(stl, MEMSUFFIX)(T0, T2); + } + T2 = tmp; +} + +void OPPROTO glue(op_casx, MEMSUFFIX)(void) +{ + uint64_t tmp; + + // XXX + tmp = (uint64_t)glue(ldl, MEMSUFFIX)(T0) << 32; + tmp |= glue(ldl, MEMSUFFIX)(T0); + if (tmp == T1) { + glue(stq, MEMSUFFIX)(T0, T2); + } + T2 = tmp; +} + +void OPPROTO glue(op_ldsw, MEMSUFFIX)(void) +{ + T1 = (int64_t)(glue(ldl, MEMSUFFIX)(T0) & 0xffffffff); +} + +SPARC_LD_OP(ldx, ldq); +SPARC_ST_OP(stx, stq); +#endif #undef MEMSUFFIX diff --git a/qemu/target-sparc/translate.c b/qemu/target-sparc/translate.c index f93c3b1..c2ba2e3 100644 --- a/qemu/target-sparc/translate.c +++ b/qemu/target-sparc/translate.c @@ -2,7 +2,7 @@ SPARC translation Copyright (C) 2003 Thomas M. Ogrisegg - Copyright (C) 2003 Fabrice Bellard + Copyright (C) 2003-2005 Fabrice Bellard This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -22,12 +22,12 @@ /* TODO-list: + Rest of V9 instructions, VIS instructions NPC/PC static optimisations (use JUMP_TB when possible) - FPU-Instructions - Privileged instructions - Coprocessor-Instructions Optimize synthetic instructions - Optional alignment and privileged instruction check + Optional alignment check + 128-bit float + Tagged add/sub */ #include @@ -69,9 +69,35 @@ enum { #include "gen-op.h" +// This function uses non-native bit order #define GET_FIELD(X, FROM, TO) \ ((X) >> (31 - (TO)) & ((1 << ((TO) - (FROM) + 1)) - 1)) +// This function uses the order in the manuals, i.e. bit 0 is 2^0 +#define GET_FIELD_SP(X, FROM, TO) \ + GET_FIELD(X, 31 - (TO), 31 - (FROM)) + +#define GET_FIELDs(x,a,b) sign_extend (GET_FIELD(x,a,b), (b) - (a) + 1) +#define GET_FIELD_SPs(x,a,b) sign_extend (GET_FIELD_SP(x,a,b), 32 - ((b) - (a) + 1)) + +#ifdef TARGET_SPARC64 +#define DFPREG(r) (((r & 1) << 6) | (r & 0x1e)) +#else +#define DFPREG(r) (r) +#endif + +#ifdef USE_DIRECT_JUMP +#define TBPARAM(x) +#else +#define TBPARAM(x) (long)(x) +#endif + +static int sign_extend(int x, int len) +{ + len = 32 - len; + return (x << len) >> len; +} + #define IS_IMM (insn & (1<<13)) static void disas_sparc_insn(DisasContext * dc); @@ -258,6 +284,34 @@ static GenOpFunc1 *gen_op_movl_TN_im[3] = { gen_op_movl_T2_im }; +// Sign extending version +static GenOpFunc1 * const gen_op_movl_TN_sim[3] = { + gen_op_movl_T0_sim, + gen_op_movl_T1_sim, + gen_op_movl_T2_sim +}; + +#ifdef TARGET_SPARC64 +#define GEN32(func, NAME) \ +static GenOpFunc *NAME ## _table [64] = { \ +NAME ## 0, NAME ## 1, NAME ## 2, NAME ## 3, \ +NAME ## 4, NAME ## 5, NAME ## 6, NAME ## 7, \ +NAME ## 8, NAME ## 9, NAME ## 10, NAME ## 11, \ +NAME ## 12, NAME ## 13, NAME ## 14, NAME ## 15, \ +NAME ## 16, NAME ## 17, NAME ## 18, NAME ## 19, \ +NAME ## 20, NAME ## 21, NAME ## 22, NAME ## 23, \ +NAME ## 24, NAME ## 25, NAME ## 26, NAME ## 27, \ +NAME ## 28, NAME ## 29, NAME ## 30, NAME ## 31, \ +NAME ## 32, 0, NAME ## 34, 0, NAME ## 36, 0, NAME ## 38, 0, \ +NAME ## 40, 0, NAME ## 42, 0, NAME ## 44, 0, NAME ## 46, 0, \ +NAME ## 48, 0, NAME ## 50, 0, NAME ## 52, 0, NAME ## 54, 0, \ +NAME ## 56, 0, NAME ## 58, 0, NAME ## 60, 0, NAME ## 62, 0, \ +}; \ +static inline void func(int n) \ +{ \ + NAME ## _table[n](); \ +} +#else #define GEN32(func, NAME) \ static GenOpFunc *NAME ## _table [32] = { \ NAME ## 0, NAME ## 1, NAME ## 2, NAME ## 3, \ @@ -273,22 +327,77 @@ static inline void func(int n) \ { \ NAME ## _table[n](); \ } +#endif /* floating point registers moves */ GEN32(gen_op_load_fpr_FT0, gen_op_load_fpr_FT0_fprf); GEN32(gen_op_load_fpr_FT1, gen_op_load_fpr_FT1_fprf); -GEN32(gen_op_load_fpr_FT2, gen_op_load_fpr_FT2_fprf); GEN32(gen_op_store_FT0_fpr, gen_op_store_FT0_fpr_fprf); GEN32(gen_op_store_FT1_fpr, gen_op_store_FT1_fpr_fprf); -GEN32(gen_op_store_FT2_fpr, gen_op_store_FT2_fpr_fprf); GEN32(gen_op_load_fpr_DT0, gen_op_load_fpr_DT0_fprf); GEN32(gen_op_load_fpr_DT1, gen_op_load_fpr_DT1_fprf); -GEN32(gen_op_load_fpr_DT2, gen_op_load_fpr_DT2_fprf); GEN32(gen_op_store_DT0_fpr, gen_op_store_DT0_fpr_fprf); GEN32(gen_op_store_DT1_fpr, gen_op_store_DT1_fpr_fprf); -GEN32(gen_op_store_DT2_fpr, gen_op_store_DT2_fpr_fprf); +#ifdef TARGET_SPARC64 +// 'a' versions allowed to user depending on asi +#if defined(CONFIG_USER_ONLY) +#define supervisor(dc) 0 +#define gen_op_ldst(name) gen_op_##name##_raw() +#define OP_LD_TABLE(width) \ + static void gen_op_##width##a(int insn, int is_ld, int size, int sign) \ + { \ + int asi, offset; \ + \ + if (IS_IMM) { \ + offset = GET_FIELD(insn, 25, 31); \ + if (is_ld) \ + gen_op_ld_asi_reg(offset, size, sign); \ + else \ + gen_op_st_asi_reg(offset, size, sign); \ + return; \ + } \ + asi = GET_FIELD(insn, 19, 26); \ + switch (asi) { \ + case 0x80: /* Primary address space */ \ + gen_op_##width##_raw(); \ + break; \ + default: \ + break; \ + } \ + } + +#else +#define gen_op_ldst(name) (*gen_op_##name[dc->mem_idx])() +#define OP_LD_TABLE(width) \ + static GenOpFunc *gen_op_##width[] = { \ + &gen_op_##width##_user, \ + &gen_op_##width##_kernel, \ + }; \ + \ + static void gen_op_##width##a(int insn, int is_ld, int size, int sign) \ + { \ + int asi, offset; \ + \ + if (IS_IMM) { \ + offset = GET_FIELD(insn, 25, 31); \ + if (is_ld) \ + gen_op_ld_asi_reg(offset, size, sign); \ + else \ + gen_op_st_asi_reg(offset, size, sign); \ + return; \ + } \ + asi = GET_FIELD(insn, 19, 26); \ + if (is_ld) \ + gen_op_ld_asi(asi, size, sign); \ + else \ + gen_op_st_asi(asi, size, sign); \ + } + +#define supervisor(dc) (dc->mem_idx == 1) +#endif +#else #if defined(CONFIG_USER_ONLY) #define gen_op_ldst(name) gen_op_##name##_raw() #define OP_LD_TABLE(width) @@ -330,6 +439,7 @@ static void gen_op_##width##a(int insn, int is_ld, int size, int sign) \ #define supervisor(dc) (dc->mem_idx == 1) #endif +#endif OP_LD_TABLE(ld); OP_LD_TABLE(st); @@ -348,21 +458,44 @@ OP_LD_TABLE(stdf); OP_LD_TABLE(ldf); OP_LD_TABLE(lddf); -static inline void gen_movl_imm_TN(int reg, int imm) +#ifdef TARGET_SPARC64 +OP_LD_TABLE(ldsw); +OP_LD_TABLE(ldx); +OP_LD_TABLE(stx); +OP_LD_TABLE(cas); +OP_LD_TABLE(casx); +#endif + +static inline void gen_movl_imm_TN(int reg, uint32_t imm) { - gen_op_movl_TN_im[reg] (imm); + gen_op_movl_TN_im[reg](imm); } -static inline void gen_movl_imm_T1(int val) +static inline void gen_movl_imm_T1(uint32_t val) { gen_movl_imm_TN(1, val); } -static inline void gen_movl_imm_T0(int val) +static inline void gen_movl_imm_T0(uint32_t val) { gen_movl_imm_TN(0, val); } +static inline void gen_movl_simm_TN(int reg, int32_t imm) +{ + gen_op_movl_TN_sim[reg](imm); +} + +static inline void gen_movl_simm_T1(int32_t val) +{ + gen_movl_simm_TN(1, val); +} + +static inline void gen_movl_simm_T0(int32_t val) +{ + gen_movl_simm_TN(0, val); +} + static inline void gen_movl_reg_TN(int reg, int t) { if (reg) @@ -402,11 +535,106 @@ static inline void gen_movl_T1_reg(int reg) gen_movl_TN_reg(reg, 1); } +static inline void gen_jmp_im(target_ulong pc) +{ +#ifdef TARGET_SPARC64 + if (pc == (uint32_t)pc) { + gen_op_jmp_im(pc); + } else { + gen_op_jmp_im64(pc >> 32, pc); + } +#else + gen_op_jmp_im(pc); +#endif +} + +static inline void gen_movl_npc_im(target_ulong npc) +{ +#ifdef TARGET_SPARC64 + if (npc == (uint32_t)npc) { + gen_op_movl_npc_im(npc); + } else { + gen_op_movq_npc_im64(npc >> 32, npc); + } +#else + gen_op_movl_npc_im(npc); +#endif +} + +static inline void gen_branch2(DisasContext *dc, long tb, target_ulong pc1, target_ulong pc2) +{ + int l1; + + l1 = gen_new_label(); + + gen_op_jz_T2_label(l1); + + gen_op_goto_tb0(TBPARAM(tb)); + gen_jmp_im(pc1); + gen_movl_npc_im(pc1 + 4); + gen_op_movl_T0_im((long)tb + 0); + gen_op_exit_tb(); + + gen_set_label(l1); + gen_op_goto_tb1(TBPARAM(tb)); + gen_jmp_im(pc2); + gen_movl_npc_im(pc2 + 4); + gen_op_movl_T0_im((long)tb + 1); + gen_op_exit_tb(); +} + +static inline void gen_branch_a(DisasContext *dc, long tb, target_ulong pc1, target_ulong pc2) +{ + int l1; + + l1 = gen_new_label(); + + gen_op_jz_T2_label(l1); + + gen_op_goto_tb0(TBPARAM(tb)); + gen_jmp_im(pc2); + gen_movl_npc_im(pc1); + gen_op_movl_T0_im((long)tb + 0); + gen_op_exit_tb(); + + gen_set_label(l1); + gen_op_goto_tb1(TBPARAM(tb)); + gen_jmp_im(pc2 + 4); + gen_movl_npc_im(pc2 + 8); + gen_op_movl_T0_im((long)tb + 1); + gen_op_exit_tb(); +} + +static inline void gen_branch(DisasContext *dc, long tb, target_ulong pc, target_ulong npc) +{ + gen_op_goto_tb0(TBPARAM(tb)); + gen_jmp_im(pc); + gen_movl_npc_im(npc); + gen_op_movl_T0_im((long)tb + 0); + gen_op_exit_tb(); +} + +static inline void gen_generic_branch(DisasContext *dc, target_ulong npc1, target_ulong npc2) +{ + int l1, l2; + + l1 = gen_new_label(); + l2 = gen_new_label(); + gen_op_jz_T2_label(l1); + + gen_movl_npc_im(npc1); + gen_op_jmp_label(l2); + + gen_set_label(l1); + gen_movl_npc_im(npc2); + gen_set_label(l2); +} + /* call this function before using T2 as it may have been set for a jump */ static inline void flush_T2(DisasContext * dc) { if (dc->npc == JUMP_PC) { - gen_op_generic_branch(dc->jump_pc[0], dc->jump_pc[1]); + gen_generic_branch(dc, dc->jump_pc[0], dc->jump_pc[1]); dc->npc = DYNAMIC_PC; } } @@ -414,23 +642,23 @@ static inline void flush_T2(DisasContext * dc) static inline void save_npc(DisasContext * dc) { if (dc->npc == JUMP_PC) { - gen_op_generic_branch(dc->jump_pc[0], dc->jump_pc[1]); + gen_generic_branch(dc, dc->jump_pc[0], dc->jump_pc[1]); dc->npc = DYNAMIC_PC; } else if (dc->npc != DYNAMIC_PC) { - gen_op_movl_npc_im(dc->npc); + gen_movl_npc_im(dc->npc); } } static inline void save_state(DisasContext * dc) { - gen_op_jmp_im(dc->pc); + gen_jmp_im(dc->pc); save_npc(dc); } static inline void gen_mov_pc_npc(DisasContext * dc) { if (dc->npc == JUMP_PC) { - gen_op_generic_branch(dc->jump_pc[0], dc->jump_pc[1]); + gen_generic_branch(dc, dc->jump_pc[0], dc->jump_pc[1]); gen_op_mov_pc_npc(); dc->pc = DYNAMIC_PC; } else if (dc->npc == DYNAMIC_PC) { @@ -441,110 +669,159 @@ static inline void gen_mov_pc_npc(DisasContext * dc) } } -static void gen_cond(int cond) -{ - switch (cond) { - case 0x1: - gen_op_eval_be(); - break; - case 0x2: - gen_op_eval_ble(); - break; - case 0x3: - gen_op_eval_bl(); - break; - case 0x4: - gen_op_eval_bleu(); - break; - case 0x5: - gen_op_eval_bcs(); - break; - case 0x6: - gen_op_eval_bneg(); - break; - case 0x7: - gen_op_eval_bvs(); - break; - case 0x9: - gen_op_eval_bne(); - break; - case 0xa: - gen_op_eval_bg(); - break; - case 0xb: - gen_op_eval_bge(); - break; - case 0xc: - gen_op_eval_bgu(); - break; - case 0xd: - gen_op_eval_bcc(); - break; - case 0xe: - gen_op_eval_bpos(); - break; - default: - case 0xf: - gen_op_eval_bvc(); - break; - } -} +static GenOpFunc * const gen_cond[2][16] = { + { + gen_op_eval_ba, + gen_op_eval_be, + gen_op_eval_ble, + gen_op_eval_bl, + gen_op_eval_bleu, + gen_op_eval_bcs, + gen_op_eval_bneg, + gen_op_eval_bvs, + gen_op_eval_bn, + gen_op_eval_bne, + gen_op_eval_bg, + gen_op_eval_bge, + gen_op_eval_bgu, + gen_op_eval_bcc, + gen_op_eval_bpos, + gen_op_eval_bvc, + }, + { +#ifdef TARGET_SPARC64 + gen_op_eval_ba, + gen_op_eval_xbe, + gen_op_eval_xble, + gen_op_eval_xbl, + gen_op_eval_xbleu, + gen_op_eval_xbcs, + gen_op_eval_xbneg, + gen_op_eval_xbvs, + gen_op_eval_bn, + gen_op_eval_xbne, + gen_op_eval_xbg, + gen_op_eval_xbge, + gen_op_eval_xbgu, + gen_op_eval_xbcc, + gen_op_eval_xbpos, + gen_op_eval_xbvc, +#endif + }, +}; -static void gen_fcond(int cond) +static GenOpFunc * const gen_fcond[4][16] = { + { + gen_op_eval_ba, + gen_op_eval_fbne, + gen_op_eval_fblg, + gen_op_eval_fbul, + gen_op_eval_fbl, + gen_op_eval_fbug, + gen_op_eval_fbg, + gen_op_eval_fbu, + gen_op_eval_bn, + gen_op_eval_fbe, + gen_op_eval_fbue, + gen_op_eval_fbge, + gen_op_eval_fbuge, + gen_op_eval_fble, + gen_op_eval_fbule, + gen_op_eval_fbo, + }, +#ifdef TARGET_SPARC64 + { + gen_op_eval_ba, + gen_op_eval_fbne_fcc1, + gen_op_eval_fblg_fcc1, + gen_op_eval_fbul_fcc1, + gen_op_eval_fbl_fcc1, + gen_op_eval_fbug_fcc1, + gen_op_eval_fbg_fcc1, + gen_op_eval_fbu_fcc1, + gen_op_eval_bn, + gen_op_eval_fbe_fcc1, + gen_op_eval_fbue_fcc1, + gen_op_eval_fbge_fcc1, + gen_op_eval_fbuge_fcc1, + gen_op_eval_fble_fcc1, + gen_op_eval_fbule_fcc1, + gen_op_eval_fbo_fcc1, + }, + { + gen_op_eval_ba, + gen_op_eval_fbne_fcc2, + gen_op_eval_fblg_fcc2, + gen_op_eval_fbul_fcc2, + gen_op_eval_fbl_fcc2, + gen_op_eval_fbug_fcc2, + gen_op_eval_fbg_fcc2, + gen_op_eval_fbu_fcc2, + gen_op_eval_bn, + gen_op_eval_fbe_fcc2, + gen_op_eval_fbue_fcc2, + gen_op_eval_fbge_fcc2, + gen_op_eval_fbuge_fcc2, + gen_op_eval_fble_fcc2, + gen_op_eval_fbule_fcc2, + gen_op_eval_fbo_fcc2, + }, + { + gen_op_eval_ba, + gen_op_eval_fbne_fcc3, + gen_op_eval_fblg_fcc3, + gen_op_eval_fbul_fcc3, + gen_op_eval_fbl_fcc3, + gen_op_eval_fbug_fcc3, + gen_op_eval_fbg_fcc3, + gen_op_eval_fbu_fcc3, + gen_op_eval_bn, + gen_op_eval_fbe_fcc3, + gen_op_eval_fbue_fcc3, + gen_op_eval_fbge_fcc3, + gen_op_eval_fbuge_fcc3, + gen_op_eval_fble_fcc3, + gen_op_eval_fbule_fcc3, + gen_op_eval_fbo_fcc3, + }, +#else + {}, {}, {}, +#endif +}; + +#ifdef TARGET_SPARC64 +static void gen_cond_reg(int cond) { switch (cond) { case 0x1: - gen_op_eval_fbne(); + gen_op_eval_brz(); break; case 0x2: - gen_op_eval_fblg(); + gen_op_eval_brlez(); break; case 0x3: - gen_op_eval_fbul(); - break; - case 0x4: - gen_op_eval_fbl(); + gen_op_eval_brlz(); break; case 0x5: - gen_op_eval_fbug(); + gen_op_eval_brnz(); break; case 0x6: - gen_op_eval_fbg(); - break; - case 0x7: - gen_op_eval_fbu(); - break; - case 0x9: - gen_op_eval_fbe(); - break; - case 0xa: - gen_op_eval_fbue(); - break; - case 0xb: - gen_op_eval_fbge(); - break; - case 0xc: - gen_op_eval_fbuge(); - break; - case 0xd: - gen_op_eval_fble(); - break; - case 0xe: - gen_op_eval_fbule(); + gen_op_eval_brgz(); break; default: - case 0xf: - gen_op_eval_fbo(); + case 0x7: + gen_op_eval_brgez(); break; } } +#endif /* XXX: potentially incorrect if dynamic npc */ -static void do_branch(DisasContext * dc, int32_t offset, uint32_t insn) +static void do_branch(DisasContext * dc, int32_t offset, uint32_t insn, int cc) { unsigned int cond = GET_FIELD(insn, 3, 6), a = (insn & (1 << 29)); target_ulong target = dc->pc + offset; - + if (cond == 0x0) { /* unconditional not taken */ if (a) { @@ -565,9 +842,9 @@ static void do_branch(DisasContext * dc, int32_t offset, uint32_t insn) } } else { flush_T2(dc); - gen_cond(cond); + gen_cond[cc][cond](); if (a) { - gen_op_branch_a((long)dc->tb, target, dc->npc); + gen_branch_a(dc, (long)dc->tb, target, dc->npc); dc->is_br = 1; } else { dc->pc = dc->npc; @@ -579,7 +856,7 @@ static void do_branch(DisasContext * dc, int32_t offset, uint32_t insn) } /* XXX: potentially incorrect if dynamic npc */ -static void do_fbranch(DisasContext * dc, int32_t offset, uint32_t insn) +static void do_fbranch(DisasContext * dc, int32_t offset, uint32_t insn, int cc) { unsigned int cond = GET_FIELD(insn, 3, 6), a = (insn & (1 << 29)); target_ulong target = dc->pc + offset; @@ -604,9 +881,9 @@ static void do_fbranch(DisasContext * dc, int32_t offset, uint32_t insn) } } else { flush_T2(dc); - gen_fcond(cond); + gen_fcond[cc][cond](); if (a) { - gen_op_branch_a((long)dc->tb, target, dc->npc); + gen_branch_a(dc, (long)dc->tb, target, dc->npc); dc->is_br = 1; } else { dc->pc = dc->npc; @@ -617,14 +894,41 @@ static void do_fbranch(DisasContext * dc, int32_t offset, uint32_t insn) } } -#define GET_FIELDs(x,a,b) sign_extend (GET_FIELD(x,a,b), (b) - (a) + 1) - -static int sign_extend(int x, int len) +#ifdef TARGET_SPARC64 +/* XXX: potentially incorrect if dynamic npc */ +static void do_branch_reg(DisasContext * dc, int32_t offset, uint32_t insn) { - len = 32 - len; - return (x << len) >> len; + unsigned int cond = GET_FIELD_SP(insn, 25, 27), a = (insn & (1 << 29)); + target_ulong target = dc->pc + offset; + + flush_T2(dc); + gen_cond_reg(cond); + if (a) { + gen_branch_a(dc, (long)dc->tb, target, dc->npc); + dc->is_br = 1; + } else { + dc->pc = dc->npc; + dc->jump_pc[0] = target; + dc->jump_pc[1] = dc->npc + 4; + dc->npc = JUMP_PC; + } } +static GenOpFunc * const gen_fcmps[4] = { + gen_op_fcmps, + gen_op_fcmps_fcc1, + gen_op_fcmps_fcc2, + gen_op_fcmps_fcc3, +}; + +static GenOpFunc * const gen_fcmpd[4] = { + gen_op_fcmpd, + gen_op_fcmpd_fcc1, + gen_op_fcmpd_fcc2, + gen_op_fcmpd_fcc3, +}; +#endif + /* before an instruction, dc->pc must be static */ static void disas_sparc_insn(DisasContext * dc) { @@ -639,19 +943,54 @@ static void disas_sparc_insn(DisasContext * dc) { unsigned int xop = GET_FIELD(insn, 7, 9); int32_t target; - target = GET_FIELD(insn, 10, 31); switch (xop) { - case 0x0: /* UNIMPL */ +#ifdef TARGET_SPARC64 case 0x1: /* V9 BPcc */ + { + int cc; + + target = GET_FIELD_SP(insn, 0, 18); + target <<= 2; + target = sign_extend(target, 18); + cc = GET_FIELD_SP(insn, 20, 21); + if (cc == 0) + do_branch(dc, target, insn, 0); + else if (cc == 2) + do_branch(dc, target, insn, 1); + else + goto illegal_insn; + goto jmp_insn; + } case 0x3: /* V9 BPr */ + { + target = GET_FIELD_SP(insn, 0, 13) | + (GET_FIELD_SP(insn, 20, 21) >> 7); + target <<= 2; + target = sign_extend(target, 16); + rs1 = GET_FIELD(insn, 13, 17); + gen_movl_reg_T0(rs1); + do_branch_reg(dc, target, insn); + goto jmp_insn; + } case 0x5: /* V9 FBPcc */ - default: - goto illegal_insn; + { + int cc = GET_FIELD_SP(insn, 20, 21); +#if !defined(CONFIG_USER_ONLY) + gen_op_trap_ifnofpu(); +#endif + target = GET_FIELD_SP(insn, 0, 18); + target <<= 2; + target = sign_extend(target, 19); + do_fbranch(dc, target, insn, cc); + goto jmp_insn; + } +#endif case 0x2: /* BN+x */ { + target = GET_FIELD(insn, 10, 31); target <<= 2; target = sign_extend(target, 22); - do_branch(dc, target, insn); + do_branch(dc, target, insn, 0); goto jmp_insn; } case 0x6: /* FBN+x */ @@ -659,9 +998,10 @@ static void disas_sparc_insn(DisasContext * dc) #if !defined(CONFIG_USER_ONLY) gen_op_trap_ifnofpu(); #endif + target = GET_FIELD(insn, 10, 31); target <<= 2; target = sign_extend(target, 22); - do_fbranch(dc, target, insn); + do_fbranch(dc, target, insn, 0); goto jmp_insn; } case 0x4: /* SETHI */ @@ -669,12 +1009,16 @@ static void disas_sparc_insn(DisasContext * dc) #if defined(OPTIM) if (rd) { // nop #endif - gen_movl_imm_T0(target << 10); + uint32_t value = GET_FIELD(insn, 10, 31); + gen_movl_imm_T0(value << 10); gen_movl_T0_reg(rd); #if defined(OPTIM) } #endif break; + case 0x0: /* UNIMPL */ + default: + goto illegal_insn; } break; } @@ -683,7 +1027,15 @@ static void disas_sparc_insn(DisasContext * dc) /*CALL*/ { target_long target = GET_FIELDs(insn, 2, 31) << 2; +#ifdef TARGET_SPARC64 + if (dc->pc == (uint32_t)dc->pc) { + gen_op_movl_T0_im(dc->pc); + } else { + gen_op_movq_T0_im64(dc->pc >> 32, dc->pc); + } +#else gen_op_movl_T0_im(dc->pc); +#endif gen_movl_T0_reg(15); target += dc->pc; gen_mov_pc_npc(dc); @@ -695,6 +1047,7 @@ static void disas_sparc_insn(DisasContext * dc) unsigned int xop = GET_FIELD(insn, 7, 12); if (xop == 0x3a) { /* generate trap */ int cond; + rs1 = GET_FIELD(insn, 13, 17); gen_movl_reg_T0(rs1); if (IS_IMM) { @@ -702,7 +1055,7 @@ static void disas_sparc_insn(DisasContext * dc) #if defined(OPTIM) if (rs2 != 0) { #endif - gen_movl_imm_T1(rs2); + gen_movl_simm_T1(rs2); gen_op_add_T1_T0(); #if defined(OPTIM) } @@ -719,51 +1072,160 @@ static void disas_sparc_insn(DisasContext * dc) #endif } save_state(dc); - /* V9 icc/xcc */ cond = GET_FIELD(insn, 3, 6); if (cond == 0x8) { gen_op_trap_T0(); dc->is_br = 1; goto jmp_insn; } else if (cond != 0) { - gen_cond(cond); +#ifdef TARGET_SPARC64 + /* V9 icc/xcc */ + int cc = GET_FIELD_SP(insn, 11, 12); + if (cc == 0) + gen_cond[0][cond](); + else if (cc == 2) + gen_cond[1][cond](); + else + goto illegal_insn; +#else + gen_cond[0][cond](); +#endif gen_op_trapcc_T0(); } } else if (xop == 0x28) { rs1 = GET_FIELD(insn, 13, 17); switch(rs1) { case 0: /* rdy */ - gen_op_rdy(); + gen_op_movtl_T0_env(offsetof(CPUSPARCState, y)); gen_movl_T0_reg(rd); break; case 15: /* stbar / V9 membar */ break; /* no effect? */ - default: +#ifdef TARGET_SPARC64 case 0x2: /* V9 rdccr */ + gen_op_rdccr(); + gen_movl_T0_reg(rd); + break; case 0x3: /* V9 rdasi */ + gen_op_movl_T0_env(offsetof(CPUSPARCState, asi)); + gen_movl_T0_reg(rd); + break; case 0x4: /* V9 rdtick */ + gen_op_rdtick(); + gen_movl_T0_reg(rd); + break; case 0x5: /* V9 rdpc */ + gen_op_movl_T0_im(dc->pc); + gen_movl_T0_reg(rd); + break; case 0x6: /* V9 rdfprs */ + gen_op_movl_T0_env(offsetof(CPUSPARCState, fprs)); + gen_movl_T0_reg(rd); + break; + case 0x17: /* Tick compare */ + gen_op_movtl_T0_env(offsetof(CPUSPARCState, tick_cmpr)); + gen_movl_T0_reg(rd); + break; + case 0x18: /* System tick */ + gen_op_rdtick(); // XXX + gen_movl_T0_reg(rd); + break; + case 0x19: /* System tick compare */ + gen_op_movtl_T0_env(offsetof(CPUSPARCState, stick_cmpr)); + gen_movl_T0_reg(rd); + break; + case 0x10: /* Performance Control */ + case 0x11: /* Performance Instrumentation Counter */ + case 0x12: /* Dispatch Control */ + case 0x13: /* Graphics Status */ + case 0x14: /* Softint set, WO */ + case 0x15: /* Softint clear, WO */ + case 0x16: /* Softint write */ +#endif + default: goto illegal_insn; } #if !defined(CONFIG_USER_ONLY) - } else if (xop == 0x29) { +#ifndef TARGET_SPARC64 + } else if (xop == 0x29) { /* rdpsr / V9 unimp */ if (!supervisor(dc)) goto priv_insn; gen_op_rdpsr(); gen_movl_T0_reg(rd); break; - } else if (xop == 0x2a) { +#endif + } else if (xop == 0x2a) { /* rdwim / V9 rdpr */ if (!supervisor(dc)) goto priv_insn; - gen_op_rdwim(); +#ifdef TARGET_SPARC64 + rs1 = GET_FIELD(insn, 13, 17); + switch (rs1) { + case 0: // tpc + gen_op_rdtpc(); + break; + case 1: // tnpc + gen_op_rdtnpc(); + break; + case 2: // tstate + gen_op_rdtstate(); + break; + case 3: // tt + gen_op_rdtt(); + break; + case 4: // tick + gen_op_rdtick(); + break; + case 5: // tba + gen_op_movtl_T0_env(offsetof(CPUSPARCState, tbr)); + break; + case 6: // pstate + gen_op_rdpstate(); + break; + case 7: // tl + gen_op_movl_T0_env(offsetof(CPUSPARCState, tl)); + break; + case 8: // pil + gen_op_movl_T0_env(offsetof(CPUSPARCState, psrpil)); + break; + case 9: // cwp + gen_op_rdcwp(); + break; + case 10: // cansave + gen_op_movl_T0_env(offsetof(CPUSPARCState, cansave)); + break; + case 11: // canrestore + gen_op_movl_T0_env(offsetof(CPUSPARCState, canrestore)); + break; + case 12: // cleanwin + gen_op_movl_T0_env(offsetof(CPUSPARCState, cleanwin)); + break; + case 13: // otherwin + gen_op_movl_T0_env(offsetof(CPUSPARCState, otherwin)); + break; + case 14: // wstate + gen_op_movl_T0_env(offsetof(CPUSPARCState, wstate)); + break; + case 31: // ver + gen_op_movtl_T0_env(offsetof(CPUSPARCState, version)); + break; + case 15: // fq + default: + goto illegal_insn; + } +#else + gen_op_movl_T0_env(offsetof(CPUSPARCState, wim)); +#endif gen_movl_T0_reg(rd); break; - } else if (xop == 0x2b) { + } else if (xop == 0x2b) { /* rdtbr / V9 flushw */ +#ifdef TARGET_SPARC64 + gen_op_flushw(); +#else if (!supervisor(dc)) goto priv_insn; - gen_op_rdtbr(); + gen_op_movtl_T0_env(offsetof(CPUSPARCState, tbr)); gen_movl_T0_reg(rd); +#endif break; #endif } else if (xop == 0x34) { /* FPU Operations */ @@ -794,9 +1256,9 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_store_FT0_fpr(rd); break; case 0x2a: /* fsqrtd */ - gen_op_load_fpr_DT1(rs2); + gen_op_load_fpr_DT1(DFPREG(rs2)); gen_op_fsqrtd(); - gen_op_store_DT0_fpr(rd); + gen_op_store_DT0_fpr(DFPREG(rd)); break; case 0x2b: /* fsqrtq */ goto nfpu_insn; @@ -807,10 +1269,10 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_store_FT0_fpr(rd); break; case 0x42: - gen_op_load_fpr_DT0(rs1); - gen_op_load_fpr_DT1(rs2); + gen_op_load_fpr_DT0(DFPREG(rs1)); + gen_op_load_fpr_DT1(DFPREG(rs2)); gen_op_faddd(); - gen_op_store_DT0_fpr(rd); + gen_op_store_DT0_fpr(DFPREG(rd)); break; case 0x43: /* faddq */ goto nfpu_insn; @@ -821,10 +1283,10 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_store_FT0_fpr(rd); break; case 0x46: - gen_op_load_fpr_DT0(rs1); - gen_op_load_fpr_DT1(rs2); + gen_op_load_fpr_DT0(DFPREG(rs1)); + gen_op_load_fpr_DT1(DFPREG(rs2)); gen_op_fsubd(); - gen_op_store_DT0_fpr(rd); + gen_op_store_DT0_fpr(DFPREG(rd)); break; case 0x47: /* fsubq */ goto nfpu_insn; @@ -835,8 +1297,8 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_store_FT0_fpr(rd); break; case 0x4a: - gen_op_load_fpr_DT0(rs1); - gen_op_load_fpr_DT1(rs2); + gen_op_load_fpr_DT0(DFPREG(rs1)); + gen_op_load_fpr_DT1(DFPREG(rs2)); gen_op_fmuld(); gen_op_store_DT0_fpr(rd); break; @@ -849,10 +1311,10 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_store_FT0_fpr(rd); break; case 0x4e: - gen_op_load_fpr_DT0(rs1); - gen_op_load_fpr_DT1(rs2); + gen_op_load_fpr_DT0(DFPREG(rs1)); + gen_op_load_fpr_DT1(DFPREG(rs2)); gen_op_fdivd(); - gen_op_store_DT0_fpr(rd); + gen_op_store_DT0_fpr(DFPREG(rd)); break; case 0x4f: /* fdivq */ goto nfpu_insn; @@ -860,7 +1322,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_load_fpr_FT0(rs1); gen_op_load_fpr_FT1(rs2); gen_op_fsmuld(); - gen_op_store_DT0_fpr(rd); + gen_op_store_DT0_fpr(DFPREG(rd)); break; case 0x6e: /* fdmulq */ goto nfpu_insn; @@ -870,7 +1332,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_store_FT0_fpr(rd); break; case 0xc6: - gen_op_load_fpr_DT1(rs2); + gen_op_load_fpr_DT1(DFPREG(rs2)); gen_op_fdtos(); gen_op_store_FT0_fpr(rd); break; @@ -879,12 +1341,12 @@ static void disas_sparc_insn(DisasContext * dc) case 0xc8: gen_op_load_fpr_FT1(rs2); gen_op_fitod(); - gen_op_store_DT0_fpr(rd); + gen_op_store_DT0_fpr(DFPREG(rd)); break; case 0xc9: gen_op_load_fpr_FT1(rs2); gen_op_fstod(); - gen_op_store_DT0_fpr(rd); + gen_op_store_DT0_fpr(DFPREG(rd)); break; case 0xcb: /* fqtod */ goto nfpu_insn; @@ -906,55 +1368,248 @@ static void disas_sparc_insn(DisasContext * dc) break; case 0xd3: /* fqtoi */ goto nfpu_insn; - default: +#ifdef TARGET_SPARC64 case 0x2: /* V9 fmovd */ + gen_op_load_fpr_DT0(DFPREG(rs2)); + gen_op_store_DT0_fpr(DFPREG(rd)); + break; case 0x6: /* V9 fnegd */ + gen_op_load_fpr_DT1(DFPREG(rs2)); + gen_op_fnegd(); + gen_op_store_DT0_fpr(DFPREG(rd)); + break; case 0xa: /* V9 fabsd */ + gen_op_load_fpr_DT1(DFPREG(rs2)); + gen_op_fabsd(); + gen_op_store_DT0_fpr(DFPREG(rd)); + break; case 0x81: /* V9 fstox */ + gen_op_load_fpr_FT1(rs2); + gen_op_fstox(); + gen_op_store_DT0_fpr(DFPREG(rd)); + break; case 0x82: /* V9 fdtox */ + gen_op_load_fpr_DT1(DFPREG(rs2)); + gen_op_fdtox(); + gen_op_store_DT0_fpr(DFPREG(rd)); + break; case 0x84: /* V9 fxtos */ + gen_op_load_fpr_DT1(DFPREG(rs2)); + gen_op_fxtos(); + gen_op_store_FT0_fpr(rd); + break; case 0x88: /* V9 fxtod */ - + gen_op_load_fpr_DT1(DFPREG(rs2)); + gen_op_fxtod(); + gen_op_store_DT0_fpr(DFPREG(rd)); + break; case 0x3: /* V9 fmovq */ case 0x7: /* V9 fnegq */ case 0xb: /* V9 fabsq */ case 0x83: /* V9 fqtox */ case 0x8c: /* V9 fxtoq */ + goto nfpu_insn; +#endif + default: goto illegal_insn; } } else if (xop == 0x35) { /* FPU Operations */ +#ifdef TARGET_SPARC64 + int cond; +#endif #if !defined(CONFIG_USER_ONLY) gen_op_trap_ifnofpu(); #endif rs1 = GET_FIELD(insn, 13, 17); rs2 = GET_FIELD(insn, 27, 31); xop = GET_FIELD(insn, 18, 26); - /* V9 fmovscc: x5, cond = x >> 1 */ - /* V9 fmovdcc: x6, cond = x >> 1 */ - - /* V9 fmovqcc: x7, cond = x >> 1 */ +#ifdef TARGET_SPARC64 + if ((xop & 0x11f) == 0x005) { // V9 fmovsr + cond = GET_FIELD_SP(insn, 14, 17); + gen_op_load_fpr_FT0(rd); + gen_op_load_fpr_FT1(rs2); + rs1 = GET_FIELD(insn, 13, 17); + gen_movl_reg_T0(rs1); + flush_T2(dc); + gen_cond_reg(cond); + gen_op_fmovs_cc(); + gen_op_store_FT0_fpr(rd); + break; + } else if ((xop & 0x11f) == 0x006) { // V9 fmovdr + cond = GET_FIELD_SP(insn, 14, 17); + gen_op_load_fpr_DT0(rd); + gen_op_load_fpr_DT1(rs2); + flush_T2(dc); + rs1 = GET_FIELD(insn, 13, 17); + gen_movl_reg_T0(rs1); + gen_cond_reg(cond); + gen_op_fmovs_cc(); + gen_op_store_DT0_fpr(rd); + break; + } else if ((xop & 0x11f) == 0x007) { // V9 fmovqr + goto nfpu_insn; + } +#endif switch (xop) { - case 0x51: +#ifdef TARGET_SPARC64 + case 0x001: /* V9 fmovscc %fcc0 */ + cond = GET_FIELD_SP(insn, 14, 17); + gen_op_load_fpr_FT0(rd); + gen_op_load_fpr_FT1(rs2); + flush_T2(dc); + gen_fcond[0][cond](); + gen_op_fmovs_cc(); + gen_op_store_FT0_fpr(rd); + break; + case 0x002: /* V9 fmovdcc %fcc0 */ + cond = GET_FIELD_SP(insn, 14, 17); + gen_op_load_fpr_DT0(rd); + gen_op_load_fpr_DT1(rs2); + flush_T2(dc); + gen_fcond[0][cond](); + gen_op_fmovd_cc(); + gen_op_store_DT0_fpr(rd); + break; + case 0x003: /* V9 fmovqcc %fcc0 */ + goto nfpu_insn; + case 0x041: /* V9 fmovscc %fcc1 */ + cond = GET_FIELD_SP(insn, 14, 17); + gen_op_load_fpr_FT0(rd); + gen_op_load_fpr_FT1(rs2); + flush_T2(dc); + gen_fcond[1][cond](); + gen_op_fmovs_cc(); + gen_op_store_FT0_fpr(rd); + break; + case 0x042: /* V9 fmovdcc %fcc1 */ + cond = GET_FIELD_SP(insn, 14, 17); + gen_op_load_fpr_DT0(rd); + gen_op_load_fpr_DT1(rs2); + flush_T2(dc); + gen_fcond[1][cond](); + gen_op_fmovd_cc(); + gen_op_store_DT0_fpr(rd); + break; + case 0x043: /* V9 fmovqcc %fcc1 */ + goto nfpu_insn; + case 0x081: /* V9 fmovscc %fcc2 */ + cond = GET_FIELD_SP(insn, 14, 17); + gen_op_load_fpr_FT0(rd); + gen_op_load_fpr_FT1(rs2); + flush_T2(dc); + gen_fcond[2][cond](); + gen_op_fmovs_cc(); + gen_op_store_FT0_fpr(rd); + break; + case 0x082: /* V9 fmovdcc %fcc2 */ + cond = GET_FIELD_SP(insn, 14, 17); + gen_op_load_fpr_DT0(rd); + gen_op_load_fpr_DT1(rs2); + flush_T2(dc); + gen_fcond[2][cond](); + gen_op_fmovd_cc(); + gen_op_store_DT0_fpr(rd); + break; + case 0x083: /* V9 fmovqcc %fcc2 */ + goto nfpu_insn; + case 0x0c1: /* V9 fmovscc %fcc3 */ + cond = GET_FIELD_SP(insn, 14, 17); + gen_op_load_fpr_FT0(rd); + gen_op_load_fpr_FT1(rs2); + flush_T2(dc); + gen_fcond[3][cond](); + gen_op_fmovs_cc(); + gen_op_store_FT0_fpr(rd); + break; + case 0x0c2: /* V9 fmovdcc %fcc3 */ + cond = GET_FIELD_SP(insn, 14, 17); + gen_op_load_fpr_DT0(rd); + gen_op_load_fpr_DT1(rs2); + flush_T2(dc); + gen_fcond[3][cond](); + gen_op_fmovd_cc(); + gen_op_store_DT0_fpr(rd); + break; + case 0x0c3: /* V9 fmovqcc %fcc3 */ + goto nfpu_insn; + case 0x101: /* V9 fmovscc %icc */ + cond = GET_FIELD_SP(insn, 14, 17); + gen_op_load_fpr_FT0(rd); + gen_op_load_fpr_FT1(rs2); + flush_T2(dc); + gen_cond[0][cond](); + gen_op_fmovs_cc(); + gen_op_store_FT0_fpr(rd); + break; + case 0x102: /* V9 fmovdcc %icc */ + cond = GET_FIELD_SP(insn, 14, 17); + gen_op_load_fpr_DT0(rd); + gen_op_load_fpr_DT1(rs2); + flush_T2(dc); + gen_cond[0][cond](); + gen_op_fmovd_cc(); + gen_op_store_DT0_fpr(rd); + break; + case 0x103: /* V9 fmovqcc %icc */ + goto nfpu_insn; + case 0x181: /* V9 fmovscc %xcc */ + cond = GET_FIELD_SP(insn, 14, 17); + gen_op_load_fpr_FT0(rd); + gen_op_load_fpr_FT1(rs2); + flush_T2(dc); + gen_cond[1][cond](); + gen_op_fmovs_cc(); + gen_op_store_FT0_fpr(rd); + break; + case 0x182: /* V9 fmovdcc %xcc */ + cond = GET_FIELD_SP(insn, 14, 17); + gen_op_load_fpr_DT0(rd); + gen_op_load_fpr_DT1(rs2); + flush_T2(dc); + gen_cond[1][cond](); + gen_op_fmovd_cc(); + gen_op_store_DT0_fpr(rd); + break; + case 0x183: /* V9 fmovqcc %xcc */ + goto nfpu_insn; +#endif + case 0x51: /* V9 %fcc */ gen_op_load_fpr_FT0(rs1); gen_op_load_fpr_FT1(rs2); +#ifdef TARGET_SPARC64 + gen_fcmps[rd & 3](); +#else gen_op_fcmps(); +#endif break; - case 0x52: - gen_op_load_fpr_DT0(rs1); - gen_op_load_fpr_DT1(rs2); + case 0x52: /* V9 %fcc */ + gen_op_load_fpr_DT0(DFPREG(rs1)); + gen_op_load_fpr_DT1(DFPREG(rs2)); +#ifdef TARGET_SPARC64 + gen_fcmpd[rd & 3](); +#else gen_op_fcmpd(); +#endif break; case 0x53: /* fcmpq */ goto nfpu_insn; - case 0x55: /* fcmpes */ + case 0x55: /* fcmpes, V9 %fcc */ gen_op_load_fpr_FT0(rs1); gen_op_load_fpr_FT1(rs2); +#ifdef TARGET_SPARC64 + gen_fcmps[rd & 3](); +#else gen_op_fcmps(); /* XXX should trap if qNaN or sNaN */ +#endif break; - case 0x56: /* fcmped */ - gen_op_load_fpr_DT0(rs1); - gen_op_load_fpr_DT1(rs2); + case 0x56: /* fcmped, V9 %fcc */ + gen_op_load_fpr_DT0(DFPREG(rs1)); + gen_op_load_fpr_DT1(DFPREG(rs2)); +#ifdef TARGET_SPARC64 + gen_fcmpd[rd & 3](); +#else gen_op_fcmpd(); /* XXX should trap if qNaN or sNaN */ +#endif break; case 0x57: /* fcmpeq */ goto nfpu_insn; @@ -970,7 +1625,7 @@ static void disas_sparc_insn(DisasContext * dc) // or %g0, x, y -> mov T1, x; mov y, T1 if (IS_IMM) { /* immediate */ rs2 = GET_FIELDs(insn, 19, 31); - gen_movl_imm_T1(rs2); + gen_movl_simm_T1(rs2); } else { /* register */ rs2 = GET_FIELD(insn, 27, 31); gen_movl_reg_T1(rs2); @@ -982,7 +1637,7 @@ static void disas_sparc_insn(DisasContext * dc) // or x, #0, y -> mov T1, x; mov y, T1 rs2 = GET_FIELDs(insn, 19, 31); if (rs2 != 0) { - gen_movl_imm_T1(rs2); + gen_movl_simm_T1(rs2); gen_op_or_T1_T0(); } } else { /* register */ @@ -996,12 +1651,56 @@ static void disas_sparc_insn(DisasContext * dc) gen_movl_T0_reg(rd); } #endif +#ifdef TARGET_SPARC64 + } else if (xop == 0x25) { /* sll, V9 sllx ( == sll) */ + rs1 = GET_FIELD(insn, 13, 17); + gen_movl_reg_T0(rs1); + if (IS_IMM) { /* immediate */ + rs2 = GET_FIELDs(insn, 20, 31); + gen_movl_simm_T1(rs2); + } else { /* register */ + rs2 = GET_FIELD(insn, 27, 31); + gen_movl_reg_T1(rs2); + } + gen_op_sll(); + gen_movl_T0_reg(rd); + } else if (xop == 0x26) { /* srl, V9 srlx */ + rs1 = GET_FIELD(insn, 13, 17); + gen_movl_reg_T0(rs1); + if (IS_IMM) { /* immediate */ + rs2 = GET_FIELDs(insn, 20, 31); + gen_movl_simm_T1(rs2); + } else { /* register */ + rs2 = GET_FIELD(insn, 27, 31); + gen_movl_reg_T1(rs2); + } + if (insn & (1 << 12)) + gen_op_srlx(); + else + gen_op_srl(); + gen_movl_T0_reg(rd); + } else if (xop == 0x27) { /* sra, V9 srax */ + rs1 = GET_FIELD(insn, 13, 17); + gen_movl_reg_T0(rs1); + if (IS_IMM) { /* immediate */ + rs2 = GET_FIELDs(insn, 20, 31); + gen_movl_simm_T1(rs2); + } else { /* register */ + rs2 = GET_FIELD(insn, 27, 31); + gen_movl_reg_T1(rs2); + } + if (insn & (1 << 12)) + gen_op_srax(); + else + gen_op_sra(); + gen_movl_T0_reg(rd); +#endif } else if (xop < 0x38) { rs1 = GET_FIELD(insn, 13, 17); gen_movl_reg_T0(rs1); if (IS_IMM) { /* immediate */ rs2 = GET_FIELDs(insn, 19, 31); - gen_movl_imm_T1(rs2); + gen_movl_simm_T1(rs2); } else { /* register */ rs2 = GET_FIELD(insn, 27, 31); gen_movl_reg_T1(rs2); @@ -1083,13 +1782,21 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_div_cc(); break; default: - case 0x9: /* V9 mulx */ - case 0xd: /* V9 udivx */ goto illegal_insn; } gen_movl_T0_reg(rd); } else { switch (xop) { +#ifdef TARGET_SPARC64 + case 0x9: /* V9 mulx */ + gen_op_mulx_T1_T0(); + gen_movl_T0_reg(rd); + break; + case 0xd: /* V9 udivx */ + gen_op_udivx_T1_T0(); + gen_movl_T0_reg(rd); + break; +#endif case 0x20: /* taddcc */ case 0x21: /* tsubcc */ case 0x22: /* taddcctv */ @@ -1099,30 +1806,74 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_mulscc_T1_T0(); gen_movl_T0_reg(rd); break; - case 0x25: /* sll, V9 sllx */ - gen_op_sll(); +#ifndef TARGET_SPARC64 + case 0x25: /* sll */ + gen_op_sll(); gen_movl_T0_reg(rd); break; - case 0x26: /* srl, V9 srlx */ - gen_op_srl(); + case 0x26: /* srl */ + gen_op_srl(); gen_movl_T0_reg(rd); break; - case 0x27: /* sra, V9 srax */ - gen_op_sra(); + case 0x27: /* sra */ + gen_op_sra(); gen_movl_T0_reg(rd); break; +#endif case 0x30: { - gen_op_xor_T1_T0(); switch(rd) { - case 0: - gen_op_wry(); + case 0: /* wry */ + gen_op_xor_T1_T0(); + gen_op_movtl_env_T0(offsetof(CPUSPARCState, y)); break; - default: +#ifdef TARGET_SPARC64 case 0x2: /* V9 wrccr */ + gen_op_wrccr(); + break; case 0x3: /* V9 wrasi */ + gen_op_movl_env_T0(offsetof(CPUSPARCState, asi)); + break; case 0x6: /* V9 wrfprs */ - case 0xf: /* V9 sir */ + gen_op_movl_env_T0(offsetof(CPUSPARCState, fprs)); + break; + case 0xf: /* V9 sir, nop if user */ +#if !defined(CONFIG_USER_ONLY) + if (supervisor(dc)) + gen_op_sir(); +#endif + break; + case 0x17: /* Tick compare */ +#if !defined(CONFIG_USER_ONLY) + if (!supervisor(dc)) + goto illegal_insn; +#endif + gen_op_movtl_env_T0(offsetof(CPUSPARCState, tick_cmpr)); + break; + case 0x18: /* System tick */ +#if !defined(CONFIG_USER_ONLY) + if (!supervisor(dc)) + goto illegal_insn; +#endif + gen_op_movtl_env_T0(offsetof(CPUSPARCState, stick_cmpr)); + break; + case 0x19: /* System tick compare */ +#if !defined(CONFIG_USER_ONLY) + if (!supervisor(dc)) + goto illegal_insn; +#endif + gen_op_movtl_env_T0(offsetof(CPUSPARCState, stick_cmpr)); + break; + + case 0x10: /* Performance Control */ + case 0x11: /* Performance Instrumentation Counter */ + case 0x12: /* Dispatch Control */ + case 0x13: /* Graphics Status */ + case 0x14: /* Softint set */ + case 0x15: /* Softint clear */ + case 0x16: /* Softint write */ +#endif + default: goto illegal_insn; } } @@ -1132,8 +1883,21 @@ static void disas_sparc_insn(DisasContext * dc) { if (!supervisor(dc)) goto priv_insn; +#ifdef TARGET_SPARC64 + switch (rd) { + case 0: + gen_op_saved(); + break; + case 1: + gen_op_restored(); + break; + default: + goto illegal_insn; + } +#else gen_op_xor_T1_T0(); gen_op_wrpsr(); +#endif } break; case 0x32: /* wrwim, V9 wrpr */ @@ -1141,28 +1905,179 @@ static void disas_sparc_insn(DisasContext * dc) if (!supervisor(dc)) goto priv_insn; gen_op_xor_T1_T0(); - gen_op_wrwim(); +#ifdef TARGET_SPARC64 + switch (rd) { + case 0: // tpc + gen_op_wrtpc(); + break; + case 1: // tnpc + gen_op_wrtnpc(); + break; + case 2: // tstate + gen_op_wrtstate(); + break; + case 3: // tt + gen_op_wrtt(); + break; + case 4: // tick + gen_op_wrtick(); + break; + case 5: // tba + gen_op_movtl_env_T0(offsetof(CPUSPARCState, tbr)); + break; + case 6: // pstate + gen_op_wrpstate(); + break; + case 7: // tl + gen_op_movl_env_T0(offsetof(CPUSPARCState, tl)); + break; + case 8: // pil + gen_op_movl_env_T0(offsetof(CPUSPARCState, psrpil)); + break; + case 9: // cwp + gen_op_wrcwp(); + break; + case 10: // cansave + gen_op_movl_env_T0(offsetof(CPUSPARCState, cansave)); + break; + case 11: // canrestore + gen_op_movl_env_T0(offsetof(CPUSPARCState, canrestore)); + break; + case 12: // cleanwin + gen_op_movl_env_T0(offsetof(CPUSPARCState, cleanwin)); + break; + case 13: // otherwin + gen_op_movl_env_T0(offsetof(CPUSPARCState, otherwin)); + break; + case 14: // wstate + gen_op_movl_env_T0(offsetof(CPUSPARCState, wstate)); + break; + default: + goto illegal_insn; + } +#else + gen_op_movl_env_T0(offsetof(CPUSPARCState, wim)); +#endif } break; - case 0x33: +#ifndef TARGET_SPARC64 + case 0x33: /* wrtbr, V9 unimp */ { if (!supervisor(dc)) goto priv_insn; gen_op_xor_T1_T0(); - gen_op_wrtbr(); + gen_op_movtl_env_T0(offsetof(CPUSPARCState, tbr)); } break; #endif - default: - case 0x2a: /* V9 rdpr */ - case 0x2b: /* V9 flushw */ +#endif +#ifdef TARGET_SPARC64 case 0x2c: /* V9 movcc */ + { + int cc = GET_FIELD_SP(insn, 11, 12); + int cond = GET_FIELD_SP(insn, 14, 17); + if (IS_IMM) { /* immediate */ + rs2 = GET_FIELD_SPs(insn, 0, 10); + gen_movl_simm_T1(rs2); + } + else { + rs2 = GET_FIELD_SP(insn, 0, 4); + gen_movl_reg_T1(rs2); + } + gen_movl_reg_T0(rd); + flush_T2(dc); + if (insn & (1 << 18)) { + if (cc == 0) + gen_cond[0][cond](); + else if (cc == 2) + gen_cond[1][cond](); + else + goto illegal_insn; + } else { + gen_fcond[cc][cond](); + } + gen_op_mov_cc(); + gen_movl_T0_reg(rd); + break; + } case 0x2d: /* V9 sdivx */ + gen_op_sdivx_T1_T0(); + gen_movl_T0_reg(rd); + break; case 0x2e: /* V9 popc */ + { + if (IS_IMM) { /* immediate */ + rs2 = GET_FIELD_SPs(insn, 0, 12); + gen_movl_simm_T1(rs2); + // XXX optimize: popc(constant) + } + else { + rs2 = GET_FIELD_SP(insn, 0, 4); + gen_movl_reg_T1(rs2); + } + gen_op_popc(); + gen_movl_T0_reg(rd); + } case 0x2f: /* V9 movr */ + { + int cond = GET_FIELD_SP(insn, 10, 12); + rs1 = GET_FIELD(insn, 13, 17); + flush_T2(dc); + gen_movl_reg_T0(rs1); + gen_cond_reg(cond); + if (IS_IMM) { /* immediate */ + rs2 = GET_FIELD_SPs(insn, 0, 10); + gen_movl_simm_T1(rs2); + } + else { + rs2 = GET_FIELD_SP(insn, 0, 4); + gen_movl_reg_T1(rs2); + } + gen_movl_reg_T0(rd); + gen_op_mov_cc(); + gen_movl_T0_reg(rd); + break; + } + case 0x36: /* UltraSparc shutdown, VIS */ + { + // XXX + } +#endif + default: goto illegal_insn; } } +#ifdef TARGET_SPARC64 + } else if (xop == 0x39) { /* V9 return */ + rs1 = GET_FIELD(insn, 13, 17); + gen_movl_reg_T0(rs1); + if (IS_IMM) { /* immediate */ + rs2 = GET_FIELDs(insn, 19, 31); +#if defined(OPTIM) + if (rs2) { +#endif + gen_movl_simm_T1(rs2); + gen_op_add_T1_T0(); +#if defined(OPTIM) + } +#endif + } else { /* register */ + rs2 = GET_FIELD(insn, 27, 31); +#if defined(OPTIM) + if (rs2) { +#endif + gen_movl_reg_T1(rs2); + gen_op_add_T1_T0(); +#if defined(OPTIM) + } +#endif + } + gen_op_restore(); + gen_mov_pc_npc(dc); + gen_op_movl_npc_T0(); + dc->npc = DYNAMIC_PC; + goto jmp_insn; +#endif } else { rs1 = GET_FIELD(insn, 13, 17); gen_movl_reg_T0(rs1); @@ -1171,7 +2086,7 @@ static void disas_sparc_insn(DisasContext * dc) #if defined(OPTIM) if (rs2) { #endif - gen_movl_imm_T1(rs2); + gen_movl_simm_T1(rs2); gen_op_add_T1_T0(); #if defined(OPTIM) } @@ -1199,7 +2114,7 @@ static void disas_sparc_insn(DisasContext * dc) dc->npc = DYNAMIC_PC; } goto jmp_insn; -#if !defined(CONFIG_USER_ONLY) +#if !defined(CONFIG_USER_ONLY) && !defined(TARGET_SPARC64) case 0x39: /* rett, V9 return */ { if (!supervisor(dc)) @@ -1224,8 +2139,31 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_restore(); gen_movl_T0_reg(rd); break; - default: +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_SPARC64) case 0x3e: /* V9 done/retry */ + { + switch (rd) { + case 0: + if (!supervisor(dc)) + goto priv_insn; + dc->npc = DYNAMIC_PC; + dc->pc = DYNAMIC_PC; + gen_op_done(); + goto jmp_insn; + case 1: + if (!supervisor(dc)) + goto priv_insn; + dc->npc = DYNAMIC_PC; + dc->pc = DYNAMIC_PC; + gen_op_retry(); + goto jmp_insn; + default: + goto illegal_insn; + } + } + break; +#endif + default: goto illegal_insn; } } @@ -1242,7 +2180,7 @@ static void disas_sparc_insn(DisasContext * dc) #if defined(OPTIM) if (rs2 != 0) { #endif - gen_movl_imm_T1(rs2); + gen_movl_simm_T1(rs2); gen_op_add_T1_T0(); #if defined(OPTIM) } @@ -1258,8 +2196,9 @@ static void disas_sparc_insn(DisasContext * dc) } #endif } - if (xop < 4 || (xop > 7 && xop < 0x14) || \ - (xop > 0x17 && xop < 0x20)) { + if (xop < 4 || (xop > 7 && xop < 0x14 && xop != 0x0e) || \ + (xop > 0x17 && xop < 0x1d ) || \ + (xop > 0x2c && xop < 0x33) || xop == 0x1f) { switch (xop) { case 0x0: /* load word */ gen_op_ldst(ld); @@ -1287,72 +2226,115 @@ static void disas_sparc_insn(DisasContext * dc) gen_movl_reg_T1(rd); gen_op_ldst(swap); break; -#if !defined(CONFIG_USER_ONLY) +#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) case 0x10: /* load word alternate */ +#ifndef TARGET_SPARC64 if (!supervisor(dc)) goto priv_insn; +#endif gen_op_lda(insn, 1, 4, 0); break; case 0x11: /* load unsigned byte alternate */ +#ifndef TARGET_SPARC64 if (!supervisor(dc)) goto priv_insn; +#endif gen_op_lduba(insn, 1, 1, 0); break; case 0x12: /* load unsigned halfword alternate */ +#ifndef TARGET_SPARC64 if (!supervisor(dc)) goto priv_insn; +#endif gen_op_lduha(insn, 1, 2, 0); break; case 0x13: /* load double word alternate */ +#ifndef TARGET_SPARC64 if (!supervisor(dc)) goto priv_insn; +#endif gen_op_ldda(insn, 1, 8, 0); gen_movl_T0_reg(rd + 1); break; case 0x19: /* load signed byte alternate */ +#ifndef TARGET_SPARC64 if (!supervisor(dc)) goto priv_insn; +#endif gen_op_ldsba(insn, 1, 1, 1); break; case 0x1a: /* load signed halfword alternate */ +#ifndef TARGET_SPARC64 if (!supervisor(dc)) goto priv_insn; +#endif gen_op_ldsha(insn, 1, 2 ,1); break; case 0x1d: /* ldstuba -- XXX: should be atomically */ +#ifndef TARGET_SPARC64 if (!supervisor(dc)) goto priv_insn; +#endif gen_op_ldstuba(insn, 1, 1, 0); break; case 0x1f: /* swap reg with alt. memory. Also atomically */ +#ifndef TARGET_SPARC64 if (!supervisor(dc)) goto priv_insn; +#endif gen_movl_reg_T1(rd); gen_op_swapa(insn, 1, 4, 0); break; - + +#ifndef TARGET_SPARC64 /* avoid warnings */ (void) &gen_op_stfa; (void) &gen_op_stdfa; (void) &gen_op_ldfa; (void) &gen_op_lddfa; +#else +#if !defined(CONFIG_USER_ONLY) + (void) &gen_op_cas; + (void) &gen_op_casx; #endif - default: +#endif +#endif +#ifdef TARGET_SPARC64 case 0x08: /* V9 ldsw */ + gen_op_ldst(ldsw); + break; case 0x0b: /* V9 ldx */ + gen_op_ldst(ldx); + break; case 0x18: /* V9 ldswa */ + gen_op_ldswa(insn, 1, 4, 1); + break; case 0x1b: /* V9 ldxa */ - case 0x2d: /* V9 prefetch */ + gen_op_ldxa(insn, 1, 8, 0); + break; + case 0x2d: /* V9 prefetch, no effect */ + goto skip_move; case 0x30: /* V9 ldfa */ + gen_op_ldfa(insn, 1, 8, 0); // XXX + break; case 0x33: /* V9 lddfa */ - case 0x3d: /* V9 prefetcha */ + gen_op_lddfa(insn, 1, 8, 0); // XXX + break; + case 0x3d: /* V9 prefetcha, no effect */ + goto skip_move; case 0x32: /* V9 ldqfa */ + goto nfpu_insn; +#endif + default: goto illegal_insn; } gen_movl_T1_reg(rd); +#ifdef TARGET_SPARC64 + skip_move: ; +#endif } else if (xop >= 0x20 && xop < 0x24) { -#if !defined(CONFIG_USER_ONLY) +#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) gen_op_trap_ifnofpu(); #endif switch (xop) { @@ -1368,12 +2350,13 @@ static void disas_sparc_insn(DisasContext * dc) goto nfpu_insn; case 0x23: /* load double fpreg */ gen_op_ldst(lddf); - gen_op_store_DT0_fpr(rd); + gen_op_store_DT0_fpr(DFPREG(rd)); break; default: goto illegal_insn; } - } else if (xop < 8 || (xop >= 0x14 && xop < 0x18)) { + } else if (xop < 8 || (xop >= 0x14 && xop < 0x18) || \ + xop == 0xe || xop == 0x1e) { gen_movl_reg_T1(rd); switch (xop) { case 0x4: @@ -1390,33 +2373,47 @@ static void disas_sparc_insn(DisasContext * dc) gen_movl_reg_T2(rd + 1); gen_op_ldst(std); break; -#if !defined(CONFIG_USER_ONLY) +#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) case 0x14: +#ifndef TARGET_SPARC64 if (!supervisor(dc)) goto priv_insn; +#endif gen_op_sta(insn, 0, 4, 0); break; case 0x15: +#ifndef TARGET_SPARC64 if (!supervisor(dc)) goto priv_insn; +#endif gen_op_stba(insn, 0, 1, 0); break; case 0x16: +#ifndef TARGET_SPARC64 if (!supervisor(dc)) goto priv_insn; +#endif gen_op_stha(insn, 0, 2, 0); break; case 0x17: +#ifndef TARGET_SPARC64 if (!supervisor(dc)) goto priv_insn; +#endif flush_T2(dc); gen_movl_reg_T2(rd + 1); gen_op_stda(insn, 0, 8, 0); break; #endif - default: +#ifdef TARGET_SPARC64 case 0x0e: /* V9 stx */ + gen_op_ldst(stx); + break; case 0x1e: /* V9 stxa */ + gen_op_stxa(insn, 0, 8, 0); // XXX + break; +#endif + default: goto illegal_insn; } } else if (xop > 0x23 && xop < 0x28) { @@ -1430,26 +2427,41 @@ static void disas_sparc_insn(DisasContext * dc) break; case 0x25: /* stfsr, V9 stxfsr */ gen_op_load_fpr_FT0(rd); + // XXX gen_op_stfsr(); break; case 0x26: /* stdfq */ goto nfpu_insn; case 0x27: - gen_op_load_fpr_DT0(rd); + gen_op_load_fpr_DT0(DFPREG(rd)); gen_op_ldst(stdf); break; default: + goto illegal_insn; + } + } else if (xop > 0x33 && xop < 0x3f) { +#ifdef TARGET_SPARC64 + switch (xop) { case 0x34: /* V9 stfa */ + gen_op_stfa(insn, 0, 0, 0); // XXX + break; case 0x37: /* V9 stdfa */ + gen_op_stdfa(insn, 0, 0, 0); // XXX + break; case 0x3c: /* V9 casa */ + gen_op_casa(insn, 0, 4, 0); // XXX + break; case 0x3e: /* V9 casxa */ - + gen_op_casxa(insn, 0, 8, 0); // XXX + break; case 0x36: /* V9 stqfa */ + goto nfpu_insn; + default: goto illegal_insn; } - } else if (xop > 0x33 && xop < 0x38) { - /* Co-processor */ +#else goto illegal_insn; +#endif } else goto illegal_insn; @@ -1462,7 +2474,7 @@ static void disas_sparc_insn(DisasContext * dc) gen_op_next_insn(); } else if (dc->npc == JUMP_PC) { /* we can do a static jump */ - gen_op_branch2((long)dc->tb, dc->jump_pc[0], dc->jump_pc[1]); + gen_branch2(dc, (long)dc->tb, dc->jump_pc[0], dc->jump_pc[1]); dc->is_br = 1; } else { dc->pc = dc->npc; @@ -1510,6 +2522,7 @@ static inline int gen_intermediate_code_internal(TranslationBlock * tb, gen_opc_ptr = gen_opc_buf; gen_opc_end = gen_opc_buf + OPC_MAX_SIZE; gen_opparam_ptr = gen_opparam_buf; + nb_gen_labels = 0; do { if (env->nb_breakpoints > 0) { @@ -1540,6 +2553,7 @@ static inline int gen_intermediate_code_internal(TranslationBlock * tb, } last_pc = dc->pc; disas_sparc_insn(dc); + if (dc->is_br) break; /* if the next PC is different, we abort now */ @@ -1552,7 +2566,7 @@ static inline int gen_intermediate_code_internal(TranslationBlock * tb, /* if single step mode, we generate only one instruction and generate an exception */ if (env->singlestep_enabled) { - gen_op_jmp_im(dc->pc); + gen_jmp_im(dc->pc); gen_op_movl_T0_0(); gen_op_exit_tb(); break; @@ -1565,10 +2579,10 @@ static inline int gen_intermediate_code_internal(TranslationBlock * tb, if (dc->pc != DYNAMIC_PC && (dc->npc != DYNAMIC_PC && dc->npc != JUMP_PC)) { /* static PC and NPC: we can use direct chaining */ - gen_op_branch((long)tb, dc->pc, dc->npc); + gen_branch(dc, (long)tb, dc->pc, dc->npc); } else { if (dc->pc != DYNAMIC_PC) - gen_op_jmp_im(dc->pc); + gen_jmp_im(dc->pc); save_npc(dc); gen_op_movl_T0_0(); gen_op_exit_tb(); @@ -1631,9 +2645,15 @@ void cpu_reset(CPUSPARCState *env) #else env->psrs = 1; env->psrps = 1; - env->pc = 0xffd00000; env->gregs[1] = ram_size; +#ifdef TARGET_SPARC64 + env->pstate = PS_PRIV; + env->version = GET_VER(env); + env->pc = 0x1fff0000000ULL; +#else env->mmuregs[0] = (0x04 << 24); /* Impl 0, ver 4, MMU disabled */ + env->pc = 0xffd00000; +#endif env->npc = env->pc + 4; #endif } @@ -1692,7 +2712,7 @@ void cpu_dump_state(CPUState *env, FILE *f, GET_FLAG(PSR_NEG, 'N'), GET_FLAG(PSR_CARRY, 'C'), env->psrs?'S':'-', env->psrps?'P':'-', env->psret?'E':'-', env->wim); - cpu_fprintf(f, "fsr: 0x%08x\n", env->fsr); + cpu_fprintf(f, "fsr: 0x%08x\n", GET_FSR32(env)); } #if defined(CONFIG_USER_ONLY) diff --git a/qemu/translate-all.c b/qemu/translate-all.c index e4f7c9c..cac91c5 100644 --- a/qemu/translate-all.c +++ b/qemu/translate-all.c @@ -300,6 +300,8 @@ int cpu_restore_state(TranslationBlock *tb, } env->access_type = type; } +#elif defined(TARGET_MIPS) + env->PC = gen_opc_pc[j]; #endif return 0; } diff --git a/qemu/vl.c b/qemu/vl.c index 40c0c0a..354968d 100644 --- a/qemu/vl.c +++ b/qemu/vl.c @@ -147,11 +147,15 @@ int full_screen = 0; TextConsole *vga_console; CharDriverState *serial_hds[MAX_SERIAL_PORTS]; CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; +#ifdef TARGET_I386 +int win2k_install_hack = 0; +#endif /***********************************************************/ /* x86 ISA bus support */ target_phys_addr_t isa_mem_base = 0; +PicState2 *isa_pic; uint32_t default_ioport_readb(void *opaque, uint32_t address) { @@ -528,6 +532,15 @@ int64_t cpu_get_real_ticks(void) return val; } +#elif defined(__s390__) + +int64_t cpu_get_real_ticks(void) +{ + int64_t val; + asm volatile("stck 0(%1)" : "=m" (val) : "a" (&val) : "cc"); + return val; +} + #else #error unsupported CPU #endif @@ -933,7 +946,7 @@ static void init_timers(void) /* timer signal */ sigfillset(&act.sa_mask); - act.sa_flags = 0; + act.sa_flags = 0; #if defined (TARGET_I386) && defined(USE_CODE_COPY) act.sa_flags |= SA_ONSTACK; #endif @@ -2345,6 +2358,17 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id) { return 0; } + +#elif defined(TARGET_MIPS) +void cpu_save(QEMUFile *f, void *opaque) +{ +} + +int cpu_load(QEMUFile *f, void *opaque, int version_id) +{ + return 0; +} + #elif defined(TARGET_SPARC) void cpu_save(QEMUFile *f, void *opaque) { @@ -2372,12 +2396,14 @@ void cpu_save(QEMUFile *f, void *opaque) qemu_put_betls(f, &env->y); tmp = GET_PSR(env); qemu_put_be32(f, tmp); - qemu_put_be32s(f, &env->fsr); + qemu_put_betls(f, &env->fsr); + qemu_put_betls(f, &env->tbr); +#ifndef TARGET_SPARC64 qemu_put_be32s(f, &env->wim); - qemu_put_be32s(f, &env->tbr); /* MMU */ for(i = 0; i < 16; i++) qemu_put_be32s(f, &env->mmuregs[i]); +#endif } int cpu_load(QEMUFile *f, void *opaque, int version_id) @@ -2408,13 +2434,14 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id) env->cwp = 0; /* needed to ensure that the wrapping registers are correctly updated */ PUT_PSR(env, tmp); - qemu_get_be32s(f, &env->fsr); + qemu_get_betls(f, &env->fsr); + qemu_get_betls(f, &env->tbr); +#ifndef TARGET_SPARC64 qemu_get_be32s(f, &env->wim); - qemu_get_be32s(f, &env->tbr); /* MMU */ for(i = 0; i < 16; i++) qemu_get_be32s(f, &env->mmuregs[i]); - +#endif tlb_flush(env, 1); return 0; } @@ -2491,6 +2518,33 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) } /***********************************************************/ +/* machine registration */ + +QEMUMachine *first_machine = NULL; + +int qemu_register_machine(QEMUMachine *m) +{ + QEMUMachine **pm; + pm = &first_machine; + while (*pm != NULL) + pm = &(*pm)->next; + m->next = NULL; + *pm = m; + return 0; +} + +QEMUMachine *find_machine(const char *name) +{ + QEMUMachine *m; + + for(m = first_machine; m != NULL; m = m->next) { + if (!strcmp(m->name, name)) + return m; + } + return NULL; +} + +/***********************************************************/ /* main execution loop */ void gui_update(void *opaque) @@ -2547,6 +2601,7 @@ typedef struct QEMUResetEntry { static QEMUResetEntry *first_reset_entry; static int reset_requested; static int shutdown_requested; +static int powerdown_requested; void qemu_register_reset(QEMUResetHandler *func, void *opaque) { @@ -2584,6 +2639,12 @@ void qemu_system_shutdown_request(void) cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT); } +void qemu_system_powerdown_request(void) +{ + powerdown_requested = 1; + cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT); +} + static void main_cpu_reset(void *opaque) { #if defined(TARGET_I386) || defined(TARGET_SPARC) @@ -2698,20 +2759,25 @@ int main_loop(void) if (vm_running) { ret = cpu_exec(env); if (shutdown_requested) { - ret = EXCP_INTERRUPT; + ret = EXCP_INTERRUPT; break; } if (reset_requested) { reset_requested = 0; qemu_system_reset(); - ret = EXCP_INTERRUPT; + ret = EXCP_INTERRUPT; + } + if (powerdown_requested) { + powerdown_requested = 0; + qemu_system_powerdown(); + ret = EXCP_INTERRUPT; } if (ret == EXCP_DEBUG) { vm_stop(EXCP_DEBUG); } /* if hlt instruction, we wait until the next IRQ */ /* XXX: use timeout computed from timers */ - if (ret == EXCP_HLT) + if (ret == EXCP_HLT) timeout = 10; else timeout = 0; @@ -2726,12 +2792,13 @@ int main_loop(void) void help(void) { - printf("QEMU PC emulator version " QEMU_VERSION ", Copyright (c) 2003-2004 Fabrice Bellard\n" + printf("QEMU PC emulator version " QEMU_VERSION ", Copyright (c) 2003-2005 Fabrice Bellard\n" "usage: %s [options] [disk_image]\n" "\n" "'disk_image' is a raw hard image image for IDE hard disk 0\n" "\n" "Standard options:\n" + "-M machine select emulated machine (-M ? for list)\n" "-fda/-fdb file use 'file' as floppy disk 0/1 image\n" "-hda/-hdb file use 'file' as IDE hard disk 0/1 image\n" "-hdc/-hdd file use 'file' as IDE hard disk 2/3 image\n" @@ -2746,8 +2813,8 @@ void help(void) "-enable-audio enable audio support\n" "-localtime set the real time clock to local time [default=utc]\n" "-full-screen start in full screen\n" -#ifdef TARGET_PPC - "-prep Simulate a PREP system (default is PowerMAC)\n" +#ifdef TARGET_I386 + "-win2k-hack use it when installing Windows 2000 to avoid a disk full bug\n" #endif #if defined(TARGET_PPC) || defined(TARGET_SPARC) "-g WxH[xDEPTH] Set the initial graphical resolution and depth\n" @@ -2829,6 +2896,7 @@ void help(void) enum { QEMU_OPTION_h, + QEMU_OPTION_M, QEMU_OPTION_fda, QEMU_OPTION_fdb, QEMU_OPTION_hda, @@ -2878,6 +2946,7 @@ enum { QEMU_OPTION_full_screen, QEMU_OPTION_pidfile, QEMU_OPTION_no_kqemu, + QEMU_OPTION_win2k_hack, }; typedef struct QEMUOption { @@ -2889,6 +2958,7 @@ typedef struct QEMUOption { const QEMUOption qemu_options[] = { { "h", 0, QEMU_OPTION_h }, + { "M", HAS_ARG, QEMU_OPTION_M }, { "fda", HAS_ARG, QEMU_OPTION_fda }, { "fdb", HAS_ARG, QEMU_OPTION_fdb }, { "hda", HAS_ARG, QEMU_OPTION_hda }, @@ -2946,7 +3016,8 @@ const QEMUOption qemu_options[] = { { "loadvm", HAS_ARG, QEMU_OPTION_loadvm }, { "full-screen", 0, QEMU_OPTION_full_screen }, { "pidfile", HAS_ARG, QEMU_OPTION_pidfile }, - + { "win2k-hack", 0, QEMU_OPTION_win2k_hack }, + /* temporary options */ { "pci", 0, QEMU_OPTION_pci }, { "cirrusvga", 0, QEMU_OPTION_cirrusvga }, @@ -2999,6 +3070,26 @@ static void read_passwords(void) } } +/* XXX: currently we cannot use simultaneously different CPUs */ +void register_machines(void) +{ +#if defined(TARGET_I386) + qemu_register_machine(&pc_machine); +#elif defined(TARGET_PPC) + qemu_register_machine(&heathrow_machine); + qemu_register_machine(&core99_machine); + qemu_register_machine(&prep_machine); +#elif defined(TARGET_MIPS) + qemu_register_machine(&mips_machine); +#elif defined(TARGET_SPARC) +#ifdef TARGET_SPARC64 + qemu_register_machine(&sun4u_machine); +#else + qemu_register_machine(&sun4m_machine); +#endif +#endif +} + #define NET_IF_TUN 0 #define NET_IF_USER 1 #define NET_IF_DUMMY 2 @@ -3008,7 +3099,7 @@ int main(int argc, char **argv) #ifdef CONFIG_GDBSTUB int use_gdbstub, gdbstub_port; #endif - int i, has_cdrom; + int i, cdrom_index; int snapshot, linux_boot; CPUState *env; const char *initrd_filename; @@ -3028,11 +3119,14 @@ int main(int argc, char **argv) char parallel_devices[MAX_PARALLEL_PORTS][128]; int parallel_device_index; const char *loadvm = NULL; - + QEMUMachine *machine; + #if !defined(CONFIG_SOFTMMU) /* we never want that malloc() uses mmap() */ mallopt(M_MMAP_THRESHOLD, 4096 * 1024); #endif + register_machines(); + machine = first_machine; initrd_filename = NULL; for(i = 0; i < MAX_FD; i++) fd_filename[i] = NULL; @@ -3050,7 +3144,11 @@ int main(int argc, char **argv) nographic = 0; kernel_filename = NULL; kernel_cmdline = ""; - has_cdrom = 1; +#ifdef TARGET_PPC + cdrom_index = 1; +#else + cdrom_index = 2; +#endif cyls = heads = secs = 0; translation = BIOS_ATA_TRANSLATION_AUTO; pstrcpy(monitor_device, sizeof(monitor_device), "vc"); @@ -3110,14 +3208,33 @@ int main(int argc, char **argv) } switch(popt->index) { + case QEMU_OPTION_M: + machine = find_machine(optarg); + if (!machine) { + QEMUMachine *m; + printf("Supported machines are:\n"); + for(m = first_machine; m != NULL; m = m->next) { + printf("%-10s %s%s\n", + m->name, m->desc, + m == first_machine ? " (default)" : ""); + } + exit(1); + } + break; case QEMU_OPTION_initrd: initrd_filename = optarg; break; case QEMU_OPTION_hda: - hd_filename[0] = optarg; - break; case QEMU_OPTION_hdb: - hd_filename[1] = optarg; + case QEMU_OPTION_hdc: + case QEMU_OPTION_hdd: + { + int hd_index; + hd_index = popt->index - QEMU_OPTION_hda; + hd_filename[hd_index] = optarg; + if (hd_index == cdrom_index) + cdrom_index = -1; + } break; case QEMU_OPTION_snapshot: snapshot = 1; @@ -3184,16 +3301,10 @@ int main(int argc, char **argv) } } break; - case QEMU_OPTION_hdc: - hd_filename[2] = optarg; - has_cdrom = 0; - break; - case QEMU_OPTION_hdd: - hd_filename[3] = optarg; - break; case QEMU_OPTION_cdrom: - hd_filename[2] = optarg; - has_cdrom = 1; + if (cdrom_index >= 0) { + hd_filename[cdrom_index] = optarg; + } break; case QEMU_OPTION_boot: boot_device = optarg[0]; @@ -3397,6 +3508,11 @@ int main(int argc, char **argv) case QEMU_OPTION_pidfile: create_pidfile(optarg); break; +#ifdef TARGET_I386 + case QEMU_OPTION_win2k_hack: + win2k_install_hack = 1; + break; +#endif #ifdef USE_KQEMU case QEMU_OPTION_no_kqemu: kqemu_allowed = 0; @@ -3408,7 +3524,9 @@ int main(int argc, char **argv) linux_boot = (kernel_filename != NULL); - if (!linux_boot && hd_filename[0] == '\0' && hd_filename[2] == '\0' && + if (!linux_boot && + hd_filename[0] == '\0' && + (cdrom_index >= 0 && hd_filename[cdrom_index] == '\0') && fd_filename[0] == '\0') help(); @@ -3518,9 +3636,9 @@ int main(int argc, char **argv) /* we always create the cdrom drive, even if no disk is there */ bdrv_init(); - if (has_cdrom) { - bs_table[2] = bdrv_new("cdrom"); - bdrv_set_type_hint(bs_table[2], BDRV_TYPE_CDROM); + if (cdrom_index >= 0) { + bs_table[cdrom_index] = bdrv_new("cdrom"); + bdrv_set_type_hint(bs_table[cdrom_index], BDRV_TYPE_CDROM); } /* open the virtual block devices */ @@ -3671,19 +3789,9 @@ int main(int argc, char **argv) #endif init_timers(); -#if defined(TARGET_I386) - pc_init(ram_size, vga_ram_size, boot_device, - ds, fd_filename, snapshot, - kernel_filename, kernel_cmdline, initrd_filename); -#elif defined(TARGET_PPC) - ppc_init(ram_size, vga_ram_size, boot_device, - ds, fd_filename, snapshot, - kernel_filename, kernel_cmdline, initrd_filename); -#elif defined(TARGET_SPARC) - sun4m_init(ram_size, vga_ram_size, boot_device, - ds, fd_filename, snapshot, - kernel_filename, kernel_cmdline, initrd_filename); -#endif + machine->init(ram_size, vga_ram_size, boot_device, + ds, fd_filename, snapshot, + kernel_filename, kernel_cmdline, initrd_filename); gui_timer = qemu_new_timer(rt_clock, gui_update, NULL); qemu_mod_timer(gui_timer, qemu_get_clock(rt_clock)); diff --git a/qemu/vl.h b/qemu/vl.h index 8d29506..e0bc8b8 100644 --- a/qemu/vl.h +++ b/qemu/vl.h @@ -110,6 +110,13 @@ typedef void QEMUResetHandler(void *opaque); void qemu_register_reset(QEMUResetHandler *func, void *opaque); void qemu_system_reset_request(void); void qemu_system_shutdown_request(void); +void qemu_system_powerdown_request(void); +#if !defined(TARGET_SPARC) +// Please implement a power failure function to signal the OS +#define qemu_system_powerdown() do{}while(0) +#else +void qemu_system_powerdown(void); +#endif void main_loop_wait(int timeout); @@ -126,10 +133,13 @@ extern int graphic_height; extern int graphic_depth; extern const char *keyboard_layout; extern int kqemu_allowed; +extern int win2k_install_hack; /* XXX: make it dynamic */ #if defined (TARGET_PPC) -#define BIOS_SIZE (512 * 1024) +#define BIOS_SIZE ((512 + 32) * 1024) +#elif defined(TARGET_MIPS) +#define BIOS_SIZE (128 * 1024) #else #define BIOS_SIZE ((256 + 64) * 1024) #endif @@ -384,6 +394,7 @@ extern BlockDriver bdrv_cloop; extern BlockDriver bdrv_dmg; extern BlockDriver bdrv_bochs; extern BlockDriver bdrv_vpc; +extern BlockDriver bdrv_vvfat; void bdrv_init(void); BlockDriver *bdrv_find_format(const char *format_name); @@ -441,6 +452,25 @@ int qcow_compress_cluster(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf); #ifndef QEMU_TOOL + +typedef void QEMUMachineInitFunc(int ram_size, int vga_ram_size, + int boot_device, + DisplayState *ds, const char **fd_filename, int snapshot, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename); + +typedef struct QEMUMachine { + const char *name; + const char *desc; + QEMUMachineInitFunc *init; + struct QEMUMachine *next; +} QEMUMachine; + +int qemu_register_machine(QEMUMachine *m); + +typedef void SetIRQFunc(void *opaque, int irq_num, int level); +typedef void IRQRequestFunc(void *opaque, int level); + /* ISA bus */ extern target_phys_addr_t isa_mem_base; @@ -525,16 +555,22 @@ void pci_bios_init(void); void pci_info(void); /* temporary: will be moved in platform specific file */ +void pci_set_pic(PCIBus *bus, SetIRQFunc *set_irq, void *irq_opaque); PCIBus *pci_prep_init(void); -struct openpic_t; -void pci_pmac_set_openpic(PCIBus *bus, struct openpic_t *openpic); +PCIBus *pci_grackle_init(uint32_t base); PCIBus *pci_pmac_init(void); +PCIBus *pci_apb_init(target_ulong special_base, target_ulong mem_base); /* openpic.c */ typedef struct openpic_t openpic_t; -void openpic_set_irq (openpic_t *opp, int n_IRQ, int level); +void openpic_set_irq(void *opaque, int n_IRQ, int level); openpic_t *openpic_init (PCIBus *bus, int *pmem_index, int nb_cpus); +/* heathrow_pic.c */ +typedef struct HeathrowPICS HeathrowPICS; +void heathrow_pic_set_irq(void *opaque, int num, int level); +HeathrowPICS *heathrow_pic_init(int *pmem_index); + /* vga.c */ #define VGA_RAM_SIZE (4096 * 1024) @@ -561,7 +597,8 @@ static inline void dpy_resize(DisplayState *s, int w, int h) } int vga_initialize(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base, - unsigned long vga_ram_offset, int vga_ram_size); + unsigned long vga_ram_offset, int vga_ram_size, + unsigned long vga_bios_offset, int vga_bios_size); void vga_update_display(void); void vga_invalidate_display(void); void vga_screen_dump(const char *filename); @@ -585,10 +622,11 @@ extern BlockDriverState *bs_table[MAX_DISKS]; void isa_ide_init(int iobase, int iobase2, int irq, BlockDriverState *hd0, BlockDriverState *hd1); -void pci_ide_init(PCIBus *bus, BlockDriverState **hd_table); +void pci_cmd646_ide_init(PCIBus *bus, BlockDriverState **hd_table, + int secondary_ide_enabled); void pci_piix3_ide_init(PCIBus *bus, BlockDriverState **hd_table); int pmac_ide_init (BlockDriverState **hd_table, - openpic_t *openpic, int irq); + SetIRQFunc *set_irq, void *irq_opaque, int irq); /* sb16.c */ void SB16_init (void); @@ -652,15 +690,26 @@ ParallelState *parallel_init(int base, int irq, CharDriverState *chr); /* i8259.c */ +typedef struct PicState2 PicState2; +extern PicState2 *isa_pic; void pic_set_irq(int irq, int level); -void pic_init(void); -uint32_t pic_intack_read(CPUState *env); +void pic_set_irq_new(void *opaque, int irq, int level); +PicState2 *pic_init(IRQRequestFunc *irq_request, void *irq_request_opaque); +void pic_set_alt_irq_func(PicState2 *s, SetIRQFunc *alt_irq_func, + void *alt_irq_opaque); +int pic_read_irq(PicState2 *s); +void pic_update_irq(PicState2 *s); +uint32_t pic_intack_read(PicState2 *s); void pic_info(void); void irq_info(void); /* APIC */ +typedef struct IOAPICState IOAPICState; + int apic_init(CPUState *env); int apic_get_interrupt(CPUState *env); +IOAPICState *ioapic_init(void); +void ioapic_set_irq(void *opaque, int vector, int level); /* i8254.c */ @@ -674,24 +723,16 @@ int pit_get_gate(PITState *pit, int channel); int pit_get_out(PITState *pit, int channel, int64_t current_time); /* pc.c */ -void pc_init(int ram_size, int vga_ram_size, int boot_device, - DisplayState *ds, const char **fd_filename, int snapshot, - const char *kernel_filename, const char *kernel_cmdline, - const char *initrd_filename); +extern QEMUMachine pc_machine; /* ppc.c */ -void ppc_init (int ram_size, int vga_ram_size, int boot_device, - DisplayState *ds, const char **fd_filename, int snapshot, - const char *kernel_filename, const char *kernel_cmdline, - const char *initrd_filename); -void ppc_prep_init (int ram_size, int vga_ram_size, int boot_device, - DisplayState *ds, const char **fd_filename, int snapshot, - const char *kernel_filename, const char *kernel_cmdline, - const char *initrd_filename); -void ppc_chrp_init(int ram_size, int vga_ram_size, int boot_device, - DisplayState *ds, const char **fd_filename, int snapshot, - const char *kernel_filename, const char *kernel_cmdline, - const char *initrd_filename); +extern QEMUMachine prep_machine; +extern QEMUMachine core99_machine; +extern QEMUMachine heathrow_machine; + +/* mips_r4k.c */ +extern QEMUMachine mips_machine; + #ifdef TARGET_PPC ppc_tb_t *cpu_ppc_tb_init (CPUState *env, uint32_t freq); #endif @@ -700,12 +741,10 @@ void PREP_debug_write (void *opaque, uint32_t addr, uint32_t val); extern CPUWriteMemoryFunc *PPC_io_write[]; extern CPUReadMemoryFunc *PPC_io_read[]; extern int prep_enabled; +void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val); /* sun4m.c */ -void sun4m_init(int ram_size, int vga_ram_size, int boot_device, - DisplayState *ds, const char **fd_filename, int snapshot, - const char *kernel_filename, const char *kernel_cmdline, - const char *initrd_filename); +extern QEMUMachine sun4m_machine; uint32_t iommu_translate(uint32_t addr); /* iommu.c */ @@ -739,9 +778,16 @@ void slavio_timer_init(uint32_t addr1, int irq1, uint32_t addr2, int irq2); SerialState *slavio_serial_init(int base, int irq, CharDriverState *chr1, CharDriverState *chr2); void slavio_serial_ms_kbd_init(int base, int irq); +/* slavio_misc.c */ +void *slavio_misc_init(uint32_t base, int irq); +void slavio_set_power_fail(void *opaque, int power_failing); + /* esp.c */ void esp_init(BlockDriverState **bd, int irq, uint32_t espaddr, uint32_t espdaddr); +/* sun4u.c */ +extern QEMUMachine sun4u_machine; + /* NVRAM helpers */ #include "hw/m48t59.h" @@ -807,7 +853,7 @@ void adb_mouse_init(ADBBusState *bus); /* cuda.c */ extern ADBBusState adb_bus; -int cuda_init(openpic_t *openpic, int irq); +int cuda_init(SetIRQFunc *set_irq, void *irq_opaque, int irq); #endif /* defined(QEMU_TOOL) */ -- 1.7.9.5