diff options
102 files changed, 16959 insertions, 10594 deletions
@@ -260,7 +260,6 @@ pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \ pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \ qemu-icon.bmp \ bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \ -mpc8544ds.dtb \ multiboot.bin linuxboot.bin kvmvapic.bin \ s390-zipl.rom \ spapr-rtas.bin slof.bin \ diff --git a/Makefile.target b/Makefile.target index 550d8897a3..8f12b0fe88 100644 --- a/Makefile.target +++ b/Makefile.target @@ -216,4 +216,4 @@ GENERATED_HEADERS += config-target.h Makefile: $(GENERATED_HEADERS) # Include automatically generated dependency files --include $(wildcard *.d) +-include $(wildcard *.d fpu/*.d tcg/*.d) diff --git a/block/raw-posix.c b/block/raw-posix.c index bf7700a238..0dce089be5 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -606,7 +606,6 @@ static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum) { - BDRVRawState *s = bs->opaque; off_t start, data, hole; int ret; @@ -616,11 +615,15 @@ static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs, } start = sector_num * BDRV_SECTOR_SIZE; + #ifdef CONFIG_FIEMAP + + BDRVRawState *s = bs->opaque; struct { struct fiemap fm; struct fiemap_extent fe; } f; + f.fm.fm_start = start; f.fm.fm_length = (int64_t)nb_sectors * BDRV_SECTOR_SIZE; f.fm.fm_flags = 0; @@ -643,7 +646,11 @@ static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs, data = f.fe.fe_logical; hole = f.fe.fe_logical + f.fe.fe_length; } + #elif defined SEEK_HOLE && defined SEEK_DATA + + BDRVRawState *s = bs->opaque; + hole = lseek(s->fd, start, SEEK_HOLE); if (hole == -1) { /* -ENXIO indicates that sector_num was past the end of the file. @@ -137,6 +137,7 @@ vnc_png="" vnc_thread="no" xen="" xen_ctrl_version="" +xen_pci_passthrough="" linux_aio="" cap_ng="" attr="" @@ -684,6 +685,10 @@ for opt do ;; --enable-xen) xen="yes" ;; + --disable-xen-pci-passthrough) xen_pci_passthrough="no" + ;; + --enable-xen-pci-passthrough) xen_pci_passthrough="yes" + ;; --disable-brlapi) brlapi="no" ;; --enable-brlapi) brlapi="yes" @@ -1031,6 +1036,8 @@ echo " (affects only QEMU, not qemu-img)" echo " --enable-mixemu enable mixer emulation" echo " --disable-xen disable xen backend driver support" echo " --enable-xen enable xen backend driver support" +echo " --disable-xen-pci-passthrough" +echo " --enable-xen-pci-passthrough" echo " --disable-brlapi disable BrlAPI" echo " --enable-brlapi enable BrlAPI" echo " --disable-vnc-tls disable TLS encryption for VNC server" @@ -1381,7 +1388,7 @@ EOF elif ( cat > $TMPC <<EOF #include <xenctrl.h> -#include <xs.h> +#include <xenstore.h> #include <stdint.h> #include <xen/hvm/hvm_info_table.h> #if !defined(HVM_MAX_VCPUS) @@ -1507,6 +1514,25 @@ EOF fi fi +if test "$xen_pci_passthrough" != "no"; then + if test "$xen" = "yes" && test "$linux" = "yes" && + test "$xen_ctrl_version" -ge 340; then + xen_pci_passthrough=yes + else + if test "$xen_pci_passthrough" = "yes"; then + echo "ERROR" + echo "ERROR: User requested feature Xen PCI Passthrough" + echo "ERROR: but this feature require /sys from Linux" + if test "$xen_ctrl_version" -lt 340; then + echo "ERROR: This feature does not work with Xen 3.3" + fi + echo "ERROR" + exit 1; + fi + xen_pci_passthrough=no + fi +fi + ########################################## # pkg-config probe @@ -3679,7 +3705,7 @@ symlink "$source_path/Makefile.target" "$target_dir/Makefile" case "$target_arch2" in - alpha | sparc* | xtensa*) + alpha | sparc* | xtensa* | ppc*) echo "CONFIG_TCG_PASS_AREG0=y" >> $config_target_mak ;; esac @@ -3702,6 +3728,9 @@ case "$target_arch2" in if test "$xen" = "yes" -a "$target_softmmu" = "yes" ; then target_phys_bits=64 echo "CONFIG_XEN=y" >> $config_target_mak + if test "$xen_pci_passthrough" = yes; then + echo "CONFIG_XEN_PCI_PASSTHROUGH=y" >> "$config_target_mak" + fi else echo "CONFIG_NO_XEN=y" >> $config_target_mak fi @@ -291,6 +291,15 @@ extern unsigned long reserved_va; #define stfl_kernel(p, v) stfl_raw(p, v) #define stfq_kernel(p, vt) stfq_raw(p, v) +#ifdef CONFIG_TCG_PASS_AREG0 +#define cpu_ldub_data(env, addr) ldub_raw(addr) +#define cpu_lduw_data(env, addr) lduw_raw(addr) +#define cpu_ldl_data(env, addr) ldl_raw(addr) + +#define cpu_stb_data(env, addr, data) stb_raw(addr, data) +#define cpu_stw_data(env, addr, data) stw_raw(addr, data) +#define cpu_stl_data(env, addr, data) stl_raw(addr, data) +#endif #endif /* defined(CONFIG_USER_ONLY) */ /* page related stuff */ diff --git a/device_tree.c b/device_tree.c index 86a694c955..b366fddeaf 100644 --- a/device_tree.c +++ b/device_tree.c @@ -22,9 +22,48 @@ #include "qemu-common.h" #include "device_tree.h" #include "hw/loader.h" +#include "qemu-option.h" +#include "qemu-config.h" #include <libfdt.h> +#define FDT_MAX_SIZE 0x10000 + +void *create_device_tree(int *sizep) +{ + void *fdt; + int ret; + + *sizep = FDT_MAX_SIZE; + fdt = g_malloc0(FDT_MAX_SIZE); + ret = fdt_create(fdt, FDT_MAX_SIZE); + if (ret < 0) { + goto fail; + } + ret = fdt_begin_node(fdt, ""); + if (ret < 0) { + goto fail; + } + ret = fdt_end_node(fdt); + if (ret < 0) { + goto fail; + } + ret = fdt_finish(fdt); + if (ret < 0) { + goto fail; + } + ret = fdt_open_into(fdt, fdt, *sizep); + if (ret) { + fprintf(stderr, "Unable to copy device tree in memory\n"); + exit(1); + } + + return fdt; +fail: + fprintf(stderr, "%s Couldn't create dt: %s\n", __func__, fdt_strerror(ret)); + exit(1); +} + void *load_device_tree(const char *filename_path, int *sizep) { int dt_size; @@ -88,7 +127,7 @@ static int findnode_nofail(void *fdt, const char *node_path) } int qemu_devtree_setprop(void *fdt, const char *node_path, - const char *property, void *val_array, int size) + const char *property, const void *val_array, int size) { int r; @@ -117,6 +156,13 @@ int qemu_devtree_setprop_cell(void *fdt, const char *node_path, return r; } +int qemu_devtree_setprop_u64(void *fdt, const char *node_path, + const char *property, uint64_t val) +{ + val = cpu_to_be64(val); + return qemu_devtree_setprop(fdt, node_path, property, &val, sizeof(val)); +} + int qemu_devtree_setprop_string(void *fdt, const char *node_path, const char *property, const char *string) { @@ -132,6 +178,59 @@ int qemu_devtree_setprop_string(void *fdt, const char *node_path, return r; } +uint32_t qemu_devtree_get_phandle(void *fdt, const char *path) +{ + uint32_t r; + + r = fdt_get_phandle(fdt, findnode_nofail(fdt, path)); + if (r <= 0) { + fprintf(stderr, "%s: Couldn't get phandle for %s: %s\n", __func__, + path, fdt_strerror(r)); + exit(1); + } + + return r; +} + +int qemu_devtree_setprop_phandle(void *fdt, const char *node_path, + const char *property, + const char *target_node_path) +{ + uint32_t phandle = qemu_devtree_get_phandle(fdt, target_node_path); + return qemu_devtree_setprop_cell(fdt, node_path, property, phandle); +} + +uint32_t qemu_devtree_alloc_phandle(void *fdt) +{ + static int phandle = 0x0; + + /* + * We need to find out if the user gave us special instruction at + * which phandle id to start allocting phandles. + */ + if (!phandle) { + QemuOpts *machine_opts; + machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0); + if (machine_opts) { + const char *phandle_start; + phandle_start = qemu_opt_get(machine_opts, "phandle_start"); + if (phandle_start) { + phandle = strtoul(phandle_start, NULL, 0); + } + } + } + + if (!phandle) { + /* + * None or invalid phandle given on the command line, so fall back to + * default starting point. + */ + phandle = 0x8000; + } + + return phandle++; +} + int qemu_devtree_nop_node(void *fdt, const char *node_path) { int r; @@ -151,6 +250,7 @@ int qemu_devtree_add_subnode(void *fdt, const char *name) char *dupname = g_strdup(name); char *basename = strrchr(dupname, '/'); int retval; + int parent = 0; if (!basename) { g_free(dupname); @@ -160,7 +260,11 @@ int qemu_devtree_add_subnode(void *fdt, const char *name) basename[0] = '\0'; basename++; - retval = fdt_add_subnode(fdt, findnode_nofail(fdt, dupname), basename); + if (dupname[0]) { + parent = findnode_nofail(fdt, dupname); + } + + retval = fdt_add_subnode(fdt, parent, basename); if (retval < 0) { fprintf(stderr, "FDT: Failed to create subnode %s: %s\n", name, fdt_strerror(retval)); diff --git a/device_tree.h b/device_tree.h index 4378685b7a..2244270b2d 100644 --- a/device_tree.h +++ b/device_tree.h @@ -14,15 +14,35 @@ #ifndef __DEVICE_TREE_H__ #define __DEVICE_TREE_H__ +void *create_device_tree(int *sizep); void *load_device_tree(const char *filename_path, int *sizep); int qemu_devtree_setprop(void *fdt, const char *node_path, - const char *property, void *val_array, int size); + const char *property, const void *val_array, int size); int qemu_devtree_setprop_cell(void *fdt, const char *node_path, const char *property, uint32_t val); +int qemu_devtree_setprop_u64(void *fdt, const char *node_path, + const char *property, uint64_t val); int qemu_devtree_setprop_string(void *fdt, const char *node_path, const char *property, const char *string); +int qemu_devtree_setprop_phandle(void *fdt, const char *node_path, + const char *property, + const char *target_node_path); +uint32_t qemu_devtree_get_phandle(void *fdt, const char *path); +uint32_t qemu_devtree_alloc_phandle(void *fdt); int qemu_devtree_nop_node(void *fdt, const char *node_path); int qemu_devtree_add_subnode(void *fdt, const char *name); +#define qemu_devtree_setprop_cells(fdt, node_path, property, ...) \ + do { \ + uint32_t qdt_tmp[] = { __VA_ARGS__ }; \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(qdt_tmp); i++) { \ + qdt_tmp[i] = cpu_to_be32(qdt_tmp[i]); \ + } \ + qemu_devtree_setprop(fdt, node_path, property, qdt_tmp, \ + sizeof(qdt_tmp)); \ + } while (0) + #endif /* __DEVICE_TREE_H__ */ diff --git a/docs/specs/ppc-spapr-hcalls.txt b/docs/specs/ppc-spapr-hcalls.txt new file mode 100644 index 0000000000..52ba8d42ab --- /dev/null +++ b/docs/specs/ppc-spapr-hcalls.txt @@ -0,0 +1,78 @@ +When used with the "pseries" machine type, QEMU-system-ppc64 implements +a set of hypervisor calls using a subset of the server "PAPR" specification +(IBM internal at this point), which is also what IBM's proprietary hypervisor +adheres too. + +The subset is selected based on the requirements of Linux as a guest. + +In addition to those calls, we have added our own private hypervisor +calls which are mostly used as a private interface between the firmware +running in the guest and QEMU. + +All those hypercalls start at hcall number 0xf000 which correspond +to a implementation specific range in PAPR. + +- H_RTAS (0xf000) + +RTAS is a set of runtime services generally provided by the firmware +inside the guest to the operating system. It predates the existence +of hypervisors (it was originally an extension to Open Firmware) and +is still used by PAPR to provide various services that aren't performance +sensitive. + +We currently implement the RTAS services in QEMU itself. The actual RTAS +"firmware" blob in the guest is a small stub of a few instructions which +calls our private H_RTAS hypervisor call to pass the RTAS calls to QEMU. + +Arguments: + + r3 : H_RTAS (0xf000) + r4 : Guest physical address of RTAS parameter block + +Returns: + + H_SUCCESS : Successully called the RTAS function (RTAS result + will have been stored in the parameter block) + H_PARAMETER : Unknown token + +- H_LOGICAL_MEMOP (0xf001) + +When the guest runs in "real mode" (in powerpc lingua this means +with MMU disabled, ie guest effective == guest physical), it only +has access to a subset of memory and no IOs. + +PAPR provides a set of hypervisor calls to perform cachable or +non-cachable accesses to any guest physical addresses that the +guest can use in order to access IO devices while in real mode. + +This is typically used by the firmware running in the guest. + +However, doing a hypercall for each access is extremely inefficient +(even more so when running KVM) when accessing the frame buffer. In +that case, things like scrolling become unusably slow. + +This hypercall allows the guest to request a "memory op" to be applied +to memory. The supported memory ops at this point are to copy a range +of memory (supports overlap of source and destination) and XOR which +is used by our SLOF firmware to invert the screen. + +Arguments: + + r3: H_LOGICAL_MEMOP (0xf001) + r4: Guest physical address of destination + r5: Guest physical address of source + r6: Individual element size + 0 = 1 byte + 1 = 2 bytes + 2 = 4 bytes + 3 = 8 bytes + r7: Number of elements + r8: Operation + 0 = copy + 1 = xor + +Returns: + + H_SUCCESS : Success + H_PARAMETER : Invalid argument + diff --git a/hw/a15mpcore.c b/hw/a15mpcore.c index 5a7b365548..fc0a02ae86 100644 --- a/hw/a15mpcore.c +++ b/hw/a15mpcore.c @@ -44,6 +44,7 @@ static int a15mp_priv_init(SysBusDevice *dev) s->gic = qdev_create(NULL, "arm_gic"); qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu); qdev_prop_set_uint32(s->gic, "num-irq", s->num_irq); + qdev_prop_set_uint32(s->gic, "revision", 2); qdev_init_nofail(s->gic); busdev = sysbus_from_qdev(s->gic); diff --git a/hw/a9mpcore.c b/hw/a9mpcore.c index c2ff74d4b6..ebd5b29173 100644 --- a/hw/a9mpcore.c +++ b/hw/a9mpcore.c @@ -75,7 +75,7 @@ static void a9_scu_write(void *opaque, target_phys_addr_t offset, break; default: fprintf(stderr, "Invalid size %u in write to a9 scu register %x\n", - size, offset); + size, (unsigned)offset); return; } diff --git a/hw/apic-msidef.h b/hw/apic-msidef.h new file mode 100644 index 0000000000..6e2eb71f2f --- /dev/null +++ b/hw/apic-msidef.h @@ -0,0 +1,30 @@ +#ifndef HW_APIC_MSIDEF_H +#define HW_APIC_MSIDEF_H + +/* + * Intel APIC constants: from include/asm/msidef.h + */ + +/* + * Shifts for MSI data + */ + +#define MSI_DATA_VECTOR_SHIFT 0 +#define MSI_DATA_VECTOR_MASK 0x000000ff + +#define MSI_DATA_DELIVERY_MODE_SHIFT 8 +#define MSI_DATA_LEVEL_SHIFT 14 +#define MSI_DATA_TRIGGER_SHIFT 15 + +/* + * Shift/mask fields for msi address + */ + +#define MSI_ADDR_DEST_MODE_SHIFT 2 + +#define MSI_ADDR_REDIRECTION_SHIFT 3 + +#define MSI_ADDR_DEST_ID_SHIFT 12 +#define MSI_ADDR_DEST_ID_MASK 0x00ffff0 + +#endif /* HW_APIC_MSIDEF_H */ @@ -23,19 +23,10 @@ #include "host-utils.h" #include "trace.h" #include "pc.h" +#include "apic-msidef.h" #define MAX_APIC_WORDS 8 -/* Intel APIC constants: from include/asm/msidef.h */ -#define MSI_DATA_VECTOR_SHIFT 0 -#define MSI_DATA_VECTOR_MASK 0x000000ff -#define MSI_DATA_DELIVERY_MODE_SHIFT 8 -#define MSI_DATA_TRIGGER_SHIFT 15 -#define MSI_DATA_LEVEL_SHIFT 14 -#define MSI_ADDR_DEST_MODE_SHIFT 2 -#define MSI_ADDR_DEST_ID_SHIFT 12 -#define MSI_ADDR_DEST_ID_MASK 0x00ffff0 - #define SYNC_FROM_VAPIC 0x1 #define SYNC_TO_VAPIC 0x2 #define SYNC_ISR_IRR_TO_VAPIC 0x4 diff --git a/hw/arm-misc.h b/hw/arm-misc.h index 1d51570c88..1f96229d3c 100644 --- a/hw/arm-misc.h +++ b/hw/arm-misc.h @@ -45,9 +45,9 @@ struct arm_boot_info { /* multicore boards that use the default secondary core boot functions * can ignore these two function calls. If the default functions won't * work, then write_secondary_boot() should write a suitable blob of - * code mimicing the secondary CPU startup process used by the board's + * code mimicking the secondary CPU startup process used by the board's * boot loader/boot ROM code, and secondary_cpu_reset_hook() should - * perform any necessary CPU reset handling and set the PC for thei + * perform any necessary CPU reset handling and set the PC for the * secondary CPUs to point at this boot blob. */ void (*write_secondary_boot)(ARMCPU *cpu, diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index a0ff6a62d6..88ff47d95e 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -6,7 +6,7 @@ obj-y += cadence_uart.o obj-y += cadence_ttc.o obj-y += cadence_gem.o obj-y += xilinx_zynq.o zynq_slcr.o -obj-y += arm_gic.o +obj-y += arm_gic.o arm_gic_common.o obj-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o obj-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o obj-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o diff --git a/hw/arm11mpcore.c b/hw/arm11mpcore.c index c528d7aa01..1bff3d3282 100644 --- a/hw/arm11mpcore.c +++ b/hw/arm11mpcore.c @@ -123,6 +123,8 @@ static int mpcore_priv_init(SysBusDevice *dev) s->gic = qdev_create(NULL, "arm_gic"); qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu); qdev_prop_set_uint32(s->gic, "num-irq", s->num_irq); + /* Request the legacy 11MPCore GIC behaviour: */ + qdev_prop_set_uint32(s->gic, "revision", 0); qdev_init_nofail(s->gic); /* Pass through outbound IRQ lines from the GIC */ diff --git a/hw/arm_boot.c b/hw/arm_boot.c index d0e643ba11..a1e6ddbc1c 100644 --- a/hw/arm_boot.c +++ b/hw/arm_boot.c @@ -242,10 +242,12 @@ static int load_dtb(target_phys_addr_t addr, const struct arm_boot_info *binfo) fprintf(stderr, "couldn't set /memory/reg\n"); } - rc = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", - binfo->kernel_cmdline); - if (rc < 0) { - fprintf(stderr, "couldn't set /chosen/bootargs\n"); + if (binfo->kernel_cmdline && *binfo->kernel_cmdline) { + rc = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", + binfo->kernel_cmdline); + if (rc < 0) { + fprintf(stderr, "couldn't set /chosen/bootargs\n"); + } } if (binfo->initrd_size) { diff --git a/hw/arm_gic.c b/hw/arm_gic.c index 72298b4b41..ec22322930 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -19,17 +19,7 @@ */ #include "sysbus.h" - -/* Maximum number of possible interrupts, determined by the GIC architecture */ -#define GIC_MAXIRQ 1020 -/* First 32 are private to each CPU (SGIs and PPIs). */ -#define GIC_INTERNAL 32 -/* Maximum number of possible CPU interfaces, determined by GIC architecture */ -#ifdef NVIC -#define NCPU 1 -#else -#define NCPU 8 -#endif +#include "arm_gic_internal.h" //#define DEBUG_GIC @@ -40,114 +30,23 @@ do { printf("arm_gic: " fmt , ## __VA_ARGS__); } while (0) #define DPRINTF(fmt, ...) do {} while(0) #endif -#ifdef NVIC -static const uint8_t gic_id[] = -{ 0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1 }; -/* The NVIC has 16 internal vectors. However these are not exposed - through the normal GIC interface. */ -#define GIC_BASE_IRQ 32 -#else -static const uint8_t gic_id[] = -{ 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; -#define GIC_BASE_IRQ 0 -#endif - -#define FROM_SYSBUSGIC(type, dev) \ - DO_UPCAST(type, gic, FROM_SYSBUS(gic_state, dev)) +static const uint8_t gic_id[] = { + 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 +}; -typedef struct gic_irq_state -{ - /* The enable bits are only banked for per-cpu interrupts. */ - unsigned enabled:NCPU; - unsigned pending:NCPU; - unsigned active:NCPU; - unsigned level:NCPU; - unsigned model:1; /* 0 = N:N, 1 = 1:N */ - unsigned trigger:1; /* nonzero = edge triggered. */ -} gic_irq_state; - -#define ALL_CPU_MASK ((unsigned)(((1 << NCPU) - 1))) -#if NCPU > 1 #define NUM_CPU(s) ((s)->num_cpu) -#else -#define NUM_CPU(s) 1 -#endif - -#define GIC_SET_ENABLED(irq, cm) s->irq_state[irq].enabled |= (cm) -#define GIC_CLEAR_ENABLED(irq, cm) s->irq_state[irq].enabled &= ~(cm) -#define GIC_TEST_ENABLED(irq, cm) ((s->irq_state[irq].enabled & (cm)) != 0) -#define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm) -#define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm) -#define GIC_TEST_PENDING(irq, cm) ((s->irq_state[irq].pending & (cm)) != 0) -#define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm) -#define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm) -#define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0) -#define GIC_SET_MODEL(irq) s->irq_state[irq].model = 1 -#define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = 0 -#define GIC_TEST_MODEL(irq) s->irq_state[irq].model -#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level = (cm) -#define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm) -#define GIC_TEST_LEVEL(irq, cm) ((s->irq_state[irq].level & (cm)) != 0) -#define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1 -#define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0 -#define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger -#define GIC_GET_PRIORITY(irq, cpu) (((irq) < GIC_INTERNAL) ? \ - s->priority1[irq][cpu] : \ - s->priority2[(irq) - GIC_INTERNAL]) -#ifdef NVIC -#define GIC_TARGET(irq) 1 -#else -#define GIC_TARGET(irq) s->irq_target[irq] -#endif - -typedef struct gic_state -{ - SysBusDevice busdev; - qemu_irq parent_irq[NCPU]; - int enabled; - int cpu_enabled[NCPU]; - - gic_irq_state irq_state[GIC_MAXIRQ]; -#ifndef NVIC - int irq_target[GIC_MAXIRQ]; -#endif - int priority1[GIC_INTERNAL][NCPU]; - int priority2[GIC_MAXIRQ - GIC_INTERNAL]; - int last_active[GIC_MAXIRQ][NCPU]; - - int priority_mask[NCPU]; - int running_irq[NCPU]; - int running_priority[NCPU]; - int current_pending[NCPU]; - -#if NCPU > 1 - uint32_t num_cpu; -#endif - - MemoryRegion iomem; /* Distributor */ -#ifndef NVIC - /* This is just so we can have an opaque pointer which identifies - * both this GIC and which CPU interface we should be accessing. - */ - struct gic_state *backref[NCPU]; - MemoryRegion cpuiomem[NCPU+1]; /* CPU interfaces */ -#endif - uint32_t num_irq; -} gic_state; static inline int gic_get_current_cpu(gic_state *s) { -#if NCPU > 1 if (s->num_cpu > 1) { return cpu_single_env->cpu_index; } -#endif return 0; } /* TODO: Many places that call this routine could be optimized. */ /* Update interrupt status after enabled or pending bits have been changed. */ -static void gic_update(gic_state *s) +void gic_update(gic_state *s) { int best_irq; int best_prio; @@ -185,8 +84,7 @@ static void gic_update(gic_state *s) } } -#ifdef NVIC -static void gic_set_pending_private(gic_state *s, int cpu, int irq) +void gic_set_pending_private(gic_state *s, int cpu, int irq) { int cm = 1 << cpu; @@ -197,7 +95,6 @@ static void gic_set_pending_private(gic_state *s, int cpu, int irq) GIC_SET_PENDING(irq, cm); gic_update(s); } -#endif /* Process a change in an external IRQ input. */ static void gic_set_irq(void *opaque, int irq, int level) @@ -251,7 +148,7 @@ static void gic_set_running_irq(gic_state *s, int cpu, int irq) gic_update(s); } -static uint32_t gic_acknowledge_irq(gic_state *s, int cpu) +uint32_t gic_acknowledge_irq(gic_state *s, int cpu) { int new_irq; int cm = 1 << cpu; @@ -270,7 +167,7 @@ static uint32_t gic_acknowledge_irq(gic_state *s, int cpu) return new_irq; } -static void gic_complete_irq(gic_state * s, int cpu, int irq) +void gic_complete_irq(gic_state *s, int cpu, int irq) { int update = 0; int cm = 1 << cpu; @@ -328,7 +225,6 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) cpu = gic_get_current_cpu(s); cm = 1 << cpu; if (offset < 0x100) { -#ifndef NVIC if (offset == 0) return s->enabled; if (offset == 4) @@ -339,7 +235,6 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) /* Interrupt Security , RAZ/WI */ return 0; } -#endif goto bad_reg; } else if (offset < 0x200) { /* Interrupt Set/Clear Enable. */ @@ -390,16 +285,21 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) if (irq >= s->num_irq) goto bad_reg; res = GIC_GET_PRIORITY(irq, cpu); -#ifndef NVIC } else if (offset < 0xc00) { /* Interrupt CPU Target. */ - irq = (offset - 0x800) + GIC_BASE_IRQ; - if (irq >= s->num_irq) - goto bad_reg; - if (irq >= 29 && irq <= 31) { - res = cm; + if (s->num_cpu == 1 && s->revision != REV_11MPCORE) { + /* For uniprocessor GICs these RAZ/WI */ + res = 0; } else { - res = GIC_TARGET(irq); + irq = (offset - 0x800) + GIC_BASE_IRQ; + if (irq >= s->num_irq) { + goto bad_reg; + } + if (irq >= 29 && irq <= 31) { + res = cm; + } else { + res = GIC_TARGET(irq); + } } } else if (offset < 0xf00) { /* Interrupt Configuration. */ @@ -413,7 +313,6 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) if (GIC_TEST_TRIGGER(irq + i)) res |= (2 << (i * 2)); } -#endif } else if (offset < 0xfe0) { goto bad_reg; } else /* offset >= 0xfe0 */ { @@ -440,13 +339,6 @@ static uint32_t gic_dist_readw(void *opaque, target_phys_addr_t offset) static uint32_t gic_dist_readl(void *opaque, target_phys_addr_t offset) { uint32_t val; -#ifdef NVIC - gic_state *s = (gic_state *)opaque; - uint32_t addr; - addr = offset; - if (addr < 0x100 || addr > 0xd00) - return nvic_readl(s, addr); -#endif val = gic_dist_readw(opaque, offset); val |= gic_dist_readw(opaque, offset + 2) << 16; return val; @@ -462,9 +354,6 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, cpu = gic_get_current_cpu(s); if (offset < 0x100) { -#ifdef NVIC - goto bad_reg; -#else if (offset == 0) { s->enabled = (value & 1); DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis"); @@ -475,7 +364,6 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, } else { goto bad_reg; } -#endif } else if (offset < 0x180) { /* Interrupt Set Enable. */ irq = (offset - 0x100) * 8 + GIC_BASE_IRQ; @@ -557,17 +445,22 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, } else { s->priority2[irq - GIC_INTERNAL] = value; } -#ifndef NVIC } else if (offset < 0xc00) { - /* Interrupt CPU Target. */ - irq = (offset - 0x800) + GIC_BASE_IRQ; - if (irq >= s->num_irq) - goto bad_reg; - if (irq < 29) - value = 0; - else if (irq < GIC_INTERNAL) - value = ALL_CPU_MASK; - s->irq_target[irq] = value & ALL_CPU_MASK; + /* Interrupt CPU Target. RAZ/WI on uniprocessor GICs, with the + * annoying exception of the 11MPCore's GIC. + */ + if (s->num_cpu != 1 || s->revision == REV_11MPCORE) { + irq = (offset - 0x800) + GIC_BASE_IRQ; + if (irq >= s->num_irq) { + goto bad_reg; + } + if (irq < 29) { + value = 0; + } else if (irq < GIC_INTERNAL) { + value = ALL_CPU_MASK; + } + s->irq_target[irq] = value & ALL_CPU_MASK; + } } else if (offset < 0xf00) { /* Interrupt Configuration. */ irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ; @@ -587,7 +480,6 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, GIC_CLEAR_TRIGGER(irq + i); } } -#endif } else { /* 0xf00 is only handled for 32-bit writes. */ goto bad_reg; @@ -609,14 +501,6 @@ static void gic_dist_writel(void *opaque, target_phys_addr_t offset, uint32_t value) { gic_state *s = (gic_state *)opaque; -#ifdef NVIC - uint32_t addr; - addr = offset; - if (addr < 0x100 || (addr > 0xd00 && addr != 0xf00)) { - nvic_writel(s, addr, value); - return; - } -#endif if (offset == 0xf00) { int cpu; int irq; @@ -655,7 +539,6 @@ static const MemoryRegionOps gic_dist_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -#ifndef NVIC static uint32_t gic_cpu_read(gic_state *s, int cpu, int offset) { switch (offset) { @@ -747,141 +630,12 @@ static const MemoryRegionOps gic_cpu_ops = { .write = gic_do_cpu_write, .endianness = DEVICE_NATIVE_ENDIAN, }; -#endif - -static void gic_reset(DeviceState *dev) -{ - gic_state *s = FROM_SYSBUS(gic_state, sysbus_from_qdev(dev)); - int i; - memset(s->irq_state, 0, GIC_MAXIRQ * sizeof(gic_irq_state)); - for (i = 0 ; i < NUM_CPU(s); i++) { - s->priority_mask[i] = 0xf0; - s->current_pending[i] = 1023; - s->running_irq[i] = 1023; - s->running_priority[i] = 0x100; -#ifdef NVIC - /* The NVIC doesn't have per-cpu interfaces, so enable by default. */ - s->cpu_enabled[i] = 1; -#else - s->cpu_enabled[i] = 0; -#endif - } - for (i = 0; i < 16; i++) { - GIC_SET_ENABLED(i, ALL_CPU_MASK); - GIC_SET_TRIGGER(i); - } -#ifdef NVIC - /* The NVIC is always enabled. */ - s->enabled = 1; -#else - s->enabled = 0; -#endif -} -static void gic_save(QEMUFile *f, void *opaque) -{ - gic_state *s = (gic_state *)opaque; - int i; - int j; - - qemu_put_be32(f, s->enabled); - for (i = 0; i < NUM_CPU(s); i++) { - qemu_put_be32(f, s->cpu_enabled[i]); - for (j = 0; j < GIC_INTERNAL; j++) - qemu_put_be32(f, s->priority1[j][i]); - for (j = 0; j < s->num_irq; j++) - qemu_put_be32(f, s->last_active[j][i]); - qemu_put_be32(f, s->priority_mask[i]); - qemu_put_be32(f, s->running_irq[i]); - qemu_put_be32(f, s->running_priority[i]); - qemu_put_be32(f, s->current_pending[i]); - } - for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) { - qemu_put_be32(f, s->priority2[i]); - } - for (i = 0; i < s->num_irq; i++) { -#ifndef NVIC - qemu_put_be32(f, s->irq_target[i]); -#endif - qemu_put_byte(f, s->irq_state[i].enabled); - qemu_put_byte(f, s->irq_state[i].pending); - qemu_put_byte(f, s->irq_state[i].active); - qemu_put_byte(f, s->irq_state[i].level); - qemu_put_byte(f, s->irq_state[i].model); - qemu_put_byte(f, s->irq_state[i].trigger); - } -} - -static int gic_load(QEMUFile *f, void *opaque, int version_id) -{ - gic_state *s = (gic_state *)opaque; - int i; - int j; - - if (version_id != 2) - return -EINVAL; - - s->enabled = qemu_get_be32(f); - for (i = 0; i < NUM_CPU(s); i++) { - s->cpu_enabled[i] = qemu_get_be32(f); - for (j = 0; j < GIC_INTERNAL; j++) - s->priority1[j][i] = qemu_get_be32(f); - for (j = 0; j < s->num_irq; j++) - s->last_active[j][i] = qemu_get_be32(f); - s->priority_mask[i] = qemu_get_be32(f); - s->running_irq[i] = qemu_get_be32(f); - s->running_priority[i] = qemu_get_be32(f); - s->current_pending[i] = qemu_get_be32(f); - } - for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) { - s->priority2[i] = qemu_get_be32(f); - } - for (i = 0; i < s->num_irq; i++) { -#ifndef NVIC - s->irq_target[i] = qemu_get_be32(f); -#endif - s->irq_state[i].enabled = qemu_get_byte(f); - s->irq_state[i].pending = qemu_get_byte(f); - s->irq_state[i].active = qemu_get_byte(f); - s->irq_state[i].level = qemu_get_byte(f); - s->irq_state[i].model = qemu_get_byte(f); - s->irq_state[i].trigger = qemu_get_byte(f); - } - - return 0; -} - -#if NCPU > 1 -static void gic_init(gic_state *s, int num_cpu, int num_irq) -#else -static void gic_init(gic_state *s, int num_irq) -#endif +void gic_init_irqs_and_distributor(gic_state *s, int num_irq) { int i; -#if NCPU > 1 - s->num_cpu = num_cpu; - if (s->num_cpu > NCPU) { - hw_error("requested %u CPUs exceeds GIC maximum %d\n", - num_cpu, NCPU); - } -#endif - s->num_irq = num_irq + GIC_BASE_IRQ; - if (s->num_irq > GIC_MAXIRQ) { - hw_error("requested %u interrupt lines exceeds GIC maximum %d\n", - num_irq, GIC_MAXIRQ); - } - /* ITLinesNumber is represented as (N / 32) - 1 (see - * gic_dist_readb) so this is an implementation imposed - * restriction, not an architectural one: - */ - if (s->num_irq < 32 || (s->num_irq % 32)) { - hw_error("%d interrupt lines unsupported: not divisible by 32\n", - num_irq); - } - i = s->num_irq - GIC_INTERNAL; -#ifndef NVIC /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU. * GPIO array layout is thus: * [0..N-1] SPIs @@ -889,14 +643,27 @@ static void gic_init(gic_state *s, int num_irq) * [N+32..N+63] PPIs for CPU 1 * ... */ - i += (GIC_INTERNAL * num_cpu); -#endif + if (s->revision != REV_NVIC) { + i += (GIC_INTERNAL * s->num_cpu); + } qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, i); for (i = 0; i < NUM_CPU(s); i++) { sysbus_init_irq(&s->busdev, &s->parent_irq[i]); } memory_region_init_io(&s->iomem, &gic_dist_ops, s, "gic_dist", 0x1000); -#ifndef NVIC +} + +static int arm_gic_init(SysBusDevice *dev) +{ + /* Device instance init function for the GIC sysbus device */ + int i; + gic_state *s = FROM_SYSBUS(gic_state, dev); + ARMGICClass *agc = ARM_GIC_GET_CLASS(s); + + agc->parent_init(dev); + + gic_init_irqs_and_distributor(s, s->num_irq); + /* Memory regions for the CPU interfaces (NVIC doesn't have these): * a region for "CPU interface for this core", then a region for * "CPU interface for core 0", "for core 1", ... @@ -912,19 +679,6 @@ static void gic_init(gic_state *s, int num_irq) memory_region_init_io(&s->cpuiomem[i+1], &gic_cpu_ops, &s->backref[i], "gic_cpu", 0x100); } -#endif - - register_savevm(NULL, "arm_gic", -1, 2, gic_save, gic_load, s); -} - -#ifndef NVIC - -static int arm_gic_init(SysBusDevice *dev) -{ - /* Device instance init function for the GIC sysbus device */ - int i; - gic_state *s = FROM_SYSBUS(gic_state, dev); - gic_init(s, s->num_cpu, s->num_irq); /* Distributor */ sysbus_init_mmio(dev, &s->iomem); /* cpu interfaces (one for "current cpu" plus one per cpu) */ @@ -934,25 +688,19 @@ static int arm_gic_init(SysBusDevice *dev) return 0; } -static Property arm_gic_properties[] = { - DEFINE_PROP_UINT32("num-cpu", gic_state, num_cpu, 1), - DEFINE_PROP_UINT32("num-irq", gic_state, num_irq, 32), - DEFINE_PROP_END_OF_LIST(), -}; - static void arm_gic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); + ARMGICClass *agc = ARM_GIC_CLASS(klass); + agc->parent_init = sbc->init; sbc->init = arm_gic_init; - dc->props = arm_gic_properties; - dc->reset = gic_reset; dc->no_user = 1; } static TypeInfo arm_gic_info = { - .name = "arm_gic", - .parent = TYPE_SYS_BUS_DEVICE, + .name = TYPE_ARM_GIC, + .parent = TYPE_ARM_GIC_COMMON, .instance_size = sizeof(gic_state), .class_init = arm_gic_class_init, }; @@ -963,5 +711,3 @@ static void arm_gic_register_types(void) } type_init(arm_gic_register_types) - -#endif diff --git a/hw/arm_gic_common.c b/hw/arm_gic_common.c new file mode 100644 index 0000000000..360e7823f7 --- /dev/null +++ b/hw/arm_gic_common.c @@ -0,0 +1,184 @@ +/* + * ARM GIC support - common bits of emulated and KVM kernel model + * + * Copyright (c) 2012 Linaro Limited + * Written by Peter Maydell + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "arm_gic_internal.h" + +static void gic_save(QEMUFile *f, void *opaque) +{ + gic_state *s = (gic_state *)opaque; + int i; + int j; + + qemu_put_be32(f, s->enabled); + for (i = 0; i < s->num_cpu; i++) { + qemu_put_be32(f, s->cpu_enabled[i]); + for (j = 0; j < GIC_INTERNAL; j++) { + qemu_put_be32(f, s->priority1[j][i]); + } + for (j = 0; j < s->num_irq; j++) { + qemu_put_be32(f, s->last_active[j][i]); + } + qemu_put_be32(f, s->priority_mask[i]); + qemu_put_be32(f, s->running_irq[i]); + qemu_put_be32(f, s->running_priority[i]); + qemu_put_be32(f, s->current_pending[i]); + } + for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) { + qemu_put_be32(f, s->priority2[i]); + } + for (i = 0; i < s->num_irq; i++) { + qemu_put_be32(f, s->irq_target[i]); + qemu_put_byte(f, s->irq_state[i].enabled); + qemu_put_byte(f, s->irq_state[i].pending); + qemu_put_byte(f, s->irq_state[i].active); + qemu_put_byte(f, s->irq_state[i].level); + qemu_put_byte(f, s->irq_state[i].model); + qemu_put_byte(f, s->irq_state[i].trigger); + } +} + +static int gic_load(QEMUFile *f, void *opaque, int version_id) +{ + gic_state *s = (gic_state *)opaque; + int i; + int j; + + if (version_id != 3) { + return -EINVAL; + } + + s->enabled = qemu_get_be32(f); + for (i = 0; i < s->num_cpu; i++) { + s->cpu_enabled[i] = qemu_get_be32(f); + for (j = 0; j < GIC_INTERNAL; j++) { + s->priority1[j][i] = qemu_get_be32(f); + } + for (j = 0; j < s->num_irq; j++) { + s->last_active[j][i] = qemu_get_be32(f); + } + s->priority_mask[i] = qemu_get_be32(f); + s->running_irq[i] = qemu_get_be32(f); + s->running_priority[i] = qemu_get_be32(f); + s->current_pending[i] = qemu_get_be32(f); + } + for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) { + s->priority2[i] = qemu_get_be32(f); + } + for (i = 0; i < s->num_irq; i++) { + s->irq_target[i] = qemu_get_be32(f); + s->irq_state[i].enabled = qemu_get_byte(f); + s->irq_state[i].pending = qemu_get_byte(f); + s->irq_state[i].active = qemu_get_byte(f); + s->irq_state[i].level = qemu_get_byte(f); + s->irq_state[i].model = qemu_get_byte(f); + s->irq_state[i].trigger = qemu_get_byte(f); + } + + return 0; +} + +static int arm_gic_common_init(SysBusDevice *dev) +{ + gic_state *s = FROM_SYSBUS(gic_state, dev); + int num_irq = s->num_irq; + + if (s->num_cpu > NCPU) { + hw_error("requested %u CPUs exceeds GIC maximum %d\n", + s->num_cpu, NCPU); + } + s->num_irq += GIC_BASE_IRQ; + if (s->num_irq > GIC_MAXIRQ) { + hw_error("requested %u interrupt lines exceeds GIC maximum %d\n", + num_irq, GIC_MAXIRQ); + } + /* ITLinesNumber is represented as (N / 32) - 1 (see + * gic_dist_readb) so this is an implementation imposed + * restriction, not an architectural one: + */ + if (s->num_irq < 32 || (s->num_irq % 32)) { + hw_error("%d interrupt lines unsupported: not divisible by 32\n", + num_irq); + } + + register_savevm(NULL, "arm_gic", -1, 3, gic_save, gic_load, s); + return 0; +} + +static void arm_gic_common_reset(DeviceState *dev) +{ + gic_state *s = FROM_SYSBUS(gic_state, sysbus_from_qdev(dev)); + int i; + memset(s->irq_state, 0, GIC_MAXIRQ * sizeof(gic_irq_state)); + for (i = 0 ; i < s->num_cpu; i++) { + s->priority_mask[i] = 0xf0; + s->current_pending[i] = 1023; + s->running_irq[i] = 1023; + s->running_priority[i] = 0x100; + s->cpu_enabled[i] = 0; + } + for (i = 0; i < 16; i++) { + GIC_SET_ENABLED(i, ALL_CPU_MASK); + GIC_SET_TRIGGER(i); + } + if (s->num_cpu == 1) { + /* For uniprocessor GICs all interrupts always target the sole CPU */ + for (i = 0; i < GIC_MAXIRQ; i++) { + s->irq_target[i] = 1; + } + } + s->enabled = 0; +} + +static Property arm_gic_common_properties[] = { + DEFINE_PROP_UINT32("num-cpu", gic_state, num_cpu, 1), + DEFINE_PROP_UINT32("num-irq", gic_state, num_irq, 32), + /* Revision can be 1 or 2 for GIC architecture specification + * versions 1 or 2, or 0 to indicate the legacy 11MPCore GIC. + * (Internally, 0xffffffff also indicates "not a GIC but an NVIC".) + */ + DEFINE_PROP_UINT32("revision", gic_state, revision, 1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void arm_gic_common_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + dc->reset = arm_gic_common_reset; + dc->props = arm_gic_common_properties; + dc->no_user = 1; + sc->init = arm_gic_common_init; +} + +static TypeInfo arm_gic_common_type = { + .name = TYPE_ARM_GIC_COMMON, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(gic_state), + .class_size = sizeof(ARMGICCommonClass), + .class_init = arm_gic_common_class_init, + .abstract = true, +}; + +static void register_types(void) +{ + type_register_static(&arm_gic_common_type); +} + +type_init(register_types) diff --git a/hw/arm_gic_internal.h b/hw/arm_gic_internal.h new file mode 100644 index 0000000000..db4fad564f --- /dev/null +++ b/hw/arm_gic_internal.h @@ -0,0 +1,136 @@ +/* + * ARM GIC support - internal interfaces + * + * Copyright (c) 2012 Linaro Limited + * Written by Peter Maydell + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef QEMU_ARM_GIC_INTERNAL_H +#define QEMU_ARM_GIC_INTERNAL_H + +#include "sysbus.h" + +/* Maximum number of possible interrupts, determined by the GIC architecture */ +#define GIC_MAXIRQ 1020 +/* First 32 are private to each CPU (SGIs and PPIs). */ +#define GIC_INTERNAL 32 +/* Maximum number of possible CPU interfaces, determined by GIC architecture */ +#define NCPU 8 + +#define ALL_CPU_MASK ((unsigned)(((1 << NCPU) - 1))) + +/* The NVIC has 16 internal vectors. However these are not exposed + through the normal GIC interface. */ +#define GIC_BASE_IRQ ((s->revision == REV_NVIC) ? 32 : 0) + +#define GIC_SET_ENABLED(irq, cm) s->irq_state[irq].enabled |= (cm) +#define GIC_CLEAR_ENABLED(irq, cm) s->irq_state[irq].enabled &= ~(cm) +#define GIC_TEST_ENABLED(irq, cm) ((s->irq_state[irq].enabled & (cm)) != 0) +#define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm) +#define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm) +#define GIC_TEST_PENDING(irq, cm) ((s->irq_state[irq].pending & (cm)) != 0) +#define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm) +#define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm) +#define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0) +#define GIC_SET_MODEL(irq) s->irq_state[irq].model = 1 +#define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = 0 +#define GIC_TEST_MODEL(irq) s->irq_state[irq].model +#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level = (cm) +#define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm) +#define GIC_TEST_LEVEL(irq, cm) ((s->irq_state[irq].level & (cm)) != 0) +#define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1 +#define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0 +#define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger +#define GIC_GET_PRIORITY(irq, cpu) (((irq) < GIC_INTERNAL) ? \ + s->priority1[irq][cpu] : \ + s->priority2[(irq) - GIC_INTERNAL]) +#define GIC_TARGET(irq) s->irq_target[irq] + +typedef struct gic_irq_state { + /* The enable bits are only banked for per-cpu interrupts. */ + unsigned enabled:NCPU; + unsigned pending:NCPU; + unsigned active:NCPU; + unsigned level:NCPU; + unsigned model:1; /* 0 = N:N, 1 = 1:N */ + unsigned trigger:1; /* nonzero = edge triggered. */ +} gic_irq_state; + +typedef struct gic_state { + SysBusDevice busdev; + qemu_irq parent_irq[NCPU]; + int enabled; + int cpu_enabled[NCPU]; + + gic_irq_state irq_state[GIC_MAXIRQ]; + int irq_target[GIC_MAXIRQ]; + int priority1[GIC_INTERNAL][NCPU]; + int priority2[GIC_MAXIRQ - GIC_INTERNAL]; + int last_active[GIC_MAXIRQ][NCPU]; + + int priority_mask[NCPU]; + int running_irq[NCPU]; + int running_priority[NCPU]; + int current_pending[NCPU]; + + uint32_t num_cpu; + + MemoryRegion iomem; /* Distributor */ + /* This is just so we can have an opaque pointer which identifies + * both this GIC and which CPU interface we should be accessing. + */ + struct gic_state *backref[NCPU]; + MemoryRegion cpuiomem[NCPU+1]; /* CPU interfaces */ + uint32_t num_irq; + uint32_t revision; +} gic_state; + +/* The special cases for the revision property: */ +#define REV_11MPCORE 0 +#define REV_NVIC 0xffffffff + +void gic_set_pending_private(gic_state *s, int cpu, int irq); +uint32_t gic_acknowledge_irq(gic_state *s, int cpu); +void gic_complete_irq(gic_state *s, int cpu, int irq); +void gic_update(gic_state *s); +void gic_init_irqs_and_distributor(gic_state *s, int num_irq); + +#define TYPE_ARM_GIC_COMMON "arm_gic_common" +#define ARM_GIC_COMMON(obj) \ + OBJECT_CHECK(gic_state, (obj), TYPE_ARM_GIC_COMMON) +#define ARM_GIC_COMMON_CLASS(klass) \ + OBJECT_CLASS_CHECK(ARMGICCommonClass, (klass), TYPE_ARM_GIC_COMMON) +#define ARM_GIC_COMMON_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ARMGICCommonClass, (obj), TYPE_ARM_GIC_COMMON) + +typedef struct ARMGICCommonClass { + SysBusDeviceClass parent_class; +} ARMGICCommonClass; + +#define TYPE_ARM_GIC "arm_gic" +#define ARM_GIC(obj) \ + OBJECT_CHECK(gic_state, (obj), TYPE_ARM_GIC) +#define ARM_GIC_CLASS(klass) \ + OBJECT_CLASS_CHECK(ARMGICClass, (klass), TYPE_ARM_GIC) +#define ARM_GIC_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ARMGICClass, (obj), TYPE_ARM_GIC) + +typedef struct ARMGICClass { + ARMGICCommonClass parent_class; + int (*parent_init)(SysBusDevice *dev); +} ARMGICClass; + +#endif /* !QEMU_ARM_GIC_INTERNAL_H */ diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c index 986a6bbd0c..4867c1d5fa 100644 --- a/hw/armv7m_nvic.c +++ b/hw/armv7m_nvic.c @@ -14,13 +14,7 @@ #include "qemu-timer.h" #include "arm-misc.h" #include "exec-memory.h" - -#define NVIC 1 - -static uint32_t nvic_readl(void *opaque, uint32_t offset); -static void nvic_writel(void *opaque, uint32_t offset, uint32_t value); - -#include "arm_gic.c" +#include "arm_gic_internal.h" typedef struct { gic_state gic; @@ -30,9 +24,38 @@ typedef struct { int64_t tick; QEMUTimer *timer; } systick; + MemoryRegion sysregmem; + MemoryRegion gic_iomem_alias; + MemoryRegion container; uint32_t num_irq; } nvic_state; +#define TYPE_NVIC "armv7m_nvic" +/** + * NVICClass: + * @parent_reset: the parent class' reset handler. + * + * A model of the v7M NVIC and System Controller + */ +typedef struct NVICClass { + /*< private >*/ + ARMGICClass parent_class; + /*< public >*/ + int (*parent_init)(SysBusDevice *dev); + void (*parent_reset)(DeviceState *dev); +} NVICClass; + +#define NVIC_CLASS(klass) \ + OBJECT_CLASS_CHECK(NVICClass, (klass), TYPE_NVIC) +#define NVIC_GET_CLASS(obj) \ + OBJECT_GET_CLASS(NVICClass, (obj), TYPE_NVIC) +#define NVIC(obj) \ + OBJECT_CHECK(nvic_state, (obj), TYPE_NVIC) + +static const uint8_t nvic_id[] = { + 0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1 +}; + /* qemu timers run at 1GHz. We want something closer to 1MHz. */ #define SYSTICK_SCALE 1000ULL @@ -358,12 +381,54 @@ static void nvic_writel(void *opaque, uint32_t offset, uint32_t value) case 0xd38: /* Bus Fault Address. */ case 0xd3c: /* Aux Fault Status. */ goto bad_reg; + case 0xf00: /* Software Triggered Interrupt Register */ + if ((value & 0x1ff) < s->num_irq) { + gic_set_pending_private(&s->gic, 0, value & 0x1ff); + } + break; default: bad_reg: hw_error("NVIC: Bad write offset 0x%x\n", offset); } } +static uint64_t nvic_sysreg_read(void *opaque, target_phys_addr_t addr, + unsigned size) +{ + /* At the moment we only support the ID registers for byte/word access. + * This is not strictly correct as a few of the other registers also + * allow byte access. + */ + uint32_t offset = addr; + if (offset >= 0xfe0) { + if (offset & 3) { + return 0; + } + return nvic_id[(offset - 0xfe0) >> 2]; + } + if (size == 4) { + return nvic_readl(opaque, offset); + } + hw_error("NVIC: Bad read of size %d at offset 0x%x\n", size, offset); +} + +static void nvic_sysreg_write(void *opaque, target_phys_addr_t addr, + uint64_t value, unsigned size) +{ + uint32_t offset = addr; + if (size == 4) { + nvic_writel(opaque, offset, value); + return; + } + hw_error("NVIC: Bad write of size %d at offset 0x%x\n", size, offset); +} + +static const MemoryRegionOps nvic_sysreg_ops = { + .read = nvic_sysreg_read, + .write = nvic_sysreg_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + static const VMStateDescription vmstate_nvic = { .name = "armv7m_nvic", .version_id = 1, @@ -380,20 +445,55 @@ static const VMStateDescription vmstate_nvic = { static void armv7m_nvic_reset(DeviceState *dev) { - nvic_state *s = FROM_SYSBUSGIC(nvic_state, sysbus_from_qdev(dev)); - gic_reset(&s->gic.busdev.qdev); + nvic_state *s = NVIC(dev); + NVICClass *nc = NVIC_GET_CLASS(s); + nc->parent_reset(dev); + /* Common GIC reset resets to disabled; the NVIC doesn't have + * per-CPU interfaces so mark our non-existent CPU interface + * as enabled by default. + */ + s->gic.cpu_enabled[0] = 1; + /* The NVIC as a whole is always enabled. */ + s->gic.enabled = 1; systick_reset(s); } static int armv7m_nvic_init(SysBusDevice *dev) { - nvic_state *s= FROM_SYSBUSGIC(nvic_state, dev); + nvic_state *s = NVIC(dev); + NVICClass *nc = NVIC_GET_CLASS(s); - /* note that for the M profile gic_init() takes the number of external - * interrupt lines only. - */ - gic_init(&s->gic, s->num_irq); - memory_region_add_subregion(get_system_memory(), 0xe000e000, &s->gic.iomem); + /* The NVIC always has only one CPU */ + s->gic.num_cpu = 1; + /* Tell the common code we're an NVIC */ + s->gic.revision = 0xffffffff; + s->gic.num_irq = s->num_irq; + nc->parent_init(dev); + gic_init_irqs_and_distributor(&s->gic, s->num_irq); + /* The NVIC and system controller register area looks like this: + * 0..0xff : system control registers, including systick + * 0x100..0xcff : GIC-like registers + * 0xd00..0xfff : system control registers + * We use overlaying to put the GIC like registers + * over the top of the system control register region. + */ + memory_region_init(&s->container, "nvic", 0x1000); + /* The system register region goes at the bottom of the priority + * stack as it covers the whole page. + */ + memory_region_init_io(&s->sysregmem, &nvic_sysreg_ops, s, + "nvic_sysregs", 0x1000); + memory_region_add_subregion(&s->container, 0, &s->sysregmem); + /* Alias the GIC region so we can get only the section of it + * we need, and layer it on top of the system register region. + */ + memory_region_init_alias(&s->gic_iomem_alias, "nvic-gic", &s->gic.iomem, + 0x100, 0xc00); + memory_region_add_subregion_overlap(&s->container, 0x100, &s->gic.iomem, 1); + /* Map the whole thing into system memory at the location required + * by the v7M architecture. + */ + memory_region_add_subregion(get_system_memory(), 0xe000e000, &s->container); s->systick.timer = qemu_new_timer_ns(vm_clock, systick_timer_tick, s); return 0; } @@ -409,9 +509,12 @@ static Property armv7m_nvic_properties[] = { static void armv7m_nvic_class_init(ObjectClass *klass, void *data) { + NVICClass *nc = NVIC_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + nc->parent_reset = dc->reset; + nc->parent_init = sdc->init; sdc->init = armv7m_nvic_init; dc->vmsd = &vmstate_nvic; dc->reset = armv7m_nvic_reset; @@ -419,10 +522,11 @@ static void armv7m_nvic_class_init(ObjectClass *klass, void *data) } static TypeInfo armv7m_nvic_info = { - .name = "armv7m_nvic", - .parent = TYPE_SYS_BUS_DEVICE, + .name = TYPE_NVIC, + .parent = TYPE_ARM_GIC_COMMON, .instance_size = sizeof(nvic_state), .class_init = armv7m_nvic_class_init, + .class_size = sizeof(NVICClass), }; static void armv7m_nvic_register_types(void) diff --git a/hw/cadence_gem.c b/hw/cadence_gem.c index e2140aea2b..dbde3920d0 100644 --- a/hw/cadence_gem.c +++ b/hw/cadence_gem.c @@ -664,7 +664,7 @@ static ssize_t gem_receive(VLANClientState *nc, const uint8_t *buf, size_t size) */ memcpy(rxbuf, buf, size); - memset(rxbuf + size, 0, sizeof(rxbuf - size)); + memset(rxbuf + size, 0, sizeof(rxbuf) - size); rxbuf_ptr = rxbuf; crc_val = cpu_to_le32(crc32(0, rxbuf, MAX(size, 60))); if (size < 60) { diff --git a/hw/cadence_ttc.c b/hw/cadence_ttc.c index 2b5477b688..dd02f86eb9 100644 --- a/hw/cadence_ttc.c +++ b/hw/cadence_ttc.c @@ -405,7 +405,7 @@ static int cadence_ttc_init(SysBusDevice *dev) int i; for (i = 0; i < 3; ++i) { - cadence_timer_init(2500000, &s->timer[i]); + cadence_timer_init(133000000, &s->timer[i]); sysbus_init_irq(dev, &s->timer[i].irq); } diff --git a/hw/exynos4210.c b/hw/exynos4210.c index dd14d01b01..9c20b3f22d 100644 --- a/hw/exynos4210.c +++ b/hw/exynos4210.c @@ -97,11 +97,11 @@ void exynos4210_write_secondary(ARMCPU *cpu, Exynos4210State *exynos4210_init(MemoryRegion *system_mem, unsigned long ram_size) { - qemu_irq cpu_irq[4]; - int n; + qemu_irq cpu_irq[EXYNOS4210_NCPUS]; + int i, n; Exynos4210State *s = g_new(Exynos4210State, 1); qemu_irq *irqp; - qemu_irq gate_irq[EXYNOS4210_IRQ_GATE_NINPUTS]; + qemu_irq gate_irq[EXYNOS4210_NCPUS][EXYNOS4210_IRQ_GATE_NINPUTS]; unsigned long mem_size; DeviceState *dev; SysBusDevice *busdev; @@ -128,16 +128,18 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, s->irq_table = exynos4210_init_irq(&s->irqs); /* IRQ Gate */ - dev = qdev_create(NULL, "exynos4210.irq_gate"); - qdev_init_nofail(dev); - /* Get IRQ Gate input in gate_irq */ - for (n = 0; n < EXYNOS4210_IRQ_GATE_NINPUTS; n++) { - gate_irq[n] = qdev_get_gpio_in(dev, n); - } - busdev = sysbus_from_qdev(dev); - /* Connect IRQ Gate output to cpu_irq */ - for (n = 0; n < EXYNOS4210_NCPUS; n++) { - sysbus_connect_irq(busdev, n, cpu_irq[n]); + for (i = 0; i < EXYNOS4210_NCPUS; i++) { + dev = qdev_create(NULL, "exynos4210.irq_gate"); + qdev_prop_set_uint32(dev, "n_in", EXYNOS4210_IRQ_GATE_NINPUTS); + qdev_init_nofail(dev); + /* Get IRQ Gate input in gate_irq */ + for (n = 0; n < EXYNOS4210_IRQ_GATE_NINPUTS; n++) { + gate_irq[i][n] = qdev_get_gpio_in(dev, n); + } + busdev = sysbus_from_qdev(dev); + + /* Connect IRQ Gate output to cpu_irq */ + sysbus_connect_irq(busdev, 0, cpu_irq[i]); } /* Private memory region and Internal GIC */ @@ -147,7 +149,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, busdev = sysbus_from_qdev(dev); sysbus_mmio_map(busdev, 0, EXYNOS4210_SMP_PRIVATE_BASE_ADDR); for (n = 0; n < EXYNOS4210_NCPUS; n++) { - sysbus_connect_irq(busdev, n, gate_irq[n * 2]); + sysbus_connect_irq(busdev, n, gate_irq[n][0]); } for (n = 0; n < EXYNOS4210_INT_GIC_NIRQ; n++) { s->irqs.int_gic_irq[n] = qdev_get_gpio_in(dev, n); @@ -166,7 +168,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, /* Map Distributer interface */ sysbus_mmio_map(busdev, 1, EXYNOS4210_EXT_GIC_DIST_BASE_ADDR); for (n = 0; n < EXYNOS4210_NCPUS; n++) { - sysbus_connect_irq(busdev, n, gate_irq[n * 2 + 1]); + sysbus_connect_irq(busdev, n, gate_irq[n][1]); } for (n = 0; n < EXYNOS4210_EXT_GIC_NIRQ; n++) { s->irqs.ext_gic_irq[n] = qdev_get_gpio_in(dev, n); diff --git a/hw/exynos4210.h b/hw/exynos4210.h index b1b4609054..9b1ae4c8b1 100644 --- a/hw/exynos4210.h +++ b/hw/exynos4210.h @@ -56,7 +56,7 @@ /* * exynos4210 IRQ subsystem stub definitions. */ -#define EXYNOS4210_IRQ_GATE_NINPUTS 8 +#define EXYNOS4210_IRQ_GATE_NINPUTS 2 /* Internal and External GIC */ #define EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ 64 #define EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ 16 diff --git a/hw/exynos4210_gic.c b/hw/exynos4210_gic.c index e1b215eff0..7d03dd9ae3 100644 --- a/hw/exynos4210_gic.c +++ b/hw/exynos4210_gic.c @@ -362,61 +362,64 @@ static void exynos4210_gic_register_types(void) type_init(exynos4210_gic_register_types) -/* - * IRQGate struct. - * IRQ Gate represents OR gate between GICs to pass IRQ to PIC. +/* IRQ OR Gate struct. + * + * This device models an OR gate. There are n_in input qdev gpio lines and one + * output sysbus IRQ line. The output IRQ level is formed as OR between all + * gpio inputs. */ typedef struct { SysBusDevice busdev; - qemu_irq pic_irq[EXYNOS4210_NCPUS]; /* output IRQs to PICs */ - uint32_t gpio_level[EXYNOS4210_IRQ_GATE_NINPUTS]; /* Input levels */ + uint32_t n_in; /* inputs amount */ + uint32_t *level; /* input levels */ + qemu_irq out; /* output IRQ */ } Exynos4210IRQGateState; +static Property exynos4210_irq_gate_properties[] = { + DEFINE_PROP_UINT32("n_in", Exynos4210IRQGateState, n_in, 1), + DEFINE_PROP_END_OF_LIST(), +}; + static const VMStateDescription vmstate_exynos4210_irq_gate = { .name = "exynos4210.irq_gate", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, + .version_id = 2, + .minimum_version_id = 2, + .minimum_version_id_old = 2, .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(gpio_level, Exynos4210IRQGateState, - EXYNOS4210_IRQ_GATE_NINPUTS), + VMSTATE_VBUFFER_UINT32(level, Exynos4210IRQGateState, 1, NULL, 0, n_in), VMSTATE_END_OF_LIST() } }; -/* Process a change in an external IRQ input. */ +/* Process a change in IRQ input. */ static void exynos4210_irq_gate_handler(void *opaque, int irq, int level) { - Exynos4210IRQGateState *s = - (Exynos4210IRQGateState *)opaque; - uint32_t odd, even; - - if (irq & 1) { - odd = irq; - even = irq & ~1; - } else { - even = irq; - odd = irq | 1; - } + Exynos4210IRQGateState *s = (Exynos4210IRQGateState *)opaque; + uint32_t i; - assert(irq < EXYNOS4210_IRQ_GATE_NINPUTS); - s->gpio_level[irq] = level; + assert(irq < s->n_in); - if (s->gpio_level[odd] >= 1 || s->gpio_level[even] >= 1) { - qemu_irq_raise(s->pic_irq[even >> 1]); - } else { - qemu_irq_lower(s->pic_irq[even >> 1]); + s->level[irq] = level; + + for (i = 0; i < s->n_in; i++) { + if (s->level[i] >= 1) { + qemu_irq_raise(s->out); + return; + } } + qemu_irq_lower(s->out); + return; } static void exynos4210_irq_gate_reset(DeviceState *d) { - Exynos4210IRQGateState *s = (Exynos4210IRQGateState *)d; + Exynos4210IRQGateState *s = + DO_UPCAST(Exynos4210IRQGateState, busdev.qdev, d); - memset(&s->gpio_level, 0, sizeof(s->gpio_level)); + memset(s->level, 0, s->n_in * sizeof(*s->level)); } /* @@ -424,19 +427,15 @@ static void exynos4210_irq_gate_reset(DeviceState *d) */ static int exynos4210_irq_gate_init(SysBusDevice *dev) { - unsigned int i; - Exynos4210IRQGateState *s = - FROM_SYSBUS(Exynos4210IRQGateState, dev); + Exynos4210IRQGateState *s = FROM_SYSBUS(Exynos4210IRQGateState, dev); /* Allocate general purpose input signals and connect a handler to each of * them */ - qdev_init_gpio_in(&s->busdev.qdev, exynos4210_irq_gate_handler, - EXYNOS4210_IRQ_GATE_NINPUTS); + qdev_init_gpio_in(&s->busdev.qdev, exynos4210_irq_gate_handler, s->n_in); - /* Connect SysBusDev irqs to device specific irqs */ - for (i = 0; i < EXYNOS4210_NCPUS; i++) { - sysbus_init_irq(dev, &s->pic_irq[i]); - } + s->level = g_malloc0(s->n_in * sizeof(*s->level)); + + sysbus_init_irq(dev, &s->out); return 0; } @@ -449,6 +448,7 @@ static void exynos4210_irq_gate_class_init(ObjectClass *klass, void *data) k->init = exynos4210_irq_gate_init; dc->reset = exynos4210_irq_gate_reset; dc->vmsd = &vmstate_exynos4210_irq_gate; + dc->props = exynos4210_irq_gate_properties; } static TypeInfo exynos4210_irq_gate_info = { diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs index eb171b7c47..8c764bbfef 100644 --- a/hw/i386/Makefile.objs +++ b/hw/i386/Makefile.objs @@ -7,6 +7,8 @@ obj-y += debugcon.o multiboot.o obj-y += pc_piix.o obj-y += pc_sysfw.o obj-$(CONFIG_XEN) += xen_platform.o xen_apic.o +obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o +obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o obj-y += kvm/ obj-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o diff --git a/hw/loader.c b/hw/loader.c index 7d64113e7f..33acc2fdab 100644 --- a/hw/loader.c +++ b/hw/loader.c @@ -377,9 +377,9 @@ static void zfree(void *x, void *addr) #define DEFLATED 8 -/* This is the maximum in uboot, so if a uImage overflows this, it would +/* This is the usual maximum in uboot, so if a uImage overflows this, it would * overflow on real hardware too. */ -#define UBOOT_MAX_GUNZIP_BYTES 0x800000 +#define UBOOT_MAX_GUNZIP_BYTES (64 << 20) static ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, size_t srclen) @@ -998,7 +998,6 @@ enum { #define OMAP_GPIOSW_OUTPUT 0x0002 # define TCMI_VERBOSE 1 -//# define MEM_VERBOSE 1 # ifdef TCMI_VERBOSE # define OMAP_8B_REG(paddr) \ @@ -1018,98 +1017,4 @@ enum { # define OMAP_MPUI_REG_MASK 0x000007ff -# ifdef MEM_VERBOSE -struct io_fn { - CPUReadMemoryFunc * const *mem_read; - CPUWriteMemoryFunc * const *mem_write; - void *opaque; - int in; -}; - -static uint32_t io_readb(void *opaque, target_phys_addr_t addr) -{ - struct io_fn *s = opaque; - uint32_t ret; - - s->in ++; - ret = s->mem_read[0](s->opaque, addr); - s->in --; - if (!s->in) - fprintf(stderr, "%08x ---> %02x\n", (uint32_t) addr, ret); - return ret; -} -static uint32_t io_readh(void *opaque, target_phys_addr_t addr) -{ - struct io_fn *s = opaque; - uint32_t ret; - - s->in ++; - ret = s->mem_read[1](s->opaque, addr); - s->in --; - if (!s->in) - fprintf(stderr, "%08x ---> %04x\n", (uint32_t) addr, ret); - return ret; -} -static uint32_t io_readw(void *opaque, target_phys_addr_t addr) -{ - struct io_fn *s = opaque; - uint32_t ret; - - s->in ++; - ret = s->mem_read[2](s->opaque, addr); - s->in --; - if (!s->in) - fprintf(stderr, "%08x ---> %08x\n", (uint32_t) addr, ret); - return ret; -} -static void io_writeb(void *opaque, target_phys_addr_t addr, uint32_t value) -{ - struct io_fn *s = opaque; - - if (!s->in) - fprintf(stderr, "%08x <--- %02x\n", (uint32_t) addr, value); - s->in ++; - s->mem_write[0](s->opaque, addr, value); - s->in --; -} -static void io_writeh(void *opaque, target_phys_addr_t addr, uint32_t value) -{ - struct io_fn *s = opaque; - - if (!s->in) - fprintf(stderr, "%08x <--- %04x\n", (uint32_t) addr, value); - s->in ++; - s->mem_write[1](s->opaque, addr, value); - s->in --; -} -static void io_writew(void *opaque, target_phys_addr_t addr, uint32_t value) -{ - struct io_fn *s = opaque; - - if (!s->in) - fprintf(stderr, "%08x <--- %08x\n", (uint32_t) addr, value); - s->in ++; - s->mem_write[2](s->opaque, addr, value); - s->in --; -} - -static CPUReadMemoryFunc * const io_readfn[] = { io_readb, io_readh, io_readw, }; -static CPUWriteMemoryFunc * const io_writefn[] = { io_writeb, io_writeh, io_writew, }; - -inline static int debug_register_io_memory(CPUReadMemoryFunc * const *mem_read, - CPUWriteMemoryFunc * const *mem_write, - void *opaque) -{ - struct io_fn *s = g_malloc(sizeof(struct io_fn)); - - s->mem_read = mem_read; - s->mem_write = mem_write; - s->opaque = opaque; - s->in = 0; - return cpu_register_io_memory(io_readfn, io_writefn, s, - DEVICE_NATIVE_ENDIAN); -} -# define cpu_register_io_memory debug_register_io_memory -# endif - #endif /* hw_omap_h */ @@ -1144,7 +1144,9 @@ static const pci_class_desc pci_class_descriptions[] = }; static void pci_for_each_device_under_bus(PCIBus *bus, - void (*fn)(PCIBus *b, PCIDevice *d)) + void (*fn)(PCIBus *b, PCIDevice *d, + void *opaque), + void *opaque) { PCIDevice *d; int devfn; @@ -1152,18 +1154,19 @@ static void pci_for_each_device_under_bus(PCIBus *bus, for(devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { d = bus->devices[devfn]; if (d) { - fn(bus, d); + fn(bus, d, opaque); } } } void pci_for_each_device(PCIBus *bus, int bus_num, - void (*fn)(PCIBus *b, PCIDevice *d)) + void (*fn)(PCIBus *b, PCIDevice *d, void *opaque), + void *opaque) { bus = pci_find_bus_nr(bus, bus_num); if (bus) { - pci_for_each_device_under_bus(bus, fn); + pci_for_each_device_under_bus(bus, fn, opaque); } } @@ -312,7 +312,9 @@ PCIDevice *pci_nic_init(NICInfo *nd, const char *default_model, PCIDevice *pci_nic_init_nofail(NICInfo *nd, const char *default_model, const char *default_devaddr); int pci_bus_num(PCIBus *s); -void pci_for_each_device(PCIBus *bus, int bus_num, void (*fn)(PCIBus *bus, PCIDevice *d)); +void pci_for_each_device(PCIBus *bus, int bus_num, + void (*fn)(PCIBus *bus, PCIDevice *d, void *opaque), + void *opaque); PCIBus *pci_find_root_bus(int domain); int pci_find_domain(const PCIBus *bus); PCIDevice *pci_find_device(PCIBus *bus, int bus_num, uint8_t devfn); diff --git a/hw/pci_ids.h b/hw/pci_ids.h index e8235a7d05..649e6b379d 100644 --- a/hw/pci_ids.h +++ b/hw/pci_ids.h @@ -118,6 +118,7 @@ #define PCI_DEVICE_ID_INTEL_82801I_UHCI6 0x2939 #define PCI_DEVICE_ID_INTEL_82801I_EHCI1 0x293a #define PCI_DEVICE_ID_INTEL_82801I_EHCI2 0x293c +#define PCI_DEVICE_ID_INTEL_82599_SFP_VF 0x10ed #define PCI_VENDOR_ID_XEN 0x5853 #define PCI_DEVICE_ID_XEN_PLATFORM 0x0001 diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 44a1e8cdab..d18dbaf6cc 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -15,7 +15,7 @@ obj-$(CONFIG_PSERIES) += spapr_pci.o pci-hotplug.o obj-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o obj-y += ppc440_bamboo.o # PowerPC E500 boards -obj-y += ppce500_mpc8544ds.o mpc8544_guts.o ppce500_spin.o +obj-$(CONFIG_FDT) += ppce500_mpc8544ds.o mpc8544_guts.o ppce500_spin.o # PowerPC 440 Xilinx ML507 reference board. obj-y += virtex_ml507.o # PowerPC OpenPIC diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c index 3eb8a23779..8b9fd83ce1 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppce500_mpc8544ds.c @@ -31,6 +31,7 @@ #include "elf.h" #include "sysbus.h" #include "exec-memory.h" +#include "host-utils.h" #define BINARY_DEVICE_TREE_FILE "mpc8544ds.dtb" #define UIMAGE_LOAD_BASE 0 @@ -41,57 +42,150 @@ #define RAM_SIZES_ALIGN (64UL << 20) -#define MPC8544_CCSRBAR_BASE 0xE0000000 -#define MPC8544_MPIC_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x40000) -#define MPC8544_SERIAL0_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x4500) -#define MPC8544_SERIAL1_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x4600) -#define MPC8544_PCI_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x8000) -#define MPC8544_PCI_REGS_SIZE 0x1000 -#define MPC8544_PCI_IO 0xE1000000 -#define MPC8544_PCI_IOLEN 0x10000 -#define MPC8544_UTIL_BASE (MPC8544_CCSRBAR_BASE + 0xe0000) -#define MPC8544_SPIN_BASE 0xEF000000 +#define MPC8544_CCSRBAR_BASE 0xE0000000ULL +#define MPC8544_CCSRBAR_SIZE 0x00100000ULL +#define MPC8544_MPIC_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x40000ULL) +#define MPC8544_SERIAL0_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x4500ULL) +#define MPC8544_SERIAL1_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x4600ULL) +#define MPC8544_PCI_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x8000ULL) +#define MPC8544_PCI_REGS_SIZE 0x1000ULL +#define MPC8544_PCI_IO 0xE1000000ULL +#define MPC8544_PCI_IOLEN 0x10000ULL +#define MPC8544_UTIL_BASE (MPC8544_CCSRBAR_BASE + 0xe0000ULL) +#define MPC8544_SPIN_BASE 0xEF000000ULL struct boot_info { uint32_t dt_base; + uint32_t dt_size; uint32_t entry; }; +static void pci_map_create(void *fdt, uint32_t *pci_map, uint32_t mpic) +{ + int i; + const uint32_t tmp[] = { + /* IDSEL 0x11 J17 Slot 1 */ + 0x8800, 0x0, 0x0, 0x1, mpic, 0x2, 0x1, 0x0, 0x0, + 0x8800, 0x0, 0x0, 0x2, mpic, 0x3, 0x1, 0x0, 0x0, + 0x8800, 0x0, 0x0, 0x3, mpic, 0x4, 0x1, 0x0, 0x0, + 0x8800, 0x0, 0x0, 0x4, mpic, 0x1, 0x1, 0x0, 0x0, + + /* IDSEL 0x12 J16 Slot 2 */ + 0x9000, 0x0, 0x0, 0x1, mpic, 0x3, 0x1, 0x0, 0x0, + 0x9000, 0x0, 0x0, 0x2, mpic, 0x4, 0x1, 0x0, 0x0, + 0x9000, 0x0, 0x0, 0x3, mpic, 0x2, 0x1, 0x0, 0x0, + 0x9000, 0x0, 0x0, 0x4, mpic, 0x1, 0x1, 0x0, 0x0, + }; + for (i = 0; i < ARRAY_SIZE(tmp); i++) { + pci_map[i] = cpu_to_be32(tmp[i]); + } +} + +static void dt_serial_create(void *fdt, unsigned long long offset, + const char *soc, const char *mpic, + const char *alias, int idx, bool defcon) +{ + char ser[128]; + + snprintf(ser, sizeof(ser), "%s/serial@%llx", soc, offset); + qemu_devtree_add_subnode(fdt, ser); + qemu_devtree_setprop_string(fdt, ser, "device_type", "serial"); + qemu_devtree_setprop_string(fdt, ser, "compatible", "ns16550"); + qemu_devtree_setprop_cells(fdt, ser, "reg", offset, 0x100); + qemu_devtree_setprop_cell(fdt, ser, "cell-index", idx); + qemu_devtree_setprop_cell(fdt, ser, "clock-frequency", 0); + qemu_devtree_setprop_cells(fdt, ser, "interrupts", 42, 2, 0, 0); + qemu_devtree_setprop_phandle(fdt, ser, "interrupt-parent", mpic); + qemu_devtree_setprop_string(fdt, "/aliases", alias, ser); + + if (defcon) { + qemu_devtree_setprop_string(fdt, "/chosen", "linux,stdout-path", ser); + } +} + static int mpc8544_load_device_tree(CPUPPCState *env, target_phys_addr_t addr, - uint32_t ramsize, + target_phys_addr_t ramsize, target_phys_addr_t initrd_base, target_phys_addr_t initrd_size, const char *kernel_cmdline) { int ret = -1; -#ifdef CONFIG_FDT - uint32_t mem_reg_property[] = {0, cpu_to_be32(ramsize)}; - char *filename; + uint64_t mem_reg_property[] = { 0, cpu_to_be64(ramsize) }; int fdt_size; void *fdt; uint8_t hypercall[16]; uint32_t clock_freq = 400000000; uint32_t tb_freq = 400000000; int i; + const char *compatible = "MPC8544DS\0MPC85xxDS"; + int compatible_len = sizeof("MPC8544DS\0MPC85xxDS"); + char compatible_sb[] = "fsl,mpc8544-immr\0simple-bus"; + char model[] = "MPC8544DS"; + char soc[128]; + char mpic[128]; + uint32_t mpic_ph; + char gutil[128]; + char pci[128]; + uint32_t pci_map[9 * 8]; + uint32_t pci_ranges[14] = + { + 0x2000000, 0x0, 0xc0000000, + 0x0, 0xc0000000, + 0x0, 0x20000000, + + 0x1000000, 0x0, 0x0, + 0x0, 0xe1000000, + 0x0, 0x10000, + }; + QemuOpts *machine_opts; + const char *dumpdtb = NULL; + const char *dtb_file = NULL; + + machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0); + if (machine_opts) { + const char *tmp; + dumpdtb = qemu_opt_get(machine_opts, "dumpdtb"); + dtb_file = qemu_opt_get(machine_opts, "dtb"); + tmp = qemu_opt_get(machine_opts, "dt_compatible"); + if (tmp) { + compatible = tmp; + compatible_len = strlen(compatible) + 1; + } + } - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); - if (!filename) { - goto out; + if (dtb_file) { + char *filename; + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, dtb_file); + if (!filename) { + goto out; + } + + fdt = load_device_tree(filename, &fdt_size); + if (!fdt) { + goto out; + } + goto done; } - fdt = load_device_tree(filename, &fdt_size); - g_free(filename); + + fdt = create_device_tree(&fdt_size); if (fdt == NULL) { goto out; } /* Manipulate device tree in memory. */ - ret = qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property, - sizeof(mem_reg_property)); - if (ret < 0) - fprintf(stderr, "couldn't set /memory/reg\n"); + qemu_devtree_setprop_string(fdt, "/", "model", model); + qemu_devtree_setprop(fdt, "/", "compatible", compatible, compatible_len); + qemu_devtree_setprop_cell(fdt, "/", "#address-cells", 2); + qemu_devtree_setprop_cell(fdt, "/", "#size-cells", 2); + + qemu_devtree_add_subnode(fdt, "/memory"); + qemu_devtree_setprop_string(fdt, "/memory", "device_type", "memory"); + qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property, + sizeof(mem_reg_property)); + qemu_devtree_add_subnode(fdt, "/chosen"); if (initrd_size) { ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start", initrd_base); @@ -117,6 +211,7 @@ static int mpc8544_load_device_tree(CPUPPCState *env, tb_freq = kvmppc_get_tbfreq(); /* indicate KVM hypercall interface */ + qemu_devtree_add_subnode(fdt, "/hypervisor"); qemu_devtree_setprop_string(fdt, "/hypervisor", "compatible", "linux,kvm"); kvmppc_get_hypercall(env, hypercall, sizeof(hypercall)); @@ -124,11 +219,16 @@ static int mpc8544_load_device_tree(CPUPPCState *env, hypercall, sizeof(hypercall)); } + /* Create CPU nodes */ + qemu_devtree_add_subnode(fdt, "/cpus"); + qemu_devtree_setprop_cell(fdt, "/cpus", "#address-cells", 1); + qemu_devtree_setprop_cell(fdt, "/cpus", "#size-cells", 0); + /* We need to generate the cpu nodes in reverse order, so Linux can pick the first node as boot node and be happy */ for (i = smp_cpus - 1; i >= 0; i--) { char cpu_name[128]; - uint64_t cpu_release_addr = cpu_to_be64(MPC8544_SPIN_BASE + (i * 0x20)); + uint64_t cpu_release_addr = MPC8544_SPIN_BASE + (i * 0x20); for (env = first_cpu; env != NULL; env = env->next_cpu) { if (env->cpu_index == i) { @@ -156,39 +256,133 @@ static int mpc8544_load_device_tree(CPUPPCState *env, if (env->cpu_index) { qemu_devtree_setprop_string(fdt, cpu_name, "status", "disabled"); qemu_devtree_setprop_string(fdt, cpu_name, "enable-method", "spin-table"); - qemu_devtree_setprop(fdt, cpu_name, "cpu-release-addr", - &cpu_release_addr, sizeof(cpu_release_addr)); + qemu_devtree_setprop_u64(fdt, cpu_name, "cpu-release-addr", + cpu_release_addr); } else { qemu_devtree_setprop_string(fdt, cpu_name, "status", "okay"); } } + qemu_devtree_add_subnode(fdt, "/aliases"); + /* XXX These should go into their respective devices' code */ + snprintf(soc, sizeof(soc), "/soc@%llx", MPC8544_CCSRBAR_BASE); + qemu_devtree_add_subnode(fdt, soc); + qemu_devtree_setprop_string(fdt, soc, "device_type", "soc"); + qemu_devtree_setprop(fdt, soc, "compatible", compatible_sb, + sizeof(compatible_sb)); + qemu_devtree_setprop_cell(fdt, soc, "#address-cells", 1); + qemu_devtree_setprop_cell(fdt, soc, "#size-cells", 1); + qemu_devtree_setprop_cells(fdt, soc, "ranges", 0x0, + MPC8544_CCSRBAR_BASE >> 32, MPC8544_CCSRBAR_BASE, + MPC8544_CCSRBAR_SIZE); + /* XXX should contain a reasonable value */ + qemu_devtree_setprop_cell(fdt, soc, "bus-frequency", 0); + + snprintf(mpic, sizeof(mpic), "%s/pic@%llx", soc, + MPC8544_MPIC_REGS_BASE - MPC8544_CCSRBAR_BASE); + qemu_devtree_add_subnode(fdt, mpic); + qemu_devtree_setprop_string(fdt, mpic, "device_type", "open-pic"); + qemu_devtree_setprop_string(fdt, mpic, "compatible", "fsl,mpic"); + qemu_devtree_setprop_cells(fdt, mpic, "reg", MPC8544_MPIC_REGS_BASE - + MPC8544_CCSRBAR_BASE, 0x40000); + qemu_devtree_setprop_cell(fdt, mpic, "#address-cells", 0); + qemu_devtree_setprop_cell(fdt, mpic, "#interrupt-cells", 4); + mpic_ph = qemu_devtree_alloc_phandle(fdt); + qemu_devtree_setprop_cell(fdt, mpic, "phandle", mpic_ph); + qemu_devtree_setprop_cell(fdt, mpic, "linux,phandle", mpic_ph); + qemu_devtree_setprop(fdt, mpic, "interrupt-controller", NULL, 0); + qemu_devtree_setprop(fdt, mpic, "big-endian", NULL, 0); + qemu_devtree_setprop(fdt, mpic, "single-cpu-affinity", NULL, 0); + qemu_devtree_setprop_cell(fdt, mpic, "last-interrupt-source", 255); + + /* + * We have to generate ser1 first, because Linux takes the first + * device it finds in the dt as serial output device. And we generate + * devices in reverse order to the dt. + */ + dt_serial_create(fdt, MPC8544_SERIAL1_REGS_BASE - MPC8544_CCSRBAR_BASE, + soc, mpic, "serial1", 1, false); + dt_serial_create(fdt, MPC8544_SERIAL0_REGS_BASE - MPC8544_CCSRBAR_BASE, + soc, mpic, "serial0", 0, true); + + snprintf(gutil, sizeof(gutil), "%s/global-utilities@%llx", soc, + MPC8544_UTIL_BASE - MPC8544_CCSRBAR_BASE); + qemu_devtree_add_subnode(fdt, gutil); + qemu_devtree_setprop_string(fdt, gutil, "compatible", "fsl,mpc8544-guts"); + qemu_devtree_setprop_cells(fdt, gutil, "reg", MPC8544_UTIL_BASE - + MPC8544_CCSRBAR_BASE, 0x1000); + qemu_devtree_setprop(fdt, gutil, "fsl,has-rstcr", NULL, 0); + + snprintf(pci, sizeof(pci), "/pci@%llx", MPC8544_PCI_REGS_BASE); + qemu_devtree_add_subnode(fdt, pci); + qemu_devtree_setprop_cell(fdt, pci, "cell-index", 0); + qemu_devtree_setprop_string(fdt, pci, "compatible", "fsl,mpc8540-pci"); + qemu_devtree_setprop_string(fdt, pci, "device_type", "pci"); + qemu_devtree_setprop_cells(fdt, pci, "interrupt-map-mask", 0xf800, 0x0, + 0x0, 0x7); + pci_map_create(fdt, pci_map, qemu_devtree_get_phandle(fdt, mpic)); + qemu_devtree_setprop(fdt, pci, "interrupt-map", pci_map, sizeof(pci_map)); + qemu_devtree_setprop_phandle(fdt, pci, "interrupt-parent", mpic); + qemu_devtree_setprop_cells(fdt, pci, "interrupts", 24, 2, 0, 0); + qemu_devtree_setprop_cells(fdt, pci, "bus-range", 0, 255); + for (i = 0; i < 14; i++) { + pci_ranges[i] = cpu_to_be32(pci_ranges[i]); + } + qemu_devtree_setprop(fdt, pci, "ranges", pci_ranges, sizeof(pci_ranges)); + qemu_devtree_setprop_cells(fdt, pci, "reg", MPC8544_PCI_REGS_BASE >> 32, + MPC8544_PCI_REGS_BASE, 0, 0x1000); + qemu_devtree_setprop_cell(fdt, pci, "clock-frequency", 66666666); + qemu_devtree_setprop_cell(fdt, pci, "#interrupt-cells", 1); + qemu_devtree_setprop_cell(fdt, pci, "#size-cells", 2); + qemu_devtree_setprop_cell(fdt, pci, "#address-cells", 3); + qemu_devtree_setprop_string(fdt, "/aliases", "pci0", pci); + +done: + if (dumpdtb) { + /* Dump the dtb to a file and quit */ + FILE *f = fopen(dumpdtb, "wb"); + size_t len; + len = fwrite(fdt, fdt_size, 1, f); + fclose(f); + if (len != fdt_size) { + exit(1); + } + exit(0); + } + ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); + if (ret < 0) { + goto out; + } g_free(fdt); + ret = fdt_size; out: -#endif return ret; } -/* Create -kernel TLB entries for BookE, linearly spanning 256MB. */ +/* Create -kernel TLB entries for BookE. */ static inline target_phys_addr_t booke206_page_size_to_tlb(uint64_t size) { - return ffs(size >> 10) - 1; + return 63 - clz64(size >> 10); } -static void mmubooke_create_initial_mapping(CPUPPCState *env, - target_ulong va, - target_phys_addr_t pa) +static void mmubooke_create_initial_mapping(CPUPPCState *env) { + struct boot_info *bi = env->load_info; ppcmas_tlb_t *tlb = booke206_get_tlbm(env, 1, 0, 0); - target_phys_addr_t size; - - size = (booke206_page_size_to_tlb(256 * 1024 * 1024) << MAS1_TSIZE_SHIFT); + target_phys_addr_t size, dt_end; + int ps; + + /* Our initial TLB entry needs to cover everything from 0 to + the device tree top */ + dt_end = bi->dt_base + bi->dt_size; + ps = booke206_page_size_to_tlb(dt_end) + 1; + size = (ps << MAS1_TSIZE_SHIFT); tlb->mas1 = MAS1_VALID | size; - tlb->mas2 = va & TARGET_PAGE_MASK; - tlb->mas7_3 = pa & TARGET_PAGE_MASK; + tlb->mas2 = 0; + tlb->mas7_3 = 0; tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX; env->tlb_dirty = true; @@ -220,7 +414,7 @@ static void mpc8544ds_cpu_reset(void *opaque) env->gpr[1] = (16<<20) - 8; env->gpr[3] = bi->dt_base; env->nip = bi->entry; - mmubooke_create_initial_mapping(env, 0, 0); + mmubooke_create_initial_mapping(env); } static void mpc8544ds_init(ram_addr_t ram_size, @@ -275,6 +469,7 @@ static void mpc8544ds_init(ram_addr_t ram_size, irqs[i][OPENPIC_OUTPUT_INT] = input[PPCE500_INPUT_INT]; irqs[i][OPENPIC_OUTPUT_CINT] = input[PPCE500_INPUT_CINT]; env->spr[SPR_BOOKE_PIR] = env->cpu_index = i; + env->mpic_cpu_base = MPC8544_MPIC_REGS_BASE + 0x20000; ppc_booke_timers_init(env, 400000000, PPC_TIMER_E500); @@ -379,13 +574,12 @@ static void mpc8544ds_init(ram_addr_t ram_size, /* If we're loading a kernel directly, we must load the device tree too. */ if (kernel_filename) { struct boot_info *boot_info; + int dt_size; -#ifndef CONFIG_FDT - cpu_abort(env, "Compiled without FDT support - can't load kernel\n"); -#endif - dt_base = (kernel_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK; - if (mpc8544_load_device_tree(env, dt_base, ram_size, - initrd_base, initrd_size, kernel_cmdline) < 0) { + dt_base = (loadaddr + kernel_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK; + dt_size = mpc8544_load_device_tree(env, dt_base, ram_size, initrd_base, + initrd_size, kernel_cmdline); + if (dt_size < 0) { fprintf(stderr, "couldn't load device tree\n"); exit(1); } @@ -393,6 +587,7 @@ static void mpc8544ds_init(ram_addr_t ram_size, boot_info = env->load_info; boot_info->entry = entry; boot_info->dt_base = dt_base; + boot_info->dt_size = dt_size; } if (kvm_enabled()) { diff --git a/hw/pxa2xx.c b/hw/pxa2xx.c index 7958d14003..d5f1420ed9 100644 --- a/hw/pxa2xx.c +++ b/hw/pxa2xx.c @@ -224,210 +224,161 @@ static const VMStateDescription vmstate_pxa2xx_cm = { } }; -static uint32_t pxa2xx_clkpwr_read(void *opaque, int op2, int reg, int crm) +static int pxa2xx_clkcfg_read(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t *value) { - PXA2xxState *s = (PXA2xxState *) opaque; - - switch (reg) { - case 6: /* Clock Configuration register */ - return s->clkcfg; - - case 7: /* Power Mode register */ - return 0; + PXA2xxState *s = (PXA2xxState *)ri->opaque; + *value = s->clkcfg; + return 0; +} - default: - printf("%s: Bad register 0x%x\n", __FUNCTION__, reg); - break; +static int pxa2xx_clkcfg_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + PXA2xxState *s = (PXA2xxState *)ri->opaque; + s->clkcfg = value & 0xf; + if (value & 2) { + printf("%s: CPU frequency change attempt\n", __func__); } return 0; } -static void pxa2xx_clkpwr_write(void *opaque, int op2, int reg, int crm, - uint32_t value) +static int pxa2xx_pwrmode_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) { - PXA2xxState *s = (PXA2xxState *) opaque; + PXA2xxState *s = (PXA2xxState *)ri->opaque; static const char *pwrmode[8] = { "Normal", "Idle", "Deep-idle", "Standby", "Sleep", "reserved (!)", "reserved (!)", "Deep-sleep", }; - switch (reg) { - case 6: /* Clock Configuration register */ - s->clkcfg = value & 0xf; - if (value & 2) - printf("%s: CPU frequency change attempt\n", __FUNCTION__); + if (value & 8) { + printf("%s: CPU voltage change attempt\n", __func__); + } + switch (value & 7) { + case 0: + /* Do nothing */ break; - case 7: /* Power Mode register */ - if (value & 8) - printf("%s: CPU voltage change attempt\n", __FUNCTION__); - switch (value & 7) { - case 0: - /* Do nothing */ + case 1: + /* Idle */ + if (!(s->cm_regs[CCCR >> 2] & (1 << 31))) { /* CPDIS */ + cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_HALT); break; + } + /* Fall through. */ - case 1: - /* Idle */ - if (!(s->cm_regs[CCCR >> 2] & (1 << 31))) { /* CPDIS */ - cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_HALT); - break; - } - /* Fall through. */ - - case 2: - /* Deep-Idle */ - cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_HALT); - s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */ - goto message; - - case 3: - s->cpu->env.uncached_cpsr = - ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I; - s->cpu->env.cp15.c1_sys = 0; - s->cpu->env.cp15.c1_coproc = 0; - s->cpu->env.cp15.c2_base0 = 0; - s->cpu->env.cp15.c3 = 0; - s->pm_regs[PSSR >> 2] |= 0x8; /* Set STS */ - s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */ - - /* - * The scratch-pad register is almost universally used - * for storing the return address on suspend. For the - * lack of a resuming bootloader, perform a jump - * directly to that address. - */ - memset(s->cpu->env.regs, 0, 4 * 15); - s->cpu->env.regs[15] = s->pm_regs[PSPR >> 2]; + case 2: + /* Deep-Idle */ + cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_HALT); + s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */ + goto message; + + case 3: + s->cpu->env.uncached_cpsr = + ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I; + s->cpu->env.cp15.c1_sys = 0; + s->cpu->env.cp15.c1_coproc = 0; + s->cpu->env.cp15.c2_base0 = 0; + s->cpu->env.cp15.c3 = 0; + s->pm_regs[PSSR >> 2] |= 0x8; /* Set STS */ + s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */ + + /* + * The scratch-pad register is almost universally used + * for storing the return address on suspend. For the + * lack of a resuming bootloader, perform a jump + * directly to that address. + */ + memset(s->cpu->env.regs, 0, 4 * 15); + s->cpu->env.regs[15] = s->pm_regs[PSPR >> 2]; #if 0 - buffer = 0xe59ff000; /* ldr pc, [pc, #0] */ - cpu_physical_memory_write(0, &buffer, 4); - buffer = s->pm_regs[PSPR >> 2]; - cpu_physical_memory_write(8, &buffer, 4); + buffer = 0xe59ff000; /* ldr pc, [pc, #0] */ + cpu_physical_memory_write(0, &buffer, 4); + buffer = s->pm_regs[PSPR >> 2]; + cpu_physical_memory_write(8, &buffer, 4); #endif - /* Suspend */ - cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HALT); + /* Suspend */ + cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HALT); - goto message; - - default: - message: - printf("%s: machine entered %s mode\n", __FUNCTION__, - pwrmode[value & 7]); - } - break; + goto message; default: - printf("%s: Bad register 0x%x\n", __FUNCTION__, reg); - break; + message: + printf("%s: machine entered %s mode\n", __func__, + pwrmode[value & 7]); } -} - -/* Performace Monitoring Registers */ -#define CPPMNC 0 /* Performance Monitor Control register */ -#define CPCCNT 1 /* Clock Counter register */ -#define CPINTEN 4 /* Interrupt Enable register */ -#define CPFLAG 5 /* Overflow Flag register */ -#define CPEVTSEL 8 /* Event Selection register */ -#define CPPMN0 0 /* Performance Count register 0 */ -#define CPPMN1 1 /* Performance Count register 1 */ -#define CPPMN2 2 /* Performance Count register 2 */ -#define CPPMN3 3 /* Performance Count register 3 */ + return 0; +} -static uint32_t pxa2xx_perf_read(void *opaque, int op2, int reg, int crm) +static int pxa2xx_cppmnc_read(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t *value) { - PXA2xxState *s = (PXA2xxState *) opaque; - - switch (reg) { - case CPPMNC: - return s->pmnc; - case CPCCNT: - if (s->pmnc & 1) - return qemu_get_clock_ns(vm_clock); - else - return 0; - case CPINTEN: - case CPFLAG: - case CPEVTSEL: - return 0; - - default: - printf("%s: Bad register 0x%x\n", __FUNCTION__, reg); - break; - } + PXA2xxState *s = (PXA2xxState *)ri->opaque; + *value = s->pmnc; return 0; } -static void pxa2xx_perf_write(void *opaque, int op2, int reg, int crm, - uint32_t value) +static int pxa2xx_cppmnc_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) { - PXA2xxState *s = (PXA2xxState *) opaque; - - switch (reg) { - case CPPMNC: - s->pmnc = value; - break; - - case CPCCNT: - case CPINTEN: - case CPFLAG: - case CPEVTSEL: - break; - - default: - printf("%s: Bad register 0x%x\n", __FUNCTION__, reg); - break; - } + PXA2xxState *s = (PXA2xxState *)ri->opaque; + s->pmnc = value; + return 0; } -static uint32_t pxa2xx_cp14_read(void *opaque, int op2, int reg, int crm) +static int pxa2xx_cpccnt_read(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t *value) { - switch (crm) { - case 0: - return pxa2xx_clkpwr_read(opaque, op2, reg, crm); - case 1: - return pxa2xx_perf_read(opaque, op2, reg, crm); - case 2: - switch (reg) { - case CPPMN0: - case CPPMN1: - case CPPMN2: - case CPPMN3: - return 0; - } - /* Fall through */ - default: - printf("%s: Bad register 0x%x\n", __FUNCTION__, reg); - break; + PXA2xxState *s = (PXA2xxState *)ri->opaque; + if (s->pmnc & 1) { + *value = qemu_get_clock_ns(vm_clock); + } else { + *value = 0; } return 0; } -static void pxa2xx_cp14_write(void *opaque, int op2, int reg, int crm, - uint32_t value) +static const ARMCPRegInfo pxa_cp_reginfo[] = { + /* cp14 crn==1: perf registers */ + { .name = "CPPMNC", .cp = 14, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, + .readfn = pxa2xx_cppmnc_read, .writefn = pxa2xx_cppmnc_write }, + { .name = "CPCCNT", .cp = 14, .crn = 1, .crm = 1, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, + .readfn = pxa2xx_cpccnt_read, .writefn = arm_cp_write_ignore }, + { .name = "CPINTEN", .cp = 14, .crn = 1, .crm = 4, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPFLAG", .cp = 14, .crn = 1, .crm = 5, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPEVTSEL", .cp = 14, .crn = 1, .crm = 8, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + /* cp14 crn==2: performance count registers */ + { .name = "CPPMN0", .cp = 14, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPPMN1", .cp = 14, .crn = 2, .crm = 1, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPPMN2", .cp = 14, .crn = 2, .crm = 2, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPPMN3", .cp = 14, .crn = 2, .crm = 3, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + /* cp14 crn==6: CLKCFG */ + { .name = "CLKCFG", .cp = 14, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, + .readfn = pxa2xx_clkcfg_read, .writefn = pxa2xx_clkcfg_write }, + /* cp14 crn==7: PWRMODE */ + { .name = "PWRMODE", .cp = 14, .crn = 7, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, + .readfn = arm_cp_read_zero, .writefn = pxa2xx_pwrmode_write }, + REGINFO_SENTINEL +}; + +static void pxa2xx_setup_cp14(PXA2xxState *s) { - switch (crm) { - case 0: - pxa2xx_clkpwr_write(opaque, op2, reg, crm, value); - break; - case 1: - pxa2xx_perf_write(opaque, op2, reg, crm, value); - break; - case 2: - switch (reg) { - case CPPMN0: - case CPPMN1: - case CPPMN2: - case CPPMN3: - return; - } - /* Fall through */ - default: - printf("%s: Bad register 0x%x\n", __FUNCTION__, reg); - break; - } + define_arm_cp_regs_with_opaque(s->cpu, pxa_cp_reginfo, s); } #define MDCNFG 0x00 /* SDRAM Configuration register */ @@ -2133,7 +2084,7 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space, memory_region_add_subregion(address_space, s->cm_base, &s->cm_iomem); vmstate_register(NULL, 0, &vmstate_pxa2xx_cm, s); - cpu_arm_set_cp_io(&s->cpu->env, 14, pxa2xx_cp14_read, pxa2xx_cp14_write, s); + pxa2xx_setup_cp14(s); s->mm_base = 0x48000000; s->mm_regs[MDMRS >> 2] = 0x00020002; @@ -2264,7 +2215,7 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) memory_region_add_subregion(address_space, s->cm_base, &s->cm_iomem); vmstate_register(NULL, 0, &vmstate_pxa2xx_cm, s); - cpu_arm_set_cp_io(&s->cpu->env, 14, pxa2xx_cp14_read, pxa2xx_cp14_write, s); + pxa2xx_setup_cp14(s); s->mm_base = 0x48000000; s->mm_regs[MDMRS >> 2] = 0x00020002; diff --git a/hw/pxa2xx_pic.c b/hw/pxa2xx_pic.c index c560133930..e1e8830ff0 100644 --- a/hw/pxa2xx_pic.c +++ b/hw/pxa2xx_pic.c @@ -209,33 +209,42 @@ static const int pxa2xx_cp_reg_map[0x10] = { [0xa] = ICPR2, }; -static uint32_t pxa2xx_pic_cp_read(void *opaque, int op2, int reg, int crm) +static int pxa2xx_pic_cp_read(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t *value) { - target_phys_addr_t offset; - - if (pxa2xx_cp_reg_map[reg] == -1) { - printf("%s: Bad register 0x%x\n", __FUNCTION__, reg); - return 0; - } - - offset = pxa2xx_cp_reg_map[reg]; - return pxa2xx_pic_mem_read(opaque, offset, 4); + int offset = pxa2xx_cp_reg_map[ri->crn]; + *value = pxa2xx_pic_mem_read(ri->opaque, offset, 4); + return 0; } -static void pxa2xx_pic_cp_write(void *opaque, int op2, int reg, int crm, - uint32_t value) +static int pxa2xx_pic_cp_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) { - target_phys_addr_t offset; - - if (pxa2xx_cp_reg_map[reg] == -1) { - printf("%s: Bad register 0x%x\n", __FUNCTION__, reg); - return; - } - - offset = pxa2xx_cp_reg_map[reg]; - pxa2xx_pic_mem_write(opaque, offset, value, 4); + int offset = pxa2xx_cp_reg_map[ri->crn]; + pxa2xx_pic_mem_write(ri->opaque, offset, value, 4); + return 0; } +#define REGINFO_FOR_PIC_CP(NAME, CRN) \ + { .name = NAME, .cp = 6, .crn = CRN, .crm = 0, .opc1 = 0, .opc2 = 0, \ + .access = PL1_RW, \ + .readfn = pxa2xx_pic_cp_read, .writefn = pxa2xx_pic_cp_write } + +static const ARMCPRegInfo pxa_pic_cp_reginfo[] = { + REGINFO_FOR_PIC_CP("ICIP", 0), + REGINFO_FOR_PIC_CP("ICMR", 1), + REGINFO_FOR_PIC_CP("ICLR", 2), + REGINFO_FOR_PIC_CP("ICFP", 3), + REGINFO_FOR_PIC_CP("ICPR", 4), + REGINFO_FOR_PIC_CP("ICHP", 5), + REGINFO_FOR_PIC_CP("ICIP2", 6), + REGINFO_FOR_PIC_CP("ICMR2", 7), + REGINFO_FOR_PIC_CP("ICLR2", 8), + REGINFO_FOR_PIC_CP("ICFP2", 9), + REGINFO_FOR_PIC_CP("ICPR2", 0xa), + REGINFO_SENTINEL +}; + static const MemoryRegionOps pxa2xx_pic_ops = { .read = pxa2xx_pic_mem_read, .write = pxa2xx_pic_mem_write, @@ -274,7 +283,7 @@ DeviceState *pxa2xx_pic_init(target_phys_addr_t base, ARMCPU *cpu) sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); /* Enable IC coprocessor access. */ - cpu_arm_set_cp_io(env, 6, pxa2xx_pic_cp_read, pxa2xx_pic_cp_write, s); + define_arm_cp_regs_with_opaque(arm_env_get_cpu(env), pxa_pic_cp_reginfo, s); return dev; } diff --git a/hw/qdev-monitor.c b/hw/qdev-monitor.c index 17452c8c01..7915b4500d 100644 --- a/hw/qdev-monitor.c +++ b/hw/qdev-monitor.c @@ -20,6 +20,7 @@ #include "qdev.h" #include "monitor.h" #include "qmp-commands.h" +#include "arch_init.h" /* * Aliases were a bad idea from the start. Let's keep them @@ -29,16 +30,18 @@ typedef struct QDevAlias { const char *typename; const char *alias; + uint32_t arch_mask; } QDevAlias; static const QDevAlias qdev_alias_table[] = { - { "virtio-blk-pci", "virtio-blk" }, - { "virtio-net-pci", "virtio-net" }, - { "virtio-serial-pci", "virtio-serial" }, - { "virtio-balloon-pci", "virtio-balloon" }, - { "virtio-blk-s390", "virtio-blk" }, - { "virtio-net-s390", "virtio-net" }, - { "virtio-serial-s390", "virtio-serial" }, + { "virtio-blk-pci", "virtio-blk", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, + { "virtio-net-pci", "virtio-net", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, + { "virtio-serial-pci", "virtio-serial", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, + { "virtio-balloon-pci", "virtio-balloon", + QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, + { "virtio-blk-s390", "virtio-blk", QEMU_ARCH_S390X }, + { "virtio-net-s390", "virtio-net", QEMU_ARCH_S390X }, + { "virtio-serial-s390", "virtio-serial", QEMU_ARCH_S390X }, { "lsi53c895a", "lsi" }, { "ich9-ahci", "ahci" }, { } @@ -50,6 +53,11 @@ static const char *qdev_class_get_alias(DeviceClass *dc) int i; for (i = 0; qdev_alias_table[i].typename; i++) { + if (qdev_alias_table[i].arch_mask && + !(qdev_alias_table[i].arch_mask & arch_type)) { + continue; + } + if (strcmp(qdev_alias_table[i].typename, typename) == 0) { return qdev_alias_table[i].alias; } @@ -110,6 +118,11 @@ static const char *find_typename_by_alias(const char *alias) int i; for (i = 0; qdev_alias_table[i].alias; i++) { + if (qdev_alias_table[i].arch_mask && + !(qdev_alias_table[i].arch_mask & arch_type)) { + continue; + } + if (strcmp(qdev_alias_table[i].alias, alias) == 0) { return qdev_alias_table[i].typename; } diff --git a/hw/qdev-properties.c b/hw/qdev-properties.c index 099a7aa96f..0b894620c9 100644 --- a/hw/qdev-properties.c +++ b/hw/qdev-properties.c @@ -899,6 +899,113 @@ PropertyInfo qdev_prop_blocksize = { .set = set_blocksize, }; +/* --- pci host address --- */ + +static void get_pci_host_devaddr(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + PCIHostDeviceAddress *addr = qdev_get_prop_ptr(dev, prop); + char buffer[] = "xxxx:xx:xx.x"; + char *p = buffer; + int rc = 0; + + rc = snprintf(buffer, sizeof(buffer), "%04x:%02x:%02x.%d", + addr->domain, addr->bus, addr->slot, addr->function); + assert(rc == sizeof(buffer) - 1); + + visit_type_str(v, &p, name, errp); +} + +/* + * Parse [<domain>:]<bus>:<slot>.<func> + * if <domain> is not supplied, it's assumed to be 0. + */ +static void set_pci_host_devaddr(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + PCIHostDeviceAddress *addr = qdev_get_prop_ptr(dev, prop); + Error *local_err = NULL; + char *str, *p; + char *e; + unsigned long val; + unsigned long dom = 0, bus = 0; + unsigned int slot = 0, func = 0; + + if (dev->state != DEV_STATE_CREATED) { + error_set(errp, QERR_PERMISSION_DENIED); + return; + } + + visit_type_str(v, &str, name, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + p = str; + val = strtoul(p, &e, 16); + if (e == p || *e != ':') { + goto inval; + } + bus = val; + + p = e + 1; + val = strtoul(p, &e, 16); + if (e == p) { + goto inval; + } + if (*e == ':') { + dom = bus; + bus = val; + p = e + 1; + val = strtoul(p, &e, 16); + if (e == p) { + goto inval; + } + } + slot = val; + + if (*e != '.') { + goto inval; + } + p = e + 1; + val = strtoul(p, &e, 10); + if (e == p) { + goto inval; + } + func = val; + + if (dom > 0xffff || bus > 0xff || slot > 0x1f || func > 7) { + goto inval; + } + + if (*e) { + goto inval; + } + + addr->domain = dom; + addr->bus = bus; + addr->slot = slot; + addr->function = func; + + g_free(str); + return; + +inval: + error_set_from_qdev_prop_error(errp, EINVAL, dev, prop, str); + g_free(str); +} + +PropertyInfo qdev_prop_pci_host_devaddr = { + .name = "pci-host-devaddr", + .get = get_pci_host_devaddr, + .set = set_pci_host_devaddr, +}; + /* --- public helpers --- */ static Property *qdev_prop_walk(Property *props, const char *name) @@ -237,6 +237,7 @@ extern PropertyInfo qdev_prop_netdev; extern PropertyInfo qdev_prop_vlan; extern PropertyInfo qdev_prop_pci_devfn; extern PropertyInfo qdev_prop_blocksize; +extern PropertyInfo qdev_prop_pci_host_devaddr; #define DEFINE_PROP(_name, _state, _field, _prop, _type) { \ .name = (_name), \ @@ -300,6 +301,8 @@ extern PropertyInfo qdev_prop_blocksize; LostTickPolicy) #define DEFINE_PROP_BLOCKSIZE(_n, _s, _f, _d) \ DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_blocksize, uint16_t) +#define DEFINE_PROP_PCI_HOST_DEVADDR(_n, _s, _f) \ + DEFINE_PROP(_n, _s, _f, qdev_prop_pci_host_devaddr, PCIHostDeviceAddress) #define DEFINE_PROP_END_OF_LIST() \ {} diff --git a/hw/spapr.c b/hw/spapr.c index d0bddbce95..09a23ff092 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -146,6 +146,40 @@ static int spapr_set_associativity(void *fdt, sPAPREnvironment *spapr) return ret; } + +static size_t create_page_sizes_prop(CPUPPCState *env, uint32_t *prop, + size_t maxsize) +{ + size_t maxcells = maxsize / sizeof(uint32_t); + int i, j, count; + uint32_t *p = prop; + + for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { + struct ppc_one_seg_page_size *sps = &env->sps.sps[i]; + + if (!sps->page_shift) { + break; + } + for (count = 0; count < PPC_PAGE_SIZES_MAX_SZ; count++) { + if (sps->enc[count].page_shift == 0) { + break; + } + } + if ((p - prop) >= (maxcells - 3 - count * 2)) { + break; + } + *(p++) = cpu_to_be32(sps->page_shift); + *(p++) = cpu_to_be32(sps->slb_enc); + *(p++) = cpu_to_be32(count); + for (j = 0; j < count; j++) { + *(p++) = cpu_to_be32(sps->enc[j].page_shift); + *(p++) = cpu_to_be32(sps->enc[j].pte_enc); + } + } + + return (p - prop) * sizeof(uint32_t); +} + static void *spapr_create_fdt_skel(const char *cpu_model, target_phys_addr_t rma_size, target_phys_addr_t initrd_base, @@ -163,6 +197,7 @@ static void *spapr_create_fdt_skel(const char *cpu_model, uint32_t pft_size_prop[] = {0, cpu_to_be32(hash_shift)}; char hypertas_prop[] = "hcall-pft\0hcall-term\0hcall-dabr\0hcall-interrupt" "\0hcall-tce\0hcall-vio\0hcall-splpar\0hcall-bulk"; + char qemu_hypertas_prop[] = "hcall-memop1"; uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)}; int i; char *modelname; @@ -298,6 +333,8 @@ static void *spapr_create_fdt_skel(const char *cpu_model, 0xffffffff, 0xffffffff}; uint32_t tbfreq = kvm_enabled() ? kvmppc_get_tbfreq() : TIMEBASE_FREQ; uint32_t cpufreq = kvm_enabled() ? kvmppc_get_clockfreq() : 1000000000; + uint32_t page_sizes_prop[64]; + size_t page_sizes_prop_size; if ((index % smt) != 0) { continue; @@ -362,6 +399,13 @@ static void *spapr_create_fdt_skel(const char *cpu_model, _FDT((fdt_property_cell(fdt, "ibm,dfp", 1))); } + page_sizes_prop_size = create_page_sizes_prop(env, page_sizes_prop, + sizeof(page_sizes_prop)); + if (page_sizes_prop_size) { + _FDT((fdt_property(fdt, "ibm,segment-page-sizes", + page_sizes_prop, page_sizes_prop_size))); + } + _FDT((fdt_end_node(fdt))); } @@ -374,6 +418,8 @@ static void *spapr_create_fdt_skel(const char *cpu_model, _FDT((fdt_property(fdt, "ibm,hypertas-functions", hypertas_prop, sizeof(hypertas_prop)))); + _FDT((fdt_property(fdt, "qemu,hypertas-functions", qemu_hypertas_prop, + sizeof(qemu_hypertas_prop)))); _FDT((fdt_property(fdt, "ibm,associativity-reference-points", refpoints, sizeof(refpoints)))); diff --git a/hw/spapr.h b/hw/spapr.h index 654a7a8a34..c75172e0c0 100644 --- a/hw/spapr.h +++ b/hw/spapr.h @@ -264,7 +264,8 @@ typedef struct sPAPREnvironment { */ #define KVMPPC_HCALL_BASE 0xf000 #define KVMPPC_H_RTAS (KVMPPC_HCALL_BASE + 0x0) -#define KVMPPC_HCALL_MAX KVMPPC_H_RTAS +#define KVMPPC_H_LOGICAL_MEMOP (KVMPPC_HCALL_BASE + 0x1) +#define KVMPPC_HCALL_MAX KVMPPC_H_LOGICAL_MEMOP extern sPAPREnvironment *spapr; diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c index 94bb504ca6..a5990a9617 100644 --- a/hw/spapr_hcall.c +++ b/hw/spapr_hcall.c @@ -608,6 +608,73 @@ static target_ulong h_logical_store(CPUPPCState *env, sPAPREnvironment *spapr, return H_PARAMETER; } +static target_ulong h_logical_memop(CPUPPCState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong dst = args[0]; /* Destination address */ + target_ulong src = args[1]; /* Source address */ + target_ulong esize = args[2]; /* Element size (0=1,1=2,2=4,3=8) */ + target_ulong count = args[3]; /* Element count */ + target_ulong op = args[4]; /* 0 = copy, 1 = invert */ + uint64_t tmp; + unsigned int mask = (1 << esize) - 1; + int step = 1 << esize; + + if (count > 0x80000000) { + return H_PARAMETER; + } + + if ((dst & mask) || (src & mask) || (op > 1)) { + return H_PARAMETER; + } + + if (dst >= src && dst < (src + (count << esize))) { + dst = dst + ((count - 1) << esize); + src = src + ((count - 1) << esize); + step = -step; + } + + while (count--) { + switch (esize) { + case 0: + tmp = ldub_phys(src); + break; + case 1: + tmp = lduw_phys(src); + break; + case 2: + tmp = ldl_phys(src); + break; + case 3: + tmp = ldq_phys(src); + break; + default: + return H_PARAMETER; + } + if (op == 1) { + tmp = ~tmp; + } + switch (esize) { + case 0: + stb_phys(dst, tmp); + break; + case 1: + stw_phys(dst, tmp); + break; + case 2: + stl_phys(dst, tmp); + break; + case 3: + stq_phys(dst, tmp); + break; + } + dst = dst + step; + src = src + step; + } + + return H_SUCCESS; +} + static target_ulong h_logical_icbi(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { @@ -700,6 +767,7 @@ static void hypercall_register_types(void) spapr_register_hypercall(H_LOGICAL_CACHE_STORE, h_logical_store); spapr_register_hypercall(H_LOGICAL_ICBI, h_logical_icbi); spapr_register_hypercall(H_LOGICAL_DCBF, h_logical_dcbf); + spapr_register_hypercall(KVMPPC_H_LOGICAL_MEMOP, h_logical_memop); /* qemu/KVM-PPC specific hcalls */ spapr_register_hypercall(KVMPPC_H_RTAS, h_rtas); diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c index 037867ab4f..2f09616dd5 100644 --- a/hw/spapr_vscsi.c +++ b/hw/spapr_vscsi.c @@ -800,6 +800,7 @@ static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq) if (crq->s.IU_length > sizeof(union viosrp_iu)) { fprintf(stderr, "VSCSI: SRP IU too long (%d bytes) !\n", crq->s.IU_length); + vscsi_put_req(req); return; } @@ -807,7 +808,8 @@ static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq) if (spapr_tce_dma_read(&s->vdev, crq->s.IU_data_ptr, &req->iu, crq->s.IU_length)) { fprintf(stderr, "vscsi_got_payload: DMA read failure !\n"); - g_free(req); + vscsi_put_req(req); + return; } memcpy(&req->crq, crq, sizeof(vscsi_crq)); diff --git a/hw/usb/bus.c b/hw/usb/bus.c index f87cc5f443..b649360dd3 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -37,10 +37,23 @@ static const TypeInfo usb_bus_info = { static int next_usb_bus = 0; static QTAILQ_HEAD(, USBBus) busses = QTAILQ_HEAD_INITIALIZER(busses); +static int usb_device_post_load(void *opaque, int version_id) +{ + USBDevice *dev = opaque; + + if (dev->state == USB_STATE_NOTATTACHED) { + dev->attached = 0; + } else { + dev->attached = 1; + } + return 0; +} + const VMStateDescription vmstate_usb_device = { .name = "USBDevice", .version_id = 1, .minimum_version_id = 1, + .post_load = usb_device_post_load, .fields = (VMStateField []) { VMSTATE_UINT8(addr, USBDevice), VMSTATE_INT32(state, USBDevice), diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 5298204d9d..6d2d54940d 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -414,16 +414,17 @@ struct EHCIState { */ QEMUTimer *frame_timer; QEMUBH *async_bh; - int astate; // Current state in asynchronous schedule - int pstate; // Current state in periodic schedule + uint32_t astate; /* Current state in asynchronous schedule */ + uint32_t pstate; /* Current state in periodic schedule */ USBPort ports[NB_PORTS]; USBPort *companion_ports[NB_PORTS]; uint32_t usbsts_pending; EHCIQueueHead aqueues; EHCIQueueHead pqueues; - uint32_t a_fetch_addr; // which address to look at next - uint32_t p_fetch_addr; // which address to look at next + /* which address to look at next */ + uint32_t a_fetch_addr; + uint32_t p_fetch_addr; USBPacket ipacket; QEMUSGList isgl; @@ -568,6 +569,7 @@ static inline void ehci_set_interrupt(EHCIState *s, int intr) level = 1; } + trace_usb_ehci_interrupt(level, s->usbsts, s->usbintr); qemu_set_irq(s->irq, level); } @@ -821,8 +823,9 @@ static void ehci_attach(USBPort *port) { EHCIState *s = port->opaque; uint32_t *portsc = &s->portsc[port->index]; + const char *owner = (*portsc & PORTSC_POWNER) ? "comp" : "ehci"; - trace_usb_ehci_port_attach(port->index, port->dev->product_desc); + trace_usb_ehci_port_attach(port->index, owner, port->dev->product_desc); if (*portsc & PORTSC_POWNER) { USBPort *companion = s->companion_ports[port->index]; @@ -841,8 +844,9 @@ static void ehci_detach(USBPort *port) { EHCIState *s = port->opaque; uint32_t *portsc = &s->portsc[port->index]; + const char *owner = (*portsc & PORTSC_POWNER) ? "comp" : "ehci"; - trace_usb_ehci_port_detach(port->index); + trace_usb_ehci_port_detach(port->index, owner); if (*portsc & PORTSC_POWNER) { USBPort *companion = s->companion_ports[port->index]; @@ -2390,9 +2394,58 @@ static USBBusOps ehci_bus_ops = { .register_companion = ehci_register_companion, }; +static int usb_ehci_post_load(void *opaque, int version_id) +{ + EHCIState *s = opaque; + int i; + + for (i = 0; i < NB_PORTS; i++) { + USBPort *companion = s->companion_ports[i]; + if (companion == NULL) { + continue; + } + if (s->portsc[i] & PORTSC_POWNER) { + companion->dev = s->ports[i].dev; + } else { + companion->dev = NULL; + } + } + + return 0; +} + static const VMStateDescription vmstate_ehci = { - .name = "ehci", - .unmigratable = 1, + .name = "ehci", + .version_id = 1, + .post_load = usb_ehci_post_load, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(dev, EHCIState), + /* mmio registers */ + VMSTATE_UINT32(usbcmd, EHCIState), + VMSTATE_UINT32(usbsts, EHCIState), + VMSTATE_UINT32(usbintr, EHCIState), + VMSTATE_UINT32(frindex, EHCIState), + VMSTATE_UINT32(ctrldssegment, EHCIState), + VMSTATE_UINT32(periodiclistbase, EHCIState), + VMSTATE_UINT32(asynclistaddr, EHCIState), + VMSTATE_UINT32(configflag, EHCIState), + VMSTATE_UINT32(portsc[0], EHCIState), + VMSTATE_UINT32(portsc[1], EHCIState), + VMSTATE_UINT32(portsc[2], EHCIState), + VMSTATE_UINT32(portsc[3], EHCIState), + VMSTATE_UINT32(portsc[4], EHCIState), + VMSTATE_UINT32(portsc[5], EHCIState), + /* frame timer */ + VMSTATE_TIMER(frame_timer, EHCIState), + VMSTATE_UINT64(last_run_ns, EHCIState), + VMSTATE_UINT32(async_stepdown, EHCIState), + /* schedule state */ + VMSTATE_UINT32(astate, EHCIState), + VMSTATE_UINT32(pstate, EHCIState), + VMSTATE_UINT32(a_fetch_addr, EHCIState), + VMSTATE_UINT32(p_fetch_addr, EHCIState), + VMSTATE_END_OF_LIST() + } }; static Property ehci_properties[] = { diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 9871e24f50..2ebce04b24 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -292,10 +292,10 @@ static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev) static void uhci_async_cancel_all(UHCIState *s) { - UHCIQueue *queue; + UHCIQueue *queue, *nq; UHCIAsync *curr, *n; - QTAILQ_FOREACH(queue, &s->queues, next) { + QTAILQ_FOREACH_SAFE(queue, &s->queues, next, nq) { QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) { uhci_async_unlink(curr); uhci_async_cancel(curr); diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c index a95b0eda55..5479fb5987 100644 --- a/hw/usb/host-linux.c +++ b/hw/usb/host-linux.c @@ -111,6 +111,7 @@ typedef struct USBHostDevice { uint32_t iso_urb_count; uint32_t options; Notifier exit; + QEMUBH *bh; struct endp_data ep_in[USB_MAX_ENDPOINTS]; struct endp_data ep_out[USB_MAX_ENDPOINTS]; @@ -1421,6 +1422,43 @@ static void usb_host_exit_notifier(struct Notifier *n, void *data) } } +/* + * This is *NOT* about restoring state. We have absolutely no idea + * what state the host device is in at the moment and whenever it is + * still present in the first place. Attemping to contine where we + * left off is impossible. + * + * What we are going to to to here is emulate a surprise removal of + * the usb device passed through, then kick host scan so the device + * will get re-attached (and re-initialized by the guest) in case it + * is still present. + * + * As the device removal will change the state of other devices (usb + * host controller, most likely interrupt controller too) we have to + * wait with it until *all* vmstate is loaded. Thus post_load just + * kicks a bottom half which then does the actual work. + */ +static void usb_host_post_load_bh(void *opaque) +{ + USBHostDevice *dev = opaque; + + if (dev->fd != -1) { + usb_host_close(dev); + } + if (dev->dev.attached) { + usb_device_detach(&dev->dev); + } + usb_host_auto_check(NULL); +} + +static int usb_host_post_load(void *opaque, int version_id) +{ + USBHostDevice *dev = opaque; + + qemu_bh_schedule(dev->bh); + return 0; +} + static int usb_host_initfn(USBDevice *dev) { USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev); @@ -1432,6 +1470,7 @@ static int usb_host_initfn(USBDevice *dev) QTAILQ_INSERT_TAIL(&hostdevs, s, next); s->exit.notify = usb_host_exit_notifier; qemu_add_exit_notifier(&s->exit); + s->bh = qemu_bh_new(usb_host_post_load_bh, s); usb_host_auto_check(NULL); if (s->match.bus_num != 0 && s->match.port != NULL) { @@ -1443,7 +1482,13 @@ static int usb_host_initfn(USBDevice *dev) static const VMStateDescription vmstate_usb_host = { .name = "usb-host", - .unmigratable = 1, + .version_id = 1, + .minimum_version_id = 1, + .post_load = usb_host_post_load, + .fields = (VMStateField[]) { + VMSTATE_USB_DEVICE(dev, USBHostDevice), + VMSTATE_END_OF_LIST() + } }; static Property usb_host_dev_properties[] = { @@ -1737,25 +1782,27 @@ static void usb_host_auto_check(void *unused) struct USBHostDevice *s; int unconnected = 0; - usb_host_scan(NULL, usb_host_auto_scan); + if (runstate_is_running()) { + usb_host_scan(NULL, usb_host_auto_scan); - QTAILQ_FOREACH(s, &hostdevs, next) { - if (s->fd == -1) { - unconnected++; - } - if (s->seen == 0) { - s->errcount = 0; + QTAILQ_FOREACH(s, &hostdevs, next) { + if (s->fd == -1) { + unconnected++; + } + if (s->seen == 0) { + s->errcount = 0; + } + s->seen = 0; } - s->seen = 0; - } - if (unconnected == 0) { - /* nothing to watch */ - if (usb_auto_timer) { - qemu_del_timer(usb_auto_timer); - trace_usb_host_auto_scan_disabled(); + if (unconnected == 0) { + /* nothing to watch */ + if (usb_auto_timer) { + qemu_del_timer(usb_auto_timer); + trace_usb_host_auto_scan_disabled(); + } + return; } - return; } if (!usb_auto_timer) { diff --git a/hw/xen-host-pci-device.c b/hw/xen-host-pci-device.c new file mode 100644 index 0000000000..e7ff680ef2 --- /dev/null +++ b/hw/xen-host-pci-device.c @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2011 Citrix Ltd. + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include "qemu-common.h" +#include "xen-host-pci-device.h" + +#define XEN_HOST_PCI_MAX_EXT_CAP \ + ((PCIE_CONFIG_SPACE_SIZE - PCI_CONFIG_SPACE_SIZE) / (PCI_CAP_SIZEOF + 4)) + +#ifdef XEN_HOST_PCI_DEVICE_DEBUG +# define XEN_HOST_PCI_LOG(f, a...) fprintf(stderr, "%s: " f, __func__, ##a) +#else +# define XEN_HOST_PCI_LOG(f, a...) (void)0 +#endif + +/* + * from linux/ioport.h + * IO resources have these defined flags. + */ +#define IORESOURCE_BITS 0x000000ff /* Bus-specific bits */ + +#define IORESOURCE_TYPE_BITS 0x00000f00 /* Resource type */ +#define IORESOURCE_IO 0x00000100 +#define IORESOURCE_MEM 0x00000200 + +#define IORESOURCE_PREFETCH 0x00001000 /* No side effects */ +#define IORESOURCE_MEM_64 0x00100000 + +static int xen_host_pci_sysfs_path(const XenHostPCIDevice *d, + const char *name, char *buf, ssize_t size) +{ + int rc; + + rc = snprintf(buf, size, "/sys/bus/pci/devices/%04x:%02x:%02x.%d/%s", + d->domain, d->bus, d->dev, d->func, name); + + if (rc >= size || rc < 0) { + /* The ouput is truncated or an other error is encountered */ + return -ENODEV; + } + return 0; +} + + +/* This size should be enough to read the first 7 lines of a ressource file */ +#define XEN_HOST_PCI_RESSOURCE_BUFFER_SIZE 400 +static int xen_host_pci_get_resource(XenHostPCIDevice *d) +{ + int i, rc, fd; + char path[PATH_MAX]; + char buf[XEN_HOST_PCI_RESSOURCE_BUFFER_SIZE]; + unsigned long long start, end, flags, size; + char *endptr, *s; + uint8_t type; + + rc = xen_host_pci_sysfs_path(d, "resource", path, sizeof (path)); + if (rc) { + return rc; + } + fd = open(path, O_RDONLY); + if (fd == -1) { + XEN_HOST_PCI_LOG("Error: Can't open %s: %s\n", path, strerror(errno)); + return -errno; + } + + do { + rc = read(fd, &buf, sizeof (buf) - 1); + if (rc < 0 && errno != EINTR) { + rc = -errno; + goto out; + } + } while (rc < 0); + buf[rc] = 0; + rc = 0; + + s = buf; + for (i = 0; i < PCI_NUM_REGIONS; i++) { + type = 0; + + start = strtoll(s, &endptr, 16); + if (*endptr != ' ' || s == endptr) { + break; + } + s = endptr + 1; + end = strtoll(s, &endptr, 16); + if (*endptr != ' ' || s == endptr) { + break; + } + s = endptr + 1; + flags = strtoll(s, &endptr, 16); + if (*endptr != '\n' || s == endptr) { + break; + } + s = endptr + 1; + + if (start) { + size = end - start + 1; + } else { + size = 0; + } + + if (flags & IORESOURCE_IO) { + type |= XEN_HOST_PCI_REGION_TYPE_IO; + } + if (flags & IORESOURCE_MEM) { + type |= XEN_HOST_PCI_REGION_TYPE_MEM; + } + if (flags & IORESOURCE_PREFETCH) { + type |= XEN_HOST_PCI_REGION_TYPE_PREFETCH; + } + if (flags & IORESOURCE_MEM_64) { + type |= XEN_HOST_PCI_REGION_TYPE_MEM_64; + } + + if (i < PCI_ROM_SLOT) { + d->io_regions[i].base_addr = start; + d->io_regions[i].size = size; + d->io_regions[i].type = type; + d->io_regions[i].bus_flags = flags & IORESOURCE_BITS; + } else { + d->rom.base_addr = start; + d->rom.size = size; + d->rom.type = type; + d->rom.bus_flags = flags & IORESOURCE_BITS; + } + } + if (i != PCI_NUM_REGIONS) { + /* Invalid format or input to short */ + rc = -ENODEV; + } + +out: + close(fd); + return rc; +} + +/* This size should be enough to read a long from a file */ +#define XEN_HOST_PCI_GET_VALUE_BUFFER_SIZE 22 +static int xen_host_pci_get_value(XenHostPCIDevice *d, const char *name, + unsigned int *pvalue, int base) +{ + char path[PATH_MAX]; + char buf[XEN_HOST_PCI_GET_VALUE_BUFFER_SIZE]; + int fd, rc; + unsigned long value; + char *endptr; + + rc = xen_host_pci_sysfs_path(d, name, path, sizeof (path)); + if (rc) { + return rc; + } + fd = open(path, O_RDONLY); + if (fd == -1) { + XEN_HOST_PCI_LOG("Error: Can't open %s: %s\n", path, strerror(errno)); + return -errno; + } + do { + rc = read(fd, &buf, sizeof (buf) - 1); + if (rc < 0 && errno != EINTR) { + rc = -errno; + goto out; + } + } while (rc < 0); + buf[rc] = 0; + value = strtol(buf, &endptr, base); + if (endptr == buf || *endptr != '\n') { + rc = -1; + } else if ((value == LONG_MIN || value == LONG_MAX) && errno == ERANGE) { + rc = -errno; + } else { + rc = 0; + *pvalue = value; + } +out: + close(fd); + return rc; +} + +static inline int xen_host_pci_get_hex_value(XenHostPCIDevice *d, + const char *name, + unsigned int *pvalue) +{ + return xen_host_pci_get_value(d, name, pvalue, 16); +} + +static inline int xen_host_pci_get_dec_value(XenHostPCIDevice *d, + const char *name, + unsigned int *pvalue) +{ + return xen_host_pci_get_value(d, name, pvalue, 10); +} + +static bool xen_host_pci_dev_is_virtfn(XenHostPCIDevice *d) +{ + char path[PATH_MAX]; + struct stat buf; + + if (xen_host_pci_sysfs_path(d, "physfn", path, sizeof (path))) { + return false; + } + return !stat(path, &buf); +} + +static int xen_host_pci_config_open(XenHostPCIDevice *d) +{ + char path[PATH_MAX]; + int rc; + + rc = xen_host_pci_sysfs_path(d, "config", path, sizeof (path)); + if (rc) { + return rc; + } + d->config_fd = open(path, O_RDWR); + if (d->config_fd < 0) { + return -errno; + } + return 0; +} + +static int xen_host_pci_config_read(XenHostPCIDevice *d, + int pos, void *buf, int len) +{ + int rc; + + do { + rc = pread(d->config_fd, buf, len, pos); + } while (rc < 0 && (errno == EINTR || errno == EAGAIN)); + if (rc != len) { + return -errno; + } + return 0; +} + +static int xen_host_pci_config_write(XenHostPCIDevice *d, + int pos, const void *buf, int len) +{ + int rc; + + do { + rc = pwrite(d->config_fd, buf, len, pos); + } while (rc < 0 && (errno == EINTR || errno == EAGAIN)); + if (rc != len) { + return -errno; + } + return 0; +} + + +int xen_host_pci_get_byte(XenHostPCIDevice *d, int pos, uint8_t *p) +{ + uint8_t buf; + int rc = xen_host_pci_config_read(d, pos, &buf, 1); + if (!rc) { + *p = buf; + } + return rc; +} + +int xen_host_pci_get_word(XenHostPCIDevice *d, int pos, uint16_t *p) +{ + uint16_t buf; + int rc = xen_host_pci_config_read(d, pos, &buf, 2); + if (!rc) { + *p = le16_to_cpu(buf); + } + return rc; +} + +int xen_host_pci_get_long(XenHostPCIDevice *d, int pos, uint32_t *p) +{ + uint32_t buf; + int rc = xen_host_pci_config_read(d, pos, &buf, 4); + if (!rc) { + *p = le32_to_cpu(buf); + } + return rc; +} + +int xen_host_pci_get_block(XenHostPCIDevice *d, int pos, uint8_t *buf, int len) +{ + return xen_host_pci_config_read(d, pos, buf, len); +} + +int xen_host_pci_set_byte(XenHostPCIDevice *d, int pos, uint8_t data) +{ + return xen_host_pci_config_write(d, pos, &data, 1); +} + +int xen_host_pci_set_word(XenHostPCIDevice *d, int pos, uint16_t data) +{ + data = cpu_to_le16(data); + return xen_host_pci_config_write(d, pos, &data, 2); +} + +int xen_host_pci_set_long(XenHostPCIDevice *d, int pos, uint32_t data) +{ + data = cpu_to_le32(data); + return xen_host_pci_config_write(d, pos, &data, 4); +} + +int xen_host_pci_set_block(XenHostPCIDevice *d, int pos, uint8_t *buf, int len) +{ + return xen_host_pci_config_write(d, pos, buf, len); +} + +int xen_host_pci_find_ext_cap_offset(XenHostPCIDevice *d, uint32_t cap) +{ + uint32_t header = 0; + int max_cap = XEN_HOST_PCI_MAX_EXT_CAP; + int pos = PCI_CONFIG_SPACE_SIZE; + + do { + if (xen_host_pci_get_long(d, pos, &header)) { + break; + } + /* + * If we have no capabilities, this is indicated by cap ID, + * cap version and next pointer all being 0. + */ + if (header == 0) { + break; + } + + if (PCI_EXT_CAP_ID(header) == cap) { + return pos; + } + + pos = PCI_EXT_CAP_NEXT(header); + if (pos < PCI_CONFIG_SPACE_SIZE) { + break; + } + + max_cap--; + } while (max_cap > 0); + + return -1; +} + +int xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain, + uint8_t bus, uint8_t dev, uint8_t func) +{ + unsigned int v; + int rc = 0; + + d->config_fd = -1; + d->domain = domain; + d->bus = bus; + d->dev = dev; + d->func = func; + + rc = xen_host_pci_config_open(d); + if (rc) { + goto error; + } + rc = xen_host_pci_get_resource(d); + if (rc) { + goto error; + } + rc = xen_host_pci_get_hex_value(d, "vendor", &v); + if (rc) { + goto error; + } + d->vendor_id = v; + rc = xen_host_pci_get_hex_value(d, "device", &v); + if (rc) { + goto error; + } + d->device_id = v; + rc = xen_host_pci_get_dec_value(d, "irq", &v); + if (rc) { + goto error; + } + d->irq = v; + d->is_virtfn = xen_host_pci_dev_is_virtfn(d); + + return 0; +error: + if (d->config_fd >= 0) { + close(d->config_fd); + d->config_fd = -1; + } + return rc; +} + +void xen_host_pci_device_put(XenHostPCIDevice *d) +{ + if (d->config_fd >= 0) { + close(d->config_fd); + d->config_fd = -1; + } +} diff --git a/hw/xen-host-pci-device.h b/hw/xen-host-pci-device.h new file mode 100644 index 0000000000..0079daca51 --- /dev/null +++ b/hw/xen-host-pci-device.h @@ -0,0 +1,55 @@ +#ifndef XEN_HOST_PCI_DEVICE_H +#define XEN_HOST_PCI_DEVICE_H + +#include "pci.h" + +enum { + XEN_HOST_PCI_REGION_TYPE_IO = 1 << 1, + XEN_HOST_PCI_REGION_TYPE_MEM = 1 << 2, + XEN_HOST_PCI_REGION_TYPE_PREFETCH = 1 << 3, + XEN_HOST_PCI_REGION_TYPE_MEM_64 = 1 << 4, +}; + +typedef struct XenHostPCIIORegion { + pcibus_t base_addr; + pcibus_t size; + uint8_t type; + uint8_t bus_flags; /* Bus-specific bits */ +} XenHostPCIIORegion; + +typedef struct XenHostPCIDevice { + uint16_t domain; + uint8_t bus; + uint8_t dev; + uint8_t func; + + uint16_t vendor_id; + uint16_t device_id; + int irq; + + XenHostPCIIORegion io_regions[PCI_NUM_REGIONS - 1]; + XenHostPCIIORegion rom; + + bool is_virtfn; + + int config_fd; +} XenHostPCIDevice; + +int xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain, + uint8_t bus, uint8_t dev, uint8_t func); +void xen_host_pci_device_put(XenHostPCIDevice *pci_dev); + +int xen_host_pci_get_byte(XenHostPCIDevice *d, int pos, uint8_t *p); +int xen_host_pci_get_word(XenHostPCIDevice *d, int pos, uint16_t *p); +int xen_host_pci_get_long(XenHostPCIDevice *d, int pos, uint32_t *p); +int xen_host_pci_get_block(XenHostPCIDevice *d, int pos, uint8_t *buf, + int len); +int xen_host_pci_set_byte(XenHostPCIDevice *d, int pos, uint8_t data); +int xen_host_pci_set_word(XenHostPCIDevice *d, int pos, uint16_t data); +int xen_host_pci_set_long(XenHostPCIDevice *d, int pos, uint32_t data); +int xen_host_pci_set_block(XenHostPCIDevice *d, int pos, uint8_t *buf, + int len); + +int xen_host_pci_find_ext_cap_offset(XenHostPCIDevice *s, uint32_t cap); + +#endif /* !XEN_HOST_PCI_DEVICE_H_ */ diff --git a/hw/xen_backend.c b/hw/xen_backend.c index 66cb144397..f83a1e1d09 100644 --- a/hw/xen_backend.c +++ b/hw/xen_backend.c @@ -34,15 +34,13 @@ #include <sys/mman.h> #include <sys/signal.h> -#include <xs.h> -#include <xenctrl.h> -#include <xen/grant_table.h> - #include "hw.h" #include "qemu-char.h" #include "qemu-log.h" #include "xen_backend.h" +#include <xen/grant_table.h> + /* ------------------------------------------------------------- */ /* public */ diff --git a/hw/xen_common.h b/hw/xen_common.h index fe7f227f92..727757afb4 100644 --- a/hw/xen_common.h +++ b/hw/xen_common.h @@ -7,7 +7,11 @@ #include <inttypes.h> #include <xenctrl.h> -#include <xs.h> +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 420 +# include <xs.h> +#else +# include <xenstore.h> +#endif #include <xen/io/xenbus.h> #include "hw.h" @@ -150,4 +154,7 @@ static inline int xen_xc_hvm_inject_msi(XenXC xen_xc, domid_t dom, void destroy_hvm_domain(bool reboot); +/* shutdown/destroy current domain because of an error */ +void xen_shutdown_fatal_error(const char *fmt, ...) GCC_FMT_ATTR(1, 2); + #endif /* QEMU_HW_XEN_COMMON_H */ diff --git a/hw/xen_console.c b/hw/xen_console.c index 3794b1972d..9426d7374f 100644 --- a/hw/xen_console.c +++ b/hw/xen_console.c @@ -28,14 +28,13 @@ #include <termios.h> #include <stdarg.h> #include <sys/mman.h> -#include <xs.h> -#include <xen/io/console.h> -#include <xenctrl.h> #include "hw.h" #include "qemu-char.h" #include "xen_backend.h" +#include <xen/io/console.h> + struct buffer { uint8_t *data; size_t consumed; diff --git a/hw/xen_disk.c b/hw/xen_disk.c index fb68ed9bbf..e6bb2f20b9 100644 --- a/hw/xen_disk.c +++ b/hw/xen_disk.c @@ -35,14 +35,10 @@ #include <sys/mman.h> #include <sys/uio.h> -#include <xs.h> -#include <xenctrl.h> -#include <xen/io/xenbus.h> - #include "hw.h" #include "qemu-char.h" -#include "xen_blkif.h" #include "xen_backend.h" +#include "xen_blkif.h" #include "blockdev.h" /* ------------------------------------------------------------- */ diff --git a/hw/xen_nic.c b/hw/xen_nic.c index 9a59bdad6e..98db9bb8f6 100644 --- a/hw/xen_nic.c +++ b/hw/xen_nic.c @@ -35,11 +35,6 @@ #include <sys/mman.h> #include <sys/wait.h> -#include <xs.h> -#include <xenctrl.h> -#include <xen/io/xenbus.h> -#include <xen/io/netif.h> - #include "hw.h" #include "net.h" #include "net/checksum.h" @@ -47,6 +42,8 @@ #include "qemu-char.h" #include "xen_backend.h" +#include <xen/io/netif.h> + /* ------------------------------------------------------------- */ struct XenNetDev { diff --git a/hw/xen_platform.c b/hw/xen_platform.c index 0214f370b2..c1fe984f07 100644 --- a/hw/xen_platform.c +++ b/hw/xen_platform.c @@ -83,7 +83,7 @@ static void log_writeb(PCIXenPlatformState *s, char val) #define UNPLUG_ALL_NICS 2 #define UNPLUG_AUX_IDE_DISKS 4 -static void unplug_nic(PCIBus *b, PCIDevice *d) +static void unplug_nic(PCIBus *b, PCIDevice *d, void *o) { if (pci_get_word(d->config + PCI_CLASS_DEVICE) == PCI_CLASS_NETWORK_ETHERNET) { @@ -96,10 +96,10 @@ static void unplug_nic(PCIBus *b, PCIDevice *d) static void pci_unplug_nics(PCIBus *bus) { - pci_for_each_device(bus, 0, unplug_nic); + pci_for_each_device(bus, 0, unplug_nic, NULL); } -static void unplug_disks(PCIBus *b, PCIDevice *d) +static void unplug_disks(PCIBus *b, PCIDevice *d, void *o) { if (pci_get_word(d->config + PCI_CLASS_DEVICE) == PCI_CLASS_STORAGE_IDE) { @@ -109,7 +109,7 @@ static void unplug_disks(PCIBus *b, PCIDevice *d) static void pci_unplug_disks(PCIBus *bus) { - pci_for_each_device(bus, 0, unplug_disks); + pci_for_each_device(bus, 0, unplug_disks, NULL); } static void platform_fixed_ioport_writew(void *opaque, uint32_t addr, uint32_t val) diff --git a/hw/xen_pt.c b/hw/xen_pt.c new file mode 100644 index 0000000000..3b6d1867ab --- /dev/null +++ b/hw/xen_pt.c @@ -0,0 +1,851 @@ +/* + * Copyright (c) 2007, Neocleus Corporation. + * Copyright (c) 2007, Intel Corporation. + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Alex Novik <alex@neocleus.com> + * Allen Kay <allen.m.kay@intel.com> + * Guy Zana <guy@neocleus.com> + * + * This file implements direct PCI assignment to a HVM guest + */ + +/* + * Interrupt Disable policy: + * + * INTx interrupt: + * Initialize(register_real_device) + * Map INTx(xc_physdev_map_pirq): + * <fail> + * - Set real Interrupt Disable bit to '1'. + * - Set machine_irq and assigned_device->machine_irq to '0'. + * * Don't bind INTx. + * + * Bind INTx(xc_domain_bind_pt_pci_irq): + * <fail> + * - Set real Interrupt Disable bit to '1'. + * - Unmap INTx. + * - Decrement xen_pt_mapped_machine_irq[machine_irq] + * - Set assigned_device->machine_irq to '0'. + * + * Write to Interrupt Disable bit by guest software(xen_pt_cmd_reg_write) + * Write '0' + * - Set real bit to '0' if assigned_device->machine_irq isn't '0'. + * + * Write '1' + * - Set real bit to '1'. + * + * MSI interrupt: + * Initialize MSI register(xen_pt_msi_setup, xen_pt_msi_update) + * Bind MSI(xc_domain_update_msi_irq) + * <fail> + * - Unmap MSI. + * - Set dev->msi->pirq to '-1'. + * + * MSI-X interrupt: + * Initialize MSI-X register(xen_pt_msix_update_one) + * Bind MSI-X(xc_domain_update_msi_irq) + * <fail> + * - Unmap MSI-X. + * - Set entry->pirq to '-1'. + */ + +#include <sys/ioctl.h> + +#include "pci.h" +#include "xen.h" +#include "xen_backend.h" +#include "xen_pt.h" +#include "range.h" + +#define XEN_PT_NR_IRQS (256) +static uint8_t xen_pt_mapped_machine_irq[XEN_PT_NR_IRQS] = {0}; + +void xen_pt_log(const PCIDevice *d, const char *f, ...) +{ + va_list ap; + + va_start(ap, f); + if (d) { + fprintf(stderr, "[%02x:%02x.%d] ", pci_bus_num(d->bus), + PCI_SLOT(d->devfn), PCI_FUNC(d->devfn)); + } + vfprintf(stderr, f, ap); + va_end(ap); +} + +/* Config Space */ + +static int xen_pt_pci_config_access_check(PCIDevice *d, uint32_t addr, int len) +{ + /* check offset range */ + if (addr >= 0xFF) { + XEN_PT_ERR(d, "Failed to access register with offset exceeding 0xFF. " + "(addr: 0x%02x, len: %d)\n", addr, len); + return -1; + } + + /* check read size */ + if ((len != 1) && (len != 2) && (len != 4)) { + XEN_PT_ERR(d, "Failed to access register with invalid access length. " + "(addr: 0x%02x, len: %d)\n", addr, len); + return -1; + } + + /* check offset alignment */ + if (addr & (len - 1)) { + XEN_PT_ERR(d, "Failed to access register with invalid access size " + "alignment. (addr: 0x%02x, len: %d)\n", addr, len); + return -1; + } + + return 0; +} + +int xen_pt_bar_offset_to_index(uint32_t offset) +{ + int index = 0; + + /* check Exp ROM BAR */ + if (offset == PCI_ROM_ADDRESS) { + return PCI_ROM_SLOT; + } + + /* calculate BAR index */ + index = (offset - PCI_BASE_ADDRESS_0) >> 2; + if (index >= PCI_NUM_REGIONS) { + return -1; + } + + return index; +} + +static uint32_t xen_pt_pci_read_config(PCIDevice *d, uint32_t addr, int len) +{ + XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d); + uint32_t val = 0; + XenPTRegGroup *reg_grp_entry = NULL; + XenPTReg *reg_entry = NULL; + int rc = 0; + int emul_len = 0; + uint32_t find_addr = addr; + + if (xen_pt_pci_config_access_check(d, addr, len)) { + goto exit; + } + + /* find register group entry */ + reg_grp_entry = xen_pt_find_reg_grp(s, addr); + if (reg_grp_entry) { + /* check 0-Hardwired register group */ + if (reg_grp_entry->reg_grp->grp_type == XEN_PT_GRP_TYPE_HARDWIRED) { + /* no need to emulate, just return 0 */ + val = 0; + goto exit; + } + } + + /* read I/O device register value */ + rc = xen_host_pci_get_block(&s->real_device, addr, (uint8_t *)&val, len); + if (rc < 0) { + XEN_PT_ERR(d, "pci_read_block failed. return value: %d.\n", rc); + memset(&val, 0xff, len); + } + + /* just return the I/O device register value for + * passthrough type register group */ + if (reg_grp_entry == NULL) { + goto exit; + } + + /* adjust the read value to appropriate CFC-CFF window */ + val <<= (addr & 3) << 3; + emul_len = len; + + /* loop around the guest requested size */ + while (emul_len > 0) { + /* find register entry to be emulated */ + reg_entry = xen_pt_find_reg(reg_grp_entry, find_addr); + if (reg_entry) { + XenPTRegInfo *reg = reg_entry->reg; + uint32_t real_offset = reg_grp_entry->base_offset + reg->offset; + uint32_t valid_mask = 0xFFFFFFFF >> ((4 - emul_len) << 3); + uint8_t *ptr_val = NULL; + + valid_mask <<= (find_addr - real_offset) << 3; + ptr_val = (uint8_t *)&val + (real_offset & 3); + + /* do emulation based on register size */ + switch (reg->size) { + case 1: + if (reg->u.b.read) { + rc = reg->u.b.read(s, reg_entry, ptr_val, valid_mask); + } + break; + case 2: + if (reg->u.w.read) { + rc = reg->u.w.read(s, reg_entry, + (uint16_t *)ptr_val, valid_mask); + } + break; + case 4: + if (reg->u.dw.read) { + rc = reg->u.dw.read(s, reg_entry, + (uint32_t *)ptr_val, valid_mask); + } + break; + } + + if (rc < 0) { + xen_shutdown_fatal_error("Internal error: Invalid read " + "emulation. (%s, rc: %d)\n", + __func__, rc); + return 0; + } + + /* calculate next address to find */ + emul_len -= reg->size; + if (emul_len > 0) { + find_addr = real_offset + reg->size; + } + } else { + /* nothing to do with passthrough type register, + * continue to find next byte */ + emul_len--; + find_addr++; + } + } + + /* need to shift back before returning them to pci bus emulator */ + val >>= ((addr & 3) << 3); + +exit: + XEN_PT_LOG_CONFIG(d, addr, val, len); + return val; +} + +static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr, + uint32_t val, int len) +{ + XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d); + int index = 0; + XenPTRegGroup *reg_grp_entry = NULL; + int rc = 0; + uint32_t read_val = 0; + int emul_len = 0; + XenPTReg *reg_entry = NULL; + uint32_t find_addr = addr; + XenPTRegInfo *reg = NULL; + + if (xen_pt_pci_config_access_check(d, addr, len)) { + return; + } + + XEN_PT_LOG_CONFIG(d, addr, val, len); + + /* check unused BAR register */ + index = xen_pt_bar_offset_to_index(addr); + if ((index >= 0) && (val > 0 && val < XEN_PT_BAR_ALLF) && + (s->bases[index].bar_flag == XEN_PT_BAR_FLAG_UNUSED)) { + XEN_PT_WARN(d, "Guest attempt to set address to unused Base Address " + "Register. (addr: 0x%02x, len: %d)\n", addr, len); + } + + /* find register group entry */ + reg_grp_entry = xen_pt_find_reg_grp(s, addr); + if (reg_grp_entry) { + /* check 0-Hardwired register group */ + if (reg_grp_entry->reg_grp->grp_type == XEN_PT_GRP_TYPE_HARDWIRED) { + /* ignore silently */ + XEN_PT_WARN(d, "Access to 0-Hardwired register. " + "(addr: 0x%02x, len: %d)\n", addr, len); + return; + } + } + + rc = xen_host_pci_get_block(&s->real_device, addr, + (uint8_t *)&read_val, len); + if (rc < 0) { + XEN_PT_ERR(d, "pci_read_block failed. return value: %d.\n", rc); + memset(&read_val, 0xff, len); + } + + /* pass directly to the real device for passthrough type register group */ + if (reg_grp_entry == NULL) { + goto out; + } + + memory_region_transaction_begin(); + pci_default_write_config(d, addr, val, len); + + /* adjust the read and write value to appropriate CFC-CFF window */ + read_val <<= (addr & 3) << 3; + val <<= (addr & 3) << 3; + emul_len = len; + + /* loop around the guest requested size */ + while (emul_len > 0) { + /* find register entry to be emulated */ + reg_entry = xen_pt_find_reg(reg_grp_entry, find_addr); + if (reg_entry) { + reg = reg_entry->reg; + uint32_t real_offset = reg_grp_entry->base_offset + reg->offset; + uint32_t valid_mask = 0xFFFFFFFF >> ((4 - emul_len) << 3); + uint8_t *ptr_val = NULL; + + valid_mask <<= (find_addr - real_offset) << 3; + ptr_val = (uint8_t *)&val + (real_offset & 3); + + /* do emulation based on register size */ + switch (reg->size) { + case 1: + if (reg->u.b.write) { + rc = reg->u.b.write(s, reg_entry, ptr_val, + read_val >> ((real_offset & 3) << 3), + valid_mask); + } + break; + case 2: + if (reg->u.w.write) { + rc = reg->u.w.write(s, reg_entry, (uint16_t *)ptr_val, + (read_val >> ((real_offset & 3) << 3)), + valid_mask); + } + break; + case 4: + if (reg->u.dw.write) { + rc = reg->u.dw.write(s, reg_entry, (uint32_t *)ptr_val, + (read_val >> ((real_offset & 3) << 3)), + valid_mask); + } + break; + } + + if (rc < 0) { + xen_shutdown_fatal_error("Internal error: Invalid write" + " emulation. (%s, rc: %d)\n", + __func__, rc); + return; + } + + /* calculate next address to find */ + emul_len -= reg->size; + if (emul_len > 0) { + find_addr = real_offset + reg->size; + } + } else { + /* nothing to do with passthrough type register, + * continue to find next byte */ + emul_len--; + find_addr++; + } + } + + /* need to shift back before passing them to xen_host_pci_device */ + val >>= (addr & 3) << 3; + + memory_region_transaction_commit(); + +out: + if (!(reg && reg->no_wb)) { + /* unknown regs are passed through */ + rc = xen_host_pci_set_block(&s->real_device, addr, + (uint8_t *)&val, len); + + if (rc < 0) { + XEN_PT_ERR(d, "pci_write_block failed. return value: %d.\n", rc); + } + } +} + +/* register regions */ + +static uint64_t xen_pt_bar_read(void *o, target_phys_addr_t addr, + unsigned size) +{ + PCIDevice *d = o; + /* if this function is called, that probably means that there is a + * misconfiguration of the IOMMU. */ + XEN_PT_ERR(d, "Should not read BAR through QEMU. @0x"TARGET_FMT_plx"\n", + addr); + return 0; +} +static void xen_pt_bar_write(void *o, target_phys_addr_t addr, uint64_t val, + unsigned size) +{ + PCIDevice *d = o; + /* Same comment as xen_pt_bar_read function */ + XEN_PT_ERR(d, "Should not write BAR through QEMU. @0x"TARGET_FMT_plx"\n", + addr); +} + +static const MemoryRegionOps ops = { + .endianness = DEVICE_NATIVE_ENDIAN, + .read = xen_pt_bar_read, + .write = xen_pt_bar_write, +}; + +static int xen_pt_register_regions(XenPCIPassthroughState *s) +{ + int i = 0; + XenHostPCIDevice *d = &s->real_device; + + /* Register PIO/MMIO BARs */ + for (i = 0; i < PCI_ROM_SLOT; i++) { + XenHostPCIIORegion *r = &d->io_regions[i]; + uint8_t type; + + if (r->base_addr == 0 || r->size == 0) { + continue; + } + + s->bases[i].access.u = r->base_addr; + + if (r->type & XEN_HOST_PCI_REGION_TYPE_IO) { + type = PCI_BASE_ADDRESS_SPACE_IO; + } else { + type = PCI_BASE_ADDRESS_SPACE_MEMORY; + if (r->type & XEN_HOST_PCI_REGION_TYPE_PREFETCH) { + type |= PCI_BASE_ADDRESS_MEM_PREFETCH; + } + } + + memory_region_init_io(&s->bar[i], &ops, &s->dev, + "xen-pci-pt-bar", r->size); + pci_register_bar(&s->dev, i, type, &s->bar[i]); + + XEN_PT_LOG(&s->dev, "IO region %i registered (size=0x%08"PRIx64 + " base_addr=0x%08"PRIx64" type: %#x)\n", + i, r->size, r->base_addr, type); + } + + /* Register expansion ROM address */ + if (d->rom.base_addr && d->rom.size) { + uint32_t bar_data = 0; + + /* Re-set BAR reported by OS, otherwise ROM can't be read. */ + if (xen_host_pci_get_long(d, PCI_ROM_ADDRESS, &bar_data)) { + return 0; + } + if ((bar_data & PCI_ROM_ADDRESS_MASK) == 0) { + bar_data |= d->rom.base_addr & PCI_ROM_ADDRESS_MASK; + xen_host_pci_set_long(d, PCI_ROM_ADDRESS, bar_data); + } + + s->bases[PCI_ROM_SLOT].access.maddr = d->rom.base_addr; + + memory_region_init_rom_device(&s->rom, NULL, NULL, + "xen-pci-pt-rom", d->rom.size); + pci_register_bar(&s->dev, PCI_ROM_SLOT, PCI_BASE_ADDRESS_MEM_PREFETCH, + &s->rom); + + XEN_PT_LOG(&s->dev, "Expansion ROM registered (size=0x%08"PRIx64 + " base_addr=0x%08"PRIx64")\n", + d->rom.size, d->rom.base_addr); + } + + return 0; +} + +static void xen_pt_unregister_regions(XenPCIPassthroughState *s) +{ + XenHostPCIDevice *d = &s->real_device; + int i; + + for (i = 0; i < PCI_NUM_REGIONS - 1; i++) { + XenHostPCIIORegion *r = &d->io_regions[i]; + + if (r->base_addr == 0 || r->size == 0) { + continue; + } + + memory_region_destroy(&s->bar[i]); + } + if (d->rom.base_addr && d->rom.size) { + memory_region_destroy(&s->rom); + } +} + +/* region mapping */ + +static int xen_pt_bar_from_region(XenPCIPassthroughState *s, MemoryRegion *mr) +{ + int i = 0; + + for (i = 0; i < PCI_NUM_REGIONS - 1; i++) { + if (mr == &s->bar[i]) { + return i; + } + } + if (mr == &s->rom) { + return PCI_ROM_SLOT; + } + return -1; +} + +/* + * This function checks if an io_region overlaps an io_region from another + * device. The io_region to check is provided with (addr, size and type) + * A callback can be provided and will be called for every region that is + * overlapped. + * The return value indicates if the region is overlappsed */ +struct CheckBarArgs { + XenPCIPassthroughState *s; + pcibus_t addr; + pcibus_t size; + uint8_t type; + bool rc; +}; +static void xen_pt_check_bar_overlap(PCIBus *bus, PCIDevice *d, void *opaque) +{ + struct CheckBarArgs *arg = opaque; + XenPCIPassthroughState *s = arg->s; + uint8_t type = arg->type; + int i; + + if (d->devfn == s->dev.devfn) { + return; + } + + /* xxx: This ignores bridges. */ + for (i = 0; i < PCI_NUM_REGIONS; i++) { + const PCIIORegion *r = &d->io_regions[i]; + + if (!r->size) { + continue; + } + if ((type & PCI_BASE_ADDRESS_SPACE_IO) + != (r->type & PCI_BASE_ADDRESS_SPACE_IO)) { + continue; + } + + if (ranges_overlap(arg->addr, arg->size, r->addr, r->size)) { + XEN_PT_WARN(&s->dev, + "Overlapped to device [%02x:%02x.%d] Region: %i" + " (addr: %#"FMT_PCIBUS", len: %#"FMT_PCIBUS")\n", + pci_bus_num(bus), PCI_SLOT(d->devfn), + PCI_FUNC(d->devfn), i, r->addr, r->size); + arg->rc = true; + } + } +} + +static void xen_pt_region_update(XenPCIPassthroughState *s, + MemoryRegionSection *sec, bool adding) +{ + PCIDevice *d = &s->dev; + MemoryRegion *mr = sec->mr; + int bar = -1; + int rc; + int op = adding ? DPCI_ADD_MAPPING : DPCI_REMOVE_MAPPING; + struct CheckBarArgs args = { + .s = s, + .addr = sec->offset_within_address_space, + .size = sec->size, + .rc = false, + }; + + bar = xen_pt_bar_from_region(s, mr); + if (bar == -1 && (!s->msix || &s->msix->mmio != mr)) { + return; + } + + if (s->msix && &s->msix->mmio == mr) { + if (adding) { + s->msix->mmio_base_addr = sec->offset_within_address_space; + rc = xen_pt_msix_update_remap(s, s->msix->bar_index); + } + return; + } + + args.type = d->io_regions[bar].type; + pci_for_each_device(d->bus, pci_bus_num(d->bus), + xen_pt_check_bar_overlap, &args); + if (args.rc) { + XEN_PT_WARN(d, "Region: %d (addr: %#"FMT_PCIBUS + ", len: %#"FMT_PCIBUS") is overlapped.\n", + bar, sec->offset_within_address_space, sec->size); + } + + if (d->io_regions[bar].type & PCI_BASE_ADDRESS_SPACE_IO) { + uint32_t guest_port = sec->offset_within_address_space; + uint32_t machine_port = s->bases[bar].access.pio_base; + uint32_t size = sec->size; + rc = xc_domain_ioport_mapping(xen_xc, xen_domid, + guest_port, machine_port, size, + op); + if (rc) { + XEN_PT_ERR(d, "%s ioport mapping failed! (rc: %i)\n", + adding ? "create new" : "remove old", rc); + } + } else { + pcibus_t guest_addr = sec->offset_within_address_space; + pcibus_t machine_addr = s->bases[bar].access.maddr + + sec->offset_within_region; + pcibus_t size = sec->size; + rc = xc_domain_memory_mapping(xen_xc, xen_domid, + XEN_PFN(guest_addr + XC_PAGE_SIZE - 1), + XEN_PFN(machine_addr + XC_PAGE_SIZE - 1), + XEN_PFN(size + XC_PAGE_SIZE - 1), + op); + if (rc) { + XEN_PT_ERR(d, "%s mem mapping failed! (rc: %i)\n", + adding ? "create new" : "remove old", rc); + } + } +} + +static void xen_pt_begin(MemoryListener *l) +{ +} + +static void xen_pt_commit(MemoryListener *l) +{ +} + +static void xen_pt_region_add(MemoryListener *l, MemoryRegionSection *sec) +{ + XenPCIPassthroughState *s = container_of(l, XenPCIPassthroughState, + memory_listener); + + xen_pt_region_update(s, sec, true); +} + +static void xen_pt_region_del(MemoryListener *l, MemoryRegionSection *sec) +{ + XenPCIPassthroughState *s = container_of(l, XenPCIPassthroughState, + memory_listener); + + xen_pt_region_update(s, sec, false); +} + +static void xen_pt_region_nop(MemoryListener *l, MemoryRegionSection *s) +{ +} + +static void xen_pt_log_fns(MemoryListener *l, MemoryRegionSection *s) +{ +} + +static void xen_pt_log_global_fns(MemoryListener *l) +{ +} + +static void xen_pt_eventfd_fns(MemoryListener *l, MemoryRegionSection *s, + bool match_data, uint64_t data, int fd) +{ +} + +static const MemoryListener xen_pt_memory_listener = { + .begin = xen_pt_begin, + .commit = xen_pt_commit, + .region_add = xen_pt_region_add, + .region_nop = xen_pt_region_nop, + .region_del = xen_pt_region_del, + .log_start = xen_pt_log_fns, + .log_stop = xen_pt_log_fns, + .log_sync = xen_pt_log_fns, + .log_global_start = xen_pt_log_global_fns, + .log_global_stop = xen_pt_log_global_fns, + .eventfd_add = xen_pt_eventfd_fns, + .eventfd_del = xen_pt_eventfd_fns, + .priority = 10, +}; + +/* init */ + +static int xen_pt_initfn(PCIDevice *d) +{ + XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d); + int rc = 0; + uint8_t machine_irq = 0; + int pirq = XEN_PT_UNASSIGNED_PIRQ; + + /* register real device */ + XEN_PT_LOG(d, "Assigning real physical device %02x:%02x.%d" + " to devfn %#x\n", + s->hostaddr.bus, s->hostaddr.slot, s->hostaddr.function, + s->dev.devfn); + + rc = xen_host_pci_device_get(&s->real_device, + s->hostaddr.domain, s->hostaddr.bus, + s->hostaddr.slot, s->hostaddr.function); + if (rc) { + XEN_PT_ERR(d, "Failed to \"open\" the real pci device. rc: %i\n", rc); + return -1; + } + + s->is_virtfn = s->real_device.is_virtfn; + if (s->is_virtfn) { + XEN_PT_LOG(d, "%04x:%02x:%02x.%d is a SR-IOV Virtual Function\n", + s->real_device.domain, bus, slot, func); + } + + /* Initialize virtualized PCI configuration (Extended 256 Bytes) */ + if (xen_host_pci_get_block(&s->real_device, 0, d->config, + PCI_CONFIG_SPACE_SIZE) == -1) { + xen_host_pci_device_put(&s->real_device); + return -1; + } + + s->memory_listener = xen_pt_memory_listener; + + /* Handle real device's MMIO/PIO BARs */ + xen_pt_register_regions(s); + + /* reinitialize each config register to be emulated */ + if (xen_pt_config_init(s)) { + XEN_PT_ERR(d, "PCI Config space initialisation failed.\n"); + xen_host_pci_device_put(&s->real_device); + return -1; + } + + /* Bind interrupt */ + if (!s->dev.config[PCI_INTERRUPT_PIN]) { + XEN_PT_LOG(d, "no pin interrupt\n"); + goto out; + } + + machine_irq = s->real_device.irq; + rc = xc_physdev_map_pirq(xen_xc, xen_domid, machine_irq, &pirq); + + if (rc < 0) { + XEN_PT_ERR(d, "Mapping machine irq %u to pirq %i failed, (rc: %d)\n", + machine_irq, pirq, rc); + + /* Disable PCI intx assertion (turn on bit10 of devctl) */ + xen_host_pci_set_word(&s->real_device, + PCI_COMMAND, + pci_get_word(s->dev.config + PCI_COMMAND) + | PCI_COMMAND_INTX_DISABLE); + machine_irq = 0; + s->machine_irq = 0; + } else { + machine_irq = pirq; + s->machine_irq = pirq; + xen_pt_mapped_machine_irq[machine_irq]++; + } + + /* bind machine_irq to device */ + if (machine_irq != 0) { + uint8_t e_intx = xen_pt_pci_intx(s); + + rc = xc_domain_bind_pt_pci_irq(xen_xc, xen_domid, machine_irq, + pci_bus_num(d->bus), + PCI_SLOT(d->devfn), + e_intx); + if (rc < 0) { + XEN_PT_ERR(d, "Binding of interrupt %i failed! (rc: %d)\n", + e_intx, rc); + + /* Disable PCI intx assertion (turn on bit10 of devctl) */ + xen_host_pci_set_word(&s->real_device, PCI_COMMAND, + *(uint16_t *)(&s->dev.config[PCI_COMMAND]) + | PCI_COMMAND_INTX_DISABLE); + xen_pt_mapped_machine_irq[machine_irq]--; + + if (xen_pt_mapped_machine_irq[machine_irq] == 0) { + if (xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq)) { + XEN_PT_ERR(d, "Unmapping of machine interrupt %i failed!" + " (rc: %d)\n", machine_irq, rc); + } + } + s->machine_irq = 0; + } + } + +out: + memory_listener_register(&s->memory_listener, NULL); + XEN_PT_LOG(d, "Real physical device %02x:%02x.%d registered successfuly!\n", + bus, slot, func); + + return 0; +} + +static int xen_pt_unregister_device(PCIDevice *d) +{ + XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d); + uint8_t machine_irq = s->machine_irq; + uint8_t intx = xen_pt_pci_intx(s); + int rc; + + if (machine_irq) { + rc = xc_domain_unbind_pt_irq(xen_xc, xen_domid, machine_irq, + PT_IRQ_TYPE_PCI, + pci_bus_num(d->bus), + PCI_SLOT(s->dev.devfn), + intx, + 0 /* isa_irq */); + if (rc < 0) { + XEN_PT_ERR(d, "unbinding of interrupt INT%c failed." + " (machine irq: %i, rc: %d)" + " But bravely continuing on..\n", + 'a' + intx, machine_irq, rc); + } + } + + if (s->msi) { + xen_pt_msi_disable(s); + } + if (s->msix) { + xen_pt_msix_disable(s); + } + + if (machine_irq) { + xen_pt_mapped_machine_irq[machine_irq]--; + + if (xen_pt_mapped_machine_irq[machine_irq] == 0) { + rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq); + + if (rc < 0) { + XEN_PT_ERR(d, "unmapping of interrupt %i failed. (rc: %d)" + " But bravely continuing on..\n", + machine_irq, rc); + } + } + } + + /* delete all emulated config registers */ + xen_pt_config_delete(s); + + xen_pt_unregister_regions(s); + memory_listener_unregister(&s->memory_listener); + + xen_host_pci_device_put(&s->real_device); + + return 0; +} + +static Property xen_pci_passthrough_properties[] = { + DEFINE_PROP_PCI_HOST_DEVADDR("hostaddr", XenPCIPassthroughState, hostaddr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void xen_pci_passthrough_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = xen_pt_initfn; + k->exit = xen_pt_unregister_device; + k->config_read = xen_pt_pci_read_config; + k->config_write = xen_pt_pci_write_config; + dc->desc = "Assign an host PCI device with Xen"; + dc->props = xen_pci_passthrough_properties; +}; + +static TypeInfo xen_pci_passthrough_info = { + .name = "xen-pci-passthrough", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(XenPCIPassthroughState), + .class_init = xen_pci_passthrough_class_init, +}; + +static void xen_pci_passthrough_register_types(void) +{ + type_register_static(&xen_pci_passthrough_info); +} + +type_init(xen_pci_passthrough_register_types) diff --git a/hw/xen_pt.h b/hw/xen_pt.h new file mode 100644 index 0000000000..41904ece93 --- /dev/null +++ b/hw/xen_pt.h @@ -0,0 +1,301 @@ +#ifndef XEN_PT_H +#define XEN_PT_H + +#include "qemu-common.h" +#include "xen_common.h" +#include "pci.h" +#include "xen-host-pci-device.h" + +void xen_pt_log(const PCIDevice *d, const char *f, ...) GCC_FMT_ATTR(2, 3); + +#define XEN_PT_ERR(d, _f, _a...) xen_pt_log(d, "%s: Error: "_f, __func__, ##_a) + +#ifdef XEN_PT_LOGGING_ENABLED +# define XEN_PT_LOG(d, _f, _a...) xen_pt_log(d, "%s: " _f, __func__, ##_a) +# define XEN_PT_WARN(d, _f, _a...) \ + xen_pt_log(d, "%s: Warning: "_f, __func__, ##_a) +#else +# define XEN_PT_LOG(d, _f, _a...) +# define XEN_PT_WARN(d, _f, _a...) +#endif + +#ifdef XEN_PT_DEBUG_PCI_CONFIG_ACCESS +# define XEN_PT_LOG_CONFIG(d, addr, val, len) \ + xen_pt_log(d, "%s: address=0x%04x val=0x%08x len=%d\n", \ + __func__, addr, val, len) +#else +# define XEN_PT_LOG_CONFIG(d, addr, val, len) +#endif + + +/* Helper */ +#define XEN_PFN(x) ((x) >> XC_PAGE_SHIFT) + +typedef struct XenPTRegInfo XenPTRegInfo; +typedef struct XenPTReg XenPTReg; + +typedef struct XenPCIPassthroughState XenPCIPassthroughState; + +/* function type for config reg */ +typedef int (*xen_pt_conf_reg_init) + (XenPCIPassthroughState *, XenPTRegInfo *, uint32_t real_offset, + uint32_t *data); +typedef int (*xen_pt_conf_dword_write) + (XenPCIPassthroughState *, XenPTReg *cfg_entry, + uint32_t *val, uint32_t dev_value, uint32_t valid_mask); +typedef int (*xen_pt_conf_word_write) + (XenPCIPassthroughState *, XenPTReg *cfg_entry, + uint16_t *val, uint16_t dev_value, uint16_t valid_mask); +typedef int (*xen_pt_conf_byte_write) + (XenPCIPassthroughState *, XenPTReg *cfg_entry, + uint8_t *val, uint8_t dev_value, uint8_t valid_mask); +typedef int (*xen_pt_conf_dword_read) + (XenPCIPassthroughState *, XenPTReg *cfg_entry, + uint32_t *val, uint32_t valid_mask); +typedef int (*xen_pt_conf_word_read) + (XenPCIPassthroughState *, XenPTReg *cfg_entry, + uint16_t *val, uint16_t valid_mask); +typedef int (*xen_pt_conf_byte_read) + (XenPCIPassthroughState *, XenPTReg *cfg_entry, + uint8_t *val, uint8_t valid_mask); + +#define XEN_PT_BAR_ALLF 0xFFFFFFFF +#define XEN_PT_BAR_UNMAPPED (-1) + +#define PCI_CAP_MAX 48 + + +typedef enum { + XEN_PT_GRP_TYPE_HARDWIRED = 0, /* 0 Hardwired reg group */ + XEN_PT_GRP_TYPE_EMU, /* emul reg group */ +} XenPTRegisterGroupType; + +typedef enum { + XEN_PT_BAR_FLAG_MEM = 0, /* Memory type BAR */ + XEN_PT_BAR_FLAG_IO, /* I/O type BAR */ + XEN_PT_BAR_FLAG_UPPER, /* upper 64bit BAR */ + XEN_PT_BAR_FLAG_UNUSED, /* unused BAR */ +} XenPTBarFlag; + + +typedef struct XenPTRegion { + /* BAR flag */ + XenPTBarFlag bar_flag; + /* Translation of the emulated address */ + union { + uint64_t maddr; + uint64_t pio_base; + uint64_t u; + } access; +} XenPTRegion; + +/* XenPTRegInfo declaration + * - only for emulated register (either a part or whole bit). + * - for passthrough register that need special behavior (like interacting with + * other component), set emu_mask to all 0 and specify r/w func properly. + * - do NOT use ALL F for init_val, otherwise the tbl will not be registered. + */ + +/* emulated register infomation */ +struct XenPTRegInfo { + uint32_t offset; + uint32_t size; + uint32_t init_val; + /* reg read only field mask (ON:RO/ROS, OFF:other) */ + uint32_t ro_mask; + /* reg emulate field mask (ON:emu, OFF:passthrough) */ + uint32_t emu_mask; + /* no write back allowed */ + uint32_t no_wb; + xen_pt_conf_reg_init init; + /* read/write function pointer + * for double_word/word/byte size */ + union { + struct { + xen_pt_conf_dword_write write; + xen_pt_conf_dword_read read; + } dw; + struct { + xen_pt_conf_word_write write; + xen_pt_conf_word_read read; + } w; + struct { + xen_pt_conf_byte_write write; + xen_pt_conf_byte_read read; + } b; + } u; +}; + +/* emulated register management */ +struct XenPTReg { + QLIST_ENTRY(XenPTReg) entries; + XenPTRegInfo *reg; + uint32_t data; /* emulated value */ +}; + +typedef struct XenPTRegGroupInfo XenPTRegGroupInfo; + +/* emul reg group size initialize method */ +typedef int (*xen_pt_reg_size_init_fn) + (XenPCIPassthroughState *, const XenPTRegGroupInfo *, + uint32_t base_offset, uint8_t *size); + +/* emulated register group infomation */ +struct XenPTRegGroupInfo { + uint8_t grp_id; + XenPTRegisterGroupType grp_type; + uint8_t grp_size; + xen_pt_reg_size_init_fn size_init; + XenPTRegInfo *emu_regs; +}; + +/* emul register group management table */ +typedef struct XenPTRegGroup { + QLIST_ENTRY(XenPTRegGroup) entries; + const XenPTRegGroupInfo *reg_grp; + uint32_t base_offset; + uint8_t size; + QLIST_HEAD(, XenPTReg) reg_tbl_list; +} XenPTRegGroup; + + +#define XEN_PT_UNASSIGNED_PIRQ (-1) +typedef struct XenPTMSI { + uint16_t flags; + uint32_t addr_lo; /* guest message address */ + uint32_t addr_hi; /* guest message upper address */ + uint16_t data; /* guest message data */ + uint32_t ctrl_offset; /* saved control offset */ + int pirq; /* guest pirq corresponding */ + bool initialized; /* when guest MSI is initialized */ + bool mapped; /* when pirq is mapped */ +} XenPTMSI; + +typedef struct XenPTMSIXEntry { + int pirq; + uint64_t addr; + uint32_t data; + uint32_t vector_ctrl; + bool updated; /* indicate whether MSI ADDR or DATA is updated */ +} XenPTMSIXEntry; +typedef struct XenPTMSIX { + uint32_t ctrl_offset; + bool enabled; + int total_entries; + int bar_index; + uint64_t table_base; + uint32_t table_offset_adjust; /* page align mmap */ + uint64_t mmio_base_addr; + MemoryRegion mmio; + void *phys_iomem_base; + XenPTMSIXEntry msix_entry[0]; +} XenPTMSIX; + +struct XenPCIPassthroughState { + PCIDevice dev; + + PCIHostDeviceAddress hostaddr; + bool is_virtfn; + XenHostPCIDevice real_device; + XenPTRegion bases[PCI_NUM_REGIONS]; /* Access regions */ + QLIST_HEAD(, XenPTRegGroup) reg_grps; + + uint32_t machine_irq; + + XenPTMSI *msi; + XenPTMSIX *msix; + + MemoryRegion bar[PCI_NUM_REGIONS - 1]; + MemoryRegion rom; + + MemoryListener memory_listener; +}; + +int xen_pt_config_init(XenPCIPassthroughState *s); +void xen_pt_config_delete(XenPCIPassthroughState *s); +XenPTRegGroup *xen_pt_find_reg_grp(XenPCIPassthroughState *s, uint32_t address); +XenPTReg *xen_pt_find_reg(XenPTRegGroup *reg_grp, uint32_t address); +int xen_pt_bar_offset_to_index(uint32_t offset); + +static inline pcibus_t xen_pt_get_emul_size(XenPTBarFlag flag, pcibus_t r_size) +{ + /* align resource size (memory type only) */ + if (flag == XEN_PT_BAR_FLAG_MEM) { + return (r_size + XC_PAGE_SIZE - 1) & XC_PAGE_MASK; + } else { + return r_size; + } +} + +/* INTx */ +/* The PCI Local Bus Specification, Rev. 3.0, + * Section 6.2.4 Miscellaneous Registers, pp 223 + * outlines 5 valid values for the interrupt pin (intx). + * 0: For devices (or device functions) that don't use an interrupt in + * 1: INTA# + * 2: INTB# + * 3: INTC# + * 4: INTD# + * + * Xen uses the following 4 values for intx + * 0: INTA# + * 1: INTB# + * 2: INTC# + * 3: INTD# + * + * Observing that these list of values are not the same, xen_pt_pci_read_intx() + * uses the following mapping from hw to xen values. + * This seems to reflect the current usage within Xen. + * + * PCI hardware | Xen | Notes + * ----------------+-----+---------------------------------------------------- + * 0 | 0 | No interrupt + * 1 | 0 | INTA# + * 2 | 1 | INTB# + * 3 | 2 | INTC# + * 4 | 3 | INTD# + * any other value | 0 | This should never happen, log error message + */ + +static inline uint8_t xen_pt_pci_read_intx(XenPCIPassthroughState *s) +{ + uint8_t v = 0; + xen_host_pci_get_byte(&s->real_device, PCI_INTERRUPT_PIN, &v); + return v; +} + +static inline uint8_t xen_pt_pci_intx(XenPCIPassthroughState *s) +{ + uint8_t r_val = xen_pt_pci_read_intx(s); + + XEN_PT_LOG(&s->dev, "intx=%i\n", r_val); + if (r_val < 1 || r_val > 4) { + XEN_PT_LOG(&s->dev, "Interrupt pin read from hardware is out of range:" + " value=%i, acceptable range is 1 - 4\n", r_val); + r_val = 0; + } else { + r_val -= 1; + } + + return r_val; +} + +/* MSI/MSI-X */ +int xen_pt_msi_set_enable(XenPCIPassthroughState *s, bool en); +int xen_pt_msi_setup(XenPCIPassthroughState *s); +int xen_pt_msi_update(XenPCIPassthroughState *d); +void xen_pt_msi_disable(XenPCIPassthroughState *s); + +int xen_pt_msix_init(XenPCIPassthroughState *s, uint32_t base); +void xen_pt_msix_delete(XenPCIPassthroughState *s); +int xen_pt_msix_update(XenPCIPassthroughState *s); +int xen_pt_msix_update_remap(XenPCIPassthroughState *s, int bar_index); +void xen_pt_msix_disable(XenPCIPassthroughState *s); + +static inline bool xen_pt_has_msix_mapping(XenPCIPassthroughState *s, int bar) +{ + return s->msix && s->msix->bar_index == bar; +} + + +#endif /* !XEN_PT_H */ diff --git a/hw/xen_pt_config_init.c b/hw/xen_pt_config_init.c new file mode 100644 index 0000000000..00eb3d997d --- /dev/null +++ b/hw/xen_pt_config_init.c @@ -0,0 +1,1869 @@ +/* + * Copyright (c) 2007, Neocleus Corporation. + * Copyright (c) 2007, Intel Corporation. + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Alex Novik <alex@neocleus.com> + * Allen Kay <allen.m.kay@intel.com> + * Guy Zana <guy@neocleus.com> + * + * This file implements direct PCI assignment to a HVM guest + */ + +#include "qemu-timer.h" +#include "xen_backend.h" +#include "xen_pt.h" + +#define XEN_PT_MERGE_VALUE(value, data, val_mask) \ + (((value) & (val_mask)) | ((data) & ~(val_mask))) + +#define XEN_PT_INVALID_REG 0xFFFFFFFF /* invalid register value */ + +/* prototype */ + +static int xen_pt_ptr_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, + uint32_t real_offset, uint32_t *data); + + +/* helper */ + +/* A return value of 1 means the capability should NOT be exposed to guest. */ +static int xen_pt_hide_dev_cap(const XenHostPCIDevice *d, uint8_t grp_id) +{ + switch (grp_id) { + case PCI_CAP_ID_EXP: + /* The PCI Express Capability Structure of the VF of Intel 82599 10GbE + * Controller looks trivial, e.g., the PCI Express Capabilities + * Register is 0. We should not try to expose it to guest. + * + * The datasheet is available at + * http://download.intel.com/design/network/datashts/82599_datasheet.pdf + * + * See 'Table 9.7. VF PCIe Configuration Space' of the datasheet, the + * PCI Express Capability Structure of the VF of Intel 82599 10GbE + * Controller looks trivial, e.g., the PCI Express Capabilities + * Register is 0, so the Capability Version is 0 and + * xen_pt_pcie_size_init() would fail. + */ + if (d->vendor_id == PCI_VENDOR_ID_INTEL && + d->device_id == PCI_DEVICE_ID_INTEL_82599_SFP_VF) { + return 1; + } + break; + } + return 0; +} + +/* find emulate register group entry */ +XenPTRegGroup *xen_pt_find_reg_grp(XenPCIPassthroughState *s, uint32_t address) +{ + XenPTRegGroup *entry = NULL; + + /* find register group entry */ + QLIST_FOREACH(entry, &s->reg_grps, entries) { + /* check address */ + if ((entry->base_offset <= address) + && ((entry->base_offset + entry->size) > address)) { + return entry; + } + } + + /* group entry not found */ + return NULL; +} + +/* find emulate register entry */ +XenPTReg *xen_pt_find_reg(XenPTRegGroup *reg_grp, uint32_t address) +{ + XenPTReg *reg_entry = NULL; + XenPTRegInfo *reg = NULL; + uint32_t real_offset = 0; + + /* find register entry */ + QLIST_FOREACH(reg_entry, ®_grp->reg_tbl_list, entries) { + reg = reg_entry->reg; + real_offset = reg_grp->base_offset + reg->offset; + /* check address */ + if ((real_offset <= address) + && ((real_offset + reg->size) > address)) { + return reg_entry; + } + } + + return NULL; +} + + +/**************** + * general register functions + */ + +/* register initialization function */ + +static int xen_pt_common_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + *data = reg->init_val; + return 0; +} + +/* Read register functions */ + +static int xen_pt_byte_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint8_t *value, uint8_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint8_t valid_emu_mask = 0; + + /* emulate byte register */ + valid_emu_mask = reg->emu_mask & valid_mask; + *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); + + return 0; +} +static int xen_pt_word_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint16_t *value, uint16_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint16_t valid_emu_mask = 0; + + /* emulate word register */ + valid_emu_mask = reg->emu_mask & valid_mask; + *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); + + return 0; +} +static int xen_pt_long_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint32_t *value, uint32_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint32_t valid_emu_mask = 0; + + /* emulate long register */ + valid_emu_mask = reg->emu_mask & valid_mask; + *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); + + return 0; +} + +/* Write register functions */ + +static int xen_pt_byte_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint8_t *val, uint8_t dev_value, + uint8_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint8_t writable_mask = 0; + uint8_t throughable_mask = 0; + + /* modify emulate register */ + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ + throughable_mask = ~reg->emu_mask & valid_mask; + *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + + return 0; +} +static int xen_pt_word_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint16_t *val, uint16_t dev_value, + uint16_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint16_t writable_mask = 0; + uint16_t throughable_mask = 0; + + /* modify emulate register */ + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ + throughable_mask = ~reg->emu_mask & valid_mask; + *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + + return 0; +} +static int xen_pt_long_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint32_t *val, uint32_t dev_value, + uint32_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint32_t writable_mask = 0; + uint32_t throughable_mask = 0; + + /* modify emulate register */ + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ + throughable_mask = ~reg->emu_mask & valid_mask; + *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + + return 0; +} + + +/* XenPTRegInfo declaration + * - only for emulated register (either a part or whole bit). + * - for passthrough register that need special behavior (like interacting with + * other component), set emu_mask to all 0 and specify r/w func properly. + * - do NOT use ALL F for init_val, otherwise the tbl will not be registered. + */ + +/******************** + * Header Type0 + */ + +static int xen_pt_vendor_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + *data = s->real_device.vendor_id; + return 0; +} +static int xen_pt_device_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + *data = s->real_device.device_id; + return 0; +} +static int xen_pt_status_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + XenPTRegGroup *reg_grp_entry = NULL; + XenPTReg *reg_entry = NULL; + uint32_t reg_field = 0; + + /* find Header register group */ + reg_grp_entry = xen_pt_find_reg_grp(s, PCI_CAPABILITY_LIST); + if (reg_grp_entry) { + /* find Capabilities Pointer register */ + reg_entry = xen_pt_find_reg(reg_grp_entry, PCI_CAPABILITY_LIST); + if (reg_entry) { + /* check Capabilities Pointer register */ + if (reg_entry->data) { + reg_field |= PCI_STATUS_CAP_LIST; + } else { + reg_field &= ~PCI_STATUS_CAP_LIST; + } + } else { + xen_shutdown_fatal_error("Internal error: Couldn't find XenPTReg*" + " for Capabilities Pointer register." + " (%s)\n", __func__); + return -1; + } + } else { + xen_shutdown_fatal_error("Internal error: Couldn't find XenPTRegGroup" + " for Header. (%s)\n", __func__); + return -1; + } + + *data = reg_field; + return 0; +} +static int xen_pt_header_type_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + /* read PCI_HEADER_TYPE */ + *data = reg->init_val | 0x80; + return 0; +} + +/* initialize Interrupt Pin register */ +static int xen_pt_irqpin_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + *data = xen_pt_pci_read_intx(s); + return 0; +} + +/* Command register */ +static int xen_pt_cmd_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint16_t *value, uint16_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint16_t valid_emu_mask = 0; + uint16_t emu_mask = reg->emu_mask; + + if (s->is_virtfn) { + emu_mask |= PCI_COMMAND_MEMORY; + } + + /* emulate word register */ + valid_emu_mask = emu_mask & valid_mask; + *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); + + return 0; +} +static int xen_pt_cmd_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint16_t *val, uint16_t dev_value, + uint16_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint16_t writable_mask = 0; + uint16_t throughable_mask = 0; + uint16_t emu_mask = reg->emu_mask; + + if (s->is_virtfn) { + emu_mask |= PCI_COMMAND_MEMORY; + } + + /* modify emulate register */ + writable_mask = ~reg->ro_mask & valid_mask; + cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ + throughable_mask = ~emu_mask & valid_mask; + + if (*val & PCI_COMMAND_INTX_DISABLE) { + throughable_mask |= PCI_COMMAND_INTX_DISABLE; + } else { + if (s->machine_irq) { + throughable_mask |= PCI_COMMAND_INTX_DISABLE; + } + } + + *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + + return 0; +} + +/* BAR */ +#define XEN_PT_BAR_MEM_RO_MASK 0x0000000F /* BAR ReadOnly mask(Memory) */ +#define XEN_PT_BAR_MEM_EMU_MASK 0xFFFFFFF0 /* BAR emul mask(Memory) */ +#define XEN_PT_BAR_IO_RO_MASK 0x00000003 /* BAR ReadOnly mask(I/O) */ +#define XEN_PT_BAR_IO_EMU_MASK 0xFFFFFFFC /* BAR emul mask(I/O) */ + +static XenPTBarFlag xen_pt_bar_reg_parse(XenPCIPassthroughState *s, + XenPTRegInfo *reg) +{ + PCIDevice *d = &s->dev; + XenPTRegion *region = NULL; + PCIIORegion *r; + int index = 0; + + /* check 64bit BAR */ + index = xen_pt_bar_offset_to_index(reg->offset); + if ((0 < index) && (index < PCI_ROM_SLOT)) { + int type = s->real_device.io_regions[index - 1].type; + + if ((type & XEN_HOST_PCI_REGION_TYPE_MEM) + && (type & XEN_HOST_PCI_REGION_TYPE_MEM_64)) { + region = &s->bases[index - 1]; + if (region->bar_flag != XEN_PT_BAR_FLAG_UPPER) { + return XEN_PT_BAR_FLAG_UPPER; + } + } + } + + /* check unused BAR */ + r = &d->io_regions[index]; + if (r->size == 0) { + return XEN_PT_BAR_FLAG_UNUSED; + } + + /* for ExpROM BAR */ + if (index == PCI_ROM_SLOT) { + return XEN_PT_BAR_FLAG_MEM; + } + + /* check BAR I/O indicator */ + if (s->real_device.io_regions[index].type & XEN_HOST_PCI_REGION_TYPE_IO) { + return XEN_PT_BAR_FLAG_IO; + } else { + return XEN_PT_BAR_FLAG_MEM; + } +} + +static inline uint32_t base_address_with_flags(XenHostPCIIORegion *hr) +{ + if (hr->type & XEN_HOST_PCI_REGION_TYPE_IO) { + return hr->base_addr | (hr->bus_flags & ~PCI_BASE_ADDRESS_IO_MASK); + } else { + return hr->base_addr | (hr->bus_flags & ~PCI_BASE_ADDRESS_MEM_MASK); + } +} + +static int xen_pt_bar_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, + uint32_t real_offset, uint32_t *data) +{ + uint32_t reg_field = 0; + int index; + + index = xen_pt_bar_offset_to_index(reg->offset); + if (index < 0 || index >= PCI_NUM_REGIONS) { + XEN_PT_ERR(&s->dev, "Internal error: Invalid BAR index [%d].\n", index); + return -1; + } + + /* set BAR flag */ + s->bases[index].bar_flag = xen_pt_bar_reg_parse(s, reg); + if (s->bases[index].bar_flag == XEN_PT_BAR_FLAG_UNUSED) { + reg_field = XEN_PT_INVALID_REG; + } + + *data = reg_field; + return 0; +} +static int xen_pt_bar_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint32_t *value, uint32_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint32_t valid_emu_mask = 0; + uint32_t bar_emu_mask = 0; + int index; + + /* get BAR index */ + index = xen_pt_bar_offset_to_index(reg->offset); + if (index < 0 || index >= PCI_NUM_REGIONS) { + XEN_PT_ERR(&s->dev, "Internal error: Invalid BAR index [%d].\n", index); + return -1; + } + + /* use fixed-up value from kernel sysfs */ + *value = base_address_with_flags(&s->real_device.io_regions[index]); + + /* set emulate mask depend on BAR flag */ + switch (s->bases[index].bar_flag) { + case XEN_PT_BAR_FLAG_MEM: + bar_emu_mask = XEN_PT_BAR_MEM_EMU_MASK; + break; + case XEN_PT_BAR_FLAG_IO: + bar_emu_mask = XEN_PT_BAR_IO_EMU_MASK; + break; + case XEN_PT_BAR_FLAG_UPPER: + bar_emu_mask = XEN_PT_BAR_ALLF; + break; + default: + break; + } + + /* emulate BAR */ + valid_emu_mask = bar_emu_mask & valid_mask; + *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); + + return 0; +} +static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint32_t *val, uint32_t dev_value, + uint32_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + XenPTRegion *base = NULL; + PCIDevice *d = &s->dev; + const PCIIORegion *r; + uint32_t writable_mask = 0; + uint32_t throughable_mask = 0; + uint32_t bar_emu_mask = 0; + uint32_t bar_ro_mask = 0; + uint32_t r_size = 0; + int index = 0; + + index = xen_pt_bar_offset_to_index(reg->offset); + if (index < 0 || index >= PCI_NUM_REGIONS) { + XEN_PT_ERR(d, "Internal error: Invalid BAR index [%d].\n", index); + return -1; + } + + r = &d->io_regions[index]; + base = &s->bases[index]; + r_size = xen_pt_get_emul_size(base->bar_flag, r->size); + + /* set emulate mask and read-only mask values depend on the BAR flag */ + switch (s->bases[index].bar_flag) { + case XEN_PT_BAR_FLAG_MEM: + bar_emu_mask = XEN_PT_BAR_MEM_EMU_MASK; + bar_ro_mask = XEN_PT_BAR_MEM_RO_MASK | (r_size - 1); + break; + case XEN_PT_BAR_FLAG_IO: + bar_emu_mask = XEN_PT_BAR_IO_EMU_MASK; + bar_ro_mask = XEN_PT_BAR_IO_RO_MASK | (r_size - 1); + break; + case XEN_PT_BAR_FLAG_UPPER: + bar_emu_mask = XEN_PT_BAR_ALLF; + bar_ro_mask = 0; /* all upper 32bit are R/W */ + break; + default: + break; + } + + /* modify emulate register */ + writable_mask = bar_emu_mask & ~bar_ro_mask & valid_mask; + cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + + /* check whether we need to update the virtual region address or not */ + switch (s->bases[index].bar_flag) { + case XEN_PT_BAR_FLAG_MEM: + /* nothing to do */ + break; + case XEN_PT_BAR_FLAG_IO: + /* nothing to do */ + break; + case XEN_PT_BAR_FLAG_UPPER: + if (cfg_entry->data) { + if (cfg_entry->data != (XEN_PT_BAR_ALLF & ~bar_ro_mask)) { + XEN_PT_WARN(d, "Guest attempt to set high MMIO Base Address. " + "Ignore mapping. " + "(offset: 0x%02x, high address: 0x%08x)\n", + reg->offset, cfg_entry->data); + } + } + break; + default: + break; + } + + /* create value for writing to I/O device register */ + throughable_mask = ~bar_emu_mask & valid_mask; + *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + + return 0; +} + +/* write Exp ROM BAR */ +static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s, + XenPTReg *cfg_entry, uint32_t *val, + uint32_t dev_value, uint32_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + XenPTRegion *base = NULL; + PCIDevice *d = (PCIDevice *)&s->dev; + uint32_t writable_mask = 0; + uint32_t throughable_mask = 0; + pcibus_t r_size = 0; + uint32_t bar_emu_mask = 0; + uint32_t bar_ro_mask = 0; + + r_size = d->io_regions[PCI_ROM_SLOT].size; + base = &s->bases[PCI_ROM_SLOT]; + /* align memory type resource size */ + r_size = xen_pt_get_emul_size(base->bar_flag, r_size); + + /* set emulate mask and read-only mask */ + bar_emu_mask = reg->emu_mask; + bar_ro_mask = (reg->ro_mask | (r_size - 1)) & ~PCI_ROM_ADDRESS_ENABLE; + + /* modify emulate register */ + writable_mask = ~bar_ro_mask & valid_mask; + cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ + throughable_mask = ~bar_emu_mask & valid_mask; + *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + + return 0; +} + +/* Header Type0 reg static infomation table */ +static XenPTRegInfo xen_pt_emu_reg_header0[] = { + /* Vendor ID reg */ + { + .offset = PCI_VENDOR_ID, + .size = 2, + .init_val = 0x0000, + .ro_mask = 0xFFFF, + .emu_mask = 0xFFFF, + .init = xen_pt_vendor_reg_init, + .u.w.read = xen_pt_word_reg_read, + .u.w.write = xen_pt_word_reg_write, + }, + /* Device ID reg */ + { + .offset = PCI_DEVICE_ID, + .size = 2, + .init_val = 0x0000, + .ro_mask = 0xFFFF, + .emu_mask = 0xFFFF, + .init = xen_pt_device_reg_init, + .u.w.read = xen_pt_word_reg_read, + .u.w.write = xen_pt_word_reg_write, + }, + /* Command reg */ + { + .offset = PCI_COMMAND, + .size = 2, + .init_val = 0x0000, + .ro_mask = 0xF880, + .emu_mask = 0x0740, + .init = xen_pt_common_reg_init, + .u.w.read = xen_pt_cmd_reg_read, + .u.w.write = xen_pt_cmd_reg_write, + }, + /* Capabilities Pointer reg */ + { + .offset = PCI_CAPABILITY_LIST, + .size = 1, + .init_val = 0x00, + .ro_mask = 0xFF, + .emu_mask = 0xFF, + .init = xen_pt_ptr_reg_init, + .u.b.read = xen_pt_byte_reg_read, + .u.b.write = xen_pt_byte_reg_write, + }, + /* Status reg */ + /* use emulated Cap Ptr value to initialize, + * so need to be declared after Cap Ptr reg + */ + { + .offset = PCI_STATUS, + .size = 2, + .init_val = 0x0000, + .ro_mask = 0x06FF, + .emu_mask = 0x0010, + .init = xen_pt_status_reg_init, + .u.w.read = xen_pt_word_reg_read, + .u.w.write = xen_pt_word_reg_write, + }, + /* Cache Line Size reg */ + { + .offset = PCI_CACHE_LINE_SIZE, + .size = 1, + .init_val = 0x00, + .ro_mask = 0x00, + .emu_mask = 0xFF, + .init = xen_pt_common_reg_init, + .u.b.read = xen_pt_byte_reg_read, + .u.b.write = xen_pt_byte_reg_write, + }, + /* Latency Timer reg */ + { + .offset = PCI_LATENCY_TIMER, + .size = 1, + .init_val = 0x00, + .ro_mask = 0x00, + .emu_mask = 0xFF, + .init = xen_pt_common_reg_init, + .u.b.read = xen_pt_byte_reg_read, + .u.b.write = xen_pt_byte_reg_write, + }, + /* Header Type reg */ + { + .offset = PCI_HEADER_TYPE, + .size = 1, + .init_val = 0x00, + .ro_mask = 0xFF, + .emu_mask = 0x00, + .init = xen_pt_header_type_reg_init, + .u.b.read = xen_pt_byte_reg_read, + .u.b.write = xen_pt_byte_reg_write, + }, + /* Interrupt Line reg */ + { + .offset = PCI_INTERRUPT_LINE, + .size = 1, + .init_val = 0x00, + .ro_mask = 0x00, + .emu_mask = 0xFF, + .init = xen_pt_common_reg_init, + .u.b.read = xen_pt_byte_reg_read, + .u.b.write = xen_pt_byte_reg_write, + }, + /* Interrupt Pin reg */ + { + .offset = PCI_INTERRUPT_PIN, + .size = 1, + .init_val = 0x00, + .ro_mask = 0xFF, + .emu_mask = 0xFF, + .init = xen_pt_irqpin_reg_init, + .u.b.read = xen_pt_byte_reg_read, + .u.b.write = xen_pt_byte_reg_write, + }, + /* BAR 0 reg */ + /* mask of BAR need to be decided later, depends on IO/MEM type */ + { + .offset = PCI_BASE_ADDRESS_0, + .size = 4, + .init_val = 0x00000000, + .init = xen_pt_bar_reg_init, + .u.dw.read = xen_pt_bar_reg_read, + .u.dw.write = xen_pt_bar_reg_write, + }, + /* BAR 1 reg */ + { + .offset = PCI_BASE_ADDRESS_1, + .size = 4, + .init_val = 0x00000000, + .init = xen_pt_bar_reg_init, + .u.dw.read = xen_pt_bar_reg_read, + .u.dw.write = xen_pt_bar_reg_write, + }, + /* BAR 2 reg */ + { + .offset = PCI_BASE_ADDRESS_2, + .size = 4, + .init_val = 0x00000000, + .init = xen_pt_bar_reg_init, + .u.dw.read = xen_pt_bar_reg_read, + .u.dw.write = xen_pt_bar_reg_write, + }, + /* BAR 3 reg */ + { + .offset = PCI_BASE_ADDRESS_3, + .size = 4, + .init_val = 0x00000000, + .init = xen_pt_bar_reg_init, + .u.dw.read = xen_pt_bar_reg_read, + .u.dw.write = xen_pt_bar_reg_write, + }, + /* BAR 4 reg */ + { + .offset = PCI_BASE_ADDRESS_4, + .size = 4, + .init_val = 0x00000000, + .init = xen_pt_bar_reg_init, + .u.dw.read = xen_pt_bar_reg_read, + .u.dw.write = xen_pt_bar_reg_write, + }, + /* BAR 5 reg */ + { + .offset = PCI_BASE_ADDRESS_5, + .size = 4, + .init_val = 0x00000000, + .init = xen_pt_bar_reg_init, + .u.dw.read = xen_pt_bar_reg_read, + .u.dw.write = xen_pt_bar_reg_write, + }, + /* Expansion ROM BAR reg */ + { + .offset = PCI_ROM_ADDRESS, + .size = 4, + .init_val = 0x00000000, + .ro_mask = 0x000007FE, + .emu_mask = 0xFFFFF800, + .init = xen_pt_bar_reg_init, + .u.dw.read = xen_pt_long_reg_read, + .u.dw.write = xen_pt_exp_rom_bar_reg_write, + }, + { + .size = 0, + }, +}; + + +/********************************* + * Vital Product Data Capability + */ + +/* Vital Product Data Capability Structure reg static infomation table */ +static XenPTRegInfo xen_pt_emu_reg_vpd[] = { + { + .offset = PCI_CAP_LIST_NEXT, + .size = 1, + .init_val = 0x00, + .ro_mask = 0xFF, + .emu_mask = 0xFF, + .init = xen_pt_ptr_reg_init, + .u.b.read = xen_pt_byte_reg_read, + .u.b.write = xen_pt_byte_reg_write, + }, + { + .size = 0, + }, +}; + + +/************************************** + * Vendor Specific Capability + */ + +/* Vendor Specific Capability Structure reg static infomation table */ +static XenPTRegInfo xen_pt_emu_reg_vendor[] = { + { + .offset = PCI_CAP_LIST_NEXT, + .size = 1, + .init_val = 0x00, + .ro_mask = 0xFF, + .emu_mask = 0xFF, + .init = xen_pt_ptr_reg_init, + .u.b.read = xen_pt_byte_reg_read, + .u.b.write = xen_pt_byte_reg_write, + }, + { + .size = 0, + }, +}; + + +/***************************** + * PCI Express Capability + */ + +static inline uint8_t get_capability_version(XenPCIPassthroughState *s, + uint32_t offset) +{ + uint8_t flags = pci_get_byte(s->dev.config + offset + PCI_EXP_FLAGS); + return flags & PCI_EXP_FLAGS_VERS; +} + +static inline uint8_t get_device_type(XenPCIPassthroughState *s, + uint32_t offset) +{ + uint8_t flags = pci_get_byte(s->dev.config + offset + PCI_EXP_FLAGS); + return (flags & PCI_EXP_FLAGS_TYPE) >> 4; +} + +/* initialize Link Control register */ +static int xen_pt_linkctrl_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + uint8_t cap_ver = get_capability_version(s, real_offset - reg->offset); + uint8_t dev_type = get_device_type(s, real_offset - reg->offset); + + /* no need to initialize in case of Root Complex Integrated Endpoint + * with cap_ver 1.x + */ + if ((dev_type == PCI_EXP_TYPE_RC_END) && (cap_ver == 1)) { + *data = XEN_PT_INVALID_REG; + } + + *data = reg->init_val; + return 0; +} +/* initialize Device Control 2 register */ +static int xen_pt_devctrl2_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + uint8_t cap_ver = get_capability_version(s, real_offset - reg->offset); + + /* no need to initialize in case of cap_ver 1.x */ + if (cap_ver == 1) { + *data = XEN_PT_INVALID_REG; + } + + *data = reg->init_val; + return 0; +} +/* initialize Link Control 2 register */ +static int xen_pt_linkctrl2_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + uint8_t cap_ver = get_capability_version(s, real_offset - reg->offset); + uint32_t reg_field = 0; + + /* no need to initialize in case of cap_ver 1.x */ + if (cap_ver == 1) { + reg_field = XEN_PT_INVALID_REG; + } else { + /* set Supported Link Speed */ + uint8_t lnkcap = pci_get_byte(s->dev.config + real_offset - reg->offset + + PCI_EXP_LNKCAP); + reg_field |= PCI_EXP_LNKCAP_SLS & lnkcap; + } + + *data = reg_field; + return 0; +} + +/* PCI Express Capability Structure reg static infomation table */ +static XenPTRegInfo xen_pt_emu_reg_pcie[] = { + /* Next Pointer reg */ + { + .offset = PCI_CAP_LIST_NEXT, + .size = 1, + .init_val = 0x00, + .ro_mask = 0xFF, + .emu_mask = 0xFF, + .init = xen_pt_ptr_reg_init, + .u.b.read = xen_pt_byte_reg_read, + .u.b.write = xen_pt_byte_reg_write, + }, + /* Device Capabilities reg */ + { + .offset = PCI_EXP_DEVCAP, + .size = 4, + .init_val = 0x00000000, + .ro_mask = 0x1FFCFFFF, + .emu_mask = 0x10000000, + .init = xen_pt_common_reg_init, + .u.dw.read = xen_pt_long_reg_read, + .u.dw.write = xen_pt_long_reg_write, + }, + /* Device Control reg */ + { + .offset = PCI_EXP_DEVCTL, + .size = 2, + .init_val = 0x2810, + .ro_mask = 0x8400, + .emu_mask = 0xFFFF, + .init = xen_pt_common_reg_init, + .u.w.read = xen_pt_word_reg_read, + .u.w.write = xen_pt_word_reg_write, + }, + /* Link Control reg */ + { + .offset = PCI_EXP_LNKCTL, + .size = 2, + .init_val = 0x0000, + .ro_mask = 0xFC34, + .emu_mask = 0xFFFF, + .init = xen_pt_linkctrl_reg_init, + .u.w.read = xen_pt_word_reg_read, + .u.w.write = xen_pt_word_reg_write, + }, + /* Device Control 2 reg */ + { + .offset = 0x28, + .size = 2, + .init_val = 0x0000, + .ro_mask = 0xFFE0, + .emu_mask = 0xFFFF, + .init = xen_pt_devctrl2_reg_init, + .u.w.read = xen_pt_word_reg_read, + .u.w.write = xen_pt_word_reg_write, + }, + /* Link Control 2 reg */ + { + .offset = 0x30, + .size = 2, + .init_val = 0x0000, + .ro_mask = 0xE040, + .emu_mask = 0xFFFF, + .init = xen_pt_linkctrl2_reg_init, + .u.w.read = xen_pt_word_reg_read, + .u.w.write = xen_pt_word_reg_write, + }, + { + .size = 0, + }, +}; + + +/********************************* + * Power Management Capability + */ + +/* read Power Management Control/Status register */ +static int xen_pt_pmcsr_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint16_t *value, uint16_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint16_t valid_emu_mask = reg->emu_mask; + + valid_emu_mask |= PCI_PM_CTRL_STATE_MASK | PCI_PM_CTRL_NO_SOFT_RESET; + + valid_emu_mask = valid_emu_mask & valid_mask; + *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); + + return 0; +} +/* write Power Management Control/Status register */ +static int xen_pt_pmcsr_reg_write(XenPCIPassthroughState *s, + XenPTReg *cfg_entry, uint16_t *val, + uint16_t dev_value, uint16_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint16_t emu_mask = reg->emu_mask; + uint16_t writable_mask = 0; + uint16_t throughable_mask = 0; + + emu_mask |= PCI_PM_CTRL_STATE_MASK | PCI_PM_CTRL_NO_SOFT_RESET; + + /* modify emulate register */ + writable_mask = emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ + throughable_mask = ~emu_mask & valid_mask; + *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + + return 0; +} + +/* Power Management Capability reg static infomation table */ +static XenPTRegInfo xen_pt_emu_reg_pm[] = { + /* Next Pointer reg */ + { + .offset = PCI_CAP_LIST_NEXT, + .size = 1, + .init_val = 0x00, + .ro_mask = 0xFF, + .emu_mask = 0xFF, + .init = xen_pt_ptr_reg_init, + .u.b.read = xen_pt_byte_reg_read, + .u.b.write = xen_pt_byte_reg_write, + }, + /* Power Management Capabilities reg */ + { + .offset = PCI_CAP_FLAGS, + .size = 2, + .init_val = 0x0000, + .ro_mask = 0xFFFF, + .emu_mask = 0xF9C8, + .init = xen_pt_common_reg_init, + .u.w.read = xen_pt_word_reg_read, + .u.w.write = xen_pt_word_reg_write, + }, + /* PCI Power Management Control/Status reg */ + { + .offset = PCI_PM_CTRL, + .size = 2, + .init_val = 0x0008, + .ro_mask = 0xE1FC, + .emu_mask = 0x8100, + .init = xen_pt_common_reg_init, + .u.w.read = xen_pt_pmcsr_reg_read, + .u.w.write = xen_pt_pmcsr_reg_write, + }, + { + .size = 0, + }, +}; + + +/******************************** + * MSI Capability + */ + +/* Helper */ +static bool xen_pt_msgdata_check_type(uint32_t offset, uint16_t flags) +{ + /* check the offset whether matches the type or not */ + bool is_32 = (offset == PCI_MSI_DATA_32) && !(flags & PCI_MSI_FLAGS_64BIT); + bool is_64 = (offset == PCI_MSI_DATA_64) && (flags & PCI_MSI_FLAGS_64BIT); + return is_32 || is_64; +} + +/* Message Control register */ +static int xen_pt_msgctrl_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + PCIDevice *d = &s->dev; + XenPTMSI *msi = s->msi; + uint16_t reg_field = 0; + + /* use I/O device register's value as initial value */ + reg_field = pci_get_word(d->config + real_offset); + + if (reg_field & PCI_MSI_FLAGS_ENABLE) { + XEN_PT_LOG(&s->dev, "MSI already enabled, disabling it first\n"); + xen_host_pci_set_word(&s->real_device, real_offset, + reg_field & ~PCI_MSI_FLAGS_ENABLE); + } + msi->flags |= reg_field; + msi->ctrl_offset = real_offset; + msi->initialized = false; + msi->mapped = false; + + *data = reg->init_val; + return 0; +} +static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s, + XenPTReg *cfg_entry, uint16_t *val, + uint16_t dev_value, uint16_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + XenPTMSI *msi = s->msi; + uint16_t writable_mask = 0; + uint16_t throughable_mask = 0; + uint16_t raw_val; + + /* Currently no support for multi-vector */ + if (*val & PCI_MSI_FLAGS_QSIZE) { + XEN_PT_WARN(&s->dev, "Tries to set more than 1 vector ctrl %x\n", *val); + } + + /* modify emulate register */ + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + msi->flags |= cfg_entry->data & ~PCI_MSI_FLAGS_ENABLE; + + /* create value for writing to I/O device register */ + raw_val = *val; + throughable_mask = ~reg->emu_mask & valid_mask; + *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + + /* update MSI */ + if (raw_val & PCI_MSI_FLAGS_ENABLE) { + /* setup MSI pirq for the first time */ + if (!msi->initialized) { + /* Init physical one */ + XEN_PT_LOG(&s->dev, "setup MSI\n"); + if (xen_pt_msi_setup(s)) { + /* We do not broadcast the error to the framework code, so + * that MSI errors are contained in MSI emulation code and + * QEMU can go on running. + * Guest MSI would be actually not working. + */ + *val &= ~PCI_MSI_FLAGS_ENABLE; + XEN_PT_WARN(&s->dev, "Can not map MSI.\n"); + return 0; + } + if (xen_pt_msi_update(s)) { + *val &= ~PCI_MSI_FLAGS_ENABLE; + XEN_PT_WARN(&s->dev, "Can not bind MSI\n"); + return 0; + } + msi->initialized = true; + msi->mapped = true; + } + msi->flags |= PCI_MSI_FLAGS_ENABLE; + } else { + msi->flags &= ~PCI_MSI_FLAGS_ENABLE; + } + + /* pass through MSI_ENABLE bit */ + *val &= ~PCI_MSI_FLAGS_ENABLE; + *val |= raw_val & PCI_MSI_FLAGS_ENABLE; + + return 0; +} + +/* initialize Message Upper Address register */ +static int xen_pt_msgaddr64_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + /* no need to initialize in case of 32 bit type */ + if (!(s->msi->flags & PCI_MSI_FLAGS_64BIT)) { + *data = XEN_PT_INVALID_REG; + } else { + *data = reg->init_val; + } + + return 0; +} +/* this function will be called twice (for 32 bit and 64 bit type) */ +/* initialize Message Data register */ +static int xen_pt_msgdata_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + uint32_t flags = s->msi->flags; + uint32_t offset = reg->offset; + + /* check the offset whether matches the type or not */ + if (xen_pt_msgdata_check_type(offset, flags)) { + *data = reg->init_val; + } else { + *data = XEN_PT_INVALID_REG; + } + return 0; +} + +/* write Message Address register */ +static int xen_pt_msgaddr32_reg_write(XenPCIPassthroughState *s, + XenPTReg *cfg_entry, uint32_t *val, + uint32_t dev_value, uint32_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint32_t writable_mask = 0; + uint32_t throughable_mask = 0; + uint32_t old_addr = cfg_entry->data; + + /* modify emulate register */ + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + s->msi->addr_lo = cfg_entry->data; + + /* create value for writing to I/O device register */ + throughable_mask = ~reg->emu_mask & valid_mask; + *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + + /* update MSI */ + if (cfg_entry->data != old_addr) { + if (s->msi->mapped) { + xen_pt_msi_update(s); + } + } + + return 0; +} +/* write Message Upper Address register */ +static int xen_pt_msgaddr64_reg_write(XenPCIPassthroughState *s, + XenPTReg *cfg_entry, uint32_t *val, + uint32_t dev_value, uint32_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint32_t writable_mask = 0; + uint32_t throughable_mask = 0; + uint32_t old_addr = cfg_entry->data; + + /* check whether the type is 64 bit or not */ + if (!(s->msi->flags & PCI_MSI_FLAGS_64BIT)) { + XEN_PT_ERR(&s->dev, + "Can't write to the upper address without 64 bit support\n"); + return -1; + } + + /* modify emulate register */ + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + /* update the msi_info too */ + s->msi->addr_hi = cfg_entry->data; + + /* create value for writing to I/O device register */ + throughable_mask = ~reg->emu_mask & valid_mask; + *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + + /* update MSI */ + if (cfg_entry->data != old_addr) { + if (s->msi->mapped) { + xen_pt_msi_update(s); + } + } + + return 0; +} + + +/* this function will be called twice (for 32 bit and 64 bit type) */ +/* write Message Data register */ +static int xen_pt_msgdata_reg_write(XenPCIPassthroughState *s, + XenPTReg *cfg_entry, uint16_t *val, + uint16_t dev_value, uint16_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + XenPTMSI *msi = s->msi; + uint16_t writable_mask = 0; + uint16_t throughable_mask = 0; + uint16_t old_data = cfg_entry->data; + uint32_t offset = reg->offset; + + /* check the offset whether matches the type or not */ + if (!xen_pt_msgdata_check_type(offset, msi->flags)) { + /* exit I/O emulator */ + XEN_PT_ERR(&s->dev, "the offset does not match the 32/64 bit type!\n"); + return -1; + } + + /* modify emulate register */ + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + /* update the msi_info too */ + msi->data = cfg_entry->data; + + /* create value for writing to I/O device register */ + throughable_mask = ~reg->emu_mask & valid_mask; + *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + + /* update MSI */ + if (cfg_entry->data != old_data) { + if (msi->mapped) { + xen_pt_msi_update(s); + } + } + + return 0; +} + +/* MSI Capability Structure reg static infomation table */ +static XenPTRegInfo xen_pt_emu_reg_msi[] = { + /* Next Pointer reg */ + { + .offset = PCI_CAP_LIST_NEXT, + .size = 1, + .init_val = 0x00, + .ro_mask = 0xFF, + .emu_mask = 0xFF, + .init = xen_pt_ptr_reg_init, + .u.b.read = xen_pt_byte_reg_read, + .u.b.write = xen_pt_byte_reg_write, + }, + /* Message Control reg */ + { + .offset = PCI_MSI_FLAGS, + .size = 2, + .init_val = 0x0000, + .ro_mask = 0xFF8E, + .emu_mask = 0x007F, + .init = xen_pt_msgctrl_reg_init, + .u.w.read = xen_pt_word_reg_read, + .u.w.write = xen_pt_msgctrl_reg_write, + }, + /* Message Address reg */ + { + .offset = PCI_MSI_ADDRESS_LO, + .size = 4, + .init_val = 0x00000000, + .ro_mask = 0x00000003, + .emu_mask = 0xFFFFFFFF, + .no_wb = 1, + .init = xen_pt_common_reg_init, + .u.dw.read = xen_pt_long_reg_read, + .u.dw.write = xen_pt_msgaddr32_reg_write, + }, + /* Message Upper Address reg (if PCI_MSI_FLAGS_64BIT set) */ + { + .offset = PCI_MSI_ADDRESS_HI, + .size = 4, + .init_val = 0x00000000, + .ro_mask = 0x00000000, + .emu_mask = 0xFFFFFFFF, + .no_wb = 1, + .init = xen_pt_msgaddr64_reg_init, + .u.dw.read = xen_pt_long_reg_read, + .u.dw.write = xen_pt_msgaddr64_reg_write, + }, + /* Message Data reg (16 bits of data for 32-bit devices) */ + { + .offset = PCI_MSI_DATA_32, + .size = 2, + .init_val = 0x0000, + .ro_mask = 0x0000, + .emu_mask = 0xFFFF, + .no_wb = 1, + .init = xen_pt_msgdata_reg_init, + .u.w.read = xen_pt_word_reg_read, + .u.w.write = xen_pt_msgdata_reg_write, + }, + /* Message Data reg (16 bits of data for 64-bit devices) */ + { + .offset = PCI_MSI_DATA_64, + .size = 2, + .init_val = 0x0000, + .ro_mask = 0x0000, + .emu_mask = 0xFFFF, + .no_wb = 1, + .init = xen_pt_msgdata_reg_init, + .u.w.read = xen_pt_word_reg_read, + .u.w.write = xen_pt_msgdata_reg_write, + }, + { + .size = 0, + }, +}; + + +/************************************** + * MSI-X Capability + */ + +/* Message Control register for MSI-X */ +static int xen_pt_msixctrl_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + PCIDevice *d = &s->dev; + uint16_t reg_field = 0; + + /* use I/O device register's value as initial value */ + reg_field = pci_get_word(d->config + real_offset); + + if (reg_field & PCI_MSIX_FLAGS_ENABLE) { + XEN_PT_LOG(d, "MSIX already enabled, disabling it first\n"); + xen_host_pci_set_word(&s->real_device, real_offset, + reg_field & ~PCI_MSIX_FLAGS_ENABLE); + } + + s->msix->ctrl_offset = real_offset; + + *data = reg->init_val; + return 0; +} +static int xen_pt_msixctrl_reg_write(XenPCIPassthroughState *s, + XenPTReg *cfg_entry, uint16_t *val, + uint16_t dev_value, uint16_t valid_mask) +{ + XenPTRegInfo *reg = cfg_entry->reg; + uint16_t writable_mask = 0; + uint16_t throughable_mask = 0; + int debug_msix_enabled_old; + + /* modify emulate register */ + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ + throughable_mask = ~reg->emu_mask & valid_mask; + *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + + /* update MSI-X */ + if ((*val & PCI_MSIX_FLAGS_ENABLE) + && !(*val & PCI_MSIX_FLAGS_MASKALL)) { + xen_pt_msix_update(s); + } + + debug_msix_enabled_old = s->msix->enabled; + s->msix->enabled = !!(*val & PCI_MSIX_FLAGS_ENABLE); + if (s->msix->enabled != debug_msix_enabled_old) { + XEN_PT_LOG(&s->dev, "%s MSI-X\n", + s->msix->enabled ? "enable" : "disable"); + } + + return 0; +} + +/* MSI-X Capability Structure reg static infomation table */ +static XenPTRegInfo xen_pt_emu_reg_msix[] = { + /* Next Pointer reg */ + { + .offset = PCI_CAP_LIST_NEXT, + .size = 1, + .init_val = 0x00, + .ro_mask = 0xFF, + .emu_mask = 0xFF, + .init = xen_pt_ptr_reg_init, + .u.b.read = xen_pt_byte_reg_read, + .u.b.write = xen_pt_byte_reg_write, + }, + /* Message Control reg */ + { + .offset = PCI_MSI_FLAGS, + .size = 2, + .init_val = 0x0000, + .ro_mask = 0x3FFF, + .emu_mask = 0x0000, + .init = xen_pt_msixctrl_reg_init, + .u.w.read = xen_pt_word_reg_read, + .u.w.write = xen_pt_msixctrl_reg_write, + }, + { + .size = 0, + }, +}; + + +/**************************** + * Capabilities + */ + +/* capability structure register group size functions */ + +static int xen_pt_reg_grp_size_init(XenPCIPassthroughState *s, + const XenPTRegGroupInfo *grp_reg, + uint32_t base_offset, uint8_t *size) +{ + *size = grp_reg->grp_size; + return 0; +} +/* get Vendor Specific Capability Structure register group size */ +static int xen_pt_vendor_size_init(XenPCIPassthroughState *s, + const XenPTRegGroupInfo *grp_reg, + uint32_t base_offset, uint8_t *size) +{ + *size = pci_get_byte(s->dev.config + base_offset + 0x02); + return 0; +} +/* get PCI Express Capability Structure register group size */ +static int xen_pt_pcie_size_init(XenPCIPassthroughState *s, + const XenPTRegGroupInfo *grp_reg, + uint32_t base_offset, uint8_t *size) +{ + PCIDevice *d = &s->dev; + uint8_t version = get_capability_version(s, base_offset); + uint8_t type = get_device_type(s, base_offset); + uint8_t pcie_size = 0; + + + /* calculate size depend on capability version and device/port type */ + /* in case of PCI Express Base Specification Rev 1.x */ + if (version == 1) { + /* The PCI Express Capabilities, Device Capabilities, and Device + * Status/Control registers are required for all PCI Express devices. + * The Link Capabilities and Link Status/Control are required for all + * Endpoints that are not Root Complex Integrated Endpoints. Endpoints + * are not required to implement registers other than those listed + * above and terminate the capability structure. + */ + switch (type) { + case PCI_EXP_TYPE_ENDPOINT: + case PCI_EXP_TYPE_LEG_END: + pcie_size = 0x14; + break; + case PCI_EXP_TYPE_RC_END: + /* has no link */ + pcie_size = 0x0C; + break; + /* only EndPoint passthrough is supported */ + case PCI_EXP_TYPE_ROOT_PORT: + case PCI_EXP_TYPE_UPSTREAM: + case PCI_EXP_TYPE_DOWNSTREAM: + case PCI_EXP_TYPE_PCI_BRIDGE: + case PCI_EXP_TYPE_PCIE_BRIDGE: + case PCI_EXP_TYPE_RC_EC: + default: + XEN_PT_ERR(d, "Unsupported device/port type %#x.\n", type); + return -1; + } + } + /* in case of PCI Express Base Specification Rev 2.0 */ + else if (version == 2) { + switch (type) { + case PCI_EXP_TYPE_ENDPOINT: + case PCI_EXP_TYPE_LEG_END: + case PCI_EXP_TYPE_RC_END: + /* For Functions that do not implement the registers, + * these spaces must be hardwired to 0b. + */ + pcie_size = 0x3C; + break; + /* only EndPoint passthrough is supported */ + case PCI_EXP_TYPE_ROOT_PORT: + case PCI_EXP_TYPE_UPSTREAM: + case PCI_EXP_TYPE_DOWNSTREAM: + case PCI_EXP_TYPE_PCI_BRIDGE: + case PCI_EXP_TYPE_PCIE_BRIDGE: + case PCI_EXP_TYPE_RC_EC: + default: + XEN_PT_ERR(d, "Unsupported device/port type %#x.\n", type); + return -1; + } + } else { + XEN_PT_ERR(d, "Unsupported capability version %#x.\n", version); + return -1; + } + + *size = pcie_size; + return 0; +} +/* get MSI Capability Structure register group size */ +static int xen_pt_msi_size_init(XenPCIPassthroughState *s, + const XenPTRegGroupInfo *grp_reg, + uint32_t base_offset, uint8_t *size) +{ + PCIDevice *d = &s->dev; + uint16_t msg_ctrl = 0; + uint8_t msi_size = 0xa; + + msg_ctrl = pci_get_word(d->config + (base_offset + PCI_MSI_FLAGS)); + + /* check if 64-bit address is capable of per-vector masking */ + if (msg_ctrl & PCI_MSI_FLAGS_64BIT) { + msi_size += 4; + } + if (msg_ctrl & PCI_MSI_FLAGS_MASKBIT) { + msi_size += 10; + } + + s->msi = g_new0(XenPTMSI, 1); + s->msi->pirq = XEN_PT_UNASSIGNED_PIRQ; + + *size = msi_size; + return 0; +} +/* get MSI-X Capability Structure register group size */ +static int xen_pt_msix_size_init(XenPCIPassthroughState *s, + const XenPTRegGroupInfo *grp_reg, + uint32_t base_offset, uint8_t *size) +{ + int rc = 0; + + rc = xen_pt_msix_init(s, base_offset); + + if (rc < 0) { + XEN_PT_ERR(&s->dev, "Internal error: Invalid xen_pt_msix_init.\n"); + return rc; + } + + *size = grp_reg->grp_size; + return 0; +} + + +static const XenPTRegGroupInfo xen_pt_emu_reg_grps[] = { + /* Header Type0 reg group */ + { + .grp_id = 0xFF, + .grp_type = XEN_PT_GRP_TYPE_EMU, + .grp_size = 0x40, + .size_init = xen_pt_reg_grp_size_init, + .emu_regs = xen_pt_emu_reg_header0, + }, + /* PCI PowerManagement Capability reg group */ + { + .grp_id = PCI_CAP_ID_PM, + .grp_type = XEN_PT_GRP_TYPE_EMU, + .grp_size = PCI_PM_SIZEOF, + .size_init = xen_pt_reg_grp_size_init, + .emu_regs = xen_pt_emu_reg_pm, + }, + /* AGP Capability Structure reg group */ + { + .grp_id = PCI_CAP_ID_AGP, + .grp_type = XEN_PT_GRP_TYPE_HARDWIRED, + .grp_size = 0x30, + .size_init = xen_pt_reg_grp_size_init, + }, + /* Vital Product Data Capability Structure reg group */ + { + .grp_id = PCI_CAP_ID_VPD, + .grp_type = XEN_PT_GRP_TYPE_EMU, + .grp_size = 0x08, + .size_init = xen_pt_reg_grp_size_init, + .emu_regs = xen_pt_emu_reg_vpd, + }, + /* Slot Identification reg group */ + { + .grp_id = PCI_CAP_ID_SLOTID, + .grp_type = XEN_PT_GRP_TYPE_HARDWIRED, + .grp_size = 0x04, + .size_init = xen_pt_reg_grp_size_init, + }, + /* MSI Capability Structure reg group */ + { + .grp_id = PCI_CAP_ID_MSI, + .grp_type = XEN_PT_GRP_TYPE_EMU, + .grp_size = 0xFF, + .size_init = xen_pt_msi_size_init, + .emu_regs = xen_pt_emu_reg_msi, + }, + /* PCI-X Capabilities List Item reg group */ + { + .grp_id = PCI_CAP_ID_PCIX, + .grp_type = XEN_PT_GRP_TYPE_HARDWIRED, + .grp_size = 0x18, + .size_init = xen_pt_reg_grp_size_init, + }, + /* Vendor Specific Capability Structure reg group */ + { + .grp_id = PCI_CAP_ID_VNDR, + .grp_type = XEN_PT_GRP_TYPE_EMU, + .grp_size = 0xFF, + .size_init = xen_pt_vendor_size_init, + .emu_regs = xen_pt_emu_reg_vendor, + }, + /* SHPC Capability List Item reg group */ + { + .grp_id = PCI_CAP_ID_SHPC, + .grp_type = XEN_PT_GRP_TYPE_HARDWIRED, + .grp_size = 0x08, + .size_init = xen_pt_reg_grp_size_init, + }, + /* Subsystem ID and Subsystem Vendor ID Capability List Item reg group */ + { + .grp_id = PCI_CAP_ID_SSVID, + .grp_type = XEN_PT_GRP_TYPE_HARDWIRED, + .grp_size = 0x08, + .size_init = xen_pt_reg_grp_size_init, + }, + /* AGP 8x Capability Structure reg group */ + { + .grp_id = PCI_CAP_ID_AGP3, + .grp_type = XEN_PT_GRP_TYPE_HARDWIRED, + .grp_size = 0x30, + .size_init = xen_pt_reg_grp_size_init, + }, + /* PCI Express Capability Structure reg group */ + { + .grp_id = PCI_CAP_ID_EXP, + .grp_type = XEN_PT_GRP_TYPE_EMU, + .grp_size = 0xFF, + .size_init = xen_pt_pcie_size_init, + .emu_regs = xen_pt_emu_reg_pcie, + }, + /* MSI-X Capability Structure reg group */ + { + .grp_id = PCI_CAP_ID_MSIX, + .grp_type = XEN_PT_GRP_TYPE_EMU, + .grp_size = 0x0C, + .size_init = xen_pt_msix_size_init, + .emu_regs = xen_pt_emu_reg_msix, + }, + { + .grp_size = 0, + }, +}; + +/* initialize Capabilities Pointer or Next Pointer register */ +static int xen_pt_ptr_reg_init(XenPCIPassthroughState *s, + XenPTRegInfo *reg, uint32_t real_offset, + uint32_t *data) +{ + int i; + uint8_t *config = s->dev.config; + uint32_t reg_field = pci_get_byte(config + real_offset); + uint8_t cap_id = 0; + + /* find capability offset */ + while (reg_field) { + for (i = 0; xen_pt_emu_reg_grps[i].grp_size != 0; i++) { + if (xen_pt_hide_dev_cap(&s->real_device, + xen_pt_emu_reg_grps[i].grp_id)) { + continue; + } + + cap_id = pci_get_byte(config + reg_field + PCI_CAP_LIST_ID); + if (xen_pt_emu_reg_grps[i].grp_id == cap_id) { + if (xen_pt_emu_reg_grps[i].grp_type == XEN_PT_GRP_TYPE_EMU) { + goto out; + } + /* ignore the 0 hardwired capability, find next one */ + break; + } + } + + /* next capability */ + reg_field = pci_get_byte(config + reg_field + PCI_CAP_LIST_NEXT); + } + +out: + *data = reg_field; + return 0; +} + + +/************* + * Main + */ + +static uint8_t find_cap_offset(XenPCIPassthroughState *s, uint8_t cap) +{ + uint8_t id; + unsigned max_cap = PCI_CAP_MAX; + uint8_t pos = PCI_CAPABILITY_LIST; + uint8_t status = 0; + + if (xen_host_pci_get_byte(&s->real_device, PCI_STATUS, &status)) { + return 0; + } + if ((status & PCI_STATUS_CAP_LIST) == 0) { + return 0; + } + + while (max_cap--) { + if (xen_host_pci_get_byte(&s->real_device, pos, &pos)) { + break; + } + if (pos < PCI_CONFIG_HEADER_SIZE) { + break; + } + + pos &= ~3; + if (xen_host_pci_get_byte(&s->real_device, + pos + PCI_CAP_LIST_ID, &id)) { + break; + } + + if (id == 0xff) { + break; + } + if (id == cap) { + return pos; + } + + pos += PCI_CAP_LIST_NEXT; + } + return 0; +} + +static int xen_pt_config_reg_init(XenPCIPassthroughState *s, + XenPTRegGroup *reg_grp, XenPTRegInfo *reg) +{ + XenPTReg *reg_entry; + uint32_t data = 0; + int rc = 0; + + reg_entry = g_new0(XenPTReg, 1); + reg_entry->reg = reg; + + if (reg->init) { + /* initialize emulate register */ + rc = reg->init(s, reg_entry->reg, + reg_grp->base_offset + reg->offset, &data); + if (rc < 0) { + free(reg_entry); + return rc; + } + if (data == XEN_PT_INVALID_REG) { + /* free unused BAR register entry */ + free(reg_entry); + return 0; + } + /* set register value */ + reg_entry->data = data; + } + /* list add register entry */ + QLIST_INSERT_HEAD(®_grp->reg_tbl_list, reg_entry, entries); + + return 0; +} + +int xen_pt_config_init(XenPCIPassthroughState *s) +{ + int i, rc; + + QLIST_INIT(&s->reg_grps); + + for (i = 0; xen_pt_emu_reg_grps[i].grp_size != 0; i++) { + uint32_t reg_grp_offset = 0; + XenPTRegGroup *reg_grp_entry = NULL; + + if (xen_pt_emu_reg_grps[i].grp_id != 0xFF) { + if (xen_pt_hide_dev_cap(&s->real_device, + xen_pt_emu_reg_grps[i].grp_id)) { + continue; + } + + reg_grp_offset = find_cap_offset(s, xen_pt_emu_reg_grps[i].grp_id); + + if (!reg_grp_offset) { + continue; + } + } + + reg_grp_entry = g_new0(XenPTRegGroup, 1); + QLIST_INIT(®_grp_entry->reg_tbl_list); + QLIST_INSERT_HEAD(&s->reg_grps, reg_grp_entry, entries); + + reg_grp_entry->base_offset = reg_grp_offset; + reg_grp_entry->reg_grp = xen_pt_emu_reg_grps + i; + if (xen_pt_emu_reg_grps[i].size_init) { + /* get register group size */ + rc = xen_pt_emu_reg_grps[i].size_init(s, reg_grp_entry->reg_grp, + reg_grp_offset, + ®_grp_entry->size); + if (rc < 0) { + xen_pt_config_delete(s); + return rc; + } + } + + if (xen_pt_emu_reg_grps[i].grp_type == XEN_PT_GRP_TYPE_EMU) { + if (xen_pt_emu_reg_grps[i].emu_regs) { + int j = 0; + XenPTRegInfo *regs = xen_pt_emu_reg_grps[i].emu_regs; + /* initialize capability register */ + for (j = 0; regs->size != 0; j++, regs++) { + /* initialize capability register */ + rc = xen_pt_config_reg_init(s, reg_grp_entry, regs); + if (rc < 0) { + xen_pt_config_delete(s); + return rc; + } + } + } + } + } + + return 0; +} + +/* delete all emulate register */ +void xen_pt_config_delete(XenPCIPassthroughState *s) +{ + struct XenPTRegGroup *reg_group, *next_grp; + struct XenPTReg *reg, *next_reg; + + /* free MSI/MSI-X info table */ + if (s->msix) { + xen_pt_msix_delete(s); + } + if (s->msi) { + g_free(s->msi); + } + + /* free all register group entry */ + QLIST_FOREACH_SAFE(reg_group, &s->reg_grps, entries, next_grp) { + /* free all register entry */ + QLIST_FOREACH_SAFE(reg, ®_group->reg_tbl_list, entries, next_reg) { + QLIST_REMOVE(reg, entries); + g_free(reg); + } + + QLIST_REMOVE(reg_group, entries); + g_free(reg_group); + } +} diff --git a/hw/xen_pt_msi.c b/hw/xen_pt_msi.c new file mode 100644 index 0000000000..2299cc7772 --- /dev/null +++ b/hw/xen_pt_msi.c @@ -0,0 +1,620 @@ +/* + * Copyright (c) 2007, Intel Corporation. + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Jiang Yunhong <yunhong.jiang@intel.com> + * + * This file implements direct PCI assignment to a HVM guest + */ + +#include <sys/mman.h> + +#include "xen_backend.h" +#include "xen_pt.h" +#include "apic-msidef.h" + + +#define XEN_PT_AUTO_ASSIGN -1 + +/* shift count for gflags */ +#define XEN_PT_GFLAGS_SHIFT_DEST_ID 0 +#define XEN_PT_GFLAGS_SHIFT_RH 8 +#define XEN_PT_GFLAGS_SHIFT_DM 9 +#define XEN_PT_GFLAGSSHIFT_DELIV_MODE 12 +#define XEN_PT_GFLAGSSHIFT_TRG_MODE 15 + + +/* + * Helpers + */ + +static inline uint8_t msi_vector(uint32_t data) +{ + return (data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT; +} + +static inline uint8_t msi_dest_id(uint32_t addr) +{ + return (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT; +} + +static inline uint32_t msi_ext_dest_id(uint32_t addr_hi) +{ + return addr_hi & 0xffffff00; +} + +static uint32_t msi_gflags(uint32_t data, uint64_t addr) +{ + uint32_t result = 0; + int rh, dm, dest_id, deliv_mode, trig_mode; + + rh = (addr >> MSI_ADDR_REDIRECTION_SHIFT) & 0x1; + dm = (addr >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1; + dest_id = msi_dest_id(addr); + deliv_mode = (data >> MSI_DATA_DELIVERY_MODE_SHIFT) & 0x7; + trig_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1; + + result = dest_id | (rh << XEN_PT_GFLAGS_SHIFT_RH) + | (dm << XEN_PT_GFLAGS_SHIFT_DM) + | (deliv_mode << XEN_PT_GFLAGSSHIFT_DELIV_MODE) + | (trig_mode << XEN_PT_GFLAGSSHIFT_TRG_MODE); + + return result; +} + +static inline uint64_t msi_addr64(XenPTMSI *msi) +{ + return (uint64_t)msi->addr_hi << 32 | msi->addr_lo; +} + +static int msi_msix_enable(XenPCIPassthroughState *s, + uint32_t address, + uint16_t flag, + bool enable) +{ + uint16_t val = 0; + + if (!address) { + return -1; + } + + xen_host_pci_get_word(&s->real_device, address, &val); + if (enable) { + val |= flag; + } else { + val &= ~flag; + } + xen_host_pci_set_word(&s->real_device, address, val); + return 0; +} + +static int msi_msix_setup(XenPCIPassthroughState *s, + uint64_t addr, + uint32_t data, + int *ppirq, + bool is_msix, + int msix_entry, + bool is_not_mapped) +{ + uint8_t gvec = msi_vector(data); + int rc = 0; + + assert((!is_msix && msix_entry == 0) || is_msix); + + if (gvec == 0) { + /* if gvec is 0, the guest is asking for a particular pirq that + * is passed as dest_id */ + *ppirq = msi_ext_dest_id(addr >> 32) | msi_dest_id(addr); + if (!*ppirq) { + /* this probably identifies an misconfiguration of the guest, + * try the emulated path */ + *ppirq = XEN_PT_UNASSIGNED_PIRQ; + } else { + XEN_PT_LOG(&s->dev, "requested pirq %d for MSI%s" + " (vec: %#x, entry: %#x)\n", + *ppirq, is_msix ? "-X" : "", gvec, msix_entry); + } + } + + if (is_not_mapped) { + uint64_t table_base = 0; + + if (is_msix) { + table_base = s->msix->table_base; + } + + rc = xc_physdev_map_pirq_msi(xen_xc, xen_domid, XEN_PT_AUTO_ASSIGN, + ppirq, PCI_DEVFN(s->real_device.dev, + s->real_device.func), + s->real_device.bus, + msix_entry, table_base); + if (rc) { + XEN_PT_ERR(&s->dev, + "Mapping of MSI%s (rc: %i, vec: %#x, entry %#x)\n", + is_msix ? "-X" : "", rc, gvec, msix_entry); + return rc; + } + } + + return 0; +} +static int msi_msix_update(XenPCIPassthroughState *s, + uint64_t addr, + uint32_t data, + int pirq, + bool is_msix, + int msix_entry, + int *old_pirq) +{ + PCIDevice *d = &s->dev; + uint8_t gvec = msi_vector(data); + uint32_t gflags = msi_gflags(data, addr); + int rc = 0; + uint64_t table_addr = 0; + + XEN_PT_LOG(d, "Updating MSI%s with pirq %d gvec %#x gflags %#x" + " (entry: %#x)\n", + is_msix ? "-X" : "", pirq, gvec, gflags, msix_entry); + + if (is_msix) { + table_addr = s->msix->mmio_base_addr; + } + + rc = xc_domain_update_msi_irq(xen_xc, xen_domid, gvec, + pirq, gflags, table_addr); + + if (rc) { + XEN_PT_ERR(d, "Updating of MSI%s failed. (rc: %d)\n", + is_msix ? "-X" : "", rc); + + if (xc_physdev_unmap_pirq(xen_xc, xen_domid, *old_pirq)) { + XEN_PT_ERR(d, "Unmapping of MSI%s pirq %d failed.\n", + is_msix ? "-X" : "", *old_pirq); + } + *old_pirq = XEN_PT_UNASSIGNED_PIRQ; + } + return rc; +} + +static int msi_msix_disable(XenPCIPassthroughState *s, + uint64_t addr, + uint32_t data, + int pirq, + bool is_msix, + bool is_binded) +{ + PCIDevice *d = &s->dev; + uint8_t gvec = msi_vector(data); + uint32_t gflags = msi_gflags(data, addr); + int rc = 0; + + if (pirq == XEN_PT_UNASSIGNED_PIRQ) { + return 0; + } + + if (is_binded) { + XEN_PT_LOG(d, "Unbind MSI%s with pirq %d, gvec %#x\n", + is_msix ? "-X" : "", pirq, gvec); + rc = xc_domain_unbind_msi_irq(xen_xc, xen_domid, gvec, pirq, gflags); + if (rc) { + XEN_PT_ERR(d, "Unbinding of MSI%s failed. (pirq: %d, gvec: %#x)\n", + is_msix ? "-X" : "", pirq, gvec); + return rc; + } + } + + XEN_PT_LOG(d, "Unmap MSI%s pirq %d\n", is_msix ? "-X" : "", pirq); + rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, pirq); + if (rc) { + XEN_PT_ERR(d, "Unmapping of MSI%s pirq %d failed. (rc: %i)\n", + is_msix ? "-X" : "", pirq, rc); + return rc; + } + + return 0; +} + +/* + * MSI virtualization functions + */ + +int xen_pt_msi_set_enable(XenPCIPassthroughState *s, bool enable) +{ + XEN_PT_LOG(&s->dev, "%s MSI.\n", enable ? "enabling" : "disabling"); + + if (!s->msi) { + return -1; + } + + return msi_msix_enable(s, s->msi->ctrl_offset, PCI_MSI_FLAGS_ENABLE, + enable); +} + +/* setup physical msi, but don't enable it */ +int xen_pt_msi_setup(XenPCIPassthroughState *s) +{ + int pirq = XEN_PT_UNASSIGNED_PIRQ; + int rc = 0; + XenPTMSI *msi = s->msi; + + if (msi->initialized) { + XEN_PT_ERR(&s->dev, + "Setup physical MSI when it has been properly initialized.\n"); + return -1; + } + + rc = msi_msix_setup(s, msi_addr64(msi), msi->data, &pirq, false, 0, true); + if (rc) { + return rc; + } + + if (pirq < 0) { + XEN_PT_ERR(&s->dev, "Invalid pirq number: %d.\n", pirq); + return -1; + } + + msi->pirq = pirq; + XEN_PT_LOG(&s->dev, "MSI mapped with pirq %d.\n", pirq); + + return 0; +} + +int xen_pt_msi_update(XenPCIPassthroughState *s) +{ + XenPTMSI *msi = s->msi; + return msi_msix_update(s, msi_addr64(msi), msi->data, msi->pirq, + false, 0, &msi->pirq); +} + +void xen_pt_msi_disable(XenPCIPassthroughState *s) +{ + XenPTMSI *msi = s->msi; + + if (!msi) { + return; + } + + xen_pt_msi_set_enable(s, false); + + msi_msix_disable(s, msi_addr64(msi), msi->data, msi->pirq, false, + msi->initialized); + + /* clear msi info */ + msi->flags = 0; + msi->mapped = false; + msi->pirq = XEN_PT_UNASSIGNED_PIRQ; +} + +/* + * MSI-X virtualization functions + */ + +static int msix_set_enable(XenPCIPassthroughState *s, bool enabled) +{ + XEN_PT_LOG(&s->dev, "%s MSI-X.\n", enabled ? "enabling" : "disabling"); + + if (!s->msix) { + return -1; + } + + return msi_msix_enable(s, s->msix->ctrl_offset, PCI_MSIX_FLAGS_ENABLE, + enabled); +} + +static int xen_pt_msix_update_one(XenPCIPassthroughState *s, int entry_nr) +{ + XenPTMSIXEntry *entry = NULL; + int pirq; + int rc; + + if (entry_nr < 0 || entry_nr >= s->msix->total_entries) { + return -EINVAL; + } + + entry = &s->msix->msix_entry[entry_nr]; + + if (!entry->updated) { + return 0; + } + + pirq = entry->pirq; + + rc = msi_msix_setup(s, entry->data, entry->data, &pirq, true, entry_nr, + entry->pirq == XEN_PT_UNASSIGNED_PIRQ); + if (rc) { + return rc; + } + if (entry->pirq == XEN_PT_UNASSIGNED_PIRQ) { + entry->pirq = pirq; + } + + rc = msi_msix_update(s, entry->addr, entry->data, pirq, true, + entry_nr, &entry->pirq); + + if (!rc) { + entry->updated = false; + } + + return rc; +} + +int xen_pt_msix_update(XenPCIPassthroughState *s) +{ + XenPTMSIX *msix = s->msix; + int i; + + for (i = 0; i < msix->total_entries; i++) { + xen_pt_msix_update_one(s, i); + } + + return 0; +} + +void xen_pt_msix_disable(XenPCIPassthroughState *s) +{ + int i = 0; + + msix_set_enable(s, false); + + for (i = 0; i < s->msix->total_entries; i++) { + XenPTMSIXEntry *entry = &s->msix->msix_entry[i]; + + msi_msix_disable(s, entry->addr, entry->data, entry->pirq, true, true); + + /* clear MSI-X info */ + entry->pirq = XEN_PT_UNASSIGNED_PIRQ; + entry->updated = false; + } +} + +int xen_pt_msix_update_remap(XenPCIPassthroughState *s, int bar_index) +{ + XenPTMSIXEntry *entry; + int i, ret; + + if (!(s->msix && s->msix->bar_index == bar_index)) { + return 0; + } + + for (i = 0; i < s->msix->total_entries; i++) { + entry = &s->msix->msix_entry[i]; + if (entry->pirq != XEN_PT_UNASSIGNED_PIRQ) { + ret = xc_domain_unbind_pt_irq(xen_xc, xen_domid, entry->pirq, + PT_IRQ_TYPE_MSI, 0, 0, 0, 0); + if (ret) { + XEN_PT_ERR(&s->dev, "unbind MSI-X entry %d failed\n", + entry->pirq); + } + entry->updated = true; + } + } + return xen_pt_msix_update(s); +} + +static uint32_t get_entry_value(XenPTMSIXEntry *e, int offset) +{ + switch (offset) { + case PCI_MSIX_ENTRY_LOWER_ADDR: + return e->addr & UINT32_MAX; + case PCI_MSIX_ENTRY_UPPER_ADDR: + return e->addr >> 32; + case PCI_MSIX_ENTRY_DATA: + return e->data; + case PCI_MSIX_ENTRY_VECTOR_CTRL: + return e->vector_ctrl; + default: + return 0; + } +} + +static void set_entry_value(XenPTMSIXEntry *e, int offset, uint32_t val) +{ + switch (offset) { + case PCI_MSIX_ENTRY_LOWER_ADDR: + e->addr = (e->addr & ((uint64_t)UINT32_MAX << 32)) | val; + break; + case PCI_MSIX_ENTRY_UPPER_ADDR: + e->addr = (uint64_t)val << 32 | (e->addr & UINT32_MAX); + break; + case PCI_MSIX_ENTRY_DATA: + e->data = val; + break; + case PCI_MSIX_ENTRY_VECTOR_CTRL: + e->vector_ctrl = val; + break; + } +} + +static void pci_msix_write(void *opaque, target_phys_addr_t addr, + uint64_t val, unsigned size) +{ + XenPCIPassthroughState *s = opaque; + XenPTMSIX *msix = s->msix; + XenPTMSIXEntry *entry; + int entry_nr, offset; + + entry_nr = addr / PCI_MSIX_ENTRY_SIZE; + if (entry_nr < 0 || entry_nr >= msix->total_entries) { + XEN_PT_ERR(&s->dev, "asked MSI-X entry '%i' invalid!\n", entry_nr); + return; + } + entry = &msix->msix_entry[entry_nr]; + offset = addr % PCI_MSIX_ENTRY_SIZE; + + if (offset != PCI_MSIX_ENTRY_VECTOR_CTRL) { + const volatile uint32_t *vec_ctrl; + + if (get_entry_value(entry, offset) == val) { + return; + } + + /* + * If Xen intercepts the mask bit access, entry->vec_ctrl may not be + * up-to-date. Read from hardware directly. + */ + vec_ctrl = s->msix->phys_iomem_base + entry_nr * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_VECTOR_CTRL; + + if (msix->enabled && !(*vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT)) { + XEN_PT_ERR(&s->dev, "Can't update msix entry %d since MSI-X is" + " already enabled.\n", entry_nr); + return; + } + + entry->updated = true; + } + + set_entry_value(entry, offset, val); + + if (offset == PCI_MSIX_ENTRY_VECTOR_CTRL) { + if (msix->enabled && !(val & PCI_MSIX_ENTRY_CTRL_MASKBIT)) { + xen_pt_msix_update_one(s, entry_nr); + } + } +} + +static uint64_t pci_msix_read(void *opaque, target_phys_addr_t addr, + unsigned size) +{ + XenPCIPassthroughState *s = opaque; + XenPTMSIX *msix = s->msix; + int entry_nr, offset; + + entry_nr = addr / PCI_MSIX_ENTRY_SIZE; + if (entry_nr < 0) { + XEN_PT_ERR(&s->dev, "asked MSI-X entry '%i' invalid!\n", entry_nr); + return 0; + } + + offset = addr % PCI_MSIX_ENTRY_SIZE; + + if (addr < msix->total_entries * PCI_MSIX_ENTRY_SIZE) { + return get_entry_value(&msix->msix_entry[entry_nr], offset); + } else { + /* Pending Bit Array (PBA) */ + return *(uint32_t *)(msix->phys_iomem_base + addr); + } +} + +static const MemoryRegionOps pci_msix_ops = { + .read = pci_msix_read, + .write = pci_msix_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +int xen_pt_msix_init(XenPCIPassthroughState *s, uint32_t base) +{ + uint8_t id = 0; + uint16_t control = 0; + uint32_t table_off = 0; + int i, total_entries, bar_index; + XenHostPCIDevice *hd = &s->real_device; + PCIDevice *d = &s->dev; + int fd = -1; + XenPTMSIX *msix = NULL; + int rc = 0; + + rc = xen_host_pci_get_byte(hd, base + PCI_CAP_LIST_ID, &id); + if (rc) { + return rc; + } + + if (id != PCI_CAP_ID_MSIX) { + XEN_PT_ERR(d, "Invalid id %#x base %#x\n", id, base); + return -1; + } + + xen_host_pci_get_word(hd, base + PCI_MSIX_FLAGS, &control); + total_entries = control & PCI_MSIX_FLAGS_QSIZE; + total_entries += 1; + + s->msix = g_malloc0(sizeof (XenPTMSIX) + + total_entries * sizeof (XenPTMSIXEntry)); + msix = s->msix; + + msix->total_entries = total_entries; + for (i = 0; i < total_entries; i++) { + msix->msix_entry[i].pirq = XEN_PT_UNASSIGNED_PIRQ; + } + + memory_region_init_io(&msix->mmio, &pci_msix_ops, s, "xen-pci-pt-msix", + (total_entries * PCI_MSIX_ENTRY_SIZE + + XC_PAGE_SIZE - 1) + & XC_PAGE_MASK); + + xen_host_pci_get_long(hd, base + PCI_MSIX_TABLE, &table_off); + bar_index = msix->bar_index = table_off & PCI_MSIX_FLAGS_BIRMASK; + table_off = table_off & ~PCI_MSIX_FLAGS_BIRMASK; + msix->table_base = s->real_device.io_regions[bar_index].base_addr; + XEN_PT_LOG(d, "get MSI-X table BAR base 0x%"PRIx64"\n", msix->table_base); + + fd = open("/dev/mem", O_RDWR); + if (fd == -1) { + rc = -errno; + XEN_PT_ERR(d, "Can't open /dev/mem: %s\n", strerror(errno)); + goto error_out; + } + XEN_PT_LOG(d, "table_off = %#x, total_entries = %d\n", + table_off, total_entries); + msix->table_offset_adjust = table_off & 0x0fff; + msix->phys_iomem_base = + mmap(NULL, + total_entries * PCI_MSIX_ENTRY_SIZE + msix->table_offset_adjust, + PROT_READ, + MAP_SHARED | MAP_LOCKED, + fd, + msix->table_base + table_off - msix->table_offset_adjust); + close(fd); + if (msix->phys_iomem_base == MAP_FAILED) { + rc = -errno; + XEN_PT_ERR(d, "Can't map physical MSI-X table: %s\n", strerror(errno)); + goto error_out; + } + msix->phys_iomem_base = (char *)msix->phys_iomem_base + + msix->table_offset_adjust; + + XEN_PT_LOG(d, "mapping physical MSI-X table to %p\n", + msix->phys_iomem_base); + + memory_region_add_subregion_overlap(&s->bar[bar_index], table_off, + &msix->mmio, + 2); /* Priority: pci default + 1 */ + + return 0; + +error_out: + memory_region_destroy(&msix->mmio); + g_free(s->msix); + s->msix = NULL; + return rc; +} + +void xen_pt_msix_delete(XenPCIPassthroughState *s) +{ + XenPTMSIX *msix = s->msix; + + if (!msix) { + return; + } + + /* unmap the MSI-X memory mapped register area */ + if (msix->phys_iomem_base) { + XEN_PT_LOG(&s->dev, "unmapping physical MSI-X table from %p\n", + msix->phys_iomem_base); + munmap(msix->phys_iomem_base, msix->total_entries * PCI_MSIX_ENTRY_SIZE + + msix->table_offset_adjust); + } + + memory_region_del_subregion(&s->bar[msix->bar_index], &msix->mmio); + memory_region_destroy(&msix->mmio); + + g_free(s->msix); + s->msix = NULL; +} diff --git a/hw/xenfb.c b/hw/xenfb.c index 1bcf171b01..338800a4d9 100644 --- a/hw/xenfb.c +++ b/hw/xenfb.c @@ -35,19 +35,16 @@ #include <string.h> #include <time.h> -#include <xs.h> -#include <xenctrl.h> -#include <xen/event_channel.h> -#include <xen/io/xenbus.h> -#include <xen/io/fbif.h> -#include <xen/io/kbdif.h> -#include <xen/io/protocols.h> - #include "hw.h" #include "console.h" #include "qemu-char.h" #include "xen_backend.h" +#include <xen/event_channel.h> +#include <xen/io/fbif.h> +#include <xen/io/kbdif.h> +#include <xen/io/protocols.h> + #ifndef BTN_LEFT #define BTN_LEFT 0x110 /* from <linux/input.h> */ #endif diff --git a/linux-headers/asm-s390/kvm.h b/linux-headers/asm-s390/kvm.h index 96076676e2..bdcbe0f8dd 100644 --- a/linux-headers/asm-s390/kvm.h +++ b/linux-headers/asm-s390/kvm.h @@ -52,4 +52,9 @@ struct kvm_sync_regs { __u32 acrs[16]; /* access registers */ __u64 crs[16]; /* control registers */ }; + +#define KVM_REG_S390_TODPR (KVM_REG_S390 | KVM_REG_SIZE_U32 | 0x1) +#define KVM_REG_S390_EPOCHDIFF (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x2) +#define KVM_REG_S390_CPU_TIMER (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x3) +#define KVM_REG_S390_CLOCK_COMP (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x4) #endif diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index c4426ec73d..5a9d4e350d 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -616,6 +616,7 @@ struct kvm_ppc_smmu_info { #define KVM_CAP_KVMCLOCK_CTRL 76 #define KVM_CAP_SIGNAL_MSI 77 #define KVM_CAP_PPC_GET_SMMU_INFO 78 +#define KVM_CAP_S390_COW 79 #ifdef KVM_CAP_IRQ_ROUTING diff --git a/linux-user/cpu-uname.c b/linux-user/cpu-uname.c index ddc37be4f9..59cd6477d5 100644 --- a/linux-user/cpu-uname.c +++ b/linux-user/cpu-uname.c @@ -35,10 +35,7 @@ const char *cpu_to_uname_machine(void *cpu_env) * armv7l; to get a list of CPU arch names from the linux source, use: * grep arch_name: -A1 linux/arch/arm/mm/proc-*.S * see arch/arm/kernel/setup.c: setup_processor() - * - * to test by CPU id, compare cpu_env->cp15.c0_cpuid to ARM_CPUID_* - * defines and to test by CPU feature, use arm_feature(cpu_env, - * ARM_FEATURE_*) */ + */ /* in theory, endianness is configurable on some ARM CPUs, but this isn't * used in user mode emulation */ diff --git a/pc-bios/mpc8544ds.dtb b/pc-bios/mpc8544ds.dtb Binary files differdeleted file mode 100644 index c6d302153c..0000000000 --- a/pc-bios/mpc8544ds.dtb +++ /dev/null diff --git a/pc-bios/mpc8544ds.dts b/pc-bios/mpc8544ds.dts deleted file mode 100644 index 7eb31604fc..0000000000 --- a/pc-bios/mpc8544ds.dts +++ /dev/null @@ -1,119 +0,0 @@ -/* - * MPC8544 DS Device Tree Source - * - * Copyright 2007, 2008 Freescale Semiconductor Inc. - * - * 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. - */ - -/dts-v1/; -/ { - model = "MPC8544DS"; - compatible = "MPC8544DS", "MPC85xxDS"; - #address-cells = <1>; - #size-cells = <1>; - - aliases { - serial0 = &serial0; - serial1 = &serial1; - pci0 = &pci0; - }; - - cpus { - #address-cells = <1>; - #size-cells = <0>; - }; - - memory { - device_type = "memory"; - reg = <0x0 0x0>; // Filled by U-Boot - }; - - soc8544@e0000000 { - #address-cells = <1>; - #size-cells = <1>; - device_type = "soc"; - compatible = "simple-bus"; - - ranges = <0x0 0xe0000000 0x100000>; - reg = <0xe0000000 0x1000>; // CCSRBAR 1M - bus-frequency = <0>; // Filled out by uboot. - - serial0: serial@4500 { - cell-index = <0>; - device_type = "serial"; - compatible = "ns16550"; - reg = <0x4500 0x100>; - clock-frequency = <0>; - interrupts = <42 2>; - interrupt-parent = <&mpic>; - }; - - serial1: serial@4600 { - cell-index = <1>; - device_type = "serial"; - compatible = "ns16550"; - reg = <0x4600 0x100>; - clock-frequency = <0>; - interrupts = <42 2>; - interrupt-parent = <&mpic>; - }; - - mpic: pic@40000 { - interrupt-controller; - #address-cells = <0>; - #interrupt-cells = <2>; - reg = <0x40000 0x40000>; - compatible = "chrp,open-pic"; - device_type = "open-pic"; - }; - - global-utilities@e0000 { //global utilities block - compatible = "fsl,mpc8544-guts"; - reg = <0xe0000 0x1000>; - fsl,has-rstcr; - }; - }; - - pci0: pci@e0008000 { - cell-index = <0>; - compatible = "fsl,mpc8540-pci"; - device_type = "pci"; - interrupt-map-mask = <0xf800 0x0 0x0 0x7>; - interrupt-map = < - - /* IDSEL 0x11 J17 Slot 1 */ - 0x8800 0x0 0x0 0x1 &mpic 0x2 0x1 - 0x8800 0x0 0x0 0x2 &mpic 0x3 0x1 - 0x8800 0x0 0x0 0x3 &mpic 0x4 0x1 - 0x8800 0x0 0x0 0x4 &mpic 0x1 0x1 - - /* IDSEL 0x12 J16 Slot 2 */ - - 0x9000 0x0 0x0 0x1 &mpic 0x3 0x1 - 0x9000 0x0 0x0 0x2 &mpic 0x4 0x1 - 0x9000 0x0 0x0 0x3 &mpic 0x2 0x1 - 0x9000 0x0 0x0 0x4 &mpic 0x1 0x1>; - - interrupt-parent = <&mpic>; - interrupts = <24 2>; - bus-range = <0 255>; - ranges = <0x2000000 0x0 0xc0000000 0xc0000000 0x0 0x20000000 - 0x1000000 0x0 0x0 0xe1000000 0x0 0x10000>; - clock-frequency = <66666666>; - #interrupt-cells = <1>; - #size-cells = <2>; - #address-cells = <3>; - reg = <0xe0008000 0x1000>; - }; - - chosen { - linux,stdout-path = "/soc8544@e0000000/serial@4500"; - }; - - hypervisor { - }; -}; diff --git a/qapi-schema-guest.json b/qapi-schema-guest.json index d4055d262a..d955cf11fb 100644 --- a/qapi-schema-guest.json +++ b/qapi-schema-guest.json @@ -351,6 +351,26 @@ 'returns': 'int' } ## +# @guest-fstrim: +# +# Discard (or "trim") blocks which are not in use by the filesystem. +# +# @minimum: +# Minimum contiguous free range to discard, in bytes. Free ranges +# smaller than this may be ignored (this is a hint and the guest +# may not respect it). By increasing this value, the fstrim +# operation will complete more quickly for filesystems with badly +# fragmented free space, although not all blocks will be discarded. +# The default value is zero, meaning "discard every free block". +# +# Returns: Nothing. +# +# Since: 1.2 +## +{ 'command': 'guest-fstrim', + 'data': { '*minimum': 'int' } } + +## # @guest-suspend-disk # # Suspend guest to disk. diff --git a/qemu-common.h b/qemu-common.h index 8f87e413a7..9d9e603c6e 100644 --- a/qemu-common.h +++ b/qemu-common.h @@ -275,6 +275,13 @@ typedef enum LostTickPolicy { LOST_TICK_MAX } LostTickPolicy; +typedef struct PCIHostDeviceAddress { + unsigned int domain; + unsigned int bus; + unsigned int slot; + unsigned int function; +} PCIHostDeviceAddress; + void tcg_exec_init(unsigned long tb_size); bool tcg_enabled(void); diff --git a/qemu-config.c b/qemu-config.c index bb3bff426a..5c3296b8c6 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -583,6 +583,18 @@ static QemuOptsList qemu_machine_opts = { .name = "dtb", .type = QEMU_OPT_STRING, .help = "Linux kernel device tree file", + }, { + .name = "dumpdtb", + .type = QEMU_OPT_STRING, + .help = "Dump current dtb to a file and quit", + }, { + .name = "phandle_start", + .type = QEMU_OPT_STRING, + .help = "The first phandle ID we may generate dynamically", + }, { + .name = "dt_compatible", + .type = QEMU_OPT_STRING, + .help = "Overrides the \"compatible\" property of the dt root node", }, { /* End of list */ } }, diff --git a/qemu-log.h b/qemu-log.h index 40f8b7b0c8..4cdc7c7a47 100644 --- a/qemu-log.h +++ b/qemu-log.h @@ -51,7 +51,8 @@ void GCC_FMT_ATTR(1, 2) qemu_log(const char *fmt, ...); /* vfprintf-like logging function */ -static inline void qemu_log_vprintf(const char *fmt, va_list va) +static inline void GCC_FMT_ATTR(1, 0) +qemu_log_vprintf(const char *fmt, va_list va) { if (qemu_logfile) { vfprintf(qemu_logfile, fmt, va); @@ -69,7 +70,9 @@ void GCC_FMT_ATTR(2, 3) qemu_log_mask(int mask, const char *fmt, ...); /* cpu_dump_state() logging functions: */ static inline void log_cpu_state(CPUArchState *env1, int flags) { - cpu_dump_state(env1, qemu_logfile, fprintf, flags); + if (qemu_log_enabled()) { + cpu_dump_state(env1, qemu_logfile, fprintf, flags); + } } static inline void log_cpu_state_mask(int mask, CPUArchState *env1, int flags) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 00d035da95..ce9042123c 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -38,9 +38,12 @@ extern char **environ; #include <sys/socket.h> #include <net/if.h> -#if defined(__linux__) && defined(FIFREEZE) +#ifdef FIFREEZE #define CONFIG_FSFREEZE #endif +#ifdef FITRIM +#define CONFIG_FSTRIM +#endif #endif void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err) @@ -312,19 +315,18 @@ static void guest_file_init(void) /* linux-specific implementations. avoid this if at all possible. */ #if defined(__linux__) -#if defined(CONFIG_FSFREEZE) - -typedef struct GuestFsfreezeMount { +#if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) +typedef struct FsMount { char *dirname; char *devtype; - QTAILQ_ENTRY(GuestFsfreezeMount) next; -} GuestFsfreezeMount; + QTAILQ_ENTRY(FsMount) next; +} FsMount; -typedef QTAILQ_HEAD(, GuestFsfreezeMount) GuestFsfreezeMountList; +typedef QTAILQ_HEAD(, FsMount) FsMountList; -static void guest_fsfreeze_free_mount_list(GuestFsfreezeMountList *mounts) +static void free_fs_mount_list(FsMountList *mounts) { - GuestFsfreezeMount *mount, *temp; + FsMount *mount, *temp; if (!mounts) { return; @@ -341,10 +343,10 @@ static void guest_fsfreeze_free_mount_list(GuestFsfreezeMountList *mounts) /* * Walk the mount table and build a list of local file systems */ -static int guest_fsfreeze_build_mount_list(GuestFsfreezeMountList *mounts) +static int build_fs_mount_list(FsMountList *mounts) { struct mntent *ment; - GuestFsfreezeMount *mount; + FsMount *mount; char const *mtab = "/proc/self/mounts"; FILE *fp; @@ -367,7 +369,7 @@ static int guest_fsfreeze_build_mount_list(GuestFsfreezeMountList *mounts) continue; } - mount = g_malloc0(sizeof(GuestFsfreezeMount)); + mount = g_malloc0(sizeof(FsMount)); mount->dirname = g_strdup(ment->mnt_dir); mount->devtype = g_strdup(ment->mnt_type); @@ -378,6 +380,9 @@ static int guest_fsfreeze_build_mount_list(GuestFsfreezeMountList *mounts) return 0; } +#endif + +#if defined(CONFIG_FSFREEZE) /* * Return status of freeze/thaw @@ -398,15 +403,15 @@ GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) int64_t qmp_guest_fsfreeze_freeze(Error **err) { int ret = 0, i = 0; - GuestFsfreezeMountList mounts; - struct GuestFsfreezeMount *mount; + FsMountList mounts; + struct FsMount *mount; int fd; char err_msg[512]; slog("guest-fsfreeze called"); QTAILQ_INIT(&mounts); - ret = guest_fsfreeze_build_mount_list(&mounts); + ret = build_fs_mount_list(&mounts); if (ret < 0) { return ret; } @@ -447,11 +452,11 @@ int64_t qmp_guest_fsfreeze_freeze(Error **err) close(fd); } - guest_fsfreeze_free_mount_list(&mounts); + free_fs_mount_list(&mounts); return i; error: - guest_fsfreeze_free_mount_list(&mounts); + free_fs_mount_list(&mounts); qmp_guest_fsfreeze_thaw(NULL); return 0; } @@ -462,12 +467,12 @@ error: int64_t qmp_guest_fsfreeze_thaw(Error **err) { int ret; - GuestFsfreezeMountList mounts; - GuestFsfreezeMount *mount; + FsMountList mounts; + FsMount *mount; int fd, i = 0, logged; QTAILQ_INIT(&mounts); - ret = guest_fsfreeze_build_mount_list(&mounts); + ret = build_fs_mount_list(&mounts); if (ret) { error_set(err, QERR_QGA_COMMAND_FAILED, "failed to enumerate filesystems"); @@ -507,7 +512,7 @@ int64_t qmp_guest_fsfreeze_thaw(Error **err) } ga_unset_frozen(ga_state); - guest_fsfreeze_free_mount_list(&mounts); + free_fs_mount_list(&mounts); return i; } @@ -525,6 +530,65 @@ static void guest_fsfreeze_cleanup(void) } #endif /* CONFIG_FSFREEZE */ +#if defined(CONFIG_FSTRIM) +/* + * Walk list of mounted file systems in the guest, and trim them. + */ +void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **err) +{ + int ret = 0; + FsMountList mounts; + struct FsMount *mount; + int fd; + char err_msg[512]; + struct fstrim_range r = { + .start = 0, + .len = -1, + .minlen = has_minimum ? minimum : 0, + }; + + slog("guest-fstrim called"); + + QTAILQ_INIT(&mounts); + ret = build_fs_mount_list(&mounts); + if (ret < 0) { + return; + } + + QTAILQ_FOREACH(mount, &mounts, next) { + fd = qemu_open(mount->dirname, O_RDONLY); + if (fd == -1) { + sprintf(err_msg, "failed to open %s, %s", mount->dirname, + strerror(errno)); + error_set(err, QERR_QGA_COMMAND_FAILED, err_msg); + goto error; + } + + /* We try to cull filesytems we know won't work in advance, but other + * filesytems may not implement fstrim for less obvious reasons. These + * will report EOPNOTSUPP; we simply ignore these errors. Any other + * error means an unexpected error, so return it in those cases. In + * some other cases ENOTTY will be reported (e.g. CD-ROMs). + */ + ret = ioctl(fd, FITRIM, &r); + if (ret == -1) { + if (errno != ENOTTY && errno != EOPNOTSUPP) { + sprintf(err_msg, "failed to trim %s, %s", + mount->dirname, strerror(errno)); + error_set(err, QERR_QGA_COMMAND_FAILED, err_msg); + close(fd); + goto error; + } + } + close(fd); + } + +error: + free_fs_mount_list(&mounts); +} +#endif /* CONFIG_FSTRIM */ + + #define LINUX_SYS_STATE_FILE "/sys/power/state" #define SUSPEND_SUPPORTED 0 #define SUSPEND_NOT_SUPPORTED 1 @@ -918,7 +982,15 @@ int64_t qmp_guest_fsfreeze_thaw(Error **err) return 0; } +#endif /* CONFIG_FSFREEZE */ +#if !defined(CONFIG_FSTRIM) +void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **err) +{ + error_set(err, QERR_UNSUPPORTED); + + return; +} #endif /* register init/cleanup routines for stateful command groups */ diff --git a/qga/commands-win32.c b/qga/commands-win32.c index eb8d1405d3..54bc5462e2 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -173,6 +173,17 @@ int64_t qmp_guest_fsfreeze_thaw(Error **err) return 0; } +/* + * Walk list of mounted file systems in the guest, and discard unused + * areas. + */ +void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **err) +{ + error_set(err, QERR_UNSUPPORTED); + + return; +} + typedef enum { GUEST_SUSPEND_MODE_DISK, GUEST_SUSPEND_MODE_RAM diff --git a/scripts/kvm/kvm_stat b/scripts/kvm/kvm_stat index 56d2bd7f21..e8d68f05ca 100755 --- a/scripts/kvm/kvm_stat +++ b/scripts/kvm/kvm_stat @@ -141,15 +141,39 @@ svm_exit_reasons = { 0x400: 'NPF', } +s390_exit_reasons = { + 0x000: 'UNKNOWN', + 0x001: 'EXCEPTION', + 0x002: 'IO', + 0x003: 'HYPERCALL', + 0x004: 'DEBUG', + 0x005: 'HLT', + 0x006: 'MMIO', + 0x007: 'IRQ_WINDOW_OPEN', + 0x008: 'SHUTDOWN', + 0x009: 'FAIL_ENTRY', + 0x010: 'INTR', + 0x011: 'SET_TPR', + 0x012: 'TPR_ACCESS', + 0x013: 'S390_SIEIC', + 0x014: 'S390_RESET', + 0x015: 'DCR', + 0x016: 'NMI', + 0x017: 'INTERNAL_ERROR', + 0x018: 'OSI', + 0x019: 'PAPR_HCALL', +} + vendor_exit_reasons = { 'vmx': vmx_exit_reasons, 'svm': svm_exit_reasons, + 'IBM/S390': s390_exit_reasons, } exit_reasons = None for line in file('/proc/cpuinfo').readlines(): - if line.startswith('flags'): + if line.startswith('flags') or line.startswith('vendor_id'): for flag in line.split(): if flag in vendor_exit_reasons: exit_reasons = vendor_exit_reasons[flag] diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h index a61c68d21b..beabf9a0a9 100644 --- a/target-arm/cpu-qom.h +++ b/target-arm/cpu-qom.h @@ -58,6 +58,9 @@ typedef struct ARMCPU { CPUARMState env; + /* Coprocessor information */ + GHashTable *cp_regs; + /* The instance init functions for implementation-specific subclasses * set these fields to specify the implementation-dependent values of * various constant registers and reset values of non-constant @@ -94,6 +97,7 @@ typedef struct ARMCPU { */ uint32_t ccsidr[16]; uint32_t reset_cbar; + uint32_t reset_auxcr; } ARMCPU; static inline ARMCPU *arm_env_get_cpu(CPUARMState *env) @@ -104,5 +108,6 @@ static inline ARMCPU *arm_env_get_cpu(CPUARMState *env) #define ENV_GET_CPU(e) CPU(arm_env_get_cpu(e)) void arm_cpu_realize(ARMCPU *cpu); +void register_cp_regs_for_features(ARMCPU *cpu); #endif diff --git a/target-arm/cpu.c b/target-arm/cpu.c index 7eb323ae4d..ae5795337f 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -23,6 +23,38 @@ #if !defined(CONFIG_USER_ONLY) #include "hw/loader.h" #endif +#include "sysemu.h" + +static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque) +{ + /* Reset a single ARMCPRegInfo register */ + ARMCPRegInfo *ri = value; + ARMCPU *cpu = opaque; + + if (ri->type & ARM_CP_SPECIAL) { + return; + } + + if (ri->resetfn) { + ri->resetfn(&cpu->env, ri); + return; + } + + /* A zero offset is never possible as it would be regs[0] + * so we use it to indicate that reset is being handled elsewhere. + * This is basically only used for fields in non-core coprocessors + * (like the pxa2xx ones). + */ + if (!ri->fieldoffset) { + return; + } + + if (ri->type & ARM_CP_64BIT) { + CPREG_FIELD64(&cpu->env, ri) = ri->resetvalue; + } else { + CPREG_FIELD32(&cpu->env, ri) = ri->resetvalue; + } +} /* CPUClass::reset() */ static void arm_cpu_reset(CPUState *s) @@ -39,30 +71,10 @@ static void arm_cpu_reset(CPUState *s) acc->parent_reset(s); memset(env, 0, offsetof(CPUARMState, breakpoints)); - env->cp15.c15_config_base_address = cpu->reset_cbar; - env->cp15.c0_cpuid = cpu->midr; + g_hash_table_foreach(cpu->cp_regs, cp_reg_reset, cpu); env->vfp.xregs[ARM_VFP_FPSID] = cpu->reset_fpsid; env->vfp.xregs[ARM_VFP_MVFR0] = cpu->mvfr0; env->vfp.xregs[ARM_VFP_MVFR1] = cpu->mvfr1; - env->cp15.c0_cachetype = cpu->ctr; - env->cp15.c1_sys = cpu->reset_sctlr; - env->cp15.c0_c1[0] = cpu->id_pfr0; - env->cp15.c0_c1[1] = cpu->id_pfr1; - env->cp15.c0_c1[2] = cpu->id_dfr0; - env->cp15.c0_c1[3] = cpu->id_afr0; - env->cp15.c0_c1[4] = cpu->id_mmfr0; - env->cp15.c0_c1[5] = cpu->id_mmfr1; - env->cp15.c0_c1[6] = cpu->id_mmfr2; - env->cp15.c0_c1[7] = cpu->id_mmfr3; - env->cp15.c0_c2[0] = cpu->id_isar0; - env->cp15.c0_c2[1] = cpu->id_isar1; - env->cp15.c0_c2[2] = cpu->id_isar2; - env->cp15.c0_c2[3] = cpu->id_isar3; - env->cp15.c0_c2[4] = cpu->id_isar4; - env->cp15.c0_c2[5] = cpu->id_isar5; - env->cp15.c15_i_min = 0xff0; - env->cp15.c0_clid = cpu->clidr; - memcpy(env->cp15.c0_ccsid, cpu->ccsidr, ARRAY_SIZE(cpu->ccsidr)); if (arm_feature(env, ARM_FEATURE_IWMMXT)) { env->iwmmxt.cregs[ARM_IWMMXT_wCID] = 0x69051000 | 'Q'; @@ -99,11 +111,6 @@ static void arm_cpu_reset(CPUState *s) } } env->vfp.xregs[ARM_VFP_FPEXC] = 0; - env->cp15.c2_base_mask = 0xffffc000u; - /* v7 performance monitor control register: same implementor - * field as main ID register, and we implement no event counters. - */ - env->cp15.c9_pmcr = (cpu->midr & 0xff000000); #endif set_flush_to_zero(1, &env->vfp.standard_fp_status); set_flush_inputs_to_zero(1, &env->vfp.standard_fp_status); @@ -130,6 +137,14 @@ static void arm_cpu_initfn(Object *obj) ARMCPU *cpu = ARM_CPU(obj); cpu_exec_init(&cpu->env); + cpu->cp_regs = g_hash_table_new_full(g_int_hash, g_int_equal, + g_free, g_free); +} + +static void arm_cpu_finalizefn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + g_hash_table_destroy(cpu->cp_regs); } void arm_cpu_realize(ARMCPU *cpu) @@ -145,6 +160,7 @@ void arm_cpu_realize(ARMCPU *cpu) if (arm_feature(env, ARM_FEATURE_V7)) { set_feature(env, ARM_FEATURE_VAPA); set_feature(env, ARM_FEATURE_THUMB2); + set_feature(env, ARM_FEATURE_MPIDR); if (!arm_feature(env, ARM_FEATURE_M)) { set_feature(env, ARM_FEATURE_V6K); } else { @@ -176,6 +192,8 @@ void arm_cpu_realize(ARMCPU *cpu) if (arm_feature(env, ARM_FEATURE_VFP3)) { set_feature(env, ARM_FEATURE_VFP); } + + register_cp_regs_for_features(cpu); } /* CPU models */ @@ -185,7 +203,9 @@ static void arm926_initfn(Object *obj) ARMCPU *cpu = ARM_CPU(obj); set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_VFP); - cpu->midr = ARM_CPUID_ARM926; + set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); + set_feature(&cpu->env, ARM_FEATURE_CACHE_TEST_CLEAN); + cpu->midr = 0x41069265; cpu->reset_fpsid = 0x41011090; cpu->ctr = 0x1dd20d2; cpu->reset_sctlr = 0x00090078; @@ -196,7 +216,8 @@ static void arm946_initfn(Object *obj) ARMCPU *cpu = ARM_CPU(obj); set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_MPU); - cpu->midr = ARM_CPUID_ARM946; + set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); + cpu->midr = 0x41059461; cpu->ctr = 0x0f004006; cpu->reset_sctlr = 0x00000078; } @@ -207,10 +228,23 @@ static void arm1026_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_VFP); set_feature(&cpu->env, ARM_FEATURE_AUXCR); - cpu->midr = ARM_CPUID_ARM1026; + set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); + set_feature(&cpu->env, ARM_FEATURE_CACHE_TEST_CLEAN); + cpu->midr = 0x4106a262; cpu->reset_fpsid = 0x410110a0; cpu->ctr = 0x1dd20d2; cpu->reset_sctlr = 0x00090078; + cpu->reset_auxcr = 1; + { + /* The 1026 had an IFAR at c6,c0,0,1 rather than the ARMv6 c6,c0,0,2 */ + ARMCPRegInfo ifar = { + .name = "IFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 1, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.c6_insn), + .resetvalue = 0 + }; + define_one_arm_cp_reg(cpu, &ifar); + } } static void arm1136_r2_initfn(Object *obj) @@ -225,7 +259,10 @@ static void arm1136_r2_initfn(Object *obj) */ set_feature(&cpu->env, ARM_FEATURE_V6); set_feature(&cpu->env, ARM_FEATURE_VFP); - cpu->midr = ARM_CPUID_ARM1136_R2; + set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); + set_feature(&cpu->env, ARM_FEATURE_CACHE_DIRTY_REG); + set_feature(&cpu->env, ARM_FEATURE_CACHE_BLOCK_OPS); + cpu->midr = 0x4107b362; cpu->reset_fpsid = 0x410120b4; cpu->mvfr0 = 0x11111111; cpu->mvfr1 = 0x00000000; @@ -243,6 +280,7 @@ static void arm1136_r2_initfn(Object *obj) cpu->id_isar2 = 0x11231111; cpu->id_isar3 = 0x01102131; cpu->id_isar4 = 0x141; + cpu->reset_auxcr = 7; } static void arm1136_initfn(Object *obj) @@ -251,7 +289,10 @@ static void arm1136_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V6K); set_feature(&cpu->env, ARM_FEATURE_V6); set_feature(&cpu->env, ARM_FEATURE_VFP); - cpu->midr = ARM_CPUID_ARM1136; + set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); + set_feature(&cpu->env, ARM_FEATURE_CACHE_DIRTY_REG); + set_feature(&cpu->env, ARM_FEATURE_CACHE_BLOCK_OPS); + cpu->midr = 0x4117b363; cpu->reset_fpsid = 0x410120b4; cpu->mvfr0 = 0x11111111; cpu->mvfr1 = 0x00000000; @@ -269,6 +310,7 @@ static void arm1136_initfn(Object *obj) cpu->id_isar2 = 0x11231111; cpu->id_isar3 = 0x01102131; cpu->id_isar4 = 0x141; + cpu->reset_auxcr = 7; } static void arm1176_initfn(Object *obj) @@ -277,7 +319,10 @@ static void arm1176_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V6K); set_feature(&cpu->env, ARM_FEATURE_VFP); set_feature(&cpu->env, ARM_FEATURE_VAPA); - cpu->midr = ARM_CPUID_ARM1176; + set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); + set_feature(&cpu->env, ARM_FEATURE_CACHE_DIRTY_REG); + set_feature(&cpu->env, ARM_FEATURE_CACHE_BLOCK_OPS); + cpu->midr = 0x410fb767; cpu->reset_fpsid = 0x410120b5; cpu->mvfr0 = 0x11111111; cpu->mvfr1 = 0x00000000; @@ -295,6 +340,7 @@ static void arm1176_initfn(Object *obj) cpu->id_isar2 = 0x11231121; cpu->id_isar3 = 0x01102131; cpu->id_isar4 = 0x01141; + cpu->reset_auxcr = 7; } static void arm11mpcore_initfn(Object *obj) @@ -303,11 +349,13 @@ static void arm11mpcore_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V6K); set_feature(&cpu->env, ARM_FEATURE_VFP); set_feature(&cpu->env, ARM_FEATURE_VAPA); - cpu->midr = ARM_CPUID_ARM11MPCORE; + set_feature(&cpu->env, ARM_FEATURE_MPIDR); + set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); + cpu->midr = 0x410fb022; cpu->reset_fpsid = 0x410120b4; cpu->mvfr0 = 0x11111111; cpu->mvfr1 = 0x00000000; - cpu->ctr = 0x1dd20d2; + cpu->ctr = 0x1d192992; /* 32K icache 32K dcache */ cpu->id_pfr0 = 0x111; cpu->id_pfr1 = 0x1; cpu->id_dfr0 = 0; @@ -320,6 +368,7 @@ static void arm11mpcore_initfn(Object *obj) cpu->id_isar2 = 0x11221011; cpu->id_isar3 = 0x01102131; cpu->id_isar4 = 0x141; + cpu->reset_auxcr = 1; } static void cortex_m3_initfn(Object *obj) @@ -327,9 +376,17 @@ static void cortex_m3_initfn(Object *obj) ARMCPU *cpu = ARM_CPU(obj); set_feature(&cpu->env, ARM_FEATURE_V7); set_feature(&cpu->env, ARM_FEATURE_M); - cpu->midr = ARM_CPUID_CORTEXM3; + cpu->midr = 0x410fc231; } +static const ARMCPRegInfo cortexa8_cp_reginfo[] = { + { .name = "L2LOCKDOWN", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "L2AUXCR", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 2, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + REGINFO_SENTINEL +}; + static void cortex_a8_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -337,7 +394,8 @@ static void cortex_a8_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_VFP3); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); - cpu->midr = ARM_CPUID_CORTEXA8; + set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); + cpu->midr = 0x410fc080; cpu->reset_fpsid = 0x410330c0; cpu->mvfr0 = 0x11110222; cpu->mvfr1 = 0x00011100; @@ -360,8 +418,39 @@ static void cortex_a8_initfn(Object *obj) cpu->ccsidr[0] = 0xe007e01a; /* 16k L1 dcache. */ cpu->ccsidr[1] = 0x2007e01a; /* 16k L1 icache. */ cpu->ccsidr[2] = 0xf0000000; /* No L2 icache. */ + cpu->reset_auxcr = 2; + define_arm_cp_regs(cpu, cortexa8_cp_reginfo); } +static const ARMCPRegInfo cortexa9_cp_reginfo[] = { + /* power_control should be set to maximum latency. Again, + * default to 0 and set by private hook + */ + { .name = "A9_PWRCTL", .cp = 15, .crn = 15, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .resetvalue = 0, + .fieldoffset = offsetof(CPUARMState, cp15.c15_power_control) }, + { .name = "A9_DIAG", .cp = 15, .crn = 15, .crm = 0, .opc1 = 0, .opc2 = 1, + .access = PL1_RW, .resetvalue = 0, + .fieldoffset = offsetof(CPUARMState, cp15.c15_diagnostic) }, + { .name = "A9_PWRDIAG", .cp = 15, .crn = 15, .crm = 0, .opc1 = 0, .opc2 = 2, + .access = PL1_RW, .resetvalue = 0, + .fieldoffset = offsetof(CPUARMState, cp15.c15_power_diagnostic) }, + { .name = "NEONBUSY", .cp = 15, .crn = 15, .crm = 1, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST }, + /* TLB lockdown control */ + { .name = "TLB_LOCKR", .cp = 15, .crn = 15, .crm = 4, .opc1 = 5, .opc2 = 2, + .access = PL1_W, .resetvalue = 0, .type = ARM_CP_NOP }, + { .name = "TLB_LOCKW", .cp = 15, .crn = 15, .crm = 4, .opc1 = 5, .opc2 = 4, + .access = PL1_W, .resetvalue = 0, .type = ARM_CP_NOP }, + { .name = "TLB_VA", .cp = 15, .crn = 15, .crm = 5, .opc1 = 5, .opc2 = 2, + .access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST }, + { .name = "TLB_PA", .cp = 15, .crn = 15, .crm = 6, .opc1 = 5, .opc2 = 2, + .access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST }, + { .name = "TLB_ATTR", .cp = 15, .crn = 15, .crm = 7, .opc1 = 5, .opc2 = 2, + .access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST }, + REGINFO_SENTINEL +}; + static void cortex_a9_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -375,7 +464,7 @@ static void cortex_a9_initfn(Object *obj) * and valid configurations; we don't model A9UP). */ set_feature(&cpu->env, ARM_FEATURE_V7MP); - cpu->midr = ARM_CPUID_CORTEXA9; + cpu->midr = 0x410fc090; cpu->reset_fpsid = 0x41033090; cpu->mvfr0 = 0x11110222; cpu->mvfr1 = 0x01111111; @@ -397,8 +486,40 @@ static void cortex_a9_initfn(Object *obj) cpu->clidr = (1 << 27) | (1 << 24) | 3; cpu->ccsidr[0] = 0xe00fe015; /* 16k L1 dcache. */ cpu->ccsidr[1] = 0x200fe015; /* 16k L1 icache. */ + { + ARMCPRegInfo cbar = { + .name = "CBAR", .cp = 15, .crn = 15, .crm = 0, .opc1 = 4, + .opc2 = 0, .access = PL1_R|PL3_W, .resetvalue = cpu->reset_cbar, + .fieldoffset = offsetof(CPUARMState, cp15.c15_config_base_address) + }; + define_one_arm_cp_reg(cpu, &cbar); + define_arm_cp_regs(cpu, cortexa9_cp_reginfo); + } } +#ifndef CONFIG_USER_ONLY +static int a15_l2ctlr_read(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t *value) +{ + /* Linux wants the number of processors from here. + * Might as well set the interrupt-controller bit too. + */ + *value = ((smp_cpus - 1) << 24) | (1 << 23); + return 0; +} +#endif + +static const ARMCPRegInfo cortexa15_cp_reginfo[] = { +#ifndef CONFIG_USER_ONLY + { .name = "L2CTLR", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 2, + .access = PL1_RW, .resetvalue = 0, .readfn = a15_l2ctlr_read, + .writefn = arm_cp_write_ignore, }, +#endif + { .name = "L2ECTLR", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 3, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + REGINFO_SENTINEL +}; + static void cortex_a15_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -410,7 +531,8 @@ static void cortex_a15_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_ARM_DIV); set_feature(&cpu->env, ARM_FEATURE_V7MP); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); - cpu->midr = ARM_CPUID_CORTEXA15; + set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); + cpu->midr = 0x412fc0f1; cpu->reset_fpsid = 0x410430f0; cpu->mvfr0 = 0x10110222; cpu->mvfr1 = 0x11111111; @@ -433,6 +555,7 @@ static void cortex_a15_initfn(Object *obj) cpu->ccsidr[0] = 0x701fe00a; /* 32K L1 dcache */ cpu->ccsidr[1] = 0x201fe00a; /* 32K L1 icache */ cpu->ccsidr[2] = 0x711fe07a; /* 4096K L2 unified cache */ + define_arm_cp_regs(cpu, cortexa15_cp_reginfo); } static void ti925t_initfn(Object *obj) @@ -449,7 +572,8 @@ static void sa1100_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); set_feature(&cpu->env, ARM_FEATURE_STRONGARM); - cpu->midr = ARM_CPUID_SA1100; + set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); + cpu->midr = 0x4401A11B; cpu->reset_sctlr = 0x00000070; } @@ -457,7 +581,8 @@ static void sa1110_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); set_feature(&cpu->env, ARM_FEATURE_STRONGARM); - cpu->midr = ARM_CPUID_SA1110; + set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); + cpu->midr = 0x6901B119; cpu->reset_sctlr = 0x00000070; } @@ -466,7 +591,7 @@ static void pxa250_initfn(Object *obj) ARMCPU *cpu = ARM_CPU(obj); set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_XSCALE); - cpu->midr = ARM_CPUID_PXA250; + cpu->midr = 0x69052100; cpu->ctr = 0xd172172; cpu->reset_sctlr = 0x00000078; } @@ -476,7 +601,7 @@ static void pxa255_initfn(Object *obj) ARMCPU *cpu = ARM_CPU(obj); set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_XSCALE); - cpu->midr = ARM_CPUID_PXA255; + cpu->midr = 0x69052d00; cpu->ctr = 0xd172172; cpu->reset_sctlr = 0x00000078; } @@ -486,7 +611,7 @@ static void pxa260_initfn(Object *obj) ARMCPU *cpu = ARM_CPU(obj); set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_XSCALE); - cpu->midr = ARM_CPUID_PXA260; + cpu->midr = 0x69052903; cpu->ctr = 0xd172172; cpu->reset_sctlr = 0x00000078; } @@ -496,7 +621,7 @@ static void pxa261_initfn(Object *obj) ARMCPU *cpu = ARM_CPU(obj); set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_XSCALE); - cpu->midr = ARM_CPUID_PXA261; + cpu->midr = 0x69052d05; cpu->ctr = 0xd172172; cpu->reset_sctlr = 0x00000078; } @@ -506,7 +631,7 @@ static void pxa262_initfn(Object *obj) ARMCPU *cpu = ARM_CPU(obj); set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_XSCALE); - cpu->midr = ARM_CPUID_PXA262; + cpu->midr = 0x69052d06; cpu->ctr = 0xd172172; cpu->reset_sctlr = 0x00000078; } @@ -517,7 +642,7 @@ static void pxa270a0_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_XSCALE); set_feature(&cpu->env, ARM_FEATURE_IWMMXT); - cpu->midr = ARM_CPUID_PXA270_A0; + cpu->midr = 0x69054110; cpu->ctr = 0xd172172; cpu->reset_sctlr = 0x00000078; } @@ -528,7 +653,7 @@ static void pxa270a1_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_XSCALE); set_feature(&cpu->env, ARM_FEATURE_IWMMXT); - cpu->midr = ARM_CPUID_PXA270_A1; + cpu->midr = 0x69054111; cpu->ctr = 0xd172172; cpu->reset_sctlr = 0x00000078; } @@ -539,7 +664,7 @@ static void pxa270b0_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_XSCALE); set_feature(&cpu->env, ARM_FEATURE_IWMMXT); - cpu->midr = ARM_CPUID_PXA270_B0; + cpu->midr = 0x69054112; cpu->ctr = 0xd172172; cpu->reset_sctlr = 0x00000078; } @@ -550,7 +675,7 @@ static void pxa270b1_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_XSCALE); set_feature(&cpu->env, ARM_FEATURE_IWMMXT); - cpu->midr = ARM_CPUID_PXA270_B1; + cpu->midr = 0x69054113; cpu->ctr = 0xd172172; cpu->reset_sctlr = 0x00000078; } @@ -561,7 +686,7 @@ static void pxa270c0_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_XSCALE); set_feature(&cpu->env, ARM_FEATURE_IWMMXT); - cpu->midr = ARM_CPUID_PXA270_C0; + cpu->midr = 0x69054114; cpu->ctr = 0xd172172; cpu->reset_sctlr = 0x00000078; } @@ -572,7 +697,7 @@ static void pxa270c5_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_XSCALE); set_feature(&cpu->env, ARM_FEATURE_IWMMXT); - cpu->midr = ARM_CPUID_PXA270_C5; + cpu->midr = 0x69054117; cpu->ctr = 0xd172172; cpu->reset_sctlr = 0x00000078; } @@ -587,7 +712,7 @@ static void arm_any_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_ARM_DIV); set_feature(&cpu->env, ARM_FEATURE_V7MP); - cpu->midr = ARM_CPUID_ANY; + cpu->midr = 0xffffffff; } typedef struct ARMCPUInfo { @@ -657,6 +782,7 @@ static const TypeInfo arm_cpu_type_info = { .parent = TYPE_CPU, .instance_size = sizeof(ARMCPU), .instance_init = arm_cpu_initfn, + .instance_finalize = arm_cpu_finalizefn, .abstract = true, .class_size = sizeof(ARMCPUClass), .class_init = arm_cpu_class_init, diff --git a/target-arm/cpu.h b/target-arm/cpu.h index d01285fd57..33afa185e9 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -107,12 +107,7 @@ typedef struct CPUARMState { /* System control coprocessor (cp15) */ struct { uint32_t c0_cpuid; - uint32_t c0_cachetype; - uint32_t c0_ccsid[16]; /* Cache size. */ - uint32_t c0_clid; /* Cache level. */ uint32_t c0_cssel; /* Cache size selection. */ - uint32_t c0_c1[8]; /* Feature registers. */ - uint32_t c0_c2[8]; /* Instruction set registers. */ uint32_t c1_sys; /* System control register. */ uint32_t c1_coproc; /* Coprocessor access register. */ uint32_t c1_xscaleauxcr; /* XScale auxiliary control register. */ @@ -228,12 +223,6 @@ typedef struct CPUARMState { /* Internal CPU feature flags. */ uint32_t features; - /* Coprocessor IO used by peripherals */ - struct { - ARMReadCPFunc *cp_read; - ARMWriteCPFunc *cp_write; - void *opaque; - } cp[15]; void *nvic; const struct arm_boot_info *boot_info; } CPUARMState; @@ -392,6 +381,11 @@ enum arm_features { ARM_FEATURE_VFP4, /* VFPv4 (implies that NEON is v2) */ ARM_FEATURE_GENERIC_TIMER, ARM_FEATURE_MVFR, /* Media and VFP Feature Registers 0 and 1 */ + ARM_FEATURE_DUMMY_C15_REGS, /* RAZ/WI all of cp15 crn=15 */ + ARM_FEATURE_CACHE_TEST_CLEAN, /* 926/1026 style test-and-clean ops */ + ARM_FEATURE_CACHE_DIRTY_REG, /* 1136/1176 cache dirty status register */ + ARM_FEATURE_CACHE_BLOCK_OPS, /* v6 optional cache block operations */ + ARM_FEATURE_MPIDR, /* has cp15 MPIDR */ }; static inline int arm_feature(CPUARMState *env, int feature) @@ -406,45 +400,215 @@ void armv7m_nvic_set_pending(void *opaque, int irq); int armv7m_nvic_acknowledge_irq(void *opaque); void armv7m_nvic_complete_irq(void *opaque, int irq); -void cpu_arm_set_cp_io(CPUARMState *env, int cpnum, - ARMReadCPFunc *cp_read, ARMWriteCPFunc *cp_write, - void *opaque); +/* Interface for defining coprocessor registers. + * Registers are defined in tables of arm_cp_reginfo structs + * which are passed to define_arm_cp_regs(). + */ + +/* When looking up a coprocessor register we look for it + * via an integer which encodes all of: + * coprocessor number + * Crn, Crm, opc1, opc2 fields + * 32 or 64 bit register (ie is it accessed via MRC/MCR + * or via MRRC/MCRR?) + * We allow 4 bits for opc1 because MRRC/MCRR have a 4 bit field. + * (In this case crn and opc2 should be zero.) + */ +#define ENCODE_CP_REG(cp, is64, crn, crm, opc1, opc2) \ + (((cp) << 16) | ((is64) << 15) | ((crn) << 11) | \ + ((crm) << 7) | ((opc1) << 3) | (opc2)) + +#define DECODE_CPREG_CRN(enc) (((enc) >> 7) & 0xf) + +/* ARMCPRegInfo type field bits. If the SPECIAL bit is set this is a + * special-behaviour cp reg and bits [15..8] indicate what behaviour + * it has. Otherwise it is a simple cp reg, where CONST indicates that + * TCG can assume the value to be constant (ie load at translate time) + * and 64BIT indicates a 64 bit wide coprocessor register. SUPPRESS_TB_END + * indicates that the TB should not be ended after a write to this register + * (the default is that the TB ends after cp writes). OVERRIDE permits + * a register definition to override a previous definition for the + * same (cp, is64, crn, crm, opc1, opc2) tuple: either the new or the + * old must have the OVERRIDE bit set. + */ +#define ARM_CP_SPECIAL 1 +#define ARM_CP_CONST 2 +#define ARM_CP_64BIT 4 +#define ARM_CP_SUPPRESS_TB_END 8 +#define ARM_CP_OVERRIDE 16 +#define ARM_CP_NOP (ARM_CP_SPECIAL | (1 << 8)) +#define ARM_CP_WFI (ARM_CP_SPECIAL | (2 << 8)) +#define ARM_LAST_SPECIAL ARM_CP_WFI +/* Used only as a terminator for ARMCPRegInfo lists */ +#define ARM_CP_SENTINEL 0xffff +/* Mask of only the flag bits in a type field */ +#define ARM_CP_FLAG_MASK 0x1f + +/* Return true if cptype is a valid type field. This is used to try to + * catch errors where the sentinel has been accidentally left off the end + * of a list of registers. + */ +static inline bool cptype_valid(int cptype) +{ + return ((cptype & ~ARM_CP_FLAG_MASK) == 0) + || ((cptype & ARM_CP_SPECIAL) && + (cptype <= ARM_LAST_SPECIAL)); +} + +/* Access rights: + * We define bits for Read and Write access for what rev C of the v7-AR ARM ARM + * defines as PL0 (user), PL1 (fiq/irq/svc/abt/und/sys, ie privileged), and + * PL2 (hyp). The other level which has Read and Write bits is Secure PL1 + * (ie any of the privileged modes in Secure state, or Monitor mode). + * If a register is accessible in one privilege level it's always accessible + * in higher privilege levels too. Since "Secure PL1" also follows this rule + * (ie anything visible in PL2 is visible in S-PL1, some things are only + * visible in S-PL1) but "Secure PL1" is a bit of a mouthful, we bend the + * terminology a little and call this PL3. + * + * If access permissions for a register are more complex than can be + * described with these bits, then use a laxer set of restrictions, and + * do the more restrictive/complex check inside a helper function. + */ +#define PL3_R 0x80 +#define PL3_W 0x40 +#define PL2_R (0x20 | PL3_R) +#define PL2_W (0x10 | PL3_W) +#define PL1_R (0x08 | PL2_R) +#define PL1_W (0x04 | PL2_W) +#define PL0_R (0x02 | PL1_R) +#define PL0_W (0x01 | PL1_W) + +#define PL3_RW (PL3_R | PL3_W) +#define PL2_RW (PL2_R | PL2_W) +#define PL1_RW (PL1_R | PL1_W) +#define PL0_RW (PL0_R | PL0_W) + +static inline int arm_current_pl(CPUARMState *env) +{ + if ((env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_USR) { + return 0; + } + /* We don't currently implement the Virtualization or TrustZone + * extensions, so PL2 and PL3 don't exist for us. + */ + return 1; +} + +typedef struct ARMCPRegInfo ARMCPRegInfo; + +/* Access functions for coprocessor registers. These should return + * 0 on success, or one of the EXCP_* constants if access should cause + * an exception (in which case *value is not written). + */ +typedef int CPReadFn(CPUARMState *env, const ARMCPRegInfo *opaque, + uint64_t *value); +typedef int CPWriteFn(CPUARMState *env, const ARMCPRegInfo *opaque, + uint64_t value); +/* Hook function for register reset */ +typedef void CPResetFn(CPUARMState *env, const ARMCPRegInfo *opaque); + +#define CP_ANY 0xff + +/* Definition of an ARM coprocessor register */ +struct ARMCPRegInfo { + /* Name of register (useful mainly for debugging, need not be unique) */ + const char *name; + /* Location of register: coprocessor number and (crn,crm,opc1,opc2) + * tuple. Any of crm, opc1 and opc2 may be CP_ANY to indicate a + * 'wildcard' field -- any value of that field in the MRC/MCR insn + * will be decoded to this register. The register read and write + * callbacks will be passed an ARMCPRegInfo with the crn/crm/opc1/opc2 + * used by the program, so it is possible to register a wildcard and + * then behave differently on read/write if necessary. + * For 64 bit registers, only crm and opc1 are relevant; crn and opc2 + * must both be zero. + */ + uint8_t cp; + uint8_t crn; + uint8_t crm; + uint8_t opc1; + uint8_t opc2; + /* Register type: ARM_CP_* bits/values */ + int type; + /* Access rights: PL*_[RW] */ + int access; + /* The opaque pointer passed to define_arm_cp_regs_with_opaque() when + * this register was defined: can be used to hand data through to the + * register read/write functions, since they are passed the ARMCPRegInfo*. + */ + void *opaque; + /* Value of this register, if it is ARM_CP_CONST. Otherwise, if + * fieldoffset is non-zero, the reset value of the register. + */ + uint64_t resetvalue; + /* Offset of the field in CPUARMState for this register. This is not + * needed if either: + * 1. type is ARM_CP_CONST or one of the ARM_CP_SPECIALs + * 2. both readfn and writefn are specified + */ + ptrdiff_t fieldoffset; /* offsetof(CPUARMState, field) */ + /* Function for handling reads of this register. If NULL, then reads + * will be done by loading from the offset into CPUARMState specified + * by fieldoffset. + */ + CPReadFn *readfn; + /* Function for handling writes of this register. If NULL, then writes + * will be done by writing to the offset into CPUARMState specified + * by fieldoffset. + */ + CPWriteFn *writefn; + /* Function for resetting the register. If NULL, then reset will be done + * by writing resetvalue to the field specified in fieldoffset. If + * fieldoffset is 0 then no reset will be done. + */ + CPResetFn *resetfn; +}; + +/* Macros which are lvalues for the field in CPUARMState for the + * ARMCPRegInfo *ri. + */ +#define CPREG_FIELD32(env, ri) \ + (*(uint32_t *)((char *)(env) + (ri)->fieldoffset)) +#define CPREG_FIELD64(env, ri) \ + (*(uint64_t *)((char *)(env) + (ri)->fieldoffset)) + +#define REGINFO_SENTINEL { .type = ARM_CP_SENTINEL } + +void define_arm_cp_regs_with_opaque(ARMCPU *cpu, + const ARMCPRegInfo *regs, void *opaque); +void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, + const ARMCPRegInfo *regs, void *opaque); +static inline void define_arm_cp_regs(ARMCPU *cpu, const ARMCPRegInfo *regs) +{ + define_arm_cp_regs_with_opaque(cpu, regs, 0); +} +static inline void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *regs) +{ + define_one_arm_cp_reg_with_opaque(cpu, regs, 0); +} +const ARMCPRegInfo *get_arm_cp_reginfo(ARMCPU *cpu, uint32_t encoded_cp); + +/* CPWriteFn that can be used to implement writes-ignored behaviour */ +int arm_cp_write_ignore(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value); +/* CPReadFn that can be used for read-as-zero behaviour */ +int arm_cp_read_zero(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t *value); + +static inline bool cp_access_ok(CPUARMState *env, + const ARMCPRegInfo *ri, int isread) +{ + return (ri->access >> ((arm_current_pl(env) * 2) + isread)) & 1; +} /* Does the core conform to the the "MicroController" profile. e.g. Cortex-M3. Note the M in older cores (eg. ARM7TDMI) stands for Multiply. These are conventional cores (ie. Application or Realtime profile). */ #define IS_M(env) arm_feature(env, ARM_FEATURE_M) -#define ARM_CPUID(env) (env->cp15.c0_cpuid) -#define ARM_CPUID_ARM1026 0x4106a262 -#define ARM_CPUID_ARM926 0x41069265 -#define ARM_CPUID_ARM946 0x41059461 #define ARM_CPUID_TI915T 0x54029152 #define ARM_CPUID_TI925T 0x54029252 -#define ARM_CPUID_SA1100 0x4401A11B -#define ARM_CPUID_SA1110 0x6901B119 -#define ARM_CPUID_PXA250 0x69052100 -#define ARM_CPUID_PXA255 0x69052d00 -#define ARM_CPUID_PXA260 0x69052903 -#define ARM_CPUID_PXA261 0x69052d05 -#define ARM_CPUID_PXA262 0x69052d06 -#define ARM_CPUID_PXA270 0x69054110 -#define ARM_CPUID_PXA270_A0 0x69054110 -#define ARM_CPUID_PXA270_A1 0x69054111 -#define ARM_CPUID_PXA270_B0 0x69054112 -#define ARM_CPUID_PXA270_B1 0x69054113 -#define ARM_CPUID_PXA270_C0 0x69054114 -#define ARM_CPUID_PXA270_C5 0x69054117 -#define ARM_CPUID_ARM1136 0x4117b363 -#define ARM_CPUID_ARM1136_R2 0x4107b362 -#define ARM_CPUID_ARM1176 0x410fb767 -#define ARM_CPUID_ARM11MPCORE 0x410fb022 -#define ARM_CPUID_CORTEXA8 0x410fc080 -#define ARM_CPUID_CORTEXA9 0x410fc090 -#define ARM_CPUID_CORTEXA15 0x412fc0f1 -#define ARM_CPUID_CORTEXM3 0x410fc231 -#define ARM_CPUID_ANY 0xffffffff #if defined(CONFIG_USER_ONLY) #define TARGET_PAGE_BITS 12 @@ -472,7 +636,7 @@ static inline CPUARMState *cpu_init(const char *cpu_model) #define cpu_signal_handler cpu_arm_signal_handler #define cpu_list arm_cpu_list -#define CPU_SAVE_VERSION 6 +#define CPU_SAVE_VERSION 7 /* MMU modes definitions */ #define MMU_MODE0_SUFFIX _kernel diff --git a/target-arm/helper.c b/target-arm/helper.c index bbb1d05d10..23099236ad 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -4,6 +4,13 @@ #include "host-utils.h" #include "sysemu.h" +#ifndef CONFIG_USER_ONLY +static inline int get_phys_addr(CPUARMState *env, uint32_t address, + int access_type, int is_user, + uint32_t *phys_ptr, int *prot, + target_ulong *page_size); +#endif + static int vfp_gdb_get_reg(CPUARMState *env, uint8_t *buf, int reg) { int nregs; @@ -56,6 +63,1054 @@ static int vfp_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) return 0; } +static int dacr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) +{ + env->cp15.c3 = value; + tlb_flush(env, 1); /* Flush TLB as domain not tracked in TLB */ + return 0; +} + +static int fcse_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) +{ + if (env->cp15.c13_fcse != value) { + /* Unlike real hardware the qemu TLB uses virtual addresses, + * not modified virtual addresses, so this causes a TLB flush. + */ + tlb_flush(env, 1); + env->cp15.c13_fcse = value; + } + return 0; +} +static int contextidr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + if (env->cp15.c13_context != value && !arm_feature(env, ARM_FEATURE_MPU)) { + /* For VMSA (when not using the LPAE long descriptor page table + * format) this register includes the ASID, so do a TLB flush. + * For PMSA it is purely a process ID and no action is needed. + */ + tlb_flush(env, 1); + } + env->cp15.c13_context = value; + return 0; +} + +static int tlbiall_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Invalidate all (TLBIALL) */ + tlb_flush(env, 1); + return 0; +} + +static int tlbimva_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Invalidate single TLB entry by MVA and ASID (TLBIMVA) */ + tlb_flush_page(env, value & TARGET_PAGE_MASK); + return 0; +} + +static int tlbiasid_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Invalidate by ASID (TLBIASID) */ + tlb_flush(env, value == 0); + return 0; +} + +static int tlbimvaa_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Invalidate single entry by MVA, all ASIDs (TLBIMVAA) */ + tlb_flush_page(env, value & TARGET_PAGE_MASK); + return 0; +} + +static const ARMCPRegInfo cp_reginfo[] = { + /* DBGDIDR: just RAZ. In particular this means the "debug architecture + * version" bits will read as a reserved value, which should cause + * Linux to not try to use the debug hardware. + */ + { .name = "DBGDIDR", .cp = 14, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + /* MMU Domain access control / MPU write buffer control */ + { .name = "DACR", .cp = 15, + .crn = 3, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c3), + .resetvalue = 0, .writefn = dacr_write }, + { .name = "FCSEIDR", .cp = 15, .crn = 13, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c13_fcse), + .resetvalue = 0, .writefn = fcse_write }, + { .name = "CONTEXTIDR", .cp = 15, .crn = 13, .crm = 0, .opc1 = 0, .opc2 = 1, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c13_fcse), + .resetvalue = 0, .writefn = contextidr_write }, + /* ??? This covers not just the impdef TLB lockdown registers but also + * some v7VMSA registers relating to TEX remap, so it is overly broad. + */ + { .name = "TLB_LOCKDOWN", .cp = 15, .crn = 10, .crm = CP_ANY, + .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .type = ARM_CP_NOP }, + /* MMU TLB control. Note that the wildcarding means we cover not just + * the unified TLB ops but also the dside/iside/inner-shareable variants. + */ + { .name = "TLBIALL", .cp = 15, .crn = 8, .crm = CP_ANY, + .opc1 = CP_ANY, .opc2 = 0, .access = PL1_W, .writefn = tlbiall_write, }, + { .name = "TLBIMVA", .cp = 15, .crn = 8, .crm = CP_ANY, + .opc1 = CP_ANY, .opc2 = 1, .access = PL1_W, .writefn = tlbimva_write, }, + { .name = "TLBIASID", .cp = 15, .crn = 8, .crm = CP_ANY, + .opc1 = CP_ANY, .opc2 = 2, .access = PL1_W, .writefn = tlbiasid_write, }, + { .name = "TLBIMVAA", .cp = 15, .crn = 8, .crm = CP_ANY, + .opc1 = CP_ANY, .opc2 = 3, .access = PL1_W, .writefn = tlbimvaa_write, }, + /* Cache maintenance ops; some of this space may be overridden later. */ + { .name = "CACHEMAINT", .cp = 15, .crn = 7, .crm = CP_ANY, + .opc1 = 0, .opc2 = CP_ANY, .access = PL1_W, + .type = ARM_CP_NOP | ARM_CP_OVERRIDE }, + REGINFO_SENTINEL +}; + +static const ARMCPRegInfo not_v6_cp_reginfo[] = { + /* Not all pre-v6 cores implemented this WFI, so this is slightly + * over-broad. + */ + { .name = "WFI_v5", .cp = 15, .crn = 7, .crm = 8, .opc1 = 0, .opc2 = 2, + .access = PL1_W, .type = ARM_CP_WFI }, + REGINFO_SENTINEL +}; + +static const ARMCPRegInfo not_v7_cp_reginfo[] = { + /* Standard v6 WFI (also used in some pre-v6 cores); not in v7 (which + * is UNPREDICTABLE; we choose to NOP as most implementations do). + */ + { .name = "WFI_v6", .cp = 15, .crn = 7, .crm = 0, .opc1 = 0, .opc2 = 4, + .access = PL1_W, .type = ARM_CP_WFI }, + /* L1 cache lockdown. Not architectural in v6 and earlier but in practice + * implemented in 926, 946, 1026, 1136, 1176 and 11MPCore. StrongARM and + * OMAPCP will override this space. + */ + { .name = "DLOCKDOWN", .cp = 15, .crn = 9, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_data), + .resetvalue = 0 }, + { .name = "ILOCKDOWN", .cp = 15, .crn = 9, .crm = 0, .opc1 = 0, .opc2 = 1, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_insn), + .resetvalue = 0 }, + /* v6 doesn't have the cache ID registers but Linux reads them anyway */ + { .name = "DUMMY", .cp = 15, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = CP_ANY, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + REGINFO_SENTINEL +}; + +static int cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) +{ + if (env->cp15.c1_coproc != value) { + env->cp15.c1_coproc = value; + /* ??? Is this safe when called from within a TB? */ + tb_flush(env); + } + return 0; +} + +static const ARMCPRegInfo v6_cp_reginfo[] = { + /* prefetch by MVA in v6, NOP in v7 */ + { .name = "MVA_prefetch", + .cp = 15, .crn = 7, .crm = 13, .opc1 = 0, .opc2 = 1, + .access = PL1_W, .type = ARM_CP_NOP }, + { .name = "ISB", .cp = 15, .crn = 7, .crm = 5, .opc1 = 0, .opc2 = 4, + .access = PL0_W, .type = ARM_CP_NOP }, + { .name = "ISB", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 4, + .access = PL0_W, .type = ARM_CP_NOP }, + { .name = "ISB", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 5, + .access = PL0_W, .type = ARM_CP_NOP }, + { .name = "IFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 2, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c6_insn), + .resetvalue = 0, }, + /* Watchpoint Fault Address Register : should actually only be present + * for 1136, 1176, 11MPCore. + */ + { .name = "WFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, }, + { .name = "CPACR", .cp = 15, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 2, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c1_coproc), + .resetvalue = 0, .writefn = cpacr_write }, + REGINFO_SENTINEL +}; + +static int pmreg_read(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t *value) +{ + /* Generic performance monitor register read function for where + * user access may be allowed by PMUSERENR. + */ + if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) { + return EXCP_UDEF; + } + *value = CPREG_FIELD32(env, ri); + return 0; +} + +static int pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) { + return EXCP_UDEF; + } + /* only the DP, X, D and E bits are writable */ + env->cp15.c9_pmcr &= ~0x39; + env->cp15.c9_pmcr |= (value & 0x39); + return 0; +} + +static int pmcntenset_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) { + return EXCP_UDEF; + } + value &= (1 << 31); + env->cp15.c9_pmcnten |= value; + return 0; +} + +static int pmcntenclr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) { + return EXCP_UDEF; + } + value &= (1 << 31); + env->cp15.c9_pmcnten &= ~value; + return 0; +} + +static int pmovsr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) { + return EXCP_UDEF; + } + env->cp15.c9_pmovsr &= ~value; + return 0; +} + +static int pmxevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) { + return EXCP_UDEF; + } + env->cp15.c9_pmxevtyper = value & 0xff; + return 0; +} + +static int pmuserenr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + env->cp15.c9_pmuserenr = value & 1; + return 0; +} + +static int pmintenset_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* We have no event counters so only the C bit can be changed */ + value &= (1 << 31); + env->cp15.c9_pminten |= value; + return 0; +} + +static int pmintenclr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + value &= (1 << 31); + env->cp15.c9_pminten &= ~value; + return 0; +} + +static int ccsidr_read(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t *value) +{ + ARMCPU *cpu = arm_env_get_cpu(env); + *value = cpu->ccsidr[env->cp15.c0_cssel]; + return 0; +} + +static int csselr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + env->cp15.c0_cssel = value & 0xf; + return 0; +} + +static const ARMCPRegInfo v7_cp_reginfo[] = { + /* DBGDRAR, DBGDSAR: always RAZ since we don't implement memory mapped + * debug components + */ + { .name = "DBGDRAR", .cp = 14, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "DBGDRAR", .cp = 14, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + /* the old v6 WFI, UNPREDICTABLE in v7 but we choose to NOP */ + { .name = "NOP", .cp = 15, .crn = 7, .crm = 0, .opc1 = 0, .opc2 = 4, + .access = PL1_W, .type = ARM_CP_NOP }, + /* Performance monitors are implementation defined in v7, + * but with an ARM recommended set of registers, which we + * follow (although we don't actually implement any counters) + * + * Performance registers fall into three categories: + * (a) always UNDEF in PL0, RW in PL1 (PMINTENSET, PMINTENCLR) + * (b) RO in PL0 (ie UNDEF on write), RW in PL1 (PMUSERENR) + * (c) UNDEF in PL0 if PMUSERENR.EN==0, otherwise accessible (all others) + * For the cases controlled by PMUSERENR we must set .access to PL0_RW + * or PL0_RO as appropriate and then check PMUSERENR in the helper fn. + */ + { .name = "PMCNTENSET", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 1, + .access = PL0_RW, .resetvalue = 0, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), + .readfn = pmreg_read, .writefn = pmcntenset_write }, + { .name = "PMCNTENCLR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 2, + .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), + .readfn = pmreg_read, .writefn = pmcntenclr_write }, + { .name = "PMOVSR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 3, + .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr), + .readfn = pmreg_read, .writefn = pmovsr_write }, + /* Unimplemented so WI. Strictly speaking write accesses in PL0 should + * respect PMUSERENR. + */ + { .name = "PMSWINC", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 4, + .access = PL0_W, .type = ARM_CP_NOP }, + /* Since we don't implement any events, writing to PMSELR is UNPREDICTABLE. + * We choose to RAZ/WI. XXX should respect PMUSERENR. + */ + { .name = "PMSELR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 5, + .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + /* Unimplemented, RAZ/WI. XXX PMUSERENR */ + { .name = "PMCCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 0, + .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "PMXEVTYPER", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 1, + .access = PL0_RW, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmxevtyper), + .readfn = pmreg_read, .writefn = pmxevtyper_write }, + /* Unimplemented, RAZ/WI. XXX PMUSERENR */ + { .name = "PMXEVCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 2, + .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "PMUSERENR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 0, + .access = PL0_R | PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmuserenr), + .resetvalue = 0, + .writefn = pmuserenr_write }, + { .name = "PMINTENSET", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 1, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), + .resetvalue = 0, + .writefn = pmintenset_write }, + { .name = "PMINTENCLR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 2, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), + .resetvalue = 0, + .writefn = pmintenclr_write }, + { .name = "SCR", .cp = 15, .crn = 1, .crm = 1, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c1_scr), + .resetvalue = 0, }, + { .name = "CCSIDR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 0, + .access = PL1_R, .readfn = ccsidr_read }, + { .name = "CSSELR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 2, .opc2 = 0, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c0_cssel), + .writefn = csselr_write, .resetvalue = 0 }, + /* Auxiliary ID register: this actually has an IMPDEF value but for now + * just RAZ for all cores: + */ + { .name = "AIDR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 7, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + REGINFO_SENTINEL +}; + +static int teecr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) +{ + value &= 1; + env->teecr = value; + return 0; +} + +static int teehbr_read(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t *value) +{ + /* This is a helper function because the user access rights + * depend on the value of the TEECR. + */ + if (arm_current_pl(env) == 0 && (env->teecr & 1)) { + return EXCP_UDEF; + } + *value = env->teehbr; + return 0; +} + +static int teehbr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + if (arm_current_pl(env) == 0 && (env->teecr & 1)) { + return EXCP_UDEF; + } + env->teehbr = value; + return 0; +} + +static const ARMCPRegInfo t2ee_cp_reginfo[] = { + { .name = "TEECR", .cp = 14, .crn = 0, .crm = 0, .opc1 = 6, .opc2 = 0, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, teecr), + .resetvalue = 0, + .writefn = teecr_write }, + { .name = "TEEHBR", .cp = 14, .crn = 1, .crm = 0, .opc1 = 6, .opc2 = 0, + .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, teehbr), + .resetvalue = 0, + .readfn = teehbr_read, .writefn = teehbr_write }, + REGINFO_SENTINEL +}; + +static const ARMCPRegInfo v6k_cp_reginfo[] = { + { .name = "TPIDRURW", .cp = 15, .crn = 13, .crm = 0, .opc1 = 0, .opc2 = 2, + .access = PL0_RW, + .fieldoffset = offsetof(CPUARMState, cp15.c13_tls1), + .resetvalue = 0 }, + { .name = "TPIDRURO", .cp = 15, .crn = 13, .crm = 0, .opc1 = 0, .opc2 = 3, + .access = PL0_R|PL1_W, + .fieldoffset = offsetof(CPUARMState, cp15.c13_tls2), + .resetvalue = 0 }, + { .name = "TPIDRPRW", .cp = 15, .crn = 13, .crm = 0, .opc1 = 0, .opc2 = 4, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.c13_tls3), + .resetvalue = 0 }, + REGINFO_SENTINEL +}; + +static const ARMCPRegInfo generic_timer_cp_reginfo[] = { + /* Dummy implementation: RAZ/WI the whole crn=14 space */ + { .name = "GENERIC_TIMER", .cp = 15, .crn = 14, + .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + REGINFO_SENTINEL +}; + +static int par_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) +{ + if (arm_feature(env, ARM_FEATURE_V7)) { + env->cp15.c7_par = value & 0xfffff6ff; + } else { + env->cp15.c7_par = value & 0xfffff1ff; + } + return 0; +} + +#ifndef CONFIG_USER_ONLY +/* get_phys_addr() isn't present for user-mode-only targets */ +static int ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) +{ + uint32_t phys_addr; + target_ulong page_size; + int prot; + int ret, is_user = ri->opc2 & 2; + int access_type = ri->opc2 & 1; + + if (ri->opc2 & 4) { + /* Other states are only available with TrustZone */ + return EXCP_UDEF; + } + ret = get_phys_addr(env, value, access_type, is_user, + &phys_addr, &prot, &page_size); + if (ret == 0) { + /* We do not set any attribute bits in the PAR */ + if (page_size == (1 << 24) + && arm_feature(env, ARM_FEATURE_V7)) { + env->cp15.c7_par = (phys_addr & 0xff000000) | 1 << 1; + } else { + env->cp15.c7_par = phys_addr & 0xfffff000; + } + } else { + env->cp15.c7_par = ((ret & (10 << 1)) >> 5) | + ((ret & (12 << 1)) >> 6) | + ((ret & 0xf) << 1) | 1; + } + return 0; +} +#endif + +static const ARMCPRegInfo vapa_cp_reginfo[] = { + { .name = "PAR", .cp = 15, .crn = 7, .crm = 4, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .resetvalue = 0, + .fieldoffset = offsetof(CPUARMState, cp15.c7_par), + .writefn = par_write }, +#ifndef CONFIG_USER_ONLY + { .name = "ATS", .cp = 15, .crn = 7, .crm = 8, .opc1 = 0, .opc2 = CP_ANY, + .access = PL1_W, .writefn = ats_write }, +#endif + REGINFO_SENTINEL +}; + +/* Return basic MPU access permission bits. */ +static uint32_t simple_mpu_ap_bits(uint32_t val) +{ + uint32_t ret; + uint32_t mask; + int i; + ret = 0; + mask = 3; + for (i = 0; i < 16; i += 2) { + ret |= (val >> i) & mask; + mask <<= 2; + } + return ret; +} + +/* Pad basic MPU access permission bits to extended format. */ +static uint32_t extended_mpu_ap_bits(uint32_t val) +{ + uint32_t ret; + uint32_t mask; + int i; + ret = 0; + mask = 3; + for (i = 0; i < 16; i += 2) { + ret |= (val & mask) << i; + mask <<= 2; + } + return ret; +} + +static int pmsav5_data_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + env->cp15.c5_data = extended_mpu_ap_bits(value); + return 0; +} + +static int pmsav5_data_ap_read(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t *value) +{ + *value = simple_mpu_ap_bits(env->cp15.c5_data); + return 0; +} + +static int pmsav5_insn_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + env->cp15.c5_insn = extended_mpu_ap_bits(value); + return 0; +} + +static int pmsav5_insn_ap_read(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t *value) +{ + *value = simple_mpu_ap_bits(env->cp15.c5_insn); + return 0; +} + +static int arm946_prbs_read(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t *value) +{ + if (ri->crm > 8) { + return EXCP_UDEF; + } + *value = env->cp15.c6_region[ri->crm]; + return 0; +} + +static int arm946_prbs_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + if (ri->crm > 8) { + return EXCP_UDEF; + } + env->cp15.c6_region[ri->crm] = value; + return 0; +} + +static const ARMCPRegInfo pmsav5_cp_reginfo[] = { + { .name = "DATA_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.c5_data), .resetvalue = 0, + .readfn = pmsav5_data_ap_read, .writefn = pmsav5_data_ap_write, }, + { .name = "INSN_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 1, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.c5_insn), .resetvalue = 0, + .readfn = pmsav5_insn_ap_read, .writefn = pmsav5_insn_ap_write, }, + { .name = "DATA_EXT_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 2, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.c5_data), .resetvalue = 0, }, + { .name = "INSN_EXT_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 3, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.c5_insn), .resetvalue = 0, }, + { .name = "DCACHE_CFG", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.c2_data), .resetvalue = 0, }, + { .name = "ICACHE_CFG", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 1, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.c2_insn), .resetvalue = 0, }, + /* Protection region base and size registers */ + { .name = "946_PRBS", .cp = 15, .crn = 6, .crm = CP_ANY, .opc1 = 0, + .opc2 = CP_ANY, .access = PL1_RW, + .readfn = arm946_prbs_read, .writefn = arm946_prbs_write, }, + REGINFO_SENTINEL +}; + +static int vmsa_ttbcr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + value &= 7; + env->cp15.c2_control = value; + env->cp15.c2_mask = ~(((uint32_t)0xffffffffu) >> value); + env->cp15.c2_base_mask = ~((uint32_t)0x3fffu >> value); + return 0; +} + +static void vmsa_ttbcr_reset(CPUARMState *env, const ARMCPRegInfo *ri) +{ + env->cp15.c2_base_mask = 0xffffc000u; + env->cp15.c2_control = 0; + env->cp15.c2_mask = 0; +} + +static const ARMCPRegInfo vmsa_cp_reginfo[] = { + { .name = "DFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.c5_data), .resetvalue = 0, }, + { .name = "IFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 1, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.c5_insn), .resetvalue = 0, }, + { .name = "TTBR0", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.c2_base0), .resetvalue = 0, }, + { .name = "TTBR1", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 1, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.c2_base0), .resetvalue = 0, }, + { .name = "TTBCR", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2, + .access = PL1_RW, .writefn = vmsa_ttbcr_write, + .resetfn = vmsa_ttbcr_reset, + .fieldoffset = offsetof(CPUARMState, cp15.c2_control) }, + { .name = "DFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c6_data), + .resetvalue = 0, }, + REGINFO_SENTINEL +}; + +static int omap_ticonfig_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + env->cp15.c15_ticonfig = value & 0xe7; + /* The OS_TYPE bit in this register changes the reported CPUID! */ + env->cp15.c0_cpuid = (value & (1 << 5)) ? + ARM_CPUID_TI915T : ARM_CPUID_TI925T; + return 0; +} + +static int omap_threadid_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + env->cp15.c15_threadid = value & 0xffff; + return 0; +} + +static int omap_wfi_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Wait-for-interrupt (deprecated) */ + cpu_interrupt(env, CPU_INTERRUPT_HALT); + return 0; +} + +static int omap_cachemaint_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* On OMAP there are registers indicating the max/min index of dcache lines + * containing a dirty line; cache flush operations have to reset these. + */ + env->cp15.c15_i_max = 0x000; + env->cp15.c15_i_min = 0xff0; + return 0; +} + +static const ARMCPRegInfo omap_cp_reginfo[] = { + { .name = "DFSR", .cp = 15, .crn = 5, .crm = CP_ANY, + .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .type = ARM_CP_OVERRIDE, + .fieldoffset = offsetof(CPUARMState, cp15.c5_data), .resetvalue = 0, }, + { .name = "", .cp = 15, .crn = 15, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_NOP }, + { .name = "TICONFIG", .cp = 15, .crn = 15, .crm = 1, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.c15_ticonfig), .resetvalue = 0, + .writefn = omap_ticonfig_write }, + { .name = "IMAX", .cp = 15, .crn = 15, .crm = 2, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.c15_i_max), .resetvalue = 0, }, + { .name = "IMIN", .cp = 15, .crn = 15, .crm = 3, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .resetvalue = 0xff0, + .fieldoffset = offsetof(CPUARMState, cp15.c15_i_min) }, + { .name = "THREADID", .cp = 15, .crn = 15, .crm = 4, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.c15_threadid), .resetvalue = 0, + .writefn = omap_threadid_write }, + { .name = "TI925T_STATUS", .cp = 15, .crn = 15, + .crm = 8, .opc1 = 0, .opc2 = 0, .access = PL1_RW, + .readfn = arm_cp_read_zero, .writefn = omap_wfi_write, }, + /* TODO: Peripheral port remap register: + * On OMAP2 mcr p15, 0, rn, c15, c2, 4 sets up the interrupt controller + * base address at $rn & ~0xfff and map size of 0x200 << ($rn & 0xfff), + * when MMU is off. + */ + { .name = "OMAP_CACHEMAINT", .cp = 15, .crn = 7, .crm = CP_ANY, + .opc1 = 0, .opc2 = CP_ANY, .access = PL1_W, .type = ARM_CP_OVERRIDE, + .writefn = omap_cachemaint_write }, + { .name = "C9", .cp = 15, .crn = 9, + .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, + .type = ARM_CP_CONST | ARM_CP_OVERRIDE, .resetvalue = 0 }, + REGINFO_SENTINEL +}; + +static int xscale_cpar_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + value &= 0x3fff; + if (env->cp15.c15_cpar != value) { + /* Changes cp0 to cp13 behavior, so needs a TB flush. */ + tb_flush(env); + env->cp15.c15_cpar = value; + } + return 0; +} + +static const ARMCPRegInfo xscale_cp_reginfo[] = { + { .name = "XSCALE_CPAR", + .cp = 15, .crn = 15, .crm = 1, .opc1 = 0, .opc2 = 0, .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.c15_cpar), .resetvalue = 0, + .writefn = xscale_cpar_write, }, + { .name = "XSCALE_AUXCR", + .cp = 15, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 1, .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.c1_xscaleauxcr), + .resetvalue = 0, }, + REGINFO_SENTINEL +}; + +static const ARMCPRegInfo dummy_c15_cp_reginfo[] = { + /* RAZ/WI the whole crn=15 space, when we don't have a more specific + * implementation of this implementation-defined space. + * Ideally this should eventually disappear in favour of actually + * implementing the correct behaviour for all cores. + */ + { .name = "C15_IMPDEF", .cp = 15, .crn = 15, + .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + REGINFO_SENTINEL +}; + +static const ARMCPRegInfo cache_dirty_status_cp_reginfo[] = { + /* Cache status: RAZ because we have no cache so it's always clean */ + { .name = "CDSR", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 6, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + REGINFO_SENTINEL +}; + +static const ARMCPRegInfo cache_block_ops_cp_reginfo[] = { + /* We never have a a block transfer operation in progress */ + { .name = "BXSR", .cp = 15, .crn = 7, .crm = 12, .opc1 = 0, .opc2 = 4, + .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + /* The cache ops themselves: these all NOP for QEMU */ + { .name = "IICR", .cp = 15, .crm = 5, .opc1 = 0, + .access = PL1_W, .type = ARM_CP_NOP|ARM_CP_64BIT }, + { .name = "IDCR", .cp = 15, .crm = 6, .opc1 = 0, + .access = PL1_W, .type = ARM_CP_NOP|ARM_CP_64BIT }, + { .name = "CDCR", .cp = 15, .crm = 12, .opc1 = 0, + .access = PL0_W, .type = ARM_CP_NOP|ARM_CP_64BIT }, + { .name = "PIR", .cp = 15, .crm = 12, .opc1 = 1, + .access = PL0_W, .type = ARM_CP_NOP|ARM_CP_64BIT }, + { .name = "PDR", .cp = 15, .crm = 12, .opc1 = 2, + .access = PL0_W, .type = ARM_CP_NOP|ARM_CP_64BIT }, + { .name = "CIDCR", .cp = 15, .crm = 14, .opc1 = 0, + .access = PL1_W, .type = ARM_CP_NOP|ARM_CP_64BIT }, + REGINFO_SENTINEL +}; + +static const ARMCPRegInfo cache_test_clean_cp_reginfo[] = { + /* The cache test-and-clean instructions always return (1 << 30) + * to indicate that there are no dirty cache lines. + */ + { .name = "TC_DCACHE", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 3, + .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = (1 << 30) }, + { .name = "TCI_DCACHE", .cp = 15, .crn = 7, .crm = 14, .opc1 = 0, .opc2 = 3, + .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = (1 << 30) }, + REGINFO_SENTINEL +}; + +static const ARMCPRegInfo strongarm_cp_reginfo[] = { + /* Ignore ReadBuffer accesses */ + { .name = "C9_READBUFFER", .cp = 15, .crn = 9, + .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, + .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_OVERRIDE, + .resetvalue = 0 }, + REGINFO_SENTINEL +}; + +static int mpidr_read(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t *value) +{ + uint32_t mpidr = env->cpu_index; + /* We don't support setting cluster ID ([8..11]) + * so these bits always RAZ. + */ + if (arm_feature(env, ARM_FEATURE_V7MP)) { + mpidr |= (1 << 31); + /* Cores which are uniprocessor (non-coherent) + * but still implement the MP extensions set + * bit 30. (For instance, A9UP.) However we do + * not currently model any of those cores. + */ + } + *value = mpidr; + return 0; +} + +static const ARMCPRegInfo mpidr_cp_reginfo[] = { + { .name = "MPIDR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 5, + .access = PL1_R, .readfn = mpidr_read }, + REGINFO_SENTINEL +}; + +static int sctlr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) +{ + env->cp15.c1_sys = value; + /* ??? Lots of these bits are not implemented. */ + /* This may enable/disable the MMU, so do a TLB flush. */ + tlb_flush(env, 1); + return 0; +} + +void register_cp_regs_for_features(ARMCPU *cpu) +{ + /* Register all the coprocessor registers based on feature bits */ + CPUARMState *env = &cpu->env; + if (arm_feature(env, ARM_FEATURE_M)) { + /* M profile has no coprocessor registers */ + return; + } + + define_arm_cp_regs(cpu, cp_reginfo); + if (arm_feature(env, ARM_FEATURE_V6)) { + /* The ID registers all have impdef reset values */ + ARMCPRegInfo v6_idregs[] = { + { .name = "ID_PFR0", .cp = 15, .crn = 0, .crm = 1, + .opc1 = 0, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->id_pfr0 }, + { .name = "ID_PFR1", .cp = 15, .crn = 0, .crm = 1, + .opc1 = 0, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->id_pfr1 }, + { .name = "ID_DFR0", .cp = 15, .crn = 0, .crm = 1, + .opc1 = 0, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->id_dfr0 }, + { .name = "ID_AFR0", .cp = 15, .crn = 0, .crm = 1, + .opc1 = 0, .opc2 = 3, .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->id_afr0 }, + { .name = "ID_MMFR0", .cp = 15, .crn = 0, .crm = 1, + .opc1 = 0, .opc2 = 4, .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->id_mmfr0 }, + { .name = "ID_MMFR1", .cp = 15, .crn = 0, .crm = 1, + .opc1 = 0, .opc2 = 5, .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->id_mmfr1 }, + { .name = "ID_MMFR2", .cp = 15, .crn = 0, .crm = 1, + .opc1 = 0, .opc2 = 6, .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->id_mmfr2 }, + { .name = "ID_MMFR3", .cp = 15, .crn = 0, .crm = 1, + .opc1 = 0, .opc2 = 7, .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->id_mmfr3 }, + { .name = "ID_ISAR0", .cp = 15, .crn = 0, .crm = 2, + .opc1 = 0, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->id_isar0 }, + { .name = "ID_ISAR1", .cp = 15, .crn = 0, .crm = 2, + .opc1 = 0, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->id_isar1 }, + { .name = "ID_ISAR2", .cp = 15, .crn = 0, .crm = 2, + .opc1 = 0, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->id_isar2 }, + { .name = "ID_ISAR3", .cp = 15, .crn = 0, .crm = 2, + .opc1 = 0, .opc2 = 3, .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->id_isar3 }, + { .name = "ID_ISAR4", .cp = 15, .crn = 0, .crm = 2, + .opc1 = 0, .opc2 = 4, .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->id_isar4 }, + { .name = "ID_ISAR5", .cp = 15, .crn = 0, .crm = 2, + .opc1 = 0, .opc2 = 5, .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = cpu->id_isar5 }, + /* 6..7 are as yet unallocated and must RAZ */ + { .name = "ID_ISAR6", .cp = 15, .crn = 0, .crm = 2, + .opc1 = 0, .opc2 = 6, .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, + { .name = "ID_ISAR7", .cp = 15, .crn = 0, .crm = 2, + .opc1 = 0, .opc2 = 7, .access = PL1_R, .type = ARM_CP_CONST, + .resetvalue = 0 }, + REGINFO_SENTINEL + }; + define_arm_cp_regs(cpu, v6_idregs); + define_arm_cp_regs(cpu, v6_cp_reginfo); + } else { + define_arm_cp_regs(cpu, not_v6_cp_reginfo); + } + if (arm_feature(env, ARM_FEATURE_V6K)) { + define_arm_cp_regs(cpu, v6k_cp_reginfo); + } + if (arm_feature(env, ARM_FEATURE_V7)) { + /* v7 performance monitor control register: same implementor + * field as main ID register, and we implement no event counters. + */ + ARMCPRegInfo pmcr = { + .name = "PMCR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 0, + .access = PL0_RW, .resetvalue = cpu->midr & 0xff000000, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr), + .readfn = pmreg_read, .writefn = pmcr_write + }; + ARMCPRegInfo clidr = { + .name = "CLIDR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 1, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->clidr + }; + define_one_arm_cp_reg(cpu, &pmcr); + define_one_arm_cp_reg(cpu, &clidr); + define_arm_cp_regs(cpu, v7_cp_reginfo); + } else { + define_arm_cp_regs(cpu, not_v7_cp_reginfo); + } + if (arm_feature(env, ARM_FEATURE_MPU)) { + /* These are the MPU registers prior to PMSAv6. Any new + * PMSA core later than the ARM946 will require that we + * implement the PMSAv6 or PMSAv7 registers, which are + * completely different. + */ + assert(!arm_feature(env, ARM_FEATURE_V6)); + define_arm_cp_regs(cpu, pmsav5_cp_reginfo); + } else { + define_arm_cp_regs(cpu, vmsa_cp_reginfo); + } + if (arm_feature(env, ARM_FEATURE_THUMB2EE)) { + define_arm_cp_regs(cpu, t2ee_cp_reginfo); + } + if (arm_feature(env, ARM_FEATURE_GENERIC_TIMER)) { + define_arm_cp_regs(cpu, generic_timer_cp_reginfo); + } + if (arm_feature(env, ARM_FEATURE_VAPA)) { + define_arm_cp_regs(cpu, vapa_cp_reginfo); + } + if (arm_feature(env, ARM_FEATURE_CACHE_TEST_CLEAN)) { + define_arm_cp_regs(cpu, cache_test_clean_cp_reginfo); + } + if (arm_feature(env, ARM_FEATURE_CACHE_DIRTY_REG)) { + define_arm_cp_regs(cpu, cache_dirty_status_cp_reginfo); + } + if (arm_feature(env, ARM_FEATURE_CACHE_BLOCK_OPS)) { + define_arm_cp_regs(cpu, cache_block_ops_cp_reginfo); + } + if (arm_feature(env, ARM_FEATURE_OMAPCP)) { + define_arm_cp_regs(cpu, omap_cp_reginfo); + } + if (arm_feature(env, ARM_FEATURE_STRONGARM)) { + define_arm_cp_regs(cpu, strongarm_cp_reginfo); + } + if (arm_feature(env, ARM_FEATURE_XSCALE)) { + define_arm_cp_regs(cpu, xscale_cp_reginfo); + } + if (arm_feature(env, ARM_FEATURE_DUMMY_C15_REGS)) { + define_arm_cp_regs(cpu, dummy_c15_cp_reginfo); + } + if (arm_feature(env, ARM_FEATURE_MPIDR)) { + define_arm_cp_regs(cpu, mpidr_cp_reginfo); + } + /* Slightly awkwardly, the OMAP and StrongARM cores need all of + * cp15 crn=0 to be writes-ignored, whereas for other cores they should + * be read-only (ie write causes UNDEF exception). + */ + { + ARMCPRegInfo id_cp_reginfo[] = { + /* Note that the MIDR isn't a simple constant register because + * of the TI925 behaviour where writes to another register can + * cause the MIDR value to change. + */ + { .name = "MIDR", + .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_R, .resetvalue = cpu->midr, + .writefn = arm_cp_write_ignore, + .fieldoffset = offsetof(CPUARMState, cp15.c0_cpuid) }, + { .name = "CTR", + .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 1, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->ctr }, + { .name = "TCMTR", + .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 2, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "TLBTR", + .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 3, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + /* crn = 0 op1 = 0 crm = 3..7 : currently unassigned; we RAZ. */ + { .name = "DUMMY", + .cp = 15, .crn = 0, .crm = 3, .opc1 = 0, .opc2 = CP_ANY, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "DUMMY", + .cp = 15, .crn = 0, .crm = 4, .opc1 = 0, .opc2 = CP_ANY, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "DUMMY", + .cp = 15, .crn = 0, .crm = 5, .opc1 = 0, .opc2 = CP_ANY, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "DUMMY", + .cp = 15, .crn = 0, .crm = 6, .opc1 = 0, .opc2 = CP_ANY, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "DUMMY", + .cp = 15, .crn = 0, .crm = 7, .opc1 = 0, .opc2 = CP_ANY, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + REGINFO_SENTINEL + }; + ARMCPRegInfo crn0_wi_reginfo = { + .name = "CRN0_WI", .cp = 15, .crn = 0, .crm = CP_ANY, + .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_W, + .type = ARM_CP_NOP | ARM_CP_OVERRIDE + }; + if (arm_feature(env, ARM_FEATURE_OMAPCP) || + arm_feature(env, ARM_FEATURE_STRONGARM)) { + ARMCPRegInfo *r; + /* Register the blanket "writes ignored" value first to cover the + * whole space. Then define the specific ID registers, but update + * their access field to allow write access, so that they ignore + * writes rather than causing them to UNDEF. + */ + define_one_arm_cp_reg(cpu, &crn0_wi_reginfo); + for (r = id_cp_reginfo; r->type != ARM_CP_SENTINEL; r++) { + r->access = PL1_RW; + define_one_arm_cp_reg(cpu, r); + } + } else { + /* Just register the standard ID registers (read-only, meaning + * that writes will UNDEF). + */ + define_arm_cp_regs(cpu, id_cp_reginfo); + } + } + + if (arm_feature(env, ARM_FEATURE_AUXCR)) { + ARMCPRegInfo auxcr = { + .name = "AUXCR", .cp = 15, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST, + .resetvalue = cpu->reset_auxcr + }; + define_one_arm_cp_reg(cpu, &auxcr); + } + + /* Generic registers whose values depend on the implementation */ + { + ARMCPRegInfo sctlr = { + .name = "SCTLR", .cp = 15, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c1_sys), + .writefn = sctlr_write, .resetvalue = cpu->reset_sctlr + }; + if (arm_feature(env, ARM_FEATURE_XSCALE)) { + /* Normally we would always end the TB on an SCTLR write, but Linux + * arch/arm/mach-pxa/sleep.S expects two instructions following + * an MMU enable to execute from cache. Imitate this behaviour. + */ + sctlr.type |= ARM_CP_SUPPRESS_TB_END; + } + define_one_arm_cp_reg(cpu, &sctlr); + } +} + ARMCPU *cpu_arm_init(const char *cpu_model) { ARMCPU *cpu; @@ -137,6 +1192,107 @@ void arm_cpu_list(FILE *f, fprintf_function cpu_fprintf) g_slist_free(list); } +void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, + const ARMCPRegInfo *r, void *opaque) +{ + /* Define implementations of coprocessor registers. + * We store these in a hashtable because typically + * there are less than 150 registers in a space which + * is 16*16*16*8*8 = 262144 in size. + * Wildcarding is supported for the crm, opc1 and opc2 fields. + * If a register is defined twice then the second definition is + * used, so this can be used to define some generic registers and + * then override them with implementation specific variations. + * At least one of the original and the second definition should + * include ARM_CP_OVERRIDE in its type bits -- this is just a guard + * against accidental use. + */ + int crm, opc1, opc2; + int crmmin = (r->crm == CP_ANY) ? 0 : r->crm; + int crmmax = (r->crm == CP_ANY) ? 15 : r->crm; + int opc1min = (r->opc1 == CP_ANY) ? 0 : r->opc1; + int opc1max = (r->opc1 == CP_ANY) ? 7 : r->opc1; + int opc2min = (r->opc2 == CP_ANY) ? 0 : r->opc2; + int opc2max = (r->opc2 == CP_ANY) ? 7 : r->opc2; + /* 64 bit registers have only CRm and Opc1 fields */ + assert(!((r->type & ARM_CP_64BIT) && (r->opc2 || r->crn))); + /* Check that the register definition has enough info to handle + * reads and writes if they are permitted. + */ + if (!(r->type & (ARM_CP_SPECIAL|ARM_CP_CONST))) { + if (r->access & PL3_R) { + assert(r->fieldoffset || r->readfn); + } + if (r->access & PL3_W) { + assert(r->fieldoffset || r->writefn); + } + } + /* Bad type field probably means missing sentinel at end of reg list */ + assert(cptype_valid(r->type)); + for (crm = crmmin; crm <= crmmax; crm++) { + for (opc1 = opc1min; opc1 <= opc1max; opc1++) { + for (opc2 = opc2min; opc2 <= opc2max; opc2++) { + uint32_t *key = g_new(uint32_t, 1); + ARMCPRegInfo *r2 = g_memdup(r, sizeof(ARMCPRegInfo)); + int is64 = (r->type & ARM_CP_64BIT) ? 1 : 0; + *key = ENCODE_CP_REG(r->cp, is64, r->crn, crm, opc1, opc2); + r2->opaque = opaque; + /* Make sure reginfo passed to helpers for wildcarded regs + * has the correct crm/opc1/opc2 for this reg, not CP_ANY: + */ + r2->crm = crm; + r2->opc1 = opc1; + r2->opc2 = opc2; + /* Overriding of an existing definition must be explicitly + * requested. + */ + if (!(r->type & ARM_CP_OVERRIDE)) { + ARMCPRegInfo *oldreg; + oldreg = g_hash_table_lookup(cpu->cp_regs, key); + if (oldreg && !(oldreg->type & ARM_CP_OVERRIDE)) { + fprintf(stderr, "Register redefined: cp=%d %d bit " + "crn=%d crm=%d opc1=%d opc2=%d, " + "was %s, now %s\n", r2->cp, 32 + 32 * is64, + r2->crn, r2->crm, r2->opc1, r2->opc2, + oldreg->name, r2->name); + assert(0); + } + } + g_hash_table_insert(cpu->cp_regs, key, r2); + } + } + } +} + +void define_arm_cp_regs_with_opaque(ARMCPU *cpu, + const ARMCPRegInfo *regs, void *opaque) +{ + /* Define a whole list of registers */ + const ARMCPRegInfo *r; + for (r = regs; r->type != ARM_CP_SENTINEL; r++) { + define_one_arm_cp_reg_with_opaque(cpu, r, opaque); + } +} + +const ARMCPRegInfo *get_arm_cp_reginfo(ARMCPU *cpu, uint32_t encoded_cp) +{ + return g_hash_table_lookup(cpu->cp_regs, &encoded_cp); +} + +int arm_cp_write_ignore(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Helper coprocessor write function for write-ignore registers */ + return 0; +} + +int arm_cp_read_zero(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t *value) +{ + /* Helper coprocessor write function for read-as-zero registers */ + *value = 0; + return 0; +} + static int bad_mode_switch(CPUARMState *env, int mode) { /* Return true if it is not valid for us to switch to @@ -286,31 +1442,6 @@ int cpu_arm_handle_mmu_fault (CPUARMState *env, target_ulong address, int rw, } /* These should probably raise undefined insn exceptions. */ -void HELPER(set_cp)(CPUARMState *env, uint32_t insn, uint32_t val) -{ - int op1 = (insn >> 8) & 0xf; - cpu_abort(env, "cp%i insn %08x\n", op1, insn); - return; -} - -uint32_t HELPER(get_cp)(CPUARMState *env, uint32_t insn) -{ - int op1 = (insn >> 8) & 0xf; - cpu_abort(env, "cp%i insn %08x\n", op1, insn); - return 0; -} - -void HELPER(set_cp15)(CPUARMState *env, uint32_t insn, uint32_t val) -{ - cpu_abort(env, "cp15 insn %08x\n", insn); -} - -uint32_t HELPER(get_cp15)(CPUARMState *env, uint32_t insn) -{ - cpu_abort(env, "cp15 insn %08x\n", insn); -} - -/* These should probably raise undefined insn exceptions. */ void HELPER(v7m_msr)(CPUARMState *env, uint32_t reg, uint32_t val) { cpu_abort(env, "v7m_mrs %d\n", reg); @@ -1036,872 +2167,6 @@ target_phys_addr_t cpu_get_phys_page_debug(CPUARMState *env, target_ulong addr) return phys_addr; } -void HELPER(set_cp)(CPUARMState *env, uint32_t insn, uint32_t val) -{ - int cp_num = (insn >> 8) & 0xf; - int cp_info = (insn >> 5) & 7; - int src = (insn >> 16) & 0xf; - int operand = insn & 0xf; - - if (env->cp[cp_num].cp_write) - env->cp[cp_num].cp_write(env->cp[cp_num].opaque, - cp_info, src, operand, val); -} - -uint32_t HELPER(get_cp)(CPUARMState *env, uint32_t insn) -{ - int cp_num = (insn >> 8) & 0xf; - int cp_info = (insn >> 5) & 7; - int dest = (insn >> 16) & 0xf; - int operand = insn & 0xf; - - if (env->cp[cp_num].cp_read) - return env->cp[cp_num].cp_read(env->cp[cp_num].opaque, - cp_info, dest, operand); - return 0; -} - -/* Return basic MPU access permission bits. */ -static uint32_t simple_mpu_ap_bits(uint32_t val) -{ - uint32_t ret; - uint32_t mask; - int i; - ret = 0; - mask = 3; - for (i = 0; i < 16; i += 2) { - ret |= (val >> i) & mask; - mask <<= 2; - } - return ret; -} - -/* Pad basic MPU access permission bits to extended format. */ -static uint32_t extended_mpu_ap_bits(uint32_t val) -{ - uint32_t ret; - uint32_t mask; - int i; - ret = 0; - mask = 3; - for (i = 0; i < 16; i += 2) { - ret |= (val & mask) << i; - mask <<= 2; - } - return ret; -} - -void HELPER(set_cp15)(CPUARMState *env, uint32_t insn, uint32_t val) -{ - int op1; - int op2; - int crm; - - op1 = (insn >> 21) & 7; - op2 = (insn >> 5) & 7; - crm = insn & 0xf; - switch ((insn >> 16) & 0xf) { - case 0: - /* ID codes. */ - if (arm_feature(env, ARM_FEATURE_XSCALE)) - break; - if (arm_feature(env, ARM_FEATURE_OMAPCP)) - break; - if (arm_feature(env, ARM_FEATURE_V7) - && op1 == 2 && crm == 0 && op2 == 0) { - env->cp15.c0_cssel = val & 0xf; - break; - } - goto bad_reg; - case 1: /* System configuration. */ - if (arm_feature(env, ARM_FEATURE_V7) - && op1 == 0 && crm == 1 && op2 == 0) { - env->cp15.c1_scr = val; - break; - } - if (arm_feature(env, ARM_FEATURE_OMAPCP)) - op2 = 0; - switch (op2) { - case 0: - if (!arm_feature(env, ARM_FEATURE_XSCALE) || crm == 0) - env->cp15.c1_sys = val; - /* ??? Lots of these bits are not implemented. */ - /* This may enable/disable the MMU, so do a TLB flush. */ - tlb_flush(env, 1); - break; - case 1: /* Auxiliary control register. */ - if (arm_feature(env, ARM_FEATURE_XSCALE)) { - env->cp15.c1_xscaleauxcr = val; - break; - } - /* Not implemented. */ - break; - case 2: - if (arm_feature(env, ARM_FEATURE_XSCALE)) - goto bad_reg; - if (env->cp15.c1_coproc != val) { - env->cp15.c1_coproc = val; - /* ??? Is this safe when called from within a TB? */ - tb_flush(env); - } - break; - default: - goto bad_reg; - } - break; - case 2: /* MMU Page table control / MPU cache control. */ - if (arm_feature(env, ARM_FEATURE_MPU)) { - switch (op2) { - case 0: - env->cp15.c2_data = val; - break; - case 1: - env->cp15.c2_insn = val; - break; - default: - goto bad_reg; - } - } else { - switch (op2) { - case 0: - env->cp15.c2_base0 = val; - break; - case 1: - env->cp15.c2_base1 = val; - break; - case 2: - val &= 7; - env->cp15.c2_control = val; - env->cp15.c2_mask = ~(((uint32_t)0xffffffffu) >> val); - env->cp15.c2_base_mask = ~((uint32_t)0x3fffu >> val); - break; - default: - goto bad_reg; - } - } - break; - case 3: /* MMU Domain access control / MPU write buffer control. */ - env->cp15.c3 = val; - tlb_flush(env, 1); /* Flush TLB as domain not tracked in TLB */ - break; - case 4: /* Reserved. */ - goto bad_reg; - case 5: /* MMU Fault status / MPU access permission. */ - if (arm_feature(env, ARM_FEATURE_OMAPCP)) - op2 = 0; - switch (op2) { - case 0: - if (arm_feature(env, ARM_FEATURE_MPU)) - val = extended_mpu_ap_bits(val); - env->cp15.c5_data = val; - break; - case 1: - if (arm_feature(env, ARM_FEATURE_MPU)) - val = extended_mpu_ap_bits(val); - env->cp15.c5_insn = val; - break; - case 2: - if (!arm_feature(env, ARM_FEATURE_MPU)) - goto bad_reg; - env->cp15.c5_data = val; - break; - case 3: - if (!arm_feature(env, ARM_FEATURE_MPU)) - goto bad_reg; - env->cp15.c5_insn = val; - break; - default: - goto bad_reg; - } - break; - case 6: /* MMU Fault address / MPU base/size. */ - if (arm_feature(env, ARM_FEATURE_MPU)) { - if (crm >= 8) - goto bad_reg; - env->cp15.c6_region[crm] = val; - } else { - if (arm_feature(env, ARM_FEATURE_OMAPCP)) - op2 = 0; - switch (op2) { - case 0: - env->cp15.c6_data = val; - break; - case 1: /* ??? This is WFAR on armv6 */ - case 2: - env->cp15.c6_insn = val; - break; - default: - goto bad_reg; - } - } - break; - case 7: /* Cache control. */ - env->cp15.c15_i_max = 0x000; - env->cp15.c15_i_min = 0xff0; - if (op1 != 0) { - goto bad_reg; - } - /* No cache, so nothing to do except VA->PA translations. */ - if (arm_feature(env, ARM_FEATURE_VAPA)) { - switch (crm) { - case 4: - if (arm_feature(env, ARM_FEATURE_V7)) { - env->cp15.c7_par = val & 0xfffff6ff; - } else { - env->cp15.c7_par = val & 0xfffff1ff; - } - break; - case 8: { - uint32_t phys_addr; - target_ulong page_size; - int prot; - int ret, is_user = op2 & 2; - int access_type = op2 & 1; - - if (op2 & 4) { - /* Other states are only available with TrustZone */ - goto bad_reg; - } - ret = get_phys_addr(env, val, access_type, is_user, - &phys_addr, &prot, &page_size); - if (ret == 0) { - /* We do not set any attribute bits in the PAR */ - if (page_size == (1 << 24) - && arm_feature(env, ARM_FEATURE_V7)) { - env->cp15.c7_par = (phys_addr & 0xff000000) | 1 << 1; - } else { - env->cp15.c7_par = phys_addr & 0xfffff000; - } - } else { - env->cp15.c7_par = ((ret & (10 << 1)) >> 5) | - ((ret & (12 << 1)) >> 6) | - ((ret & 0xf) << 1) | 1; - } - break; - } - } - } - break; - case 8: /* MMU TLB control. */ - switch (op2) { - case 0: /* Invalidate all (TLBIALL) */ - tlb_flush(env, 1); - break; - case 1: /* Invalidate single TLB entry by MVA and ASID (TLBIMVA) */ - tlb_flush_page(env, val & TARGET_PAGE_MASK); - break; - case 2: /* Invalidate by ASID (TLBIASID) */ - tlb_flush(env, val == 0); - break; - case 3: /* Invalidate single entry by MVA, all ASIDs (TLBIMVAA) */ - tlb_flush_page(env, val & TARGET_PAGE_MASK); - break; - default: - goto bad_reg; - } - break; - case 9: - if (arm_feature(env, ARM_FEATURE_OMAPCP)) - break; - if (arm_feature(env, ARM_FEATURE_STRONGARM)) - break; /* Ignore ReadBuffer access */ - switch (crm) { - case 0: /* Cache lockdown. */ - switch (op1) { - case 0: /* L1 cache. */ - switch (op2) { - case 0: - env->cp15.c9_data = val; - break; - case 1: - env->cp15.c9_insn = val; - break; - default: - goto bad_reg; - } - break; - case 1: /* L2 cache. */ - /* Ignore writes to L2 lockdown/auxiliary registers. */ - break; - default: - goto bad_reg; - } - break; - case 1: /* TCM memory region registers. */ - /* Not implemented. */ - goto bad_reg; - case 12: /* Performance monitor control */ - /* Performance monitors are implementation defined in v7, - * but with an ARM recommended set of registers, which we - * follow (although we don't actually implement any counters) - */ - if (!arm_feature(env, ARM_FEATURE_V7)) { - goto bad_reg; - } - switch (op2) { - case 0: /* performance monitor control register */ - /* only the DP, X, D and E bits are writable */ - env->cp15.c9_pmcr &= ~0x39; - env->cp15.c9_pmcr |= (val & 0x39); - break; - case 1: /* Count enable set register */ - val &= (1 << 31); - env->cp15.c9_pmcnten |= val; - break; - case 2: /* Count enable clear */ - val &= (1 << 31); - env->cp15.c9_pmcnten &= ~val; - break; - case 3: /* Overflow flag status */ - env->cp15.c9_pmovsr &= ~val; - break; - case 4: /* Software increment */ - /* RAZ/WI since we don't implement the software-count event */ - break; - case 5: /* Event counter selection register */ - /* Since we don't implement any events, writing to this register - * is actually UNPREDICTABLE. So we choose to RAZ/WI. - */ - break; - default: - goto bad_reg; - } - break; - case 13: /* Performance counters */ - if (!arm_feature(env, ARM_FEATURE_V7)) { - goto bad_reg; - } - switch (op2) { - case 0: /* Cycle count register: not implemented, so RAZ/WI */ - break; - case 1: /* Event type select */ - env->cp15.c9_pmxevtyper = val & 0xff; - break; - case 2: /* Event count register */ - /* Unimplemented (we have no events), RAZ/WI */ - break; - default: - goto bad_reg; - } - break; - case 14: /* Performance monitor control */ - if (!arm_feature(env, ARM_FEATURE_V7)) { - goto bad_reg; - } - switch (op2) { - case 0: /* user enable */ - env->cp15.c9_pmuserenr = val & 1; - /* changes access rights for cp registers, so flush tbs */ - tb_flush(env); - break; - case 1: /* interrupt enable set */ - /* We have no event counters so only the C bit can be changed */ - val &= (1 << 31); - env->cp15.c9_pminten |= val; - break; - case 2: /* interrupt enable clear */ - val &= (1 << 31); - env->cp15.c9_pminten &= ~val; - break; - } - break; - default: - goto bad_reg; - } - break; - case 10: /* MMU TLB lockdown. */ - /* ??? TLB lockdown not implemented. */ - break; - case 12: /* Reserved. */ - goto bad_reg; - case 13: /* Process ID. */ - switch (op2) { - case 0: - /* Unlike real hardware the qemu TLB uses virtual addresses, - not modified virtual addresses, so this causes a TLB flush. - */ - if (env->cp15.c13_fcse != val) - tlb_flush(env, 1); - env->cp15.c13_fcse = val; - break; - case 1: - /* This changes the ASID, so do a TLB flush. */ - if (env->cp15.c13_context != val - && !arm_feature(env, ARM_FEATURE_MPU)) - tlb_flush(env, 0); - env->cp15.c13_context = val; - break; - default: - goto bad_reg; - } - break; - case 14: /* Generic timer */ - if (arm_feature(env, ARM_FEATURE_GENERIC_TIMER)) { - /* Dummy implementation: RAZ/WI for all */ - break; - } - goto bad_reg; - case 15: /* Implementation specific. */ - if (arm_feature(env, ARM_FEATURE_XSCALE)) { - if (op2 == 0 && crm == 1) { - if (env->cp15.c15_cpar != (val & 0x3fff)) { - /* Changes cp0 to cp13 behavior, so needs a TB flush. */ - tb_flush(env); - env->cp15.c15_cpar = val & 0x3fff; - } - break; - } - goto bad_reg; - } - if (arm_feature(env, ARM_FEATURE_OMAPCP)) { - switch (crm) { - case 0: - break; - case 1: /* Set TI925T configuration. */ - env->cp15.c15_ticonfig = val & 0xe7; - env->cp15.c0_cpuid = (val & (1 << 5)) ? /* OS_TYPE bit */ - ARM_CPUID_TI915T : ARM_CPUID_TI925T; - break; - case 2: /* Set I_max. */ - env->cp15.c15_i_max = val; - break; - case 3: /* Set I_min. */ - env->cp15.c15_i_min = val; - break; - case 4: /* Set thread-ID. */ - env->cp15.c15_threadid = val & 0xffff; - break; - case 8: /* Wait-for-interrupt (deprecated). */ - cpu_interrupt(env, CPU_INTERRUPT_HALT); - break; - default: - goto bad_reg; - } - } - if (ARM_CPUID(env) == ARM_CPUID_CORTEXA9) { - switch (crm) { - case 0: - if ((op1 == 0) && (op2 == 0)) { - env->cp15.c15_power_control = val; - } else if ((op1 == 0) && (op2 == 1)) { - env->cp15.c15_diagnostic = val; - } else if ((op1 == 0) && (op2 == 2)) { - env->cp15.c15_power_diagnostic = val; - } - default: - break; - } - } - break; - } - return; -bad_reg: - /* ??? For debugging only. Should raise illegal instruction exception. */ - cpu_abort(env, "Unimplemented cp15 register write (c%d, c%d, {%d, %d})\n", - (insn >> 16) & 0xf, crm, op1, op2); -} - -uint32_t HELPER(get_cp15)(CPUARMState *env, uint32_t insn) -{ - int op1; - int op2; - int crm; - - op1 = (insn >> 21) & 7; - op2 = (insn >> 5) & 7; - crm = insn & 0xf; - switch ((insn >> 16) & 0xf) { - case 0: /* ID codes. */ - switch (op1) { - case 0: - switch (crm) { - case 0: - switch (op2) { - case 0: /* Device ID. */ - return env->cp15.c0_cpuid; - case 1: /* Cache Type. */ - return env->cp15.c0_cachetype; - case 2: /* TCM status. */ - return 0; - case 3: /* TLB type register. */ - return 0; /* No lockable TLB entries. */ - case 5: /* MPIDR */ - /* The MPIDR was standardised in v7; prior to - * this it was implemented only in the 11MPCore. - * For all other pre-v7 cores it does not exist. - */ - if (arm_feature(env, ARM_FEATURE_V7) || - ARM_CPUID(env) == ARM_CPUID_ARM11MPCORE) { - int mpidr = env->cpu_index; - /* We don't support setting cluster ID ([8..11]) - * so these bits always RAZ. - */ - if (arm_feature(env, ARM_FEATURE_V7MP)) { - mpidr |= (1 << 31); - /* Cores which are uniprocessor (non-coherent) - * but still implement the MP extensions set - * bit 30. (For instance, A9UP.) However we do - * not currently model any of those cores. - */ - } - return mpidr; - } - /* otherwise fall through to the unimplemented-reg case */ - default: - goto bad_reg; - } - case 1: - if (!arm_feature(env, ARM_FEATURE_V6)) - goto bad_reg; - return env->cp15.c0_c1[op2]; - case 2: - if (!arm_feature(env, ARM_FEATURE_V6)) - goto bad_reg; - return env->cp15.c0_c2[op2]; - case 3: case 4: case 5: case 6: case 7: - return 0; - default: - goto bad_reg; - } - case 1: - /* These registers aren't documented on arm11 cores. However - Linux looks at them anyway. */ - if (!arm_feature(env, ARM_FEATURE_V6)) - goto bad_reg; - if (crm != 0) - goto bad_reg; - if (!arm_feature(env, ARM_FEATURE_V7)) - return 0; - - switch (op2) { - case 0: - return env->cp15.c0_ccsid[env->cp15.c0_cssel]; - case 1: - return env->cp15.c0_clid; - case 7: - return 0; - } - goto bad_reg; - case 2: - if (op2 != 0 || crm != 0) - goto bad_reg; - return env->cp15.c0_cssel; - default: - goto bad_reg; - } - case 1: /* System configuration. */ - if (arm_feature(env, ARM_FEATURE_V7) - && op1 == 0 && crm == 1 && op2 == 0) { - return env->cp15.c1_scr; - } - if (arm_feature(env, ARM_FEATURE_OMAPCP)) - op2 = 0; - switch (op2) { - case 0: /* Control register. */ - return env->cp15.c1_sys; - case 1: /* Auxiliary control register. */ - if (arm_feature(env, ARM_FEATURE_XSCALE)) - return env->cp15.c1_xscaleauxcr; - if (!arm_feature(env, ARM_FEATURE_AUXCR)) - goto bad_reg; - switch (ARM_CPUID(env)) { - case ARM_CPUID_ARM1026: - return 1; - case ARM_CPUID_ARM1136: - case ARM_CPUID_ARM1136_R2: - case ARM_CPUID_ARM1176: - return 7; - case ARM_CPUID_ARM11MPCORE: - return 1; - case ARM_CPUID_CORTEXA8: - return 2; - case ARM_CPUID_CORTEXA9: - case ARM_CPUID_CORTEXA15: - return 0; - default: - goto bad_reg; - } - case 2: /* Coprocessor access register. */ - if (arm_feature(env, ARM_FEATURE_XSCALE)) - goto bad_reg; - return env->cp15.c1_coproc; - default: - goto bad_reg; - } - case 2: /* MMU Page table control / MPU cache control. */ - if (arm_feature(env, ARM_FEATURE_MPU)) { - switch (op2) { - case 0: - return env->cp15.c2_data; - break; - case 1: - return env->cp15.c2_insn; - break; - default: - goto bad_reg; - } - } else { - switch (op2) { - case 0: - return env->cp15.c2_base0; - case 1: - return env->cp15.c2_base1; - case 2: - return env->cp15.c2_control; - default: - goto bad_reg; - } - } - case 3: /* MMU Domain access control / MPU write buffer control. */ - return env->cp15.c3; - case 4: /* Reserved. */ - goto bad_reg; - case 5: /* MMU Fault status / MPU access permission. */ - if (arm_feature(env, ARM_FEATURE_OMAPCP)) - op2 = 0; - switch (op2) { - case 0: - if (arm_feature(env, ARM_FEATURE_MPU)) - return simple_mpu_ap_bits(env->cp15.c5_data); - return env->cp15.c5_data; - case 1: - if (arm_feature(env, ARM_FEATURE_MPU)) - return simple_mpu_ap_bits(env->cp15.c5_insn); - return env->cp15.c5_insn; - case 2: - if (!arm_feature(env, ARM_FEATURE_MPU)) - goto bad_reg; - return env->cp15.c5_data; - case 3: - if (!arm_feature(env, ARM_FEATURE_MPU)) - goto bad_reg; - return env->cp15.c5_insn; - default: - goto bad_reg; - } - case 6: /* MMU Fault address. */ - if (arm_feature(env, ARM_FEATURE_MPU)) { - if (crm >= 8) - goto bad_reg; - return env->cp15.c6_region[crm]; - } else { - if (arm_feature(env, ARM_FEATURE_OMAPCP)) - op2 = 0; - switch (op2) { - case 0: - return env->cp15.c6_data; - case 1: - if (arm_feature(env, ARM_FEATURE_V6)) { - /* Watchpoint Fault Adrress. */ - return 0; /* Not implemented. */ - } else { - /* Instruction Fault Adrress. */ - /* Arm9 doesn't have an IFAR, but implementing it anyway - shouldn't do any harm. */ - return env->cp15.c6_insn; - } - case 2: - if (arm_feature(env, ARM_FEATURE_V6)) { - /* Instruction Fault Adrress. */ - return env->cp15.c6_insn; - } else { - goto bad_reg; - } - default: - goto bad_reg; - } - } - case 7: /* Cache control. */ - if (crm == 4 && op1 == 0 && op2 == 0) { - return env->cp15.c7_par; - } - /* FIXME: Should only clear Z flag if destination is r15. */ - env->ZF = 0; - return 0; - case 8: /* MMU TLB control. */ - goto bad_reg; - case 9: - switch (crm) { - case 0: /* Cache lockdown */ - switch (op1) { - case 0: /* L1 cache. */ - if (arm_feature(env, ARM_FEATURE_OMAPCP)) { - return 0; - } - switch (op2) { - case 0: - return env->cp15.c9_data; - case 1: - return env->cp15.c9_insn; - default: - goto bad_reg; - } - case 1: /* L2 cache */ - /* L2 Lockdown and Auxiliary control. */ - switch (op2) { - case 0: - /* L2 cache lockdown (A8 only) */ - return 0; - case 2: - /* L2 cache auxiliary control (A8) or control (A15) */ - if (ARM_CPUID(env) == ARM_CPUID_CORTEXA15) { - /* Linux wants the number of processors from here. - * Might as well set the interrupt-controller bit too. - */ - return ((smp_cpus - 1) << 24) | (1 << 23); - } - return 0; - case 3: - /* L2 cache extended control (A15) */ - return 0; - default: - goto bad_reg; - } - default: - goto bad_reg; - } - break; - case 12: /* Performance monitor control */ - if (!arm_feature(env, ARM_FEATURE_V7)) { - goto bad_reg; - } - switch (op2) { - case 0: /* performance monitor control register */ - return env->cp15.c9_pmcr; - case 1: /* count enable set */ - case 2: /* count enable clear */ - return env->cp15.c9_pmcnten; - case 3: /* overflow flag status */ - return env->cp15.c9_pmovsr; - case 4: /* software increment */ - case 5: /* event counter selection register */ - return 0; /* Unimplemented, RAZ/WI */ - default: - goto bad_reg; - } - case 13: /* Performance counters */ - if (!arm_feature(env, ARM_FEATURE_V7)) { - goto bad_reg; - } - switch (op2) { - case 1: /* Event type select */ - return env->cp15.c9_pmxevtyper; - case 0: /* Cycle count register */ - case 2: /* Event count register */ - /* Unimplemented, so RAZ/WI */ - return 0; - default: - goto bad_reg; - } - case 14: /* Performance monitor control */ - if (!arm_feature(env, ARM_FEATURE_V7)) { - goto bad_reg; - } - switch (op2) { - case 0: /* user enable */ - return env->cp15.c9_pmuserenr; - case 1: /* interrupt enable set */ - case 2: /* interrupt enable clear */ - return env->cp15.c9_pminten; - default: - goto bad_reg; - } - default: - goto bad_reg; - } - break; - case 10: /* MMU TLB lockdown. */ - /* ??? TLB lockdown not implemented. */ - return 0; - case 11: /* TCM DMA control. */ - case 12: /* Reserved. */ - goto bad_reg; - case 13: /* Process ID. */ - switch (op2) { - case 0: - return env->cp15.c13_fcse; - case 1: - return env->cp15.c13_context; - default: - goto bad_reg; - } - case 14: /* Generic timer */ - if (arm_feature(env, ARM_FEATURE_GENERIC_TIMER)) { - /* Dummy implementation: RAZ/WI for all */ - return 0; - } - goto bad_reg; - case 15: /* Implementation specific. */ - if (arm_feature(env, ARM_FEATURE_XSCALE)) { - if (op2 == 0 && crm == 1) - return env->cp15.c15_cpar; - - goto bad_reg; - } - if (arm_feature(env, ARM_FEATURE_OMAPCP)) { - switch (crm) { - case 0: - return 0; - case 1: /* Read TI925T configuration. */ - return env->cp15.c15_ticonfig; - case 2: /* Read I_max. */ - return env->cp15.c15_i_max; - case 3: /* Read I_min. */ - return env->cp15.c15_i_min; - case 4: /* Read thread-ID. */ - return env->cp15.c15_threadid; - case 8: /* TI925T_status */ - return 0; - } - /* TODO: Peripheral port remap register: - * On OMAP2 mcr p15, 0, rn, c15, c2, 4 sets up the interrupt - * controller base address at $rn & ~0xfff and map size of - * 0x200 << ($rn & 0xfff), when MMU is off. */ - goto bad_reg; - } - if (ARM_CPUID(env) == ARM_CPUID_CORTEXA9) { - switch (crm) { - case 0: - if ((op1 == 4) && (op2 == 0)) { - /* The config_base_address should hold the value of - * the peripheral base. ARM should get this from a CPU - * object property, but that support isn't available in - * December 2011. Default to 0 for now and board models - * that care can set it by a private hook */ - return env->cp15.c15_config_base_address; - } else if ((op1 == 0) && (op2 == 0)) { - /* power_control should be set to maximum latency. Again, - default to 0 and set by private hook */ - return env->cp15.c15_power_control; - } else if ((op1 == 0) && (op2 == 1)) { - return env->cp15.c15_diagnostic; - } else if ((op1 == 0) && (op2 == 2)) { - return env->cp15.c15_power_diagnostic; - } - break; - case 1: /* NEON Busy */ - return 0; - case 5: /* tlb lockdown */ - case 6: - case 7: - if ((op1 == 5) && (op2 == 2)) { - return 0; - } - break; - default: - break; - } - goto bad_reg; - } - return 0; - } -bad_reg: - /* ??? For debugging only. Should raise illegal instruction exception. */ - cpu_abort(env, "Unimplemented cp15 register read (c%d, c%d, {%d, %d})\n", - (insn >> 16) & 0xf, crm, op1, op2); - return 0; -} - void HELPER(set_r13_banked)(CPUARMState *env, uint32_t mode, uint32_t val) { if ((env->uncached_cpsr & CPSR_M) == mode) { @@ -2024,20 +2289,6 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t reg, uint32_t val) } } -void cpu_arm_set_cp_io(CPUARMState *env, int cpnum, - ARMReadCPFunc *cp_read, ARMWriteCPFunc *cp_write, - void *opaque) -{ - if (cpnum < 0 || cpnum > 14) { - cpu_abort(env, "Bad coprocessor number: %i\n", cpnum); - return; - } - - env->cp[cpnum].cp_read = cp_read; - env->cp[cpnum].cp_write = cp_write; - env->cp[cpnum].opaque = opaque; -} - #endif /* Note that signed overflow is undefined in C. The following routines are @@ -2868,12 +3119,3 @@ float64 VFP_HELPER(muladd, d)(float64 a, float64 b, float64 c, void *fpstp) float_status *fpst = fpstp; return float64_muladd(a, b, c, 0, fpst); } - -void HELPER(set_teecr)(CPUARMState *env, uint32_t val) -{ - val &= 1; - if (env->teecr != val) { - env->teecr = val; - tb_flush(env); - } -} diff --git a/target-arm/helper.h b/target-arm/helper.h index 16dd5fcc89..21e9cfe05f 100644 --- a/target-arm/helper.h +++ b/target-arm/helper.h @@ -59,11 +59,10 @@ DEF_HELPER_0(cpsr_read, i32) DEF_HELPER_3(v7m_msr, void, env, i32, i32) DEF_HELPER_2(v7m_mrs, i32, env, i32) -DEF_HELPER_3(set_cp15, void, env, i32, i32) -DEF_HELPER_2(get_cp15, i32, env, i32) - -DEF_HELPER_3(set_cp, void, env, i32, i32) -DEF_HELPER_2(get_cp, i32, env, i32) +DEF_HELPER_3(set_cp_reg, void, env, ptr, i32) +DEF_HELPER_2(get_cp_reg, i32, env, ptr) +DEF_HELPER_3(set_cp_reg64, void, env, ptr, i64) +DEF_HELPER_2(get_cp_reg64, i64, env, ptr) DEF_HELPER_2(get_r13_banked, i32, env, i32) DEF_HELPER_3(set_r13_banked, void, env, i32, i32) @@ -459,8 +458,6 @@ DEF_HELPER_3(iwmmxt_muladdsl, i64, i64, i32, i32) DEF_HELPER_3(iwmmxt_muladdsw, i64, i64, i32, i32) DEF_HELPER_3(iwmmxt_muladdswl, i64, i64, i32, i32) -DEF_HELPER_2(set_teecr, void, env, i32) - DEF_HELPER_3(neon_unzip8, void, env, i32, i32) DEF_HELPER_3(neon_unzip16, void, env, i32, i32) DEF_HELPER_3(neon_qunzip8, void, env, i32, i32) diff --git a/target-arm/machine.c b/target-arm/machine.c index f66b8dfa1f..a2a75fbd19 100644 --- a/target-arm/machine.c +++ b/target-arm/machine.c @@ -21,7 +21,6 @@ void cpu_save(QEMUFile *f, void *opaque) qemu_put_be32(f, env->fiq_regs[i]); } qemu_put_be32(f, env->cp15.c0_cpuid); - qemu_put_be32(f, env->cp15.c0_cachetype); qemu_put_be32(f, env->cp15.c0_cssel); qemu_put_be32(f, env->cp15.c1_sys); qemu_put_be32(f, env->cp15.c1_coproc); @@ -139,7 +138,6 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id) env->fiq_regs[i] = qemu_get_be32(f); } env->cp15.c0_cpuid = qemu_get_be32(f); - env->cp15.c0_cachetype = qemu_get_be32(f); env->cp15.c0_cssel = qemu_get_be32(f); env->cp15.c1_sys = qemu_get_be32(f); env->cp15.c1_coproc = qemu_get_be32(f); diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index b53369d7cb..490111c22f 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -23,13 +23,11 @@ #define SIGNBIT (uint32_t)0x80000000 #define SIGNBIT64 ((uint64_t)1 << 63) -#if !defined(CONFIG_USER_ONLY) static void raise_exception(int tt) { env->exception_index = tt; cpu_loop_exit(env); } -#endif uint32_t HELPER(neon_tbl)(uint32_t ireg, uint32_t def, uint32_t rn, uint32_t maxindex) @@ -287,6 +285,46 @@ void HELPER(set_user_reg)(uint32_t regno, uint32_t val) } } +void HELPER(set_cp_reg)(CPUARMState *env, void *rip, uint32_t value) +{ + const ARMCPRegInfo *ri = rip; + int excp = ri->writefn(env, ri, value); + if (excp) { + raise_exception(excp); + } +} + +uint32_t HELPER(get_cp_reg)(CPUARMState *env, void *rip) +{ + const ARMCPRegInfo *ri = rip; + uint64_t value; + int excp = ri->readfn(env, ri, &value); + if (excp) { + raise_exception(excp); + } + return value; +} + +void HELPER(set_cp_reg64)(CPUARMState *env, void *rip, uint64_t value) +{ + const ARMCPRegInfo *ri = rip; + int excp = ri->writefn(env, ri, value); + if (excp) { + raise_exception(excp); + } +} + +uint64_t HELPER(get_cp_reg64)(CPUARMState *env, void *rip) +{ + const ARMCPRegInfo *ri = rip; + uint64_t value; + int excp = ri->readfn(env, ri, &value); + if (excp) { + raise_exception(excp); + } + return value; +} + /* ??? Flag setting arithmetic is awkward because we need to do comparisons. The only way to do that in TCG is a conditional branch, which clobbers all our temporaries. For now implement these as helper functions. */ diff --git a/target-arm/translate.c b/target-arm/translate.c index 437d9dbf0e..a2a0ecddad 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -2439,226 +2439,6 @@ static int disas_dsp_insn(CPUARMState *env, DisasContext *s, uint32_t insn) return 1; } -/* Disassemble system coprocessor instruction. Return nonzero if - instruction is not defined. */ -static int disas_cp_insn(CPUARMState *env, DisasContext *s, uint32_t insn) -{ - TCGv tmp, tmp2; - uint32_t rd = (insn >> 12) & 0xf; - uint32_t cp = (insn >> 8) & 0xf; - if (IS_USER(s)) { - return 1; - } - - if (insn & ARM_CP_RW_BIT) { - if (!env->cp[cp].cp_read) - return 1; - gen_set_pc_im(s->pc); - tmp = tcg_temp_new_i32(); - tmp2 = tcg_const_i32(insn); - gen_helper_get_cp(tmp, cpu_env, tmp2); - tcg_temp_free(tmp2); - store_reg(s, rd, tmp); - } else { - if (!env->cp[cp].cp_write) - return 1; - gen_set_pc_im(s->pc); - tmp = load_reg(s, rd); - tmp2 = tcg_const_i32(insn); - gen_helper_set_cp(cpu_env, tmp2, tmp); - tcg_temp_free(tmp2); - tcg_temp_free_i32(tmp); - } - return 0; -} - -static int cp15_user_ok(CPUARMState *env, uint32_t insn) -{ - int cpn = (insn >> 16) & 0xf; - int cpm = insn & 0xf; - int op = ((insn >> 5) & 7) | ((insn >> 18) & 0x38); - - if (arm_feature(env, ARM_FEATURE_V7) && cpn == 9) { - /* Performance monitor registers fall into three categories: - * (a) always UNDEF in usermode - * (b) UNDEF only if PMUSERENR.EN is 0 - * (c) always read OK and UNDEF on write (PMUSERENR only) - */ - if ((cpm == 12 && (op < 6)) || - (cpm == 13 && (op < 3))) { - return env->cp15.c9_pmuserenr; - } else if (cpm == 14 && op == 0 && (insn & ARM_CP_RW_BIT)) { - /* PMUSERENR, read only */ - return 1; - } - return 0; - } - - if (cpn == 13 && cpm == 0) { - /* TLS register. */ - if (op == 2 || (op == 3 && (insn & ARM_CP_RW_BIT))) - return 1; - } - return 0; -} - -static int cp15_tls_load_store(CPUARMState *env, DisasContext *s, uint32_t insn, uint32_t rd) -{ - TCGv tmp; - int cpn = (insn >> 16) & 0xf; - int cpm = insn & 0xf; - int op = ((insn >> 5) & 7) | ((insn >> 18) & 0x38); - - if (!arm_feature(env, ARM_FEATURE_V6K)) - return 0; - - if (!(cpn == 13 && cpm == 0)) - return 0; - - if (insn & ARM_CP_RW_BIT) { - switch (op) { - case 2: - tmp = load_cpu_field(cp15.c13_tls1); - break; - case 3: - tmp = load_cpu_field(cp15.c13_tls2); - break; - case 4: - tmp = load_cpu_field(cp15.c13_tls3); - break; - default: - return 0; - } - store_reg(s, rd, tmp); - - } else { - tmp = load_reg(s, rd); - switch (op) { - case 2: - store_cpu_field(tmp, cp15.c13_tls1); - break; - case 3: - store_cpu_field(tmp, cp15.c13_tls2); - break; - case 4: - store_cpu_field(tmp, cp15.c13_tls3); - break; - default: - tcg_temp_free_i32(tmp); - return 0; - } - } - return 1; -} - -/* Disassemble system coprocessor (cp15) instruction. Return nonzero if - instruction is not defined. */ -static int disas_cp15_insn(CPUARMState *env, DisasContext *s, uint32_t insn) -{ - uint32_t rd; - TCGv tmp, tmp2; - - /* M profile cores use memory mapped registers instead of cp15. */ - if (arm_feature(env, ARM_FEATURE_M)) - return 1; - - if ((insn & (1 << 25)) == 0) { - if (insn & (1 << 20)) { - /* mrrc */ - return 1; - } - /* mcrr. Used for block cache operations, so implement as no-op. */ - return 0; - } - if ((insn & (1 << 4)) == 0) { - /* cdp */ - return 1; - } - /* We special case a number of cp15 instructions which were used - * for things which are real instructions in ARMv7. This allows - * them to work in linux-user mode which doesn't provide functional - * get_cp15/set_cp15 helpers, and is more efficient anyway. - */ - switch ((insn & 0x0fff0fff)) { - case 0x0e070f90: - /* 0,c7,c0,4: Standard v6 WFI (also used in some pre-v6 cores). - * In v7, this must NOP. - */ - if (IS_USER(s)) { - return 1; - } - if (!arm_feature(env, ARM_FEATURE_V7)) { - /* Wait for interrupt. */ - gen_set_pc_im(s->pc); - s->is_jmp = DISAS_WFI; - } - return 0; - case 0x0e070f58: - /* 0,c7,c8,2: Not all pre-v6 cores implemented this WFI, - * so this is slightly over-broad. - */ - if (!IS_USER(s) && !arm_feature(env, ARM_FEATURE_V6)) { - /* Wait for interrupt. */ - gen_set_pc_im(s->pc); - s->is_jmp = DISAS_WFI; - return 0; - } - /* Otherwise continue to handle via helper function. - * In particular, on v7 and some v6 cores this is one of - * the VA-PA registers. - */ - break; - case 0x0e070f3d: - /* 0,c7,c13,1: prefetch-by-MVA in v6, NOP in v7 */ - if (arm_feature(env, ARM_FEATURE_V6)) { - return IS_USER(s) ? 1 : 0; - } - break; - case 0x0e070f95: /* 0,c7,c5,4 : ISB */ - case 0x0e070f9a: /* 0,c7,c10,4: DSB */ - case 0x0e070fba: /* 0,c7,c10,5: DMB */ - /* Barriers in both v6 and v7 */ - if (arm_feature(env, ARM_FEATURE_V6)) { - return 0; - } - break; - default: - break; - } - - if (IS_USER(s) && !cp15_user_ok(env, insn)) { - return 1; - } - - rd = (insn >> 12) & 0xf; - - if (cp15_tls_load_store(env, s, insn, rd)) - return 0; - - tmp2 = tcg_const_i32(insn); - if (insn & ARM_CP_RW_BIT) { - tmp = tcg_temp_new_i32(); - gen_helper_get_cp15(tmp, cpu_env, tmp2); - /* If the destination register is r15 then sets condition codes. */ - if (rd != 15) - store_reg(s, rd, tmp); - else - tcg_temp_free_i32(tmp); - } else { - tmp = load_reg(s, rd); - gen_helper_set_cp15(cpu_env, tmp2, tmp); - tcg_temp_free_i32(tmp); - /* Normally we would always end the TB here, but Linux - * arch/arm/mach-pxa/sleep.S expects two instructions following - * an MMU enable to execute from cache. Imitate this behaviour. */ - if (!arm_feature(env, ARM_FEATURE_XSCALE) || - (insn & 0x0fff0fff) != 0x0e010f10) - gen_lookup_tb(s); - } - tcg_temp_free_i32(tmp2); - return 0; -} - #define VFP_REG_SHR(x, n) (((n) > 0) ? (x) >> (n) : (x) << -(n)) #define VFP_SREG(insn, bigbit, smallbit) \ ((VFP_REG_SHR(insn, bigbit - 1) & 0x1e) | (((insn) >> (smallbit)) & 1)) @@ -6388,104 +6168,18 @@ static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t ins return 0; } -static int disas_cp14_read(CPUARMState * env, DisasContext *s, uint32_t insn) -{ - int crn = (insn >> 16) & 0xf; - int crm = insn & 0xf; - int op1 = (insn >> 21) & 7; - int op2 = (insn >> 5) & 7; - int rt = (insn >> 12) & 0xf; - TCGv tmp; - - /* Minimal set of debug registers, since we don't support debug */ - if (op1 == 0 && crn == 0 && op2 == 0) { - switch (crm) { - case 0: - /* DBGDIDR: just RAZ. In particular this means the - * "debug architecture version" bits will read as - * a reserved value, which should cause Linux to - * not try to use the debug hardware. - */ - tmp = tcg_const_i32(0); - store_reg(s, rt, tmp); - return 0; - case 1: - case 2: - /* DBGDRAR and DBGDSAR: v7 only. Always RAZ since we - * don't implement memory mapped debug components - */ - if (ENABLE_ARCH_7) { - tmp = tcg_const_i32(0); - store_reg(s, rt, tmp); - return 0; - } - break; - default: - break; - } - } - - if (arm_feature(env, ARM_FEATURE_THUMB2EE)) { - if (op1 == 6 && crn == 0 && crm == 0 && op2 == 0) { - /* TEECR */ - if (IS_USER(s)) - return 1; - tmp = load_cpu_field(teecr); - store_reg(s, rt, tmp); - return 0; - } - if (op1 == 6 && crn == 1 && crm == 0 && op2 == 0) { - /* TEEHBR */ - if (IS_USER(s) && (env->teecr & 1)) - return 1; - tmp = load_cpu_field(teehbr); - store_reg(s, rt, tmp); - return 0; - } - } - return 1; -} - -static int disas_cp14_write(CPUARMState * env, DisasContext *s, uint32_t insn) -{ - int crn = (insn >> 16) & 0xf; - int crm = insn & 0xf; - int op1 = (insn >> 21) & 7; - int op2 = (insn >> 5) & 7; - int rt = (insn >> 12) & 0xf; - TCGv tmp; - - if (arm_feature(env, ARM_FEATURE_THUMB2EE)) { - if (op1 == 6 && crn == 0 && crm == 0 && op2 == 0) { - /* TEECR */ - if (IS_USER(s)) - return 1; - tmp = load_reg(s, rt); - gen_helper_set_teecr(cpu_env, tmp); - tcg_temp_free_i32(tmp); - return 0; - } - if (op1 == 6 && crn == 1 && crm == 0 && op2 == 0) { - /* TEEHBR */ - if (IS_USER(s) && (env->teecr & 1)) - return 1; - tmp = load_reg(s, rt); - store_cpu_field(tmp, teehbr); - return 0; - } - } - return 1; -} - static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn) { - int cpnum; + int cpnum, is64, crn, crm, opc1, opc2, isread, rt, rt2; + const ARMCPRegInfo *ri; + ARMCPU *cpu = arm_env_get_cpu(env); cpnum = (insn >> 8) & 0xf; if (arm_feature(env, ARM_FEATURE_XSCALE) && ((env->cp15.c15_cpar ^ 0x3fff) & (1 << cpnum))) return 1; + /* First check for coprocessor space used for actual instructions */ switch (cpnum) { case 0: case 1: @@ -6498,22 +6192,154 @@ static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn) case 10: case 11: return disas_vfp_insn (env, s, insn); - case 14: - /* Coprocessors 7-15 are architecturally reserved by ARM. - Unfortunately Intel decided to ignore this. */ - if (arm_feature(env, ARM_FEATURE_XSCALE)) - goto board; - if (insn & (1 << 20)) - return disas_cp14_read(env, s, insn); - else - return disas_cp14_write(env, s, insn); - case 15: - return disas_cp15_insn (env, s, insn); default: - board: - /* Unknown coprocessor. See if the board has hooked it. */ - return disas_cp_insn (env, s, insn); + break; } + + /* Otherwise treat as a generic register access */ + is64 = (insn & (1 << 25)) == 0; + if (!is64 && ((insn & (1 << 4)) == 0)) { + /* cdp */ + return 1; + } + + crm = insn & 0xf; + if (is64) { + crn = 0; + opc1 = (insn >> 4) & 0xf; + opc2 = 0; + rt2 = (insn >> 16) & 0xf; + } else { + crn = (insn >> 16) & 0xf; + opc1 = (insn >> 21) & 7; + opc2 = (insn >> 5) & 7; + rt2 = 0; + } + isread = (insn >> 20) & 1; + rt = (insn >> 12) & 0xf; + + ri = get_arm_cp_reginfo(cpu, + ENCODE_CP_REG(cpnum, is64, crn, crm, opc1, opc2)); + if (ri) { + /* Check access permissions */ + if (!cp_access_ok(env, ri, isread)) { + return 1; + } + + /* Handle special cases first */ + switch (ri->type & ~(ARM_CP_FLAG_MASK & ~ARM_CP_SPECIAL)) { + case ARM_CP_NOP: + return 0; + case ARM_CP_WFI: + if (isread) { + return 1; + } + gen_set_pc_im(s->pc); + s->is_jmp = DISAS_WFI; + break; + default: + break; + } + + if (isread) { + /* Read */ + if (is64) { + TCGv_i64 tmp64; + TCGv_i32 tmp; + if (ri->type & ARM_CP_CONST) { + tmp64 = tcg_const_i64(ri->resetvalue); + } else if (ri->readfn) { + TCGv_ptr tmpptr; + gen_set_pc_im(s->pc); + tmp64 = tcg_temp_new_i64(); + tmpptr = tcg_const_ptr(ri); + gen_helper_get_cp_reg64(tmp64, cpu_env, tmpptr); + tcg_temp_free_ptr(tmpptr); + } else { + tmp64 = tcg_temp_new_i64(); + tcg_gen_ld_i64(tmp64, cpu_env, ri->fieldoffset); + } + tmp = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(tmp, tmp64); + store_reg(s, rt, tmp); + tcg_gen_shri_i64(tmp64, tmp64, 32); + tcg_gen_trunc_i64_i32(tmp, tmp64); + store_reg(s, rt2, tmp); + } else { + TCGv tmp; + if (ri->type & ARM_CP_CONST) { + tmp = tcg_const_i32(ri->resetvalue); + } else if (ri->readfn) { + TCGv_ptr tmpptr; + gen_set_pc_im(s->pc); + tmp = tcg_temp_new_i32(); + tmpptr = tcg_const_ptr(ri); + gen_helper_get_cp_reg(tmp, cpu_env, tmpptr); + tcg_temp_free_ptr(tmpptr); + } else { + tmp = load_cpu_offset(ri->fieldoffset); + } + if (rt == 15) { + /* Destination register of r15 for 32 bit loads sets + * the condition codes from the high 4 bits of the value + */ + gen_set_nzcv(tmp); + tcg_temp_free_i32(tmp); + } else { + store_reg(s, rt, tmp); + } + } + } else { + /* Write */ + if (ri->type & ARM_CP_CONST) { + /* If not forbidden by access permissions, treat as WI */ + return 0; + } + + if (is64) { + TCGv tmplo, tmphi; + TCGv_i64 tmp64 = tcg_temp_new_i64(); + tmplo = load_reg(s, rt); + tmphi = load_reg(s, rt2); + tcg_gen_concat_i32_i64(tmp64, tmplo, tmphi); + tcg_temp_free_i32(tmplo); + tcg_temp_free_i32(tmphi); + if (ri->writefn) { + TCGv_ptr tmpptr = tcg_const_ptr(ri); + gen_set_pc_im(s->pc); + gen_helper_set_cp_reg64(cpu_env, tmpptr, tmp64); + tcg_temp_free_ptr(tmpptr); + } else { + tcg_gen_st_i64(tmp64, cpu_env, ri->fieldoffset); + } + tcg_temp_free_i64(tmp64); + } else { + if (ri->writefn) { + TCGv tmp; + TCGv_ptr tmpptr; + gen_set_pc_im(s->pc); + tmp = load_reg(s, rt); + tmpptr = tcg_const_ptr(ri); + gen_helper_set_cp_reg(cpu_env, tmpptr, tmp); + tcg_temp_free_ptr(tmpptr); + tcg_temp_free_i32(tmp); + } else { + TCGv tmp = load_reg(s, rt); + store_cpu_offset(tmp, ri->fieldoffset); + } + } + /* We default to ending the TB on a coprocessor register write, + * but allow this to be suppressed by the register definition + * (usually only necessary to work around guest bugs). + */ + if (!(ri->type & ARM_CP_SUPPRESS_TB_END)) { + gen_lookup_tb(s); + } + } + return 0; + } + + return 1; } diff --git a/target-ppc/Makefile.objs b/target-ppc/Makefile.objs index f78161004e..237a0ed4f7 100644 --- a/target-ppc/Makefile.objs +++ b/target-ppc/Makefile.objs @@ -1,6 +1,12 @@ -obj-y += translate.o op_helper.o helper.o +obj-y += translate.o helper.o obj-$(CONFIG_SOFTMMU) += machine.o obj-$(CONFIG_KVM) += kvm.o kvm_ppc.o -obj-y += op_helper.o helper.o - -$(obj)/op_helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS) +obj-y += helper.o +obj-y += excp_helper.o +obj-y += fpu_helper.o +obj-y += int_helper.o +obj-y += mmu_helper.o +obj-y += timebase_helper.o +obj-y += misc_helper.o +obj-y += mem_helper.o +obj-y += mpic_helper.o diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 77a28580af..ca2fc2198e 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -119,6 +119,8 @@ enum powerpc_mmu_t { POWERPC_MMU_620 = POWERPC_MMU_64 | 0x00000002, /* Architecture 2.06 variant */ POWERPC_MMU_2_06 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG | 0x00000003, + /* Architecture 2.06 "degraded" (no 1T segments) */ + POWERPC_MMU_2_06d = POWERPC_MMU_64 | 0x00000003, #endif /* defined(TARGET_PPC64) */ }; @@ -691,7 +693,7 @@ enum { #define MAS1_VALID 0x80000000 #define MAS2_EPN_SHIFT 12 -#define MAS2_EPN_MASK (0xfffff << MAS2_EPN_SHIFT) +#define MAS2_EPN_MASK (~0ULL << MAS2_EPN_SHIFT) #define MAS2_ACM_SHIFT 6 #define MAS2_ACM (1 << MAS2_ACM_SHIFT) @@ -874,6 +876,29 @@ enum { #define DBELL_PIRTAG_MASK 0x3fff /*****************************************************************************/ +/* Segment page size information, used by recent hash MMUs + * The format of this structure mirrors kvm_ppc_smmu_info + */ + +#define PPC_PAGE_SIZES_MAX_SZ 8 + +struct ppc_one_page_size { + uint32_t page_shift; /* Page shift (or 0) */ + uint32_t pte_enc; /* Encoding in the HPTE (>>12) */ +}; + +struct ppc_one_seg_page_size { + uint32_t page_shift; /* Base page shift of segment (or 0) */ + uint32_t slb_enc; /* SLB encoding for BookS */ + struct ppc_one_page_size enc[PPC_PAGE_SIZES_MAX_SZ]; +}; + +struct ppc_segment_page_sizes { + struct ppc_one_seg_page_size sps[PPC_PAGE_SIZES_MAX_SZ]; +}; + + +/*****************************************************************************/ /* The whole PowerPC CPU context */ #define NB_MMU_MODES 3 @@ -889,6 +914,9 @@ struct ppc_def_t { powerpc_input_t bus_model; uint32_t flags; int bfd_mach; +#if defined(TARGET_PPC64) + const struct ppc_segment_page_sizes *sps; +#endif void (*init_proc)(CPUPPCState *env); int (*check_pow)(CPUPPCState *env); }; @@ -1012,6 +1040,9 @@ struct CPUPPCState { uint32_t flags; uint64_t insns_flags; uint64_t insns_flags2; +#if defined(TARGET_PPC64) + struct ppc_segment_page_sizes sps; +#endif #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) target_phys_addr_t vpa; @@ -1035,6 +1066,7 @@ struct CPUPPCState { target_ulong ivor_mask; target_ulong ivpr_mask; target_ulong hreset_vector; + target_phys_addr_t mpic_cpu_base; #endif /* Those resources are used only during code translation */ @@ -1117,27 +1149,12 @@ int get_physical_address (CPUPPCState *env, mmu_ctx_t *ctx, target_ulong vaddr, void do_interrupt (CPUPPCState *env); void ppc_hw_interrupt (CPUPPCState *env); -void cpu_dump_rfi (target_ulong RA, target_ulong msr); - #if !defined(CONFIG_USER_ONLY) -void ppc6xx_tlb_store (CPUPPCState *env, target_ulong EPN, int way, int is_code, - target_ulong pte0, target_ulong pte1); -void ppc_store_ibatu (CPUPPCState *env, int nr, target_ulong value); -void ppc_store_ibatl (CPUPPCState *env, int nr, target_ulong value); -void ppc_store_dbatu (CPUPPCState *env, int nr, target_ulong value); -void ppc_store_dbatl (CPUPPCState *env, int nr, target_ulong value); -void ppc_store_ibatu_601 (CPUPPCState *env, int nr, target_ulong value); -void ppc_store_ibatl_601 (CPUPPCState *env, int nr, target_ulong value); void ppc_store_sdr1 (CPUPPCState *env, target_ulong value); #if defined(TARGET_PPC64) void ppc_store_asr (CPUPPCState *env, target_ulong value); -target_ulong ppc_load_slb (CPUPPCState *env, int slb_nr); -target_ulong ppc_load_sr (CPUPPCState *env, int sr_nr); int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs); -int ppc_load_slb_esid (CPUPPCState *env, target_ulong rb, target_ulong *rt); -int ppc_load_slb_vsid (CPUPPCState *env, target_ulong rb, target_ulong *rt); #endif /* defined(TARGET_PPC64) */ -void ppc_store_sr (CPUPPCState *env, int srnum, target_ulong value); #endif /* !defined(CONFIG_USER_ONLY) */ void ppc_store_msr (CPUPPCState *env, target_ulong value); @@ -1176,19 +1193,11 @@ void store_booke_tcr (CPUPPCState *env, target_ulong val); void store_booke_tsr (CPUPPCState *env, target_ulong val); void booke206_flush_tlb(CPUPPCState *env, int flags, const int check_iprot); target_phys_addr_t booke206_tlb_to_page_size(CPUPPCState *env, ppcmas_tlb_t *tlb); -int ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, - target_phys_addr_t *raddrp, target_ulong address, - uint32_t pid, int ext, int i); int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, target_phys_addr_t *raddrp, target_ulong address, uint32_t pid); void ppc_tlb_invalidate_all (CPUPPCState *env); void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr); -#if defined(TARGET_PPC64) -void ppc_slb_invalidate_all (CPUPPCState *env); -void ppc_slb_invalidate_one (CPUPPCState *env, uint64_t T0); -#endif -int ppcemb_tlb_search (CPUPPCState *env, target_ulong address, uint32_t pid); #endif #endif @@ -1387,6 +1396,7 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp) #define SPR_BOOKE_TLB1PS (0x159) #define SPR_BOOKE_TLB2PS (0x15A) #define SPR_BOOKE_TLB3PS (0x15B) +#define SPR_BOOKE_MAS7_MAS3 (0x174) #define SPR_BOOKE_IVOR0 (0x190) #define SPR_BOOKE_IVOR1 (0x191) #define SPR_BOOKE_IVOR2 (0x192) @@ -1754,6 +1764,27 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp) #define SPR_604_HID15 (0x3FF) #define SPR_E500_SVR (0x3FF) +/* Disable MAS Interrupt Updates for Hypervisor */ +#define EPCR_DMIUH (1 << 22) +/* Disable Guest TLB Management Instructions */ +#define EPCR_DGTMI (1 << 23) +/* Guest Interrupt Computation Mode */ +#define EPCR_GICM (1 << 24) +/* Interrupt Computation Mode */ +#define EPCR_ICM (1 << 25) +/* Disable Embedded Hypervisor Debug */ +#define EPCR_DUVD (1 << 26) +/* Instruction Storage Interrupt Directed to Guest State */ +#define EPCR_ISIGS (1 << 27) +/* Data Storage Interrupt Directed to Guest State */ +#define EPCR_DSIGS (1 << 28) +/* Instruction TLB Error Interrupt Directed to Guest State */ +#define EPCR_ITLBGS (1 << 29) +/* Data TLB Error Interrupt Directed to Guest State */ +#define EPCR_DTLBGS (1 << 30) +/* External Input Interrupt Directed to Guest State */ +#define EPCR_EXTGS (1 << 31) + /*****************************************************************************/ /* PowerPC Instructions types definitions */ enum { @@ -2182,6 +2213,15 @@ static inline uint32_t booke206_tlbnps(CPUPPCState *env, const int tlbn) #endif +static inline bool msr_is_64bit(CPUPPCState *env, target_ulong msr) +{ + if (env->mmu_model == POWERPC_MMU_BOOKE206) { + return msr & (1ULL << MSR_CM); + } + + return msr & (1ULL << MSR_SF); +} + extern void (*cpu_ppc_hypercall)(CPUPPCState *); static inline bool cpu_has_work(CPUPPCState *env) diff --git a/target-ppc/excp_helper.c b/target-ppc/excp_helper.c new file mode 100644 index 0000000000..1a593f6f3f --- /dev/null +++ b/target-ppc/excp_helper.c @@ -0,0 +1,969 @@ +/* + * PowerPC exception emulation helpers for QEMU. + * + * Copyright (c) 2003-2007 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, see <http://www.gnu.org/licenses/>. + */ +#include "cpu.h" +#include "helper.h" + +#include "helper_regs.h" + +//#define DEBUG_OP +//#define DEBUG_EXCEPTIONS + +#ifdef DEBUG_EXCEPTIONS +# define LOG_EXCP(...) qemu_log(__VA_ARGS__) +#else +# define LOG_EXCP(...) do { } while (0) +#endif + +/*****************************************************************************/ +/* PowerPC Hypercall emulation */ + +void (*cpu_ppc_hypercall)(CPUPPCState *); + +/*****************************************************************************/ +/* Exception processing */ +#if defined(CONFIG_USER_ONLY) +void do_interrupt(CPUPPCState *env) +{ + env->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; +} + +void ppc_hw_interrupt(CPUPPCState *env) +{ + env->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; +} +#else /* defined(CONFIG_USER_ONLY) */ +static inline void dump_syscall(CPUPPCState *env) +{ + qemu_log_mask(CPU_LOG_INT, "syscall r0=%016" PRIx64 " r3=%016" PRIx64 + " r4=%016" PRIx64 " r5=%016" PRIx64 " r6=%016" PRIx64 + " nip=" TARGET_FMT_lx "\n", + ppc_dump_gpr(env, 0), ppc_dump_gpr(env, 3), + ppc_dump_gpr(env, 4), ppc_dump_gpr(env, 5), + ppc_dump_gpr(env, 6), env->nip); +} + +/* Note that this function should be greatly optimized + * when called with a constant excp, from ppc_hw_interrupt + */ +static inline void powerpc_excp(CPUPPCState *env, int excp_model, int excp) +{ + target_ulong msr, new_msr, vector; + int srr0, srr1, asrr0, asrr1; + int lpes0, lpes1, lev; + + if (0) { + /* XXX: find a suitable condition to enable the hypervisor mode */ + lpes0 = (env->spr[SPR_LPCR] >> 1) & 1; + lpes1 = (env->spr[SPR_LPCR] >> 2) & 1; + } else { + /* Those values ensure we won't enter the hypervisor mode */ + lpes0 = 0; + lpes1 = 1; + } + + qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx + " => %08x (%02x)\n", env->nip, excp, env->error_code); + + /* new srr1 value excluding must-be-zero bits */ + msr = env->msr & ~0x783f0000ULL; + + /* new interrupt handler msr */ + new_msr = env->msr & ((target_ulong)1 << MSR_ME); + + /* target registers */ + srr0 = SPR_SRR0; + srr1 = SPR_SRR1; + asrr0 = -1; + asrr1 = -1; + + switch (excp) { + case POWERPC_EXCP_NONE: + /* Should never happen */ + return; + case POWERPC_EXCP_CRITICAL: /* Critical input */ + switch (excp_model) { + case POWERPC_EXCP_40x: + srr0 = SPR_40x_SRR2; + srr1 = SPR_40x_SRR3; + break; + case POWERPC_EXCP_BOOKE: + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + break; + case POWERPC_EXCP_G2: + break; + default: + goto excp_invalid; + } + goto store_next; + case POWERPC_EXCP_MCHECK: /* Machine check exception */ + if (msr_me == 0) { + /* Machine check exception is not enabled. + * Enter checkstop state. + */ + if (qemu_log_enabled()) { + qemu_log("Machine check while not allowed. " + "Entering checkstop state\n"); + } else { + fprintf(stderr, "Machine check while not allowed. " + "Entering checkstop state\n"); + } + env->halted = 1; + env->interrupt_request |= CPU_INTERRUPT_EXITTB; + } + if (0) { + /* XXX: find a suitable condition to enable the hypervisor mode */ + new_msr |= (target_ulong)MSR_HVB; + } + + /* machine check exceptions don't have ME set */ + new_msr &= ~((target_ulong)1 << MSR_ME); + + /* XXX: should also have something loaded in DAR / DSISR */ + switch (excp_model) { + case POWERPC_EXCP_40x: + srr0 = SPR_40x_SRR2; + srr1 = SPR_40x_SRR3; + break; + case POWERPC_EXCP_BOOKE: + srr0 = SPR_BOOKE_MCSRR0; + srr1 = SPR_BOOKE_MCSRR1; + asrr0 = SPR_BOOKE_CSRR0; + asrr1 = SPR_BOOKE_CSRR1; + break; + default: + break; + } + goto store_next; + case POWERPC_EXCP_DSI: /* Data storage exception */ + LOG_EXCP("DSI exception: DSISR=" TARGET_FMT_lx" DAR=" TARGET_FMT_lx + "\n", env->spr[SPR_DSISR], env->spr[SPR_DAR]); + if (lpes1 == 0) { + new_msr |= (target_ulong)MSR_HVB; + } + goto store_next; + case POWERPC_EXCP_ISI: /* Instruction storage exception */ + LOG_EXCP("ISI exception: msr=" TARGET_FMT_lx ", nip=" TARGET_FMT_lx + "\n", msr, env->nip); + if (lpes1 == 0) { + new_msr |= (target_ulong)MSR_HVB; + } + msr |= env->error_code; + goto store_next; + case POWERPC_EXCP_EXTERNAL: /* External input */ + if (lpes0 == 1) { + new_msr |= (target_ulong)MSR_HVB; + } + goto store_next; + case POWERPC_EXCP_ALIGN: /* Alignment exception */ + if (lpes1 == 0) { + new_msr |= (target_ulong)MSR_HVB; + } + /* XXX: this is false */ + /* Get rS/rD and rA from faulting opcode */ + env->spr[SPR_DSISR] |= (cpu_ldl_code(env, (env->nip - 4)) + & 0x03FF0000) >> 16; + goto store_current; + case POWERPC_EXCP_PROGRAM: /* Program exception */ + switch (env->error_code & ~0xF) { + case POWERPC_EXCP_FP: + if ((msr_fe0 == 0 && msr_fe1 == 0) || msr_fp == 0) { + LOG_EXCP("Ignore floating point exception\n"); + env->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; + return; + } + if (lpes1 == 0) { + new_msr |= (target_ulong)MSR_HVB; + } + msr |= 0x00100000; + if (msr_fe0 == msr_fe1) { + goto store_next; + } + msr |= 0x00010000; + break; + case POWERPC_EXCP_INVAL: + LOG_EXCP("Invalid instruction at " TARGET_FMT_lx "\n", env->nip); + if (lpes1 == 0) { + new_msr |= (target_ulong)MSR_HVB; + } + msr |= 0x00080000; + env->spr[SPR_BOOKE_ESR] = ESR_PIL; + break; + case POWERPC_EXCP_PRIV: + if (lpes1 == 0) { + new_msr |= (target_ulong)MSR_HVB; + } + msr |= 0x00040000; + env->spr[SPR_BOOKE_ESR] = ESR_PPR; + break; + case POWERPC_EXCP_TRAP: + if (lpes1 == 0) { + new_msr |= (target_ulong)MSR_HVB; + } + msr |= 0x00020000; + env->spr[SPR_BOOKE_ESR] = ESR_PTR; + break; + default: + /* Should never occur */ + cpu_abort(env, "Invalid program exception %d. Aborting\n", + env->error_code); + break; + } + goto store_current; + case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ + if (lpes1 == 0) { + new_msr |= (target_ulong)MSR_HVB; + } + goto store_current; + case POWERPC_EXCP_SYSCALL: /* System call exception */ + dump_syscall(env); + lev = env->error_code; + if ((lev == 1) && cpu_ppc_hypercall) { + cpu_ppc_hypercall(env); + return; + } + if (lev == 1 || (lpes0 == 0 && lpes1 == 0)) { + new_msr |= (target_ulong)MSR_HVB; + } + goto store_next; + case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ + goto store_current; + case POWERPC_EXCP_DECR: /* Decrementer exception */ + if (lpes1 == 0) { + new_msr |= (target_ulong)MSR_HVB; + } + goto store_next; + case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ + /* FIT on 4xx */ + LOG_EXCP("FIT exception\n"); + goto store_next; + case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ + LOG_EXCP("WDT exception\n"); + switch (excp_model) { + case POWERPC_EXCP_BOOKE: + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + break; + default: + break; + } + goto store_next; + case POWERPC_EXCP_DTLB: /* Data TLB error */ + goto store_next; + case POWERPC_EXCP_ITLB: /* Instruction TLB error */ + goto store_next; + case POWERPC_EXCP_DEBUG: /* Debug interrupt */ + switch (excp_model) { + case POWERPC_EXCP_BOOKE: + srr0 = SPR_BOOKE_DSRR0; + srr1 = SPR_BOOKE_DSRR1; + asrr0 = SPR_BOOKE_CSRR0; + asrr1 = SPR_BOOKE_CSRR1; + break; + default: + break; + } + /* XXX: TODO */ + cpu_abort(env, "Debug exception is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable */ + env->spr[SPR_BOOKE_ESR] = ESR_SPV; + goto store_current; + case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ + /* XXX: TODO */ + cpu_abort(env, "Embedded floating point data exception " + "is not implemented yet !\n"); + env->spr[SPR_BOOKE_ESR] = ESR_SPV; + goto store_next; + case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */ + /* XXX: TODO */ + cpu_abort(env, "Embedded floating point round exception " + "is not implemented yet !\n"); + env->spr[SPR_BOOKE_ESR] = ESR_SPV; + goto store_next; + case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */ + /* XXX: TODO */ + cpu_abort(env, + "Performance counter exception is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ + goto store_next; + case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + goto store_next; + case POWERPC_EXCP_RESET: /* System reset exception */ + if (msr_pow) { + /* indicate that we resumed from power save mode */ + msr |= 0x10000; + } else { + new_msr &= ~((target_ulong)1 << MSR_ME); + } + + if (0) { + /* XXX: find a suitable condition to enable the hypervisor mode */ + new_msr |= (target_ulong)MSR_HVB; + } + goto store_next; + case POWERPC_EXCP_DSEG: /* Data segment exception */ + if (lpes1 == 0) { + new_msr |= (target_ulong)MSR_HVB; + } + goto store_next; + case POWERPC_EXCP_ISEG: /* Instruction segment exception */ + if (lpes1 == 0) { + new_msr |= (target_ulong)MSR_HVB; + } + goto store_next; + case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + goto store_next; + case POWERPC_EXCP_TRACE: /* Trace exception */ + if (lpes1 == 0) { + new_msr |= (target_ulong)MSR_HVB; + } + goto store_next; + case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + goto store_next; + case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + goto store_next; + case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + goto store_next; + case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */ + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + goto store_next; + case POWERPC_EXCP_VPU: /* Vector unavailable exception */ + if (lpes1 == 0) { + new_msr |= (target_ulong)MSR_HVB; + } + goto store_current; + case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ + LOG_EXCP("PIT exception\n"); + goto store_next; + case POWERPC_EXCP_IO: /* IO error exception */ + /* XXX: TODO */ + cpu_abort(env, "601 IO error exception is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_RUNM: /* Run mode exception */ + /* XXX: TODO */ + cpu_abort(env, "601 run mode exception is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_EMUL: /* Emulation trap exception */ + /* XXX: TODO */ + cpu_abort(env, "602 emulation trap exception " + "is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ + if (lpes1 == 0) { /* XXX: check this */ + new_msr |= (target_ulong)MSR_HVB; + } + switch (excp_model) { + case POWERPC_EXCP_602: + case POWERPC_EXCP_603: + case POWERPC_EXCP_603E: + case POWERPC_EXCP_G2: + goto tlb_miss_tgpr; + case POWERPC_EXCP_7x5: + goto tlb_miss; + case POWERPC_EXCP_74xx: + goto tlb_miss_74xx; + default: + cpu_abort(env, "Invalid instruction TLB miss exception\n"); + break; + } + break; + case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ + if (lpes1 == 0) { /* XXX: check this */ + new_msr |= (target_ulong)MSR_HVB; + } + switch (excp_model) { + case POWERPC_EXCP_602: + case POWERPC_EXCP_603: + case POWERPC_EXCP_603E: + case POWERPC_EXCP_G2: + goto tlb_miss_tgpr; + case POWERPC_EXCP_7x5: + goto tlb_miss; + case POWERPC_EXCP_74xx: + goto tlb_miss_74xx; + default: + cpu_abort(env, "Invalid data load TLB miss exception\n"); + break; + } + break; + case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ + if (lpes1 == 0) { /* XXX: check this */ + new_msr |= (target_ulong)MSR_HVB; + } + switch (excp_model) { + case POWERPC_EXCP_602: + case POWERPC_EXCP_603: + case POWERPC_EXCP_603E: + case POWERPC_EXCP_G2: + tlb_miss_tgpr: + /* Swap temporary saved registers with GPRs */ + if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) { + new_msr |= (target_ulong)1 << MSR_TGPR; + hreg_swap_gpr_tgpr(env); + } + goto tlb_miss; + case POWERPC_EXCP_7x5: + tlb_miss: +#if defined(DEBUG_SOFTWARE_TLB) + if (qemu_log_enabled()) { + const char *es; + target_ulong *miss, *cmp; + int en; + + if (excp == POWERPC_EXCP_IFTLB) { + es = "I"; + en = 'I'; + miss = &env->spr[SPR_IMISS]; + cmp = &env->spr[SPR_ICMP]; + } else { + if (excp == POWERPC_EXCP_DLTLB) { + es = "DL"; + } else { + es = "DS"; + } + en = 'D'; + miss = &env->spr[SPR_DMISS]; + cmp = &env->spr[SPR_DCMP]; + } + qemu_log("6xx %sTLB miss: %cM " TARGET_FMT_lx " %cC " + TARGET_FMT_lx " H1 " TARGET_FMT_lx " H2 " + TARGET_FMT_lx " %08x\n", es, en, *miss, en, *cmp, + env->spr[SPR_HASH1], env->spr[SPR_HASH2], + env->error_code); + } +#endif + 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) & (env->nb_ways - 1)) << 17; + break; + case POWERPC_EXCP_74xx: + tlb_miss_74xx: +#if defined(DEBUG_SOFTWARE_TLB) + if (qemu_log_enabled()) { + const char *es; + target_ulong *miss, *cmp; + int en; + + if (excp == POWERPC_EXCP_IFTLB) { + es = "I"; + en = 'I'; + miss = &env->spr[SPR_TLBMISS]; + cmp = &env->spr[SPR_PTEHI]; + } else { + if (excp == POWERPC_EXCP_DLTLB) { + es = "DL"; + } else { + es = "DS"; + } + en = 'D'; + miss = &env->spr[SPR_TLBMISS]; + cmp = &env->spr[SPR_PTEHI]; + } + qemu_log("74xx %sTLB miss: %cM " TARGET_FMT_lx " %cC " + TARGET_FMT_lx " %08x\n", es, en, *miss, en, *cmp, + env->error_code); + } +#endif + msr |= env->error_code; /* key bit */ + break; + default: + cpu_abort(env, "Invalid data store TLB miss exception\n"); + break; + } + goto store_next; + case POWERPC_EXCP_FPA: /* Floating-point assist exception */ + /* XXX: TODO */ + cpu_abort(env, "Floating point assist exception " + "is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_DABR: /* Data address breakpoint */ + /* XXX: TODO */ + cpu_abort(env, "DABR exception is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ + /* XXX: TODO */ + cpu_abort(env, "IABR exception is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_SMI: /* System management interrupt */ + /* XXX: TODO */ + cpu_abort(env, "SMI exception is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_THERM: /* Thermal interrupt */ + /* XXX: TODO */ + cpu_abort(env, "Thermal management exception " + "is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ + if (lpes1 == 0) { + new_msr |= (target_ulong)MSR_HVB; + } + /* XXX: TODO */ + cpu_abort(env, + "Performance counter exception is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_VPUA: /* Vector assist exception */ + /* XXX: TODO */ + cpu_abort(env, "VPU assist exception is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_SOFTP: /* Soft patch exception */ + /* XXX: TODO */ + cpu_abort(env, + "970 soft-patch exception is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_MAINT: /* Maintenance exception */ + /* XXX: TODO */ + cpu_abort(env, + "970 maintenance exception is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */ + /* XXX: TODO */ + cpu_abort(env, "Maskable external exception " + "is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */ + /* XXX: TODO */ + cpu_abort(env, "Non maskable external exception " + "is not implemented yet !\n"); + goto store_next; + default: + excp_invalid: + cpu_abort(env, "Invalid PowerPC exception %d. Aborting\n", excp); + break; + store_current: + /* save current instruction location */ + env->spr[srr0] = env->nip - 4; + break; + store_next: + /* save next instruction location */ + env->spr[srr0] = env->nip; + break; + } + /* Save MSR */ + env->spr[srr1] = msr; + /* If any alternate SRR register are defined, duplicate saved values */ + if (asrr0 != -1) { + env->spr[asrr0] = env->spr[srr0]; + } + if (asrr1 != -1) { + env->spr[asrr1] = env->spr[srr1]; + } + /* If we disactivated any translation, flush TLBs */ + if (msr & ((1 << MSR_IR) | (1 << MSR_DR))) { + tlb_flush(env, 1); + } + + if (msr_ile) { + new_msr |= (target_ulong)1 << MSR_LE; + } + + /* Jump to handler */ + vector = env->excp_vectors[excp]; + if (vector == (target_ulong)-1ULL) { + cpu_abort(env, "Raised an exception without defined vector %d\n", + excp); + } + vector |= env->excp_prefix; +#if defined(TARGET_PPC64) + if (excp_model == POWERPC_EXCP_BOOKE) { + if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) { + /* Cat.64-bit: EPCR.ICM is copied to MSR.CM */ + new_msr |= (target_ulong)1 << MSR_CM; + } else { + vector = (uint32_t)vector; + } + } else { + if (!msr_isf && !(env->mmu_model & POWERPC_MMU_64)) { + vector = (uint32_t)vector; + } else { + new_msr |= (target_ulong)1 << MSR_SF; + } + } +#endif + /* XXX: we don't use hreg_store_msr here as already have treated + * any special case that could occur. Just store MSR and update hflags + */ + env->msr = new_msr & env->msr_mask; + hreg_compute_hflags(env); + env->nip = vector; + /* Reset exception state */ + env->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; + + if ((env->mmu_model == POWERPC_MMU_BOOKE) || + (env->mmu_model == POWERPC_MMU_BOOKE206)) { + /* XXX: The BookE changes address space when switching modes, + we should probably implement that as different MMU indexes, + but for the moment we do it the slow way and flush all. */ + tlb_flush(env, 1); + } +} + +void do_interrupt(CPUPPCState *env) +{ + powerpc_excp(env, env->excp_model, env->exception_index); +} + +void ppc_hw_interrupt(CPUPPCState *env) +{ + int hdice; + +#if 0 + qemu_log_mask(CPU_LOG_INT, "%s: %p pending %08x req %08x me %d ee %d\n", + __func__, env, env->pending_interrupts, + env->interrupt_request, (int)msr_me, (int)msr_ee); +#endif + /* External reset */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_RESET)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_RESET); + powerpc_excp(env, env->excp_model, POWERPC_EXCP_RESET); + return; + } + /* Machine check exception */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_MCK)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_MCK); + powerpc_excp(env, env->excp_model, POWERPC_EXCP_MCHECK); + return; + } +#if 0 /* TODO */ + /* External debug exception */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_DEBUG)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DEBUG); + powerpc_excp(env, env->excp_model, POWERPC_EXCP_DEBUG); + return; + } +#endif + if (0) { + /* XXX: find a suitable condition to enable the hypervisor mode */ + hdice = env->spr[SPR_LPCR] & 1; + } else { + hdice = 0; + } + if ((msr_ee != 0 || msr_hv == 0 || msr_pr != 0) && hdice != 0) { + /* Hypervisor decrementer exception */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR); + powerpc_excp(env, env->excp_model, POWERPC_EXCP_HDECR); + return; + } + } + if (msr_ce != 0) { + /* External critical interrupt */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_CEXT)) { + /* Taking a critical external interrupt does not clear the external + * critical interrupt status + */ +#if 0 + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_CEXT); +#endif + powerpc_excp(env, env->excp_model, POWERPC_EXCP_CRITICAL); + return; + } + } + if (msr_ee != 0) { + /* Watchdog timer on embedded PowerPC */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_WDT)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_WDT); + powerpc_excp(env, env->excp_model, POWERPC_EXCP_WDT); + return; + } + if (env->pending_interrupts & (1 << PPC_INTERRUPT_CDOORBELL)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_CDOORBELL); + powerpc_excp(env, env->excp_model, POWERPC_EXCP_DOORCI); + return; + } + /* Fixed interval timer on embedded PowerPC */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_FIT)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_FIT); + powerpc_excp(env, env->excp_model, POWERPC_EXCP_FIT); + return; + } + /* Programmable interval timer on embedded PowerPC */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_PIT)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PIT); + powerpc_excp(env, env->excp_model, POWERPC_EXCP_PIT); + return; + } + /* Decrementer exception */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_DECR)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DECR); + powerpc_excp(env, env->excp_model, POWERPC_EXCP_DECR); + return; + } + /* External interrupt */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) { + /* Taking an external interrupt does not clear the external + * interrupt status + */ +#if 0 + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_EXT); +#endif + powerpc_excp(env, env->excp_model, POWERPC_EXCP_EXTERNAL); + return; + } + if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL); + powerpc_excp(env, env->excp_model, POWERPC_EXCP_DOORI); + return; + } + if (env->pending_interrupts & (1 << PPC_INTERRUPT_PERFM)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PERFM); + powerpc_excp(env, env->excp_model, POWERPC_EXCP_PERFM); + return; + } + /* Thermal interrupt */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_THERM)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_THERM); + powerpc_excp(env, env->excp_model, POWERPC_EXCP_THERM); + return; + } + } +} +#endif /* !CONFIG_USER_ONLY */ + +#if defined(DEBUG_OP) +static void cpu_dump_rfi(target_ulong RA, target_ulong msr) +{ + qemu_log("Return from exception at " TARGET_FMT_lx " with flags " + TARGET_FMT_lx "\n", RA, msr); +} +#endif + +/*****************************************************************************/ +/* Exceptions processing helpers */ + +void helper_raise_exception_err(CPUPPCState *env, uint32_t exception, + uint32_t error_code) +{ +#if 0 + printf("Raise exception %3x code : %d\n", exception, error_code); +#endif + env->exception_index = exception; + env->error_code = error_code; + cpu_loop_exit(env); +} + +void helper_raise_exception(CPUPPCState *env, uint32_t exception) +{ + helper_raise_exception_err(env, exception, 0); +} + +#if !defined(CONFIG_USER_ONLY) +void helper_store_msr(CPUPPCState *env, target_ulong val) +{ + val = hreg_store_msr(env, val, 0); + if (val != 0) { + env->interrupt_request |= CPU_INTERRUPT_EXITTB; + helper_raise_exception(env, val); + } +} + +static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr, + target_ulong msrm, int keep_msrh) +{ +#if defined(TARGET_PPC64) + if (msr_is_64bit(env, msr)) { + nip = (uint64_t)nip; + msr &= (uint64_t)msrm; + } else { + nip = (uint32_t)nip; + msr = (uint32_t)(msr & msrm); + if (keep_msrh) { + msr |= env->msr & ~((uint64_t)0xFFFFFFFF); + } + } +#else + nip = (uint32_t)nip; + msr &= (uint32_t)msrm; +#endif + /* XXX: beware: this is false if VLE is supported */ + env->nip = nip & ~((target_ulong)0x00000003); + hreg_store_msr(env, msr, 1); +#if defined(DEBUG_OP) + cpu_dump_rfi(env->nip, env->msr); +#endif + /* No need to raise an exception here, + * as rfi is always the last insn of a TB + */ + env->interrupt_request |= CPU_INTERRUPT_EXITTB; +} + +void helper_rfi(CPUPPCState *env) +{ + do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1], + ~((target_ulong)0x783F0000), 1); +} + +#if defined(TARGET_PPC64) +void helper_rfid(CPUPPCState *env) +{ + do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1], + ~((target_ulong)0x783F0000), 0); +} + +void helper_hrfid(CPUPPCState *env) +{ + do_rfi(env, env->spr[SPR_HSRR0], env->spr[SPR_HSRR1], + ~((target_ulong)0x783F0000), 0); +} +#endif + +/*****************************************************************************/ +/* Embedded PowerPC specific helpers */ +void helper_40x_rfci(CPUPPCState *env) +{ + do_rfi(env, env->spr[SPR_40x_SRR2], env->spr[SPR_40x_SRR3], + ~((target_ulong)0xFFFF0000), 0); +} + +void helper_rfci(CPUPPCState *env) +{ + do_rfi(env, env->spr[SPR_BOOKE_CSRR0], SPR_BOOKE_CSRR1, + ~((target_ulong)0x3FFF0000), 0); +} + +void helper_rfdi(CPUPPCState *env) +{ + do_rfi(env, env->spr[SPR_BOOKE_DSRR0], SPR_BOOKE_DSRR1, + ~((target_ulong)0x3FFF0000), 0); +} + +void helper_rfmci(CPUPPCState *env) +{ + do_rfi(env, env->spr[SPR_BOOKE_MCSRR0], SPR_BOOKE_MCSRR1, + ~((target_ulong)0x3FFF0000), 0); +} +#endif + +void helper_tw(CPUPPCState *env, target_ulong arg1, target_ulong arg2, + uint32_t flags) +{ + if (!likely(!(((int32_t)arg1 < (int32_t)arg2 && (flags & 0x10)) || + ((int32_t)arg1 > (int32_t)arg2 && (flags & 0x08)) || + ((int32_t)arg1 == (int32_t)arg2 && (flags & 0x04)) || + ((uint32_t)arg1 < (uint32_t)arg2 && (flags & 0x02)) || + ((uint32_t)arg1 > (uint32_t)arg2 && (flags & 0x01))))) { + helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_TRAP); + } +} + +#if defined(TARGET_PPC64) +void helper_td(CPUPPCState *env, target_ulong arg1, target_ulong arg2, + uint32_t flags) +{ + if (!likely(!(((int64_t)arg1 < (int64_t)arg2 && (flags & 0x10)) || + ((int64_t)arg1 > (int64_t)arg2 && (flags & 0x08)) || + ((int64_t)arg1 == (int64_t)arg2 && (flags & 0x04)) || + ((uint64_t)arg1 < (uint64_t)arg2 && (flags & 0x02)) || + ((uint64_t)arg1 > (uint64_t)arg2 && (flags & 0x01))))) { + helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_TRAP); + } +} +#endif + +#if !defined(CONFIG_USER_ONLY) +/*****************************************************************************/ +/* PowerPC 601 specific instructions (POWER bridge) */ + +void helper_rfsvc(CPUPPCState *env) +{ + do_rfi(env, env->lr, env->ctr, 0x0000FFFF, 0); +} + +/* Embedded.Processor Control */ +static int dbell2irq(target_ulong rb) +{ + int msg = rb & DBELL_TYPE_MASK; + int irq = -1; + + switch (msg) { + case DBELL_TYPE_DBELL: + irq = PPC_INTERRUPT_DOORBELL; + break; + case DBELL_TYPE_DBELL_CRIT: + irq = PPC_INTERRUPT_CDOORBELL; + break; + case DBELL_TYPE_G_DBELL: + case DBELL_TYPE_G_DBELL_CRIT: + case DBELL_TYPE_G_DBELL_MC: + /* XXX implement */ + default: + break; + } + + return irq; +} + +void helper_msgclr(CPUPPCState *env, target_ulong rb) +{ + int irq = dbell2irq(rb); + + if (irq < 0) { + return; + } + + env->pending_interrupts &= ~(1 << irq); +} + +void helper_msgsnd(target_ulong rb) +{ + int irq = dbell2irq(rb); + int pir = rb & DBELL_PIRTAG_MASK; + CPUPPCState *cenv; + + if (irq < 0) { + return; + } + + for (cenv = first_cpu; cenv != NULL; cenv = cenv->next_cpu) { + if ((rb & DBELL_BRDCAST) || (cenv->spr[SPR_BOOKE_PIR] == pir)) { + cenv->pending_interrupts |= 1 << irq; + cpu_interrupt(cenv, CPU_INTERRUPT_HARD); + } + } +} +#endif diff --git a/target-ppc/fpu_helper.c b/target-ppc/fpu_helper.c new file mode 100644 index 0000000000..9d67926209 --- /dev/null +++ b/target-ppc/fpu_helper.c @@ -0,0 +1,1740 @@ +/* + * PowerPC floating point and SPE emulation helpers for QEMU. + * + * Copyright (c) 2003-2007 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, see <http://www.gnu.org/licenses/>. + */ +#include "cpu.h" +#include "helper.h" + +/*****************************************************************************/ +/* Floating point operations helpers */ +uint64_t helper_float32_to_float64(CPUPPCState *env, uint32_t arg) +{ + CPU_FloatU f; + CPU_DoubleU d; + + f.l = arg; + d.d = float32_to_float64(f.f, &env->fp_status); + return d.ll; +} + +uint32_t helper_float64_to_float32(CPUPPCState *env, uint64_t arg) +{ + CPU_FloatU f; + CPU_DoubleU d; + + d.ll = arg; + f.f = float64_to_float32(d.d, &env->fp_status); + return f.l; +} + +static inline int isden(float64 d) +{ + CPU_DoubleU u; + + u.d = d; + + return ((u.ll >> 52) & 0x7FF) == 0; +} + +uint32_t helper_compute_fprf(CPUPPCState *env, uint64_t arg, uint32_t set_fprf) +{ + CPU_DoubleU farg; + int isneg; + int ret; + + farg.ll = arg; + isneg = float64_is_neg(farg.d); + if (unlikely(float64_is_any_nan(farg.d))) { + if (float64_is_signaling_nan(farg.d)) { + /* Signaling NaN: flags are undefined */ + ret = 0x00; + } else { + /* Quiet NaN */ + ret = 0x11; + } + } else if (unlikely(float64_is_infinity(farg.d))) { + /* +/- infinity */ + if (isneg) { + ret = 0x09; + } else { + ret = 0x05; + } + } else { + if (float64_is_zero(farg.d)) { + /* +/- zero */ + if (isneg) { + ret = 0x12; + } else { + ret = 0x02; + } + } else { + if (isden(farg.d)) { + /* Denormalized numbers */ + ret = 0x10; + } else { + /* Normalized numbers */ + ret = 0x00; + } + if (isneg) { + ret |= 0x08; + } else { + ret |= 0x04; + } + } + } + if (set_fprf) { + /* We update FPSCR_FPRF */ + env->fpscr &= ~(0x1F << FPSCR_FPRF); + env->fpscr |= ret << FPSCR_FPRF; + } + /* We just need fpcc to update Rc1 */ + return ret & 0xF; +} + +/* Floating-point invalid operations exception */ +static inline uint64_t fload_invalid_op_excp(CPUPPCState *env, int op) +{ + uint64_t ret = 0; + int ve; + + ve = fpscr_ve; + switch (op) { + case POWERPC_EXCP_FP_VXSNAN: + env->fpscr |= 1 << FPSCR_VXSNAN; + break; + case POWERPC_EXCP_FP_VXSOFT: + env->fpscr |= 1 << FPSCR_VXSOFT; + break; + case POWERPC_EXCP_FP_VXISI: + /* Magnitude subtraction of infinities */ + env->fpscr |= 1 << FPSCR_VXISI; + goto update_arith; + case POWERPC_EXCP_FP_VXIDI: + /* Division of infinity by infinity */ + env->fpscr |= 1 << FPSCR_VXIDI; + goto update_arith; + case POWERPC_EXCP_FP_VXZDZ: + /* Division of zero by zero */ + env->fpscr |= 1 << FPSCR_VXZDZ; + goto update_arith; + case POWERPC_EXCP_FP_VXIMZ: + /* Multiplication of zero by infinity */ + env->fpscr |= 1 << FPSCR_VXIMZ; + goto update_arith; + case POWERPC_EXCP_FP_VXVC: + /* Ordered comparison of NaN */ + env->fpscr |= 1 << FPSCR_VXVC; + env->fpscr &= ~(0xF << FPSCR_FPCC); + env->fpscr |= 0x11 << FPSCR_FPCC; + /* We must update the target FPR before raising the exception */ + if (ve != 0) { + env->exception_index = POWERPC_EXCP_PROGRAM; + env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_VXVC; + /* Update the floating-point enabled exception summary */ + env->fpscr |= 1 << FPSCR_FEX; + /* Exception is differed */ + ve = 0; + } + break; + case POWERPC_EXCP_FP_VXSQRT: + /* Square root of a negative number */ + env->fpscr |= 1 << FPSCR_VXSQRT; + update_arith: + env->fpscr &= ~((1 << FPSCR_FR) | (1 << FPSCR_FI)); + if (ve == 0) { + /* Set the result to quiet NaN */ + ret = 0x7FF8000000000000ULL; + env->fpscr &= ~(0xF << FPSCR_FPCC); + env->fpscr |= 0x11 << FPSCR_FPCC; + } + break; + case POWERPC_EXCP_FP_VXCVI: + /* Invalid conversion */ + env->fpscr |= 1 << FPSCR_VXCVI; + env->fpscr &= ~((1 << FPSCR_FR) | (1 << FPSCR_FI)); + if (ve == 0) { + /* Set the result to quiet NaN */ + ret = 0x7FF8000000000000ULL; + env->fpscr &= ~(0xF << FPSCR_FPCC); + env->fpscr |= 0x11 << FPSCR_FPCC; + } + break; + } + /* Update the floating-point invalid operation summary */ + env->fpscr |= 1 << FPSCR_VX; + /* Update the floating-point exception summary */ + env->fpscr |= 1 << FPSCR_FX; + if (ve != 0) { + /* Update the floating-point enabled exception summary */ + env->fpscr |= 1 << FPSCR_FEX; + if (msr_fe0 != 0 || msr_fe1 != 0) { + helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_FP | op); + } + } + return ret; +} + +static inline void float_zero_divide_excp(CPUPPCState *env) +{ + env->fpscr |= 1 << FPSCR_ZX; + env->fpscr &= ~((1 << FPSCR_FR) | (1 << FPSCR_FI)); + /* Update the floating-point exception summary */ + env->fpscr |= 1 << FPSCR_FX; + if (fpscr_ze != 0) { + /* Update the floating-point enabled exception summary */ + env->fpscr |= 1 << FPSCR_FEX; + if (msr_fe0 != 0 || msr_fe1 != 0) { + helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_FP | POWERPC_EXCP_FP_ZX); + } + } +} + +static inline void float_overflow_excp(CPUPPCState *env) +{ + env->fpscr |= 1 << FPSCR_OX; + /* Update the floating-point exception summary */ + env->fpscr |= 1 << FPSCR_FX; + if (fpscr_oe != 0) { + /* XXX: should adjust the result */ + /* Update the floating-point enabled exception summary */ + env->fpscr |= 1 << FPSCR_FEX; + /* We must update the target FPR before raising the exception */ + env->exception_index = POWERPC_EXCP_PROGRAM; + env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_OX; + } else { + env->fpscr |= 1 << FPSCR_XX; + env->fpscr |= 1 << FPSCR_FI; + } +} + +static inline void float_underflow_excp(CPUPPCState *env) +{ + env->fpscr |= 1 << FPSCR_UX; + /* Update the floating-point exception summary */ + env->fpscr |= 1 << FPSCR_FX; + if (fpscr_ue != 0) { + /* XXX: should adjust the result */ + /* Update the floating-point enabled exception summary */ + env->fpscr |= 1 << FPSCR_FEX; + /* We must update the target FPR before raising the exception */ + env->exception_index = POWERPC_EXCP_PROGRAM; + env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_UX; + } +} + +static inline void float_inexact_excp(CPUPPCState *env) +{ + env->fpscr |= 1 << FPSCR_XX; + /* Update the floating-point exception summary */ + env->fpscr |= 1 << FPSCR_FX; + if (fpscr_xe != 0) { + /* Update the floating-point enabled exception summary */ + env->fpscr |= 1 << FPSCR_FEX; + /* We must update the target FPR before raising the exception */ + env->exception_index = POWERPC_EXCP_PROGRAM; + env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_XX; + } +} + +static inline void fpscr_set_rounding_mode(CPUPPCState *env) +{ + int rnd_type; + + /* Set rounding mode */ + switch (fpscr_rn) { + 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 helper_fpscr_clrbit(CPUPPCState *env, uint32_t bit) +{ + int prev; + + prev = (env->fpscr >> bit) & 1; + env->fpscr &= ~(1 << bit); + if (prev == 1) { + switch (bit) { + case FPSCR_RN1: + case FPSCR_RN: + fpscr_set_rounding_mode(env); + break; + default: + break; + } + } +} + +void helper_fpscr_setbit(CPUPPCState *env, uint32_t bit) +{ + int prev; + + prev = (env->fpscr >> bit) & 1; + env->fpscr |= 1 << bit; + if (prev == 0) { + switch (bit) { + case FPSCR_VX: + env->fpscr |= 1 << FPSCR_FX; + if (fpscr_ve) { + goto raise_ve; + } + break; + case FPSCR_OX: + env->fpscr |= 1 << FPSCR_FX; + if (fpscr_oe) { + goto raise_oe; + } + break; + case FPSCR_UX: + env->fpscr |= 1 << FPSCR_FX; + if (fpscr_ue) { + goto raise_ue; + } + break; + case FPSCR_ZX: + env->fpscr |= 1 << FPSCR_FX; + if (fpscr_ze) { + goto raise_ze; + } + break; + case FPSCR_XX: + env->fpscr |= 1 << FPSCR_FX; + if (fpscr_xe) { + goto raise_xe; + } + break; + case FPSCR_VXSNAN: + case FPSCR_VXISI: + case FPSCR_VXIDI: + case FPSCR_VXZDZ: + case FPSCR_VXIMZ: + case FPSCR_VXVC: + case FPSCR_VXSOFT: + case FPSCR_VXSQRT: + case FPSCR_VXCVI: + env->fpscr |= 1 << FPSCR_VX; + env->fpscr |= 1 << FPSCR_FX; + if (fpscr_ve != 0) { + goto raise_ve; + } + break; + case FPSCR_VE: + if (fpscr_vx != 0) { + raise_ve: + env->error_code = POWERPC_EXCP_FP; + if (fpscr_vxsnan) { + env->error_code |= POWERPC_EXCP_FP_VXSNAN; + } + if (fpscr_vxisi) { + env->error_code |= POWERPC_EXCP_FP_VXISI; + } + if (fpscr_vxidi) { + env->error_code |= POWERPC_EXCP_FP_VXIDI; + } + if (fpscr_vxzdz) { + env->error_code |= POWERPC_EXCP_FP_VXZDZ; + } + if (fpscr_vximz) { + env->error_code |= POWERPC_EXCP_FP_VXIMZ; + } + if (fpscr_vxvc) { + env->error_code |= POWERPC_EXCP_FP_VXVC; + } + if (fpscr_vxsoft) { + env->error_code |= POWERPC_EXCP_FP_VXSOFT; + } + if (fpscr_vxsqrt) { + env->error_code |= POWERPC_EXCP_FP_VXSQRT; + } + if (fpscr_vxcvi) { + env->error_code |= POWERPC_EXCP_FP_VXCVI; + } + goto raise_excp; + } + break; + case FPSCR_OE: + if (fpscr_ox != 0) { + raise_oe: + env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_OX; + goto raise_excp; + } + break; + case FPSCR_UE: + if (fpscr_ux != 0) { + raise_ue: + env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_UX; + goto raise_excp; + } + break; + case FPSCR_ZE: + if (fpscr_zx != 0) { + raise_ze: + env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_ZX; + goto raise_excp; + } + break; + case FPSCR_XE: + if (fpscr_xx != 0) { + raise_xe: + env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_XX; + goto raise_excp; + } + break; + case FPSCR_RN1: + case FPSCR_RN: + fpscr_set_rounding_mode(env); + break; + default: + break; + raise_excp: + /* Update the floating-point enabled exception summary */ + env->fpscr |= 1 << FPSCR_FEX; + /* We have to update Rc1 before raising the exception */ + env->exception_index = POWERPC_EXCP_PROGRAM; + break; + } + } +} + +void helper_store_fpscr(CPUPPCState *env, uint64_t arg, uint32_t mask) +{ + /* + * We use only the 32 LSB of the incoming fpr + */ + uint32_t prev, new; + int i; + + prev = env->fpscr; + new = (uint32_t)arg; + new &= ~0x60000000; + new |= prev & 0x60000000; + for (i = 0; i < 8; i++) { + if (mask & (1 << i)) { + env->fpscr &= ~(0xF << (4 * i)); + env->fpscr |= new & (0xF << (4 * i)); + } + } + /* Update VX and FEX */ + if (fpscr_ix != 0) { + env->fpscr |= 1 << FPSCR_VX; + } else { + env->fpscr &= ~(1 << FPSCR_VX); + } + if ((fpscr_ex & fpscr_eex) != 0) { + env->fpscr |= 1 << FPSCR_FEX; + env->exception_index = POWERPC_EXCP_PROGRAM; + /* XXX: we should compute it properly */ + env->error_code = POWERPC_EXCP_FP; + } else { + env->fpscr &= ~(1 << FPSCR_FEX); + } + fpscr_set_rounding_mode(env); +} + +void helper_float_check_status(CPUPPCState *env) +{ + if (env->exception_index == POWERPC_EXCP_PROGRAM && + (env->error_code & POWERPC_EXCP_FP)) { + /* Differred floating-point exception after target FPR update */ + if (msr_fe0 != 0 || msr_fe1 != 0) { + helper_raise_exception_err(env, env->exception_index, + env->error_code); + } + } else { + int status = get_float_exception_flags(&env->fp_status); + if (status & float_flag_divbyzero) { + float_zero_divide_excp(env); + } else if (status & float_flag_overflow) { + float_overflow_excp(env); + } else if (status & float_flag_underflow) { + float_underflow_excp(env); + } else if (status & float_flag_inexact) { + float_inexact_excp(env); + } + } +} + +void helper_reset_fpstatus(CPUPPCState *env) +{ + set_float_exception_flags(0, &env->fp_status); +} + +/* fadd - fadd. */ +uint64_t helper_fadd(CPUPPCState *env, uint64_t arg1, uint64_t arg2) +{ + CPU_DoubleU farg1, farg2; + + farg1.ll = arg1; + farg2.ll = arg2; + + if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) && + float64_is_neg(farg1.d) != float64_is_neg(farg2.d))) { + /* Magnitude subtraction of infinities */ + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI); + } else { + if (unlikely(float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d))) { + /* sNaN addition */ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + } + farg1.d = float64_add(farg1.d, farg2.d, &env->fp_status); + } + + return farg1.ll; +} + +/* fsub - fsub. */ +uint64_t helper_fsub(CPUPPCState *env, uint64_t arg1, uint64_t arg2) +{ + CPU_DoubleU farg1, farg2; + + farg1.ll = arg1; + farg2.ll = arg2; + + if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) && + float64_is_neg(farg1.d) == float64_is_neg(farg2.d))) { + /* Magnitude subtraction of infinities */ + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI); + } else { + if (unlikely(float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d))) { + /* sNaN subtraction */ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + } + farg1.d = float64_sub(farg1.d, farg2.d, &env->fp_status); + } + + return farg1.ll; +} + +/* fmul - fmul. */ +uint64_t helper_fmul(CPUPPCState *env, uint64_t arg1, uint64_t arg2) +{ + CPU_DoubleU farg1, farg2; + + farg1.ll = arg1; + farg2.ll = arg2; + + if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || + (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { + /* Multiplication of zero by infinity */ + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ); + } else { + if (unlikely(float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d))) { + /* sNaN multiplication */ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + } + farg1.d = float64_mul(farg1.d, farg2.d, &env->fp_status); + } + + return farg1.ll; +} + +/* fdiv - fdiv. */ +uint64_t helper_fdiv(CPUPPCState *env, uint64_t arg1, uint64_t arg2) +{ + CPU_DoubleU farg1, farg2; + + farg1.ll = arg1; + farg2.ll = arg2; + + if (unlikely(float64_is_infinity(farg1.d) && + float64_is_infinity(farg2.d))) { + /* Division of infinity by infinity */ + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIDI); + } else if (unlikely(float64_is_zero(farg1.d) && float64_is_zero(farg2.d))) { + /* Division of zero by zero */ + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXZDZ); + } else { + if (unlikely(float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d))) { + /* sNaN division */ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + } + farg1.d = float64_div(farg1.d, farg2.d, &env->fp_status); + } + + return farg1.ll; +} + +/* fabs */ +uint64_t helper_fabs(CPUPPCState *env, uint64_t arg) +{ + CPU_DoubleU farg; + + farg.ll = arg; + farg.d = float64_abs(farg.d); + return farg.ll; +} + +/* fnabs */ +uint64_t helper_fnabs(CPUPPCState *env, uint64_t arg) +{ + CPU_DoubleU farg; + + farg.ll = arg; + farg.d = float64_abs(farg.d); + farg.d = float64_chs(farg.d); + return farg.ll; +} + +/* fneg */ +uint64_t helper_fneg(CPUPPCState *env, uint64_t arg) +{ + CPU_DoubleU farg; + + farg.ll = arg; + farg.d = float64_chs(farg.d); + return farg.ll; +} + +/* fctiw - fctiw. */ +uint64_t helper_fctiw(CPUPPCState *env, uint64_t arg) +{ + CPU_DoubleU farg; + + farg.ll = arg; + + if (unlikely(float64_is_signaling_nan(farg.d))) { + /* sNaN conversion */ + farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | + POWERPC_EXCP_FP_VXCVI); + } else if (unlikely(float64_is_quiet_nan(farg.d) || + float64_is_infinity(farg.d))) { + /* qNan / infinity conversion */ + farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI); + } else { + farg.ll = float64_to_int32(farg.d, &env->fp_status); + /* XXX: higher bits are not supposed to be significant. + * to make tests easier, return the same as a real PowerPC 750 + */ + farg.ll |= 0xFFF80000ULL << 32; + } + return farg.ll; +} + +/* fctiwz - fctiwz. */ +uint64_t helper_fctiwz(CPUPPCState *env, uint64_t arg) +{ + CPU_DoubleU farg; + + farg.ll = arg; + + if (unlikely(float64_is_signaling_nan(farg.d))) { + /* sNaN conversion */ + farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | + POWERPC_EXCP_FP_VXCVI); + } else if (unlikely(float64_is_quiet_nan(farg.d) || + float64_is_infinity(farg.d))) { + /* qNan / infinity conversion */ + farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI); + } else { + farg.ll = float64_to_int32_round_to_zero(farg.d, &env->fp_status); + /* XXX: higher bits are not supposed to be significant. + * to make tests easier, return the same as a real PowerPC 750 + */ + farg.ll |= 0xFFF80000ULL << 32; + } + return farg.ll; +} + +#if defined(TARGET_PPC64) +/* fcfid - fcfid. */ +uint64_t helper_fcfid(CPUPPCState *env, uint64_t arg) +{ + CPU_DoubleU farg; + + farg.d = int64_to_float64(arg, &env->fp_status); + return farg.ll; +} + +/* fctid - fctid. */ +uint64_t helper_fctid(CPUPPCState *env, uint64_t arg) +{ + CPU_DoubleU farg; + + farg.ll = arg; + + if (unlikely(float64_is_signaling_nan(farg.d))) { + /* sNaN conversion */ + farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | + POWERPC_EXCP_FP_VXCVI); + } else if (unlikely(float64_is_quiet_nan(farg.d) || + float64_is_infinity(farg.d))) { + /* qNan / infinity conversion */ + farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI); + } else { + farg.ll = float64_to_int64(farg.d, &env->fp_status); + } + return farg.ll; +} + +/* fctidz - fctidz. */ +uint64_t helper_fctidz(CPUPPCState *env, uint64_t arg) +{ + CPU_DoubleU farg; + + farg.ll = arg; + + if (unlikely(float64_is_signaling_nan(farg.d))) { + /* sNaN conversion */ + farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | + POWERPC_EXCP_FP_VXCVI); + } else if (unlikely(float64_is_quiet_nan(farg.d) || + float64_is_infinity(farg.d))) { + /* qNan / infinity conversion */ + farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI); + } else { + farg.ll = float64_to_int64_round_to_zero(farg.d, &env->fp_status); + } + return farg.ll; +} + +#endif + +static inline uint64_t do_fri(CPUPPCState *env, uint64_t arg, + int rounding_mode) +{ + CPU_DoubleU farg; + + farg.ll = arg; + + if (unlikely(float64_is_signaling_nan(farg.d))) { + /* sNaN round */ + farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | + POWERPC_EXCP_FP_VXCVI); + } else if (unlikely(float64_is_quiet_nan(farg.d) || + float64_is_infinity(farg.d))) { + /* qNan / infinity round */ + farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI); + } else { + set_float_rounding_mode(rounding_mode, &env->fp_status); + farg.ll = float64_round_to_int(farg.d, &env->fp_status); + /* Restore rounding mode from FPSCR */ + fpscr_set_rounding_mode(env); + } + return farg.ll; +} + +uint64_t helper_frin(CPUPPCState *env, uint64_t arg) +{ + return do_fri(env, arg, float_round_nearest_even); +} + +uint64_t helper_friz(CPUPPCState *env, uint64_t arg) +{ + return do_fri(env, arg, float_round_to_zero); +} + +uint64_t helper_frip(CPUPPCState *env, uint64_t arg) +{ + return do_fri(env, arg, float_round_up); +} + +uint64_t helper_frim(CPUPPCState *env, uint64_t arg) +{ + return do_fri(env, arg, float_round_down); +} + +/* fmadd - fmadd. */ +uint64_t helper_fmadd(CPUPPCState *env, uint64_t arg1, uint64_t arg2, + uint64_t arg3) +{ + CPU_DoubleU farg1, farg2, farg3; + + farg1.ll = arg1; + farg2.ll = arg2; + farg3.ll = arg3; + + if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || + (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { + /* Multiplication of zero by infinity */ + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ); + } else { + if (unlikely(float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d) || + float64_is_signaling_nan(farg3.d))) { + /* sNaN operation */ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + } + /* This is the way the PowerPC specification defines it */ + float128 ft0_128, ft1_128; + + ft0_128 = float64_to_float128(farg1.d, &env->fp_status); + ft1_128 = float64_to_float128(farg2.d, &env->fp_status); + ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status); + if (unlikely(float128_is_infinity(ft0_128) && + float64_is_infinity(farg3.d) && + float128_is_neg(ft0_128) != float64_is_neg(farg3.d))) { + /* Magnitude subtraction of infinities */ + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI); + } else { + ft1_128 = float64_to_float128(farg3.d, &env->fp_status); + ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status); + farg1.d = float128_to_float64(ft0_128, &env->fp_status); + } + } + + return farg1.ll; +} + +/* fmsub - fmsub. */ +uint64_t helper_fmsub(CPUPPCState *env, uint64_t arg1, uint64_t arg2, + uint64_t arg3) +{ + CPU_DoubleU farg1, farg2, farg3; + + farg1.ll = arg1; + farg2.ll = arg2; + farg3.ll = arg3; + + if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || + (float64_is_zero(farg1.d) && + float64_is_infinity(farg2.d)))) { + /* Multiplication of zero by infinity */ + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ); + } else { + if (unlikely(float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d) || + float64_is_signaling_nan(farg3.d))) { + /* sNaN operation */ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + } + /* This is the way the PowerPC specification defines it */ + float128 ft0_128, ft1_128; + + ft0_128 = float64_to_float128(farg1.d, &env->fp_status); + ft1_128 = float64_to_float128(farg2.d, &env->fp_status); + ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status); + if (unlikely(float128_is_infinity(ft0_128) && + float64_is_infinity(farg3.d) && + float128_is_neg(ft0_128) == float64_is_neg(farg3.d))) { + /* Magnitude subtraction of infinities */ + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI); + } else { + ft1_128 = float64_to_float128(farg3.d, &env->fp_status); + ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status); + farg1.d = float128_to_float64(ft0_128, &env->fp_status); + } + } + return farg1.ll; +} + +/* fnmadd - fnmadd. */ +uint64_t helper_fnmadd(CPUPPCState *env, uint64_t arg1, uint64_t arg2, + uint64_t arg3) +{ + CPU_DoubleU farg1, farg2, farg3; + + farg1.ll = arg1; + farg2.ll = arg2; + farg3.ll = arg3; + + if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || + (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { + /* Multiplication of zero by infinity */ + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ); + } else { + if (unlikely(float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d) || + float64_is_signaling_nan(farg3.d))) { + /* sNaN operation */ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + } + /* This is the way the PowerPC specification defines it */ + float128 ft0_128, ft1_128; + + ft0_128 = float64_to_float128(farg1.d, &env->fp_status); + ft1_128 = float64_to_float128(farg2.d, &env->fp_status); + ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status); + if (unlikely(float128_is_infinity(ft0_128) && + float64_is_infinity(farg3.d) && + float128_is_neg(ft0_128) != float64_is_neg(farg3.d))) { + /* Magnitude subtraction of infinities */ + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI); + } else { + ft1_128 = float64_to_float128(farg3.d, &env->fp_status); + ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status); + farg1.d = float128_to_float64(ft0_128, &env->fp_status); + } + if (likely(!float64_is_any_nan(farg1.d))) { + farg1.d = float64_chs(farg1.d); + } + } + return farg1.ll; +} + +/* fnmsub - fnmsub. */ +uint64_t helper_fnmsub(CPUPPCState *env, uint64_t arg1, uint64_t arg2, + uint64_t arg3) +{ + CPU_DoubleU farg1, farg2, farg3; + + farg1.ll = arg1; + farg2.ll = arg2; + farg3.ll = arg3; + + if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || + (float64_is_zero(farg1.d) && + float64_is_infinity(farg2.d)))) { + /* Multiplication of zero by infinity */ + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ); + } else { + if (unlikely(float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d) || + float64_is_signaling_nan(farg3.d))) { + /* sNaN operation */ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + } + /* This is the way the PowerPC specification defines it */ + float128 ft0_128, ft1_128; + + ft0_128 = float64_to_float128(farg1.d, &env->fp_status); + ft1_128 = float64_to_float128(farg2.d, &env->fp_status); + ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status); + if (unlikely(float128_is_infinity(ft0_128) && + float64_is_infinity(farg3.d) && + float128_is_neg(ft0_128) == float64_is_neg(farg3.d))) { + /* Magnitude subtraction of infinities */ + farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI); + } else { + ft1_128 = float64_to_float128(farg3.d, &env->fp_status); + ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status); + farg1.d = float128_to_float64(ft0_128, &env->fp_status); + } + if (likely(!float64_is_any_nan(farg1.d))) { + farg1.d = float64_chs(farg1.d); + } + } + return farg1.ll; +} + +/* frsp - frsp. */ +uint64_t helper_frsp(CPUPPCState *env, uint64_t arg) +{ + CPU_DoubleU farg; + float32 f32; + + farg.ll = arg; + + if (unlikely(float64_is_signaling_nan(farg.d))) { + /* sNaN square root */ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + } + f32 = float64_to_float32(farg.d, &env->fp_status); + farg.d = float32_to_float64(f32, &env->fp_status); + + return farg.ll; +} + +/* fsqrt - fsqrt. */ +uint64_t helper_fsqrt(CPUPPCState *env, uint64_t arg) +{ + CPU_DoubleU farg; + + farg.ll = arg; + + if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { + /* Square root of a negative nonzero number */ + farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT); + } else { + if (unlikely(float64_is_signaling_nan(farg.d))) { + /* sNaN square root */ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + } + farg.d = float64_sqrt(farg.d, &env->fp_status); + } + return farg.ll; +} + +/* fre - fre. */ +uint64_t helper_fre(CPUPPCState *env, uint64_t arg) +{ + CPU_DoubleU farg; + + farg.ll = arg; + + if (unlikely(float64_is_signaling_nan(farg.d))) { + /* sNaN reciprocal */ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + } + farg.d = float64_div(float64_one, farg.d, &env->fp_status); + return farg.d; +} + +/* fres - fres. */ +uint64_t helper_fres(CPUPPCState *env, uint64_t arg) +{ + CPU_DoubleU farg; + float32 f32; + + farg.ll = arg; + + if (unlikely(float64_is_signaling_nan(farg.d))) { + /* sNaN reciprocal */ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + } + farg.d = float64_div(float64_one, farg.d, &env->fp_status); + f32 = float64_to_float32(farg.d, &env->fp_status); + farg.d = float32_to_float64(f32, &env->fp_status); + + return farg.ll; +} + +/* frsqrte - frsqrte. */ +uint64_t helper_frsqrte(CPUPPCState *env, uint64_t arg) +{ + CPU_DoubleU farg; + float32 f32; + + farg.ll = arg; + + if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { + /* Reciprocal square root of a negative nonzero number */ + farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT); + } else { + if (unlikely(float64_is_signaling_nan(farg.d))) { + /* sNaN reciprocal square root */ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + } + farg.d = float64_sqrt(farg.d, &env->fp_status); + farg.d = float64_div(float64_one, farg.d, &env->fp_status); + f32 = float64_to_float32(farg.d, &env->fp_status); + farg.d = float32_to_float64(f32, &env->fp_status); + } + return farg.ll; +} + +/* fsel - fsel. */ +uint64_t helper_fsel(CPUPPCState *env, uint64_t arg1, uint64_t arg2, + uint64_t arg3) +{ + CPU_DoubleU farg1; + + farg1.ll = arg1; + + if ((!float64_is_neg(farg1.d) || float64_is_zero(farg1.d)) && + !float64_is_any_nan(farg1.d)) { + return arg2; + } else { + return arg3; + } +} + +void helper_fcmpu(CPUPPCState *env, uint64_t arg1, uint64_t arg2, + uint32_t crfD) +{ + CPU_DoubleU farg1, farg2; + uint32_t ret = 0; + + farg1.ll = arg1; + farg2.ll = arg2; + + if (unlikely(float64_is_any_nan(farg1.d) || + float64_is_any_nan(farg2.d))) { + ret = 0x01UL; + } else if (float64_lt(farg1.d, farg2.d, &env->fp_status)) { + ret = 0x08UL; + } else if (!float64_le(farg1.d, farg2.d, &env->fp_status)) { + ret = 0x04UL; + } else { + ret = 0x02UL; + } + + env->fpscr &= ~(0x0F << FPSCR_FPRF); + env->fpscr |= ret << FPSCR_FPRF; + env->crf[crfD] = ret; + if (unlikely(ret == 0x01UL + && (float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d)))) { + /* sNaN comparison */ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); + } +} + +void helper_fcmpo(CPUPPCState *env, uint64_t arg1, uint64_t arg2, + uint32_t crfD) +{ + CPU_DoubleU farg1, farg2; + uint32_t ret = 0; + + farg1.ll = arg1; + farg2.ll = arg2; + + if (unlikely(float64_is_any_nan(farg1.d) || + float64_is_any_nan(farg2.d))) { + ret = 0x01UL; + } else if (float64_lt(farg1.d, farg2.d, &env->fp_status)) { + ret = 0x08UL; + } else if (!float64_le(farg1.d, farg2.d, &env->fp_status)) { + ret = 0x04UL; + } else { + ret = 0x02UL; + } + + env->fpscr &= ~(0x0F << FPSCR_FPRF); + env->fpscr |= ret << FPSCR_FPRF; + env->crf[crfD] = ret; + if (unlikely(ret == 0x01UL)) { + if (float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d)) { + /* sNaN comparison */ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | + POWERPC_EXCP_FP_VXVC); + } else { + /* qNaN comparison */ + fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC); + } + } +} + +/* Single-precision floating-point conversions */ +static inline uint32_t efscfsi(CPUPPCState *env, uint32_t val) +{ + CPU_FloatU u; + + u.f = int32_to_float32(val, &env->vec_status); + + return u.l; +} + +static inline uint32_t efscfui(CPUPPCState *env, uint32_t val) +{ + CPU_FloatU u; + + u.f = uint32_to_float32(val, &env->vec_status); + + return u.l; +} + +static inline int32_t efsctsi(CPUPPCState *env, uint32_t val) +{ + CPU_FloatU u; + + u.l = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float32_is_quiet_nan(u.f))) { + return 0; + } + + return float32_to_int32(u.f, &env->vec_status); +} + +static inline uint32_t efsctui(CPUPPCState *env, uint32_t val) +{ + CPU_FloatU u; + + u.l = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float32_is_quiet_nan(u.f))) { + return 0; + } + + return float32_to_uint32(u.f, &env->vec_status); +} + +static inline uint32_t efsctsiz(CPUPPCState *env, uint32_t val) +{ + CPU_FloatU u; + + u.l = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float32_is_quiet_nan(u.f))) { + return 0; + } + + return float32_to_int32_round_to_zero(u.f, &env->vec_status); +} + +static inline uint32_t efsctuiz(CPUPPCState *env, uint32_t val) +{ + CPU_FloatU u; + + u.l = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float32_is_quiet_nan(u.f))) { + return 0; + } + + return float32_to_uint32_round_to_zero(u.f, &env->vec_status); +} + +static inline uint32_t efscfsf(CPUPPCState *env, uint32_t val) +{ + CPU_FloatU u; + float32 tmp; + + u.f = int32_to_float32(val, &env->vec_status); + tmp = int64_to_float32(1ULL << 32, &env->vec_status); + u.f = float32_div(u.f, tmp, &env->vec_status); + + return u.l; +} + +static inline uint32_t efscfuf(CPUPPCState *env, uint32_t val) +{ + CPU_FloatU u; + float32 tmp; + + u.f = uint32_to_float32(val, &env->vec_status); + tmp = uint64_to_float32(1ULL << 32, &env->vec_status); + u.f = float32_div(u.f, tmp, &env->vec_status); + + return u.l; +} + +static inline uint32_t efsctsf(CPUPPCState *env, uint32_t val) +{ + CPU_FloatU u; + float32 tmp; + + u.l = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float32_is_quiet_nan(u.f))) { + return 0; + } + tmp = uint64_to_float32(1ULL << 32, &env->vec_status); + u.f = float32_mul(u.f, tmp, &env->vec_status); + + return float32_to_int32(u.f, &env->vec_status); +} + +static inline uint32_t efsctuf(CPUPPCState *env, uint32_t val) +{ + CPU_FloatU u; + float32 tmp; + + u.l = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float32_is_quiet_nan(u.f))) { + return 0; + } + tmp = uint64_to_float32(1ULL << 32, &env->vec_status); + u.f = float32_mul(u.f, tmp, &env->vec_status); + + return float32_to_uint32(u.f, &env->vec_status); +} + +#define HELPER_SPE_SINGLE_CONV(name) \ + uint32_t helper_e##name(CPUPPCState *env, uint32_t val) \ + { \ + return e##name(env, val); \ + } +/* efscfsi */ +HELPER_SPE_SINGLE_CONV(fscfsi); +/* efscfui */ +HELPER_SPE_SINGLE_CONV(fscfui); +/* efscfuf */ +HELPER_SPE_SINGLE_CONV(fscfuf); +/* efscfsf */ +HELPER_SPE_SINGLE_CONV(fscfsf); +/* efsctsi */ +HELPER_SPE_SINGLE_CONV(fsctsi); +/* efsctui */ +HELPER_SPE_SINGLE_CONV(fsctui); +/* efsctsiz */ +HELPER_SPE_SINGLE_CONV(fsctsiz); +/* efsctuiz */ +HELPER_SPE_SINGLE_CONV(fsctuiz); +/* efsctsf */ +HELPER_SPE_SINGLE_CONV(fsctsf); +/* efsctuf */ +HELPER_SPE_SINGLE_CONV(fsctuf); + +#define HELPER_SPE_VECTOR_CONV(name) \ + uint64_t helper_ev##name(CPUPPCState *env, uint64_t val) \ + { \ + return ((uint64_t)e##name(env, val >> 32) << 32) | \ + (uint64_t)e##name(env, val); \ + } +/* evfscfsi */ +HELPER_SPE_VECTOR_CONV(fscfsi); +/* evfscfui */ +HELPER_SPE_VECTOR_CONV(fscfui); +/* evfscfuf */ +HELPER_SPE_VECTOR_CONV(fscfuf); +/* evfscfsf */ +HELPER_SPE_VECTOR_CONV(fscfsf); +/* evfsctsi */ +HELPER_SPE_VECTOR_CONV(fsctsi); +/* evfsctui */ +HELPER_SPE_VECTOR_CONV(fsctui); +/* evfsctsiz */ +HELPER_SPE_VECTOR_CONV(fsctsiz); +/* evfsctuiz */ +HELPER_SPE_VECTOR_CONV(fsctuiz); +/* evfsctsf */ +HELPER_SPE_VECTOR_CONV(fsctsf); +/* evfsctuf */ +HELPER_SPE_VECTOR_CONV(fsctuf); + +/* Single-precision floating-point arithmetic */ +static inline uint32_t efsadd(CPUPPCState *env, uint32_t op1, uint32_t op2) +{ + CPU_FloatU u1, u2; + + u1.l = op1; + u2.l = op2; + u1.f = float32_add(u1.f, u2.f, &env->vec_status); + return u1.l; +} + +static inline uint32_t efssub(CPUPPCState *env, uint32_t op1, uint32_t op2) +{ + CPU_FloatU u1, u2; + + u1.l = op1; + u2.l = op2; + u1.f = float32_sub(u1.f, u2.f, &env->vec_status); + return u1.l; +} + +static inline uint32_t efsmul(CPUPPCState *env, uint32_t op1, uint32_t op2) +{ + CPU_FloatU u1, u2; + + u1.l = op1; + u2.l = op2; + u1.f = float32_mul(u1.f, u2.f, &env->vec_status); + return u1.l; +} + +static inline uint32_t efsdiv(CPUPPCState *env, uint32_t op1, uint32_t op2) +{ + CPU_FloatU u1, u2; + + u1.l = op1; + u2.l = op2; + u1.f = float32_div(u1.f, u2.f, &env->vec_status); + return u1.l; +} + +#define HELPER_SPE_SINGLE_ARITH(name) \ + uint32_t helper_e##name(CPUPPCState *env, uint32_t op1, uint32_t op2) \ + { \ + return e##name(env, op1, op2); \ + } +/* efsadd */ +HELPER_SPE_SINGLE_ARITH(fsadd); +/* efssub */ +HELPER_SPE_SINGLE_ARITH(fssub); +/* efsmul */ +HELPER_SPE_SINGLE_ARITH(fsmul); +/* efsdiv */ +HELPER_SPE_SINGLE_ARITH(fsdiv); + +#define HELPER_SPE_VECTOR_ARITH(name) \ + uint64_t helper_ev##name(CPUPPCState *env, uint64_t op1, uint64_t op2) \ + { \ + return ((uint64_t)e##name(env, op1 >> 32, op2 >> 32) << 32) | \ + (uint64_t)e##name(env, op1, op2); \ + } +/* evfsadd */ +HELPER_SPE_VECTOR_ARITH(fsadd); +/* evfssub */ +HELPER_SPE_VECTOR_ARITH(fssub); +/* evfsmul */ +HELPER_SPE_VECTOR_ARITH(fsmul); +/* evfsdiv */ +HELPER_SPE_VECTOR_ARITH(fsdiv); + +/* Single-precision floating-point comparisons */ +static inline uint32_t efscmplt(CPUPPCState *env, uint32_t op1, uint32_t op2) +{ + CPU_FloatU u1, u2; + + u1.l = op1; + u2.l = op2; + return float32_lt(u1.f, u2.f, &env->vec_status) ? 4 : 0; +} + +static inline uint32_t efscmpgt(CPUPPCState *env, uint32_t op1, uint32_t op2) +{ + CPU_FloatU u1, u2; + + u1.l = op1; + u2.l = op2; + return float32_le(u1.f, u2.f, &env->vec_status) ? 0 : 4; +} + +static inline uint32_t efscmpeq(CPUPPCState *env, uint32_t op1, uint32_t op2) +{ + CPU_FloatU u1, u2; + + u1.l = op1; + u2.l = op2; + return float32_eq(u1.f, u2.f, &env->vec_status) ? 4 : 0; +} + +static inline uint32_t efststlt(CPUPPCState *env, uint32_t op1, uint32_t op2) +{ + /* XXX: TODO: ignore special values (NaN, infinites, ...) */ + return efscmplt(env, op1, op2); +} + +static inline uint32_t efststgt(CPUPPCState *env, uint32_t op1, uint32_t op2) +{ + /* XXX: TODO: ignore special values (NaN, infinites, ...) */ + return efscmpgt(env, op1, op2); +} + +static inline uint32_t efststeq(CPUPPCState *env, uint32_t op1, uint32_t op2) +{ + /* XXX: TODO: ignore special values (NaN, infinites, ...) */ + return efscmpeq(env, op1, op2); +} + +#define HELPER_SINGLE_SPE_CMP(name) \ + uint32_t helper_e##name(CPUPPCState *env, uint32_t op1, uint32_t op2) \ + { \ + return e##name(env, op1, op2) << 2; \ + } +/* efststlt */ +HELPER_SINGLE_SPE_CMP(fststlt); +/* efststgt */ +HELPER_SINGLE_SPE_CMP(fststgt); +/* efststeq */ +HELPER_SINGLE_SPE_CMP(fststeq); +/* efscmplt */ +HELPER_SINGLE_SPE_CMP(fscmplt); +/* efscmpgt */ +HELPER_SINGLE_SPE_CMP(fscmpgt); +/* efscmpeq */ +HELPER_SINGLE_SPE_CMP(fscmpeq); + +static inline uint32_t evcmp_merge(int t0, int t1) +{ + return (t0 << 3) | (t1 << 2) | ((t0 | t1) << 1) | (t0 & t1); +} + +#define HELPER_VECTOR_SPE_CMP(name) \ + uint32_t helper_ev##name(CPUPPCState *env, uint64_t op1, uint64_t op2) \ + { \ + return evcmp_merge(e##name(env, op1 >> 32, op2 >> 32), \ + e##name(env, op1, op2)); \ + } +/* evfststlt */ +HELPER_VECTOR_SPE_CMP(fststlt); +/* evfststgt */ +HELPER_VECTOR_SPE_CMP(fststgt); +/* evfststeq */ +HELPER_VECTOR_SPE_CMP(fststeq); +/* evfscmplt */ +HELPER_VECTOR_SPE_CMP(fscmplt); +/* evfscmpgt */ +HELPER_VECTOR_SPE_CMP(fscmpgt); +/* evfscmpeq */ +HELPER_VECTOR_SPE_CMP(fscmpeq); + +/* Double-precision floating-point conversion */ +uint64_t helper_efdcfsi(CPUPPCState *env, uint32_t val) +{ + CPU_DoubleU u; + + u.d = int32_to_float64(val, &env->vec_status); + + return u.ll; +} + +uint64_t helper_efdcfsid(CPUPPCState *env, uint64_t val) +{ + CPU_DoubleU u; + + u.d = int64_to_float64(val, &env->vec_status); + + return u.ll; +} + +uint64_t helper_efdcfui(CPUPPCState *env, uint32_t val) +{ + CPU_DoubleU u; + + u.d = uint32_to_float64(val, &env->vec_status); + + return u.ll; +} + +uint64_t helper_efdcfuid(CPUPPCState *env, uint64_t val) +{ + CPU_DoubleU u; + + u.d = uint64_to_float64(val, &env->vec_status); + + return u.ll; +} + +uint32_t helper_efdctsi(CPUPPCState *env, uint64_t val) +{ + CPU_DoubleU u; + + u.ll = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float64_is_any_nan(u.d))) { + return 0; + } + + return float64_to_int32(u.d, &env->vec_status); +} + +uint32_t helper_efdctui(CPUPPCState *env, uint64_t val) +{ + CPU_DoubleU u; + + u.ll = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float64_is_any_nan(u.d))) { + return 0; + } + + return float64_to_uint32(u.d, &env->vec_status); +} + +uint32_t helper_efdctsiz(CPUPPCState *env, uint64_t val) +{ + CPU_DoubleU u; + + u.ll = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float64_is_any_nan(u.d))) { + return 0; + } + + return float64_to_int32_round_to_zero(u.d, &env->vec_status); +} + +uint64_t helper_efdctsidz(CPUPPCState *env, uint64_t val) +{ + CPU_DoubleU u; + + u.ll = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float64_is_any_nan(u.d))) { + return 0; + } + + return float64_to_int64_round_to_zero(u.d, &env->vec_status); +} + +uint32_t helper_efdctuiz(CPUPPCState *env, uint64_t val) +{ + CPU_DoubleU u; + + u.ll = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float64_is_any_nan(u.d))) { + return 0; + } + + return float64_to_uint32_round_to_zero(u.d, &env->vec_status); +} + +uint64_t helper_efdctuidz(CPUPPCState *env, uint64_t val) +{ + CPU_DoubleU u; + + u.ll = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float64_is_any_nan(u.d))) { + return 0; + } + + return float64_to_uint64_round_to_zero(u.d, &env->vec_status); +} + +uint64_t helper_efdcfsf(CPUPPCState *env, uint32_t val) +{ + CPU_DoubleU u; + float64 tmp; + + u.d = int32_to_float64(val, &env->vec_status); + tmp = int64_to_float64(1ULL << 32, &env->vec_status); + u.d = float64_div(u.d, tmp, &env->vec_status); + + return u.ll; +} + +uint64_t helper_efdcfuf(CPUPPCState *env, uint32_t val) +{ + CPU_DoubleU u; + float64 tmp; + + u.d = uint32_to_float64(val, &env->vec_status); + tmp = int64_to_float64(1ULL << 32, &env->vec_status); + u.d = float64_div(u.d, tmp, &env->vec_status); + + return u.ll; +} + +uint32_t helper_efdctsf(CPUPPCState *env, uint64_t val) +{ + CPU_DoubleU u; + float64 tmp; + + u.ll = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float64_is_any_nan(u.d))) { + return 0; + } + tmp = uint64_to_float64(1ULL << 32, &env->vec_status); + u.d = float64_mul(u.d, tmp, &env->vec_status); + + return float64_to_int32(u.d, &env->vec_status); +} + +uint32_t helper_efdctuf(CPUPPCState *env, uint64_t val) +{ + CPU_DoubleU u; + float64 tmp; + + u.ll = val; + /* NaN are not treated the same way IEEE 754 does */ + if (unlikely(float64_is_any_nan(u.d))) { + return 0; + } + tmp = uint64_to_float64(1ULL << 32, &env->vec_status); + u.d = float64_mul(u.d, tmp, &env->vec_status); + + return float64_to_uint32(u.d, &env->vec_status); +} + +uint32_t helper_efscfd(CPUPPCState *env, uint64_t val) +{ + CPU_DoubleU u1; + CPU_FloatU u2; + + u1.ll = val; + u2.f = float64_to_float32(u1.d, &env->vec_status); + + return u2.l; +} + +uint64_t helper_efdcfs(CPUPPCState *env, uint32_t val) +{ + CPU_DoubleU u2; + CPU_FloatU u1; + + u1.l = val; + u2.d = float32_to_float64(u1.f, &env->vec_status); + + return u2.ll; +} + +/* Double precision fixed-point arithmetic */ +uint64_t helper_efdadd(CPUPPCState *env, uint64_t op1, uint64_t op2) +{ + CPU_DoubleU u1, u2; + + u1.ll = op1; + u2.ll = op2; + u1.d = float64_add(u1.d, u2.d, &env->vec_status); + return u1.ll; +} + +uint64_t helper_efdsub(CPUPPCState *env, uint64_t op1, uint64_t op2) +{ + CPU_DoubleU u1, u2; + + u1.ll = op1; + u2.ll = op2; + u1.d = float64_sub(u1.d, u2.d, &env->vec_status); + return u1.ll; +} + +uint64_t helper_efdmul(CPUPPCState *env, uint64_t op1, uint64_t op2) +{ + CPU_DoubleU u1, u2; + + u1.ll = op1; + u2.ll = op2; + u1.d = float64_mul(u1.d, u2.d, &env->vec_status); + return u1.ll; +} + +uint64_t helper_efddiv(CPUPPCState *env, uint64_t op1, uint64_t op2) +{ + CPU_DoubleU u1, u2; + + u1.ll = op1; + u2.ll = op2; + u1.d = float64_div(u1.d, u2.d, &env->vec_status); + return u1.ll; +} + +/* Double precision floating point helpers */ +uint32_t helper_efdtstlt(CPUPPCState *env, uint64_t op1, uint64_t op2) +{ + CPU_DoubleU u1, u2; + + u1.ll = op1; + u2.ll = op2; + return float64_lt(u1.d, u2.d, &env->vec_status) ? 4 : 0; +} + +uint32_t helper_efdtstgt(CPUPPCState *env, uint64_t op1, uint64_t op2) +{ + CPU_DoubleU u1, u2; + + u1.ll = op1; + u2.ll = op2; + return float64_le(u1.d, u2.d, &env->vec_status) ? 0 : 4; +} + +uint32_t helper_efdtsteq(CPUPPCState *env, uint64_t op1, uint64_t op2) +{ + CPU_DoubleU u1, u2; + + u1.ll = op1; + u2.ll = op2; + return float64_eq_quiet(u1.d, u2.d, &env->vec_status) ? 4 : 0; +} + +uint32_t helper_efdcmplt(CPUPPCState *env, uint64_t op1, uint64_t op2) +{ + /* XXX: TODO: test special values (NaN, infinites, ...) */ + return helper_efdtstlt(env, op1, op2); +} + +uint32_t helper_efdcmpgt(CPUPPCState *env, uint64_t op1, uint64_t op2) +{ + /* XXX: TODO: test special values (NaN, infinites, ...) */ + return helper_efdtstgt(env, op1, op2); +} + +uint32_t helper_efdcmpeq(CPUPPCState *env, uint64_t op1, uint64_t op2) +{ + /* XXX: TODO: test special values (NaN, infinites, ...) */ + return helper_efdtsteq(env, op1, op2); +} diff --git a/target-ppc/helper.c b/target-ppc/helper.c index 3f7d8a464f..48b19a7e1d 100644 --- a/target-ppc/helper.c +++ b/target-ppc/helper.c @@ -1,5 +1,5 @@ /* - * PowerPC emulation helpers for qemu. + * PowerPC emulation helpers for QEMU. * * Copyright (c) 2003-2007 Jocelyn Mayer * @@ -23,3169 +23,6 @@ #include "kvm_ppc.h" #include "cpus.h" -//#define DEBUG_MMU -//#define DEBUG_BATS -//#define DEBUG_SLB -//#define DEBUG_SOFTWARE_TLB -//#define DUMP_PAGE_TABLES -//#define DEBUG_EXCEPTIONS -//#define FLUSH_ALL_TLBS - -#ifdef DEBUG_MMU -# define LOG_MMU(...) qemu_log(__VA_ARGS__) -# define LOG_MMU_STATE(env) log_cpu_state((env), 0) -#else -# define LOG_MMU(...) do { } while (0) -# define LOG_MMU_STATE(...) do { } while (0) -#endif - - -#ifdef DEBUG_SOFTWARE_TLB -# define LOG_SWTLB(...) qemu_log(__VA_ARGS__) -#else -# define LOG_SWTLB(...) do { } while (0) -#endif - -#ifdef DEBUG_BATS -# define LOG_BATS(...) qemu_log(__VA_ARGS__) -#else -# define LOG_BATS(...) do { } while (0) -#endif - -#ifdef DEBUG_SLB -# define LOG_SLB(...) qemu_log(__VA_ARGS__) -#else -# define LOG_SLB(...) do { } while (0) -#endif - -#ifdef DEBUG_EXCEPTIONS -# define LOG_EXCP(...) qemu_log(__VA_ARGS__) -#else -# define LOG_EXCP(...) do { } while (0) -#endif - -/*****************************************************************************/ -/* PowerPC Hypercall emulation */ - -void (*cpu_ppc_hypercall)(CPUPPCState *); - -/*****************************************************************************/ -/* PowerPC MMU emulation */ - -#if defined(CONFIG_USER_ONLY) -int cpu_ppc_handle_mmu_fault (CPUPPCState *env, target_ulong address, int rw, - int mmu_idx) -{ - int exception, error_code; - - if (rw == 2) { - exception = POWERPC_EXCP_ISI; - error_code = 0x40000000; - } else { - exception = POWERPC_EXCP_DSI; - error_code = 0x40000000; - if (rw) - error_code |= 0x02000000; - env->spr[SPR_DAR] = address; - env->spr[SPR_DSISR] = error_code; - } - env->exception_index = exception; - env->error_code = error_code; - - return 1; -} - -#else -/* Common routines used by software and hardware TLBs emulation */ -static inline int pte_is_valid(target_ulong pte0) -{ - return pte0 & 0x80000000 ? 1 : 0; -} - -static inline void pte_invalidate(target_ulong *pte0) -{ - *pte0 &= ~0x80000000; -} - -#if defined(TARGET_PPC64) -static inline int pte64_is_valid(target_ulong pte0) -{ - return pte0 & 0x0000000000000001ULL ? 1 : 0; -} - -static inline void pte64_invalidate(target_ulong *pte0) -{ - *pte0 &= ~0x0000000000000001ULL; -} -#endif - -#define PTE_PTEM_MASK 0x7FFFFFBF -#define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B) -#if defined(TARGET_PPC64) -#define PTE64_PTEM_MASK 0xFFFFFFFFFFFFFF80ULL -#define PTE64_CHECK_MASK (TARGET_PAGE_MASK | 0x7F) -#endif - -static inline int pp_check(int key, int pp, int nx) -{ - int access; - - /* Compute access rights */ - /* When pp is 3/7, the result is undefined. Set it to noaccess */ - access = 0; - if (key == 0) { - switch (pp) { - case 0x0: - case 0x1: - case 0x2: - access |= PAGE_WRITE; - /* No break here */ - case 0x3: - case 0x6: - access |= PAGE_READ; - break; - } - } else { - switch (pp) { - case 0x0: - case 0x6: - access = 0; - break; - case 0x1: - case 0x3: - access = PAGE_READ; - break; - case 0x2: - access = PAGE_READ | PAGE_WRITE; - break; - } - } - if (nx == 0) - access |= PAGE_EXEC; - - return access; -} - -static inline int check_prot(int prot, int rw, int access_type) -{ - int ret; - - if (access_type == ACCESS_CODE) { - if (prot & PAGE_EXEC) - ret = 0; - else - ret = -2; - } else if (rw) { - if (prot & PAGE_WRITE) - ret = 0; - else - ret = -2; - } else { - if (prot & PAGE_READ) - ret = 0; - else - ret = -2; - } - - return ret; -} - -static inline int _pte_check(mmu_ctx_t *ctx, int is_64b, target_ulong pte0, - target_ulong pte1, int h, int rw, int type) -{ - target_ulong ptem, mmask; - int access, ret, pteh, ptev, pp; - - ret = -1; - /* Check validity and table match */ -#if defined(TARGET_PPC64) - if (is_64b) { - ptev = pte64_is_valid(pte0); - pteh = (pte0 >> 1) & 1; - } else -#endif - { - ptev = pte_is_valid(pte0); - pteh = (pte0 >> 6) & 1; - } - if (ptev && h == pteh) { - /* Check vsid & api */ -#if defined(TARGET_PPC64) - if (is_64b) { - ptem = pte0 & PTE64_PTEM_MASK; - mmask = PTE64_CHECK_MASK; - pp = (pte1 & 0x00000003) | ((pte1 >> 61) & 0x00000004); - ctx->nx = (pte1 >> 2) & 1; /* No execute bit */ - ctx->nx |= (pte1 >> 3) & 1; /* Guarded bit */ - } else -#endif - { - ptem = pte0 & PTE_PTEM_MASK; - mmask = PTE_CHECK_MASK; - pp = pte1 & 0x00000003; - } - if (ptem == ctx->ptem) { - if (ctx->raddr != (target_phys_addr_t)-1ULL) { - /* all matches should have equal RPN, WIMG & PP */ - if ((ctx->raddr & mmask) != (pte1 & mmask)) { - qemu_log("Bad RPN/WIMG/PP\n"); - return -3; - } - } - /* Compute access rights */ - access = pp_check(ctx->key, pp, ctx->nx); - /* Keep the matching PTE informations */ - ctx->raddr = pte1; - ctx->prot = access; - ret = check_prot(ctx->prot, rw, type); - if (ret == 0) { - /* Access granted */ - LOG_MMU("PTE access granted !\n"); - } else { - /* Access right violation */ - LOG_MMU("PTE access rejected\n"); - } - } - } - - return ret; -} - -static inline int pte32_check(mmu_ctx_t *ctx, target_ulong pte0, - target_ulong pte1, int h, int rw, int type) -{ - return _pte_check(ctx, 0, pte0, pte1, h, rw, type); -} - -#if defined(TARGET_PPC64) -static inline int pte64_check(mmu_ctx_t *ctx, target_ulong pte0, - target_ulong pte1, int h, int rw, int type) -{ - return _pte_check(ctx, 1, pte0, pte1, h, rw, type); -} -#endif - -static inline int pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, - int ret, int rw) -{ - int store = 0; - - /* Update page flags */ - if (!(*pte1p & 0x00000100)) { - /* Update accessed flag */ - *pte1p |= 0x00000100; - store = 1; - } - if (!(*pte1p & 0x00000080)) { - if (rw == 1 && ret == 0) { - /* Update changed flag */ - *pte1p |= 0x00000080; - store = 1; - } else { - /* Force page fault for first write access */ - ctx->prot &= ~PAGE_WRITE; - } - } - - return store; -} - -/* Software driven TLB helpers */ -static inline int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, int way, - int is_code) -{ - int nr; - - /* Select TLB num in a way from address */ - nr = (eaddr >> TARGET_PAGE_BITS) & (env->tlb_per_way - 1); - /* Select TLB way */ - nr += env->tlb_per_way * way; - /* 6xx have separate TLBs for instructions and data */ - if (is_code && env->id_tlbs == 1) - nr += env->nb_tlb; - - return nr; -} - -static inline void ppc6xx_tlb_invalidate_all(CPUPPCState *env) -{ - ppc6xx_tlb_t *tlb; - int nr, max; - - //LOG_SWTLB("Invalidate all TLBs\n"); - /* Invalidate all defined software TLB */ - max = env->nb_tlb; - if (env->id_tlbs == 1) - max *= 2; - for (nr = 0; nr < max; nr++) { - tlb = &env->tlb.tlb6[nr]; - pte_invalidate(&tlb->pte0); - } - tlb_flush(env, 1); -} - -static inline void __ppc6xx_tlb_invalidate_virt(CPUPPCState *env, - target_ulong eaddr, - int is_code, int match_epn) -{ -#if !defined(FLUSH_ALL_TLBS) - ppc6xx_tlb_t *tlb; - int way, nr; - - /* Invalidate ITLB + DTLB, all ways */ - for (way = 0; way < env->nb_ways; way++) { - nr = ppc6xx_tlb_getnum(env, eaddr, way, is_code); - tlb = &env->tlb.tlb6[nr]; - if (pte_is_valid(tlb->pte0) && (match_epn == 0 || eaddr == tlb->EPN)) { - LOG_SWTLB("TLB invalidate %d/%d " TARGET_FMT_lx "\n", nr, - env->nb_tlb, eaddr); - pte_invalidate(&tlb->pte0); - tlb_flush_page(env, tlb->EPN); - } - } -#else - /* XXX: PowerPC specification say this is valid as well */ - ppc6xx_tlb_invalidate_all(env); -#endif -} - -static inline void ppc6xx_tlb_invalidate_virt(CPUPPCState *env, - target_ulong eaddr, int is_code) -{ - __ppc6xx_tlb_invalidate_virt(env, eaddr, is_code, 0); -} - -void ppc6xx_tlb_store (CPUPPCState *env, target_ulong EPN, int way, int is_code, - target_ulong pte0, target_ulong pte1) -{ - ppc6xx_tlb_t *tlb; - int nr; - - nr = ppc6xx_tlb_getnum(env, EPN, way, is_code); - tlb = &env->tlb.tlb6[nr]; - LOG_SWTLB("Set TLB %d/%d EPN " TARGET_FMT_lx " PTE0 " TARGET_FMT_lx - " PTE1 " TARGET_FMT_lx "\n", nr, env->nb_tlb, EPN, pte0, pte1); - /* Invalidate any pending reference in QEMU for this virtual address */ - __ppc6xx_tlb_invalidate_virt(env, EPN, is_code, 1); - tlb->pte0 = pte0; - tlb->pte1 = pte1; - tlb->EPN = EPN; - /* Store last way for LRU mechanism */ - env->last_way = way; -} - -static inline int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, int rw, int access_type) -{ - ppc6xx_tlb_t *tlb; - int nr, best, way; - int ret; - - best = -1; - ret = -1; /* No TLB found */ - for (way = 0; way < env->nb_ways; way++) { - nr = ppc6xx_tlb_getnum(env, eaddr, way, - access_type == ACCESS_CODE ? 1 : 0); - tlb = &env->tlb.tlb6[nr]; - /* This test "emulates" the PTE index match for hardware TLBs */ - if ((eaddr & TARGET_PAGE_MASK) != tlb->EPN) { - LOG_SWTLB("TLB %d/%d %s [" TARGET_FMT_lx " " TARGET_FMT_lx - "] <> " TARGET_FMT_lx "\n", nr, env->nb_tlb, - pte_is_valid(tlb->pte0) ? "valid" : "inval", - tlb->EPN, tlb->EPN + TARGET_PAGE_SIZE, eaddr); - continue; - } - LOG_SWTLB("TLB %d/%d %s " TARGET_FMT_lx " <> " TARGET_FMT_lx " " - TARGET_FMT_lx " %c %c\n", nr, env->nb_tlb, - pte_is_valid(tlb->pte0) ? "valid" : "inval", - tlb->EPN, eaddr, tlb->pte1, - rw ? 'S' : 'L', access_type == ACCESS_CODE ? 'I' : 'D'); - switch (pte32_check(ctx, tlb->pte0, tlb->pte1, 0, rw, access_type)) { - case -3: - /* TLB inconsistency */ - return -1; - case -2: - /* Access violation */ - ret = -2; - best = nr; - break; - case -1: - default: - /* No match */ - break; - case 0: - /* access granted */ - /* XXX: we should go on looping to check all TLBs consistency - * but we can speed-up the whole thing as the - * result would be undefined if TLBs are not consistent. - */ - ret = 0; - best = nr; - goto done; - } - } - if (best != -1) { - done: - LOG_SWTLB("found TLB at addr " TARGET_FMT_plx " prot=%01x ret=%d\n", - ctx->raddr & TARGET_PAGE_MASK, ctx->prot, ret); - /* Update page flags */ - pte_update_flags(ctx, &env->tlb.tlb6[best].pte1, ret, rw); - } - - return ret; -} - -/* Perform BAT hit & translation */ -static inline void bat_size_prot(CPUPPCState *env, target_ulong *blp, int *validp, - int *protp, target_ulong *BATu, - target_ulong *BATl) -{ - target_ulong bl; - int pp, valid, prot; - - bl = (*BATu & 0x00001FFC) << 15; - valid = 0; - prot = 0; - if (((msr_pr == 0) && (*BATu & 0x00000002)) || - ((msr_pr != 0) && (*BATu & 0x00000001))) { - valid = 1; - pp = *BATl & 0x00000003; - if (pp != 0) { - prot = PAGE_READ | PAGE_EXEC; - if (pp == 0x2) - prot |= PAGE_WRITE; - } - } - *blp = bl; - *validp = valid; - *protp = prot; -} - -static inline void bat_601_size_prot(CPUPPCState *env, target_ulong *blp, - int *validp, int *protp, - target_ulong *BATu, target_ulong *BATl) -{ - target_ulong bl; - int key, pp, valid, prot; - - bl = (*BATl & 0x0000003F) << 17; - LOG_BATS("b %02x ==> bl " TARGET_FMT_lx " msk " TARGET_FMT_lx "\n", - (uint8_t)(*BATl & 0x0000003F), bl, ~bl); - prot = 0; - valid = (*BATl >> 6) & 1; - if (valid) { - pp = *BATu & 0x00000003; - if (msr_pr == 0) - key = (*BATu >> 3) & 1; - else - key = (*BATu >> 2) & 1; - prot = pp_check(key, pp, 0); - } - *blp = bl; - *validp = valid; - *protp = prot; -} - -static inline int get_bat(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong virtual, - int rw, int type) -{ - target_ulong *BATlt, *BATut, *BATu, *BATl; - target_ulong BEPIl, BEPIu, bl; - int i, valid, prot; - int ret = -1; - - LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__, - type == ACCESS_CODE ? 'I' : 'D', virtual); - switch (type) { - case ACCESS_CODE: - BATlt = env->IBAT[1]; - BATut = env->IBAT[0]; - break; - default: - BATlt = env->DBAT[1]; - BATut = env->DBAT[0]; - break; - } - for (i = 0; i < env->nb_BATs; i++) { - BATu = &BATut[i]; - BATl = &BATlt[i]; - BEPIu = *BATu & 0xF0000000; - BEPIl = *BATu & 0x0FFE0000; - if (unlikely(env->mmu_model == POWERPC_MMU_601)) { - bat_601_size_prot(env, &bl, &valid, &prot, BATu, BATl); - } else { - bat_size_prot(env, &bl, &valid, &prot, BATu, BATl); - } - LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx - " BATl " TARGET_FMT_lx "\n", __func__, - type == ACCESS_CODE ? 'I' : 'D', i, virtual, *BATu, *BATl); - if ((virtual & 0xF0000000) == BEPIu && - ((virtual & 0x0FFE0000) & ~bl) == BEPIl) { - /* BAT matches */ - if (valid != 0) { - /* Get physical address */ - ctx->raddr = (*BATl & 0xF0000000) | - ((virtual & 0x0FFE0000 & bl) | (*BATl & 0x0FFE0000)) | - (virtual & 0x0001F000); - /* Compute access rights */ - ctx->prot = prot; - ret = check_prot(ctx->prot, rw, type); - if (ret == 0) - LOG_BATS("BAT %d match: r " TARGET_FMT_plx " prot=%c%c\n", - i, ctx->raddr, ctx->prot & PAGE_READ ? 'R' : '-', - ctx->prot & PAGE_WRITE ? 'W' : '-'); - break; - } - } - } - if (ret < 0) { -#if defined(DEBUG_BATS) - if (qemu_log_enabled()) { - LOG_BATS("no BAT match for " TARGET_FMT_lx ":\n", virtual); - for (i = 0; i < 4; i++) { - BATu = &BATut[i]; - BATl = &BATlt[i]; - BEPIu = *BATu & 0xF0000000; - BEPIl = *BATu & 0x0FFE0000; - bl = (*BATu & 0x00001FFC) << 15; - LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx - " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " - TARGET_FMT_lx " " TARGET_FMT_lx "\n", - __func__, type == ACCESS_CODE ? 'I' : 'D', i, virtual, - *BATu, *BATl, BEPIu, BEPIl, bl); - } - } -#endif - } - /* No hit */ - return ret; -} - -static inline target_phys_addr_t get_pteg_offset(CPUPPCState *env, - target_phys_addr_t hash, - int pte_size) -{ - return (hash * pte_size * 8) & env->htab_mask; -} - -/* PTE table lookup */ -static inline int _find_pte(CPUPPCState *env, mmu_ctx_t *ctx, int is_64b, int h, - int rw, int type, int target_page_bits) -{ - target_phys_addr_t pteg_off; - target_ulong pte0, pte1; - int i, good = -1; - int ret, r; - - ret = -1; /* No entry found */ - pteg_off = get_pteg_offset(env, ctx->hash[h], - is_64b ? HASH_PTE_SIZE_64 : HASH_PTE_SIZE_32); - for (i = 0; i < 8; i++) { -#if defined(TARGET_PPC64) - if (is_64b) { - if (env->external_htab) { - pte0 = ldq_p(env->external_htab + pteg_off + (i * 16)); - pte1 = ldq_p(env->external_htab + pteg_off + (i * 16) + 8); - } else { - pte0 = ldq_phys(env->htab_base + pteg_off + (i * 16)); - pte1 = ldq_phys(env->htab_base + pteg_off + (i * 16) + 8); - } - - r = pte64_check(ctx, pte0, pte1, h, rw, type); - LOG_MMU("Load pte from " TARGET_FMT_lx " => " TARGET_FMT_lx " " - TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n", - pteg_off + (i * 16), pte0, pte1, (int)(pte0 & 1), h, - (int)((pte0 >> 1) & 1), ctx->ptem); - } else -#endif - { - if (env->external_htab) { - pte0 = ldl_p(env->external_htab + pteg_off + (i * 8)); - pte1 = ldl_p(env->external_htab + pteg_off + (i * 8) + 4); - } else { - pte0 = ldl_phys(env->htab_base + pteg_off + (i * 8)); - pte1 = ldl_phys(env->htab_base + pteg_off + (i * 8) + 4); - } - r = pte32_check(ctx, pte0, pte1, h, rw, type); - LOG_MMU("Load pte from " TARGET_FMT_lx " => " TARGET_FMT_lx " " - TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n", - pteg_off + (i * 8), pte0, pte1, (int)(pte0 >> 31), h, - (int)((pte0 >> 6) & 1), ctx->ptem); - } - switch (r) { - case -3: - /* PTE inconsistency */ - return -1; - case -2: - /* Access violation */ - ret = -2; - good = i; - break; - case -1: - default: - /* No PTE match */ - break; - case 0: - /* access granted */ - /* XXX: we should go on looping to check all PTEs consistency - * but if we can speed-up the whole thing as the - * result would be undefined if PTEs are not consistent. - */ - ret = 0; - good = i; - goto done; - } - } - if (good != -1) { - done: - LOG_MMU("found PTE at addr " TARGET_FMT_lx " prot=%01x ret=%d\n", - ctx->raddr, ctx->prot, ret); - /* Update page flags */ - pte1 = ctx->raddr; - if (pte_update_flags(ctx, &pte1, ret, rw) == 1) { -#if defined(TARGET_PPC64) - if (is_64b) { - if (env->external_htab) { - stq_p(env->external_htab + pteg_off + (good * 16) + 8, - pte1); - } else { - stq_phys_notdirty(env->htab_base + pteg_off + - (good * 16) + 8, pte1); - } - } else -#endif - { - if (env->external_htab) { - stl_p(env->external_htab + pteg_off + (good * 8) + 4, - pte1); - } else { - stl_phys_notdirty(env->htab_base + pteg_off + - (good * 8) + 4, pte1); - } - } - } - } - - /* We have a TLB that saves 4K pages, so let's - * split a huge page to 4k chunks */ - if (target_page_bits != TARGET_PAGE_BITS) { - ctx->raddr |= (ctx->eaddr & ((1 << target_page_bits) - 1)) - & TARGET_PAGE_MASK; - } - return ret; -} - -static inline int find_pte(CPUPPCState *env, mmu_ctx_t *ctx, int h, int rw, - int type, int target_page_bits) -{ -#if defined(TARGET_PPC64) - if (env->mmu_model & POWERPC_MMU_64) - return _find_pte(env, ctx, 1, h, rw, type, target_page_bits); -#endif - - return _find_pte(env, ctx, 0, h, rw, type, target_page_bits); -} - -#if defined(TARGET_PPC64) -static inline ppc_slb_t *slb_lookup(CPUPPCState *env, target_ulong eaddr) -{ - uint64_t esid_256M, esid_1T; - int n; - - LOG_SLB("%s: eaddr " TARGET_FMT_lx "\n", __func__, eaddr); - - esid_256M = (eaddr & SEGMENT_MASK_256M) | SLB_ESID_V; - esid_1T = (eaddr & SEGMENT_MASK_1T) | SLB_ESID_V; - - for (n = 0; n < env->slb_nr; n++) { - ppc_slb_t *slb = &env->slb[n]; - - LOG_SLB("%s: slot %d %016" PRIx64 " %016" - PRIx64 "\n", __func__, n, slb->esid, slb->vsid); - /* We check for 1T matches on all MMUs here - if the MMU - * doesn't have 1T segment support, we will have prevented 1T - * entries from being inserted in the slbmte code. */ - if (((slb->esid == esid_256M) && - ((slb->vsid & SLB_VSID_B) == SLB_VSID_B_256M)) - || ((slb->esid == esid_1T) && - ((slb->vsid & SLB_VSID_B) == SLB_VSID_B_1T))) { - return slb; - } - } - - return NULL; -} - -void ppc_slb_invalidate_all (CPUPPCState *env) -{ - int n, do_invalidate; - - do_invalidate = 0; - /* XXX: Warning: slbia never invalidates the first segment */ - for (n = 1; n < env->slb_nr; n++) { - ppc_slb_t *slb = &env->slb[n]; - - if (slb->esid & SLB_ESID_V) { - slb->esid &= ~SLB_ESID_V; - /* XXX: given the fact that segment size is 256 MB or 1TB, - * and we still don't have a tlb_flush_mask(env, n, mask) - * in QEMU, we just invalidate all TLBs - */ - do_invalidate = 1; - } - } - if (do_invalidate) - tlb_flush(env, 1); -} - -void ppc_slb_invalidate_one (CPUPPCState *env, uint64_t T0) -{ - ppc_slb_t *slb; - - slb = slb_lookup(env, T0); - if (!slb) { - return; - } - - if (slb->esid & SLB_ESID_V) { - slb->esid &= ~SLB_ESID_V; - - /* XXX: given the fact that segment size is 256 MB or 1TB, - * and we still don't have a tlb_flush_mask(env, n, mask) - * in QEMU, we just invalidate all TLBs - */ - tlb_flush(env, 1); - } -} - -int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs) -{ - int slot = rb & 0xfff; - ppc_slb_t *slb = &env->slb[slot]; - - if (rb & (0x1000 - env->slb_nr)) { - return -1; /* Reserved bits set or slot too high */ - } - if (rs & (SLB_VSID_B & ~SLB_VSID_B_1T)) { - return -1; /* Bad segment size */ - } - if ((rs & SLB_VSID_B) && !(env->mmu_model & POWERPC_MMU_1TSEG)) { - return -1; /* 1T segment on MMU that doesn't support it */ - } - - /* Mask out the slot number as we store the entry */ - slb->esid = rb & (SLB_ESID_ESID | SLB_ESID_V); - slb->vsid = rs; - - LOG_SLB("%s: %d " TARGET_FMT_lx " - " TARGET_FMT_lx " => %016" PRIx64 - " %016" PRIx64 "\n", __func__, slot, rb, rs, - slb->esid, slb->vsid); - - return 0; -} - -int ppc_load_slb_esid (CPUPPCState *env, target_ulong rb, target_ulong *rt) -{ - int slot = rb & 0xfff; - ppc_slb_t *slb = &env->slb[slot]; - - if (slot >= env->slb_nr) { - return -1; - } - - *rt = slb->esid; - return 0; -} - -int ppc_load_slb_vsid (CPUPPCState *env, target_ulong rb, target_ulong *rt) -{ - int slot = rb & 0xfff; - ppc_slb_t *slb = &env->slb[slot]; - - if (slot >= env->slb_nr) { - return -1; - } - - *rt = slb->vsid; - return 0; -} -#endif /* defined(TARGET_PPC64) */ - -/* Perform segment based translation */ -static inline int get_segment(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, int rw, int type) -{ - target_phys_addr_t hash; - target_ulong vsid; - int ds, pr, target_page_bits; - int ret, ret2; - - pr = msr_pr; - ctx->eaddr = eaddr; -#if defined(TARGET_PPC64) - if (env->mmu_model & POWERPC_MMU_64) { - ppc_slb_t *slb; - target_ulong pageaddr; - int segment_bits; - - LOG_MMU("Check SLBs\n"); - slb = slb_lookup(env, eaddr); - if (!slb) { - return -5; - } - - if (slb->vsid & SLB_VSID_B) { - vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT_1T; - segment_bits = 40; - } else { - vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT; - segment_bits = 28; - } - - target_page_bits = (slb->vsid & SLB_VSID_L) - ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS; - ctx->key = !!(pr ? (slb->vsid & SLB_VSID_KP) - : (slb->vsid & SLB_VSID_KS)); - ds = 0; - ctx->nx = !!(slb->vsid & SLB_VSID_N); - - pageaddr = eaddr & ((1ULL << segment_bits) - - (1ULL << target_page_bits)); - if (slb->vsid & SLB_VSID_B) { - hash = vsid ^ (vsid << 25) ^ (pageaddr >> target_page_bits); - } else { - hash = vsid ^ (pageaddr >> target_page_bits); - } - /* Only 5 bits of the page index are used in the AVPN */ - ctx->ptem = (slb->vsid & SLB_VSID_PTEM) | - ((pageaddr >> 16) & ((1ULL << segment_bits) - 0x80)); - } else -#endif /* defined(TARGET_PPC64) */ - { - target_ulong sr, pgidx; - - sr = env->sr[eaddr >> 28]; - ctx->key = (((sr & 0x20000000) && (pr != 0)) || - ((sr & 0x40000000) && (pr == 0))) ? 1 : 0; - ds = sr & 0x80000000 ? 1 : 0; - ctx->nx = sr & 0x10000000 ? 1 : 0; - vsid = sr & 0x00FFFFFF; - target_page_bits = TARGET_PAGE_BITS; - LOG_MMU("Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip=" - TARGET_FMT_lx " lr=" TARGET_FMT_lx - " ir=%d dr=%d pr=%d %d t=%d\n", - eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir, - (int)msr_dr, pr != 0 ? 1 : 0, rw, type); - pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits; - hash = vsid ^ pgidx; - ctx->ptem = (vsid << 7) | (pgidx >> 10); - } - LOG_MMU("pte segment: key=%d ds %d nx %d vsid " TARGET_FMT_lx "\n", - ctx->key, ds, ctx->nx, vsid); - ret = -1; - if (!ds) { - /* Check if instruction fetch is allowed, if needed */ - if (type != ACCESS_CODE || ctx->nx == 0) { - /* Page address translation */ - LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx - " hash " TARGET_FMT_plx "\n", - env->htab_base, env->htab_mask, hash); - ctx->hash[0] = hash; - ctx->hash[1] = ~hash; - - /* Initialize real address with an invalid value */ - ctx->raddr = (target_phys_addr_t)-1ULL; - if (unlikely(env->mmu_model == POWERPC_MMU_SOFT_6xx || - env->mmu_model == POWERPC_MMU_SOFT_74xx)) { - /* Software TLB search */ - ret = ppc6xx_tlb_check(env, ctx, eaddr, rw, type); - } else { - LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx - " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx - " hash=" TARGET_FMT_plx "\n", - env->htab_base, env->htab_mask, vsid, ctx->ptem, - ctx->hash[0]); - /* Primary table lookup */ - ret = find_pte(env, ctx, 0, rw, type, target_page_bits); - if (ret < 0) { - /* Secondary table lookup */ - if (eaddr != 0xEFFFFFFF) - LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx - " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx - " hash=" TARGET_FMT_plx "\n", env->htab_base, - env->htab_mask, vsid, ctx->ptem, ctx->hash[1]); - ret2 = find_pte(env, ctx, 1, rw, type, - target_page_bits); - if (ret2 != -1) - ret = ret2; - } - } -#if defined (DUMP_PAGE_TABLES) - if (qemu_log_enabled()) { - target_phys_addr_t curaddr; - uint32_t a0, a1, a2, a3; - qemu_log("Page table: " TARGET_FMT_plx " len " TARGET_FMT_plx - "\n", sdr, mask + 0x80); - for (curaddr = sdr; curaddr < (sdr + mask + 0x80); - curaddr += 16) { - a0 = ldl_phys(curaddr); - a1 = ldl_phys(curaddr + 4); - a2 = ldl_phys(curaddr + 8); - a3 = ldl_phys(curaddr + 12); - if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) { - qemu_log(TARGET_FMT_plx ": %08x %08x %08x %08x\n", - curaddr, a0, a1, a2, a3); - } - } - } -#endif - } else { - LOG_MMU("No access allowed\n"); - ret = -3; - } - } else { - target_ulong sr; - LOG_MMU("direct store...\n"); - /* Direct-store segment : absolutely *BUGGY* for now */ - - /* Direct-store implies a 32-bit MMU. - * Check the Segment Register's bus unit ID (BUID). - */ - sr = env->sr[eaddr >> 28]; - if ((sr & 0x1FF00000) >> 20 == 0x07f) { - /* Memory-forced I/O controller interface access */ - /* If T=1 and BUID=x'07F', the 601 performs a memory access - * to SR[28-31] LA[4-31], bypassing all protection mechanisms. - */ - ctx->raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF); - ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - return 0; - } - - switch (type) { - case ACCESS_INT: - /* Integer load/store : only access allowed */ - break; - case ACCESS_CODE: - /* No code fetch is allowed in direct-store areas */ - return -4; - case ACCESS_FLOAT: - /* Floating point load/store */ - return -4; - case ACCESS_RES: - /* lwarx, ldarx or srwcx. */ - return -4; - case ACCESS_CACHE: - /* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */ - /* Should make the instruction do no-op. - * As it already do no-op, it's quite easy :-) - */ - ctx->raddr = eaddr; - return 0; - case ACCESS_EXT: - /* eciwx or ecowx */ - return -4; - default: - qemu_log("ERROR: instruction should not need " - "address translation\n"); - return -4; - } - if ((rw == 1 || ctx->key != 1) && (rw == 0 || ctx->key != 0)) { - ctx->raddr = eaddr; - ret = 2; - } else { - ret = -2; - } - } - - return ret; -} - -/* Generic TLB check function for embedded PowerPC implementations */ -int ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, - target_phys_addr_t *raddrp, - target_ulong address, uint32_t pid, int ext, - int i) -{ - target_ulong mask; - - /* Check valid flag */ - if (!(tlb->prot & PAGE_VALID)) { - return -1; - } - mask = ~(tlb->size - 1); - LOG_SWTLB("%s: TLB %d address " TARGET_FMT_lx " PID %u <=> " TARGET_FMT_lx - " " TARGET_FMT_lx " %u %x\n", __func__, i, address, pid, tlb->EPN, - mask, (uint32_t)tlb->PID, tlb->prot); - /* Check PID */ - if (tlb->PID != 0 && tlb->PID != pid) - return -1; - /* Check effective address */ - if ((address & mask) != tlb->EPN) - return -1; - *raddrp = (tlb->RPN & mask) | (address & ~mask); -#if (TARGET_PHYS_ADDR_BITS >= 36) - if (ext) { - /* Extend the physical address to 36 bits */ - *raddrp |= (target_phys_addr_t)(tlb->RPN & 0xF) << 32; - } -#endif - - return 0; -} - -/* Generic TLB search function for PowerPC embedded implementations */ -int ppcemb_tlb_search (CPUPPCState *env, target_ulong address, uint32_t pid) -{ - ppcemb_tlb_t *tlb; - target_phys_addr_t raddr; - int i, ret; - - /* Default return value is no match */ - ret = -1; - for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb.tlbe[i]; - if (ppcemb_tlb_check(env, tlb, &raddr, address, pid, 0, i) == 0) { - ret = i; - break; - } - } - - return ret; -} - -/* Helpers specific to PowerPC 40x implementations */ -static inline void ppc4xx_tlb_invalidate_all(CPUPPCState *env) -{ - ppcemb_tlb_t *tlb; - int i; - - for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb.tlbe[i]; - tlb->prot &= ~PAGE_VALID; - } - tlb_flush(env, 1); -} - -static inline void ppc4xx_tlb_invalidate_virt(CPUPPCState *env, - target_ulong eaddr, uint32_t pid) -{ -#if !defined(FLUSH_ALL_TLBS) - ppcemb_tlb_t *tlb; - target_phys_addr_t raddr; - target_ulong page, end; - int i; - - for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb.tlbe[i]; - if (ppcemb_tlb_check(env, tlb, &raddr, eaddr, pid, 0, i) == 0) { - end = tlb->EPN + tlb->size; - for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) - tlb_flush_page(env, page); - tlb->prot &= ~PAGE_VALID; - break; - } - } -#else - ppc4xx_tlb_invalidate_all(env); -#endif -} - -static int mmu40x_get_physical_address (CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong address, int rw, int access_type) -{ - ppcemb_tlb_t *tlb; - target_phys_addr_t raddr; - int i, ret, zsel, zpr, pr; - - ret = -1; - raddr = (target_phys_addr_t)-1ULL; - pr = msr_pr; - for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb.tlbe[i]; - if (ppcemb_tlb_check(env, tlb, &raddr, address, - env->spr[SPR_40x_PID], 0, i) < 0) - continue; - zsel = (tlb->attr >> 4) & 0xF; - zpr = (env->spr[SPR_40x_ZPR] >> (30 - (2 * zsel))) & 0x3; - LOG_SWTLB("%s: TLB %d zsel %d zpr %d rw %d attr %08x\n", - __func__, i, zsel, zpr, rw, tlb->attr); - /* Check execute enable bit */ - switch (zpr) { - case 0x2: - if (pr != 0) - goto check_perms; - /* No break here */ - case 0x3: - /* All accesses granted */ - ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - ret = 0; - break; - case 0x0: - if (pr != 0) { - /* Raise Zone protection fault. */ - env->spr[SPR_40x_ESR] = 1 << 22; - ctx->prot = 0; - ret = -2; - break; - } - /* No break here */ - case 0x1: - check_perms: - /* Check from TLB entry */ - ctx->prot = tlb->prot; - ret = check_prot(ctx->prot, rw, access_type); - if (ret == -2) - env->spr[SPR_40x_ESR] = 0; - break; - } - if (ret >= 0) { - ctx->raddr = raddr; - LOG_SWTLB("%s: access granted " TARGET_FMT_lx " => " TARGET_FMT_plx - " %d %d\n", __func__, address, ctx->raddr, ctx->prot, - ret); - return 0; - } - } - LOG_SWTLB("%s: access refused " TARGET_FMT_lx " => " TARGET_FMT_plx - " %d %d\n", __func__, address, raddr, ctx->prot, ret); - - return ret; -} - -void store_40x_sler (CPUPPCState *env, uint32_t val) -{ - /* XXX: TO BE FIXED */ - if (val != 0x00000000) { - cpu_abort(env, "Little-endian regions are not supported by now\n"); - } - env->spr[SPR_405_SLER] = val; -} - -static inline int mmubooke_check_tlb (CPUPPCState *env, ppcemb_tlb_t *tlb, - target_phys_addr_t *raddr, int *prot, - target_ulong address, int rw, - int access_type, int i) -{ - int ret, _prot; - - if (ppcemb_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID], - !env->nb_pids, i) >= 0) { - goto found_tlb; - } - - if (env->spr[SPR_BOOKE_PID1] && - ppcemb_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID1], 0, i) >= 0) { - goto found_tlb; - } - - if (env->spr[SPR_BOOKE_PID2] && - ppcemb_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID2], 0, i) >= 0) { - goto found_tlb; - } - - LOG_SWTLB("%s: TLB entry not found\n", __func__); - return -1; - -found_tlb: - - if (msr_pr != 0) { - _prot = tlb->prot & 0xF; - } else { - _prot = (tlb->prot >> 4) & 0xF; - } - - /* Check the address space */ - if (access_type == ACCESS_CODE) { - if (msr_ir != (tlb->attr & 1)) { - LOG_SWTLB("%s: AS doesn't match\n", __func__); - return -1; - } - - *prot = _prot; - if (_prot & PAGE_EXEC) { - LOG_SWTLB("%s: good TLB!\n", __func__); - return 0; - } - - LOG_SWTLB("%s: no PAGE_EXEC: %x\n", __func__, _prot); - ret = -3; - } else { - if (msr_dr != (tlb->attr & 1)) { - LOG_SWTLB("%s: AS doesn't match\n", __func__); - return -1; - } - - *prot = _prot; - if ((!rw && _prot & PAGE_READ) || (rw && (_prot & PAGE_WRITE))) { - LOG_SWTLB("%s: found TLB!\n", __func__); - return 0; - } - - LOG_SWTLB("%s: PAGE_READ/WRITE doesn't match: %x\n", __func__, _prot); - ret = -2; - } - - return ret; -} - -static int mmubooke_get_physical_address (CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong address, int rw, - int access_type) -{ - ppcemb_tlb_t *tlb; - target_phys_addr_t raddr; - int i, ret; - - ret = -1; - raddr = (target_phys_addr_t)-1ULL; - for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb.tlbe[i]; - ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, rw, - access_type, i); - if (!ret) { - break; - } - } - - if (ret >= 0) { - ctx->raddr = raddr; - LOG_SWTLB("%s: access granted " TARGET_FMT_lx " => " TARGET_FMT_plx - " %d %d\n", __func__, address, ctx->raddr, ctx->prot, - ret); - } else { - LOG_SWTLB("%s: access refused " TARGET_FMT_lx " => " TARGET_FMT_plx - " %d %d\n", __func__, address, raddr, ctx->prot, ret); - } - - return ret; -} - -void booke206_flush_tlb(CPUPPCState *env, int flags, const int check_iprot) -{ - int tlb_size; - int i, j; - ppcmas_tlb_t *tlb = env->tlb.tlbm; - - for (i = 0; i < BOOKE206_MAX_TLBN; i++) { - if (flags & (1 << i)) { - tlb_size = booke206_tlb_size(env, i); - for (j = 0; j < tlb_size; j++) { - if (!check_iprot || !(tlb[j].mas1 & MAS1_IPROT)) { - tlb[j].mas1 &= ~MAS1_VALID; - } - } - } - tlb += booke206_tlb_size(env, i); - } - - tlb_flush(env, 1); -} - -target_phys_addr_t booke206_tlb_to_page_size(CPUPPCState *env, ppcmas_tlb_t *tlb) -{ - int tlbm_size; - - tlbm_size = (tlb->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; - - return 1024ULL << tlbm_size; -} - -/* TLB check function for MAS based SoftTLBs */ -int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, - target_phys_addr_t *raddrp, - target_ulong address, uint32_t pid) -{ - target_ulong mask; - uint32_t tlb_pid; - - /* Check valid flag */ - if (!(tlb->mas1 & MAS1_VALID)) { - return -1; - } - - mask = ~(booke206_tlb_to_page_size(env, tlb) - 1); - LOG_SWTLB("%s: TLB ADDR=0x" TARGET_FMT_lx " PID=0x%x MAS1=0x%x MAS2=0x%" - PRIx64 " mask=0x" TARGET_FMT_lx " MAS7_3=0x%" PRIx64 " MAS8=%x\n", - __func__, address, pid, tlb->mas1, tlb->mas2, mask, tlb->mas7_3, - tlb->mas8); - - /* Check PID */ - tlb_pid = (tlb->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT; - if (tlb_pid != 0 && tlb_pid != pid) { - return -1; - } - - /* Check effective address */ - if ((address & mask) != (tlb->mas2 & MAS2_EPN_MASK)) { - return -1; - } - - if (raddrp) { - *raddrp = (tlb->mas7_3 & mask) | (address & ~mask); - } - - return 0; -} - -static int mmubooke206_check_tlb(CPUPPCState *env, ppcmas_tlb_t *tlb, - target_phys_addr_t *raddr, int *prot, - target_ulong address, int rw, - int access_type) -{ - int ret; - int _prot = 0; - - if (ppcmas_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID]) >= 0) { - goto found_tlb; - } - - if (env->spr[SPR_BOOKE_PID1] && - ppcmas_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID1]) >= 0) { - goto found_tlb; - } - - if (env->spr[SPR_BOOKE_PID2] && - ppcmas_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID2]) >= 0) { - goto found_tlb; - } - - LOG_SWTLB("%s: TLB entry not found\n", __func__); - return -1; - -found_tlb: - - if (msr_pr != 0) { - if (tlb->mas7_3 & MAS3_UR) { - _prot |= PAGE_READ; - } - if (tlb->mas7_3 & MAS3_UW) { - _prot |= PAGE_WRITE; - } - if (tlb->mas7_3 & MAS3_UX) { - _prot |= PAGE_EXEC; - } - } else { - if (tlb->mas7_3 & MAS3_SR) { - _prot |= PAGE_READ; - } - if (tlb->mas7_3 & MAS3_SW) { - _prot |= PAGE_WRITE; - } - if (tlb->mas7_3 & MAS3_SX) { - _prot |= PAGE_EXEC; - } - } - - /* Check the address space and permissions */ - if (access_type == ACCESS_CODE) { - if (msr_ir != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { - LOG_SWTLB("%s: AS doesn't match\n", __func__); - return -1; - } - - *prot = _prot; - if (_prot & PAGE_EXEC) { - LOG_SWTLB("%s: good TLB!\n", __func__); - return 0; - } - - LOG_SWTLB("%s: no PAGE_EXEC: %x\n", __func__, _prot); - ret = -3; - } else { - if (msr_dr != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { - LOG_SWTLB("%s: AS doesn't match\n", __func__); - return -1; - } - - *prot = _prot; - if ((!rw && _prot & PAGE_READ) || (rw && (_prot & PAGE_WRITE))) { - LOG_SWTLB("%s: found TLB!\n", __func__); - return 0; - } - - LOG_SWTLB("%s: PAGE_READ/WRITE doesn't match: %x\n", __func__, _prot); - ret = -2; - } - - return ret; -} - -static int mmubooke206_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong address, int rw, - int access_type) -{ - ppcmas_tlb_t *tlb; - target_phys_addr_t raddr; - int i, j, ret; - - ret = -1; - raddr = (target_phys_addr_t)-1ULL; - - for (i = 0; i < BOOKE206_MAX_TLBN; i++) { - int ways = booke206_tlb_ways(env, i); - - for (j = 0; j < ways; j++) { - tlb = booke206_get_tlbm(env, i, address, j); - if (!tlb) { - continue; - } - ret = mmubooke206_check_tlb(env, tlb, &raddr, &ctx->prot, address, - rw, access_type); - if (ret != -1) { - goto found_tlb; - } - } - } - -found_tlb: - - if (ret >= 0) { - ctx->raddr = raddr; - LOG_SWTLB("%s: access granted " TARGET_FMT_lx " => " TARGET_FMT_plx - " %d %d\n", __func__, address, ctx->raddr, ctx->prot, - ret); - } else { - LOG_SWTLB("%s: access refused " TARGET_FMT_lx " => " TARGET_FMT_plx - " %d %d\n", __func__, address, raddr, ctx->prot, ret); - } - - return ret; -} - -static const char *book3e_tsize_to_str[32] = { - "1K", "2K", "4K", "8K", "16K", "32K", "64K", "128K", "256K", "512K", - "1M", "2M", "4M", "8M", "16M", "32M", "64M", "128M", "256M", "512M", - "1G", "2G", "4G", "8G", "16G", "32G", "64G", "128G", "256G", "512G", - "1T", "2T" -}; - -static void mmubooke_dump_mmu(FILE *f, fprintf_function cpu_fprintf, - CPUPPCState *env) -{ - ppcemb_tlb_t *entry; - int i; - - if (kvm_enabled() && !env->kvm_sw_tlb) { - cpu_fprintf(f, "Cannot access KVM TLB\n"); - return; - } - - cpu_fprintf(f, "\nTLB:\n"); - cpu_fprintf(f, "Effective Physical Size PID Prot " - "Attr\n"); - - entry = &env->tlb.tlbe[0]; - for (i = 0; i < env->nb_tlb; i++, entry++) { - target_phys_addr_t ea, pa; - target_ulong mask; - uint64_t size = (uint64_t)entry->size; - char size_buf[20]; - - /* Check valid flag */ - if (!(entry->prot & PAGE_VALID)) { - continue; - } - - mask = ~(entry->size - 1); - ea = entry->EPN & mask; - pa = entry->RPN & mask; -#if (TARGET_PHYS_ADDR_BITS >= 36) - /* Extend the physical address to 36 bits */ - pa |= (target_phys_addr_t)(entry->RPN & 0xF) << 32; -#endif - size /= 1024; - if (size >= 1024) { - snprintf(size_buf, sizeof(size_buf), "%3" PRId64 "M", size / 1024); - } else { - snprintf(size_buf, sizeof(size_buf), "%3" PRId64 "k", size); - } - cpu_fprintf(f, "0x%016" PRIx64 " 0x%016" PRIx64 " %s %-5u %08x %08x\n", - (uint64_t)ea, (uint64_t)pa, size_buf, (uint32_t)entry->PID, - entry->prot, entry->attr); - } - -} - -static void mmubooke206_dump_one_tlb(FILE *f, fprintf_function cpu_fprintf, - CPUPPCState *env, int tlbn, int offset, - int tlbsize) -{ - ppcmas_tlb_t *entry; - int i; - - cpu_fprintf(f, "\nTLB%d:\n", tlbn); - cpu_fprintf(f, "Effective Physical Size TID TS SRWX URWX WIMGE U0123\n"); - - entry = &env->tlb.tlbm[offset]; - for (i = 0; i < tlbsize; i++, entry++) { - target_phys_addr_t ea, pa, size; - int tsize; - - if (!(entry->mas1 & MAS1_VALID)) { - continue; - } - - tsize = (entry->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; - size = 1024ULL << tsize; - ea = entry->mas2 & ~(size - 1); - pa = entry->mas7_3 & ~(size - 1); - - cpu_fprintf(f, "0x%016" PRIx64 " 0x%016" PRIx64 " %4s %-5u %1u S%c%c%c U%c%c%c %c%c%c%c%c U%c%c%c%c\n", - (uint64_t)ea, (uint64_t)pa, - book3e_tsize_to_str[tsize], - (entry->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT, - (entry->mas1 & MAS1_TS) >> MAS1_TS_SHIFT, - entry->mas7_3 & MAS3_SR ? 'R' : '-', - entry->mas7_3 & MAS3_SW ? 'W' : '-', - entry->mas7_3 & MAS3_SX ? 'X' : '-', - entry->mas7_3 & MAS3_UR ? 'R' : '-', - entry->mas7_3 & MAS3_UW ? 'W' : '-', - entry->mas7_3 & MAS3_UX ? 'X' : '-', - entry->mas2 & MAS2_W ? 'W' : '-', - entry->mas2 & MAS2_I ? 'I' : '-', - entry->mas2 & MAS2_M ? 'M' : '-', - entry->mas2 & MAS2_G ? 'G' : '-', - entry->mas2 & MAS2_E ? 'E' : '-', - entry->mas7_3 & MAS3_U0 ? '0' : '-', - entry->mas7_3 & MAS3_U1 ? '1' : '-', - entry->mas7_3 & MAS3_U2 ? '2' : '-', - entry->mas7_3 & MAS3_U3 ? '3' : '-'); - } -} - -static void mmubooke206_dump_mmu(FILE *f, fprintf_function cpu_fprintf, - CPUPPCState *env) -{ - int offset = 0; - int i; - - if (kvm_enabled() && !env->kvm_sw_tlb) { - cpu_fprintf(f, "Cannot access KVM TLB\n"); - return; - } - - for (i = 0; i < BOOKE206_MAX_TLBN; i++) { - int size = booke206_tlb_size(env, i); - - if (size == 0) { - continue; - } - - mmubooke206_dump_one_tlb(f, cpu_fprintf, env, i, offset, size); - offset += size; - } -} - -#if defined(TARGET_PPC64) -static void mmubooks_dump_mmu(FILE *f, fprintf_function cpu_fprintf, - CPUPPCState *env) -{ - int i; - uint64_t slbe, slbv; - - cpu_synchronize_state(env); - - cpu_fprintf(f, "SLB\tESID\t\t\tVSID\n"); - for (i = 0; i < env->slb_nr; i++) { - slbe = env->slb[i].esid; - slbv = env->slb[i].vsid; - if (slbe == 0 && slbv == 0) { - continue; - } - cpu_fprintf(f, "%d\t0x%016" PRIx64 "\t0x%016" PRIx64 "\n", - i, slbe, slbv); - } -} -#endif - -void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env) -{ - switch (env->mmu_model) { - case POWERPC_MMU_BOOKE: - mmubooke_dump_mmu(f, cpu_fprintf, env); - break; - case POWERPC_MMU_BOOKE206: - mmubooke206_dump_mmu(f, cpu_fprintf, env); - break; -#if defined(TARGET_PPC64) - case POWERPC_MMU_64B: - case POWERPC_MMU_2_06: - mmubooks_dump_mmu(f, cpu_fprintf, env); - break; -#endif - default: - qemu_log_mask(LOG_UNIMP, "%s: unimplemented\n", __func__); - } -} - -static inline int check_physical(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, int rw) -{ - int in_plb, ret; - - ctx->raddr = eaddr; - ctx->prot = PAGE_READ | PAGE_EXEC; - ret = 0; - switch (env->mmu_model) { - case POWERPC_MMU_32B: - case POWERPC_MMU_601: - case POWERPC_MMU_SOFT_6xx: - case POWERPC_MMU_SOFT_74xx: - case POWERPC_MMU_SOFT_4xx: - case POWERPC_MMU_REAL: - case POWERPC_MMU_BOOKE: - ctx->prot |= PAGE_WRITE; - break; -#if defined(TARGET_PPC64) - case POWERPC_MMU_620: - case POWERPC_MMU_64B: - case POWERPC_MMU_2_06: - /* Real address are 60 bits long */ - ctx->raddr &= 0x0FFFFFFFFFFFFFFFULL; - ctx->prot |= PAGE_WRITE; - break; -#endif - case POWERPC_MMU_SOFT_4xx_Z: - if (unlikely(msr_pe != 0)) { - /* 403 family add some particular protections, - * using PBL/PBU registers for accesses with no translation. - */ - in_plb = - /* Check PLB validity */ - (env->pb[0] < env->pb[1] && - /* and address in plb area */ - eaddr >= env->pb[0] && eaddr < env->pb[1]) || - (env->pb[2] < env->pb[3] && - eaddr >= env->pb[2] && eaddr < env->pb[3]) ? 1 : 0; - if (in_plb ^ msr_px) { - /* Access in protected area */ - if (rw == 1) { - /* Access is not allowed */ - ret = -2; - } - } else { - /* Read-write access is allowed */ - ctx->prot |= PAGE_WRITE; - } - } - break; - case POWERPC_MMU_MPC8xx: - /* XXX: TODO */ - cpu_abort(env, "MPC8xx MMU model is not implemented\n"); - break; - case POWERPC_MMU_BOOKE206: - cpu_abort(env, "BookE 2.06 MMU doesn't have physical real mode\n"); - break; - default: - cpu_abort(env, "Unknown or invalid MMU model\n"); - return -1; - } - - return ret; -} - -int get_physical_address (CPUPPCState *env, mmu_ctx_t *ctx, target_ulong eaddr, - int rw, int access_type) -{ - int ret; - -#if 0 - qemu_log("%s\n", __func__); -#endif - if ((access_type == ACCESS_CODE && msr_ir == 0) || - (access_type != ACCESS_CODE && msr_dr == 0)) { - if (env->mmu_model == POWERPC_MMU_BOOKE) { - /* The BookE MMU always performs address translation. The - IS and DS bits only affect the address space. */ - ret = mmubooke_get_physical_address(env, ctx, eaddr, - rw, access_type); - } else if (env->mmu_model == POWERPC_MMU_BOOKE206) { - ret = mmubooke206_get_physical_address(env, ctx, eaddr, rw, - access_type); - } else { - /* No address translation. */ - ret = check_physical(env, ctx, eaddr, rw); - } - } else { - ret = -1; - switch (env->mmu_model) { - case POWERPC_MMU_32B: - case POWERPC_MMU_601: - case POWERPC_MMU_SOFT_6xx: - case POWERPC_MMU_SOFT_74xx: - /* Try to find a BAT */ - if (env->nb_BATs != 0) - ret = get_bat(env, ctx, eaddr, rw, access_type); -#if defined(TARGET_PPC64) - case POWERPC_MMU_620: - case POWERPC_MMU_64B: - case POWERPC_MMU_2_06: -#endif - if (ret < 0) { - /* We didn't match any BAT entry or don't have BATs */ - ret = get_segment(env, ctx, eaddr, rw, access_type); - } - break; - case POWERPC_MMU_SOFT_4xx: - case POWERPC_MMU_SOFT_4xx_Z: - ret = mmu40x_get_physical_address(env, ctx, eaddr, - rw, access_type); - break; - case POWERPC_MMU_BOOKE: - ret = mmubooke_get_physical_address(env, ctx, eaddr, - rw, access_type); - break; - case POWERPC_MMU_BOOKE206: - ret = mmubooke206_get_physical_address(env, ctx, eaddr, rw, - access_type); - break; - case POWERPC_MMU_MPC8xx: - /* XXX: TODO */ - cpu_abort(env, "MPC8xx MMU model is not implemented\n"); - break; - case POWERPC_MMU_REAL: - cpu_abort(env, "PowerPC in real mode do not do any translation\n"); - return -1; - default: - cpu_abort(env, "Unknown or invalid MMU model\n"); - return -1; - } - } -#if 0 - qemu_log("%s address " TARGET_FMT_lx " => %d " TARGET_FMT_plx "\n", - __func__, eaddr, ret, ctx->raddr); -#endif - - return ret; -} - -target_phys_addr_t cpu_get_phys_page_debug (CPUPPCState *env, target_ulong addr) -{ - mmu_ctx_t ctx; - - if (unlikely(get_physical_address(env, &ctx, addr, 0, ACCESS_INT) != 0)) - return -1; - - return ctx.raddr & TARGET_PAGE_MASK; -} - -static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, - int rw) -{ - env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK; - env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK; - env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK; - env->spr[SPR_BOOKE_MAS3] = 0; - env->spr[SPR_BOOKE_MAS6] = 0; - env->spr[SPR_BOOKE_MAS7] = 0; - - /* AS */ - if (((rw == 2) && msr_ir) || ((rw != 2) && msr_dr)) { - env->spr[SPR_BOOKE_MAS1] |= MAS1_TS; - env->spr[SPR_BOOKE_MAS6] |= MAS6_SAS; - } - - env->spr[SPR_BOOKE_MAS1] |= MAS1_VALID; - env->spr[SPR_BOOKE_MAS2] |= address & MAS2_EPN_MASK; - - switch (env->spr[SPR_BOOKE_MAS4] & MAS4_TIDSELD_PIDZ) { - case MAS4_TIDSELD_PID0: - env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID] << MAS1_TID_SHIFT; - break; - case MAS4_TIDSELD_PID1: - env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID1] << MAS1_TID_SHIFT; - break; - case MAS4_TIDSELD_PID2: - env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID2] << MAS1_TID_SHIFT; - break; - } - - env->spr[SPR_BOOKE_MAS6] |= env->spr[SPR_BOOKE_PID] << 16; - - /* next victim logic */ - env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT; - env->last_way++; - env->last_way &= booke206_tlb_ways(env, 0) - 1; - env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT; -} - -/* Perform address translation */ -int cpu_ppc_handle_mmu_fault (CPUPPCState *env, target_ulong address, int rw, - int mmu_idx) -{ - mmu_ctx_t ctx; - int access_type; - int ret = 0; - - if (rw == 2) { - /* code access */ - rw = 0; - access_type = ACCESS_CODE; - } else { - /* data access */ - access_type = env->access_type; - } - ret = get_physical_address(env, &ctx, address, rw, access_type); - if (ret == 0) { - tlb_set_page(env, address & TARGET_PAGE_MASK, - ctx.raddr & TARGET_PAGE_MASK, ctx.prot, - mmu_idx, TARGET_PAGE_SIZE); - ret = 0; - } else if (ret < 0) { - LOG_MMU_STATE(env); - if (access_type == ACCESS_CODE) { - switch (ret) { - case -1: - /* No matches in page tables or TLB */ - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_6xx: - env->exception_index = POWERPC_EXCP_IFTLB; - env->error_code = 1 << 18; - env->spr[SPR_IMISS] = address; - env->spr[SPR_ICMP] = 0x80000000 | ctx.ptem; - goto tlb_miss; - case POWERPC_MMU_SOFT_74xx: - env->exception_index = POWERPC_EXCP_IFTLB; - goto tlb_miss_74xx; - case POWERPC_MMU_SOFT_4xx: - case POWERPC_MMU_SOFT_4xx_Z: - env->exception_index = POWERPC_EXCP_ITLB; - env->error_code = 0; - env->spr[SPR_40x_DEAR] = address; - env->spr[SPR_40x_ESR] = 0x00000000; - break; - case POWERPC_MMU_32B: - case POWERPC_MMU_601: -#if defined(TARGET_PPC64) - case POWERPC_MMU_620: - case POWERPC_MMU_64B: - case POWERPC_MMU_2_06: -#endif - env->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0x40000000; - break; - case POWERPC_MMU_BOOKE206: - booke206_update_mas_tlb_miss(env, address, rw); - /* fall through */ - case POWERPC_MMU_BOOKE: - env->exception_index = POWERPC_EXCP_ITLB; - env->error_code = 0; - env->spr[SPR_BOOKE_DEAR] = address; - return -1; - case POWERPC_MMU_MPC8xx: - /* XXX: TODO */ - cpu_abort(env, "MPC8xx MMU model is not implemented\n"); - break; - case POWERPC_MMU_REAL: - cpu_abort(env, "PowerPC in real mode should never raise " - "any MMU exceptions\n"); - return -1; - default: - cpu_abort(env, "Unknown or invalid MMU model\n"); - return -1; - } - break; - case -2: - /* Access rights violation */ - env->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0x08000000; - break; - case -3: - /* No execute protection violation */ - if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - env->spr[SPR_BOOKE_ESR] = 0x00000000; - } - env->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0x10000000; - break; - case -4: - /* Direct store exception */ - /* No code fetch is allowed in direct-store areas */ - env->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0x10000000; - break; -#if defined(TARGET_PPC64) - case -5: - /* No match in segment table */ - if (env->mmu_model == POWERPC_MMU_620) { - env->exception_index = POWERPC_EXCP_ISI; - /* XXX: this might be incorrect */ - env->error_code = 0x40000000; - } else { - env->exception_index = POWERPC_EXCP_ISEG; - env->error_code = 0; - } - break; -#endif - } - } else { - switch (ret) { - case -1: - /* No matches in page tables or TLB */ - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_6xx: - if (rw == 1) { - env->exception_index = POWERPC_EXCP_DSTLB; - env->error_code = 1 << 16; - } else { - env->exception_index = POWERPC_EXCP_DLTLB; - env->error_code = 0; - } - env->spr[SPR_DMISS] = address; - env->spr[SPR_DCMP] = 0x80000000 | ctx.ptem; - tlb_miss: - env->error_code |= ctx.key << 19; - env->spr[SPR_HASH1] = env->htab_base + - get_pteg_offset(env, ctx.hash[0], HASH_PTE_SIZE_32); - env->spr[SPR_HASH2] = env->htab_base + - get_pteg_offset(env, ctx.hash[1], HASH_PTE_SIZE_32); - break; - case POWERPC_MMU_SOFT_74xx: - if (rw == 1) { - env->exception_index = POWERPC_EXCP_DSTLB; - } else { - env->exception_index = POWERPC_EXCP_DLTLB; - } - tlb_miss_74xx: - /* Implement LRU algorithm */ - env->error_code = ctx.key << 19; - env->spr[SPR_TLBMISS] = (address & ~((target_ulong)0x3)) | - ((env->last_way + 1) & (env->nb_ways - 1)); - env->spr[SPR_PTEHI] = 0x80000000 | ctx.ptem; - break; - case POWERPC_MMU_SOFT_4xx: - case POWERPC_MMU_SOFT_4xx_Z: - env->exception_index = POWERPC_EXCP_DTLB; - env->error_code = 0; - env->spr[SPR_40x_DEAR] = address; - if (rw) - env->spr[SPR_40x_ESR] = 0x00800000; - else - env->spr[SPR_40x_ESR] = 0x00000000; - break; - case POWERPC_MMU_32B: - case POWERPC_MMU_601: -#if defined(TARGET_PPC64) - case POWERPC_MMU_620: - case POWERPC_MMU_64B: - case POWERPC_MMU_2_06: -#endif - env->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - env->spr[SPR_DAR] = address; - if (rw == 1) - env->spr[SPR_DSISR] = 0x42000000; - else - env->spr[SPR_DSISR] = 0x40000000; - break; - case POWERPC_MMU_MPC8xx: - /* XXX: TODO */ - cpu_abort(env, "MPC8xx MMU model is not implemented\n"); - break; - case POWERPC_MMU_BOOKE206: - booke206_update_mas_tlb_miss(env, address, rw); - /* fall through */ - case POWERPC_MMU_BOOKE: - env->exception_index = POWERPC_EXCP_DTLB; - env->error_code = 0; - env->spr[SPR_BOOKE_DEAR] = address; - env->spr[SPR_BOOKE_ESR] = rw ? ESR_ST : 0; - return -1; - case POWERPC_MMU_REAL: - cpu_abort(env, "PowerPC in real mode should never raise " - "any MMU exceptions\n"); - return -1; - default: - cpu_abort(env, "Unknown or invalid MMU model\n"); - return -1; - } - break; - case -2: - /* Access rights violation */ - env->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - if (env->mmu_model == POWERPC_MMU_SOFT_4xx - || env->mmu_model == POWERPC_MMU_SOFT_4xx_Z) { - env->spr[SPR_40x_DEAR] = address; - if (rw) { - env->spr[SPR_40x_ESR] |= 0x00800000; - } - } else if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - env->spr[SPR_BOOKE_DEAR] = address; - env->spr[SPR_BOOKE_ESR] = rw ? ESR_ST : 0; - } else { - env->spr[SPR_DAR] = address; - if (rw == 1) { - env->spr[SPR_DSISR] = 0x0A000000; - } else { - env->spr[SPR_DSISR] = 0x08000000; - } - } - break; - case -4: - /* Direct store exception */ - switch (access_type) { - case ACCESS_FLOAT: - /* Floating point load/store */ - env->exception_index = POWERPC_EXCP_ALIGN; - env->error_code = POWERPC_EXCP_ALIGN_FP; - env->spr[SPR_DAR] = address; - break; - case ACCESS_RES: - /* lwarx, ldarx or stwcx. */ - env->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - env->spr[SPR_DAR] = address; - if (rw == 1) - env->spr[SPR_DSISR] = 0x06000000; - else - env->spr[SPR_DSISR] = 0x04000000; - break; - case ACCESS_EXT: - /* eciwx or ecowx */ - env->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - env->spr[SPR_DAR] = address; - if (rw == 1) - env->spr[SPR_DSISR] = 0x06100000; - else - env->spr[SPR_DSISR] = 0x04100000; - break; - default: - printf("DSI: invalid exception (%d)\n", ret); - env->exception_index = POWERPC_EXCP_PROGRAM; - env->error_code = - POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL; - env->spr[SPR_DAR] = address; - break; - } - break; -#if defined(TARGET_PPC64) - case -5: - /* No match in segment table */ - if (env->mmu_model == POWERPC_MMU_620) { - env->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - env->spr[SPR_DAR] = address; - /* XXX: this might be incorrect */ - if (rw == 1) - env->spr[SPR_DSISR] = 0x42000000; - else - env->spr[SPR_DSISR] = 0x40000000; - } else { - env->exception_index = POWERPC_EXCP_DSEG; - env->error_code = 0; - env->spr[SPR_DAR] = address; - } - break; -#endif - } - } -#if 0 - printf("%s: set exception to %d %02x\n", __func__, - env->exception, env->error_code); -#endif - ret = 1; - } - - return ret; -} - -/*****************************************************************************/ -/* 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; - LOG_BATS("Flush BAT from " TARGET_FMT_lx " to " TARGET_FMT_lx " (" - TARGET_FMT_lx ")\n", base, end, mask); - for (page = base; page != end; page += TARGET_PAGE_SIZE) - tlb_flush_page(env, page); - LOG_BATS("Flush done\n"); -} -#endif - -static inline void dump_store_bat(CPUPPCState *env, char ID, int ul, int nr, - target_ulong value) -{ - LOG_BATS("Set %cBAT%d%c to " TARGET_FMT_lx " (" TARGET_FMT_lx ")\n", ID, - nr, ul == 0 ? 'u' : 'l', value, env->nip); -} - -void ppc_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); -#else - tlb_flush(env, 1); -#endif - } -} - -void ppc_store_ibatl (CPUPPCState *env, int nr, target_ulong value) -{ - dump_store_bat(env, 'I', 1, nr, value); - env->IBAT[1][nr] = value; -} - -void ppc_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 ppc_store_dbatl (CPUPPCState *env, int nr, target_ulong value) -{ - dump_store_bat(env, 'D', 1, nr, value); - env->DBAT[1][nr] = value; -} - -void ppc_store_ibatu_601 (CPUPPCState *env, int nr, target_ulong value) -{ - target_ulong mask; -#if defined(FLUSH_ALL_TLBS) - int do_inval; -#endif - - dump_store_bat(env, 'I', 0, nr, value); - if (env->IBAT[0][nr] != value) { -#if defined(FLUSH_ALL_TLBS) - do_inval = 0; -#endif - mask = (env->IBAT[1][nr] << 17) & 0x0FFE0000UL; - if (env->IBAT[1][nr] & 0x40) { - /* Invalidate BAT only if it is valid */ -#if !defined(FLUSH_ALL_TLBS) - do_invalidate_BAT(env, env->IBAT[0][nr], mask); -#else - do_inval = 1; -#endif - } - /* When storing valid upper BAT, mask BEPI and BRPN - * and invalidate all TLBs covered by this BAT - */ - env->IBAT[0][nr] = (value & 0x00001FFFUL) | - (value & ~0x0001FFFFUL & ~mask); - env->DBAT[0][nr] = env->IBAT[0][nr]; - if (env->IBAT[1][nr] & 0x40) { -#if !defined(FLUSH_ALL_TLBS) - do_invalidate_BAT(env, env->IBAT[0][nr], mask); -#else - do_inval = 1; -#endif - } -#if defined(FLUSH_ALL_TLBS) - if (do_inval) - tlb_flush(env, 1); -#endif - } -} - -void ppc_store_ibatl_601 (CPUPPCState *env, int nr, target_ulong value) -{ - target_ulong mask; -#if defined(FLUSH_ALL_TLBS) - int do_inval; -#endif - - dump_store_bat(env, 'I', 1, nr, value); - if (env->IBAT[1][nr] != value) { -#if defined(FLUSH_ALL_TLBS) - do_inval = 0; -#endif - if (env->IBAT[1][nr] & 0x40) { -#if !defined(FLUSH_ALL_TLBS) - mask = (env->IBAT[1][nr] << 17) & 0x0FFE0000UL; - do_invalidate_BAT(env, env->IBAT[0][nr], mask); -#else - do_inval = 1; -#endif - } - if (value & 0x40) { -#if !defined(FLUSH_ALL_TLBS) - mask = (value << 17) & 0x0FFE0000UL; - do_invalidate_BAT(env, env->IBAT[0][nr], mask); -#else - do_inval = 1; -#endif - } - env->IBAT[1][nr] = value; - env->DBAT[1][nr] = value; -#if defined(FLUSH_ALL_TLBS) - if (do_inval) - tlb_flush(env, 1); -#endif - } -} - -/*****************************************************************************/ -/* TLB management */ -void ppc_tlb_invalidate_all (CPUPPCState *env) -{ - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_6xx: - case POWERPC_MMU_SOFT_74xx: - ppc6xx_tlb_invalidate_all(env); - break; - case POWERPC_MMU_SOFT_4xx: - case POWERPC_MMU_SOFT_4xx_Z: - ppc4xx_tlb_invalidate_all(env); - break; - case POWERPC_MMU_REAL: - cpu_abort(env, "No TLB for PowerPC 4xx in real mode\n"); - break; - case POWERPC_MMU_MPC8xx: - /* XXX: TODO */ - cpu_abort(env, "MPC8xx MMU model is not implemented\n"); - break; - case POWERPC_MMU_BOOKE: - tlb_flush(env, 1); - break; - case POWERPC_MMU_BOOKE206: - booke206_flush_tlb(env, -1, 0); - break; - case POWERPC_MMU_32B: - case POWERPC_MMU_601: -#if defined(TARGET_PPC64) - case POWERPC_MMU_620: - case POWERPC_MMU_64B: - case POWERPC_MMU_2_06: -#endif /* defined(TARGET_PPC64) */ - tlb_flush(env, 1); - break; - default: - /* XXX: TODO */ - cpu_abort(env, "Unknown MMU model\n"); - break; - } -} - -void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr) -{ -#if !defined(FLUSH_ALL_TLBS) - addr &= TARGET_PAGE_MASK; - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_6xx: - case POWERPC_MMU_SOFT_74xx: - ppc6xx_tlb_invalidate_virt(env, addr, 0); - if (env->id_tlbs == 1) - ppc6xx_tlb_invalidate_virt(env, addr, 1); - break; - case POWERPC_MMU_SOFT_4xx: - case POWERPC_MMU_SOFT_4xx_Z: - ppc4xx_tlb_invalidate_virt(env, addr, env->spr[SPR_40x_PID]); - break; - case POWERPC_MMU_REAL: - cpu_abort(env, "No TLB for PowerPC 4xx in real mode\n"); - break; - case POWERPC_MMU_MPC8xx: - /* XXX: TODO */ - cpu_abort(env, "MPC8xx MMU model is not implemented\n"); - break; - case POWERPC_MMU_BOOKE: - /* XXX: TODO */ - cpu_abort(env, "BookE MMU model is not implemented\n"); - break; - case POWERPC_MMU_BOOKE206: - /* XXX: TODO */ - cpu_abort(env, "BookE 2.06 MMU model is not implemented\n"); - break; - case POWERPC_MMU_32B: - case POWERPC_MMU_601: - /* tlbie invalidate TLBs for all segments */ - addr &= ~((target_ulong)-1ULL << 28); - /* XXX: this case should be optimized, - * giving a mask to tlb_flush_page - */ - tlb_flush_page(env, addr | (0x0 << 28)); - tlb_flush_page(env, addr | (0x1 << 28)); - tlb_flush_page(env, addr | (0x2 << 28)); - tlb_flush_page(env, addr | (0x3 << 28)); - tlb_flush_page(env, addr | (0x4 << 28)); - tlb_flush_page(env, addr | (0x5 << 28)); - tlb_flush_page(env, addr | (0x6 << 28)); - tlb_flush_page(env, addr | (0x7 << 28)); - tlb_flush_page(env, addr | (0x8 << 28)); - tlb_flush_page(env, addr | (0x9 << 28)); - tlb_flush_page(env, addr | (0xA << 28)); - tlb_flush_page(env, addr | (0xB << 28)); - tlb_flush_page(env, addr | (0xC << 28)); - tlb_flush_page(env, addr | (0xD << 28)); - tlb_flush_page(env, addr | (0xE << 28)); - tlb_flush_page(env, addr | (0xF << 28)); - break; -#if defined(TARGET_PPC64) - case POWERPC_MMU_620: - case POWERPC_MMU_64B: - case POWERPC_MMU_2_06: - /* tlbie invalidate TLBs for all segments */ - /* XXX: given the fact that there are too many segments to invalidate, - * and we still don't have a tlb_flush_mask(env, n, mask) in QEMU, - * we just invalidate all TLBs - */ - tlb_flush(env, 1); - break; -#endif /* defined(TARGET_PPC64) */ - default: - /* XXX: TODO */ - cpu_abort(env, "Unknown MMU model\n"); - break; - } -#else - ppc_tlb_invalidate_all(env); -#endif -} - -/*****************************************************************************/ -/* Special registers manipulation */ -#if defined(TARGET_PPC64) -void ppc_store_asr (CPUPPCState *env, target_ulong value) -{ - if (env->asr != value) { - env->asr = value; - tlb_flush(env, 1); - } -} -#endif - -void ppc_store_sdr1 (CPUPPCState *env, target_ulong value) -{ - LOG_MMU("%s: " TARGET_FMT_lx "\n", __func__, value); - if (env->spr[SPR_SDR1] != value) { - env->spr[SPR_SDR1] = value; -#if defined(TARGET_PPC64) - if (env->mmu_model & POWERPC_MMU_64) { - target_ulong htabsize = value & SDR_64_HTABSIZE; - - if (htabsize > 28) { - fprintf(stderr, "Invalid HTABSIZE 0x" TARGET_FMT_lx - " stored in SDR1\n", htabsize); - htabsize = 28; - } - env->htab_mask = (1ULL << (htabsize + 18)) - 1; - env->htab_base = value & SDR_64_HTABORG; - } else -#endif /* defined(TARGET_PPC64) */ - { - /* FIXME: Should check for valid HTABMASK values */ - env->htab_mask = ((value & SDR_32_HTABMASK) << 16) | 0xFFFF; - env->htab_base = value & SDR_32_HTABORG; - } - tlb_flush(env, 1); - } -} - -#if defined(TARGET_PPC64) -target_ulong ppc_load_sr (CPUPPCState *env, int slb_nr) -{ - // XXX - return 0; -} -#endif - -void ppc_store_sr (CPUPPCState *env, int srnum, target_ulong value) -{ - LOG_MMU("%s: reg=%d " TARGET_FMT_lx " " TARGET_FMT_lx "\n", __func__, - srnum, value, env->sr[srnum]); -#if defined(TARGET_PPC64) - if (env->mmu_model & POWERPC_MMU_64) { - uint64_t rb = 0, rs = 0; - - /* ESID = srnum */ - rb |= ((uint32_t)srnum & 0xf) << 28; - /* Set the valid bit */ - rb |= 1 << 27; - /* Index = ESID */ - rb |= (uint32_t)srnum; - - /* VSID = VSID */ - rs |= (value & 0xfffffff) << 12; - /* flags = flags */ - rs |= ((value >> 27) & 0xf) << 8; - - ppc_store_slb(env, rb, rs); - } else -#endif - if (env->sr[srnum] != value) { - env->sr[srnum] = value; -/* Invalidating 256MB of virtual memory in 4kB pages is way longer than - flusing the whole TLB. */ -#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 - tlb_flush(env, 1); -#endif - } -} -#endif /* !defined (CONFIG_USER_ONLY) */ - -/* GDBstub can read and write MSR... */ -void ppc_store_msr (CPUPPCState *env, target_ulong value) -{ - hreg_store_msr(env, value, 0); -} - -/*****************************************************************************/ -/* Exception processing */ -#if defined (CONFIG_USER_ONLY) -void do_interrupt (CPUPPCState *env) -{ - env->exception_index = POWERPC_EXCP_NONE; - env->error_code = 0; -} - -void ppc_hw_interrupt (CPUPPCState *env) -{ - env->exception_index = POWERPC_EXCP_NONE; - env->error_code = 0; -} -#else /* defined (CONFIG_USER_ONLY) */ -static inline void dump_syscall(CPUPPCState *env) -{ - qemu_log_mask(CPU_LOG_INT, "syscall r0=%016" PRIx64 " r3=%016" PRIx64 - " r4=%016" PRIx64 " r5=%016" PRIx64 " r6=%016" PRIx64 - " nip=" TARGET_FMT_lx "\n", - ppc_dump_gpr(env, 0), ppc_dump_gpr(env, 3), - ppc_dump_gpr(env, 4), ppc_dump_gpr(env, 5), - ppc_dump_gpr(env, 6), env->nip); -} - -/* Note that this function should be greatly optimized - * when called with a constant excp, from ppc_hw_interrupt - */ -static inline void powerpc_excp(CPUPPCState *env, int excp_model, int excp) -{ - target_ulong msr, new_msr, vector; - int srr0, srr1, asrr0, asrr1; - int lpes0, lpes1, lev; - - if (0) { - /* XXX: find a suitable condition to enable the hypervisor mode */ - lpes0 = (env->spr[SPR_LPCR] >> 1) & 1; - lpes1 = (env->spr[SPR_LPCR] >> 2) & 1; - } else { - /* Those values ensure we won't enter the hypervisor mode */ - lpes0 = 0; - lpes1 = 1; - } - - qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx - " => %08x (%02x)\n", env->nip, excp, env->error_code); - - /* new srr1 value excluding must-be-zero bits */ - msr = env->msr & ~0x783f0000ULL; - - /* new interrupt handler msr */ - new_msr = env->msr & ((target_ulong)1 << MSR_ME); - - /* target registers */ - srr0 = SPR_SRR0; - srr1 = SPR_SRR1; - asrr0 = -1; - asrr1 = -1; - - switch (excp) { - case POWERPC_EXCP_NONE: - /* Should never happen */ - return; - case POWERPC_EXCP_CRITICAL: /* Critical input */ - switch (excp_model) { - case POWERPC_EXCP_40x: - srr0 = SPR_40x_SRR2; - srr1 = SPR_40x_SRR3; - break; - case POWERPC_EXCP_BOOKE: - srr0 = SPR_BOOKE_CSRR0; - srr1 = SPR_BOOKE_CSRR1; - break; - case POWERPC_EXCP_G2: - break; - default: - goto excp_invalid; - } - goto store_next; - case POWERPC_EXCP_MCHECK: /* Machine check exception */ - if (msr_me == 0) { - /* Machine check exception is not enabled. - * Enter checkstop state. - */ - if (qemu_log_enabled()) { - qemu_log("Machine check while not allowed. " - "Entering checkstop state\n"); - } else { - fprintf(stderr, "Machine check while not allowed. " - "Entering checkstop state\n"); - } - env->halted = 1; - env->interrupt_request |= CPU_INTERRUPT_EXITTB; - } - if (0) { - /* XXX: find a suitable condition to enable the hypervisor mode */ - new_msr |= (target_ulong)MSR_HVB; - } - - /* machine check exceptions don't have ME set */ - new_msr &= ~((target_ulong)1 << MSR_ME); - - /* XXX: should also have something loaded in DAR / DSISR */ - switch (excp_model) { - case POWERPC_EXCP_40x: - srr0 = SPR_40x_SRR2; - srr1 = SPR_40x_SRR3; - break; - case POWERPC_EXCP_BOOKE: - srr0 = SPR_BOOKE_MCSRR0; - srr1 = SPR_BOOKE_MCSRR1; - asrr0 = SPR_BOOKE_CSRR0; - asrr1 = SPR_BOOKE_CSRR1; - break; - default: - break; - } - goto store_next; - case POWERPC_EXCP_DSI: /* Data storage exception */ - LOG_EXCP("DSI exception: DSISR=" TARGET_FMT_lx" DAR=" TARGET_FMT_lx - "\n", env->spr[SPR_DSISR], env->spr[SPR_DAR]); - if (lpes1 == 0) - new_msr |= (target_ulong)MSR_HVB; - goto store_next; - case POWERPC_EXCP_ISI: /* Instruction storage exception */ - LOG_EXCP("ISI exception: msr=" TARGET_FMT_lx ", nip=" TARGET_FMT_lx - "\n", msr, env->nip); - if (lpes1 == 0) - new_msr |= (target_ulong)MSR_HVB; - msr |= env->error_code; - goto store_next; - case POWERPC_EXCP_EXTERNAL: /* External input */ - if (lpes0 == 1) - new_msr |= (target_ulong)MSR_HVB; - goto store_next; - case POWERPC_EXCP_ALIGN: /* Alignment exception */ - if (lpes1 == 0) - new_msr |= (target_ulong)MSR_HVB; - /* XXX: this is false */ - /* Get rS/rD and rA from faulting opcode */ - env->spr[SPR_DSISR] |= (ldl_code((env->nip - 4)) & 0x03FF0000) >> 16; - goto store_current; - case POWERPC_EXCP_PROGRAM: /* Program exception */ - switch (env->error_code & ~0xF) { - case POWERPC_EXCP_FP: - if ((msr_fe0 == 0 && msr_fe1 == 0) || msr_fp == 0) { - LOG_EXCP("Ignore floating point exception\n"); - env->exception_index = POWERPC_EXCP_NONE; - env->error_code = 0; - return; - } - if (lpes1 == 0) - new_msr |= (target_ulong)MSR_HVB; - msr |= 0x00100000; - if (msr_fe0 == msr_fe1) - goto store_next; - msr |= 0x00010000; - break; - case POWERPC_EXCP_INVAL: - LOG_EXCP("Invalid instruction at " TARGET_FMT_lx "\n", env->nip); - if (lpes1 == 0) - new_msr |= (target_ulong)MSR_HVB; - msr |= 0x00080000; - env->spr[SPR_BOOKE_ESR] = ESR_PIL; - break; - case POWERPC_EXCP_PRIV: - if (lpes1 == 0) - new_msr |= (target_ulong)MSR_HVB; - msr |= 0x00040000; - env->spr[SPR_BOOKE_ESR] = ESR_PPR; - break; - case POWERPC_EXCP_TRAP: - if (lpes1 == 0) - new_msr |= (target_ulong)MSR_HVB; - msr |= 0x00020000; - env->spr[SPR_BOOKE_ESR] = ESR_PTR; - break; - default: - /* Should never occur */ - cpu_abort(env, "Invalid program exception %d. Aborting\n", - env->error_code); - break; - } - goto store_current; - case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ - if (lpes1 == 0) - new_msr |= (target_ulong)MSR_HVB; - goto store_current; - case POWERPC_EXCP_SYSCALL: /* System call exception */ - dump_syscall(env); - lev = env->error_code; - if ((lev == 1) && cpu_ppc_hypercall) { - cpu_ppc_hypercall(env); - return; - } - if (lev == 1 || (lpes0 == 0 && lpes1 == 0)) - new_msr |= (target_ulong)MSR_HVB; - goto store_next; - case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ - goto store_current; - case POWERPC_EXCP_DECR: /* Decrementer exception */ - if (lpes1 == 0) - new_msr |= (target_ulong)MSR_HVB; - goto store_next; - case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ - /* FIT on 4xx */ - LOG_EXCP("FIT exception\n"); - goto store_next; - case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ - LOG_EXCP("WDT exception\n"); - switch (excp_model) { - case POWERPC_EXCP_BOOKE: - srr0 = SPR_BOOKE_CSRR0; - srr1 = SPR_BOOKE_CSRR1; - break; - default: - break; - } - goto store_next; - case POWERPC_EXCP_DTLB: /* Data TLB error */ - goto store_next; - case POWERPC_EXCP_ITLB: /* Instruction TLB error */ - goto store_next; - case POWERPC_EXCP_DEBUG: /* Debug interrupt */ - switch (excp_model) { - case POWERPC_EXCP_BOOKE: - srr0 = SPR_BOOKE_DSRR0; - srr1 = SPR_BOOKE_DSRR1; - asrr0 = SPR_BOOKE_CSRR0; - asrr1 = SPR_BOOKE_CSRR1; - break; - default: - break; - } - /* XXX: TODO */ - cpu_abort(env, "Debug exception is not implemented yet !\n"); - goto store_next; - case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable */ - env->spr[SPR_BOOKE_ESR] = ESR_SPV; - goto store_current; - case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ - /* XXX: TODO */ - cpu_abort(env, "Embedded floating point data exception " - "is not implemented yet !\n"); - env->spr[SPR_BOOKE_ESR] = ESR_SPV; - goto store_next; - case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */ - /* XXX: TODO */ - cpu_abort(env, "Embedded floating point round exception " - "is not implemented yet !\n"); - env->spr[SPR_BOOKE_ESR] = ESR_SPV; - goto store_next; - case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */ - /* XXX: TODO */ - cpu_abort(env, - "Performance counter exception is not implemented yet !\n"); - goto store_next; - case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ - goto store_next; - case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ - srr0 = SPR_BOOKE_CSRR0; - srr1 = SPR_BOOKE_CSRR1; - goto store_next; - case POWERPC_EXCP_RESET: /* System reset exception */ - if (msr_pow) { - /* indicate that we resumed from power save mode */ - msr |= 0x10000; - } else { - new_msr &= ~((target_ulong)1 << MSR_ME); - } - - if (0) { - /* XXX: find a suitable condition to enable the hypervisor mode */ - new_msr |= (target_ulong)MSR_HVB; - } - goto store_next; - case POWERPC_EXCP_DSEG: /* Data segment exception */ - if (lpes1 == 0) - new_msr |= (target_ulong)MSR_HVB; - goto store_next; - case POWERPC_EXCP_ISEG: /* Instruction segment exception */ - if (lpes1 == 0) - new_msr |= (target_ulong)MSR_HVB; - goto store_next; - case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - goto store_next; - case POWERPC_EXCP_TRACE: /* Trace exception */ - if (lpes1 == 0) - new_msr |= (target_ulong)MSR_HVB; - goto store_next; - case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - goto store_next; - case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - goto store_next; - case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - goto store_next; - case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */ - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - goto store_next; - case POWERPC_EXCP_VPU: /* Vector unavailable exception */ - if (lpes1 == 0) - new_msr |= (target_ulong)MSR_HVB; - goto store_current; - case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ - LOG_EXCP("PIT exception\n"); - goto store_next; - case POWERPC_EXCP_IO: /* IO error exception */ - /* XXX: TODO */ - cpu_abort(env, "601 IO error exception is not implemented yet !\n"); - goto store_next; - case POWERPC_EXCP_RUNM: /* Run mode exception */ - /* XXX: TODO */ - cpu_abort(env, "601 run mode exception is not implemented yet !\n"); - goto store_next; - case POWERPC_EXCP_EMUL: /* Emulation trap exception */ - /* XXX: TODO */ - cpu_abort(env, "602 emulation trap exception " - "is not implemented yet !\n"); - goto store_next; - case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ - if (lpes1 == 0) /* XXX: check this */ - new_msr |= (target_ulong)MSR_HVB; - switch (excp_model) { - case POWERPC_EXCP_602: - case POWERPC_EXCP_603: - case POWERPC_EXCP_603E: - case POWERPC_EXCP_G2: - goto tlb_miss_tgpr; - case POWERPC_EXCP_7x5: - goto tlb_miss; - case POWERPC_EXCP_74xx: - goto tlb_miss_74xx; - default: - cpu_abort(env, "Invalid instruction TLB miss exception\n"); - break; - } - break; - case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ - if (lpes1 == 0) /* XXX: check this */ - new_msr |= (target_ulong)MSR_HVB; - switch (excp_model) { - case POWERPC_EXCP_602: - case POWERPC_EXCP_603: - case POWERPC_EXCP_603E: - case POWERPC_EXCP_G2: - goto tlb_miss_tgpr; - case POWERPC_EXCP_7x5: - goto tlb_miss; - case POWERPC_EXCP_74xx: - goto tlb_miss_74xx; - default: - cpu_abort(env, "Invalid data load TLB miss exception\n"); - break; - } - break; - case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ - if (lpes1 == 0) /* XXX: check this */ - new_msr |= (target_ulong)MSR_HVB; - switch (excp_model) { - case POWERPC_EXCP_602: - case POWERPC_EXCP_603: - case POWERPC_EXCP_603E: - case POWERPC_EXCP_G2: - tlb_miss_tgpr: - /* Swap temporary saved registers with GPRs */ - if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) { - new_msr |= (target_ulong)1 << MSR_TGPR; - hreg_swap_gpr_tgpr(env); - } - goto tlb_miss; - case POWERPC_EXCP_7x5: - tlb_miss: -#if defined (DEBUG_SOFTWARE_TLB) - if (qemu_log_enabled()) { - const char *es; - target_ulong *miss, *cmp; - int en; - if (excp == POWERPC_EXCP_IFTLB) { - es = "I"; - en = 'I'; - miss = &env->spr[SPR_IMISS]; - cmp = &env->spr[SPR_ICMP]; - } else { - if (excp == POWERPC_EXCP_DLTLB) - es = "DL"; - else - es = "DS"; - en = 'D'; - miss = &env->spr[SPR_DMISS]; - cmp = &env->spr[SPR_DCMP]; - } - qemu_log("6xx %sTLB miss: %cM " TARGET_FMT_lx " %cC " - TARGET_FMT_lx " H1 " TARGET_FMT_lx " H2 " - TARGET_FMT_lx " %08x\n", es, en, *miss, en, *cmp, - env->spr[SPR_HASH1], env->spr[SPR_HASH2], - env->error_code); - } -#endif - 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) & (env->nb_ways - 1)) << 17; - break; - case POWERPC_EXCP_74xx: - tlb_miss_74xx: -#if defined (DEBUG_SOFTWARE_TLB) - if (qemu_log_enabled()) { - const char *es; - target_ulong *miss, *cmp; - int en; - if (excp == POWERPC_EXCP_IFTLB) { - es = "I"; - en = 'I'; - miss = &env->spr[SPR_TLBMISS]; - cmp = &env->spr[SPR_PTEHI]; - } else { - if (excp == POWERPC_EXCP_DLTLB) - es = "DL"; - else - es = "DS"; - en = 'D'; - miss = &env->spr[SPR_TLBMISS]; - cmp = &env->spr[SPR_PTEHI]; - } - qemu_log("74xx %sTLB miss: %cM " TARGET_FMT_lx " %cC " - TARGET_FMT_lx " %08x\n", es, en, *miss, en, *cmp, - env->error_code); - } -#endif - msr |= env->error_code; /* key bit */ - break; - default: - cpu_abort(env, "Invalid data store TLB miss exception\n"); - break; - } - goto store_next; - case POWERPC_EXCP_FPA: /* Floating-point assist exception */ - /* XXX: TODO */ - cpu_abort(env, "Floating point assist exception " - "is not implemented yet !\n"); - goto store_next; - case POWERPC_EXCP_DABR: /* Data address breakpoint */ - /* XXX: TODO */ - cpu_abort(env, "DABR exception is not implemented yet !\n"); - goto store_next; - case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ - /* XXX: TODO */ - cpu_abort(env, "IABR exception is not implemented yet !\n"); - goto store_next; - case POWERPC_EXCP_SMI: /* System management interrupt */ - /* XXX: TODO */ - cpu_abort(env, "SMI exception is not implemented yet !\n"); - goto store_next; - case POWERPC_EXCP_THERM: /* Thermal interrupt */ - /* XXX: TODO */ - cpu_abort(env, "Thermal management exception " - "is not implemented yet !\n"); - goto store_next; - case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ - if (lpes1 == 0) - new_msr |= (target_ulong)MSR_HVB; - /* XXX: TODO */ - cpu_abort(env, - "Performance counter exception is not implemented yet !\n"); - goto store_next; - case POWERPC_EXCP_VPUA: /* Vector assist exception */ - /* XXX: TODO */ - cpu_abort(env, "VPU assist exception is not implemented yet !\n"); - goto store_next; - case POWERPC_EXCP_SOFTP: /* Soft patch exception */ - /* XXX: TODO */ - cpu_abort(env, - "970 soft-patch exception is not implemented yet !\n"); - goto store_next; - case POWERPC_EXCP_MAINT: /* Maintenance exception */ - /* XXX: TODO */ - cpu_abort(env, - "970 maintenance exception is not implemented yet !\n"); - goto store_next; - case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */ - /* XXX: TODO */ - cpu_abort(env, "Maskable external exception " - "is not implemented yet !\n"); - goto store_next; - case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */ - /* XXX: TODO */ - cpu_abort(env, "Non maskable external exception " - "is not implemented yet !\n"); - goto store_next; - default: - excp_invalid: - cpu_abort(env, "Invalid PowerPC exception %d. Aborting\n", excp); - break; - store_current: - /* save current instruction location */ - env->spr[srr0] = env->nip - 4; - break; - store_next: - /* save next instruction location */ - env->spr[srr0] = env->nip; - break; - } - /* Save MSR */ - env->spr[srr1] = msr; - /* If any alternate SRR register are defined, duplicate saved values */ - if (asrr0 != -1) - env->spr[asrr0] = env->spr[srr0]; - if (asrr1 != -1) - env->spr[asrr1] = env->spr[srr1]; - /* If we disactivated any translation, flush TLBs */ - if (msr & ((1 << MSR_IR) | (1 << MSR_DR))) - tlb_flush(env, 1); - - if (msr_ile) { - new_msr |= (target_ulong)1 << MSR_LE; - } - - /* Jump to handler */ - vector = env->excp_vectors[excp]; - if (vector == (target_ulong)-1ULL) { - cpu_abort(env, "Raised an exception without defined vector %d\n", - excp); - } - vector |= env->excp_prefix; -#if defined(TARGET_PPC64) - if (excp_model == POWERPC_EXCP_BOOKE) { - if (!msr_icm) { - vector = (uint32_t)vector; - } else { - new_msr |= (target_ulong)1 << MSR_CM; - } - } else { - if (!msr_isf && !(env->mmu_model & POWERPC_MMU_64)) { - vector = (uint32_t)vector; - } else { - new_msr |= (target_ulong)1 << MSR_SF; - } - } -#endif - /* XXX: we don't use hreg_store_msr here as already have treated - * any special case that could occur. Just store MSR and update hflags - */ - env->msr = new_msr & env->msr_mask; - hreg_compute_hflags(env); - env->nip = vector; - /* Reset exception state */ - env->exception_index = POWERPC_EXCP_NONE; - env->error_code = 0; - - if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - /* XXX: The BookE changes address space when switching modes, - we should probably implement that as different MMU indexes, - but for the moment we do it the slow way and flush all. */ - tlb_flush(env, 1); - } -} - -void do_interrupt (CPUPPCState *env) -{ - powerpc_excp(env, env->excp_model, env->exception_index); -} - -void ppc_hw_interrupt (CPUPPCState *env) -{ - int hdice; - -#if 0 - qemu_log_mask(CPU_LOG_INT, "%s: %p pending %08x req %08x me %d ee %d\n", - __func__, env, env->pending_interrupts, - env->interrupt_request, (int)msr_me, (int)msr_ee); -#endif - /* External reset */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_RESET)) { - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_RESET); - powerpc_excp(env, env->excp_model, POWERPC_EXCP_RESET); - return; - } - /* Machine check exception */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_MCK)) { - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_MCK); - powerpc_excp(env, env->excp_model, POWERPC_EXCP_MCHECK); - return; - } -#if 0 /* TODO */ - /* External debug exception */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_DEBUG)) { - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DEBUG); - powerpc_excp(env, env->excp_model, POWERPC_EXCP_DEBUG); - return; - } -#endif - if (0) { - /* XXX: find a suitable condition to enable the hypervisor mode */ - hdice = env->spr[SPR_LPCR] & 1; - } else { - hdice = 0; - } - if ((msr_ee != 0 || msr_hv == 0 || msr_pr != 0) && hdice != 0) { - /* Hypervisor decrementer exception */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) { - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR); - powerpc_excp(env, env->excp_model, POWERPC_EXCP_HDECR); - return; - } - } - if (msr_ce != 0) { - /* External critical interrupt */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_CEXT)) { - /* Taking a critical external interrupt does not clear the external - * critical interrupt status - */ -#if 0 - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_CEXT); -#endif - powerpc_excp(env, env->excp_model, POWERPC_EXCP_CRITICAL); - return; - } - } - if (msr_ee != 0) { - /* Watchdog timer on embedded PowerPC */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_WDT)) { - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_WDT); - powerpc_excp(env, env->excp_model, POWERPC_EXCP_WDT); - return; - } - if (env->pending_interrupts & (1 << PPC_INTERRUPT_CDOORBELL)) { - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_CDOORBELL); - powerpc_excp(env, env->excp_model, POWERPC_EXCP_DOORCI); - return; - } - /* Fixed interval timer on embedded PowerPC */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_FIT)) { - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_FIT); - powerpc_excp(env, env->excp_model, POWERPC_EXCP_FIT); - return; - } - /* Programmable interval timer on embedded PowerPC */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_PIT)) { - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PIT); - powerpc_excp(env, env->excp_model, POWERPC_EXCP_PIT); - return; - } - /* Decrementer exception */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_DECR)) { - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DECR); - powerpc_excp(env, env->excp_model, POWERPC_EXCP_DECR); - return; - } - /* External interrupt */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) { - /* Taking an external interrupt does not clear the external - * interrupt status - */ -#if 0 - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_EXT); -#endif - powerpc_excp(env, env->excp_model, POWERPC_EXCP_EXTERNAL); - return; - } - if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) { - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL); - powerpc_excp(env, env->excp_model, POWERPC_EXCP_DOORI); - return; - } - if (env->pending_interrupts & (1 << PPC_INTERRUPT_PERFM)) { - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PERFM); - powerpc_excp(env, env->excp_model, POWERPC_EXCP_PERFM); - return; - } - /* Thermal interrupt */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_THERM)) { - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_THERM); - powerpc_excp(env, env->excp_model, POWERPC_EXCP_THERM); - return; - } - } -} -#endif /* !CONFIG_USER_ONLY */ - -void cpu_dump_rfi (target_ulong RA, target_ulong msr) -{ - qemu_log("Return from exception at " TARGET_FMT_lx " with flags " - TARGET_FMT_lx "\n", RA, msr); -} - PowerPCCPU *cpu_ppc_init(const char *cpu_model) { PowerPCCPU *cpu; @@ -3193,8 +30,9 @@ PowerPCCPU *cpu_ppc_init(const char *cpu_model) const ppc_def_t *def; def = cpu_ppc_find_by_name(cpu_model); - if (!def) + if (!def) { return NULL; + } cpu = POWERPC_CPU(object_new(TYPE_POWERPC_CPU)); env = &cpu->env; diff --git a/target-ppc/helper.h b/target-ppc/helper.h index 148543a8a1..fd04c063e2 100644 --- a/target-ppc/helper.h +++ b/target-ppc/helper.h @@ -1,96 +1,96 @@ #include "def-helper.h" -DEF_HELPER_2(raise_exception_err, void, i32, i32) -DEF_HELPER_1(raise_exception, void, i32) -DEF_HELPER_3(tw, void, tl, tl, i32) +DEF_HELPER_3(raise_exception_err, void, env, i32, i32) +DEF_HELPER_2(raise_exception, void, env, i32) +DEF_HELPER_4(tw, void, env, tl, tl, i32) #if defined(TARGET_PPC64) -DEF_HELPER_3(td, void, tl, tl, i32) +DEF_HELPER_4(td, void, env, tl, tl, i32) #endif #if !defined(CONFIG_USER_ONLY) -DEF_HELPER_1(store_msr, void, tl) -DEF_HELPER_0(rfi, void) -DEF_HELPER_0(rfsvc, void) -DEF_HELPER_0(40x_rfci, void) -DEF_HELPER_0(rfci, void) -DEF_HELPER_0(rfdi, void) -DEF_HELPER_0(rfmci, void) +DEF_HELPER_2(store_msr, void, env, tl) +DEF_HELPER_1(rfi, void, env) +DEF_HELPER_1(rfsvc, void, env) +DEF_HELPER_1(40x_rfci, void, env) +DEF_HELPER_1(rfci, void, env) +DEF_HELPER_1(rfdi, void, env) +DEF_HELPER_1(rfmci, void, env) #if defined(TARGET_PPC64) -DEF_HELPER_0(rfid, void) -DEF_HELPER_0(hrfid, void) +DEF_HELPER_1(rfid, void, env) +DEF_HELPER_1(hrfid, void, env) #endif #endif -DEF_HELPER_2(lmw, void, tl, i32) -DEF_HELPER_2(stmw, void, tl, i32) -DEF_HELPER_3(lsw, void, tl, i32, i32) -DEF_HELPER_4(lswx, void, tl, i32, i32, i32) -DEF_HELPER_3(stsw, void, tl, i32, i32) -DEF_HELPER_1(dcbz, void, tl) -DEF_HELPER_1(dcbz_970, void, tl) -DEF_HELPER_1(icbi, void, tl) -DEF_HELPER_4(lscbx, tl, tl, i32, i32, i32) +DEF_HELPER_3(lmw, void, env, tl, i32) +DEF_HELPER_3(stmw, void, env, tl, i32) +DEF_HELPER_4(lsw, void, env, tl, i32, i32) +DEF_HELPER_5(lswx, void, env, tl, i32, i32, i32) +DEF_HELPER_4(stsw, void, env, tl, i32, i32) +DEF_HELPER_2(dcbz, void, env, tl) +DEF_HELPER_2(dcbz_970, void, env, tl) +DEF_HELPER_2(icbi, void, env, tl) +DEF_HELPER_5(lscbx, tl, env, tl, i32, i32, i32) #if defined(TARGET_PPC64) DEF_HELPER_FLAGS_2(mulhd, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) DEF_HELPER_FLAGS_2(mulhdu, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) -DEF_HELPER_2(mulldo, i64, i64, i64) +DEF_HELPER_3(mulldo, i64, env, i64, i64) #endif DEF_HELPER_FLAGS_1(cntlzw, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl) DEF_HELPER_FLAGS_1(popcntb, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl) DEF_HELPER_FLAGS_1(popcntw, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl) -DEF_HELPER_2(sraw, tl, tl, tl) +DEF_HELPER_3(sraw, tl, env, tl, tl) #if defined(TARGET_PPC64) DEF_HELPER_FLAGS_1(cntlzd, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl) DEF_HELPER_FLAGS_1(popcntd, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl) -DEF_HELPER_2(srad, tl, tl, tl) +DEF_HELPER_3(srad, tl, env, tl, tl) #endif DEF_HELPER_FLAGS_1(cntlsw32, TCG_CALL_CONST | TCG_CALL_PURE, i32, i32) DEF_HELPER_FLAGS_1(cntlzw32, TCG_CALL_CONST | TCG_CALL_PURE, i32, i32) DEF_HELPER_FLAGS_2(brinc, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl, tl) -DEF_HELPER_0(float_check_status, void) -DEF_HELPER_0(reset_fpstatus, void) -DEF_HELPER_2(compute_fprf, i32, i64, i32) -DEF_HELPER_2(store_fpscr, void, i64, i32) -DEF_HELPER_1(fpscr_clrbit, void, i32) -DEF_HELPER_1(fpscr_setbit, void, i32) -DEF_HELPER_1(float64_to_float32, i32, i64) -DEF_HELPER_1(float32_to_float64, i64, i32) +DEF_HELPER_1(float_check_status, void, env) +DEF_HELPER_1(reset_fpstatus, void, env) +DEF_HELPER_3(compute_fprf, i32, env, i64, i32) +DEF_HELPER_3(store_fpscr, void, env, i64, i32) +DEF_HELPER_2(fpscr_clrbit, void, env, i32) +DEF_HELPER_2(fpscr_setbit, void, env, i32) +DEF_HELPER_2(float64_to_float32, i32, env, i64) +DEF_HELPER_2(float32_to_float64, i64, env, i32) -DEF_HELPER_3(fcmpo, void, i64, i64, i32) -DEF_HELPER_3(fcmpu, void, i64, i64, i32) +DEF_HELPER_4(fcmpo, void, env, i64, i64, i32) +DEF_HELPER_4(fcmpu, void, env, i64, i64, i32) -DEF_HELPER_1(fctiw, i64, i64) -DEF_HELPER_1(fctiwz, i64, i64) +DEF_HELPER_2(fctiw, i64, env, i64) +DEF_HELPER_2(fctiwz, i64, env, i64) #if defined(TARGET_PPC64) -DEF_HELPER_1(fcfid, i64, i64) -DEF_HELPER_1(fctid, i64, i64) -DEF_HELPER_1(fctidz, i64, i64) +DEF_HELPER_2(fcfid, i64, env, i64) +DEF_HELPER_2(fctid, i64, env, i64) +DEF_HELPER_2(fctidz, i64, env, i64) #endif -DEF_HELPER_1(frsp, i64, i64) -DEF_HELPER_1(frin, i64, i64) -DEF_HELPER_1(friz, i64, i64) -DEF_HELPER_1(frip, i64, i64) -DEF_HELPER_1(frim, i64, i64) +DEF_HELPER_2(frsp, i64, env, i64) +DEF_HELPER_2(frin, i64, env, i64) +DEF_HELPER_2(friz, i64, env, i64) +DEF_HELPER_2(frip, i64, env, i64) +DEF_HELPER_2(frim, i64, env, i64) -DEF_HELPER_2(fadd, i64, i64, i64) -DEF_HELPER_2(fsub, i64, i64, i64) -DEF_HELPER_2(fmul, i64, i64, i64) -DEF_HELPER_2(fdiv, i64, i64, i64) -DEF_HELPER_3(fmadd, i64, i64, i64, i64) -DEF_HELPER_3(fmsub, i64, i64, i64, i64) -DEF_HELPER_3(fnmadd, i64, i64, i64, i64) -DEF_HELPER_3(fnmsub, i64, i64, i64, i64) -DEF_HELPER_1(fabs, i64, i64) -DEF_HELPER_1(fnabs, i64, i64) -DEF_HELPER_1(fneg, i64, i64) -DEF_HELPER_1(fsqrt, i64, i64) -DEF_HELPER_1(fre, i64, i64) -DEF_HELPER_1(fres, i64, i64) -DEF_HELPER_1(frsqrte, i64, i64) -DEF_HELPER_3(fsel, i64, i64, i64, i64) +DEF_HELPER_3(fadd, i64, env, i64, i64) +DEF_HELPER_3(fsub, i64, env, i64, i64) +DEF_HELPER_3(fmul, i64, env, i64, i64) +DEF_HELPER_3(fdiv, i64, env, i64, i64) +DEF_HELPER_4(fmadd, i64, env, i64, i64, i64) +DEF_HELPER_4(fmsub, i64, env, i64, i64, i64) +DEF_HELPER_4(fnmadd, i64, env, i64, i64, i64) +DEF_HELPER_4(fnmsub, i64, env, i64, i64, i64) +DEF_HELPER_2(fabs, i64, env, i64) +DEF_HELPER_2(fnabs, i64, env, i64) +DEF_HELPER_2(fneg, i64, env, i64) +DEF_HELPER_2(fsqrt, i64, env, i64) +DEF_HELPER_2(fre, i64, env, i64) +DEF_HELPER_2(fres, i64, env, i64) +DEF_HELPER_2(frsqrte, i64, env, i64) +DEF_HELPER_4(fsel, i64, env, i64, i64, i64) #define dh_alias_avr ptr #define dh_ctype_avr ppc_avr_t * @@ -120,32 +120,32 @@ DEF_HELPER_3(vminuw, void, avr, avr, avr) DEF_HELPER_3(vmaxub, void, avr, avr, avr) DEF_HELPER_3(vmaxuh, void, avr, avr, avr) DEF_HELPER_3(vmaxuw, void, avr, avr, avr) -DEF_HELPER_3(vcmpequb, void, avr, avr, avr) -DEF_HELPER_3(vcmpequh, void, avr, avr, avr) -DEF_HELPER_3(vcmpequw, void, avr, avr, avr) -DEF_HELPER_3(vcmpgtub, void, avr, avr, avr) -DEF_HELPER_3(vcmpgtuh, void, avr, avr, avr) -DEF_HELPER_3(vcmpgtuw, void, avr, avr, avr) -DEF_HELPER_3(vcmpgtsb, void, avr, avr, avr) -DEF_HELPER_3(vcmpgtsh, void, avr, avr, avr) -DEF_HELPER_3(vcmpgtsw, void, avr, avr, avr) -DEF_HELPER_3(vcmpeqfp, void, avr, avr, avr) -DEF_HELPER_3(vcmpgefp, void, avr, avr, avr) -DEF_HELPER_3(vcmpgtfp, void, avr, avr, avr) -DEF_HELPER_3(vcmpbfp, void, avr, avr, avr) -DEF_HELPER_3(vcmpequb_dot, void, avr, avr, avr) -DEF_HELPER_3(vcmpequh_dot, void, avr, avr, avr) -DEF_HELPER_3(vcmpequw_dot, void, avr, avr, avr) -DEF_HELPER_3(vcmpgtub_dot, void, avr, avr, avr) -DEF_HELPER_3(vcmpgtuh_dot, void, avr, avr, avr) -DEF_HELPER_3(vcmpgtuw_dot, void, avr, avr, avr) -DEF_HELPER_3(vcmpgtsb_dot, void, avr, avr, avr) -DEF_HELPER_3(vcmpgtsh_dot, void, avr, avr, avr) -DEF_HELPER_3(vcmpgtsw_dot, void, avr, avr, avr) -DEF_HELPER_3(vcmpeqfp_dot, void, avr, avr, avr) -DEF_HELPER_3(vcmpgefp_dot, void, avr, avr, avr) -DEF_HELPER_3(vcmpgtfp_dot, void, avr, avr, avr) -DEF_HELPER_3(vcmpbfp_dot, void, avr, avr, avr) +DEF_HELPER_4(vcmpequb, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpequh, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpequw, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtub, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtuh, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtuw, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtsb, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtsh, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtsw, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpeqfp, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgefp, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtfp, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpbfp, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpequb_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpequh_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpequw_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtub_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtuh_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtuw_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtsb_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtsh_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtsw_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpeqfp_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgefp_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpgtfp_dot, void, env, avr, avr, avr) +DEF_HELPER_4(vcmpbfp_dot, void, env, avr, avr, avr) DEF_HELPER_3(vmrglb, void, avr, avr, avr) DEF_HELPER_3(vmrglh, void, avr, avr, avr) DEF_HELPER_3(vmrglw, void, avr, avr, avr) @@ -175,18 +175,18 @@ DEF_HELPER_3(vaddcuw, void, avr, avr, avr) DEF_HELPER_3(vsubcuw, void, avr, avr, avr) DEF_HELPER_2(lvsl, void, avr, tl); DEF_HELPER_2(lvsr, void, avr, tl); -DEF_HELPER_3(vaddsbs, void, avr, avr, avr) -DEF_HELPER_3(vaddshs, void, avr, avr, avr) -DEF_HELPER_3(vaddsws, void, avr, avr, avr) -DEF_HELPER_3(vsubsbs, void, avr, avr, avr) -DEF_HELPER_3(vsubshs, void, avr, avr, avr) -DEF_HELPER_3(vsubsws, void, avr, avr, avr) -DEF_HELPER_3(vaddubs, void, avr, avr, avr) -DEF_HELPER_3(vadduhs, void, avr, avr, avr) -DEF_HELPER_3(vadduws, void, avr, avr, avr) -DEF_HELPER_3(vsububs, void, avr, avr, avr) -DEF_HELPER_3(vsubuhs, void, avr, avr, avr) -DEF_HELPER_3(vsubuws, void, avr, avr, avr) +DEF_HELPER_4(vaddsbs, void, env, avr, avr, avr) +DEF_HELPER_4(vaddshs, void, env, avr, avr, avr) +DEF_HELPER_4(vaddsws, void, env, avr, avr, avr) +DEF_HELPER_4(vsubsbs, void, env, avr, avr, avr) +DEF_HELPER_4(vsubshs, void, env, avr, avr, avr) +DEF_HELPER_4(vsubsws, void, env, avr, avr, avr) +DEF_HELPER_4(vaddubs, void, env, avr, avr, avr) +DEF_HELPER_4(vadduhs, void, env, avr, avr, avr) +DEF_HELPER_4(vadduws, void, env, avr, avr, avr) +DEF_HELPER_4(vsububs, void, env, avr, avr, avr) +DEF_HELPER_4(vsubuhs, void, env, avr, avr, avr) +DEF_HELPER_4(vsubuws, void, env, avr, avr, avr) DEF_HELPER_3(vrlb, void, avr, avr, avr) DEF_HELPER_3(vrlh, void, avr, avr, avr) DEF_HELPER_3(vrlw, void, avr, avr, avr) @@ -205,212 +205,213 @@ DEF_HELPER_2(vupkhsb, void, avr, avr) DEF_HELPER_2(vupkhsh, void, avr, avr) DEF_HELPER_2(vupklsb, void, avr, avr) DEF_HELPER_2(vupklsh, void, avr, avr) -DEF_HELPER_4(vmsumubm, void, avr, avr, avr, avr) -DEF_HELPER_4(vmsummbm, void, avr, avr, avr, avr) -DEF_HELPER_4(vsel, void, avr, avr, avr, avr) -DEF_HELPER_4(vperm, void, avr, avr, avr, avr) -DEF_HELPER_3(vpkshss, void, avr, avr, avr) -DEF_HELPER_3(vpkshus, void, avr, avr, avr) -DEF_HELPER_3(vpkswss, void, avr, avr, avr) -DEF_HELPER_3(vpkswus, void, avr, avr, avr) -DEF_HELPER_3(vpkuhus, void, avr, avr, avr) -DEF_HELPER_3(vpkuwus, void, avr, avr, avr) -DEF_HELPER_3(vpkuhum, void, avr, avr, avr) -DEF_HELPER_3(vpkuwum, void, avr, avr, avr) +DEF_HELPER_5(vmsumubm, void, env, avr, avr, avr, avr) +DEF_HELPER_5(vmsummbm, void, env, avr, avr, avr, avr) +DEF_HELPER_5(vsel, void, env, avr, avr, avr, avr) +DEF_HELPER_5(vperm, void, env, avr, avr, avr, avr) +DEF_HELPER_4(vpkshss, void, env, avr, avr, avr) +DEF_HELPER_4(vpkshus, void, env, avr, avr, avr) +DEF_HELPER_4(vpkswss, void, env, avr, avr, avr) +DEF_HELPER_4(vpkswus, void, env, avr, avr, avr) +DEF_HELPER_4(vpkuhus, void, env, avr, avr, avr) +DEF_HELPER_4(vpkuwus, void, env, avr, avr, avr) +DEF_HELPER_4(vpkuhum, void, env, avr, avr, avr) +DEF_HELPER_4(vpkuwum, void, env, avr, avr, avr) DEF_HELPER_3(vpkpx, void, avr, avr, avr) -DEF_HELPER_4(vmhaddshs, void, avr, avr, avr, avr) -DEF_HELPER_4(vmhraddshs, void, avr, avr, avr, avr) -DEF_HELPER_4(vmsumuhm, void, avr, avr, avr, avr) -DEF_HELPER_4(vmsumuhs, void, avr, avr, avr, avr) -DEF_HELPER_4(vmsumshm, void, avr, avr, avr, avr) -DEF_HELPER_4(vmsumshs, void, avr, avr, avr, avr) +DEF_HELPER_5(vmhaddshs, void, env, avr, avr, avr, avr) +DEF_HELPER_5(vmhraddshs, void, env, avr, avr, avr, avr) +DEF_HELPER_5(vmsumuhm, void, env, avr, avr, avr, avr) +DEF_HELPER_5(vmsumuhs, void, env, avr, avr, avr, avr) +DEF_HELPER_5(vmsumshm, void, env, avr, avr, avr, avr) +DEF_HELPER_5(vmsumshs, void, env, avr, avr, avr, avr) DEF_HELPER_4(vmladduhm, void, avr, avr, avr, avr) -DEF_HELPER_1(mtvscr, void, avr); -DEF_HELPER_2(lvebx, void, avr, tl) -DEF_HELPER_2(lvehx, void, avr, tl) -DEF_HELPER_2(lvewx, void, avr, tl) -DEF_HELPER_2(stvebx, void, avr, tl) -DEF_HELPER_2(stvehx, void, avr, tl) -DEF_HELPER_2(stvewx, void, avr, tl) -DEF_HELPER_3(vsumsws, void, avr, avr, avr) -DEF_HELPER_3(vsum2sws, void, avr, avr, avr) -DEF_HELPER_3(vsum4sbs, void, avr, avr, avr) -DEF_HELPER_3(vsum4shs, void, avr, avr, avr) -DEF_HELPER_3(vsum4ubs, void, avr, avr, avr) -DEF_HELPER_3(vaddfp, void, avr, avr, avr) -DEF_HELPER_3(vsubfp, void, avr, avr, avr) -DEF_HELPER_3(vmaxfp, void, avr, avr, avr) -DEF_HELPER_3(vminfp, void, avr, avr, avr) -DEF_HELPER_2(vrefp, void, avr, avr) -DEF_HELPER_2(vrsqrtefp, void, avr, avr) -DEF_HELPER_4(vmaddfp, void, avr, avr, avr, avr) -DEF_HELPER_4(vnmsubfp, void, avr, avr, avr, avr) -DEF_HELPER_2(vexptefp, void, avr, avr) -DEF_HELPER_2(vlogefp, void, avr, avr) -DEF_HELPER_2(vrfim, void, avr, avr) -DEF_HELPER_2(vrfin, void, avr, avr) -DEF_HELPER_2(vrfip, void, avr, avr) -DEF_HELPER_2(vrfiz, void, avr, avr) -DEF_HELPER_3(vcfux, void, avr, avr, i32) -DEF_HELPER_3(vcfsx, void, avr, avr, i32) -DEF_HELPER_3(vctuxs, void, avr, avr, i32) -DEF_HELPER_3(vctsxs, void, avr, avr, i32) +DEF_HELPER_2(mtvscr, void, env, avr); +DEF_HELPER_3(lvebx, void, env, avr, tl) +DEF_HELPER_3(lvehx, void, env, avr, tl) +DEF_HELPER_3(lvewx, void, env, avr, tl) +DEF_HELPER_3(stvebx, void, env, avr, tl) +DEF_HELPER_3(stvehx, void, env, avr, tl) +DEF_HELPER_3(stvewx, void, env, avr, tl) +DEF_HELPER_4(vsumsws, void, env, avr, avr, avr) +DEF_HELPER_4(vsum2sws, void, env, avr, avr, avr) +DEF_HELPER_4(vsum4sbs, void, env, avr, avr, avr) +DEF_HELPER_4(vsum4shs, void, env, avr, avr, avr) +DEF_HELPER_4(vsum4ubs, void, env, avr, avr, avr) +DEF_HELPER_4(vaddfp, void, env, avr, avr, avr) +DEF_HELPER_4(vsubfp, void, env, avr, avr, avr) +DEF_HELPER_4(vmaxfp, void, env, avr, avr, avr) +DEF_HELPER_4(vminfp, void, env, avr, avr, avr) +DEF_HELPER_3(vrefp, void, env, avr, avr) +DEF_HELPER_3(vrsqrtefp, void, env, avr, avr) +DEF_HELPER_5(vmaddfp, void, env, avr, avr, avr, avr) +DEF_HELPER_5(vnmsubfp, void, env, avr, avr, avr, avr) +DEF_HELPER_3(vexptefp, void, env, avr, avr) +DEF_HELPER_3(vlogefp, void, env, avr, avr) +DEF_HELPER_3(vrfim, void, env, avr, avr) +DEF_HELPER_3(vrfin, void, env, avr, avr) +DEF_HELPER_3(vrfip, void, env, avr, avr) +DEF_HELPER_3(vrfiz, void, env, avr, avr) +DEF_HELPER_4(vcfux, void, env, avr, avr, i32) +DEF_HELPER_4(vcfsx, void, env, avr, avr, i32) +DEF_HELPER_4(vctuxs, void, env, avr, avr, i32) +DEF_HELPER_4(vctsxs, void, env, avr, avr, i32) -DEF_HELPER_1(efscfsi, i32, i32) -DEF_HELPER_1(efscfui, i32, i32) -DEF_HELPER_1(efscfuf, i32, i32) -DEF_HELPER_1(efscfsf, i32, i32) -DEF_HELPER_1(efsctsi, i32, i32) -DEF_HELPER_1(efsctui, i32, i32) -DEF_HELPER_1(efsctsiz, i32, i32) -DEF_HELPER_1(efsctuiz, i32, i32) -DEF_HELPER_1(efsctsf, i32, i32) -DEF_HELPER_1(efsctuf, i32, i32) -DEF_HELPER_1(evfscfsi, i64, i64) -DEF_HELPER_1(evfscfui, i64, i64) -DEF_HELPER_1(evfscfuf, i64, i64) -DEF_HELPER_1(evfscfsf, i64, i64) -DEF_HELPER_1(evfsctsi, i64, i64) -DEF_HELPER_1(evfsctui, i64, i64) -DEF_HELPER_1(evfsctsiz, i64, i64) -DEF_HELPER_1(evfsctuiz, i64, i64) -DEF_HELPER_1(evfsctsf, i64, i64) -DEF_HELPER_1(evfsctuf, i64, i64) -DEF_HELPER_2(efsadd, i32, i32, i32) -DEF_HELPER_2(efssub, i32, i32, i32) -DEF_HELPER_2(efsmul, i32, i32, i32) -DEF_HELPER_2(efsdiv, i32, i32, i32) -DEF_HELPER_2(evfsadd, i64, i64, i64) -DEF_HELPER_2(evfssub, i64, i64, i64) -DEF_HELPER_2(evfsmul, i64, i64, i64) -DEF_HELPER_2(evfsdiv, i64, i64, i64) -DEF_HELPER_2(efststlt, i32, i32, i32) -DEF_HELPER_2(efststgt, i32, i32, i32) -DEF_HELPER_2(efststeq, i32, i32, i32) -DEF_HELPER_2(efscmplt, i32, i32, i32) -DEF_HELPER_2(efscmpgt, i32, i32, i32) -DEF_HELPER_2(efscmpeq, i32, i32, i32) -DEF_HELPER_2(evfststlt, i32, i64, i64) -DEF_HELPER_2(evfststgt, i32, i64, i64) -DEF_HELPER_2(evfststeq, i32, i64, i64) -DEF_HELPER_2(evfscmplt, i32, i64, i64) -DEF_HELPER_2(evfscmpgt, i32, i64, i64) -DEF_HELPER_2(evfscmpeq, i32, i64, i64) -DEF_HELPER_1(efdcfsi, i64, i32) -DEF_HELPER_1(efdcfsid, i64, i64) -DEF_HELPER_1(efdcfui, i64, i32) -DEF_HELPER_1(efdcfuid, i64, i64) -DEF_HELPER_1(efdctsi, i32, i64) -DEF_HELPER_1(efdctui, i32, i64) -DEF_HELPER_1(efdctsiz, i32, i64) -DEF_HELPER_1(efdctsidz, i64, i64) -DEF_HELPER_1(efdctuiz, i32, i64) -DEF_HELPER_1(efdctuidz, i64, i64) -DEF_HELPER_1(efdcfsf, i64, i32) -DEF_HELPER_1(efdcfuf, i64, i32) -DEF_HELPER_1(efdctsf, i32, i64) -DEF_HELPER_1(efdctuf, i32, i64) -DEF_HELPER_1(efscfd, i32, i64) -DEF_HELPER_1(efdcfs, i64, i32) -DEF_HELPER_2(efdadd, i64, i64, i64) -DEF_HELPER_2(efdsub, i64, i64, i64) -DEF_HELPER_2(efdmul, i64, i64, i64) -DEF_HELPER_2(efddiv, i64, i64, i64) -DEF_HELPER_2(efdtstlt, i32, i64, i64) -DEF_HELPER_2(efdtstgt, i32, i64, i64) -DEF_HELPER_2(efdtsteq, i32, i64, i64) -DEF_HELPER_2(efdcmplt, i32, i64, i64) -DEF_HELPER_2(efdcmpgt, i32, i64, i64) -DEF_HELPER_2(efdcmpeq, i32, i64, i64) +DEF_HELPER_2(efscfsi, i32, env, i32) +DEF_HELPER_2(efscfui, i32, env, i32) +DEF_HELPER_2(efscfuf, i32, env, i32) +DEF_HELPER_2(efscfsf, i32, env, i32) +DEF_HELPER_2(efsctsi, i32, env, i32) +DEF_HELPER_2(efsctui, i32, env, i32) +DEF_HELPER_2(efsctsiz, i32, env, i32) +DEF_HELPER_2(efsctuiz, i32, env, i32) +DEF_HELPER_2(efsctsf, i32, env, i32) +DEF_HELPER_2(efsctuf, i32, env, i32) +DEF_HELPER_2(evfscfsi, i64, env, i64) +DEF_HELPER_2(evfscfui, i64, env, i64) +DEF_HELPER_2(evfscfuf, i64, env, i64) +DEF_HELPER_2(evfscfsf, i64, env, i64) +DEF_HELPER_2(evfsctsi, i64, env, i64) +DEF_HELPER_2(evfsctui, i64, env, i64) +DEF_HELPER_2(evfsctsiz, i64, env, i64) +DEF_HELPER_2(evfsctuiz, i64, env, i64) +DEF_HELPER_2(evfsctsf, i64, env, i64) +DEF_HELPER_2(evfsctuf, i64, env, i64) +DEF_HELPER_3(efsadd, i32, env, i32, i32) +DEF_HELPER_3(efssub, i32, env, i32, i32) +DEF_HELPER_3(efsmul, i32, env, i32, i32) +DEF_HELPER_3(efsdiv, i32, env, i32, i32) +DEF_HELPER_3(evfsadd, i64, env, i64, i64) +DEF_HELPER_3(evfssub, i64, env, i64, i64) +DEF_HELPER_3(evfsmul, i64, env, i64, i64) +DEF_HELPER_3(evfsdiv, i64, env, i64, i64) +DEF_HELPER_3(efststlt, i32, env, i32, i32) +DEF_HELPER_3(efststgt, i32, env, i32, i32) +DEF_HELPER_3(efststeq, i32, env, i32, i32) +DEF_HELPER_3(efscmplt, i32, env, i32, i32) +DEF_HELPER_3(efscmpgt, i32, env, i32, i32) +DEF_HELPER_3(efscmpeq, i32, env, i32, i32) +DEF_HELPER_3(evfststlt, i32, env, i64, i64) +DEF_HELPER_3(evfststgt, i32, env, i64, i64) +DEF_HELPER_3(evfststeq, i32, env, i64, i64) +DEF_HELPER_3(evfscmplt, i32, env, i64, i64) +DEF_HELPER_3(evfscmpgt, i32, env, i64, i64) +DEF_HELPER_3(evfscmpeq, i32, env, i64, i64) +DEF_HELPER_2(efdcfsi, i64, env, i32) +DEF_HELPER_2(efdcfsid, i64, env, i64) +DEF_HELPER_2(efdcfui, i64, env, i32) +DEF_HELPER_2(efdcfuid, i64, env, i64) +DEF_HELPER_2(efdctsi, i32, env, i64) +DEF_HELPER_2(efdctui, i32, env, i64) +DEF_HELPER_2(efdctsiz, i32, env, i64) +DEF_HELPER_2(efdctsidz, i64, env, i64) +DEF_HELPER_2(efdctuiz, i32, env, i64) +DEF_HELPER_2(efdctuidz, i64, env, i64) +DEF_HELPER_2(efdcfsf, i64, env, i32) +DEF_HELPER_2(efdcfuf, i64, env, i32) +DEF_HELPER_2(efdctsf, i32, env, i64) +DEF_HELPER_2(efdctuf, i32, env, i64) +DEF_HELPER_2(efscfd, i32, env, i64) +DEF_HELPER_2(efdcfs, i64, env, i32) +DEF_HELPER_3(efdadd, i64, env, i64, i64) +DEF_HELPER_3(efdsub, i64, env, i64, i64) +DEF_HELPER_3(efdmul, i64, env, i64, i64) +DEF_HELPER_3(efddiv, i64, env, i64, i64) +DEF_HELPER_3(efdtstlt, i32, env, i64, i64) +DEF_HELPER_3(efdtstgt, i32, env, i64, i64) +DEF_HELPER_3(efdtsteq, i32, env, i64, i64) +DEF_HELPER_3(efdcmplt, i32, env, i64, i64) +DEF_HELPER_3(efdcmpgt, i32, env, i64, i64) +DEF_HELPER_3(efdcmpeq, i32, env, i64, i64) #if !defined(CONFIG_USER_ONLY) -DEF_HELPER_1(4xx_tlbre_hi, tl, tl) -DEF_HELPER_1(4xx_tlbre_lo, tl, tl) -DEF_HELPER_2(4xx_tlbwe_hi, void, tl, tl) -DEF_HELPER_2(4xx_tlbwe_lo, void, tl, tl) -DEF_HELPER_1(4xx_tlbsx, tl, tl) -DEF_HELPER_2(440_tlbre, tl, i32, tl) -DEF_HELPER_3(440_tlbwe, void, i32, tl, tl) -DEF_HELPER_1(440_tlbsx, tl, tl) -DEF_HELPER_0(booke206_tlbre, void) -DEF_HELPER_0(booke206_tlbwe, void) -DEF_HELPER_1(booke206_tlbsx, void, tl) -DEF_HELPER_1(booke206_tlbivax, void, tl) -DEF_HELPER_1(booke206_tlbilx0, void, tl) -DEF_HELPER_1(booke206_tlbilx1, void, tl) -DEF_HELPER_1(booke206_tlbilx3, void, tl) -DEF_HELPER_1(booke206_tlbflush, void, i32) -DEF_HELPER_2(booke_setpid, void, i32, tl) -DEF_HELPER_1(6xx_tlbd, void, tl) -DEF_HELPER_1(6xx_tlbi, void, tl) -DEF_HELPER_1(74xx_tlbd, void, tl) -DEF_HELPER_1(74xx_tlbi, void, tl) -DEF_HELPER_FLAGS_0(tlbia, TCG_CALL_CONST, void) -DEF_HELPER_FLAGS_1(tlbie, TCG_CALL_CONST, void, tl) +DEF_HELPER_2(4xx_tlbre_hi, tl, env, tl) +DEF_HELPER_2(4xx_tlbre_lo, tl, env, tl) +DEF_HELPER_3(4xx_tlbwe_hi, void, env, tl, tl) +DEF_HELPER_3(4xx_tlbwe_lo, void, env, tl, tl) +DEF_HELPER_2(4xx_tlbsx, tl, env, tl) +DEF_HELPER_3(440_tlbre, tl, env, i32, tl) +DEF_HELPER_4(440_tlbwe, void, env, i32, tl, tl) +DEF_HELPER_2(440_tlbsx, tl, env, tl) +DEF_HELPER_1(booke206_tlbre, void, env) +DEF_HELPER_1(booke206_tlbwe, void, env) +DEF_HELPER_2(booke206_tlbsx, void, env, tl) +DEF_HELPER_2(booke206_tlbivax, void, env, tl) +DEF_HELPER_2(booke206_tlbilx0, void, env, tl) +DEF_HELPER_2(booke206_tlbilx1, void, env, tl) +DEF_HELPER_2(booke206_tlbilx3, void, env, tl) +DEF_HELPER_2(booke206_tlbflush, void, env, i32) +DEF_HELPER_3(booke_setpid, void, env, i32, tl) +DEF_HELPER_2(6xx_tlbd, void, env, tl) +DEF_HELPER_2(6xx_tlbi, void, env, tl) +DEF_HELPER_2(74xx_tlbd, void, env, tl) +DEF_HELPER_2(74xx_tlbi, void, env, tl) +DEF_HELPER_FLAGS_1(tlbia, TCG_CALL_CONST, void, env) +DEF_HELPER_FLAGS_2(tlbie, TCG_CALL_CONST, void, env, tl) #if defined(TARGET_PPC64) -DEF_HELPER_FLAGS_2(store_slb, TCG_CALL_CONST, void, tl, tl) -DEF_HELPER_1(load_slb_esid, tl, tl) -DEF_HELPER_1(load_slb_vsid, tl, tl) -DEF_HELPER_FLAGS_0(slbia, TCG_CALL_CONST, void) -DEF_HELPER_FLAGS_1(slbie, TCG_CALL_CONST, void, tl) +DEF_HELPER_FLAGS_3(store_slb, TCG_CALL_CONST, void, env, tl, tl) +DEF_HELPER_2(load_slb_esid, tl, env, tl) +DEF_HELPER_2(load_slb_vsid, tl, env, tl) +DEF_HELPER_FLAGS_1(slbia, TCG_CALL_CONST, void, env) +DEF_HELPER_FLAGS_2(slbie, TCG_CALL_CONST, void, env, tl) #endif -DEF_HELPER_FLAGS_1(load_sr, TCG_CALL_CONST, tl, tl); -DEF_HELPER_FLAGS_2(store_sr, TCG_CALL_CONST, void, tl, tl) +DEF_HELPER_FLAGS_2(load_sr, TCG_CALL_CONST, tl, env, tl); +DEF_HELPER_FLAGS_3(store_sr, TCG_CALL_CONST, void, env, tl, tl) DEF_HELPER_FLAGS_1(602_mfrom, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl) DEF_HELPER_1(msgsnd, void, tl) -DEF_HELPER_1(msgclr, void, tl) +DEF_HELPER_2(msgclr, void, env, tl) #endif -DEF_HELPER_3(dlmzb, tl, tl, tl, i32) -DEF_HELPER_FLAGS_1(clcs, TCG_CALL_CONST | TCG_CALL_PURE, tl, i32) +DEF_HELPER_4(dlmzb, tl, env, tl, tl, i32) +DEF_HELPER_FLAGS_2(clcs, TCG_CALL_CONST | TCG_CALL_PURE, tl, env, i32) #if !defined(CONFIG_USER_ONLY) -DEF_HELPER_1(rac, tl, tl) +DEF_HELPER_2(rac, tl, env, tl) #endif -DEF_HELPER_2(div, tl, tl, tl) -DEF_HELPER_2(divo, tl, tl, tl) -DEF_HELPER_2(divs, tl, tl, tl) -DEF_HELPER_2(divso, tl, tl, tl) +DEF_HELPER_3(div, tl, env, tl, tl) +DEF_HELPER_3(divo, tl, env, tl, tl) +DEF_HELPER_3(divs, tl, env, tl, tl) +DEF_HELPER_3(divso, tl, env, tl, tl) -DEF_HELPER_1(load_dcr, tl, tl); -DEF_HELPER_2(store_dcr, void, tl, tl) +DEF_HELPER_2(load_dcr, tl, env, tl); +DEF_HELPER_3(store_dcr, void, env, tl, tl) -DEF_HELPER_1(load_dump_spr, void, i32) -DEF_HELPER_1(store_dump_spr, void, i32) -DEF_HELPER_0(load_tbl, tl) -DEF_HELPER_0(load_tbu, tl) -DEF_HELPER_0(load_atbl, tl) -DEF_HELPER_0(load_atbu, tl) -DEF_HELPER_0(load_601_rtcl, tl) -DEF_HELPER_0(load_601_rtcu, tl) +DEF_HELPER_2(load_dump_spr, void, env, i32) +DEF_HELPER_2(store_dump_spr, void, env, i32) +DEF_HELPER_1(load_tbl, tl, env) +DEF_HELPER_1(load_tbu, tl, env) +DEF_HELPER_1(load_atbl, tl, env) +DEF_HELPER_1(load_atbu, tl, env) +DEF_HELPER_1(load_601_rtcl, tl, env) +DEF_HELPER_1(load_601_rtcu, tl, env) #if !defined(CONFIG_USER_ONLY) #if defined(TARGET_PPC64) -DEF_HELPER_1(store_asr, void, tl) -DEF_HELPER_0(load_purr, tl) +DEF_HELPER_2(store_asr, void, env, tl) +DEF_HELPER_1(load_purr, tl, env) #endif -DEF_HELPER_1(store_sdr1, void, tl) -DEF_HELPER_1(store_tbl, void, tl) -DEF_HELPER_1(store_tbu, void, tl) -DEF_HELPER_1(store_atbl, void, tl) -DEF_HELPER_1(store_atbu, void, tl) -DEF_HELPER_1(store_601_rtcl, void, tl) -DEF_HELPER_1(store_601_rtcu, void, tl) -DEF_HELPER_0(load_decr, tl) -DEF_HELPER_1(store_decr, void, tl) -DEF_HELPER_1(store_hid0_601, void, tl) -DEF_HELPER_2(store_403_pbr, void, i32, tl) -DEF_HELPER_0(load_40x_pit, tl) -DEF_HELPER_1(store_40x_pit, void, tl) -DEF_HELPER_1(store_40x_dbcr0, void, tl) -DEF_HELPER_1(store_40x_sler, void, tl) -DEF_HELPER_1(store_booke_tcr, void, tl) -DEF_HELPER_1(store_booke_tsr, void, tl) -DEF_HELPER_2(store_ibatl, void, i32, tl) -DEF_HELPER_2(store_ibatu, void, i32, tl) -DEF_HELPER_2(store_dbatl, void, i32, tl) -DEF_HELPER_2(store_dbatu, void, i32, tl) -DEF_HELPER_2(store_601_batl, void, i32, tl) -DEF_HELPER_2(store_601_batu, void, i32, tl) +DEF_HELPER_2(store_sdr1, void, env, tl) +DEF_HELPER_2(store_tbl, void, env, tl) +DEF_HELPER_2(store_tbu, void, env, tl) +DEF_HELPER_2(store_atbl, void, env, tl) +DEF_HELPER_2(store_atbu, void, env, tl) +DEF_HELPER_2(store_601_rtcl, void, env, tl) +DEF_HELPER_2(store_601_rtcu, void, env, tl) +DEF_HELPER_1(load_decr, tl, env) +DEF_HELPER_2(store_decr, void, env, tl) +DEF_HELPER_2(store_hid0_601, void, env, tl) +DEF_HELPER_3(store_403_pbr, void, env, i32, tl) +DEF_HELPER_1(load_40x_pit, tl, env) +DEF_HELPER_2(store_40x_pit, void, env, tl) +DEF_HELPER_2(store_40x_dbcr0, void, env, tl) +DEF_HELPER_2(store_40x_sler, void, env, tl) +DEF_HELPER_2(store_booke_tcr, void, env, tl) +DEF_HELPER_2(store_booke_tsr, void, env, tl) +DEF_HELPER_1(load_epr, tl, env) +DEF_HELPER_3(store_ibatl, void, env, i32, tl) +DEF_HELPER_3(store_ibatu, void, env, i32, tl) +DEF_HELPER_3(store_dbatl, void, env, i32, tl) +DEF_HELPER_3(store_dbatu, void, env, i32, tl) +DEF_HELPER_3(store_601_batl, void, env, i32, tl) +DEF_HELPER_3(store_601_batu, void, env, i32, tl) #endif #include "def-helper.h" diff --git a/target-ppc/int_helper.c b/target-ppc/int_helper.c new file mode 100644 index 0000000000..f638b2a07c --- /dev/null +++ b/target-ppc/int_helper.c @@ -0,0 +1,1564 @@ +/* + * PowerPC integer and vector emulation helpers for QEMU. + * + * Copyright (c) 2003-2007 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, see <http://www.gnu.org/licenses/>. + */ +#include "cpu.h" +#include "host-utils.h" +#include "helper.h" + +#include "helper_regs.h" +/*****************************************************************************/ +/* Fixed point operations helpers */ +#if defined(TARGET_PPC64) + +/* multiply high word */ +uint64_t helper_mulhd(uint64_t arg1, uint64_t arg2) +{ + uint64_t tl, th; + + muls64(&tl, &th, arg1, arg2); + return th; +} + +/* multiply high word unsigned */ +uint64_t helper_mulhdu(uint64_t arg1, uint64_t arg2) +{ + uint64_t tl, th; + + mulu64(&tl, &th, arg1, arg2); + return th; +} + +uint64_t helper_mulldo(CPUPPCState *env, uint64_t arg1, uint64_t arg2) +{ + int64_t th; + uint64_t tl; + + muls64(&tl, (uint64_t *)&th, arg1, arg2); + /* If th != 0 && th != -1, then we had an overflow */ + if (likely((uint64_t)(th + 1) <= 1)) { + env->xer &= ~(1 << XER_OV); + } else { + env->xer |= (1 << XER_OV) | (1 << XER_SO); + } + return (int64_t)tl; +} +#endif + +target_ulong helper_cntlzw(target_ulong t) +{ + return clz32(t); +} + +#if defined(TARGET_PPC64) +target_ulong helper_cntlzd(target_ulong t) +{ + return clz64(t); +} +#endif + +/* shift right arithmetic helper */ +target_ulong helper_sraw(CPUPPCState *env, target_ulong value, + target_ulong shift) +{ + int32_t ret; + + if (likely(!(shift & 0x20))) { + if (likely((uint32_t)shift != 0)) { + shift &= 0x1f; + ret = (int32_t)value >> shift; + if (likely(ret >= 0 || (value & ((1 << shift) - 1)) == 0)) { + env->xer &= ~(1 << XER_CA); + } else { + env->xer |= (1 << XER_CA); + } + } else { + ret = (int32_t)value; + env->xer &= ~(1 << XER_CA); + } + } else { + ret = (int32_t)value >> 31; + if (ret) { + env->xer |= (1 << XER_CA); + } else { + env->xer &= ~(1 << XER_CA); + } + } + return (target_long)ret; +} + +#if defined(TARGET_PPC64) +target_ulong helper_srad(CPUPPCState *env, target_ulong value, + target_ulong shift) +{ + int64_t ret; + + if (likely(!(shift & 0x40))) { + if (likely((uint64_t)shift != 0)) { + shift &= 0x3f; + ret = (int64_t)value >> shift; + if (likely(ret >= 0 || (value & ((1 << shift) - 1)) == 0)) { + env->xer &= ~(1 << XER_CA); + } else { + env->xer |= (1 << XER_CA); + } + } else { + ret = (int64_t)value; + env->xer &= ~(1 << XER_CA); + } + } else { + ret = (int64_t)value >> 63; + if (ret) { + env->xer |= (1 << XER_CA); + } else { + env->xer &= ~(1 << XER_CA); + } + } + return ret; +} +#endif + +#if defined(TARGET_PPC64) +target_ulong helper_popcntb(target_ulong val) +{ + val = (val & 0x5555555555555555ULL) + ((val >> 1) & + 0x5555555555555555ULL); + val = (val & 0x3333333333333333ULL) + ((val >> 2) & + 0x3333333333333333ULL); + val = (val & 0x0f0f0f0f0f0f0f0fULL) + ((val >> 4) & + 0x0f0f0f0f0f0f0f0fULL); + return val; +} + +target_ulong helper_popcntw(target_ulong val) +{ + val = (val & 0x5555555555555555ULL) + ((val >> 1) & + 0x5555555555555555ULL); + val = (val & 0x3333333333333333ULL) + ((val >> 2) & + 0x3333333333333333ULL); + val = (val & 0x0f0f0f0f0f0f0f0fULL) + ((val >> 4) & + 0x0f0f0f0f0f0f0f0fULL); + val = (val & 0x00ff00ff00ff00ffULL) + ((val >> 8) & + 0x00ff00ff00ff00ffULL); + val = (val & 0x0000ffff0000ffffULL) + ((val >> 16) & + 0x0000ffff0000ffffULL); + return val; +} + +target_ulong helper_popcntd(target_ulong val) +{ + return ctpop64(val); +} +#else +target_ulong helper_popcntb(target_ulong val) +{ + val = (val & 0x55555555) + ((val >> 1) & 0x55555555); + val = (val & 0x33333333) + ((val >> 2) & 0x33333333); + val = (val & 0x0f0f0f0f) + ((val >> 4) & 0x0f0f0f0f); + return val; +} + +target_ulong helper_popcntw(target_ulong val) +{ + val = (val & 0x55555555) + ((val >> 1) & 0x55555555); + val = (val & 0x33333333) + ((val >> 2) & 0x33333333); + val = (val & 0x0f0f0f0f) + ((val >> 4) & 0x0f0f0f0f); + val = (val & 0x00ff00ff) + ((val >> 8) & 0x00ff00ff); + val = (val & 0x0000ffff) + ((val >> 16) & 0x0000ffff); + return val; +} +#endif + +/*****************************************************************************/ +/* PowerPC 601 specific instructions (POWER bridge) */ +target_ulong helper_div(CPUPPCState *env, target_ulong arg1, target_ulong arg2) +{ + uint64_t tmp = (uint64_t)arg1 << 32 | env->spr[SPR_MQ]; + + if (((int32_t)tmp == INT32_MIN && (int32_t)arg2 == (int32_t)-1) || + (int32_t)arg2 == 0) { + env->spr[SPR_MQ] = 0; + return INT32_MIN; + } else { + env->spr[SPR_MQ] = tmp % arg2; + return tmp / (int32_t)arg2; + } +} + +target_ulong helper_divo(CPUPPCState *env, target_ulong arg1, + target_ulong arg2) +{ + uint64_t tmp = (uint64_t)arg1 << 32 | env->spr[SPR_MQ]; + + if (((int32_t)tmp == INT32_MIN && (int32_t)arg2 == (int32_t)-1) || + (int32_t)arg2 == 0) { + env->xer |= (1 << XER_OV) | (1 << XER_SO); + env->spr[SPR_MQ] = 0; + return INT32_MIN; + } else { + env->spr[SPR_MQ] = tmp % arg2; + tmp /= (int32_t)arg2; + if ((int32_t)tmp != tmp) { + env->xer |= (1 << XER_OV) | (1 << XER_SO); + } else { + env->xer &= ~(1 << XER_OV); + } + return tmp; + } +} + +target_ulong helper_divs(CPUPPCState *env, target_ulong arg1, + target_ulong arg2) +{ + if (((int32_t)arg1 == INT32_MIN && (int32_t)arg2 == (int32_t)-1) || + (int32_t)arg2 == 0) { + env->spr[SPR_MQ] = 0; + return INT32_MIN; + } else { + env->spr[SPR_MQ] = (int32_t)arg1 % (int32_t)arg2; + return (int32_t)arg1 / (int32_t)arg2; + } +} + +target_ulong helper_divso(CPUPPCState *env, target_ulong arg1, + target_ulong arg2) +{ + if (((int32_t)arg1 == INT32_MIN && (int32_t)arg2 == (int32_t)-1) || + (int32_t)arg2 == 0) { + env->xer |= (1 << XER_OV) | (1 << XER_SO); + env->spr[SPR_MQ] = 0; + return INT32_MIN; + } else { + env->xer &= ~(1 << XER_OV); + env->spr[SPR_MQ] = (int32_t)arg1 % (int32_t)arg2; + return (int32_t)arg1 / (int32_t)arg2; + } +} + +/*****************************************************************************/ +/* 602 specific instructions */ +/* mfrom is the most crazy instruction ever seen, imho ! */ +/* Real implementation uses a ROM table. Do the same */ +/* Extremely decomposed: + * -arg / 256 + * return 256 * log10(10 + 1.0) + 0.5 + */ +#if !defined(CONFIG_USER_ONLY) +target_ulong helper_602_mfrom(target_ulong arg) +{ + if (likely(arg < 602)) { +#include "mfrom_table.c" + return mfrom_ROM_table[arg]; + } else { + return 0; + } +} +#endif + +/*****************************************************************************/ +/* Altivec extension helpers */ +#if defined(HOST_WORDS_BIGENDIAN) +#define HI_IDX 0 +#define LO_IDX 1 +#else +#define HI_IDX 1 +#define LO_IDX 0 +#endif + +#if defined(HOST_WORDS_BIGENDIAN) +#define VECTOR_FOR_INORDER_I(index, element) \ + for (index = 0; index < ARRAY_SIZE(r->element); index++) +#else +#define VECTOR_FOR_INORDER_I(index, element) \ + for (index = ARRAY_SIZE(r->element)-1; index >= 0; index--) +#endif + +/* If X is a NaN, store the corresponding QNaN into RESULT. Otherwise, + * execute the following block. */ +#define DO_HANDLE_NAN(result, x) \ + if (float32_is_any_nan(x)) { \ + CPU_FloatU __f; \ + __f.f = x; \ + __f.l = __f.l | (1 << 22); /* Set QNaN bit. */ \ + result = __f.f; \ + } else + +#define HANDLE_NAN1(result, x) \ + DO_HANDLE_NAN(result, x) +#define HANDLE_NAN2(result, x, y) \ + DO_HANDLE_NAN(result, x) DO_HANDLE_NAN(result, y) +#define HANDLE_NAN3(result, x, y, z) \ + DO_HANDLE_NAN(result, x) DO_HANDLE_NAN(result, y) DO_HANDLE_NAN(result, z) + +/* Saturating arithmetic helpers. */ +#define SATCVT(from, to, from_type, to_type, min, max) \ + static inline to_type cvt##from##to(from_type x, int *sat) \ + { \ + to_type r; \ + \ + if (x < (from_type)min) { \ + r = min; \ + *sat = 1; \ + } else if (x > (from_type)max) { \ + r = max; \ + *sat = 1; \ + } else { \ + r = x; \ + } \ + return r; \ + } +#define SATCVTU(from, to, from_type, to_type, min, max) \ + static inline to_type cvt##from##to(from_type x, int *sat) \ + { \ + to_type r; \ + \ + if (x > (from_type)max) { \ + r = max; \ + *sat = 1; \ + } else { \ + r = x; \ + } \ + return r; \ + } +SATCVT(sh, sb, int16_t, int8_t, INT8_MIN, INT8_MAX) +SATCVT(sw, sh, int32_t, int16_t, INT16_MIN, INT16_MAX) +SATCVT(sd, sw, int64_t, int32_t, INT32_MIN, INT32_MAX) + +SATCVTU(uh, ub, uint16_t, uint8_t, 0, UINT8_MAX) +SATCVTU(uw, uh, uint32_t, uint16_t, 0, UINT16_MAX) +SATCVTU(ud, uw, uint64_t, uint32_t, 0, UINT32_MAX) +SATCVT(sh, ub, int16_t, uint8_t, 0, UINT8_MAX) +SATCVT(sw, uh, int32_t, uint16_t, 0, UINT16_MAX) +SATCVT(sd, uw, int64_t, uint32_t, 0, UINT32_MAX) +#undef SATCVT +#undef SATCVTU + +void helper_lvsl(ppc_avr_t *r, target_ulong sh) +{ + int i, j = (sh & 0xf); + + VECTOR_FOR_INORDER_I(i, u8) { + r->u8[i] = j++; + } +} + +void helper_lvsr(ppc_avr_t *r, target_ulong sh) +{ + int i, j = 0x10 - (sh & 0xf); + + VECTOR_FOR_INORDER_I(i, u8) { + r->u8[i] = j++; + } +} + +void helper_mtvscr(CPUPPCState *env, ppc_avr_t *r) +{ +#if defined(HOST_WORDS_BIGENDIAN) + env->vscr = r->u32[3]; +#else + env->vscr = r->u32[0]; +#endif + set_flush_to_zero(vscr_nj, &env->vec_status); +} + +void helper_vaddcuw(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(r->u32); i++) { + r->u32[i] = ~a->u32[i] < b->u32[i]; + } +} + +#define VARITH_DO(name, op, element) \ + void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ + { \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + r->element[i] = a->element[i] op b->element[i]; \ + } \ + } +#define VARITH(suffix, element) \ + VARITH_DO(add##suffix, +, element) \ + VARITH_DO(sub##suffix, -, element) +VARITH(ubm, u8) +VARITH(uhm, u16) +VARITH(uwm, u32) +#undef VARITH_DO +#undef VARITH + +#define VARITHFP(suffix, func) \ + void helper_v##suffix(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, \ + ppc_avr_t *b) \ + { \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->f); i++) { \ + HANDLE_NAN2(r->f[i], a->f[i], b->f[i]) { \ + r->f[i] = func(a->f[i], b->f[i], &env->vec_status); \ + } \ + } \ + } +VARITHFP(addfp, float32_add) +VARITHFP(subfp, float32_sub) +#undef VARITHFP + +#define VARITHSAT_CASE(type, op, cvt, element) \ + { \ + type result = (type)a->element[i] op (type)b->element[i]; \ + r->element[i] = cvt(result, &sat); \ + } + +#define VARITHSAT_DO(name, op, optype, cvt, element) \ + void helper_v##name(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, \ + ppc_avr_t *b) \ + { \ + int sat = 0; \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + switch (sizeof(r->element[0])) { \ + case 1: \ + VARITHSAT_CASE(optype, op, cvt, element); \ + break; \ + case 2: \ + VARITHSAT_CASE(optype, op, cvt, element); \ + break; \ + case 4: \ + VARITHSAT_CASE(optype, op, cvt, element); \ + break; \ + } \ + } \ + if (sat) { \ + env->vscr |= (1 << VSCR_SAT); \ + } \ + } +#define VARITHSAT_SIGNED(suffix, element, optype, cvt) \ + VARITHSAT_DO(adds##suffix##s, +, optype, cvt, element) \ + VARITHSAT_DO(subs##suffix##s, -, optype, cvt, element) +#define VARITHSAT_UNSIGNED(suffix, element, optype, cvt) \ + VARITHSAT_DO(addu##suffix##s, +, optype, cvt, element) \ + VARITHSAT_DO(subu##suffix##s, -, optype, cvt, element) +VARITHSAT_SIGNED(b, s8, int16_t, cvtshsb) +VARITHSAT_SIGNED(h, s16, int32_t, cvtswsh) +VARITHSAT_SIGNED(w, s32, int64_t, cvtsdsw) +VARITHSAT_UNSIGNED(b, u8, uint16_t, cvtshub) +VARITHSAT_UNSIGNED(h, u16, uint32_t, cvtswuh) +VARITHSAT_UNSIGNED(w, u32, uint64_t, cvtsduw) +#undef VARITHSAT_CASE +#undef VARITHSAT_DO +#undef VARITHSAT_SIGNED +#undef VARITHSAT_UNSIGNED + +#define VAVG_DO(name, element, etype) \ + void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ + { \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + etype x = (etype)a->element[i] + (etype)b->element[i] + 1; \ + r->element[i] = x >> 1; \ + } \ + } + +#define VAVG(type, signed_element, signed_type, unsigned_element, \ + unsigned_type) \ + VAVG_DO(avgs##type, signed_element, signed_type) \ + VAVG_DO(avgu##type, unsigned_element, unsigned_type) +VAVG(b, s8, int16_t, u8, uint16_t) +VAVG(h, s16, int32_t, u16, uint32_t) +VAVG(w, s32, int64_t, u32, uint64_t) +#undef VAVG_DO +#undef VAVG + +#define VCF(suffix, cvt, element) \ + void helper_vcf##suffix(CPUPPCState *env, ppc_avr_t *r, \ + ppc_avr_t *b, uint32_t uim) \ + { \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->f); i++) { \ + float32 t = cvt(b->element[i], &env->vec_status); \ + r->f[i] = float32_scalbn(t, -uim, &env->vec_status); \ + } \ + } +VCF(ux, uint32_to_float32, u32) +VCF(sx, int32_to_float32, s32) +#undef VCF + +#define VCMP_DO(suffix, compare, element, record) \ + void helper_vcmp##suffix(CPUPPCState *env, ppc_avr_t *r, \ + ppc_avr_t *a, ppc_avr_t *b) \ + { \ + uint32_t ones = (uint32_t)-1; \ + uint32_t all = ones; \ + uint32_t none = 0; \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + uint32_t result = (a->element[i] compare b->element[i] ? \ + ones : 0x0); \ + switch (sizeof(a->element[0])) { \ + case 4: \ + r->u32[i] = result; \ + break; \ + case 2: \ + r->u16[i] = result; \ + break; \ + case 1: \ + r->u8[i] = result; \ + break; \ + } \ + all &= result; \ + none |= result; \ + } \ + if (record) { \ + env->crf[6] = ((all != 0) << 3) | ((none == 0) << 1); \ + } \ + } +#define VCMP(suffix, compare, element) \ + VCMP_DO(suffix, compare, element, 0) \ + VCMP_DO(suffix##_dot, compare, element, 1) +VCMP(equb, ==, u8) +VCMP(equh, ==, u16) +VCMP(equw, ==, u32) +VCMP(gtub, >, u8) +VCMP(gtuh, >, u16) +VCMP(gtuw, >, u32) +VCMP(gtsb, >, s8) +VCMP(gtsh, >, s16) +VCMP(gtsw, >, s32) +#undef VCMP_DO +#undef VCMP + +#define VCMPFP_DO(suffix, compare, order, record) \ + void helper_vcmp##suffix(CPUPPCState *env, ppc_avr_t *r, \ + ppc_avr_t *a, ppc_avr_t *b) \ + { \ + uint32_t ones = (uint32_t)-1; \ + uint32_t all = ones; \ + uint32_t none = 0; \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->f); i++) { \ + uint32_t result; \ + int rel = float32_compare_quiet(a->f[i], b->f[i], \ + &env->vec_status); \ + if (rel == float_relation_unordered) { \ + result = 0; \ + } else if (rel compare order) { \ + result = ones; \ + } else { \ + result = 0; \ + } \ + r->u32[i] = result; \ + all &= result; \ + none |= result; \ + } \ + if (record) { \ + env->crf[6] = ((all != 0) << 3) | ((none == 0) << 1); \ + } \ + } +#define VCMPFP(suffix, compare, order) \ + VCMPFP_DO(suffix, compare, order, 0) \ + VCMPFP_DO(suffix##_dot, compare, order, 1) +VCMPFP(eqfp, ==, float_relation_equal) +VCMPFP(gefp, !=, float_relation_less) +VCMPFP(gtfp, ==, float_relation_greater) +#undef VCMPFP_DO +#undef VCMPFP + +static inline void vcmpbfp_internal(CPUPPCState *env, ppc_avr_t *r, + ppc_avr_t *a, ppc_avr_t *b, int record) +{ + int i; + int all_in = 0; + + for (i = 0; i < ARRAY_SIZE(r->f); i++) { + int le_rel = float32_compare_quiet(a->f[i], b->f[i], &env->vec_status); + if (le_rel == float_relation_unordered) { + r->u32[i] = 0xc0000000; + /* ALL_IN does not need to be updated here. */ + } else { + float32 bneg = float32_chs(b->f[i]); + int ge_rel = float32_compare_quiet(a->f[i], bneg, &env->vec_status); + int le = le_rel != float_relation_greater; + int ge = ge_rel != float_relation_less; + + r->u32[i] = ((!le) << 31) | ((!ge) << 30); + all_in |= (!le | !ge); + } + } + if (record) { + env->crf[6] = (all_in == 0) << 1; + } +} + +void helper_vcmpbfp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + vcmpbfp_internal(env, r, a, b, 0); +} + +void helper_vcmpbfp_dot(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, + ppc_avr_t *b) +{ + vcmpbfp_internal(env, r, a, b, 1); +} + +#define VCT(suffix, satcvt, element) \ + void helper_vct##suffix(CPUPPCState *env, ppc_avr_t *r, \ + ppc_avr_t *b, uint32_t uim) \ + { \ + int i; \ + int sat = 0; \ + float_status s = env->vec_status; \ + \ + set_float_rounding_mode(float_round_to_zero, &s); \ + for (i = 0; i < ARRAY_SIZE(r->f); i++) { \ + if (float32_is_any_nan(b->f[i])) { \ + r->element[i] = 0; \ + } else { \ + float64 t = float32_to_float64(b->f[i], &s); \ + int64_t j; \ + \ + t = float64_scalbn(t, uim, &s); \ + j = float64_to_int64(t, &s); \ + r->element[i] = satcvt(j, &sat); \ + } \ + } \ + if (sat) { \ + env->vscr |= (1 << VSCR_SAT); \ + } \ + } +VCT(uxs, cvtsduw, u32) +VCT(sxs, cvtsdsw, s32) +#undef VCT + +void helper_vmaddfp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, + ppc_avr_t *c) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(r->f); i++) { + HANDLE_NAN3(r->f[i], a->f[i], b->f[i], c->f[i]) { + /* Need to do the computation in higher precision and round + * once at the end. */ + float64 af, bf, cf, t; + + af = float32_to_float64(a->f[i], &env->vec_status); + bf = float32_to_float64(b->f[i], &env->vec_status); + cf = float32_to_float64(c->f[i], &env->vec_status); + t = float64_mul(af, cf, &env->vec_status); + t = float64_add(t, bf, &env->vec_status); + r->f[i] = float64_to_float32(t, &env->vec_status); + } + } +} + +void helper_vmhaddshs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, + ppc_avr_t *b, ppc_avr_t *c) +{ + int sat = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(r->s16); i++) { + int32_t prod = a->s16[i] * b->s16[i]; + int32_t t = (int32_t)c->s16[i] + (prod >> 15); + + r->s16[i] = cvtswsh(t, &sat); + } + + if (sat) { + env->vscr |= (1 << VSCR_SAT); + } +} + +void helper_vmhraddshs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, + ppc_avr_t *b, ppc_avr_t *c) +{ + int sat = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(r->s16); i++) { + int32_t prod = a->s16[i] * b->s16[i] + 0x00004000; + int32_t t = (int32_t)c->s16[i] + (prod >> 15); + r->s16[i] = cvtswsh(t, &sat); + } + + if (sat) { + env->vscr |= (1 << VSCR_SAT); + } +} + +#define VMINMAX_DO(name, compare, element) \ + void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ + { \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + if (a->element[i] compare b->element[i]) { \ + r->element[i] = b->element[i]; \ + } else { \ + r->element[i] = a->element[i]; \ + } \ + } \ + } +#define VMINMAX(suffix, element) \ + VMINMAX_DO(min##suffix, >, element) \ + VMINMAX_DO(max##suffix, <, element) +VMINMAX(sb, s8) +VMINMAX(sh, s16) +VMINMAX(sw, s32) +VMINMAX(ub, u8) +VMINMAX(uh, u16) +VMINMAX(uw, u32) +#undef VMINMAX_DO +#undef VMINMAX + +#define VMINMAXFP(suffix, rT, rF) \ + void helper_v##suffix(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, \ + ppc_avr_t *b) \ + { \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->f); i++) { \ + HANDLE_NAN2(r->f[i], a->f[i], b->f[i]) { \ + if (float32_lt_quiet(a->f[i], b->f[i], \ + &env->vec_status)) { \ + r->f[i] = rT->f[i]; \ + } else { \ + r->f[i] = rF->f[i]; \ + } \ + } \ + } \ + } +VMINMAXFP(minfp, a, b) +VMINMAXFP(maxfp, b, a) +#undef VMINMAXFP + +void helper_vmladduhm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(r->s16); i++) { + int32_t prod = a->s16[i] * b->s16[i]; + r->s16[i] = (int16_t) (prod + c->s16[i]); + } +} + +#define VMRG_DO(name, element, highp) \ + void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ + { \ + ppc_avr_t result; \ + int i; \ + size_t n_elems = ARRAY_SIZE(r->element); \ + \ + for (i = 0; i < n_elems / 2; i++) { \ + if (highp) { \ + result.element[i*2+HI_IDX] = a->element[i]; \ + result.element[i*2+LO_IDX] = b->element[i]; \ + } else { \ + result.element[n_elems - i * 2 - (1 + HI_IDX)] = \ + b->element[n_elems - i - 1]; \ + result.element[n_elems - i * 2 - (1 + LO_IDX)] = \ + a->element[n_elems - i - 1]; \ + } \ + } \ + *r = result; \ + } +#if defined(HOST_WORDS_BIGENDIAN) +#define MRGHI 0 +#define MRGLO 1 +#else +#define MRGHI 1 +#define MRGLO 0 +#endif +#define VMRG(suffix, element) \ + VMRG_DO(mrgl##suffix, element, MRGHI) \ + VMRG_DO(mrgh##suffix, element, MRGLO) +VMRG(b, u8) +VMRG(h, u16) +VMRG(w, u32) +#undef VMRG_DO +#undef VMRG +#undef MRGHI +#undef MRGLO + +void helper_vmsummbm(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, + ppc_avr_t *b, ppc_avr_t *c) +{ + int32_t prod[16]; + int i; + + for (i = 0; i < ARRAY_SIZE(r->s8); i++) { + prod[i] = (int32_t)a->s8[i] * b->u8[i]; + } + + VECTOR_FOR_INORDER_I(i, s32) { + r->s32[i] = c->s32[i] + prod[4 * i] + prod[4 * i + 1] + + prod[4 * i + 2] + prod[4 * i + 3]; + } +} + +void helper_vmsumshm(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, + ppc_avr_t *b, ppc_avr_t *c) +{ + int32_t prod[8]; + int i; + + for (i = 0; i < ARRAY_SIZE(r->s16); i++) { + prod[i] = a->s16[i] * b->s16[i]; + } + + VECTOR_FOR_INORDER_I(i, s32) { + r->s32[i] = c->s32[i] + prod[2 * i] + prod[2 * i + 1]; + } +} + +void helper_vmsumshs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, + ppc_avr_t *b, ppc_avr_t *c) +{ + int32_t prod[8]; + int i; + int sat = 0; + + for (i = 0; i < ARRAY_SIZE(r->s16); i++) { + prod[i] = (int32_t)a->s16[i] * b->s16[i]; + } + + VECTOR_FOR_INORDER_I(i, s32) { + int64_t t = (int64_t)c->s32[i] + prod[2 * i] + prod[2 * i + 1]; + + r->u32[i] = cvtsdsw(t, &sat); + } + + if (sat) { + env->vscr |= (1 << VSCR_SAT); + } +} + +void helper_vmsumubm(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, + ppc_avr_t *b, ppc_avr_t *c) +{ + uint16_t prod[16]; + int i; + + for (i = 0; i < ARRAY_SIZE(r->u8); i++) { + prod[i] = a->u8[i] * b->u8[i]; + } + + VECTOR_FOR_INORDER_I(i, u32) { + r->u32[i] = c->u32[i] + prod[4 * i] + prod[4 * i + 1] + + prod[4 * i + 2] + prod[4 * i + 3]; + } +} + +void helper_vmsumuhm(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, + ppc_avr_t *b, ppc_avr_t *c) +{ + uint32_t prod[8]; + int i; + + for (i = 0; i < ARRAY_SIZE(r->u16); i++) { + prod[i] = a->u16[i] * b->u16[i]; + } + + VECTOR_FOR_INORDER_I(i, u32) { + r->u32[i] = c->u32[i] + prod[2 * i] + prod[2 * i + 1]; + } +} + +void helper_vmsumuhs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, + ppc_avr_t *b, ppc_avr_t *c) +{ + uint32_t prod[8]; + int i; + int sat = 0; + + for (i = 0; i < ARRAY_SIZE(r->u16); i++) { + prod[i] = a->u16[i] * b->u16[i]; + } + + VECTOR_FOR_INORDER_I(i, s32) { + uint64_t t = (uint64_t)c->u32[i] + prod[2 * i] + prod[2 * i + 1]; + + r->u32[i] = cvtuduw(t, &sat); + } + + if (sat) { + env->vscr |= (1 << VSCR_SAT); + } +} + +#define VMUL_DO(name, mul_element, prod_element, evenp) \ + void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ + { \ + int i; \ + \ + VECTOR_FOR_INORDER_I(i, prod_element) { \ + if (evenp) { \ + r->prod_element[i] = a->mul_element[i * 2 + HI_IDX] * \ + b->mul_element[i * 2 + HI_IDX]; \ + } else { \ + r->prod_element[i] = a->mul_element[i * 2 + LO_IDX] * \ + b->mul_element[i * 2 + LO_IDX]; \ + } \ + } \ + } +#define VMUL(suffix, mul_element, prod_element) \ + VMUL_DO(mule##suffix, mul_element, prod_element, 1) \ + VMUL_DO(mulo##suffix, mul_element, prod_element, 0) +VMUL(sb, s8, s16) +VMUL(sh, s16, s32) +VMUL(ub, u8, u16) +VMUL(uh, u16, u32) +#undef VMUL_DO +#undef VMUL + +void helper_vnmsubfp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, + ppc_avr_t *b, ppc_avr_t *c) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(r->f); i++) { + HANDLE_NAN3(r->f[i], a->f[i], b->f[i], c->f[i]) { + /* Need to do the computation is higher precision and round + * once at the end. */ + float64 af, bf, cf, t; + + af = float32_to_float64(a->f[i], &env->vec_status); + bf = float32_to_float64(b->f[i], &env->vec_status); + cf = float32_to_float64(c->f[i], &env->vec_status); + t = float64_mul(af, cf, &env->vec_status); + t = float64_sub(t, bf, &env->vec_status); + t = float64_chs(t); + r->f[i] = float64_to_float32(t, &env->vec_status); + } + } +} + +void helper_vperm(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, + ppc_avr_t *c) +{ + ppc_avr_t result; + int i; + + VECTOR_FOR_INORDER_I(i, u8) { + int s = c->u8[i] & 0x1f; +#if defined(HOST_WORDS_BIGENDIAN) + int index = s & 0xf; +#else + int index = 15 - (s & 0xf); +#endif + + if (s & 0x10) { + result.u8[i] = b->u8[index]; + } else { + result.u8[i] = a->u8[index]; + } + } + *r = result; +} + +#if defined(HOST_WORDS_BIGENDIAN) +#define PKBIG 1 +#else +#define PKBIG 0 +#endif +void helper_vpkpx(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int i, j; + ppc_avr_t result; +#if defined(HOST_WORDS_BIGENDIAN) + const ppc_avr_t *x[2] = { a, b }; +#else + const ppc_avr_t *x[2] = { b, a }; +#endif + + VECTOR_FOR_INORDER_I(i, u64) { + VECTOR_FOR_INORDER_I(j, u32) { + uint32_t e = x[i]->u32[j]; + + result.u16[4*i+j] = (((e >> 9) & 0xfc00) | + ((e >> 6) & 0x3e0) | + ((e >> 3) & 0x1f)); + } + } + *r = result; +} + +#define VPK(suffix, from, to, cvt, dosat) \ + void helper_vpk##suffix(CPUPPCState *env, ppc_avr_t *r, \ + ppc_avr_t *a, ppc_avr_t *b) \ + { \ + int i; \ + int sat = 0; \ + ppc_avr_t result; \ + ppc_avr_t *a0 = PKBIG ? a : b; \ + ppc_avr_t *a1 = PKBIG ? b : a; \ + \ + VECTOR_FOR_INORDER_I(i, from) { \ + result.to[i] = cvt(a0->from[i], &sat); \ + result.to[i+ARRAY_SIZE(r->from)] = cvt(a1->from[i], &sat); \ + } \ + *r = result; \ + if (dosat && sat) { \ + env->vscr |= (1 << VSCR_SAT); \ + } \ + } +#define I(x, y) (x) +VPK(shss, s16, s8, cvtshsb, 1) +VPK(shus, s16, u8, cvtshub, 1) +VPK(swss, s32, s16, cvtswsh, 1) +VPK(swus, s32, u16, cvtswuh, 1) +VPK(uhus, u16, u8, cvtuhub, 1) +VPK(uwus, u32, u16, cvtuwuh, 1) +VPK(uhum, u16, u8, I, 0) +VPK(uwum, u32, u16, I, 0) +#undef I +#undef VPK +#undef PKBIG + +void helper_vrefp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *b) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(r->f); i++) { + HANDLE_NAN1(r->f[i], b->f[i]) { + r->f[i] = float32_div(float32_one, b->f[i], &env->vec_status); + } + } +} + +#define VRFI(suffix, rounding) \ + void helper_vrfi##suffix(CPUPPCState *env, ppc_avr_t *r, \ + ppc_avr_t *b) \ + { \ + int i; \ + float_status s = env->vec_status; \ + \ + set_float_rounding_mode(rounding, &s); \ + for (i = 0; i < ARRAY_SIZE(r->f); i++) { \ + HANDLE_NAN1(r->f[i], b->f[i]) { \ + r->f[i] = float32_round_to_int (b->f[i], &s); \ + } \ + } \ + } +VRFI(n, float_round_nearest_even) +VRFI(m, float_round_down) +VRFI(p, float_round_up) +VRFI(z, float_round_to_zero) +#undef VRFI + +#define VROTATE(suffix, element) \ + void helper_vrl##suffix(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ + { \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + unsigned int mask = ((1 << \ + (3 + (sizeof(a->element[0]) >> 1))) \ + - 1); \ + unsigned int shift = b->element[i] & mask; \ + r->element[i] = (a->element[i] << shift) | \ + (a->element[i] >> (sizeof(a->element[0]) * 8 - shift)); \ + } \ + } +VROTATE(b, u8) +VROTATE(h, u16) +VROTATE(w, u32) +#undef VROTATE + +void helper_vrsqrtefp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *b) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(r->f); i++) { + HANDLE_NAN1(r->f[i], b->f[i]) { + float32 t = float32_sqrt(b->f[i], &env->vec_status); + + r->f[i] = float32_div(float32_one, t, &env->vec_status); + } + } +} + +void helper_vsel(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, + ppc_avr_t *c) +{ + r->u64[0] = (a->u64[0] & ~c->u64[0]) | (b->u64[0] & c->u64[0]); + r->u64[1] = (a->u64[1] & ~c->u64[1]) | (b->u64[1] & c->u64[1]); +} + +void helper_vexptefp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *b) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(r->f); i++) { + HANDLE_NAN1(r->f[i], b->f[i]) { + r->f[i] = float32_exp2(b->f[i], &env->vec_status); + } + } +} + +void helper_vlogefp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *b) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(r->f); i++) { + HANDLE_NAN1(r->f[i], b->f[i]) { + r->f[i] = float32_log2(b->f[i], &env->vec_status); + } + } +} + +#if defined(HOST_WORDS_BIGENDIAN) +#define LEFT 0 +#define RIGHT 1 +#else +#define LEFT 1 +#define RIGHT 0 +#endif +/* The specification says that the results are undefined if all of the + * shift counts are not identical. We check to make sure that they are + * to conform to what real hardware appears to do. */ +#define VSHIFT(suffix, leftp) \ + void helper_vs##suffix(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ + { \ + int shift = b->u8[LO_IDX*15] & 0x7; \ + int doit = 1; \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->u8); i++) { \ + doit = doit && ((b->u8[i] & 0x7) == shift); \ + } \ + if (doit) { \ + if (shift == 0) { \ + *r = *a; \ + } else if (leftp) { \ + uint64_t carry = a->u64[LO_IDX] >> (64 - shift); \ + \ + r->u64[HI_IDX] = (a->u64[HI_IDX] << shift) | carry; \ + r->u64[LO_IDX] = a->u64[LO_IDX] << shift; \ + } else { \ + uint64_t carry = a->u64[HI_IDX] << (64 - shift); \ + \ + r->u64[LO_IDX] = (a->u64[LO_IDX] >> shift) | carry; \ + r->u64[HI_IDX] = a->u64[HI_IDX] >> shift; \ + } \ + } \ + } +VSHIFT(l, LEFT) +VSHIFT(r, RIGHT) +#undef VSHIFT +#undef LEFT +#undef RIGHT + +#define VSL(suffix, element) \ + void helper_vsl##suffix(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ + { \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + unsigned int mask = ((1 << \ + (3 + (sizeof(a->element[0]) >> 1))) \ + - 1); \ + unsigned int shift = b->element[i] & mask; \ + \ + r->element[i] = a->element[i] << shift; \ + } \ + } +VSL(b, u8) +VSL(h, u16) +VSL(w, u32) +#undef VSL + +void helper_vsldoi(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t shift) +{ + int sh = shift & 0xf; + int i; + ppc_avr_t result; + +#if defined(HOST_WORDS_BIGENDIAN) + for (i = 0; i < ARRAY_SIZE(r->u8); i++) { + int index = sh + i; + if (index > 0xf) { + result.u8[i] = b->u8[index - 0x10]; + } else { + result.u8[i] = a->u8[index]; + } + } +#else + for (i = 0; i < ARRAY_SIZE(r->u8); i++) { + int index = (16 - sh) + i; + if (index > 0xf) { + result.u8[i] = a->u8[index - 0x10]; + } else { + result.u8[i] = b->u8[index]; + } + } +#endif + *r = result; +} + +void helper_vslo(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int sh = (b->u8[LO_IDX*0xf] >> 3) & 0xf; + +#if defined(HOST_WORDS_BIGENDIAN) + memmove(&r->u8[0], &a->u8[sh], 16 - sh); + memset(&r->u8[16-sh], 0, sh); +#else + memmove(&r->u8[sh], &a->u8[0], 16 - sh); + memset(&r->u8[0], 0, sh); +#endif +} + +/* Experimental testing shows that hardware masks the immediate. */ +#define _SPLAT_MASKED(element) (splat & (ARRAY_SIZE(r->element) - 1)) +#if defined(HOST_WORDS_BIGENDIAN) +#define SPLAT_ELEMENT(element) _SPLAT_MASKED(element) +#else +#define SPLAT_ELEMENT(element) \ + (ARRAY_SIZE(r->element) - 1 - _SPLAT_MASKED(element)) +#endif +#define VSPLT(suffix, element) \ + void helper_vsplt##suffix(ppc_avr_t *r, ppc_avr_t *b, uint32_t splat) \ + { \ + uint32_t s = b->element[SPLAT_ELEMENT(element)]; \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + r->element[i] = s; \ + } \ + } +VSPLT(b, u8) +VSPLT(h, u16) +VSPLT(w, u32) +#undef VSPLT +#undef SPLAT_ELEMENT +#undef _SPLAT_MASKED + +#define VSPLTI(suffix, element, splat_type) \ + void helper_vspltis##suffix(ppc_avr_t *r, uint32_t splat) \ + { \ + splat_type x = (int8_t)(splat << 3) >> 3; \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + r->element[i] = x; \ + } \ + } +VSPLTI(b, s8, int8_t) +VSPLTI(h, s16, int16_t) +VSPLTI(w, s32, int32_t) +#undef VSPLTI + +#define VSR(suffix, element) \ + void helper_vsr##suffix(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ + { \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + unsigned int mask = ((1 << \ + (3 + (sizeof(a->element[0]) >> 1))) \ + - 1); \ + unsigned int shift = b->element[i] & mask; \ + \ + r->element[i] = a->element[i] >> shift; \ + } \ + } +VSR(ab, s8) +VSR(ah, s16) +VSR(aw, s32) +VSR(b, u8) +VSR(h, u16) +VSR(w, u32) +#undef VSR + +void helper_vsro(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int sh = (b->u8[LO_IDX * 0xf] >> 3) & 0xf; + +#if defined(HOST_WORDS_BIGENDIAN) + memmove(&r->u8[sh], &a->u8[0], 16 - sh); + memset(&r->u8[0], 0, sh); +#else + memmove(&r->u8[0], &a->u8[sh], 16 - sh); + memset(&r->u8[16 - sh], 0, sh); +#endif +} + +void helper_vsubcuw(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(r->u32); i++) { + r->u32[i] = a->u32[i] >= b->u32[i]; + } +} + +void helper_vsumsws(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int64_t t; + int i, upper; + ppc_avr_t result; + int sat = 0; + +#if defined(HOST_WORDS_BIGENDIAN) + upper = ARRAY_SIZE(r->s32)-1; +#else + upper = 0; +#endif + t = (int64_t)b->s32[upper]; + for (i = 0; i < ARRAY_SIZE(r->s32); i++) { + t += a->s32[i]; + result.s32[i] = 0; + } + result.s32[upper] = cvtsdsw(t, &sat); + *r = result; + + if (sat) { + env->vscr |= (1 << VSCR_SAT); + } +} + +void helper_vsum2sws(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int i, j, upper; + ppc_avr_t result; + int sat = 0; + +#if defined(HOST_WORDS_BIGENDIAN) + upper = 1; +#else + upper = 0; +#endif + for (i = 0; i < ARRAY_SIZE(r->u64); i++) { + int64_t t = (int64_t)b->s32[upper + i * 2]; + + result.u64[i] = 0; + for (j = 0; j < ARRAY_SIZE(r->u64); j++) { + t += a->s32[2 * i + j]; + } + result.s32[upper + i * 2] = cvtsdsw(t, &sat); + } + + *r = result; + if (sat) { + env->vscr |= (1 << VSCR_SAT); + } +} + +void helper_vsum4sbs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int i, j; + int sat = 0; + + for (i = 0; i < ARRAY_SIZE(r->s32); i++) { + int64_t t = (int64_t)b->s32[i]; + + for (j = 0; j < ARRAY_SIZE(r->s32); j++) { + t += a->s8[4 * i + j]; + } + r->s32[i] = cvtsdsw(t, &sat); + } + + if (sat) { + env->vscr |= (1 << VSCR_SAT); + } +} + +void helper_vsum4shs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int sat = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(r->s32); i++) { + int64_t t = (int64_t)b->s32[i]; + + t += a->s16[2 * i] + a->s16[2 * i + 1]; + r->s32[i] = cvtsdsw(t, &sat); + } + + if (sat) { + env->vscr |= (1 << VSCR_SAT); + } +} + +void helper_vsum4ubs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) +{ + int i, j; + int sat = 0; + + for (i = 0; i < ARRAY_SIZE(r->u32); i++) { + uint64_t t = (uint64_t)b->u32[i]; + + for (j = 0; j < ARRAY_SIZE(r->u32); j++) { + t += a->u8[4 * i + j]; + } + r->u32[i] = cvtuduw(t, &sat); + } + + if (sat) { + env->vscr |= (1 << VSCR_SAT); + } +} + +#if defined(HOST_WORDS_BIGENDIAN) +#define UPKHI 1 +#define UPKLO 0 +#else +#define UPKHI 0 +#define UPKLO 1 +#endif +#define VUPKPX(suffix, hi) \ + void helper_vupk##suffix(ppc_avr_t *r, ppc_avr_t *b) \ + { \ + int i; \ + ppc_avr_t result; \ + \ + for (i = 0; i < ARRAY_SIZE(r->u32); i++) { \ + uint16_t e = b->u16[hi ? i : i+4]; \ + uint8_t a = (e >> 15) ? 0xff : 0; \ + uint8_t r = (e >> 10) & 0x1f; \ + uint8_t g = (e >> 5) & 0x1f; \ + uint8_t b = e & 0x1f; \ + \ + result.u32[i] = (a << 24) | (r << 16) | (g << 8) | b; \ + } \ + *r = result; \ + } +VUPKPX(lpx, UPKLO) +VUPKPX(hpx, UPKHI) +#undef VUPKPX + +#define VUPK(suffix, unpacked, packee, hi) \ + void helper_vupk##suffix(ppc_avr_t *r, ppc_avr_t *b) \ + { \ + int i; \ + ppc_avr_t result; \ + \ + if (hi) { \ + for (i = 0; i < ARRAY_SIZE(r->unpacked); i++) { \ + result.unpacked[i] = b->packee[i]; \ + } \ + } else { \ + for (i = ARRAY_SIZE(r->unpacked); i < ARRAY_SIZE(r->packee); \ + i++) { \ + result.unpacked[i - ARRAY_SIZE(r->unpacked)] = b->packee[i]; \ + } \ + } \ + *r = result; \ + } +VUPK(hsb, s16, s8, UPKHI) +VUPK(hsh, s32, s16, UPKHI) +VUPK(lsb, s16, s8, UPKLO) +VUPK(lsh, s32, s16, UPKLO) +#undef VUPK +#undef UPKHI +#undef UPKLO + +#undef DO_HANDLE_NAN +#undef HANDLE_NAN1 +#undef HANDLE_NAN2 +#undef HANDLE_NAN3 +#undef VECTOR_FOR_INORDER_I +#undef HI_IDX +#undef LO_IDX + +/*****************************************************************************/ +/* SPE extension helpers */ +/* Use a table to make this quicker */ +static const uint8_t hbrev[16] = { + 0x0, 0x8, 0x4, 0xC, 0x2, 0xA, 0x6, 0xE, + 0x1, 0x9, 0x5, 0xD, 0x3, 0xB, 0x7, 0xF, +}; + +static inline uint8_t byte_reverse(uint8_t val) +{ + return hbrev[val >> 4] | (hbrev[val & 0xF] << 4); +} + +static inline uint32_t word_reverse(uint32_t val) +{ + return byte_reverse(val >> 24) | (byte_reverse(val >> 16) << 8) | + (byte_reverse(val >> 8) << 16) | (byte_reverse(val) << 24); +} + +#define MASKBITS 16 /* Random value - to be fixed (implementation dependent) */ +target_ulong helper_brinc(target_ulong arg1, target_ulong arg2) +{ + uint32_t a, b, d, mask; + + mask = UINT32_MAX >> (32 - MASKBITS); + a = arg1 & mask; + b = arg2 & mask; + d = word_reverse(1 + word_reverse(a | ~b)); + return (arg1 & ~mask) | (d & b); +} + +uint32_t helper_cntlsw32(uint32_t val) +{ + if (val & 0x80000000) { + return clz32(~val); + } else { + return clz32(val); + } +} + +uint32_t helper_cntlzw32(uint32_t val) +{ + return clz32(val); +} + +/* 440 specific */ +target_ulong helper_dlmzb(CPUPPCState *env, target_ulong high, + target_ulong low, uint32_t update_Rc) +{ + target_ulong mask; + int i; + + i = 1; + for (mask = 0xFF000000; mask != 0; mask = mask >> 8) { + if ((high & mask) == 0) { + if (update_Rc) { + env->crf[0] = 0x4; + } + goto done; + } + i++; + } + for (mask = 0xFF000000; mask != 0; mask = mask >> 8) { + if ((low & mask) == 0) { + if (update_Rc) { + env->crf[0] = 0x8; + } + goto done; + } + i++; + } + if (update_Rc) { + env->crf[0] = 0x2; + } + done: + env->xer = (env->xer & ~0x7F) | i; + if (update_Rc) { + env->crf[0] |= xer_so; + } + return i; +} diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index c09cc39c78..b6ef72d16b 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -18,6 +18,7 @@ #include <sys/types.h> #include <sys/ioctl.h> #include <sys/mman.h> +#include <sys/vfs.h> #include <linux/kvm.h> @@ -167,10 +168,217 @@ static int kvm_booke206_tlb_init(CPUPPCState *env) return 0; } + +#if defined(TARGET_PPC64) +static void kvm_get_fallback_smmu_info(CPUPPCState *env, + struct kvm_ppc_smmu_info *info) +{ + memset(info, 0, sizeof(*info)); + + /* We don't have the new KVM_PPC_GET_SMMU_INFO ioctl, so + * need to "guess" what the supported page sizes are. + * + * For that to work we make a few assumptions: + * + * - If KVM_CAP_PPC_GET_PVINFO is supported we are running "PR" + * KVM which only supports 4K and 16M pages, but supports them + * regardless of the backing store characteritics. We also don't + * support 1T segments. + * + * This is safe as if HV KVM ever supports that capability or PR + * KVM grows supports for more page/segment sizes, those versions + * will have implemented KVM_CAP_PPC_GET_SMMU_INFO and thus we + * will not hit this fallback + * + * - Else we are running HV KVM. This means we only support page + * sizes that fit in the backing store. Additionally we only + * advertize 64K pages if the processor is ARCH 2.06 and we assume + * P7 encodings for the SLB and hash table. Here too, we assume + * support for any newer processor will mean a kernel that + * implements KVM_CAP_PPC_GET_SMMU_INFO and thus doesn't hit + * this fallback. + */ + if (kvm_check_extension(env->kvm_state, KVM_CAP_PPC_GET_PVINFO)) { + /* No flags */ + info->flags = 0; + info->slb_size = 64; + + /* Standard 4k base page size segment */ + info->sps[0].page_shift = 12; + info->sps[0].slb_enc = 0; + info->sps[0].enc[0].page_shift = 12; + info->sps[0].enc[0].pte_enc = 0; + + /* Standard 16M large page size segment */ + info->sps[1].page_shift = 24; + info->sps[1].slb_enc = SLB_VSID_L; + info->sps[1].enc[0].page_shift = 24; + info->sps[1].enc[0].pte_enc = 0; + } else { + int i = 0; + + /* HV KVM has backing store size restrictions */ + info->flags = KVM_PPC_PAGE_SIZES_REAL; + + if (env->mmu_model & POWERPC_MMU_1TSEG) { + info->flags |= KVM_PPC_1T_SEGMENTS; + } + + if (env->mmu_model == POWERPC_MMU_2_06) { + info->slb_size = 32; + } else { + info->slb_size = 64; + } + + /* Standard 4k base page size segment */ + info->sps[i].page_shift = 12; + info->sps[i].slb_enc = 0; + info->sps[i].enc[0].page_shift = 12; + info->sps[i].enc[0].pte_enc = 0; + i++; + + /* 64K on MMU 2.06 */ + if (env->mmu_model == POWERPC_MMU_2_06) { + info->sps[i].page_shift = 16; + info->sps[i].slb_enc = 0x110; + info->sps[i].enc[0].page_shift = 16; + info->sps[i].enc[0].pte_enc = 1; + i++; + } + + /* Standard 16M large page size segment */ + info->sps[i].page_shift = 24; + info->sps[i].slb_enc = SLB_VSID_L; + info->sps[i].enc[0].page_shift = 24; + info->sps[i].enc[0].pte_enc = 0; + } +} + +static void kvm_get_smmu_info(CPUPPCState *env, struct kvm_ppc_smmu_info *info) +{ + int ret; + + if (kvm_check_extension(env->kvm_state, KVM_CAP_PPC_GET_SMMU_INFO)) { + ret = kvm_vm_ioctl(env->kvm_state, KVM_PPC_GET_SMMU_INFO, info); + if (ret == 0) { + return; + } + } + + kvm_get_fallback_smmu_info(env, info); +} + +static long getrampagesize(void) +{ + struct statfs fs; + int ret; + + if (!mem_path) { + /* guest RAM is backed by normal anonymous pages */ + return getpagesize(); + } + + do { + ret = statfs(mem_path, &fs); + } while (ret != 0 && errno == EINTR); + + if (ret != 0) { + fprintf(stderr, "Couldn't statfs() memory path: %s\n", + strerror(errno)); + exit(1); + } + +#define HUGETLBFS_MAGIC 0x958458f6 + + if (fs.f_type != HUGETLBFS_MAGIC) { + /* Explicit mempath, but it's ordinary pages */ + return getpagesize(); + } + + /* It's hugepage, return the huge page size */ + return fs.f_bsize; +} + +static bool kvm_valid_page_size(uint32_t flags, long rampgsize, uint32_t shift) +{ + if (!(flags & KVM_PPC_PAGE_SIZES_REAL)) { + return true; + } + + return (1ul << shift) <= rampgsize; +} + +static void kvm_fixup_page_sizes(CPUPPCState *env) +{ + static struct kvm_ppc_smmu_info smmu_info; + static bool has_smmu_info; + long rampagesize; + int iq, ik, jq, jk; + + /* We only handle page sizes for 64-bit server guests for now */ + if (!(env->mmu_model & POWERPC_MMU_64)) { + return; + } + + /* Collect MMU info from kernel if not already */ + if (!has_smmu_info) { + kvm_get_smmu_info(env, &smmu_info); + has_smmu_info = true; + } + + rampagesize = getrampagesize(); + + /* Convert to QEMU form */ + memset(&env->sps, 0, sizeof(env->sps)); + + for (ik = iq = 0; ik < KVM_PPC_PAGE_SIZES_MAX_SZ; ik++) { + struct ppc_one_seg_page_size *qsps = &env->sps.sps[iq]; + struct kvm_ppc_one_seg_page_size *ksps = &smmu_info.sps[ik]; + + if (!kvm_valid_page_size(smmu_info.flags, rampagesize, + ksps->page_shift)) { + continue; + } + qsps->page_shift = ksps->page_shift; + qsps->slb_enc = ksps->slb_enc; + for (jk = jq = 0; jk < KVM_PPC_PAGE_SIZES_MAX_SZ; jk++) { + if (!kvm_valid_page_size(smmu_info.flags, rampagesize, + ksps->enc[jk].page_shift)) { + continue; + } + qsps->enc[jq].page_shift = ksps->enc[jk].page_shift; + qsps->enc[jq].pte_enc = ksps->enc[jk].pte_enc; + if (++jq >= PPC_PAGE_SIZES_MAX_SZ) { + break; + } + } + if (++iq >= PPC_PAGE_SIZES_MAX_SZ) { + break; + } + } + env->slb_nr = smmu_info.slb_size; + if (smmu_info.flags & KVM_PPC_1T_SEGMENTS) { + env->mmu_model |= POWERPC_MMU_1TSEG; + } else { + env->mmu_model &= ~POWERPC_MMU_1TSEG; + } +} +#else /* defined (TARGET_PPC64) */ + +static inline void kvm_fixup_page_sizes(CPUPPCState *env) +{ +} + +#endif /* !defined (TARGET_PPC64) */ + int kvm_arch_init_vcpu(CPUPPCState *cenv) { int ret; + /* Gather server mmu info from KVM and update the CPU state */ + kvm_fixup_page_sizes(cenv); + + /* Synchronize sregs with kvm */ ret = kvm_arch_sync_sregs(cenv); if (ret) { return ret; diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h index 34ecad3e26..e2f8703853 100644 --- a/target-ppc/kvm_ppc.h +++ b/target-ppc/kvm_ppc.h @@ -58,6 +58,11 @@ static inline int kvmppc_get_hypercall(CPUPPCState *env, uint8_t *buf, int buf_l return -1; } +static inline int kvmppc_read_segment_page_sizes(uint32_t *prop, int maxcells) +{ + return -1; +} + static inline int kvmppc_set_interrupt(CPUPPCState *env, int irq, int level) { return -1; diff --git a/target-ppc/mem_helper.c b/target-ppc/mem_helper.c new file mode 100644 index 0000000000..5b5f1bdd23 --- /dev/null +++ b/target-ppc/mem_helper.c @@ -0,0 +1,295 @@ +/* + * PowerPC memory access emulation helpers for QEMU. + * + * Copyright (c) 2003-2007 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, see <http://www.gnu.org/licenses/>. + */ +#include "cpu.h" +#include "host-utils.h" +#include "helper.h" + +#include "helper_regs.h" + +#if !defined(CONFIG_USER_ONLY) +#include "softmmu_exec.h" +#endif /* !defined(CONFIG_USER_ONLY) */ + +//#define DEBUG_OP + +/*****************************************************************************/ +/* Memory load and stores */ + +static inline target_ulong addr_add(CPUPPCState *env, target_ulong addr, + target_long arg) +{ +#if defined(TARGET_PPC64) + if (!msr_is_64bit(env, env->msr)) { + return (uint32_t)(addr + arg); + } else +#endif + { + return addr + arg; + } +} + +void helper_lmw(CPUPPCState *env, target_ulong addr, uint32_t reg) +{ + for (; reg < 32; reg++) { + if (msr_le) { + env->gpr[reg] = bswap32(cpu_ldl_data(env, addr)); + } else { + env->gpr[reg] = cpu_ldl_data(env, addr); + } + addr = addr_add(env, addr, 4); + } +} + +void helper_stmw(CPUPPCState *env, target_ulong addr, uint32_t reg) +{ + for (; reg < 32; reg++) { + if (msr_le) { + cpu_stl_data(env, addr, bswap32((uint32_t)env->gpr[reg])); + } else { + cpu_stl_data(env, addr, (uint32_t)env->gpr[reg]); + } + addr = addr_add(env, addr, 4); + } +} + +void helper_lsw(CPUPPCState *env, target_ulong addr, uint32_t nb, uint32_t reg) +{ + int sh; + + for (; nb > 3; nb -= 4) { + env->gpr[reg] = cpu_ldl_data(env, addr); + reg = (reg + 1) % 32; + addr = addr_add(env, addr, 4); + } + if (unlikely(nb > 0)) { + env->gpr[reg] = 0; + for (sh = 24; nb > 0; nb--, sh -= 8) { + env->gpr[reg] |= cpu_ldub_data(env, addr) << sh; + addr = addr_add(env, addr, 1); + } + } +} +/* PPC32 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... + */ +void helper_lswx(CPUPPCState *env, target_ulong addr, uint32_t reg, + uint32_t ra, uint32_t rb) +{ + if (likely(xer_bc != 0)) { + if (unlikely((ra != 0 && reg < ra && (reg + xer_bc) > ra) || + (reg < rb && (reg + xer_bc) > rb))) { + helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL | + POWERPC_EXCP_INVAL_LSWX); + } else { + helper_lsw(env, addr, xer_bc, reg); + } + } +} + +void helper_stsw(CPUPPCState *env, target_ulong addr, uint32_t nb, + uint32_t reg) +{ + int sh; + + for (; nb > 3; nb -= 4) { + cpu_stl_data(env, addr, env->gpr[reg]); + reg = (reg + 1) % 32; + addr = addr_add(env, addr, 4); + } + if (unlikely(nb > 0)) { + for (sh = 24; nb > 0; nb--, sh -= 8) { + cpu_stb_data(env, addr, (env->gpr[reg] >> sh) & 0xFF); + addr = addr_add(env, addr, 1); + } + } +} + +static void do_dcbz(CPUPPCState *env, target_ulong addr, int dcache_line_size) +{ + int i; + + addr &= ~(dcache_line_size - 1); + for (i = 0; i < dcache_line_size; i += 4) { + cpu_stl_data(env, addr + i, 0); + } + if (env->reserve_addr == addr) { + env->reserve_addr = (target_ulong)-1ULL; + } +} + +void helper_dcbz(CPUPPCState *env, target_ulong addr) +{ + do_dcbz(env, addr, env->dcache_line_size); +} + +void helper_dcbz_970(CPUPPCState *env, target_ulong addr) +{ + if (((env->spr[SPR_970_HID5] >> 7) & 0x3) == 1) { + do_dcbz(env, addr, 32); + } else { + do_dcbz(env, addr, env->dcache_line_size); + } +} + +void helper_icbi(CPUPPCState *env, target_ulong addr) +{ + addr &= ~(env->dcache_line_size - 1); + /* 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". + */ + cpu_ldl_data(env, addr); +} + +/* XXX: to be tested */ +target_ulong helper_lscbx(CPUPPCState *env, target_ulong addr, uint32_t reg, + uint32_t ra, uint32_t rb) +{ + int i, c, d; + + d = 24; + for (i = 0; i < xer_bc; i++) { + c = cpu_ldub_data(env, addr); + addr = addr_add(env, addr, 1); + /* ra (if not 0) and rb are never modified */ + if (likely(reg != rb && (ra == 0 || reg != ra))) { + env->gpr[reg] = (env->gpr[reg] & ~(0xFF << d)) | (c << d); + } + if (unlikely(c == xer_cmp)) { + break; + } + if (likely(d != 0)) { + d -= 8; + } else { + d = 24; + reg++; + reg = reg & 0x1F; + } + } + return i; +} + +/*****************************************************************************/ +/* Altivec extension helpers */ +#if defined(HOST_WORDS_BIGENDIAN) +#define HI_IDX 0 +#define LO_IDX 1 +#else +#define HI_IDX 1 +#define LO_IDX 0 +#endif + +#define LVE(name, access, swap, element) \ + void helper_##name(CPUPPCState *env, ppc_avr_t *r, \ + target_ulong addr) \ + { \ + size_t n_elems = ARRAY_SIZE(r->element); \ + int adjust = HI_IDX*(n_elems - 1); \ + int sh = sizeof(r->element[0]) >> 1; \ + int index = (addr & 0xf) >> sh; \ + \ + if (msr_le) { \ + r->element[LO_IDX ? index : (adjust - index)] = \ + swap(access(env, addr)); \ + } else { \ + r->element[LO_IDX ? index : (adjust - index)] = \ + access(env, addr); \ + } \ + } +#define I(x) (x) +LVE(lvebx, cpu_ldub_data, I, u8) +LVE(lvehx, cpu_lduw_data, bswap16, u16) +LVE(lvewx, cpu_ldl_data, bswap32, u32) +#undef I +#undef LVE + +#define STVE(name, access, swap, element) \ + void helper_##name(CPUPPCState *env, ppc_avr_t *r, \ + target_ulong addr) \ + { \ + size_t n_elems = ARRAY_SIZE(r->element); \ + int adjust = HI_IDX * (n_elems - 1); \ + int sh = sizeof(r->element[0]) >> 1; \ + int index = (addr & 0xf) >> sh; \ + \ + if (msr_le) { \ + access(env, addr, swap(r->element[LO_IDX ? index : \ + (adjust - index)])); \ + } else { \ + access(env, addr, r->element[LO_IDX ? index : \ + (adjust - index)]); \ + } \ + } +#define I(x) (x) +STVE(stvebx, cpu_stb_data, I, u8) +STVE(stvehx, cpu_stw_data, bswap16, u16) +STVE(stvewx, cpu_stl_data, bswap32, u32) +#undef I +#undef LVE + +#undef HI_IDX +#undef LO_IDX + +/*****************************************************************************/ +/* Softmmu support */ +#if !defined(CONFIG_USER_ONLY) + +#define MMUSUFFIX _mmu + +#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(CPUPPCState *env, target_ulong addr, int is_write, int mmu_idx, + uintptr_t retaddr) +{ + TranslationBlock *tb; + int ret; + + ret = cpu_ppc_handle_mmu_fault(env, addr, is_write, mmu_idx); + if (unlikely(ret != 0)) { + if (likely(retaddr)) { + /* now we have a real cpu fault */ + tb = tb_find_pc(retaddr); + if (likely(tb)) { + /* the PC is inside the translated code. It means that we have + a virtual CPU fault */ + cpu_restore_state(tb, env, retaddr); + } + } + helper_raise_exception_err(env, env->exception_index, env->error_code); + } +} +#endif /* !CONFIG_USER_ONLY */ diff --git a/target-ppc/misc_helper.c b/target-ppc/misc_helper.c new file mode 100644 index 0000000000..26edcca2df --- /dev/null +++ b/target-ppc/misc_helper.c @@ -0,0 +1,124 @@ +/* + * Miscellaneous PowerPC emulation helpers for QEMU. + * + * Copyright (c) 2003-2007 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, see <http://www.gnu.org/licenses/>. + */ +#include "cpu.h" +#include "helper.h" + +#include "helper_regs.h" + +/*****************************************************************************/ +/* SPR accesses */ +void helper_load_dump_spr(CPUPPCState *env, uint32_t sprn) +{ + qemu_log("Read SPR %d %03x => " TARGET_FMT_lx "\n", sprn, sprn, + env->spr[sprn]); +} + +void helper_store_dump_spr(CPUPPCState *env, uint32_t sprn) +{ + qemu_log("Write SPR %d %03x <= " TARGET_FMT_lx "\n", sprn, sprn, + env->spr[sprn]); +} +#if !defined(CONFIG_USER_ONLY) +#if defined(TARGET_PPC64) +void helper_store_asr(CPUPPCState *env, target_ulong val) +{ + ppc_store_asr(env, val); +} +#endif + +void helper_store_sdr1(CPUPPCState *env, target_ulong val) +{ + ppc_store_sdr1(env, val); +} + +void helper_store_hid0_601(CPUPPCState *env, target_ulong val) +{ + target_ulong hid0; + + hid0 = env->spr[SPR_HID0]; + if ((val ^ hid0) & 0x00000008) { + /* Change current endianness */ + env->hflags &= ~(1 << MSR_LE); + env->hflags_nmsr &= ~(1 << MSR_LE); + env->hflags_nmsr |= (1 << MSR_LE) & (((val >> 3) & 1) << MSR_LE); + env->hflags |= env->hflags_nmsr; + qemu_log("%s: set endianness to %c => " TARGET_FMT_lx "\n", __func__, + val & 0x8 ? 'l' : 'b', env->hflags); + } + env->spr[SPR_HID0] = (uint32_t)val; +} + +void helper_store_403_pbr(CPUPPCState *env, uint32_t num, target_ulong value) +{ + if (likely(env->pb[num] != value)) { + env->pb[num] = value; + /* Should be optimized */ + tlb_flush(env, 1); + } +} + +void helper_store_40x_dbcr0(CPUPPCState *env, target_ulong val) +{ + store_40x_dbcr0(env, val); +} + +void helper_store_40x_sler(CPUPPCState *env, target_ulong val) +{ + store_40x_sler(env, val); +} +#endif +/*****************************************************************************/ +/* PowerPC 601 specific instructions (POWER bridge) */ + +target_ulong helper_clcs(CPUPPCState *env, uint32_t arg) +{ + switch (arg) { + case 0x0CUL: + /* Instruction cache line size */ + return env->icache_line_size; + break; + case 0x0DUL: + /* Data cache line size */ + return env->dcache_line_size; + break; + case 0x0EUL: + /* Minimum cache line size */ + return (env->icache_line_size < env->dcache_line_size) ? + env->icache_line_size : env->dcache_line_size; + break; + case 0x0FUL: + /* Maximum cache line size */ + return (env->icache_line_size > env->dcache_line_size) ? + env->icache_line_size : env->dcache_line_size; + break; + default: + /* Undefined */ + return 0; + break; + } +} + +/*****************************************************************************/ +/* Special registers manipulation */ + +/* GDBstub can read and write MSR... */ +void ppc_store_msr(CPUPPCState *env, target_ulong value) +{ + hreg_store_msr(env, value, 0); +} diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c new file mode 100644 index 0000000000..d2664acef0 --- /dev/null +++ b/target-ppc/mmu_helper.c @@ -0,0 +1,3326 @@ +/* + * PowerPC MMU, TLB, SLB and BAT emulation helpers for QEMU. + * + * Copyright (c) 2003-2007 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, see <http://www.gnu.org/licenses/>. + */ +#include "cpu.h" +#include "helper.h" +#include "kvm.h" +#include "kvm_ppc.h" + +//#define DEBUG_MMU +//#define DEBUG_BATS +//#define DEBUG_SLB +//#define DEBUG_SOFTWARE_TLB +//#define DUMP_PAGE_TABLES +//#define DEBUG_SOFTWARE_TLB +//#define FLUSH_ALL_TLBS + +#ifdef DEBUG_MMU +# define LOG_MMU(...) qemu_log(__VA_ARGS__) +# define LOG_MMU_STATE(env) log_cpu_state((env), 0) +#else +# define LOG_MMU(...) do { } while (0) +# define LOG_MMU_STATE(...) do { } while (0) +#endif + +#ifdef DEBUG_SOFTWARE_TLB +# define LOG_SWTLB(...) qemu_log(__VA_ARGS__) +#else +# define LOG_SWTLB(...) do { } while (0) +#endif + +#ifdef DEBUG_BATS +# define LOG_BATS(...) qemu_log(__VA_ARGS__) +#else +# define LOG_BATS(...) do { } while (0) +#endif + +#ifdef DEBUG_SLB +# define LOG_SLB(...) qemu_log(__VA_ARGS__) +#else +# define LOG_SLB(...) do { } while (0) +#endif + +/*****************************************************************************/ +/* PowerPC MMU emulation */ +#if defined(CONFIG_USER_ONLY) +int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, + int mmu_idx) +{ + int exception, error_code; + + if (rw == 2) { + exception = POWERPC_EXCP_ISI; + error_code = 0x40000000; + } else { + exception = POWERPC_EXCP_DSI; + error_code = 0x40000000; + if (rw) { + error_code |= 0x02000000; + } + env->spr[SPR_DAR] = address; + env->spr[SPR_DSISR] = error_code; + } + env->exception_index = exception; + env->error_code = error_code; + + return 1; +} + +#else +/* Common routines used by software and hardware TLBs emulation */ +static inline int pte_is_valid(target_ulong pte0) +{ + return pte0 & 0x80000000 ? 1 : 0; +} + +static inline void pte_invalidate(target_ulong *pte0) +{ + *pte0 &= ~0x80000000; +} + +#if defined(TARGET_PPC64) +static inline int pte64_is_valid(target_ulong pte0) +{ + return pte0 & 0x0000000000000001ULL ? 1 : 0; +} + +static inline void pte64_invalidate(target_ulong *pte0) +{ + *pte0 &= ~0x0000000000000001ULL; +} +#endif + +#define PTE_PTEM_MASK 0x7FFFFFBF +#define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B) +#if defined(TARGET_PPC64) +#define PTE64_PTEM_MASK 0xFFFFFFFFFFFFFF80ULL +#define PTE64_CHECK_MASK (TARGET_PAGE_MASK | 0x7F) +#endif + +static inline int pp_check(int key, int pp, int nx) +{ + int access; + + /* Compute access rights */ + /* When pp is 3/7, the result is undefined. Set it to noaccess */ + access = 0; + if (key == 0) { + switch (pp) { + case 0x0: + case 0x1: + case 0x2: + access |= PAGE_WRITE; + /* No break here */ + case 0x3: + case 0x6: + access |= PAGE_READ; + break; + } + } else { + switch (pp) { + case 0x0: + case 0x6: + access = 0; + break; + case 0x1: + case 0x3: + access = PAGE_READ; + break; + case 0x2: + access = PAGE_READ | PAGE_WRITE; + break; + } + } + if (nx == 0) { + access |= PAGE_EXEC; + } + + return access; +} + +static inline int check_prot(int prot, int rw, int access_type) +{ + int ret; + + if (access_type == ACCESS_CODE) { + if (prot & PAGE_EXEC) { + ret = 0; + } else { + ret = -2; + } + } else if (rw) { + if (prot & PAGE_WRITE) { + ret = 0; + } else { + ret = -2; + } + } else { + if (prot & PAGE_READ) { + ret = 0; + } else { + ret = -2; + } + } + + return ret; +} + +static inline int pte_check(mmu_ctx_t *ctx, int is_64b, target_ulong pte0, + target_ulong pte1, int h, int rw, int type) +{ + target_ulong ptem, mmask; + int access, ret, pteh, ptev, pp; + + ret = -1; + /* Check validity and table match */ +#if defined(TARGET_PPC64) + if (is_64b) { + ptev = pte64_is_valid(pte0); + pteh = (pte0 >> 1) & 1; + } else +#endif + { + ptev = pte_is_valid(pte0); + pteh = (pte0 >> 6) & 1; + } + if (ptev && h == pteh) { + /* Check vsid & api */ +#if defined(TARGET_PPC64) + if (is_64b) { + ptem = pte0 & PTE64_PTEM_MASK; + mmask = PTE64_CHECK_MASK; + pp = (pte1 & 0x00000003) | ((pte1 >> 61) & 0x00000004); + ctx->nx = (pte1 >> 2) & 1; /* No execute bit */ + ctx->nx |= (pte1 >> 3) & 1; /* Guarded bit */ + } else +#endif + { + ptem = pte0 & PTE_PTEM_MASK; + mmask = PTE_CHECK_MASK; + pp = pte1 & 0x00000003; + } + if (ptem == ctx->ptem) { + if (ctx->raddr != (target_phys_addr_t)-1ULL) { + /* all matches should have equal RPN, WIMG & PP */ + if ((ctx->raddr & mmask) != (pte1 & mmask)) { + qemu_log("Bad RPN/WIMG/PP\n"); + return -3; + } + } + /* Compute access rights */ + access = pp_check(ctx->key, pp, ctx->nx); + /* Keep the matching PTE informations */ + ctx->raddr = pte1; + ctx->prot = access; + ret = check_prot(ctx->prot, rw, type); + if (ret == 0) { + /* Access granted */ + LOG_MMU("PTE access granted !\n"); + } else { + /* Access right violation */ + LOG_MMU("PTE access rejected\n"); + } + } + } + + return ret; +} + +static inline int pte32_check(mmu_ctx_t *ctx, target_ulong pte0, + target_ulong pte1, int h, int rw, int type) +{ + return pte_check(ctx, 0, pte0, pte1, h, rw, type); +} + +#if defined(TARGET_PPC64) +static inline int pte64_check(mmu_ctx_t *ctx, target_ulong pte0, + target_ulong pte1, int h, int rw, int type) +{ + return pte_check(ctx, 1, pte0, pte1, h, rw, type); +} +#endif + +static inline int pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, + int ret, int rw) +{ + int store = 0; + + /* Update page flags */ + if (!(*pte1p & 0x00000100)) { + /* Update accessed flag */ + *pte1p |= 0x00000100; + store = 1; + } + if (!(*pte1p & 0x00000080)) { + if (rw == 1 && ret == 0) { + /* Update changed flag */ + *pte1p |= 0x00000080; + store = 1; + } else { + /* Force page fault for first write access */ + ctx->prot &= ~PAGE_WRITE; + } + } + + return store; +} + +/* Software driven TLB helpers */ +static inline int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, + int way, int is_code) +{ + int nr; + + /* Select TLB num in a way from address */ + nr = (eaddr >> TARGET_PAGE_BITS) & (env->tlb_per_way - 1); + /* Select TLB way */ + nr += env->tlb_per_way * way; + /* 6xx have separate TLBs for instructions and data */ + if (is_code && env->id_tlbs == 1) { + nr += env->nb_tlb; + } + + return nr; +} + +static inline void ppc6xx_tlb_invalidate_all(CPUPPCState *env) +{ + ppc6xx_tlb_t *tlb; + int nr, max; + + /* LOG_SWTLB("Invalidate all TLBs\n"); */ + /* Invalidate all defined software TLB */ + max = env->nb_tlb; + if (env->id_tlbs == 1) { + max *= 2; + } + for (nr = 0; nr < max; nr++) { + tlb = &env->tlb.tlb6[nr]; + pte_invalidate(&tlb->pte0); + } + tlb_flush(env, 1); +} + +static inline void ppc6xx_tlb_invalidate_virt2(CPUPPCState *env, + target_ulong eaddr, + int is_code, int match_epn) +{ +#if !defined(FLUSH_ALL_TLBS) + ppc6xx_tlb_t *tlb; + int way, nr; + + /* Invalidate ITLB + DTLB, all ways */ + for (way = 0; way < env->nb_ways; way++) { + nr = ppc6xx_tlb_getnum(env, eaddr, way, is_code); + tlb = &env->tlb.tlb6[nr]; + if (pte_is_valid(tlb->pte0) && (match_epn == 0 || eaddr == tlb->EPN)) { + LOG_SWTLB("TLB invalidate %d/%d " TARGET_FMT_lx "\n", nr, + env->nb_tlb, eaddr); + pte_invalidate(&tlb->pte0); + tlb_flush_page(env, tlb->EPN); + } + } +#else + /* XXX: PowerPC specification say this is valid as well */ + ppc6xx_tlb_invalidate_all(env); +#endif +} + +static inline void ppc6xx_tlb_invalidate_virt(CPUPPCState *env, + target_ulong eaddr, int is_code) +{ + ppc6xx_tlb_invalidate_virt2(env, eaddr, is_code, 0); +} + +static void ppc6xx_tlb_store(CPUPPCState *env, target_ulong EPN, int way, + int is_code, target_ulong pte0, target_ulong pte1) +{ + ppc6xx_tlb_t *tlb; + int nr; + + nr = ppc6xx_tlb_getnum(env, EPN, way, is_code); + tlb = &env->tlb.tlb6[nr]; + LOG_SWTLB("Set TLB %d/%d EPN " TARGET_FMT_lx " PTE0 " TARGET_FMT_lx + " PTE1 " TARGET_FMT_lx "\n", nr, env->nb_tlb, EPN, pte0, pte1); + /* Invalidate any pending reference in QEMU for this virtual address */ + ppc6xx_tlb_invalidate_virt2(env, EPN, is_code, 1); + tlb->pte0 = pte0; + tlb->pte1 = pte1; + tlb->EPN = EPN; + /* Store last way for LRU mechanism */ + env->last_way = way; +} + +static inline int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, int rw, int access_type) +{ + ppc6xx_tlb_t *tlb; + int nr, best, way; + int ret; + + best = -1; + ret = -1; /* No TLB found */ + for (way = 0; way < env->nb_ways; way++) { + nr = ppc6xx_tlb_getnum(env, eaddr, way, + access_type == ACCESS_CODE ? 1 : 0); + tlb = &env->tlb.tlb6[nr]; + /* This test "emulates" the PTE index match for hardware TLBs */ + if ((eaddr & TARGET_PAGE_MASK) != tlb->EPN) { + LOG_SWTLB("TLB %d/%d %s [" TARGET_FMT_lx " " TARGET_FMT_lx + "] <> " TARGET_FMT_lx "\n", nr, env->nb_tlb, + pte_is_valid(tlb->pte0) ? "valid" : "inval", + tlb->EPN, tlb->EPN + TARGET_PAGE_SIZE, eaddr); + continue; + } + LOG_SWTLB("TLB %d/%d %s " TARGET_FMT_lx " <> " TARGET_FMT_lx " " + TARGET_FMT_lx " %c %c\n", nr, env->nb_tlb, + pte_is_valid(tlb->pte0) ? "valid" : "inval", + tlb->EPN, eaddr, tlb->pte1, + rw ? 'S' : 'L', access_type == ACCESS_CODE ? 'I' : 'D'); + switch (pte32_check(ctx, tlb->pte0, tlb->pte1, 0, rw, access_type)) { + case -3: + /* TLB inconsistency */ + return -1; + case -2: + /* Access violation */ + ret = -2; + best = nr; + break; + case -1: + default: + /* No match */ + break; + case 0: + /* access granted */ + /* XXX: we should go on looping to check all TLBs consistency + * but we can speed-up the whole thing as the + * result would be undefined if TLBs are not consistent. + */ + ret = 0; + best = nr; + goto done; + } + } + if (best != -1) { + done: + LOG_SWTLB("found TLB at addr " TARGET_FMT_plx " prot=%01x ret=%d\n", + ctx->raddr & TARGET_PAGE_MASK, ctx->prot, ret); + /* Update page flags */ + pte_update_flags(ctx, &env->tlb.tlb6[best].pte1, ret, rw); + } + + return ret; +} + +/* Perform BAT hit & translation */ +static inline void bat_size_prot(CPUPPCState *env, target_ulong *blp, + int *validp, int *protp, target_ulong *BATu, + target_ulong *BATl) +{ + target_ulong bl; + int pp, valid, prot; + + bl = (*BATu & 0x00001FFC) << 15; + valid = 0; + prot = 0; + if (((msr_pr == 0) && (*BATu & 0x00000002)) || + ((msr_pr != 0) && (*BATu & 0x00000001))) { + valid = 1; + pp = *BATl & 0x00000003; + if (pp != 0) { + prot = PAGE_READ | PAGE_EXEC; + if (pp == 0x2) { + prot |= PAGE_WRITE; + } + } + } + *blp = bl; + *validp = valid; + *protp = prot; +} + +static inline void bat_601_size_prot(CPUPPCState *env, target_ulong *blp, + int *validp, int *protp, + target_ulong *BATu, target_ulong *BATl) +{ + target_ulong bl; + int key, pp, valid, prot; + + bl = (*BATl & 0x0000003F) << 17; + LOG_BATS("b %02x ==> bl " TARGET_FMT_lx " msk " TARGET_FMT_lx "\n", + (uint8_t)(*BATl & 0x0000003F), bl, ~bl); + prot = 0; + valid = (*BATl >> 6) & 1; + if (valid) { + pp = *BATu & 0x00000003; + if (msr_pr == 0) { + key = (*BATu >> 3) & 1; + } else { + key = (*BATu >> 2) & 1; + } + prot = pp_check(key, pp, 0); + } + *blp = bl; + *validp = valid; + *protp = prot; +} + +static inline int get_bat(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong virtual, int rw, int type) +{ + target_ulong *BATlt, *BATut, *BATu, *BATl; + target_ulong BEPIl, BEPIu, bl; + int i, valid, prot; + int ret = -1; + + LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__, + type == ACCESS_CODE ? 'I' : 'D', virtual); + switch (type) { + case ACCESS_CODE: + BATlt = env->IBAT[1]; + BATut = env->IBAT[0]; + break; + default: + BATlt = env->DBAT[1]; + BATut = env->DBAT[0]; + break; + } + for (i = 0; i < env->nb_BATs; i++) { + BATu = &BATut[i]; + BATl = &BATlt[i]; + BEPIu = *BATu & 0xF0000000; + BEPIl = *BATu & 0x0FFE0000; + if (unlikely(env->mmu_model == POWERPC_MMU_601)) { + bat_601_size_prot(env, &bl, &valid, &prot, BATu, BATl); + } else { + bat_size_prot(env, &bl, &valid, &prot, BATu, BATl); + } + LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx + " BATl " TARGET_FMT_lx "\n", __func__, + type == ACCESS_CODE ? 'I' : 'D', i, virtual, *BATu, *BATl); + if ((virtual & 0xF0000000) == BEPIu && + ((virtual & 0x0FFE0000) & ~bl) == BEPIl) { + /* BAT matches */ + if (valid != 0) { + /* Get physical address */ + ctx->raddr = (*BATl & 0xF0000000) | + ((virtual & 0x0FFE0000 & bl) | (*BATl & 0x0FFE0000)) | + (virtual & 0x0001F000); + /* Compute access rights */ + ctx->prot = prot; + ret = check_prot(ctx->prot, rw, type); + if (ret == 0) { + LOG_BATS("BAT %d match: r " TARGET_FMT_plx " prot=%c%c\n", + i, ctx->raddr, ctx->prot & PAGE_READ ? 'R' : '-', + ctx->prot & PAGE_WRITE ? 'W' : '-'); + } + break; + } + } + } + if (ret < 0) { +#if defined(DEBUG_BATS) + if (qemu_log_enabled()) { + LOG_BATS("no BAT match for " TARGET_FMT_lx ":\n", virtual); + for (i = 0; i < 4; i++) { + BATu = &BATut[i]; + BATl = &BATlt[i]; + BEPIu = *BATu & 0xF0000000; + BEPIl = *BATu & 0x0FFE0000; + bl = (*BATu & 0x00001FFC) << 15; + LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx + " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " + TARGET_FMT_lx " " TARGET_FMT_lx "\n", + __func__, type == ACCESS_CODE ? 'I' : 'D', i, virtual, + *BATu, *BATl, BEPIu, BEPIl, bl); + } + } +#endif + } + /* No hit */ + return ret; +} + +static inline target_phys_addr_t get_pteg_offset(CPUPPCState *env, + target_phys_addr_t hash, + int pte_size) +{ + return (hash * pte_size * 8) & env->htab_mask; +} + +/* PTE table lookup */ +static inline int find_pte2(CPUPPCState *env, mmu_ctx_t *ctx, int is_64b, int h, + int rw, int type, int target_page_bits) +{ + target_phys_addr_t pteg_off; + target_ulong pte0, pte1; + int i, good = -1; + int ret, r; + + ret = -1; /* No entry found */ + pteg_off = get_pteg_offset(env, ctx->hash[h], + is_64b ? HASH_PTE_SIZE_64 : HASH_PTE_SIZE_32); + for (i = 0; i < 8; i++) { +#if defined(TARGET_PPC64) + if (is_64b) { + if (env->external_htab) { + pte0 = ldq_p(env->external_htab + pteg_off + (i * 16)); + pte1 = ldq_p(env->external_htab + pteg_off + (i * 16) + 8); + } else { + pte0 = ldq_phys(env->htab_base + pteg_off + (i * 16)); + pte1 = ldq_phys(env->htab_base + pteg_off + (i * 16) + 8); + } + + r = pte64_check(ctx, pte0, pte1, h, rw, type); + LOG_MMU("Load pte from " TARGET_FMT_lx " => " TARGET_FMT_lx " " + TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n", + pteg_off + (i * 16), pte0, pte1, (int)(pte0 & 1), h, + (int)((pte0 >> 1) & 1), ctx->ptem); + } else +#endif + { + if (env->external_htab) { + pte0 = ldl_p(env->external_htab + pteg_off + (i * 8)); + pte1 = ldl_p(env->external_htab + pteg_off + (i * 8) + 4); + } else { + pte0 = ldl_phys(env->htab_base + pteg_off + (i * 8)); + pte1 = ldl_phys(env->htab_base + pteg_off + (i * 8) + 4); + } + r = pte32_check(ctx, pte0, pte1, h, rw, type); + LOG_MMU("Load pte from " TARGET_FMT_lx " => " TARGET_FMT_lx " " + TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n", + pteg_off + (i * 8), pte0, pte1, (int)(pte0 >> 31), h, + (int)((pte0 >> 6) & 1), ctx->ptem); + } + switch (r) { + case -3: + /* PTE inconsistency */ + return -1; + case -2: + /* Access violation */ + ret = -2; + good = i; + break; + case -1: + default: + /* No PTE match */ + break; + case 0: + /* access granted */ + /* XXX: we should go on looping to check all PTEs consistency + * but if we can speed-up the whole thing as the + * result would be undefined if PTEs are not consistent. + */ + ret = 0; + good = i; + goto done; + } + } + if (good != -1) { + done: + LOG_MMU("found PTE at addr " TARGET_FMT_lx " prot=%01x ret=%d\n", + ctx->raddr, ctx->prot, ret); + /* Update page flags */ + pte1 = ctx->raddr; + if (pte_update_flags(ctx, &pte1, ret, rw) == 1) { +#if defined(TARGET_PPC64) + if (is_64b) { + if (env->external_htab) { + stq_p(env->external_htab + pteg_off + (good * 16) + 8, + pte1); + } else { + stq_phys_notdirty(env->htab_base + pteg_off + + (good * 16) + 8, pte1); + } + } else +#endif + { + if (env->external_htab) { + stl_p(env->external_htab + pteg_off + (good * 8) + 4, + pte1); + } else { + stl_phys_notdirty(env->htab_base + pteg_off + + (good * 8) + 4, pte1); + } + } + } + } + + /* We have a TLB that saves 4K pages, so let's + * split a huge page to 4k chunks */ + if (target_page_bits != TARGET_PAGE_BITS) { + ctx->raddr |= (ctx->eaddr & ((1 << target_page_bits) - 1)) + & TARGET_PAGE_MASK; + } + return ret; +} + +static inline int find_pte(CPUPPCState *env, mmu_ctx_t *ctx, int h, int rw, + int type, int target_page_bits) +{ +#if defined(TARGET_PPC64) + if (env->mmu_model & POWERPC_MMU_64) { + return find_pte2(env, ctx, 1, h, rw, type, target_page_bits); + } +#endif + + return find_pte2(env, ctx, 0, h, rw, type, target_page_bits); +} + +#if defined(TARGET_PPC64) +static inline ppc_slb_t *slb_lookup(CPUPPCState *env, target_ulong eaddr) +{ + uint64_t esid_256M, esid_1T; + int n; + + LOG_SLB("%s: eaddr " TARGET_FMT_lx "\n", __func__, eaddr); + + esid_256M = (eaddr & SEGMENT_MASK_256M) | SLB_ESID_V; + esid_1T = (eaddr & SEGMENT_MASK_1T) | SLB_ESID_V; + + for (n = 0; n < env->slb_nr; n++) { + ppc_slb_t *slb = &env->slb[n]; + + LOG_SLB("%s: slot %d %016" PRIx64 " %016" + PRIx64 "\n", __func__, n, slb->esid, slb->vsid); + /* We check for 1T matches on all MMUs here - if the MMU + * doesn't have 1T segment support, we will have prevented 1T + * entries from being inserted in the slbmte code. */ + if (((slb->esid == esid_256M) && + ((slb->vsid & SLB_VSID_B) == SLB_VSID_B_256M)) + || ((slb->esid == esid_1T) && + ((slb->vsid & SLB_VSID_B) == SLB_VSID_B_1T))) { + return slb; + } + } + + return NULL; +} + +/*****************************************************************************/ +/* SPR accesses */ + +void helper_slbia(CPUPPCState *env) +{ + int n, do_invalidate; + + do_invalidate = 0; + /* XXX: Warning: slbia never invalidates the first segment */ + for (n = 1; n < env->slb_nr; n++) { + ppc_slb_t *slb = &env->slb[n]; + + if (slb->esid & SLB_ESID_V) { + slb->esid &= ~SLB_ESID_V; + /* XXX: given the fact that segment size is 256 MB or 1TB, + * and we still don't have a tlb_flush_mask(env, n, mask) + * in QEMU, we just invalidate all TLBs + */ + do_invalidate = 1; + } + } + if (do_invalidate) { + tlb_flush(env, 1); + } +} + +void helper_slbie(CPUPPCState *env, target_ulong addr) +{ + ppc_slb_t *slb; + + slb = slb_lookup(env, addr); + if (!slb) { + return; + } + + if (slb->esid & SLB_ESID_V) { + slb->esid &= ~SLB_ESID_V; + + /* XXX: given the fact that segment size is 256 MB or 1TB, + * and we still don't have a tlb_flush_mask(env, n, mask) + * in QEMU, we just invalidate all TLBs + */ + tlb_flush(env, 1); + } +} + +int ppc_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs) +{ + int slot = rb & 0xfff; + ppc_slb_t *slb = &env->slb[slot]; + + if (rb & (0x1000 - env->slb_nr)) { + return -1; /* Reserved bits set or slot too high */ + } + if (rs & (SLB_VSID_B & ~SLB_VSID_B_1T)) { + return -1; /* Bad segment size */ + } + if ((rs & SLB_VSID_B) && !(env->mmu_model & POWERPC_MMU_1TSEG)) { + return -1; /* 1T segment on MMU that doesn't support it */ + } + + /* Mask out the slot number as we store the entry */ + slb->esid = rb & (SLB_ESID_ESID | SLB_ESID_V); + slb->vsid = rs; + + LOG_SLB("%s: %d " TARGET_FMT_lx " - " TARGET_FMT_lx " => %016" PRIx64 + " %016" PRIx64 "\n", __func__, slot, rb, rs, + slb->esid, slb->vsid); + + return 0; +} + +static int ppc_load_slb_esid(CPUPPCState *env, target_ulong rb, + target_ulong *rt) +{ + int slot = rb & 0xfff; + ppc_slb_t *slb = &env->slb[slot]; + + if (slot >= env->slb_nr) { + return -1; + } + + *rt = slb->esid; + return 0; +} + +static int ppc_load_slb_vsid(CPUPPCState *env, target_ulong rb, + target_ulong *rt) +{ + int slot = rb & 0xfff; + ppc_slb_t *slb = &env->slb[slot]; + + if (slot >= env->slb_nr) { + return -1; + } + + *rt = slb->vsid; + return 0; +} +#endif /* defined(TARGET_PPC64) */ + +/* Perform segment based translation */ +static inline int get_segment(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, int rw, int type) +{ + target_phys_addr_t hash; + target_ulong vsid; + int ds, pr, target_page_bits; + int ret, ret2; + + pr = msr_pr; + ctx->eaddr = eaddr; +#if defined(TARGET_PPC64) + if (env->mmu_model & POWERPC_MMU_64) { + ppc_slb_t *slb; + target_ulong pageaddr; + int segment_bits; + + LOG_MMU("Check SLBs\n"); + slb = slb_lookup(env, eaddr); + if (!slb) { + return -5; + } + + if (slb->vsid & SLB_VSID_B) { + vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT_1T; + segment_bits = 40; + } else { + vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT; + segment_bits = 28; + } + + target_page_bits = (slb->vsid & SLB_VSID_L) + ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS; + ctx->key = !!(pr ? (slb->vsid & SLB_VSID_KP) + : (slb->vsid & SLB_VSID_KS)); + ds = 0; + ctx->nx = !!(slb->vsid & SLB_VSID_N); + + pageaddr = eaddr & ((1ULL << segment_bits) + - (1ULL << target_page_bits)); + if (slb->vsid & SLB_VSID_B) { + hash = vsid ^ (vsid << 25) ^ (pageaddr >> target_page_bits); + } else { + hash = vsid ^ (pageaddr >> target_page_bits); + } + /* Only 5 bits of the page index are used in the AVPN */ + ctx->ptem = (slb->vsid & SLB_VSID_PTEM) | + ((pageaddr >> 16) & ((1ULL << segment_bits) - 0x80)); + } else +#endif /* defined(TARGET_PPC64) */ + { + target_ulong sr, pgidx; + + sr = env->sr[eaddr >> 28]; + ctx->key = (((sr & 0x20000000) && (pr != 0)) || + ((sr & 0x40000000) && (pr == 0))) ? 1 : 0; + ds = sr & 0x80000000 ? 1 : 0; + ctx->nx = sr & 0x10000000 ? 1 : 0; + vsid = sr & 0x00FFFFFF; + target_page_bits = TARGET_PAGE_BITS; + LOG_MMU("Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip=" + TARGET_FMT_lx " lr=" TARGET_FMT_lx + " ir=%d dr=%d pr=%d %d t=%d\n", + eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir, + (int)msr_dr, pr != 0 ? 1 : 0, rw, type); + pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits; + hash = vsid ^ pgidx; + ctx->ptem = (vsid << 7) | (pgidx >> 10); + } + LOG_MMU("pte segment: key=%d ds %d nx %d vsid " TARGET_FMT_lx "\n", + ctx->key, ds, ctx->nx, vsid); + ret = -1; + if (!ds) { + /* Check if instruction fetch is allowed, if needed */ + if (type != ACCESS_CODE || ctx->nx == 0) { + /* Page address translation */ + LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx + " hash " TARGET_FMT_plx "\n", + env->htab_base, env->htab_mask, hash); + ctx->hash[0] = hash; + ctx->hash[1] = ~hash; + + /* Initialize real address with an invalid value */ + ctx->raddr = (target_phys_addr_t)-1ULL; + if (unlikely(env->mmu_model == POWERPC_MMU_SOFT_6xx || + env->mmu_model == POWERPC_MMU_SOFT_74xx)) { + /* Software TLB search */ + ret = ppc6xx_tlb_check(env, ctx, eaddr, rw, type); + } else { + LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx + " hash=" TARGET_FMT_plx "\n", + env->htab_base, env->htab_mask, vsid, ctx->ptem, + ctx->hash[0]); + /* Primary table lookup */ + ret = find_pte(env, ctx, 0, rw, type, target_page_bits); + if (ret < 0) { + /* Secondary table lookup */ + if (eaddr != 0xEFFFFFFF) { + LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx + " hash=" TARGET_FMT_plx "\n", env->htab_base, + env->htab_mask, vsid, ctx->ptem, ctx->hash[1]); + } + ret2 = find_pte(env, ctx, 1, rw, type, + target_page_bits); + if (ret2 != -1) { + ret = ret2; + } + } + } +#if defined(DUMP_PAGE_TABLES) + if (qemu_log_enabled()) { + target_phys_addr_t curaddr; + uint32_t a0, a1, a2, a3; + + qemu_log("Page table: " TARGET_FMT_plx " len " TARGET_FMT_plx + "\n", sdr, mask + 0x80); + for (curaddr = sdr; curaddr < (sdr + mask + 0x80); + curaddr += 16) { + a0 = ldl_phys(curaddr); + a1 = ldl_phys(curaddr + 4); + a2 = ldl_phys(curaddr + 8); + a3 = ldl_phys(curaddr + 12); + if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) { + qemu_log(TARGET_FMT_plx ": %08x %08x %08x %08x\n", + curaddr, a0, a1, a2, a3); + } + } + } +#endif + } else { + LOG_MMU("No access allowed\n"); + ret = -3; + } + } else { + target_ulong sr; + + LOG_MMU("direct store...\n"); + /* Direct-store segment : absolutely *BUGGY* for now */ + + /* Direct-store implies a 32-bit MMU. + * Check the Segment Register's bus unit ID (BUID). + */ + sr = env->sr[eaddr >> 28]; + if ((sr & 0x1FF00000) >> 20 == 0x07f) { + /* Memory-forced I/O controller interface access */ + /* If T=1 and BUID=x'07F', the 601 performs a memory access + * to SR[28-31] LA[4-31], bypassing all protection mechanisms. + */ + ctx->raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF); + ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return 0; + } + + switch (type) { + case ACCESS_INT: + /* Integer load/store : only access allowed */ + break; + case ACCESS_CODE: + /* No code fetch is allowed in direct-store areas */ + return -4; + case ACCESS_FLOAT: + /* Floating point load/store */ + return -4; + case ACCESS_RES: + /* lwarx, ldarx or srwcx. */ + return -4; + case ACCESS_CACHE: + /* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */ + /* Should make the instruction do no-op. + * As it already do no-op, it's quite easy :-) + */ + ctx->raddr = eaddr; + return 0; + case ACCESS_EXT: + /* eciwx or ecowx */ + return -4; + default: + qemu_log("ERROR: instruction should not need " + "address translation\n"); + return -4; + } + if ((rw == 1 || ctx->key != 1) && (rw == 0 || ctx->key != 0)) { + ctx->raddr = eaddr; + ret = 2; + } else { + ret = -2; + } + } + + return ret; +} + +/* Generic TLB check function for embedded PowerPC implementations */ +static int ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, + target_phys_addr_t *raddrp, + target_ulong address, uint32_t pid, int ext, + int i) +{ + target_ulong mask; + + /* Check valid flag */ + if (!(tlb->prot & PAGE_VALID)) { + return -1; + } + mask = ~(tlb->size - 1); + LOG_SWTLB("%s: TLB %d address " TARGET_FMT_lx " PID %u <=> " TARGET_FMT_lx + " " TARGET_FMT_lx " %u %x\n", __func__, i, address, pid, tlb->EPN, + mask, (uint32_t)tlb->PID, tlb->prot); + /* Check PID */ + if (tlb->PID != 0 && tlb->PID != pid) { + return -1; + } + /* Check effective address */ + if ((address & mask) != tlb->EPN) { + return -1; + } + *raddrp = (tlb->RPN & mask) | (address & ~mask); +#if (TARGET_PHYS_ADDR_BITS >= 36) + if (ext) { + /* Extend the physical address to 36 bits */ + *raddrp |= (target_phys_addr_t)(tlb->RPN & 0xF) << 32; + } +#endif + + return 0; +} + +/* Generic TLB search function for PowerPC embedded implementations */ +static int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, + uint32_t pid) +{ + ppcemb_tlb_t *tlb; + target_phys_addr_t raddr; + int i, ret; + + /* Default return value is no match */ + ret = -1; + for (i = 0; i < env->nb_tlb; i++) { + tlb = &env->tlb.tlbe[i]; + if (ppcemb_tlb_check(env, tlb, &raddr, address, pid, 0, i) == 0) { + ret = i; + break; + } + } + + return ret; +} + +/* Helpers specific to PowerPC 40x implementations */ +static inline void ppc4xx_tlb_invalidate_all(CPUPPCState *env) +{ + ppcemb_tlb_t *tlb; + int i; + + for (i = 0; i < env->nb_tlb; i++) { + tlb = &env->tlb.tlbe[i]; + tlb->prot &= ~PAGE_VALID; + } + tlb_flush(env, 1); +} + +static inline void ppc4xx_tlb_invalidate_virt(CPUPPCState *env, + target_ulong eaddr, uint32_t pid) +{ +#if !defined(FLUSH_ALL_TLBS) + ppcemb_tlb_t *tlb; + target_phys_addr_t raddr; + target_ulong page, end; + int i; + + for (i = 0; i < env->nb_tlb; i++) { + tlb = &env->tlb.tlbe[i]; + if (ppcemb_tlb_check(env, tlb, &raddr, eaddr, pid, 0, i) == 0) { + end = tlb->EPN + tlb->size; + for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) { + tlb_flush_page(env, page); + } + tlb->prot &= ~PAGE_VALID; + break; + } + } +#else + ppc4xx_tlb_invalidate_all(env); +#endif +} + +static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong address, int rw, + int access_type) +{ + ppcemb_tlb_t *tlb; + target_phys_addr_t raddr; + int i, ret, zsel, zpr, pr; + + ret = -1; + raddr = (target_phys_addr_t)-1ULL; + pr = msr_pr; + for (i = 0; i < env->nb_tlb; i++) { + tlb = &env->tlb.tlbe[i]; + if (ppcemb_tlb_check(env, tlb, &raddr, address, + env->spr[SPR_40x_PID], 0, i) < 0) { + continue; + } + zsel = (tlb->attr >> 4) & 0xF; + zpr = (env->spr[SPR_40x_ZPR] >> (30 - (2 * zsel))) & 0x3; + LOG_SWTLB("%s: TLB %d zsel %d zpr %d rw %d attr %08x\n", + __func__, i, zsel, zpr, rw, tlb->attr); + /* Check execute enable bit */ + switch (zpr) { + case 0x2: + if (pr != 0) { + goto check_perms; + } + /* No break here */ + case 0x3: + /* All accesses granted */ + ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + ret = 0; + break; + case 0x0: + if (pr != 0) { + /* Raise Zone protection fault. */ + env->spr[SPR_40x_ESR] = 1 << 22; + ctx->prot = 0; + ret = -2; + break; + } + /* No break here */ + case 0x1: + check_perms: + /* Check from TLB entry */ + ctx->prot = tlb->prot; + ret = check_prot(ctx->prot, rw, access_type); + if (ret == -2) { + env->spr[SPR_40x_ESR] = 0; + } + break; + } + if (ret >= 0) { + ctx->raddr = raddr; + LOG_SWTLB("%s: access granted " TARGET_FMT_lx " => " TARGET_FMT_plx + " %d %d\n", __func__, address, ctx->raddr, ctx->prot, + ret); + return 0; + } + } + LOG_SWTLB("%s: access refused " TARGET_FMT_lx " => " TARGET_FMT_plx + " %d %d\n", __func__, address, raddr, ctx->prot, ret); + + return ret; +} + +void store_40x_sler(CPUPPCState *env, uint32_t val) +{ + /* XXX: TO BE FIXED */ + if (val != 0x00000000) { + cpu_abort(env, "Little-endian regions are not supported by now\n"); + } + env->spr[SPR_405_SLER] = val; +} + +static inline int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, + target_phys_addr_t *raddr, int *prot, + target_ulong address, int rw, + int access_type, int i) +{ + int ret, prot2; + + if (ppcemb_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID], + !env->nb_pids, i) >= 0) { + goto found_tlb; + } + + if (env->spr[SPR_BOOKE_PID1] && + ppcemb_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID1], 0, i) >= 0) { + goto found_tlb; + } + + if (env->spr[SPR_BOOKE_PID2] && + ppcemb_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID2], 0, i) >= 0) { + goto found_tlb; + } + + LOG_SWTLB("%s: TLB entry not found\n", __func__); + return -1; + +found_tlb: + + if (msr_pr != 0) { + prot2 = tlb->prot & 0xF; + } else { + prot2 = (tlb->prot >> 4) & 0xF; + } + + /* Check the address space */ + if (access_type == ACCESS_CODE) { + if (msr_ir != (tlb->attr & 1)) { + LOG_SWTLB("%s: AS doesn't match\n", __func__); + return -1; + } + + *prot = prot2; + if (prot2 & PAGE_EXEC) { + LOG_SWTLB("%s: good TLB!\n", __func__); + return 0; + } + + LOG_SWTLB("%s: no PAGE_EXEC: %x\n", __func__, prot2); + ret = -3; + } else { + if (msr_dr != (tlb->attr & 1)) { + LOG_SWTLB("%s: AS doesn't match\n", __func__); + return -1; + } + + *prot = prot2; + if ((!rw && prot2 & PAGE_READ) || (rw && (prot2 & PAGE_WRITE))) { + LOG_SWTLB("%s: found TLB!\n", __func__); + return 0; + } + + LOG_SWTLB("%s: PAGE_READ/WRITE doesn't match: %x\n", __func__, prot2); + ret = -2; + } + + return ret; +} + +static int mmubooke_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong address, int rw, + int access_type) +{ + ppcemb_tlb_t *tlb; + target_phys_addr_t raddr; + int i, ret; + + ret = -1; + raddr = (target_phys_addr_t)-1ULL; + for (i = 0; i < env->nb_tlb; i++) { + tlb = &env->tlb.tlbe[i]; + ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, rw, + access_type, i); + if (!ret) { + break; + } + } + + if (ret >= 0) { + ctx->raddr = raddr; + LOG_SWTLB("%s: access granted " TARGET_FMT_lx " => " TARGET_FMT_plx + " %d %d\n", __func__, address, ctx->raddr, ctx->prot, + ret); + } else { + LOG_SWTLB("%s: access refused " TARGET_FMT_lx " => " TARGET_FMT_plx + " %d %d\n", __func__, address, raddr, ctx->prot, ret); + } + + return ret; +} + +void booke206_flush_tlb(CPUPPCState *env, int flags, const int check_iprot) +{ + int tlb_size; + int i, j; + ppcmas_tlb_t *tlb = env->tlb.tlbm; + + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + if (flags & (1 << i)) { + tlb_size = booke206_tlb_size(env, i); + for (j = 0; j < tlb_size; j++) { + if (!check_iprot || !(tlb[j].mas1 & MAS1_IPROT)) { + tlb[j].mas1 &= ~MAS1_VALID; + } + } + } + tlb += booke206_tlb_size(env, i); + } + + tlb_flush(env, 1); +} + +target_phys_addr_t booke206_tlb_to_page_size(CPUPPCState *env, + ppcmas_tlb_t *tlb) +{ + int tlbm_size; + + tlbm_size = (tlb->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; + + return 1024ULL << tlbm_size; +} + +/* TLB check function for MAS based SoftTLBs */ +int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, + target_phys_addr_t *raddrp, + target_ulong address, uint32_t pid) +{ + target_ulong mask; + uint32_t tlb_pid; + + /* Check valid flag */ + if (!(tlb->mas1 & MAS1_VALID)) { + return -1; + } + + mask = ~(booke206_tlb_to_page_size(env, tlb) - 1); + LOG_SWTLB("%s: TLB ADDR=0x" TARGET_FMT_lx " PID=0x%x MAS1=0x%x MAS2=0x%" + PRIx64 " mask=0x" TARGET_FMT_lx " MAS7_3=0x%" PRIx64 " MAS8=%x\n", + __func__, address, pid, tlb->mas1, tlb->mas2, mask, tlb->mas7_3, + tlb->mas8); + + /* Check PID */ + tlb_pid = (tlb->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT; + if (tlb_pid != 0 && tlb_pid != pid) { + return -1; + } + + /* Check effective address */ + if ((address & mask) != (tlb->mas2 & MAS2_EPN_MASK)) { + return -1; + } + + if (raddrp) { + *raddrp = (tlb->mas7_3 & mask) | (address & ~mask); + } + + return 0; +} + +static int mmubooke206_check_tlb(CPUPPCState *env, ppcmas_tlb_t *tlb, + target_phys_addr_t *raddr, int *prot, + target_ulong address, int rw, + int access_type) +{ + int ret; + int prot2 = 0; + + if (ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID]) >= 0) { + goto found_tlb; + } + + if (env->spr[SPR_BOOKE_PID1] && + ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID1]) >= 0) { + goto found_tlb; + } + + if (env->spr[SPR_BOOKE_PID2] && + ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID2]) >= 0) { + goto found_tlb; + } + + LOG_SWTLB("%s: TLB entry not found\n", __func__); + return -1; + +found_tlb: + + if (msr_pr != 0) { + if (tlb->mas7_3 & MAS3_UR) { + prot2 |= PAGE_READ; + } + if (tlb->mas7_3 & MAS3_UW) { + prot2 |= PAGE_WRITE; + } + if (tlb->mas7_3 & MAS3_UX) { + prot2 |= PAGE_EXEC; + } + } else { + if (tlb->mas7_3 & MAS3_SR) { + prot2 |= PAGE_READ; + } + if (tlb->mas7_3 & MAS3_SW) { + prot2 |= PAGE_WRITE; + } + if (tlb->mas7_3 & MAS3_SX) { + prot2 |= PAGE_EXEC; + } + } + + /* Check the address space and permissions */ + if (access_type == ACCESS_CODE) { + if (msr_ir != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { + LOG_SWTLB("%s: AS doesn't match\n", __func__); + return -1; + } + + *prot = prot2; + if (prot2 & PAGE_EXEC) { + LOG_SWTLB("%s: good TLB!\n", __func__); + return 0; + } + + LOG_SWTLB("%s: no PAGE_EXEC: %x\n", __func__, prot2); + ret = -3; + } else { + if (msr_dr != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { + LOG_SWTLB("%s: AS doesn't match\n", __func__); + return -1; + } + + *prot = prot2; + if ((!rw && prot2 & PAGE_READ) || (rw && (prot2 & PAGE_WRITE))) { + LOG_SWTLB("%s: found TLB!\n", __func__); + return 0; + } + + LOG_SWTLB("%s: PAGE_READ/WRITE doesn't match: %x\n", __func__, prot2); + ret = -2; + } + + return ret; +} + +static int mmubooke206_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong address, int rw, + int access_type) +{ + ppcmas_tlb_t *tlb; + target_phys_addr_t raddr; + int i, j, ret; + + ret = -1; + raddr = (target_phys_addr_t)-1ULL; + + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + int ways = booke206_tlb_ways(env, i); + + for (j = 0; j < ways; j++) { + tlb = booke206_get_tlbm(env, i, address, j); + if (!tlb) { + continue; + } + ret = mmubooke206_check_tlb(env, tlb, &raddr, &ctx->prot, address, + rw, access_type); + if (ret != -1) { + goto found_tlb; + } + } + } + +found_tlb: + + if (ret >= 0) { + ctx->raddr = raddr; + LOG_SWTLB("%s: access granted " TARGET_FMT_lx " => " TARGET_FMT_plx + " %d %d\n", __func__, address, ctx->raddr, ctx->prot, + ret); + } else { + LOG_SWTLB("%s: access refused " TARGET_FMT_lx " => " TARGET_FMT_plx + " %d %d\n", __func__, address, raddr, ctx->prot, ret); + } + + return ret; +} + +static const char *book3e_tsize_to_str[32] = { + "1K", "2K", "4K", "8K", "16K", "32K", "64K", "128K", "256K", "512K", + "1M", "2M", "4M", "8M", "16M", "32M", "64M", "128M", "256M", "512M", + "1G", "2G", "4G", "8G", "16G", "32G", "64G", "128G", "256G", "512G", + "1T", "2T" +}; + +static void mmubooke_dump_mmu(FILE *f, fprintf_function cpu_fprintf, + CPUPPCState *env) +{ + ppcemb_tlb_t *entry; + int i; + + if (kvm_enabled() && !env->kvm_sw_tlb) { + cpu_fprintf(f, "Cannot access KVM TLB\n"); + return; + } + + cpu_fprintf(f, "\nTLB:\n"); + cpu_fprintf(f, "Effective Physical Size PID Prot " + "Attr\n"); + + entry = &env->tlb.tlbe[0]; + for (i = 0; i < env->nb_tlb; i++, entry++) { + target_phys_addr_t ea, pa; + target_ulong mask; + uint64_t size = (uint64_t)entry->size; + char size_buf[20]; + + /* Check valid flag */ + if (!(entry->prot & PAGE_VALID)) { + continue; + } + + mask = ~(entry->size - 1); + ea = entry->EPN & mask; + pa = entry->RPN & mask; +#if (TARGET_PHYS_ADDR_BITS >= 36) + /* Extend the physical address to 36 bits */ + pa |= (target_phys_addr_t)(entry->RPN & 0xF) << 32; +#endif + size /= 1024; + if (size >= 1024) { + snprintf(size_buf, sizeof(size_buf), "%3" PRId64 "M", size / 1024); + } else { + snprintf(size_buf, sizeof(size_buf), "%3" PRId64 "k", size); + } + cpu_fprintf(f, "0x%016" PRIx64 " 0x%016" PRIx64 " %s %-5u %08x %08x\n", + (uint64_t)ea, (uint64_t)pa, size_buf, (uint32_t)entry->PID, + entry->prot, entry->attr); + } + +} + +static void mmubooke206_dump_one_tlb(FILE *f, fprintf_function cpu_fprintf, + CPUPPCState *env, int tlbn, int offset, + int tlbsize) +{ + ppcmas_tlb_t *entry; + int i; + + cpu_fprintf(f, "\nTLB%d:\n", tlbn); + cpu_fprintf(f, "Effective Physical Size TID TS SRWX" + " URWX WIMGE U0123\n"); + + entry = &env->tlb.tlbm[offset]; + for (i = 0; i < tlbsize; i++, entry++) { + target_phys_addr_t ea, pa, size; + int tsize; + + if (!(entry->mas1 & MAS1_VALID)) { + continue; + } + + tsize = (entry->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; + size = 1024ULL << tsize; + ea = entry->mas2 & ~(size - 1); + pa = entry->mas7_3 & ~(size - 1); + + cpu_fprintf(f, "0x%016" PRIx64 " 0x%016" PRIx64 " %4s %-5u %1u S%c%c%c" + "U%c%c%c %c%c%c%c%c U%c%c%c%c\n", + (uint64_t)ea, (uint64_t)pa, + book3e_tsize_to_str[tsize], + (entry->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT, + (entry->mas1 & MAS1_TS) >> MAS1_TS_SHIFT, + entry->mas7_3 & MAS3_SR ? 'R' : '-', + entry->mas7_3 & MAS3_SW ? 'W' : '-', + entry->mas7_3 & MAS3_SX ? 'X' : '-', + entry->mas7_3 & MAS3_UR ? 'R' : '-', + entry->mas7_3 & MAS3_UW ? 'W' : '-', + entry->mas7_3 & MAS3_UX ? 'X' : '-', + entry->mas2 & MAS2_W ? 'W' : '-', + entry->mas2 & MAS2_I ? 'I' : '-', + entry->mas2 & MAS2_M ? 'M' : '-', + entry->mas2 & MAS2_G ? 'G' : '-', + entry->mas2 & MAS2_E ? 'E' : '-', + entry->mas7_3 & MAS3_U0 ? '0' : '-', + entry->mas7_3 & MAS3_U1 ? '1' : '-', + entry->mas7_3 & MAS3_U2 ? '2' : '-', + entry->mas7_3 & MAS3_U3 ? '3' : '-'); + } +} + +static void mmubooke206_dump_mmu(FILE *f, fprintf_function cpu_fprintf, + CPUPPCState *env) +{ + int offset = 0; + int i; + + if (kvm_enabled() && !env->kvm_sw_tlb) { + cpu_fprintf(f, "Cannot access KVM TLB\n"); + return; + } + + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + int size = booke206_tlb_size(env, i); + + if (size == 0) { + continue; + } + + mmubooke206_dump_one_tlb(f, cpu_fprintf, env, i, offset, size); + offset += size; + } +} + +#if defined(TARGET_PPC64) +static void mmubooks_dump_mmu(FILE *f, fprintf_function cpu_fprintf, + CPUPPCState *env) +{ + int i; + uint64_t slbe, slbv; + + cpu_synchronize_state(env); + + cpu_fprintf(f, "SLB\tESID\t\t\tVSID\n"); + for (i = 0; i < env->slb_nr; i++) { + slbe = env->slb[i].esid; + slbv = env->slb[i].vsid; + if (slbe == 0 && slbv == 0) { + continue; + } + cpu_fprintf(f, "%d\t0x%016" PRIx64 "\t0x%016" PRIx64 "\n", + i, slbe, slbv); + } +} +#endif + +void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env) +{ + switch (env->mmu_model) { + case POWERPC_MMU_BOOKE: + mmubooke_dump_mmu(f, cpu_fprintf, env); + break; + case POWERPC_MMU_BOOKE206: + mmubooke206_dump_mmu(f, cpu_fprintf, env); + break; +#if defined(TARGET_PPC64) + case POWERPC_MMU_64B: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_06d: + mmubooks_dump_mmu(f, cpu_fprintf, env); + break; +#endif + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented\n", __func__); + } +} + +static inline int check_physical(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, int rw) +{ + int in_plb, ret; + + ctx->raddr = eaddr; + ctx->prot = PAGE_READ | PAGE_EXEC; + ret = 0; + switch (env->mmu_model) { + case POWERPC_MMU_32B: + case POWERPC_MMU_601: + case POWERPC_MMU_SOFT_6xx: + case POWERPC_MMU_SOFT_74xx: + case POWERPC_MMU_SOFT_4xx: + case POWERPC_MMU_REAL: + case POWERPC_MMU_BOOKE: + ctx->prot |= PAGE_WRITE; + break; +#if defined(TARGET_PPC64) + case POWERPC_MMU_620: + case POWERPC_MMU_64B: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_06d: + /* Real address are 60 bits long */ + ctx->raddr &= 0x0FFFFFFFFFFFFFFFULL; + ctx->prot |= PAGE_WRITE; + break; +#endif + case POWERPC_MMU_SOFT_4xx_Z: + if (unlikely(msr_pe != 0)) { + /* 403 family add some particular protections, + * using PBL/PBU registers for accesses with no translation. + */ + in_plb = + /* Check PLB validity */ + (env->pb[0] < env->pb[1] && + /* and address in plb area */ + eaddr >= env->pb[0] && eaddr < env->pb[1]) || + (env->pb[2] < env->pb[3] && + eaddr >= env->pb[2] && eaddr < env->pb[3]) ? 1 : 0; + if (in_plb ^ msr_px) { + /* Access in protected area */ + if (rw == 1) { + /* Access is not allowed */ + ret = -2; + } + } else { + /* Read-write access is allowed */ + ctx->prot |= PAGE_WRITE; + } + } + break; + case POWERPC_MMU_MPC8xx: + /* XXX: TODO */ + cpu_abort(env, "MPC8xx MMU model is not implemented\n"); + break; + case POWERPC_MMU_BOOKE206: + cpu_abort(env, "BookE 2.06 MMU doesn't have physical real mode\n"); + break; + default: + cpu_abort(env, "Unknown or invalid MMU model\n"); + return -1; + } + + return ret; +} + +int get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong eaddr, + int rw, int access_type) +{ + int ret; + +#if 0 + qemu_log("%s\n", __func__); +#endif + if ((access_type == ACCESS_CODE && msr_ir == 0) || + (access_type != ACCESS_CODE && msr_dr == 0)) { + if (env->mmu_model == POWERPC_MMU_BOOKE) { + /* The BookE MMU always performs address translation. The + IS and DS bits only affect the address space. */ + ret = mmubooke_get_physical_address(env, ctx, eaddr, + rw, access_type); + } else if (env->mmu_model == POWERPC_MMU_BOOKE206) { + ret = mmubooke206_get_physical_address(env, ctx, eaddr, rw, + access_type); + } else { + /* No address translation. */ + ret = check_physical(env, ctx, eaddr, rw); + } + } else { + ret = -1; + switch (env->mmu_model) { + case POWERPC_MMU_32B: + case POWERPC_MMU_601: + case POWERPC_MMU_SOFT_6xx: + case POWERPC_MMU_SOFT_74xx: + /* Try to find a BAT */ + if (env->nb_BATs != 0) { + ret = get_bat(env, ctx, eaddr, rw, access_type); + } +#if defined(TARGET_PPC64) + case POWERPC_MMU_620: + case POWERPC_MMU_64B: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_06d: +#endif + if (ret < 0) { + /* We didn't match any BAT entry or don't have BATs */ + ret = get_segment(env, ctx, eaddr, rw, access_type); + } + break; + case POWERPC_MMU_SOFT_4xx: + case POWERPC_MMU_SOFT_4xx_Z: + ret = mmu40x_get_physical_address(env, ctx, eaddr, + rw, access_type); + break; + case POWERPC_MMU_BOOKE: + ret = mmubooke_get_physical_address(env, ctx, eaddr, + rw, access_type); + break; + case POWERPC_MMU_BOOKE206: + ret = mmubooke206_get_physical_address(env, ctx, eaddr, rw, + access_type); + break; + case POWERPC_MMU_MPC8xx: + /* XXX: TODO */ + cpu_abort(env, "MPC8xx MMU model is not implemented\n"); + break; + case POWERPC_MMU_REAL: + cpu_abort(env, "PowerPC in real mode do not do any translation\n"); + return -1; + default: + cpu_abort(env, "Unknown or invalid MMU model\n"); + return -1; + } + } +#if 0 + qemu_log("%s address " TARGET_FMT_lx " => %d " TARGET_FMT_plx "\n", + __func__, eaddr, ret, ctx->raddr); +#endif + + return ret; +} + +target_phys_addr_t cpu_get_phys_page_debug(CPUPPCState *env, target_ulong addr) +{ + mmu_ctx_t ctx; + + if (unlikely(get_physical_address(env, &ctx, addr, 0, ACCESS_INT) != 0)) { + return -1; + } + + return ctx.raddr & TARGET_PAGE_MASK; +} + +static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, + int rw) +{ + env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK; + env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK; + env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK; + env->spr[SPR_BOOKE_MAS3] = 0; + env->spr[SPR_BOOKE_MAS6] = 0; + env->spr[SPR_BOOKE_MAS7] = 0; + + /* AS */ + if (((rw == 2) && msr_ir) || ((rw != 2) && msr_dr)) { + env->spr[SPR_BOOKE_MAS1] |= MAS1_TS; + env->spr[SPR_BOOKE_MAS6] |= MAS6_SAS; + } + + env->spr[SPR_BOOKE_MAS1] |= MAS1_VALID; + env->spr[SPR_BOOKE_MAS2] |= address & MAS2_EPN_MASK; + + switch (env->spr[SPR_BOOKE_MAS4] & MAS4_TIDSELD_PIDZ) { + case MAS4_TIDSELD_PID0: + env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID] << MAS1_TID_SHIFT; + break; + case MAS4_TIDSELD_PID1: + env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID1] << MAS1_TID_SHIFT; + break; + case MAS4_TIDSELD_PID2: + env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID2] << MAS1_TID_SHIFT; + break; + } + + env->spr[SPR_BOOKE_MAS6] |= env->spr[SPR_BOOKE_PID] << 16; + + /* next victim logic */ + env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT; + env->last_way++; + env->last_way &= booke206_tlb_ways(env, 0) - 1; + env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT; +} + +/* Perform address translation */ +int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, + int mmu_idx) +{ + mmu_ctx_t ctx; + int access_type; + int ret = 0; + + if (rw == 2) { + /* code access */ + rw = 0; + access_type = ACCESS_CODE; + } else { + /* data access */ + access_type = env->access_type; + } + ret = get_physical_address(env, &ctx, address, rw, access_type); + if (ret == 0) { + tlb_set_page(env, address & TARGET_PAGE_MASK, + ctx.raddr & TARGET_PAGE_MASK, ctx.prot, + mmu_idx, TARGET_PAGE_SIZE); + ret = 0; + } else if (ret < 0) { + LOG_MMU_STATE(env); + if (access_type == ACCESS_CODE) { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + switch (env->mmu_model) { + case POWERPC_MMU_SOFT_6xx: + env->exception_index = POWERPC_EXCP_IFTLB; + env->error_code = 1 << 18; + env->spr[SPR_IMISS] = address; + env->spr[SPR_ICMP] = 0x80000000 | ctx.ptem; + goto tlb_miss; + case POWERPC_MMU_SOFT_74xx: + env->exception_index = POWERPC_EXCP_IFTLB; + goto tlb_miss_74xx; + case POWERPC_MMU_SOFT_4xx: + case POWERPC_MMU_SOFT_4xx_Z: + env->exception_index = POWERPC_EXCP_ITLB; + env->error_code = 0; + env->spr[SPR_40x_DEAR] = address; + env->spr[SPR_40x_ESR] = 0x00000000; + break; + case POWERPC_MMU_32B: + case POWERPC_MMU_601: +#if defined(TARGET_PPC64) + case POWERPC_MMU_620: + case POWERPC_MMU_64B: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_06d: +#endif + env->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x40000000; + break; + case POWERPC_MMU_BOOKE206: + booke206_update_mas_tlb_miss(env, address, rw); + /* fall through */ + case POWERPC_MMU_BOOKE: + env->exception_index = POWERPC_EXCP_ITLB; + env->error_code = 0; + env->spr[SPR_BOOKE_DEAR] = address; + return -1; + case POWERPC_MMU_MPC8xx: + /* XXX: TODO */ + cpu_abort(env, "MPC8xx MMU model is not implemented\n"); + break; + case POWERPC_MMU_REAL: + cpu_abort(env, "PowerPC in real mode should never raise " + "any MMU exceptions\n"); + return -1; + default: + cpu_abort(env, "Unknown or invalid MMU model\n"); + return -1; + } + break; + case -2: + /* Access rights violation */ + env->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x08000000; + break; + case -3: + /* No execute protection violation */ + if ((env->mmu_model == POWERPC_MMU_BOOKE) || + (env->mmu_model == POWERPC_MMU_BOOKE206)) { + env->spr[SPR_BOOKE_ESR] = 0x00000000; + } + env->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x10000000; + break; + case -4: + /* Direct store exception */ + /* No code fetch is allowed in direct-store areas */ + env->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x10000000; + break; +#if defined(TARGET_PPC64) + case -5: + /* No match in segment table */ + if (env->mmu_model == POWERPC_MMU_620) { + env->exception_index = POWERPC_EXCP_ISI; + /* XXX: this might be incorrect */ + env->error_code = 0x40000000; + } else { + env->exception_index = POWERPC_EXCP_ISEG; + env->error_code = 0; + } + break; +#endif + } + } else { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + switch (env->mmu_model) { + case POWERPC_MMU_SOFT_6xx: + if (rw == 1) { + env->exception_index = POWERPC_EXCP_DSTLB; + env->error_code = 1 << 16; + } else { + env->exception_index = POWERPC_EXCP_DLTLB; + env->error_code = 0; + } + env->spr[SPR_DMISS] = address; + env->spr[SPR_DCMP] = 0x80000000 | ctx.ptem; + tlb_miss: + env->error_code |= ctx.key << 19; + env->spr[SPR_HASH1] = env->htab_base + + get_pteg_offset(env, ctx.hash[0], HASH_PTE_SIZE_32); + env->spr[SPR_HASH2] = env->htab_base + + get_pteg_offset(env, ctx.hash[1], HASH_PTE_SIZE_32); + break; + case POWERPC_MMU_SOFT_74xx: + if (rw == 1) { + env->exception_index = POWERPC_EXCP_DSTLB; + } else { + env->exception_index = POWERPC_EXCP_DLTLB; + } + tlb_miss_74xx: + /* Implement LRU algorithm */ + env->error_code = ctx.key << 19; + env->spr[SPR_TLBMISS] = (address & ~((target_ulong)0x3)) | + ((env->last_way + 1) & (env->nb_ways - 1)); + env->spr[SPR_PTEHI] = 0x80000000 | ctx.ptem; + break; + case POWERPC_MMU_SOFT_4xx: + case POWERPC_MMU_SOFT_4xx_Z: + env->exception_index = POWERPC_EXCP_DTLB; + env->error_code = 0; + env->spr[SPR_40x_DEAR] = address; + if (rw) { + env->spr[SPR_40x_ESR] = 0x00800000; + } else { + env->spr[SPR_40x_ESR] = 0x00000000; + } + break; + case POWERPC_MMU_32B: + case POWERPC_MMU_601: +#if defined(TARGET_PPC64) + case POWERPC_MMU_620: + case POWERPC_MMU_64B: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_06d: +#endif + env->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = address; + if (rw == 1) { + env->spr[SPR_DSISR] = 0x42000000; + } else { + env->spr[SPR_DSISR] = 0x40000000; + } + break; + case POWERPC_MMU_MPC8xx: + /* XXX: TODO */ + cpu_abort(env, "MPC8xx MMU model is not implemented\n"); + break; + case POWERPC_MMU_BOOKE206: + booke206_update_mas_tlb_miss(env, address, rw); + /* fall through */ + case POWERPC_MMU_BOOKE: + env->exception_index = POWERPC_EXCP_DTLB; + env->error_code = 0; + env->spr[SPR_BOOKE_DEAR] = address; + env->spr[SPR_BOOKE_ESR] = rw ? ESR_ST : 0; + return -1; + case POWERPC_MMU_REAL: + cpu_abort(env, "PowerPC in real mode should never raise " + "any MMU exceptions\n"); + return -1; + default: + cpu_abort(env, "Unknown or invalid MMU model\n"); + return -1; + } + break; + case -2: + /* Access rights violation */ + env->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + if (env->mmu_model == POWERPC_MMU_SOFT_4xx + || env->mmu_model == POWERPC_MMU_SOFT_4xx_Z) { + env->spr[SPR_40x_DEAR] = address; + if (rw) { + env->spr[SPR_40x_ESR] |= 0x00800000; + } + } else if ((env->mmu_model == POWERPC_MMU_BOOKE) || + (env->mmu_model == POWERPC_MMU_BOOKE206)) { + env->spr[SPR_BOOKE_DEAR] = address; + env->spr[SPR_BOOKE_ESR] = rw ? ESR_ST : 0; + } else { + env->spr[SPR_DAR] = address; + if (rw == 1) { + env->spr[SPR_DSISR] = 0x0A000000; + } else { + env->spr[SPR_DSISR] = 0x08000000; + } + } + break; + case -4: + /* Direct store exception */ + switch (access_type) { + case ACCESS_FLOAT: + /* Floating point load/store */ + env->exception_index = POWERPC_EXCP_ALIGN; + env->error_code = POWERPC_EXCP_ALIGN_FP; + env->spr[SPR_DAR] = address; + break; + case ACCESS_RES: + /* lwarx, ldarx or stwcx. */ + env->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = address; + if (rw == 1) { + env->spr[SPR_DSISR] = 0x06000000; + } else { + env->spr[SPR_DSISR] = 0x04000000; + } + break; + case ACCESS_EXT: + /* eciwx or ecowx */ + env->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = address; + if (rw == 1) { + env->spr[SPR_DSISR] = 0x06100000; + } else { + env->spr[SPR_DSISR] = 0x04100000; + } + break; + default: + printf("DSI: invalid exception (%d)\n", ret); + env->exception_index = POWERPC_EXCP_PROGRAM; + env->error_code = + POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL; + env->spr[SPR_DAR] = address; + break; + } + break; +#if defined(TARGET_PPC64) + case -5: + /* No match in segment table */ + if (env->mmu_model == POWERPC_MMU_620) { + env->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = address; + /* XXX: this might be incorrect */ + if (rw == 1) { + env->spr[SPR_DSISR] = 0x42000000; + } else { + env->spr[SPR_DSISR] = 0x40000000; + } + } else { + env->exception_index = POWERPC_EXCP_DSEG; + env->error_code = 0; + env->spr[SPR_DAR] = address; + } + break; +#endif + } + } +#if 0 + printf("%s: set exception to %d %02x\n", __func__, + env->exception, env->error_code); +#endif + ret = 1; + } + + return ret; +} + +/*****************************************************************************/ +/* 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; + LOG_BATS("Flush BAT from " TARGET_FMT_lx " to " TARGET_FMT_lx " (" + TARGET_FMT_lx ")\n", base, end, mask); + for (page = base; page != end; page += TARGET_PAGE_SIZE) { + tlb_flush_page(env, page); + } + LOG_BATS("Flush done\n"); +} +#endif + +static inline void dump_store_bat(CPUPPCState *env, char ID, int ul, int nr, + target_ulong value) +{ + LOG_BATS("Set %cBAT%d%c to " TARGET_FMT_lx " (" TARGET_FMT_lx ")\n", ID, + nr, ul == 0 ? 'u' : 'l', value, env->nip); +} + +void helper_store_ibatu(CPUPPCState *env, uint32_t 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); +#else + tlb_flush(env, 1); +#endif + } +} + +void helper_store_ibatl(CPUPPCState *env, uint32_t nr, target_ulong value) +{ + dump_store_bat(env, 'I', 1, nr, value); + env->IBAT[1][nr] = value; +} + +void helper_store_dbatu(CPUPPCState *env, uint32_t 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 helper_store_dbatl(CPUPPCState *env, uint32_t nr, target_ulong value) +{ + dump_store_bat(env, 'D', 1, nr, value); + env->DBAT[1][nr] = value; +} + +void helper_store_601_batu(CPUPPCState *env, uint32_t nr, target_ulong value) +{ + target_ulong mask; +#if defined(FLUSH_ALL_TLBS) + int do_inval; +#endif + + dump_store_bat(env, 'I', 0, nr, value); + if (env->IBAT[0][nr] != value) { +#if defined(FLUSH_ALL_TLBS) + do_inval = 0; +#endif + mask = (env->IBAT[1][nr] << 17) & 0x0FFE0000UL; + if (env->IBAT[1][nr] & 0x40) { + /* Invalidate BAT only if it is valid */ +#if !defined(FLUSH_ALL_TLBS) + do_invalidate_BAT(env, env->IBAT[0][nr], mask); +#else + do_inval = 1; +#endif + } + /* When storing valid upper BAT, mask BEPI and BRPN + * and invalidate all TLBs covered by this BAT + */ + env->IBAT[0][nr] = (value & 0x00001FFFUL) | + (value & ~0x0001FFFFUL & ~mask); + env->DBAT[0][nr] = env->IBAT[0][nr]; + if (env->IBAT[1][nr] & 0x40) { +#if !defined(FLUSH_ALL_TLBS) + do_invalidate_BAT(env, env->IBAT[0][nr], mask); +#else + do_inval = 1; +#endif + } +#if defined(FLUSH_ALL_TLBS) + if (do_inval) { + tlb_flush(env, 1); + } +#endif + } +} + +void helper_store_601_batl(CPUPPCState *env, uint32_t nr, target_ulong value) +{ + target_ulong mask; +#if defined(FLUSH_ALL_TLBS) + int do_inval; +#endif + + dump_store_bat(env, 'I', 1, nr, value); + if (env->IBAT[1][nr] != value) { +#if defined(FLUSH_ALL_TLBS) + do_inval = 0; +#endif + if (env->IBAT[1][nr] & 0x40) { +#if !defined(FLUSH_ALL_TLBS) + mask = (env->IBAT[1][nr] << 17) & 0x0FFE0000UL; + do_invalidate_BAT(env, env->IBAT[0][nr], mask); +#else + do_inval = 1; +#endif + } + if (value & 0x40) { +#if !defined(FLUSH_ALL_TLBS) + mask = (value << 17) & 0x0FFE0000UL; + do_invalidate_BAT(env, env->IBAT[0][nr], mask); +#else + do_inval = 1; +#endif + } + env->IBAT[1][nr] = value; + env->DBAT[1][nr] = value; +#if defined(FLUSH_ALL_TLBS) + if (do_inval) { + tlb_flush(env, 1); + } +#endif + } +} + +/*****************************************************************************/ +/* TLB management */ +void ppc_tlb_invalidate_all(CPUPPCState *env) +{ + switch (env->mmu_model) { + case POWERPC_MMU_SOFT_6xx: + case POWERPC_MMU_SOFT_74xx: + ppc6xx_tlb_invalidate_all(env); + break; + case POWERPC_MMU_SOFT_4xx: + case POWERPC_MMU_SOFT_4xx_Z: + ppc4xx_tlb_invalidate_all(env); + break; + case POWERPC_MMU_REAL: + cpu_abort(env, "No TLB for PowerPC 4xx in real mode\n"); + break; + case POWERPC_MMU_MPC8xx: + /* XXX: TODO */ + cpu_abort(env, "MPC8xx MMU model is not implemented\n"); + break; + case POWERPC_MMU_BOOKE: + tlb_flush(env, 1); + break; + case POWERPC_MMU_BOOKE206: + booke206_flush_tlb(env, -1, 0); + break; + case POWERPC_MMU_32B: + case POWERPC_MMU_601: +#if defined(TARGET_PPC64) + case POWERPC_MMU_620: + case POWERPC_MMU_64B: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_06d: +#endif /* defined(TARGET_PPC64) */ + tlb_flush(env, 1); + break; + default: + /* XXX: TODO */ + cpu_abort(env, "Unknown MMU model\n"); + break; + } +} + +void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr) +{ +#if !defined(FLUSH_ALL_TLBS) + addr &= TARGET_PAGE_MASK; + switch (env->mmu_model) { + case POWERPC_MMU_SOFT_6xx: + case POWERPC_MMU_SOFT_74xx: + ppc6xx_tlb_invalidate_virt(env, addr, 0); + if (env->id_tlbs == 1) { + ppc6xx_tlb_invalidate_virt(env, addr, 1); + } + break; + case POWERPC_MMU_SOFT_4xx: + case POWERPC_MMU_SOFT_4xx_Z: + ppc4xx_tlb_invalidate_virt(env, addr, env->spr[SPR_40x_PID]); + break; + case POWERPC_MMU_REAL: + cpu_abort(env, "No TLB for PowerPC 4xx in real mode\n"); + break; + case POWERPC_MMU_MPC8xx: + /* XXX: TODO */ + cpu_abort(env, "MPC8xx MMU model is not implemented\n"); + break; + case POWERPC_MMU_BOOKE: + /* XXX: TODO */ + cpu_abort(env, "BookE MMU model is not implemented\n"); + break; + case POWERPC_MMU_BOOKE206: + /* XXX: TODO */ + cpu_abort(env, "BookE 2.06 MMU model is not implemented\n"); + break; + case POWERPC_MMU_32B: + case POWERPC_MMU_601: + /* tlbie invalidate TLBs for all segments */ + addr &= ~((target_ulong)-1ULL << 28); + /* XXX: this case should be optimized, + * giving a mask to tlb_flush_page + */ + tlb_flush_page(env, addr | (0x0 << 28)); + tlb_flush_page(env, addr | (0x1 << 28)); + tlb_flush_page(env, addr | (0x2 << 28)); + tlb_flush_page(env, addr | (0x3 << 28)); + tlb_flush_page(env, addr | (0x4 << 28)); + tlb_flush_page(env, addr | (0x5 << 28)); + tlb_flush_page(env, addr | (0x6 << 28)); + tlb_flush_page(env, addr | (0x7 << 28)); + tlb_flush_page(env, addr | (0x8 << 28)); + tlb_flush_page(env, addr | (0x9 << 28)); + tlb_flush_page(env, addr | (0xA << 28)); + tlb_flush_page(env, addr | (0xB << 28)); + tlb_flush_page(env, addr | (0xC << 28)); + tlb_flush_page(env, addr | (0xD << 28)); + tlb_flush_page(env, addr | (0xE << 28)); + tlb_flush_page(env, addr | (0xF << 28)); + break; +#if defined(TARGET_PPC64) + case POWERPC_MMU_620: + case POWERPC_MMU_64B: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_06d: + /* tlbie invalidate TLBs for all segments */ + /* XXX: given the fact that there are too many segments to invalidate, + * and we still don't have a tlb_flush_mask(env, n, mask) in QEMU, + * we just invalidate all TLBs + */ + tlb_flush(env, 1); + break; +#endif /* defined(TARGET_PPC64) */ + default: + /* XXX: TODO */ + cpu_abort(env, "Unknown MMU model\n"); + break; + } +#else + ppc_tlb_invalidate_all(env); +#endif +} + +/*****************************************************************************/ +/* Special registers manipulation */ +#if defined(TARGET_PPC64) +void ppc_store_asr(CPUPPCState *env, target_ulong value) +{ + if (env->asr != value) { + env->asr = value; + tlb_flush(env, 1); + } +} +#endif + +void ppc_store_sdr1(CPUPPCState *env, target_ulong value) +{ + LOG_MMU("%s: " TARGET_FMT_lx "\n", __func__, value); + if (env->spr[SPR_SDR1] != value) { + env->spr[SPR_SDR1] = value; +#if defined(TARGET_PPC64) + if (env->mmu_model & POWERPC_MMU_64) { + target_ulong htabsize = value & SDR_64_HTABSIZE; + + if (htabsize > 28) { + fprintf(stderr, "Invalid HTABSIZE 0x" TARGET_FMT_lx + " stored in SDR1\n", htabsize); + htabsize = 28; + } + env->htab_mask = (1ULL << (htabsize + 18)) - 1; + env->htab_base = value & SDR_64_HTABORG; + } else +#endif /* defined(TARGET_PPC64) */ + { + /* FIXME: Should check for valid HTABMASK values */ + env->htab_mask = ((value & SDR_32_HTABMASK) << 16) | 0xFFFF; + env->htab_base = value & SDR_32_HTABORG; + } + tlb_flush(env, 1); + } +} + +/* Segment registers load and store */ +target_ulong helper_load_sr(CPUPPCState *env, target_ulong sr_num) +{ +#if defined(TARGET_PPC64) + if (env->mmu_model & POWERPC_MMU_64) { + /* XXX */ + return 0; + } +#endif + return env->sr[sr_num]; +} + +void helper_store_sr(CPUPPCState *env, target_ulong srnum, target_ulong value) +{ + LOG_MMU("%s: reg=%d " TARGET_FMT_lx " " TARGET_FMT_lx "\n", __func__, + (int)srnum, value, env->sr[srnum]); +#if defined(TARGET_PPC64) + if (env->mmu_model & POWERPC_MMU_64) { + uint64_t rb = 0, rs = 0; + + /* ESID = srnum */ + rb |= ((uint32_t)srnum & 0xf) << 28; + /* Set the valid bit */ + rb |= 1 << 27; + /* Index = ESID */ + rb |= (uint32_t)srnum; + + /* VSID = VSID */ + rs |= (value & 0xfffffff) << 12; + /* flags = flags */ + rs |= ((value >> 27) & 0xf) << 8; + + ppc_store_slb(env, rb, rs); + } else +#endif + if (env->sr[srnum] != value) { + env->sr[srnum] = value; +/* Invalidating 256MB of virtual memory in 4kB pages is way longer than + flusing the whole TLB. */ +#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 + tlb_flush(env, 1); +#endif + } +} +#endif /* !defined(CONFIG_USER_ONLY) */ + +#if !defined(CONFIG_USER_ONLY) +/* SLB management */ +#if defined(TARGET_PPC64) +void helper_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs) +{ + if (ppc_store_slb(env, rb, rs) < 0) { + helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL); + } +} + +target_ulong helper_load_slb_esid(CPUPPCState *env, target_ulong rb) +{ + target_ulong rt = 0; + + if (ppc_load_slb_esid(env, rb, &rt) < 0) { + helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL); + } + return rt; +} + +target_ulong helper_load_slb_vsid(CPUPPCState *env, target_ulong rb) +{ + target_ulong rt = 0; + + if (ppc_load_slb_vsid(env, rb, &rt) < 0) { + helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL); + } + return rt; +} +#endif /* defined(TARGET_PPC64) */ + +/* TLB management */ +void helper_tlbia(CPUPPCState *env) +{ + ppc_tlb_invalidate_all(env); +} + +void helper_tlbie(CPUPPCState *env, target_ulong addr) +{ + ppc_tlb_invalidate_one(env, addr); +} + +/* Software driven TLBs management */ +/* PowerPC 602/603 software TLB load instructions helpers */ +static void do_6xx_tlb(CPUPPCState *env, target_ulong new_EPN, int is_code) +{ + target_ulong RPN, CMP, EPN; + int way; + + RPN = env->spr[SPR_RPA]; + if (is_code) { + CMP = env->spr[SPR_ICMP]; + EPN = env->spr[SPR_IMISS]; + } else { + CMP = env->spr[SPR_DCMP]; + EPN = env->spr[SPR_DMISS]; + } + way = (env->spr[SPR_SRR1] >> 17) & 1; + (void)EPN; /* avoid a compiler warning */ + LOG_SWTLB("%s: EPN " TARGET_FMT_lx " " TARGET_FMT_lx " PTE0 " TARGET_FMT_lx + " PTE1 " TARGET_FMT_lx " way %d\n", __func__, new_EPN, EPN, CMP, + RPN, way); + /* Store this TLB */ + ppc6xx_tlb_store(env, (uint32_t)(new_EPN & TARGET_PAGE_MASK), + way, is_code, CMP, RPN); +} + +void helper_6xx_tlbd(CPUPPCState *env, target_ulong EPN) +{ + do_6xx_tlb(env, EPN, 0); +} + +void helper_6xx_tlbi(CPUPPCState *env, target_ulong EPN) +{ + do_6xx_tlb(env, EPN, 1); +} + +/* PowerPC 74xx software TLB load instructions helpers */ +static void do_74xx_tlb(CPUPPCState *env, target_ulong new_EPN, int is_code) +{ + target_ulong RPN, CMP, EPN; + int way; + + RPN = env->spr[SPR_PTELO]; + CMP = env->spr[SPR_PTEHI]; + EPN = env->spr[SPR_TLBMISS] & ~0x3; + way = env->spr[SPR_TLBMISS] & 0x3; + (void)EPN; /* avoid a compiler warning */ + LOG_SWTLB("%s: EPN " TARGET_FMT_lx " " TARGET_FMT_lx " PTE0 " TARGET_FMT_lx + " PTE1 " TARGET_FMT_lx " way %d\n", __func__, new_EPN, EPN, CMP, + RPN, way); + /* Store this TLB */ + ppc6xx_tlb_store(env, (uint32_t)(new_EPN & TARGET_PAGE_MASK), + way, is_code, CMP, RPN); +} + +void helper_74xx_tlbd(CPUPPCState *env, target_ulong EPN) +{ + do_74xx_tlb(env, EPN, 0); +} + +void helper_74xx_tlbi(CPUPPCState *env, target_ulong EPN) +{ + do_74xx_tlb(env, EPN, 1); +} + +/*****************************************************************************/ +/* PowerPC 601 specific instructions (POWER bridge) */ + +target_ulong helper_rac(CPUPPCState *env, target_ulong addr) +{ + mmu_ctx_t ctx; + int nb_BATs; + target_ulong ret = 0; + + /* We don't have to generate many instances of this instruction, + * as rac is supervisor only. + */ + /* XXX: FIX THIS: Pretend we have no BAT */ + nb_BATs = env->nb_BATs; + env->nb_BATs = 0; + if (get_physical_address(env, &ctx, addr, 0, ACCESS_INT) == 0) { + ret = ctx.raddr; + } + env->nb_BATs = nb_BATs; + return ret; +} + +static inline target_ulong booke_tlb_to_page_size(int size) +{ + return 1024 << (2 * size); +} + +static inline int booke_page_size_to_tlb(target_ulong page_size) +{ + int size; + + switch (page_size) { + case 0x00000400UL: + size = 0x0; + break; + case 0x00001000UL: + size = 0x1; + break; + case 0x00004000UL: + size = 0x2; + break; + case 0x00010000UL: + size = 0x3; + break; + case 0x00040000UL: + size = 0x4; + break; + case 0x00100000UL: + size = 0x5; + break; + case 0x00400000UL: + size = 0x6; + break; + case 0x01000000UL: + size = 0x7; + break; + case 0x04000000UL: + size = 0x8; + break; + case 0x10000000UL: + size = 0x9; + break; + case 0x40000000UL: + size = 0xA; + break; +#if defined(TARGET_PPC64) + case 0x000100000000ULL: + size = 0xB; + break; + case 0x000400000000ULL: + size = 0xC; + break; + case 0x001000000000ULL: + size = 0xD; + break; + case 0x004000000000ULL: + size = 0xE; + break; + case 0x010000000000ULL: + size = 0xF; + break; +#endif + default: + size = -1; + break; + } + + return size; +} + +/* Helpers for 4xx TLB management */ +#define PPC4XX_TLB_ENTRY_MASK 0x0000003f /* Mask for 64 TLB entries */ + +#define PPC4XX_TLBHI_V 0x00000040 +#define PPC4XX_TLBHI_E 0x00000020 +#define PPC4XX_TLBHI_SIZE_MIN 0 +#define PPC4XX_TLBHI_SIZE_MAX 7 +#define PPC4XX_TLBHI_SIZE_DEFAULT 1 +#define PPC4XX_TLBHI_SIZE_SHIFT 7 +#define PPC4XX_TLBHI_SIZE_MASK 0x00000007 + +#define PPC4XX_TLBLO_EX 0x00000200 +#define PPC4XX_TLBLO_WR 0x00000100 +#define PPC4XX_TLBLO_ATTR_MASK 0x000000FF +#define PPC4XX_TLBLO_RPN_MASK 0xFFFFFC00 + +target_ulong helper_4xx_tlbre_hi(CPUPPCState *env, target_ulong entry) +{ + ppcemb_tlb_t *tlb; + target_ulong ret; + int size; + + entry &= PPC4XX_TLB_ENTRY_MASK; + tlb = &env->tlb.tlbe[entry]; + ret = tlb->EPN; + if (tlb->prot & PAGE_VALID) { + ret |= PPC4XX_TLBHI_V; + } + size = booke_page_size_to_tlb(tlb->size); + if (size < PPC4XX_TLBHI_SIZE_MIN || size > PPC4XX_TLBHI_SIZE_MAX) { + size = PPC4XX_TLBHI_SIZE_DEFAULT; + } + ret |= size << PPC4XX_TLBHI_SIZE_SHIFT; + env->spr[SPR_40x_PID] = tlb->PID; + return ret; +} + +target_ulong helper_4xx_tlbre_lo(CPUPPCState *env, target_ulong entry) +{ + ppcemb_tlb_t *tlb; + target_ulong ret; + + entry &= PPC4XX_TLB_ENTRY_MASK; + tlb = &env->tlb.tlbe[entry]; + ret = tlb->RPN; + if (tlb->prot & PAGE_EXEC) { + ret |= PPC4XX_TLBLO_EX; + } + if (tlb->prot & PAGE_WRITE) { + ret |= PPC4XX_TLBLO_WR; + } + return ret; +} + +void helper_4xx_tlbwe_hi(CPUPPCState *env, target_ulong entry, + target_ulong val) +{ + ppcemb_tlb_t *tlb; + target_ulong page, end; + + LOG_SWTLB("%s entry %d val " TARGET_FMT_lx "\n", __func__, (int)entry, + val); + entry &= PPC4XX_TLB_ENTRY_MASK; + tlb = &env->tlb.tlbe[entry]; + /* Invalidate previous TLB (if it's valid) */ + if (tlb->prot & PAGE_VALID) { + end = tlb->EPN + tlb->size; + LOG_SWTLB("%s: invalidate old TLB %d start " TARGET_FMT_lx " end " + TARGET_FMT_lx "\n", __func__, (int)entry, tlb->EPN, end); + for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) { + tlb_flush_page(env, page); + } + } + tlb->size = booke_tlb_to_page_size((val >> PPC4XX_TLBHI_SIZE_SHIFT) + & PPC4XX_TLBHI_SIZE_MASK); + /* We cannot handle TLB size < TARGET_PAGE_SIZE. + * If this ever occurs, one should use the ppcemb target instead + * of the ppc or ppc64 one + */ + if ((val & PPC4XX_TLBHI_V) && tlb->size < TARGET_PAGE_SIZE) { + cpu_abort(env, "TLB size " TARGET_FMT_lu " < %u " + "are not supported (%d)\n", + tlb->size, TARGET_PAGE_SIZE, (int)((val >> 7) & 0x7)); + } + tlb->EPN = val & ~(tlb->size - 1); + if (val & PPC4XX_TLBHI_V) { + tlb->prot |= PAGE_VALID; + if (val & PPC4XX_TLBHI_E) { + /* XXX: TO BE FIXED */ + cpu_abort(env, + "Little-endian TLB entries are not supported by now\n"); + } + } else { + tlb->prot &= ~PAGE_VALID; + } + tlb->PID = env->spr[SPR_40x_PID]; /* PID */ + LOG_SWTLB("%s: set up TLB %d RPN " TARGET_FMT_plx " EPN " TARGET_FMT_lx + " size " TARGET_FMT_lx " prot %c%c%c%c PID %d\n", __func__, + (int)entry, tlb->RPN, tlb->EPN, tlb->size, + tlb->prot & PAGE_READ ? 'r' : '-', + tlb->prot & PAGE_WRITE ? 'w' : '-', + tlb->prot & PAGE_EXEC ? 'x' : '-', + tlb->prot & PAGE_VALID ? 'v' : '-', (int)tlb->PID); + /* Invalidate new TLB (if valid) */ + if (tlb->prot & PAGE_VALID) { + end = tlb->EPN + tlb->size; + LOG_SWTLB("%s: invalidate TLB %d start " TARGET_FMT_lx " end " + TARGET_FMT_lx "\n", __func__, (int)entry, tlb->EPN, end); + for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) { + tlb_flush_page(env, page); + } + } +} + +void helper_4xx_tlbwe_lo(CPUPPCState *env, target_ulong entry, + target_ulong val) +{ + ppcemb_tlb_t *tlb; + + LOG_SWTLB("%s entry %i val " TARGET_FMT_lx "\n", __func__, (int)entry, + val); + entry &= PPC4XX_TLB_ENTRY_MASK; + tlb = &env->tlb.tlbe[entry]; + tlb->attr = val & PPC4XX_TLBLO_ATTR_MASK; + tlb->RPN = val & PPC4XX_TLBLO_RPN_MASK; + tlb->prot = PAGE_READ; + if (val & PPC4XX_TLBLO_EX) { + tlb->prot |= PAGE_EXEC; + } + if (val & PPC4XX_TLBLO_WR) { + tlb->prot |= PAGE_WRITE; + } + LOG_SWTLB("%s: set up TLB %d RPN " TARGET_FMT_plx " EPN " TARGET_FMT_lx + " size " TARGET_FMT_lx " prot %c%c%c%c PID %d\n", __func__, + (int)entry, tlb->RPN, tlb->EPN, tlb->size, + tlb->prot & PAGE_READ ? 'r' : '-', + tlb->prot & PAGE_WRITE ? 'w' : '-', + tlb->prot & PAGE_EXEC ? 'x' : '-', + tlb->prot & PAGE_VALID ? 'v' : '-', (int)tlb->PID); +} + +target_ulong helper_4xx_tlbsx(CPUPPCState *env, target_ulong address) +{ + return ppcemb_tlb_search(env, address, env->spr[SPR_40x_PID]); +} + +/* PowerPC 440 TLB management */ +void helper_440_tlbwe(CPUPPCState *env, uint32_t word, target_ulong entry, + target_ulong value) +{ + ppcemb_tlb_t *tlb; + target_ulong EPN, RPN, size; + int do_flush_tlbs; + + LOG_SWTLB("%s word %d entry %d value " TARGET_FMT_lx "\n", + __func__, word, (int)entry, value); + do_flush_tlbs = 0; + entry &= 0x3F; + tlb = &env->tlb.tlbe[entry]; + switch (word) { + default: + /* Just here to please gcc */ + case 0: + EPN = value & 0xFFFFFC00; + if ((tlb->prot & PAGE_VALID) && EPN != tlb->EPN) { + do_flush_tlbs = 1; + } + tlb->EPN = EPN; + size = booke_tlb_to_page_size((value >> 4) & 0xF); + if ((tlb->prot & PAGE_VALID) && tlb->size < size) { + do_flush_tlbs = 1; + } + tlb->size = size; + tlb->attr &= ~0x1; + tlb->attr |= (value >> 8) & 1; + if (value & 0x200) { + tlb->prot |= PAGE_VALID; + } else { + if (tlb->prot & PAGE_VALID) { + tlb->prot &= ~PAGE_VALID; + do_flush_tlbs = 1; + } + } + tlb->PID = env->spr[SPR_440_MMUCR] & 0x000000FF; + if (do_flush_tlbs) { + tlb_flush(env, 1); + } + break; + case 1: + RPN = value & 0xFFFFFC0F; + if ((tlb->prot & PAGE_VALID) && tlb->RPN != RPN) { + tlb_flush(env, 1); + } + tlb->RPN = RPN; + break; + case 2: + tlb->attr = (tlb->attr & 0x1) | (value & 0x0000FF00); + tlb->prot = tlb->prot & PAGE_VALID; + if (value & 0x1) { + tlb->prot |= PAGE_READ << 4; + } + if (value & 0x2) { + tlb->prot |= PAGE_WRITE << 4; + } + if (value & 0x4) { + tlb->prot |= PAGE_EXEC << 4; + } + if (value & 0x8) { + tlb->prot |= PAGE_READ; + } + if (value & 0x10) { + tlb->prot |= PAGE_WRITE; + } + if (value & 0x20) { + tlb->prot |= PAGE_EXEC; + } + break; + } +} + +target_ulong helper_440_tlbre(CPUPPCState *env, uint32_t word, + target_ulong entry) +{ + ppcemb_tlb_t *tlb; + target_ulong ret; + int size; + + entry &= 0x3F; + tlb = &env->tlb.tlbe[entry]; + switch (word) { + default: + /* Just here to please gcc */ + case 0: + ret = tlb->EPN; + size = booke_page_size_to_tlb(tlb->size); + if (size < 0 || size > 0xF) { + size = 1; + } + ret |= size << 4; + if (tlb->attr & 0x1) { + ret |= 0x100; + } + if (tlb->prot & PAGE_VALID) { + ret |= 0x200; + } + env->spr[SPR_440_MMUCR] &= ~0x000000FF; + env->spr[SPR_440_MMUCR] |= tlb->PID; + break; + case 1: + ret = tlb->RPN; + break; + case 2: + ret = tlb->attr & ~0x1; + if (tlb->prot & (PAGE_READ << 4)) { + ret |= 0x1; + } + if (tlb->prot & (PAGE_WRITE << 4)) { + ret |= 0x2; + } + if (tlb->prot & (PAGE_EXEC << 4)) { + ret |= 0x4; + } + if (tlb->prot & PAGE_READ) { + ret |= 0x8; + } + if (tlb->prot & PAGE_WRITE) { + ret |= 0x10; + } + if (tlb->prot & PAGE_EXEC) { + ret |= 0x20; + } + break; + } + return ret; +} + +target_ulong helper_440_tlbsx(CPUPPCState *env, target_ulong address) +{ + return ppcemb_tlb_search(env, address, env->spr[SPR_440_MMUCR] & 0xFF); +} + +/* PowerPC BookE 2.06 TLB management */ + +static ppcmas_tlb_t *booke206_cur_tlb(CPUPPCState *env) +{ + uint32_t tlbncfg = 0; + int esel = (env->spr[SPR_BOOKE_MAS0] & MAS0_ESEL_MASK) >> MAS0_ESEL_SHIFT; + int ea = (env->spr[SPR_BOOKE_MAS2] & MAS2_EPN_MASK); + int tlb; + + tlb = (env->spr[SPR_BOOKE_MAS0] & MAS0_TLBSEL_MASK) >> MAS0_TLBSEL_SHIFT; + tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlb]; + + if ((tlbncfg & TLBnCFG_HES) && (env->spr[SPR_BOOKE_MAS0] & MAS0_HES)) { + cpu_abort(env, "we don't support HES yet\n"); + } + + return booke206_get_tlbm(env, tlb, ea, esel); +} + +void helper_booke_setpid(CPUPPCState *env, uint32_t pidn, target_ulong pid) +{ + env->spr[pidn] = pid; + /* changing PIDs mean we're in a different address space now */ + tlb_flush(env, 1); +} + +void helper_booke206_tlbwe(CPUPPCState *env) +{ + uint32_t tlbncfg, tlbn; + ppcmas_tlb_t *tlb; + uint32_t size_tlb, size_ps; + target_ulong mask; + + + switch (env->spr[SPR_BOOKE_MAS0] & MAS0_WQ_MASK) { + case MAS0_WQ_ALWAYS: + /* good to go, write that entry */ + break; + case MAS0_WQ_COND: + /* XXX check if reserved */ + if (0) { + return; + } + break; + case MAS0_WQ_CLR_RSRV: + /* XXX clear entry */ + return; + default: + /* no idea what to do */ + return; + } + + if (((env->spr[SPR_BOOKE_MAS0] & MAS0_ATSEL) == MAS0_ATSEL_LRAT) && + !msr_gs) { + /* XXX we don't support direct LRAT setting yet */ + fprintf(stderr, "cpu: don't support LRAT setting yet\n"); + return; + } + + tlbn = (env->spr[SPR_BOOKE_MAS0] & MAS0_TLBSEL_MASK) >> MAS0_TLBSEL_SHIFT; + tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlbn]; + + tlb = booke206_cur_tlb(env); + + if (!tlb) { + helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL | + POWERPC_EXCP_INVAL_INVAL); + } + + /* check that we support the targeted size */ + size_tlb = (env->spr[SPR_BOOKE_MAS1] & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; + size_ps = booke206_tlbnps(env, tlbn); + if ((env->spr[SPR_BOOKE_MAS1] & MAS1_VALID) && (tlbncfg & TLBnCFG_AVAIL) && + !(size_ps & (1 << size_tlb))) { + helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL | + POWERPC_EXCP_INVAL_INVAL); + } + + if (msr_gs) { + cpu_abort(env, "missing HV implementation\n"); + } + tlb->mas7_3 = ((uint64_t)env->spr[SPR_BOOKE_MAS7] << 32) | + env->spr[SPR_BOOKE_MAS3]; + tlb->mas1 = env->spr[SPR_BOOKE_MAS1]; + + /* MAV 1.0 only */ + if (!(tlbncfg & TLBnCFG_AVAIL)) { + /* force !AVAIL TLB entries to correct page size */ + tlb->mas1 &= ~MAS1_TSIZE_MASK; + /* XXX can be configured in MMUCSR0 */ + tlb->mas1 |= (tlbncfg & TLBnCFG_MINSIZE) >> 12; + } + + /* Make a mask from TLB size to discard invalid bits in EPN field */ + mask = ~(booke206_tlb_to_page_size(env, tlb) - 1); + /* Add a mask for page attributes */ + mask |= MAS2_ACM | MAS2_VLE | MAS2_W | MAS2_I | MAS2_M | MAS2_G | MAS2_E; + + if (!msr_cm) { + /* Executing a tlbwe instruction in 32-bit mode will set + * bits 0:31 of the TLB EPN field to zero. + */ + mask &= 0xffffffff; + } + + tlb->mas2 = env->spr[SPR_BOOKE_MAS2] & mask; + + if (!(tlbncfg & TLBnCFG_IPROT)) { + /* no IPROT supported by TLB */ + tlb->mas1 &= ~MAS1_IPROT; + } + + if (booke206_tlb_to_page_size(env, tlb) == TARGET_PAGE_SIZE) { + tlb_flush_page(env, tlb->mas2 & MAS2_EPN_MASK); + } else { + tlb_flush(env, 1); + } +} + +static inline void booke206_tlb_to_mas(CPUPPCState *env, ppcmas_tlb_t *tlb) +{ + int tlbn = booke206_tlbm_to_tlbn(env, tlb); + int way = booke206_tlbm_to_way(env, tlb); + + env->spr[SPR_BOOKE_MAS0] = tlbn << MAS0_TLBSEL_SHIFT; + env->spr[SPR_BOOKE_MAS0] |= way << MAS0_ESEL_SHIFT; + env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT; + + env->spr[SPR_BOOKE_MAS1] = tlb->mas1; + env->spr[SPR_BOOKE_MAS2] = tlb->mas2; + env->spr[SPR_BOOKE_MAS3] = tlb->mas7_3; + env->spr[SPR_BOOKE_MAS7] = tlb->mas7_3 >> 32; +} + +void helper_booke206_tlbre(CPUPPCState *env) +{ + ppcmas_tlb_t *tlb = NULL; + + tlb = booke206_cur_tlb(env); + if (!tlb) { + env->spr[SPR_BOOKE_MAS1] = 0; + } else { + booke206_tlb_to_mas(env, tlb); + } +} + +void helper_booke206_tlbsx(CPUPPCState *env, target_ulong address) +{ + ppcmas_tlb_t *tlb = NULL; + int i, j; + target_phys_addr_t raddr; + uint32_t spid, sas; + + spid = (env->spr[SPR_BOOKE_MAS6] & MAS6_SPID_MASK) >> MAS6_SPID_SHIFT; + sas = env->spr[SPR_BOOKE_MAS6] & MAS6_SAS; + + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + int ways = booke206_tlb_ways(env, i); + + for (j = 0; j < ways; j++) { + tlb = booke206_get_tlbm(env, i, address, j); + + if (!tlb) { + continue; + } + + if (ppcmas_tlb_check(env, tlb, &raddr, address, spid)) { + continue; + } + + if (sas != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { + continue; + } + + booke206_tlb_to_mas(env, tlb); + return; + } + } + + /* no entry found, fill with defaults */ + env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK; + env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK; + env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK; + env->spr[SPR_BOOKE_MAS3] = 0; + env->spr[SPR_BOOKE_MAS7] = 0; + + if (env->spr[SPR_BOOKE_MAS6] & MAS6_SAS) { + env->spr[SPR_BOOKE_MAS1] |= MAS1_TS; + } + + env->spr[SPR_BOOKE_MAS1] |= (env->spr[SPR_BOOKE_MAS6] >> 16) + << MAS1_TID_SHIFT; + + /* next victim logic */ + env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT; + env->last_way++; + env->last_way &= booke206_tlb_ways(env, 0) - 1; + env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT; +} + +static inline void booke206_invalidate_ea_tlb(CPUPPCState *env, int tlbn, + uint32_t ea) +{ + int i; + int ways = booke206_tlb_ways(env, tlbn); + target_ulong mask; + + for (i = 0; i < ways; i++) { + ppcmas_tlb_t *tlb = booke206_get_tlbm(env, tlbn, ea, i); + if (!tlb) { + continue; + } + mask = ~(booke206_tlb_to_page_size(env, tlb) - 1); + if (((tlb->mas2 & MAS2_EPN_MASK) == (ea & mask)) && + !(tlb->mas1 & MAS1_IPROT)) { + tlb->mas1 &= ~MAS1_VALID; + } + } +} + +void helper_booke206_tlbivax(CPUPPCState *env, target_ulong address) +{ + if (address & 0x4) { + /* flush all entries */ + if (address & 0x8) { + /* flush all of TLB1 */ + booke206_flush_tlb(env, BOOKE206_FLUSH_TLB1, 1); + } else { + /* flush all of TLB0 */ + booke206_flush_tlb(env, BOOKE206_FLUSH_TLB0, 0); + } + return; + } + + if (address & 0x8) { + /* flush TLB1 entries */ + booke206_invalidate_ea_tlb(env, 1, address); + tlb_flush(env, 1); + } else { + /* flush TLB0 entries */ + booke206_invalidate_ea_tlb(env, 0, address); + tlb_flush_page(env, address & MAS2_EPN_MASK); + } +} + +void helper_booke206_tlbilx0(CPUPPCState *env, target_ulong address) +{ + /* XXX missing LPID handling */ + booke206_flush_tlb(env, -1, 1); +} + +void helper_booke206_tlbilx1(CPUPPCState *env, target_ulong address) +{ + int i, j; + int tid = (env->spr[SPR_BOOKE_MAS6] & MAS6_SPID); + ppcmas_tlb_t *tlb = env->tlb.tlbm; + int tlb_size; + + /* XXX missing LPID handling */ + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + tlb_size = booke206_tlb_size(env, i); + for (j = 0; j < tlb_size; j++) { + if (!(tlb[j].mas1 & MAS1_IPROT) && + ((tlb[j].mas1 & MAS1_TID_MASK) == tid)) { + tlb[j].mas1 &= ~MAS1_VALID; + } + } + tlb += booke206_tlb_size(env, i); + } + tlb_flush(env, 1); +} + +void helper_booke206_tlbilx3(CPUPPCState *env, target_ulong address) +{ + int i, j; + ppcmas_tlb_t *tlb; + int tid = (env->spr[SPR_BOOKE_MAS6] & MAS6_SPID); + int pid = tid >> MAS6_SPID_SHIFT; + int sgs = env->spr[SPR_BOOKE_MAS5] & MAS5_SGS; + int ind = (env->spr[SPR_BOOKE_MAS6] & MAS6_SIND) ? MAS1_IND : 0; + /* XXX check for unsupported isize and raise an invalid opcode then */ + int size = env->spr[SPR_BOOKE_MAS6] & MAS6_ISIZE_MASK; + /* XXX implement MAV2 handling */ + bool mav2 = false; + + /* XXX missing LPID handling */ + /* flush by pid and ea */ + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + int ways = booke206_tlb_ways(env, i); + + for (j = 0; j < ways; j++) { + tlb = booke206_get_tlbm(env, i, address, j); + if (!tlb) { + continue; + } + if ((ppcmas_tlb_check(env, tlb, NULL, address, pid) != 0) || + (tlb->mas1 & MAS1_IPROT) || + ((tlb->mas1 & MAS1_IND) != ind) || + ((tlb->mas8 & MAS8_TGS) != sgs)) { + continue; + } + if (mav2 && ((tlb->mas1 & MAS1_TSIZE_MASK) != size)) { + /* XXX only check when MMUCFG[TWC] || TLBnCFG[HES] */ + continue; + } + /* XXX e500mc doesn't match SAS, but other cores might */ + tlb->mas1 &= ~MAS1_VALID; + } + } + tlb_flush(env, 1); +} + +void helper_booke206_tlbflush(CPUPPCState *env, uint32_t type) +{ + int flags = 0; + + if (type & 2) { + flags |= BOOKE206_FLUSH_TLB1; + } + + if (type & 4) { + flags |= BOOKE206_FLUSH_TLB0; + } + + booke206_flush_tlb(env, flags, 1); +} +#endif diff --git a/target-ppc/mpic_helper.c b/target-ppc/mpic_helper.c new file mode 100644 index 0000000000..2c6a4d30a9 --- /dev/null +++ b/target-ppc/mpic_helper.c @@ -0,0 +1,35 @@ +/* + * PowerPC emulation helpers for QEMU. + * + * Copyright (c) 2003-2007 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, see <http://www.gnu.org/licenses/>. + */ +#include "cpu.h" +#include "helper.h" + +/*****************************************************************************/ +/* SPR accesses */ + +#if !defined(CONFIG_USER_ONLY) +/* + * This is an ugly helper for EPR, which is basically the same as accessing + * the IACK (PIAC) register on the MPIC. Because we model the MPIC as a device + * that can only talk to the CPU through MMIO, let's access it that way! + */ +target_ulong helper_load_epr(CPUPPCState *env) +{ + return ldl_phys(env->mpic_cpu_base + 0xA0); +} +#endif diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c deleted file mode 100644 index 4ef2332a5a..0000000000 --- a/target-ppc/op_helper.c +++ /dev/null @@ -1,4568 +0,0 @@ -/* - * PowerPC emulation helpers for qemu. - * - * Copyright (c) 2003-2007 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, see <http://www.gnu.org/licenses/>. - */ -#include <string.h> -#include "cpu.h" -#include "dyngen-exec.h" -#include "host-utils.h" -#include "helper.h" - -#include "helper_regs.h" - -#if !defined(CONFIG_USER_ONLY) -#include "softmmu_exec.h" -#endif /* !defined(CONFIG_USER_ONLY) */ - -//#define DEBUG_OP -//#define DEBUG_EXCEPTIONS -//#define DEBUG_SOFTWARE_TLB - -#ifdef DEBUG_SOFTWARE_TLB -# define LOG_SWTLB(...) qemu_log(__VA_ARGS__) -#else -# define LOG_SWTLB(...) do { } while (0) -#endif - - -/*****************************************************************************/ -/* Exceptions processing helpers */ - -void helper_raise_exception_err (uint32_t exception, uint32_t error_code) -{ -#if 0 - printf("Raise exception %3x code : %d\n", exception, error_code); -#endif - env->exception_index = exception; - env->error_code = error_code; - cpu_loop_exit(env); -} - -void helper_raise_exception (uint32_t exception) -{ - helper_raise_exception_err(exception, 0); -} - -/*****************************************************************************/ -/* SPR accesses */ -void helper_load_dump_spr (uint32_t sprn) -{ - qemu_log("Read SPR %d %03x => " TARGET_FMT_lx "\n", sprn, sprn, - env->spr[sprn]); -} - -void helper_store_dump_spr (uint32_t sprn) -{ - qemu_log("Write SPR %d %03x <= " TARGET_FMT_lx "\n", sprn, sprn, - env->spr[sprn]); -} - -target_ulong helper_load_tbl (void) -{ - return (target_ulong)cpu_ppc_load_tbl(env); -} - -target_ulong helper_load_tbu (void) -{ - return cpu_ppc_load_tbu(env); -} - -target_ulong helper_load_atbl (void) -{ - return (target_ulong)cpu_ppc_load_atbl(env); -} - -target_ulong helper_load_atbu (void) -{ - return cpu_ppc_load_atbu(env); -} - -#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) -target_ulong helper_load_purr (void) -{ - return (target_ulong)cpu_ppc_load_purr(env); -} -#endif - -target_ulong helper_load_601_rtcl (void) -{ - return cpu_ppc601_load_rtcl(env); -} - -target_ulong helper_load_601_rtcu (void) -{ - return cpu_ppc601_load_rtcu(env); -} - -#if !defined(CONFIG_USER_ONLY) -#if defined (TARGET_PPC64) -void helper_store_asr (target_ulong val) -{ - ppc_store_asr(env, val); -} -#endif - -void helper_store_sdr1 (target_ulong val) -{ - ppc_store_sdr1(env, val); -} - -void helper_store_tbl (target_ulong val) -{ - cpu_ppc_store_tbl(env, val); -} - -void helper_store_tbu (target_ulong val) -{ - cpu_ppc_store_tbu(env, val); -} - -void helper_store_atbl (target_ulong val) -{ - cpu_ppc_store_atbl(env, val); -} - -void helper_store_atbu (target_ulong val) -{ - cpu_ppc_store_atbu(env, val); -} - -void helper_store_601_rtcl (target_ulong val) -{ - cpu_ppc601_store_rtcl(env, val); -} - -void helper_store_601_rtcu (target_ulong val) -{ - cpu_ppc601_store_rtcu(env, val); -} - -target_ulong helper_load_decr (void) -{ - return cpu_ppc_load_decr(env); -} - -void helper_store_decr (target_ulong val) -{ - cpu_ppc_store_decr(env, val); -} - -void helper_store_hid0_601 (target_ulong val) -{ - target_ulong hid0; - - hid0 = env->spr[SPR_HID0]; - if ((val ^ hid0) & 0x00000008) { - /* Change current endianness */ - env->hflags &= ~(1 << MSR_LE); - env->hflags_nmsr &= ~(1 << MSR_LE); - env->hflags_nmsr |= (1 << MSR_LE) & (((val >> 3) & 1) << MSR_LE); - env->hflags |= env->hflags_nmsr; - qemu_log("%s: set endianness to %c => " TARGET_FMT_lx "\n", __func__, - val & 0x8 ? 'l' : 'b', env->hflags); - } - env->spr[SPR_HID0] = (uint32_t)val; -} - -void helper_store_403_pbr (uint32_t num, target_ulong value) -{ - if (likely(env->pb[num] != value)) { - env->pb[num] = value; - /* Should be optimized */ - tlb_flush(env, 1); - } -} - -target_ulong helper_load_40x_pit (void) -{ - return load_40x_pit(env); -} - -void helper_store_40x_pit (target_ulong val) -{ - store_40x_pit(env, val); -} - -void helper_store_40x_dbcr0 (target_ulong val) -{ - store_40x_dbcr0(env, val); -} - -void helper_store_40x_sler (target_ulong val) -{ - store_40x_sler(env, val); -} - -void helper_store_booke_tcr (target_ulong val) -{ - store_booke_tcr(env, val); -} - -void helper_store_booke_tsr (target_ulong val) -{ - store_booke_tsr(env, val); -} - -void helper_store_ibatu (uint32_t nr, target_ulong val) -{ - ppc_store_ibatu(env, nr, val); -} - -void helper_store_ibatl (uint32_t nr, target_ulong val) -{ - ppc_store_ibatl(env, nr, val); -} - -void helper_store_dbatu (uint32_t nr, target_ulong val) -{ - ppc_store_dbatu(env, nr, val); -} - -void helper_store_dbatl (uint32_t nr, target_ulong val) -{ - ppc_store_dbatl(env, nr, val); -} - -void helper_store_601_batl (uint32_t nr, target_ulong val) -{ - ppc_store_ibatl_601(env, nr, val); -} - -void helper_store_601_batu (uint32_t nr, target_ulong val) -{ - ppc_store_ibatu_601(env, nr, val); -} -#endif - -/*****************************************************************************/ -/* Memory load and stores */ - -static inline target_ulong addr_add(target_ulong addr, target_long arg) -{ -#if defined(TARGET_PPC64) - if (!msr_sf) - return (uint32_t)(addr + arg); - else -#endif - return addr + arg; -} - -void helper_lmw (target_ulong addr, uint32_t reg) -{ - for (; reg < 32; reg++) { - if (msr_le) - env->gpr[reg] = bswap32(ldl(addr)); - else - env->gpr[reg] = ldl(addr); - addr = addr_add(addr, 4); - } -} - -void helper_stmw (target_ulong addr, uint32_t reg) -{ - for (; reg < 32; reg++) { - if (msr_le) - stl(addr, bswap32((uint32_t)env->gpr[reg])); - else - stl(addr, (uint32_t)env->gpr[reg]); - addr = addr_add(addr, 4); - } -} - -void helper_lsw(target_ulong addr, uint32_t nb, uint32_t reg) -{ - int sh; - for (; nb > 3; nb -= 4) { - env->gpr[reg] = ldl(addr); - reg = (reg + 1) % 32; - addr = addr_add(addr, 4); - } - if (unlikely(nb > 0)) { - env->gpr[reg] = 0; - for (sh = 24; nb > 0; nb--, sh -= 8) { - env->gpr[reg] |= ldub(addr) << sh; - addr = addr_add(addr, 1); - } - } -} -/* PPC32 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... - */ -void helper_lswx(target_ulong addr, uint32_t reg, uint32_t ra, uint32_t rb) -{ - if (likely(xer_bc != 0)) { - if (unlikely((ra != 0 && reg < ra && (reg + xer_bc) > ra) || - (reg < rb && (reg + xer_bc) > rb))) { - helper_raise_exception_err(POWERPC_EXCP_PROGRAM, - POWERPC_EXCP_INVAL | - POWERPC_EXCP_INVAL_LSWX); - } else { - helper_lsw(addr, xer_bc, reg); - } - } -} - -void helper_stsw(target_ulong addr, uint32_t nb, uint32_t reg) -{ - int sh; - for (; nb > 3; nb -= 4) { - stl(addr, env->gpr[reg]); - reg = (reg + 1) % 32; - addr = addr_add(addr, 4); - } - if (unlikely(nb > 0)) { - for (sh = 24; nb > 0; nb--, sh -= 8) { - stb(addr, (env->gpr[reg] >> sh) & 0xFF); - addr = addr_add(addr, 1); - } - } -} - -static void do_dcbz(target_ulong addr, int dcache_line_size) -{ - addr &= ~(dcache_line_size - 1); - int i; - for (i = 0 ; i < dcache_line_size ; i += 4) { - stl(addr + i , 0); - } - if (env->reserve_addr == addr) - env->reserve_addr = (target_ulong)-1ULL; -} - -void helper_dcbz(target_ulong addr) -{ - do_dcbz(addr, env->dcache_line_size); -} - -void helper_dcbz_970(target_ulong addr) -{ - if (((env->spr[SPR_970_HID5] >> 7) & 0x3) == 1) - do_dcbz(addr, 32); - else - do_dcbz(addr, env->dcache_line_size); -} - -void helper_icbi(target_ulong addr) -{ - addr &= ~(env->dcache_line_size - 1); - /* 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". - */ - ldl(addr); -} - -// XXX: to be tested -target_ulong helper_lscbx (target_ulong addr, uint32_t reg, uint32_t ra, uint32_t rb) -{ - int i, c, d; - d = 24; - for (i = 0; i < xer_bc; i++) { - c = ldub(addr); - addr = addr_add(addr, 1); - /* ra (if not 0) and rb are never modified */ - if (likely(reg != rb && (ra == 0 || reg != ra))) { - env->gpr[reg] = (env->gpr[reg] & ~(0xFF << d)) | (c << d); - } - if (unlikely(c == xer_cmp)) - break; - if (likely(d != 0)) { - d -= 8; - } else { - d = 24; - reg++; - reg = reg & 0x1F; - } - } - return i; -} - -/*****************************************************************************/ -/* Fixed point operations helpers */ -#if defined(TARGET_PPC64) - -/* multiply high word */ -uint64_t helper_mulhd (uint64_t arg1, uint64_t arg2) -{ - uint64_t tl, th; - - muls64(&tl, &th, arg1, arg2); - return th; -} - -/* multiply high word unsigned */ -uint64_t helper_mulhdu (uint64_t arg1, uint64_t arg2) -{ - uint64_t tl, th; - - mulu64(&tl, &th, arg1, arg2); - return th; -} - -uint64_t helper_mulldo (uint64_t arg1, uint64_t arg2) -{ - int64_t th; - uint64_t tl; - - muls64(&tl, (uint64_t *)&th, arg1, arg2); - /* If th != 0 && th != -1, then we had an overflow */ - if (likely((uint64_t)(th + 1) <= 1)) { - env->xer &= ~(1 << XER_OV); - } else { - env->xer |= (1 << XER_OV) | (1 << XER_SO); - } - return (int64_t)tl; -} -#endif - -target_ulong helper_cntlzw (target_ulong t) -{ - return clz32(t); -} - -#if defined(TARGET_PPC64) -target_ulong helper_cntlzd (target_ulong t) -{ - return clz64(t); -} -#endif - -/* shift right arithmetic helper */ -target_ulong helper_sraw (target_ulong value, target_ulong shift) -{ - int32_t ret; - - if (likely(!(shift & 0x20))) { - if (likely((uint32_t)shift != 0)) { - shift &= 0x1f; - ret = (int32_t)value >> shift; - if (likely(ret >= 0 || (value & ((1 << shift) - 1)) == 0)) { - env->xer &= ~(1 << XER_CA); - } else { - env->xer |= (1 << XER_CA); - } - } else { - ret = (int32_t)value; - env->xer &= ~(1 << XER_CA); - } - } else { - ret = (int32_t)value >> 31; - if (ret) { - env->xer |= (1 << XER_CA); - } else { - env->xer &= ~(1 << XER_CA); - } - } - return (target_long)ret; -} - -#if defined(TARGET_PPC64) -target_ulong helper_srad (target_ulong value, target_ulong shift) -{ - int64_t ret; - - if (likely(!(shift & 0x40))) { - if (likely((uint64_t)shift != 0)) { - shift &= 0x3f; - ret = (int64_t)value >> shift; - if (likely(ret >= 0 || (value & ((1 << shift) - 1)) == 0)) { - env->xer &= ~(1 << XER_CA); - } else { - env->xer |= (1 << XER_CA); - } - } else { - ret = (int64_t)value; - env->xer &= ~(1 << XER_CA); - } - } else { - ret = (int64_t)value >> 63; - if (ret) { - env->xer |= (1 << XER_CA); - } else { - env->xer &= ~(1 << XER_CA); - } - } - return ret; -} -#endif - -#if defined(TARGET_PPC64) -target_ulong helper_popcntb (target_ulong val) -{ - val = (val & 0x5555555555555555ULL) + ((val >> 1) & - 0x5555555555555555ULL); - val = (val & 0x3333333333333333ULL) + ((val >> 2) & - 0x3333333333333333ULL); - val = (val & 0x0f0f0f0f0f0f0f0fULL) + ((val >> 4) & - 0x0f0f0f0f0f0f0f0fULL); - return val; -} - -target_ulong helper_popcntw (target_ulong val) -{ - val = (val & 0x5555555555555555ULL) + ((val >> 1) & - 0x5555555555555555ULL); - val = (val & 0x3333333333333333ULL) + ((val >> 2) & - 0x3333333333333333ULL); - val = (val & 0x0f0f0f0f0f0f0f0fULL) + ((val >> 4) & - 0x0f0f0f0f0f0f0f0fULL); - val = (val & 0x00ff00ff00ff00ffULL) + ((val >> 8) & - 0x00ff00ff00ff00ffULL); - val = (val & 0x0000ffff0000ffffULL) + ((val >> 16) & - 0x0000ffff0000ffffULL); - return val; -} - -target_ulong helper_popcntd (target_ulong val) -{ - return ctpop64(val); -} -#else -target_ulong helper_popcntb (target_ulong val) -{ - val = (val & 0x55555555) + ((val >> 1) & 0x55555555); - val = (val & 0x33333333) + ((val >> 2) & 0x33333333); - val = (val & 0x0f0f0f0f) + ((val >> 4) & 0x0f0f0f0f); - return val; -} - -target_ulong helper_popcntw (target_ulong val) -{ - val = (val & 0x55555555) + ((val >> 1) & 0x55555555); - val = (val & 0x33333333) + ((val >> 2) & 0x33333333); - val = (val & 0x0f0f0f0f) + ((val >> 4) & 0x0f0f0f0f); - val = (val & 0x00ff00ff) + ((val >> 8) & 0x00ff00ff); - val = (val & 0x0000ffff) + ((val >> 16) & 0x0000ffff); - return val; -} -#endif - -/*****************************************************************************/ -/* Floating point operations helpers */ -uint64_t helper_float32_to_float64(uint32_t arg) -{ - CPU_FloatU f; - CPU_DoubleU d; - f.l = arg; - d.d = float32_to_float64(f.f, &env->fp_status); - return d.ll; -} - -uint32_t helper_float64_to_float32(uint64_t arg) -{ - CPU_FloatU f; - CPU_DoubleU d; - d.ll = arg; - f.f = float64_to_float32(d.d, &env->fp_status); - return f.l; -} - -static inline int isden(float64 d) -{ - CPU_DoubleU u; - - u.d = d; - - return ((u.ll >> 52) & 0x7FF) == 0; -} - -uint32_t helper_compute_fprf (uint64_t arg, uint32_t set_fprf) -{ - CPU_DoubleU farg; - int isneg; - int ret; - farg.ll = arg; - isneg = float64_is_neg(farg.d); - if (unlikely(float64_is_any_nan(farg.d))) { - if (float64_is_signaling_nan(farg.d)) { - /* Signaling NaN: flags are undefined */ - ret = 0x00; - } else { - /* Quiet NaN */ - ret = 0x11; - } - } else if (unlikely(float64_is_infinity(farg.d))) { - /* +/- infinity */ - if (isneg) - ret = 0x09; - else - ret = 0x05; - } else { - if (float64_is_zero(farg.d)) { - /* +/- zero */ - if (isneg) - ret = 0x12; - else - ret = 0x02; - } else { - if (isden(farg.d)) { - /* Denormalized numbers */ - ret = 0x10; - } else { - /* Normalized numbers */ - ret = 0x00; - } - if (isneg) { - ret |= 0x08; - } else { - ret |= 0x04; - } - } - } - if (set_fprf) { - /* We update FPSCR_FPRF */ - env->fpscr &= ~(0x1F << FPSCR_FPRF); - env->fpscr |= ret << FPSCR_FPRF; - } - /* We just need fpcc to update Rc1 */ - return ret & 0xF; -} - -/* Floating-point invalid operations exception */ -static inline uint64_t fload_invalid_op_excp(int op) -{ - uint64_t ret = 0; - int ve; - - ve = fpscr_ve; - switch (op) { - case POWERPC_EXCP_FP_VXSNAN: - env->fpscr |= 1 << FPSCR_VXSNAN; - break; - case POWERPC_EXCP_FP_VXSOFT: - env->fpscr |= 1 << FPSCR_VXSOFT; - break; - case POWERPC_EXCP_FP_VXISI: - /* Magnitude subtraction of infinities */ - env->fpscr |= 1 << FPSCR_VXISI; - goto update_arith; - case POWERPC_EXCP_FP_VXIDI: - /* Division of infinity by infinity */ - env->fpscr |= 1 << FPSCR_VXIDI; - goto update_arith; - case POWERPC_EXCP_FP_VXZDZ: - /* Division of zero by zero */ - env->fpscr |= 1 << FPSCR_VXZDZ; - goto update_arith; - case POWERPC_EXCP_FP_VXIMZ: - /* Multiplication of zero by infinity */ - env->fpscr |= 1 << FPSCR_VXIMZ; - goto update_arith; - case POWERPC_EXCP_FP_VXVC: - /* Ordered comparison of NaN */ - env->fpscr |= 1 << FPSCR_VXVC; - env->fpscr &= ~(0xF << FPSCR_FPCC); - env->fpscr |= 0x11 << FPSCR_FPCC; - /* We must update the target FPR before raising the exception */ - if (ve != 0) { - env->exception_index = POWERPC_EXCP_PROGRAM; - env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_VXVC; - /* Update the floating-point enabled exception summary */ - env->fpscr |= 1 << FPSCR_FEX; - /* Exception is differed */ - ve = 0; - } - break; - case POWERPC_EXCP_FP_VXSQRT: - /* Square root of a negative number */ - env->fpscr |= 1 << FPSCR_VXSQRT; - update_arith: - env->fpscr &= ~((1 << FPSCR_FR) | (1 << FPSCR_FI)); - if (ve == 0) { - /* Set the result to quiet NaN */ - ret = 0x7FF8000000000000ULL; - env->fpscr &= ~(0xF << FPSCR_FPCC); - env->fpscr |= 0x11 << FPSCR_FPCC; - } - break; - case POWERPC_EXCP_FP_VXCVI: - /* Invalid conversion */ - env->fpscr |= 1 << FPSCR_VXCVI; - env->fpscr &= ~((1 << FPSCR_FR) | (1 << FPSCR_FI)); - if (ve == 0) { - /* Set the result to quiet NaN */ - ret = 0x7FF8000000000000ULL; - env->fpscr &= ~(0xF << FPSCR_FPCC); - env->fpscr |= 0x11 << FPSCR_FPCC; - } - break; - } - /* Update the floating-point invalid operation summary */ - env->fpscr |= 1 << FPSCR_VX; - /* Update the floating-point exception summary */ - env->fpscr |= 1 << FPSCR_FX; - if (ve != 0) { - /* Update the floating-point enabled exception summary */ - env->fpscr |= 1 << FPSCR_FEX; - if (msr_fe0 != 0 || msr_fe1 != 0) - helper_raise_exception_err(POWERPC_EXCP_PROGRAM, POWERPC_EXCP_FP | op); - } - return ret; -} - -static inline void float_zero_divide_excp(void) -{ - env->fpscr |= 1 << FPSCR_ZX; - env->fpscr &= ~((1 << FPSCR_FR) | (1 << FPSCR_FI)); - /* Update the floating-point exception summary */ - env->fpscr |= 1 << FPSCR_FX; - if (fpscr_ze != 0) { - /* Update the floating-point enabled exception summary */ - env->fpscr |= 1 << FPSCR_FEX; - if (msr_fe0 != 0 || msr_fe1 != 0) { - helper_raise_exception_err(POWERPC_EXCP_PROGRAM, - POWERPC_EXCP_FP | POWERPC_EXCP_FP_ZX); - } - } -} - -static inline void float_overflow_excp(void) -{ - env->fpscr |= 1 << FPSCR_OX; - /* Update the floating-point exception summary */ - env->fpscr |= 1 << FPSCR_FX; - if (fpscr_oe != 0) { - /* XXX: should adjust the result */ - /* Update the floating-point enabled exception summary */ - env->fpscr |= 1 << FPSCR_FEX; - /* We must update the target FPR before raising the exception */ - env->exception_index = POWERPC_EXCP_PROGRAM; - env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_OX; - } else { - env->fpscr |= 1 << FPSCR_XX; - env->fpscr |= 1 << FPSCR_FI; - } -} - -static inline void float_underflow_excp(void) -{ - env->fpscr |= 1 << FPSCR_UX; - /* Update the floating-point exception summary */ - env->fpscr |= 1 << FPSCR_FX; - if (fpscr_ue != 0) { - /* XXX: should adjust the result */ - /* Update the floating-point enabled exception summary */ - env->fpscr |= 1 << FPSCR_FEX; - /* We must update the target FPR before raising the exception */ - env->exception_index = POWERPC_EXCP_PROGRAM; - env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_UX; - } -} - -static inline void float_inexact_excp(void) -{ - env->fpscr |= 1 << FPSCR_XX; - /* Update the floating-point exception summary */ - env->fpscr |= 1 << FPSCR_FX; - if (fpscr_xe != 0) { - /* Update the floating-point enabled exception summary */ - env->fpscr |= 1 << FPSCR_FEX; - /* We must update the target FPR before raising the exception */ - env->exception_index = POWERPC_EXCP_PROGRAM; - env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_XX; - } -} - -static inline void fpscr_set_rounding_mode(void) -{ - int rnd_type; - - /* Set rounding mode */ - switch (fpscr_rn) { - 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 helper_fpscr_clrbit (uint32_t bit) -{ - int prev; - - prev = (env->fpscr >> bit) & 1; - env->fpscr &= ~(1 << bit); - if (prev == 1) { - switch (bit) { - case FPSCR_RN1: - case FPSCR_RN: - fpscr_set_rounding_mode(); - break; - default: - break; - } - } -} - -void helper_fpscr_setbit (uint32_t bit) -{ - int prev; - - prev = (env->fpscr >> bit) & 1; - env->fpscr |= 1 << bit; - if (prev == 0) { - switch (bit) { - case FPSCR_VX: - env->fpscr |= 1 << FPSCR_FX; - if (fpscr_ve) - goto raise_ve; - case FPSCR_OX: - env->fpscr |= 1 << FPSCR_FX; - if (fpscr_oe) - goto raise_oe; - break; - case FPSCR_UX: - env->fpscr |= 1 << FPSCR_FX; - if (fpscr_ue) - goto raise_ue; - break; - case FPSCR_ZX: - env->fpscr |= 1 << FPSCR_FX; - if (fpscr_ze) - goto raise_ze; - break; - case FPSCR_XX: - env->fpscr |= 1 << FPSCR_FX; - if (fpscr_xe) - goto raise_xe; - break; - case FPSCR_VXSNAN: - case FPSCR_VXISI: - case FPSCR_VXIDI: - case FPSCR_VXZDZ: - case FPSCR_VXIMZ: - case FPSCR_VXVC: - case FPSCR_VXSOFT: - case FPSCR_VXSQRT: - case FPSCR_VXCVI: - env->fpscr |= 1 << FPSCR_VX; - env->fpscr |= 1 << FPSCR_FX; - if (fpscr_ve != 0) - goto raise_ve; - break; - case FPSCR_VE: - if (fpscr_vx != 0) { - raise_ve: - env->error_code = POWERPC_EXCP_FP; - if (fpscr_vxsnan) - env->error_code |= POWERPC_EXCP_FP_VXSNAN; - if (fpscr_vxisi) - env->error_code |= POWERPC_EXCP_FP_VXISI; - if (fpscr_vxidi) - env->error_code |= POWERPC_EXCP_FP_VXIDI; - if (fpscr_vxzdz) - env->error_code |= POWERPC_EXCP_FP_VXZDZ; - if (fpscr_vximz) - env->error_code |= POWERPC_EXCP_FP_VXIMZ; - if (fpscr_vxvc) - env->error_code |= POWERPC_EXCP_FP_VXVC; - if (fpscr_vxsoft) - env->error_code |= POWERPC_EXCP_FP_VXSOFT; - if (fpscr_vxsqrt) - env->error_code |= POWERPC_EXCP_FP_VXSQRT; - if (fpscr_vxcvi) - env->error_code |= POWERPC_EXCP_FP_VXCVI; - goto raise_excp; - } - break; - case FPSCR_OE: - if (fpscr_ox != 0) { - raise_oe: - env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_OX; - goto raise_excp; - } - break; - case FPSCR_UE: - if (fpscr_ux != 0) { - raise_ue: - env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_UX; - goto raise_excp; - } - break; - case FPSCR_ZE: - if (fpscr_zx != 0) { - raise_ze: - env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_ZX; - goto raise_excp; - } - break; - case FPSCR_XE: - if (fpscr_xx != 0) { - raise_xe: - env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_XX; - goto raise_excp; - } - break; - case FPSCR_RN1: - case FPSCR_RN: - fpscr_set_rounding_mode(); - break; - default: - break; - raise_excp: - /* Update the floating-point enabled exception summary */ - env->fpscr |= 1 << FPSCR_FEX; - /* We have to update Rc1 before raising the exception */ - env->exception_index = POWERPC_EXCP_PROGRAM; - break; - } - } -} - -void helper_store_fpscr (uint64_t arg, uint32_t mask) -{ - /* - * We use only the 32 LSB of the incoming fpr - */ - uint32_t prev, new; - int i; - - prev = env->fpscr; - new = (uint32_t)arg; - new &= ~0x60000000; - new |= prev & 0x60000000; - for (i = 0; i < 8; i++) { - if (mask & (1 << i)) { - env->fpscr &= ~(0xF << (4 * i)); - env->fpscr |= new & (0xF << (4 * i)); - } - } - /* Update VX and FEX */ - if (fpscr_ix != 0) - env->fpscr |= 1 << FPSCR_VX; - else - env->fpscr &= ~(1 << FPSCR_VX); - if ((fpscr_ex & fpscr_eex) != 0) { - env->fpscr |= 1 << FPSCR_FEX; - env->exception_index = POWERPC_EXCP_PROGRAM; - /* XXX: we should compute it properly */ - env->error_code = POWERPC_EXCP_FP; - } - else - env->fpscr &= ~(1 << FPSCR_FEX); - fpscr_set_rounding_mode(); -} - -void helper_float_check_status (void) -{ - if (env->exception_index == POWERPC_EXCP_PROGRAM && - (env->error_code & POWERPC_EXCP_FP)) { - /* Differred floating-point exception after target FPR update */ - if (msr_fe0 != 0 || msr_fe1 != 0) - helper_raise_exception_err(env->exception_index, env->error_code); - } else { - int status = get_float_exception_flags(&env->fp_status); - if (status & float_flag_divbyzero) { - float_zero_divide_excp(); - } else if (status & float_flag_overflow) { - float_overflow_excp(); - } else if (status & float_flag_underflow) { - float_underflow_excp(); - } else if (status & float_flag_inexact) { - float_inexact_excp(); - } - } -} - -void helper_reset_fpstatus (void) -{ - set_float_exception_flags(0, &env->fp_status); -} - -/* fadd - fadd. */ -uint64_t helper_fadd (uint64_t arg1, uint64_t arg2) -{ - CPU_DoubleU farg1, farg2; - - farg1.ll = arg1; - farg2.ll = arg2; - - if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) && - float64_is_neg(farg1.d) != float64_is_neg(farg2.d))) { - /* Magnitude subtraction of infinities */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXISI); - } else { - if (unlikely(float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d))) { - /* sNaN addition */ - fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } - farg1.d = float64_add(farg1.d, farg2.d, &env->fp_status); - } - - return farg1.ll; -} - -/* fsub - fsub. */ -uint64_t helper_fsub (uint64_t arg1, uint64_t arg2) -{ - CPU_DoubleU farg1, farg2; - - farg1.ll = arg1; - farg2.ll = arg2; - - if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) && - float64_is_neg(farg1.d) == float64_is_neg(farg2.d))) { - /* Magnitude subtraction of infinities */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXISI); - } else { - if (unlikely(float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d))) { - /* sNaN subtraction */ - fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } - farg1.d = float64_sub(farg1.d, farg2.d, &env->fp_status); - } - - return farg1.ll; -} - -/* fmul - fmul. */ -uint64_t helper_fmul (uint64_t arg1, uint64_t arg2) -{ - CPU_DoubleU farg1, farg2; - - farg1.ll = arg1; - farg2.ll = arg2; - - if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || - (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { - /* Multiplication of zero by infinity */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXIMZ); - } else { - if (unlikely(float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d))) { - /* sNaN multiplication */ - fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } - farg1.d = float64_mul(farg1.d, farg2.d, &env->fp_status); - } - - return farg1.ll; -} - -/* fdiv - fdiv. */ -uint64_t helper_fdiv (uint64_t arg1, uint64_t arg2) -{ - CPU_DoubleU farg1, farg2; - - farg1.ll = arg1; - farg2.ll = arg2; - - if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d))) { - /* Division of infinity by infinity */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXIDI); - } else if (unlikely(float64_is_zero(farg1.d) && float64_is_zero(farg2.d))) { - /* Division of zero by zero */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXZDZ); - } else { - if (unlikely(float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d))) { - /* sNaN division */ - fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } - farg1.d = float64_div(farg1.d, farg2.d, &env->fp_status); - } - - return farg1.ll; -} - -/* fabs */ -uint64_t helper_fabs (uint64_t arg) -{ - CPU_DoubleU farg; - - farg.ll = arg; - farg.d = float64_abs(farg.d); - return farg.ll; -} - -/* fnabs */ -uint64_t helper_fnabs (uint64_t arg) -{ - CPU_DoubleU farg; - - farg.ll = arg; - farg.d = float64_abs(farg.d); - farg.d = float64_chs(farg.d); - return farg.ll; -} - -/* fneg */ -uint64_t helper_fneg (uint64_t arg) -{ - CPU_DoubleU farg; - - farg.ll = arg; - farg.d = float64_chs(farg.d); - return farg.ll; -} - -/* fctiw - fctiw. */ -uint64_t helper_fctiw (uint64_t arg) -{ - CPU_DoubleU farg; - farg.ll = arg; - - if (unlikely(float64_is_signaling_nan(farg.d))) { - /* sNaN conversion */ - farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI); - } else if (unlikely(float64_is_quiet_nan(farg.d) || float64_is_infinity(farg.d))) { - /* qNan / infinity conversion */ - farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI); - } else { - farg.ll = float64_to_int32(farg.d, &env->fp_status); - /* XXX: higher bits are not supposed to be significant. - * to make tests easier, return the same as a real PowerPC 750 - */ - farg.ll |= 0xFFF80000ULL << 32; - } - return farg.ll; -} - -/* fctiwz - fctiwz. */ -uint64_t helper_fctiwz (uint64_t arg) -{ - CPU_DoubleU farg; - farg.ll = arg; - - if (unlikely(float64_is_signaling_nan(farg.d))) { - /* sNaN conversion */ - farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI); - } else if (unlikely(float64_is_quiet_nan(farg.d) || float64_is_infinity(farg.d))) { - /* qNan / infinity conversion */ - farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI); - } else { - farg.ll = float64_to_int32_round_to_zero(farg.d, &env->fp_status); - /* XXX: higher bits are not supposed to be significant. - * to make tests easier, return the same as a real PowerPC 750 - */ - farg.ll |= 0xFFF80000ULL << 32; - } - return farg.ll; -} - -#if defined(TARGET_PPC64) -/* fcfid - fcfid. */ -uint64_t helper_fcfid (uint64_t arg) -{ - CPU_DoubleU farg; - farg.d = int64_to_float64(arg, &env->fp_status); - return farg.ll; -} - -/* fctid - fctid. */ -uint64_t helper_fctid (uint64_t arg) -{ - CPU_DoubleU farg; - farg.ll = arg; - - if (unlikely(float64_is_signaling_nan(farg.d))) { - /* sNaN conversion */ - farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI); - } else if (unlikely(float64_is_quiet_nan(farg.d) || float64_is_infinity(farg.d))) { - /* qNan / infinity conversion */ - farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI); - } else { - farg.ll = float64_to_int64(farg.d, &env->fp_status); - } - return farg.ll; -} - -/* fctidz - fctidz. */ -uint64_t helper_fctidz (uint64_t arg) -{ - CPU_DoubleU farg; - farg.ll = arg; - - if (unlikely(float64_is_signaling_nan(farg.d))) { - /* sNaN conversion */ - farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI); - } else if (unlikely(float64_is_quiet_nan(farg.d) || float64_is_infinity(farg.d))) { - /* qNan / infinity conversion */ - farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI); - } else { - farg.ll = float64_to_int64_round_to_zero(farg.d, &env->fp_status); - } - return farg.ll; -} - -#endif - -static inline uint64_t do_fri(uint64_t arg, int rounding_mode) -{ - CPU_DoubleU farg; - farg.ll = arg; - - if (unlikely(float64_is_signaling_nan(farg.d))) { - /* sNaN round */ - farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI); - } else if (unlikely(float64_is_quiet_nan(farg.d) || float64_is_infinity(farg.d))) { - /* qNan / infinity round */ - farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI); - } else { - set_float_rounding_mode(rounding_mode, &env->fp_status); - farg.ll = float64_round_to_int(farg.d, &env->fp_status); - /* Restore rounding mode from FPSCR */ - fpscr_set_rounding_mode(); - } - return farg.ll; -} - -uint64_t helper_frin (uint64_t arg) -{ - return do_fri(arg, float_round_nearest_even); -} - -uint64_t helper_friz (uint64_t arg) -{ - return do_fri(arg, float_round_to_zero); -} - -uint64_t helper_frip (uint64_t arg) -{ - return do_fri(arg, float_round_up); -} - -uint64_t helper_frim (uint64_t arg) -{ - return do_fri(arg, float_round_down); -} - -/* fmadd - fmadd. */ -uint64_t helper_fmadd (uint64_t arg1, uint64_t arg2, uint64_t arg3) -{ - CPU_DoubleU farg1, farg2, farg3; - - farg1.ll = arg1; - farg2.ll = arg2; - farg3.ll = arg3; - - if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || - (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { - /* Multiplication of zero by infinity */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXIMZ); - } else { - if (unlikely(float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d) || - float64_is_signaling_nan(farg3.d))) { - /* sNaN operation */ - fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } - /* This is the way the PowerPC specification defines it */ - float128 ft0_128, ft1_128; - - ft0_128 = float64_to_float128(farg1.d, &env->fp_status); - ft1_128 = float64_to_float128(farg2.d, &env->fp_status); - ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status); - if (unlikely(float128_is_infinity(ft0_128) && float64_is_infinity(farg3.d) && - float128_is_neg(ft0_128) != float64_is_neg(farg3.d))) { - /* Magnitude subtraction of infinities */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXISI); - } else { - ft1_128 = float64_to_float128(farg3.d, &env->fp_status); - ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status); - farg1.d = float128_to_float64(ft0_128, &env->fp_status); - } - } - - return farg1.ll; -} - -/* fmsub - fmsub. */ -uint64_t helper_fmsub (uint64_t arg1, uint64_t arg2, uint64_t arg3) -{ - CPU_DoubleU farg1, farg2, farg3; - - farg1.ll = arg1; - farg2.ll = arg2; - farg3.ll = arg3; - - if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || - (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { - /* Multiplication of zero by infinity */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXIMZ); - } else { - if (unlikely(float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d) || - float64_is_signaling_nan(farg3.d))) { - /* sNaN operation */ - fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } - /* This is the way the PowerPC specification defines it */ - float128 ft0_128, ft1_128; - - ft0_128 = float64_to_float128(farg1.d, &env->fp_status); - ft1_128 = float64_to_float128(farg2.d, &env->fp_status); - ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status); - if (unlikely(float128_is_infinity(ft0_128) && float64_is_infinity(farg3.d) && - float128_is_neg(ft0_128) == float64_is_neg(farg3.d))) { - /* Magnitude subtraction of infinities */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXISI); - } else { - ft1_128 = float64_to_float128(farg3.d, &env->fp_status); - ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status); - farg1.d = float128_to_float64(ft0_128, &env->fp_status); - } - } - return farg1.ll; -} - -/* fnmadd - fnmadd. */ -uint64_t helper_fnmadd (uint64_t arg1, uint64_t arg2, uint64_t arg3) -{ - CPU_DoubleU farg1, farg2, farg3; - - farg1.ll = arg1; - farg2.ll = arg2; - farg3.ll = arg3; - - if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || - (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { - /* Multiplication of zero by infinity */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXIMZ); - } else { - if (unlikely(float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d) || - float64_is_signaling_nan(farg3.d))) { - /* sNaN operation */ - fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } - /* This is the way the PowerPC specification defines it */ - float128 ft0_128, ft1_128; - - ft0_128 = float64_to_float128(farg1.d, &env->fp_status); - ft1_128 = float64_to_float128(farg2.d, &env->fp_status); - ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status); - if (unlikely(float128_is_infinity(ft0_128) && float64_is_infinity(farg3.d) && - float128_is_neg(ft0_128) != float64_is_neg(farg3.d))) { - /* Magnitude subtraction of infinities */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXISI); - } else { - ft1_128 = float64_to_float128(farg3.d, &env->fp_status); - ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status); - farg1.d = float128_to_float64(ft0_128, &env->fp_status); - } - if (likely(!float64_is_any_nan(farg1.d))) { - farg1.d = float64_chs(farg1.d); - } - } - return farg1.ll; -} - -/* fnmsub - fnmsub. */ -uint64_t helper_fnmsub (uint64_t arg1, uint64_t arg2, uint64_t arg3) -{ - CPU_DoubleU farg1, farg2, farg3; - - farg1.ll = arg1; - farg2.ll = arg2; - farg3.ll = arg3; - - if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || - (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { - /* Multiplication of zero by infinity */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXIMZ); - } else { - if (unlikely(float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d) || - float64_is_signaling_nan(farg3.d))) { - /* sNaN operation */ - fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } - /* This is the way the PowerPC specification defines it */ - float128 ft0_128, ft1_128; - - ft0_128 = float64_to_float128(farg1.d, &env->fp_status); - ft1_128 = float64_to_float128(farg2.d, &env->fp_status); - ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status); - if (unlikely(float128_is_infinity(ft0_128) && float64_is_infinity(farg3.d) && - float128_is_neg(ft0_128) == float64_is_neg(farg3.d))) { - /* Magnitude subtraction of infinities */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXISI); - } else { - ft1_128 = float64_to_float128(farg3.d, &env->fp_status); - ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status); - farg1.d = float128_to_float64(ft0_128, &env->fp_status); - } - if (likely(!float64_is_any_nan(farg1.d))) { - farg1.d = float64_chs(farg1.d); - } - } - return farg1.ll; -} - -/* frsp - frsp. */ -uint64_t helper_frsp (uint64_t arg) -{ - CPU_DoubleU farg; - float32 f32; - farg.ll = arg; - - if (unlikely(float64_is_signaling_nan(farg.d))) { - /* sNaN square root */ - fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } - f32 = float64_to_float32(farg.d, &env->fp_status); - farg.d = float32_to_float64(f32, &env->fp_status); - - return farg.ll; -} - -/* fsqrt - fsqrt. */ -uint64_t helper_fsqrt (uint64_t arg) -{ - CPU_DoubleU farg; - farg.ll = arg; - - if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { - /* Square root of a negative nonzero number */ - farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSQRT); - } else { - if (unlikely(float64_is_signaling_nan(farg.d))) { - /* sNaN square root */ - fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } - farg.d = float64_sqrt(farg.d, &env->fp_status); - } - return farg.ll; -} - -/* fre - fre. */ -uint64_t helper_fre (uint64_t arg) -{ - CPU_DoubleU farg; - farg.ll = arg; - - if (unlikely(float64_is_signaling_nan(farg.d))) { - /* sNaN reciprocal */ - fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } - farg.d = float64_div(float64_one, farg.d, &env->fp_status); - return farg.d; -} - -/* fres - fres. */ -uint64_t helper_fres (uint64_t arg) -{ - CPU_DoubleU farg; - float32 f32; - farg.ll = arg; - - if (unlikely(float64_is_signaling_nan(farg.d))) { - /* sNaN reciprocal */ - fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } - farg.d = float64_div(float64_one, farg.d, &env->fp_status); - f32 = float64_to_float32(farg.d, &env->fp_status); - farg.d = float32_to_float64(f32, &env->fp_status); - - return farg.ll; -} - -/* frsqrte - frsqrte. */ -uint64_t helper_frsqrte (uint64_t arg) -{ - CPU_DoubleU farg; - float32 f32; - farg.ll = arg; - - if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { - /* Reciprocal square root of a negative nonzero number */ - farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSQRT); - } else { - if (unlikely(float64_is_signaling_nan(farg.d))) { - /* sNaN reciprocal square root */ - fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } - farg.d = float64_sqrt(farg.d, &env->fp_status); - farg.d = float64_div(float64_one, farg.d, &env->fp_status); - f32 = float64_to_float32(farg.d, &env->fp_status); - farg.d = float32_to_float64(f32, &env->fp_status); - } - return farg.ll; -} - -/* fsel - fsel. */ -uint64_t helper_fsel (uint64_t arg1, uint64_t arg2, uint64_t arg3) -{ - CPU_DoubleU farg1; - - farg1.ll = arg1; - - if ((!float64_is_neg(farg1.d) || float64_is_zero(farg1.d)) && !float64_is_any_nan(farg1.d)) { - return arg2; - } else { - return arg3; - } -} - -void helper_fcmpu (uint64_t arg1, uint64_t arg2, uint32_t crfD) -{ - CPU_DoubleU farg1, farg2; - uint32_t ret = 0; - farg1.ll = arg1; - farg2.ll = arg2; - - if (unlikely(float64_is_any_nan(farg1.d) || - float64_is_any_nan(farg2.d))) { - ret = 0x01UL; - } else if (float64_lt(farg1.d, farg2.d, &env->fp_status)) { - ret = 0x08UL; - } else if (!float64_le(farg1.d, farg2.d, &env->fp_status)) { - ret = 0x04UL; - } else { - ret = 0x02UL; - } - - env->fpscr &= ~(0x0F << FPSCR_FPRF); - env->fpscr |= ret << FPSCR_FPRF; - env->crf[crfD] = ret; - if (unlikely(ret == 0x01UL - && (float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d)))) { - /* sNaN comparison */ - fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } -} - -void helper_fcmpo (uint64_t arg1, uint64_t arg2, uint32_t crfD) -{ - CPU_DoubleU farg1, farg2; - uint32_t ret = 0; - farg1.ll = arg1; - farg2.ll = arg2; - - if (unlikely(float64_is_any_nan(farg1.d) || - float64_is_any_nan(farg2.d))) { - ret = 0x01UL; - } else if (float64_lt(farg1.d, farg2.d, &env->fp_status)) { - ret = 0x08UL; - } else if (!float64_le(farg1.d, farg2.d, &env->fp_status)) { - ret = 0x04UL; - } else { - ret = 0x02UL; - } - - env->fpscr &= ~(0x0F << FPSCR_FPRF); - env->fpscr |= ret << FPSCR_FPRF; - env->crf[crfD] = ret; - if (unlikely (ret == 0x01UL)) { - if (float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d)) { - /* sNaN comparison */ - fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | - POWERPC_EXCP_FP_VXVC); - } else { - /* qNaN comparison */ - fload_invalid_op_excp(POWERPC_EXCP_FP_VXVC); - } - } -} - -#if !defined (CONFIG_USER_ONLY) -void helper_store_msr (target_ulong val) -{ - val = hreg_store_msr(env, val, 0); - if (val != 0) { - env->interrupt_request |= CPU_INTERRUPT_EXITTB; - helper_raise_exception(val); - } -} - -static inline void do_rfi(target_ulong nip, target_ulong msr, - target_ulong msrm, int keep_msrh) -{ -#if defined(TARGET_PPC64) - if (msr & (1ULL << MSR_SF)) { - nip = (uint64_t)nip; - msr &= (uint64_t)msrm; - } else { - nip = (uint32_t)nip; - msr = (uint32_t)(msr & msrm); - if (keep_msrh) - msr |= env->msr & ~((uint64_t)0xFFFFFFFF); - } -#else - nip = (uint32_t)nip; - msr &= (uint32_t)msrm; -#endif - /* XXX: beware: this is false if VLE is supported */ - env->nip = nip & ~((target_ulong)0x00000003); - hreg_store_msr(env, msr, 1); -#if defined (DEBUG_OP) - cpu_dump_rfi(env->nip, env->msr); -#endif - /* No need to raise an exception here, - * as rfi is always the last insn of a TB - */ - env->interrupt_request |= CPU_INTERRUPT_EXITTB; -} - -void helper_rfi (void) -{ - do_rfi(env->spr[SPR_SRR0], env->spr[SPR_SRR1], - ~((target_ulong)0x783F0000), 1); -} - -#if defined(TARGET_PPC64) -void helper_rfid (void) -{ - do_rfi(env->spr[SPR_SRR0], env->spr[SPR_SRR1], - ~((target_ulong)0x783F0000), 0); -} - -void helper_hrfid (void) -{ - do_rfi(env->spr[SPR_HSRR0], env->spr[SPR_HSRR1], - ~((target_ulong)0x783F0000), 0); -} -#endif -#endif - -void helper_tw (target_ulong arg1, target_ulong arg2, uint32_t flags) -{ - if (!likely(!(((int32_t)arg1 < (int32_t)arg2 && (flags & 0x10)) || - ((int32_t)arg1 > (int32_t)arg2 && (flags & 0x08)) || - ((int32_t)arg1 == (int32_t)arg2 && (flags & 0x04)) || - ((uint32_t)arg1 < (uint32_t)arg2 && (flags & 0x02)) || - ((uint32_t)arg1 > (uint32_t)arg2 && (flags & 0x01))))) { - helper_raise_exception_err(POWERPC_EXCP_PROGRAM, POWERPC_EXCP_TRAP); - } -} - -#if defined(TARGET_PPC64) -void helper_td (target_ulong arg1, target_ulong arg2, uint32_t flags) -{ - if (!likely(!(((int64_t)arg1 < (int64_t)arg2 && (flags & 0x10)) || - ((int64_t)arg1 > (int64_t)arg2 && (flags & 0x08)) || - ((int64_t)arg1 == (int64_t)arg2 && (flags & 0x04)) || - ((uint64_t)arg1 < (uint64_t)arg2 && (flags & 0x02)) || - ((uint64_t)arg1 > (uint64_t)arg2 && (flags & 0x01))))) - helper_raise_exception_err(POWERPC_EXCP_PROGRAM, POWERPC_EXCP_TRAP); -} -#endif - -/*****************************************************************************/ -/* PowerPC 601 specific instructions (POWER bridge) */ - -target_ulong helper_clcs (uint32_t arg) -{ - switch (arg) { - case 0x0CUL: - /* Instruction cache line size */ - return env->icache_line_size; - break; - case 0x0DUL: - /* Data cache line size */ - return env->dcache_line_size; - break; - case 0x0EUL: - /* Minimum cache line size */ - return (env->icache_line_size < env->dcache_line_size) ? - env->icache_line_size : env->dcache_line_size; - break; - case 0x0FUL: - /* Maximum cache line size */ - return (env->icache_line_size > env->dcache_line_size) ? - env->icache_line_size : env->dcache_line_size; - break; - default: - /* Undefined */ - return 0; - break; - } -} - -target_ulong helper_div (target_ulong arg1, target_ulong arg2) -{ - uint64_t tmp = (uint64_t)arg1 << 32 | env->spr[SPR_MQ]; - - if (((int32_t)tmp == INT32_MIN && (int32_t)arg2 == (int32_t)-1) || - (int32_t)arg2 == 0) { - env->spr[SPR_MQ] = 0; - return INT32_MIN; - } else { - env->spr[SPR_MQ] = tmp % arg2; - return tmp / (int32_t)arg2; - } -} - -target_ulong helper_divo (target_ulong arg1, target_ulong arg2) -{ - uint64_t tmp = (uint64_t)arg1 << 32 | env->spr[SPR_MQ]; - - if (((int32_t)tmp == INT32_MIN && (int32_t)arg2 == (int32_t)-1) || - (int32_t)arg2 == 0) { - env->xer |= (1 << XER_OV) | (1 << XER_SO); - env->spr[SPR_MQ] = 0; - return INT32_MIN; - } else { - env->spr[SPR_MQ] = tmp % arg2; - tmp /= (int32_t)arg2; - if ((int32_t)tmp != tmp) { - env->xer |= (1 << XER_OV) | (1 << XER_SO); - } else { - env->xer &= ~(1 << XER_OV); - } - return tmp; - } -} - -target_ulong helper_divs (target_ulong arg1, target_ulong arg2) -{ - if (((int32_t)arg1 == INT32_MIN && (int32_t)arg2 == (int32_t)-1) || - (int32_t)arg2 == 0) { - env->spr[SPR_MQ] = 0; - return INT32_MIN; - } else { - env->spr[SPR_MQ] = (int32_t)arg1 % (int32_t)arg2; - return (int32_t)arg1 / (int32_t)arg2; - } -} - -target_ulong helper_divso (target_ulong arg1, target_ulong arg2) -{ - if (((int32_t)arg1 == INT32_MIN && (int32_t)arg2 == (int32_t)-1) || - (int32_t)arg2 == 0) { - env->xer |= (1 << XER_OV) | (1 << XER_SO); - env->spr[SPR_MQ] = 0; - return INT32_MIN; - } else { - env->xer &= ~(1 << XER_OV); - env->spr[SPR_MQ] = (int32_t)arg1 % (int32_t)arg2; - return (int32_t)arg1 / (int32_t)arg2; - } -} - -#if !defined (CONFIG_USER_ONLY) -target_ulong helper_rac (target_ulong addr) -{ - mmu_ctx_t ctx; - int nb_BATs; - target_ulong ret = 0; - - /* We don't have to generate many instances of this instruction, - * as rac is supervisor only. - */ - /* XXX: FIX THIS: Pretend we have no BAT */ - nb_BATs = env->nb_BATs; - env->nb_BATs = 0; - if (get_physical_address(env, &ctx, addr, 0, ACCESS_INT) == 0) - ret = ctx.raddr; - env->nb_BATs = nb_BATs; - return ret; -} - -void helper_rfsvc (void) -{ - do_rfi(env->lr, env->ctr, 0x0000FFFF, 0); -} -#endif - -/*****************************************************************************/ -/* 602 specific instructions */ -/* mfrom is the most crazy instruction ever seen, imho ! */ -/* Real implementation uses a ROM table. Do the same */ -/* Extremely decomposed: - * -arg / 256 - * return 256 * log10(10 + 1.0) + 0.5 - */ -#if !defined (CONFIG_USER_ONLY) -target_ulong helper_602_mfrom (target_ulong arg) -{ - if (likely(arg < 602)) { -#include "mfrom_table.c" - return mfrom_ROM_table[arg]; - } else { - return 0; - } -} -#endif - -/*****************************************************************************/ -/* Embedded PowerPC specific helpers */ - -/* XXX: to be improved to check access rights when in user-mode */ -target_ulong helper_load_dcr (target_ulong dcrn) -{ - uint32_t val = 0; - - if (unlikely(env->dcr_env == NULL)) { - qemu_log("No DCR environment\n"); - helper_raise_exception_err(POWERPC_EXCP_PROGRAM, - POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL); - } else if (unlikely(ppc_dcr_read(env->dcr_env, (uint32_t)dcrn, &val) != 0)) { - qemu_log("DCR read error %d %03x\n", (uint32_t)dcrn, (uint32_t)dcrn); - helper_raise_exception_err(POWERPC_EXCP_PROGRAM, - POWERPC_EXCP_INVAL | POWERPC_EXCP_PRIV_REG); - } - return val; -} - -void helper_store_dcr (target_ulong dcrn, target_ulong val) -{ - if (unlikely(env->dcr_env == NULL)) { - qemu_log("No DCR environment\n"); - helper_raise_exception_err(POWERPC_EXCP_PROGRAM, - POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL); - } else if (unlikely(ppc_dcr_write(env->dcr_env, (uint32_t)dcrn, (uint32_t)val) != 0)) { - qemu_log("DCR write error %d %03x\n", (uint32_t)dcrn, (uint32_t)dcrn); - helper_raise_exception_err(POWERPC_EXCP_PROGRAM, - POWERPC_EXCP_INVAL | POWERPC_EXCP_PRIV_REG); - } -} - -#if !defined(CONFIG_USER_ONLY) -void helper_40x_rfci (void) -{ - do_rfi(env->spr[SPR_40x_SRR2], env->spr[SPR_40x_SRR3], - ~((target_ulong)0xFFFF0000), 0); -} - -void helper_rfci (void) -{ - do_rfi(env->spr[SPR_BOOKE_CSRR0], SPR_BOOKE_CSRR1, - ~((target_ulong)0x3FFF0000), 0); -} - -void helper_rfdi (void) -{ - do_rfi(env->spr[SPR_BOOKE_DSRR0], SPR_BOOKE_DSRR1, - ~((target_ulong)0x3FFF0000), 0); -} - -void helper_rfmci (void) -{ - do_rfi(env->spr[SPR_BOOKE_MCSRR0], SPR_BOOKE_MCSRR1, - ~((target_ulong)0x3FFF0000), 0); -} -#endif - -/* 440 specific */ -target_ulong helper_dlmzb (target_ulong high, target_ulong low, uint32_t update_Rc) -{ - target_ulong mask; - int i; - - i = 1; - for (mask = 0xFF000000; mask != 0; mask = mask >> 8) { - if ((high & mask) == 0) { - if (update_Rc) { - env->crf[0] = 0x4; - } - goto done; - } - i++; - } - for (mask = 0xFF000000; mask != 0; mask = mask >> 8) { - if ((low & mask) == 0) { - if (update_Rc) { - env->crf[0] = 0x8; - } - goto done; - } - i++; - } - if (update_Rc) { - env->crf[0] = 0x2; - } - done: - env->xer = (env->xer & ~0x7F) | i; - if (update_Rc) { - env->crf[0] |= xer_so; - } - return i; -} - -/*****************************************************************************/ -/* Altivec extension helpers */ -#if defined(HOST_WORDS_BIGENDIAN) -#define HI_IDX 0 -#define LO_IDX 1 -#else -#define HI_IDX 1 -#define LO_IDX 0 -#endif - -#if defined(HOST_WORDS_BIGENDIAN) -#define VECTOR_FOR_INORDER_I(index, element) \ - for (index = 0; index < ARRAY_SIZE(r->element); index++) -#else -#define VECTOR_FOR_INORDER_I(index, element) \ - for (index = ARRAY_SIZE(r->element)-1; index >= 0; index--) -#endif - -/* If X is a NaN, store the corresponding QNaN into RESULT. Otherwise, - * execute the following block. */ -#define DO_HANDLE_NAN(result, x) \ - if (float32_is_any_nan(x)) { \ - CPU_FloatU __f; \ - __f.f = x; \ - __f.l = __f.l | (1 << 22); /* Set QNaN bit. */ \ - result = __f.f; \ - } else - -#define HANDLE_NAN1(result, x) \ - DO_HANDLE_NAN(result, x) -#define HANDLE_NAN2(result, x, y) \ - DO_HANDLE_NAN(result, x) DO_HANDLE_NAN(result, y) -#define HANDLE_NAN3(result, x, y, z) \ - DO_HANDLE_NAN(result, x) DO_HANDLE_NAN(result, y) DO_HANDLE_NAN(result, z) - -/* Saturating arithmetic helpers. */ -#define SATCVT(from, to, from_type, to_type, min, max) \ - static inline to_type cvt##from##to(from_type x, int *sat) \ - { \ - to_type r; \ - if (x < (from_type)min) { \ - r = min; \ - *sat = 1; \ - } else if (x > (from_type)max) { \ - r = max; \ - *sat = 1; \ - } else { \ - r = x; \ - } \ - return r; \ - } -#define SATCVTU(from, to, from_type, to_type, min, max) \ - static inline to_type cvt##from##to(from_type x, int *sat) \ - { \ - to_type r; \ - if (x > (from_type)max) { \ - r = max; \ - *sat = 1; \ - } else { \ - r = x; \ - } \ - return r; \ - } -SATCVT(sh, sb, int16_t, int8_t, INT8_MIN, INT8_MAX) -SATCVT(sw, sh, int32_t, int16_t, INT16_MIN, INT16_MAX) -SATCVT(sd, sw, int64_t, int32_t, INT32_MIN, INT32_MAX) - -SATCVTU(uh, ub, uint16_t, uint8_t, 0, UINT8_MAX) -SATCVTU(uw, uh, uint32_t, uint16_t, 0, UINT16_MAX) -SATCVTU(ud, uw, uint64_t, uint32_t, 0, UINT32_MAX) -SATCVT(sh, ub, int16_t, uint8_t, 0, UINT8_MAX) -SATCVT(sw, uh, int32_t, uint16_t, 0, UINT16_MAX) -SATCVT(sd, uw, int64_t, uint32_t, 0, UINT32_MAX) -#undef SATCVT -#undef SATCVTU - -#define LVE(name, access, swap, element) \ - void helper_##name (ppc_avr_t *r, target_ulong addr) \ - { \ - size_t n_elems = ARRAY_SIZE(r->element); \ - int adjust = HI_IDX*(n_elems-1); \ - int sh = sizeof(r->element[0]) >> 1; \ - int index = (addr & 0xf) >> sh; \ - if(msr_le) { \ - r->element[LO_IDX ? index : (adjust - index)] = swap(access(addr)); \ - } else { \ - r->element[LO_IDX ? index : (adjust - index)] = access(addr); \ - } \ - } -#define I(x) (x) -LVE(lvebx, ldub, I, u8) -LVE(lvehx, lduw, bswap16, u16) -LVE(lvewx, ldl, bswap32, u32) -#undef I -#undef LVE - -void helper_lvsl (ppc_avr_t *r, target_ulong sh) -{ - int i, j = (sh & 0xf); - - VECTOR_FOR_INORDER_I (i, u8) { - r->u8[i] = j++; - } -} - -void helper_lvsr (ppc_avr_t *r, target_ulong sh) -{ - int i, j = 0x10 - (sh & 0xf); - - VECTOR_FOR_INORDER_I (i, u8) { - r->u8[i] = j++; - } -} - -#define STVE(name, access, swap, element) \ - void helper_##name (ppc_avr_t *r, target_ulong addr) \ - { \ - size_t n_elems = ARRAY_SIZE(r->element); \ - int adjust = HI_IDX*(n_elems-1); \ - int sh = sizeof(r->element[0]) >> 1; \ - int index = (addr & 0xf) >> sh; \ - if(msr_le) { \ - access(addr, swap(r->element[LO_IDX ? index : (adjust - index)])); \ - } else { \ - access(addr, r->element[LO_IDX ? index : (adjust - index)]); \ - } \ - } -#define I(x) (x) -STVE(stvebx, stb, I, u8) -STVE(stvehx, stw, bswap16, u16) -STVE(stvewx, stl, bswap32, u32) -#undef I -#undef LVE - -void helper_mtvscr (ppc_avr_t *r) -{ -#if defined(HOST_WORDS_BIGENDIAN) - env->vscr = r->u32[3]; -#else - env->vscr = r->u32[0]; -#endif - set_flush_to_zero(vscr_nj, &env->vec_status); -} - -void helper_vaddcuw (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) -{ - int i; - for (i = 0; i < ARRAY_SIZE(r->u32); i++) { - r->u32[i] = ~a->u32[i] < b->u32[i]; - } -} - -#define VARITH_DO(name, op, element) \ -void helper_v##name (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ -{ \ - int i; \ - for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - r->element[i] = a->element[i] op b->element[i]; \ - } \ -} -#define VARITH(suffix, element) \ - VARITH_DO(add##suffix, +, element) \ - VARITH_DO(sub##suffix, -, element) -VARITH(ubm, u8) -VARITH(uhm, u16) -VARITH(uwm, u32) -#undef VARITH_DO -#undef VARITH - -#define VARITHFP(suffix, func) \ - void helper_v##suffix (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ - { \ - int i; \ - for (i = 0; i < ARRAY_SIZE(r->f); i++) { \ - HANDLE_NAN2(r->f[i], a->f[i], b->f[i]) { \ - r->f[i] = func(a->f[i], b->f[i], &env->vec_status); \ - } \ - } \ - } -VARITHFP(addfp, float32_add) -VARITHFP(subfp, float32_sub) -#undef VARITHFP - -#define VARITHSAT_CASE(type, op, cvt, element) \ - { \ - type result = (type)a->element[i] op (type)b->element[i]; \ - r->element[i] = cvt(result, &sat); \ - } - -#define VARITHSAT_DO(name, op, optype, cvt, element) \ - void helper_v##name (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ - { \ - int sat = 0; \ - int i; \ - for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - switch (sizeof(r->element[0])) { \ - case 1: VARITHSAT_CASE(optype, op, cvt, element); break; \ - case 2: VARITHSAT_CASE(optype, op, cvt, element); break; \ - case 4: VARITHSAT_CASE(optype, op, cvt, element); break; \ - } \ - } \ - if (sat) { \ - env->vscr |= (1 << VSCR_SAT); \ - } \ - } -#define VARITHSAT_SIGNED(suffix, element, optype, cvt) \ - VARITHSAT_DO(adds##suffix##s, +, optype, cvt, element) \ - VARITHSAT_DO(subs##suffix##s, -, optype, cvt, element) -#define VARITHSAT_UNSIGNED(suffix, element, optype, cvt) \ - VARITHSAT_DO(addu##suffix##s, +, optype, cvt, element) \ - VARITHSAT_DO(subu##suffix##s, -, optype, cvt, element) -VARITHSAT_SIGNED(b, s8, int16_t, cvtshsb) -VARITHSAT_SIGNED(h, s16, int32_t, cvtswsh) -VARITHSAT_SIGNED(w, s32, int64_t, cvtsdsw) -VARITHSAT_UNSIGNED(b, u8, uint16_t, cvtshub) -VARITHSAT_UNSIGNED(h, u16, uint32_t, cvtswuh) -VARITHSAT_UNSIGNED(w, u32, uint64_t, cvtsduw) -#undef VARITHSAT_CASE -#undef VARITHSAT_DO -#undef VARITHSAT_SIGNED -#undef VARITHSAT_UNSIGNED - -#define VAVG_DO(name, element, etype) \ - void helper_v##name (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ - { \ - int i; \ - for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - etype x = (etype)a->element[i] + (etype)b->element[i] + 1; \ - r->element[i] = x >> 1; \ - } \ - } - -#define VAVG(type, signed_element, signed_type, unsigned_element, unsigned_type) \ - VAVG_DO(avgs##type, signed_element, signed_type) \ - VAVG_DO(avgu##type, unsigned_element, unsigned_type) -VAVG(b, s8, int16_t, u8, uint16_t) -VAVG(h, s16, int32_t, u16, uint32_t) -VAVG(w, s32, int64_t, u32, uint64_t) -#undef VAVG_DO -#undef VAVG - -#define VCF(suffix, cvt, element) \ - void helper_vcf##suffix (ppc_avr_t *r, ppc_avr_t *b, uint32_t uim) \ - { \ - int i; \ - for (i = 0; i < ARRAY_SIZE(r->f); i++) { \ - float32 t = cvt(b->element[i], &env->vec_status); \ - r->f[i] = float32_scalbn (t, -uim, &env->vec_status); \ - } \ - } -VCF(ux, uint32_to_float32, u32) -VCF(sx, int32_to_float32, s32) -#undef VCF - -#define VCMP_DO(suffix, compare, element, record) \ - void helper_vcmp##suffix (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ - { \ - uint32_t ones = (uint32_t)-1; \ - uint32_t all = ones; \ - uint32_t none = 0; \ - int i; \ - for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - uint32_t result = (a->element[i] compare b->element[i] ? ones : 0x0); \ - switch (sizeof (a->element[0])) { \ - case 4: r->u32[i] = result; break; \ - case 2: r->u16[i] = result; break; \ - case 1: r->u8[i] = result; break; \ - } \ - all &= result; \ - none |= result; \ - } \ - if (record) { \ - env->crf[6] = ((all != 0) << 3) | ((none == 0) << 1); \ - } \ - } -#define VCMP(suffix, compare, element) \ - VCMP_DO(suffix, compare, element, 0) \ - VCMP_DO(suffix##_dot, compare, element, 1) -VCMP(equb, ==, u8) -VCMP(equh, ==, u16) -VCMP(equw, ==, u32) -VCMP(gtub, >, u8) -VCMP(gtuh, >, u16) -VCMP(gtuw, >, u32) -VCMP(gtsb, >, s8) -VCMP(gtsh, >, s16) -VCMP(gtsw, >, s32) -#undef VCMP_DO -#undef VCMP - -#define VCMPFP_DO(suffix, compare, order, record) \ - void helper_vcmp##suffix (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ - { \ - uint32_t ones = (uint32_t)-1; \ - uint32_t all = ones; \ - uint32_t none = 0; \ - int i; \ - for (i = 0; i < ARRAY_SIZE(r->f); i++) { \ - uint32_t result; \ - int rel = float32_compare_quiet(a->f[i], b->f[i], &env->vec_status); \ - if (rel == float_relation_unordered) { \ - result = 0; \ - } else if (rel compare order) { \ - result = ones; \ - } else { \ - result = 0; \ - } \ - r->u32[i] = result; \ - all &= result; \ - none |= result; \ - } \ - if (record) { \ - env->crf[6] = ((all != 0) << 3) | ((none == 0) << 1); \ - } \ - } -#define VCMPFP(suffix, compare, order) \ - VCMPFP_DO(suffix, compare, order, 0) \ - VCMPFP_DO(suffix##_dot, compare, order, 1) -VCMPFP(eqfp, ==, float_relation_equal) -VCMPFP(gefp, !=, float_relation_less) -VCMPFP(gtfp, ==, float_relation_greater) -#undef VCMPFP_DO -#undef VCMPFP - -static inline void vcmpbfp_internal(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, - int record) -{ - int i; - int all_in = 0; - for (i = 0; i < ARRAY_SIZE(r->f); i++) { - int le_rel = float32_compare_quiet(a->f[i], b->f[i], &env->vec_status); - if (le_rel == float_relation_unordered) { - r->u32[i] = 0xc0000000; - /* ALL_IN does not need to be updated here. */ - } else { - float32 bneg = float32_chs(b->f[i]); - int ge_rel = float32_compare_quiet(a->f[i], bneg, &env->vec_status); - int le = le_rel != float_relation_greater; - int ge = ge_rel != float_relation_less; - r->u32[i] = ((!le) << 31) | ((!ge) << 30); - all_in |= (!le | !ge); - } - } - if (record) { - env->crf[6] = (all_in == 0) << 1; - } -} - -void helper_vcmpbfp (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) -{ - vcmpbfp_internal(r, a, b, 0); -} - -void helper_vcmpbfp_dot (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) -{ - vcmpbfp_internal(r, a, b, 1); -} - -#define VCT(suffix, satcvt, element) \ - void helper_vct##suffix (ppc_avr_t *r, ppc_avr_t *b, uint32_t uim) \ - { \ - int i; \ - int sat = 0; \ - float_status s = env->vec_status; \ - set_float_rounding_mode(float_round_to_zero, &s); \ - for (i = 0; i < ARRAY_SIZE(r->f); i++) { \ - if (float32_is_any_nan(b->f[i])) { \ - r->element[i] = 0; \ - } else { \ - float64 t = float32_to_float64(b->f[i], &s); \ - int64_t j; \ - t = float64_scalbn(t, uim, &s); \ - j = float64_to_int64(t, &s); \ - r->element[i] = satcvt(j, &sat); \ - } \ - } \ - if (sat) { \ - env->vscr |= (1 << VSCR_SAT); \ - } \ - } -VCT(uxs, cvtsduw, u32) -VCT(sxs, cvtsdsw, s32) -#undef VCT - -void helper_vmaddfp (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) -{ - int i; - for (i = 0; i < ARRAY_SIZE(r->f); i++) { - HANDLE_NAN3(r->f[i], a->f[i], b->f[i], c->f[i]) { - /* Need to do the computation in higher precision and round - * once at the end. */ - float64 af, bf, cf, t; - af = float32_to_float64(a->f[i], &env->vec_status); - bf = float32_to_float64(b->f[i], &env->vec_status); - cf = float32_to_float64(c->f[i], &env->vec_status); - t = float64_mul(af, cf, &env->vec_status); - t = float64_add(t, bf, &env->vec_status); - r->f[i] = float64_to_float32(t, &env->vec_status); - } - } -} - -void helper_vmhaddshs (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) -{ - int sat = 0; - int i; - - for (i = 0; i < ARRAY_SIZE(r->s16); i++) { - int32_t prod = a->s16[i] * b->s16[i]; - int32_t t = (int32_t)c->s16[i] + (prod >> 15); - r->s16[i] = cvtswsh (t, &sat); - } - - if (sat) { - env->vscr |= (1 << VSCR_SAT); - } -} - -void helper_vmhraddshs (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) -{ - int sat = 0; - int i; - - for (i = 0; i < ARRAY_SIZE(r->s16); i++) { - int32_t prod = a->s16[i] * b->s16[i] + 0x00004000; - int32_t t = (int32_t)c->s16[i] + (prod >> 15); - r->s16[i] = cvtswsh (t, &sat); - } - - if (sat) { - env->vscr |= (1 << VSCR_SAT); - } -} - -#define VMINMAX_DO(name, compare, element) \ - void helper_v##name (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ - { \ - int i; \ - for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - if (a->element[i] compare b->element[i]) { \ - r->element[i] = b->element[i]; \ - } else { \ - r->element[i] = a->element[i]; \ - } \ - } \ - } -#define VMINMAX(suffix, element) \ - VMINMAX_DO(min##suffix, >, element) \ - VMINMAX_DO(max##suffix, <, element) -VMINMAX(sb, s8) -VMINMAX(sh, s16) -VMINMAX(sw, s32) -VMINMAX(ub, u8) -VMINMAX(uh, u16) -VMINMAX(uw, u32) -#undef VMINMAX_DO -#undef VMINMAX - -#define VMINMAXFP(suffix, rT, rF) \ - void helper_v##suffix (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ - { \ - int i; \ - for (i = 0; i < ARRAY_SIZE(r->f); i++) { \ - HANDLE_NAN2(r->f[i], a->f[i], b->f[i]) { \ - if (float32_lt_quiet(a->f[i], b->f[i], &env->vec_status)) { \ - r->f[i] = rT->f[i]; \ - } else { \ - r->f[i] = rF->f[i]; \ - } \ - } \ - } \ - } -VMINMAXFP(minfp, a, b) -VMINMAXFP(maxfp, b, a) -#undef VMINMAXFP - -void helper_vmladduhm (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) -{ - int i; - for (i = 0; i < ARRAY_SIZE(r->s16); i++) { - int32_t prod = a->s16[i] * b->s16[i]; - r->s16[i] = (int16_t) (prod + c->s16[i]); - } -} - -#define VMRG_DO(name, element, highp) \ - void helper_v##name (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ - { \ - ppc_avr_t result; \ - int i; \ - size_t n_elems = ARRAY_SIZE(r->element); \ - for (i = 0; i < n_elems/2; i++) { \ - if (highp) { \ - result.element[i*2+HI_IDX] = a->element[i]; \ - result.element[i*2+LO_IDX] = b->element[i]; \ - } else { \ - result.element[n_elems - i*2 - (1+HI_IDX)] = b->element[n_elems - i - 1]; \ - result.element[n_elems - i*2 - (1+LO_IDX)] = a->element[n_elems - i - 1]; \ - } \ - } \ - *r = result; \ - } -#if defined(HOST_WORDS_BIGENDIAN) -#define MRGHI 0 -#define MRGLO 1 -#else -#define MRGHI 1 -#define MRGLO 0 -#endif -#define VMRG(suffix, element) \ - VMRG_DO(mrgl##suffix, element, MRGHI) \ - VMRG_DO(mrgh##suffix, element, MRGLO) -VMRG(b, u8) -VMRG(h, u16) -VMRG(w, u32) -#undef VMRG_DO -#undef VMRG -#undef MRGHI -#undef MRGLO - -void helper_vmsummbm (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) -{ - int32_t prod[16]; - int i; - - for (i = 0; i < ARRAY_SIZE(r->s8); i++) { - prod[i] = (int32_t)a->s8[i] * b->u8[i]; - } - - VECTOR_FOR_INORDER_I(i, s32) { - r->s32[i] = c->s32[i] + prod[4*i] + prod[4*i+1] + prod[4*i+2] + prod[4*i+3]; - } -} - -void helper_vmsumshm (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) -{ - int32_t prod[8]; - int i; - - for (i = 0; i < ARRAY_SIZE(r->s16); i++) { - prod[i] = a->s16[i] * b->s16[i]; - } - - VECTOR_FOR_INORDER_I(i, s32) { - r->s32[i] = c->s32[i] + prod[2*i] + prod[2*i+1]; - } -} - -void helper_vmsumshs (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) -{ - int32_t prod[8]; - int i; - int sat = 0; - - for (i = 0; i < ARRAY_SIZE(r->s16); i++) { - prod[i] = (int32_t)a->s16[i] * b->s16[i]; - } - - VECTOR_FOR_INORDER_I (i, s32) { - int64_t t = (int64_t)c->s32[i] + prod[2*i] + prod[2*i+1]; - r->u32[i] = cvtsdsw(t, &sat); - } - - if (sat) { - env->vscr |= (1 << VSCR_SAT); - } -} - -void helper_vmsumubm (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) -{ - uint16_t prod[16]; - int i; - - for (i = 0; i < ARRAY_SIZE(r->u8); i++) { - prod[i] = a->u8[i] * b->u8[i]; - } - - VECTOR_FOR_INORDER_I(i, u32) { - r->u32[i] = c->u32[i] + prod[4*i] + prod[4*i+1] + prod[4*i+2] + prod[4*i+3]; - } -} - -void helper_vmsumuhm (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) -{ - uint32_t prod[8]; - int i; - - for (i = 0; i < ARRAY_SIZE(r->u16); i++) { - prod[i] = a->u16[i] * b->u16[i]; - } - - VECTOR_FOR_INORDER_I(i, u32) { - r->u32[i] = c->u32[i] + prod[2*i] + prod[2*i+1]; - } -} - -void helper_vmsumuhs (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) -{ - uint32_t prod[8]; - int i; - int sat = 0; - - for (i = 0; i < ARRAY_SIZE(r->u16); i++) { - prod[i] = a->u16[i] * b->u16[i]; - } - - VECTOR_FOR_INORDER_I (i, s32) { - uint64_t t = (uint64_t)c->u32[i] + prod[2*i] + prod[2*i+1]; - r->u32[i] = cvtuduw(t, &sat); - } - - if (sat) { - env->vscr |= (1 << VSCR_SAT); - } -} - -#define VMUL_DO(name, mul_element, prod_element, evenp) \ - void helper_v##name (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ - { \ - int i; \ - VECTOR_FOR_INORDER_I(i, prod_element) { \ - if (evenp) { \ - r->prod_element[i] = a->mul_element[i*2+HI_IDX] * b->mul_element[i*2+HI_IDX]; \ - } else { \ - r->prod_element[i] = a->mul_element[i*2+LO_IDX] * b->mul_element[i*2+LO_IDX]; \ - } \ - } \ - } -#define VMUL(suffix, mul_element, prod_element) \ - VMUL_DO(mule##suffix, mul_element, prod_element, 1) \ - VMUL_DO(mulo##suffix, mul_element, prod_element, 0) -VMUL(sb, s8, s16) -VMUL(sh, s16, s32) -VMUL(ub, u8, u16) -VMUL(uh, u16, u32) -#undef VMUL_DO -#undef VMUL - -void helper_vnmsubfp (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) -{ - int i; - for (i = 0; i < ARRAY_SIZE(r->f); i++) { - HANDLE_NAN3(r->f[i], a->f[i], b->f[i], c->f[i]) { - /* Need to do the computation is higher precision and round - * once at the end. */ - float64 af, bf, cf, t; - af = float32_to_float64(a->f[i], &env->vec_status); - bf = float32_to_float64(b->f[i], &env->vec_status); - cf = float32_to_float64(c->f[i], &env->vec_status); - t = float64_mul(af, cf, &env->vec_status); - t = float64_sub(t, bf, &env->vec_status); - t = float64_chs(t); - r->f[i] = float64_to_float32(t, &env->vec_status); - } - } -} - -void helper_vperm (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) -{ - ppc_avr_t result; - int i; - VECTOR_FOR_INORDER_I (i, u8) { - int s = c->u8[i] & 0x1f; -#if defined(HOST_WORDS_BIGENDIAN) - int index = s & 0xf; -#else - int index = 15 - (s & 0xf); -#endif - if (s & 0x10) { - result.u8[i] = b->u8[index]; - } else { - result.u8[i] = a->u8[index]; - } - } - *r = result; -} - -#if defined(HOST_WORDS_BIGENDIAN) -#define PKBIG 1 -#else -#define PKBIG 0 -#endif -void helper_vpkpx (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) -{ - int i, j; - ppc_avr_t result; -#if defined(HOST_WORDS_BIGENDIAN) - const ppc_avr_t *x[2] = { a, b }; -#else - const ppc_avr_t *x[2] = { b, a }; -#endif - - VECTOR_FOR_INORDER_I (i, u64) { - VECTOR_FOR_INORDER_I (j, u32){ - uint32_t e = x[i]->u32[j]; - result.u16[4*i+j] = (((e >> 9) & 0xfc00) | - ((e >> 6) & 0x3e0) | - ((e >> 3) & 0x1f)); - } - } - *r = result; -} - -#define VPK(suffix, from, to, cvt, dosat) \ - void helper_vpk##suffix (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ - { \ - int i; \ - int sat = 0; \ - ppc_avr_t result; \ - ppc_avr_t *a0 = PKBIG ? a : b; \ - ppc_avr_t *a1 = PKBIG ? b : a; \ - VECTOR_FOR_INORDER_I (i, from) { \ - result.to[i] = cvt(a0->from[i], &sat); \ - result.to[i+ARRAY_SIZE(r->from)] = cvt(a1->from[i], &sat); \ - } \ - *r = result; \ - if (dosat && sat) { \ - env->vscr |= (1 << VSCR_SAT); \ - } \ - } -#define I(x, y) (x) -VPK(shss, s16, s8, cvtshsb, 1) -VPK(shus, s16, u8, cvtshub, 1) -VPK(swss, s32, s16, cvtswsh, 1) -VPK(swus, s32, u16, cvtswuh, 1) -VPK(uhus, u16, u8, cvtuhub, 1) -VPK(uwus, u32, u16, cvtuwuh, 1) -VPK(uhum, u16, u8, I, 0) -VPK(uwum, u32, u16, I, 0) -#undef I -#undef VPK -#undef PKBIG - -void helper_vrefp (ppc_avr_t *r, ppc_avr_t *b) -{ - int i; - for (i = 0; i < ARRAY_SIZE(r->f); i++) { - HANDLE_NAN1(r->f[i], b->f[i]) { - r->f[i] = float32_div(float32_one, b->f[i], &env->vec_status); - } - } -} - -#define VRFI(suffix, rounding) \ - void helper_vrfi##suffix (ppc_avr_t *r, ppc_avr_t *b) \ - { \ - int i; \ - float_status s = env->vec_status; \ - set_float_rounding_mode(rounding, &s); \ - for (i = 0; i < ARRAY_SIZE(r->f); i++) { \ - HANDLE_NAN1(r->f[i], b->f[i]) { \ - r->f[i] = float32_round_to_int (b->f[i], &s); \ - } \ - } \ - } -VRFI(n, float_round_nearest_even) -VRFI(m, float_round_down) -VRFI(p, float_round_up) -VRFI(z, float_round_to_zero) -#undef VRFI - -#define VROTATE(suffix, element) \ - void helper_vrl##suffix (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ - { \ - int i; \ - for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - unsigned int mask = ((1 << (3 + (sizeof (a->element[0]) >> 1))) - 1); \ - unsigned int shift = b->element[i] & mask; \ - r->element[i] = (a->element[i] << shift) | (a->element[i] >> (sizeof(a->element[0]) * 8 - shift)); \ - } \ - } -VROTATE(b, u8) -VROTATE(h, u16) -VROTATE(w, u32) -#undef VROTATE - -void helper_vrsqrtefp (ppc_avr_t *r, ppc_avr_t *b) -{ - int i; - for (i = 0; i < ARRAY_SIZE(r->f); i++) { - HANDLE_NAN1(r->f[i], b->f[i]) { - float32 t = float32_sqrt(b->f[i], &env->vec_status); - r->f[i] = float32_div(float32_one, t, &env->vec_status); - } - } -} - -void helper_vsel (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) -{ - r->u64[0] = (a->u64[0] & ~c->u64[0]) | (b->u64[0] & c->u64[0]); - r->u64[1] = (a->u64[1] & ~c->u64[1]) | (b->u64[1] & c->u64[1]); -} - -void helper_vexptefp (ppc_avr_t *r, ppc_avr_t *b) -{ - int i; - for (i = 0; i < ARRAY_SIZE(r->f); i++) { - HANDLE_NAN1(r->f[i], b->f[i]) { - r->f[i] = float32_exp2(b->f[i], &env->vec_status); - } - } -} - -void helper_vlogefp (ppc_avr_t *r, ppc_avr_t *b) -{ - int i; - for (i = 0; i < ARRAY_SIZE(r->f); i++) { - HANDLE_NAN1(r->f[i], b->f[i]) { - r->f[i] = float32_log2(b->f[i], &env->vec_status); - } - } -} - -#if defined(HOST_WORDS_BIGENDIAN) -#define LEFT 0 -#define RIGHT 1 -#else -#define LEFT 1 -#define RIGHT 0 -#endif -/* The specification says that the results are undefined if all of the - * shift counts are not identical. We check to make sure that they are - * to conform to what real hardware appears to do. */ -#define VSHIFT(suffix, leftp) \ - void helper_vs##suffix (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ - { \ - int shift = b->u8[LO_IDX*15] & 0x7; \ - int doit = 1; \ - int i; \ - for (i = 0; i < ARRAY_SIZE(r->u8); i++) { \ - doit = doit && ((b->u8[i] & 0x7) == shift); \ - } \ - if (doit) { \ - if (shift == 0) { \ - *r = *a; \ - } else if (leftp) { \ - uint64_t carry = a->u64[LO_IDX] >> (64 - shift); \ - r->u64[HI_IDX] = (a->u64[HI_IDX] << shift) | carry; \ - r->u64[LO_IDX] = a->u64[LO_IDX] << shift; \ - } else { \ - uint64_t carry = a->u64[HI_IDX] << (64 - shift); \ - r->u64[LO_IDX] = (a->u64[LO_IDX] >> shift) | carry; \ - r->u64[HI_IDX] = a->u64[HI_IDX] >> shift; \ - } \ - } \ - } -VSHIFT(l, LEFT) -VSHIFT(r, RIGHT) -#undef VSHIFT -#undef LEFT -#undef RIGHT - -#define VSL(suffix, element) \ - void helper_vsl##suffix (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ - { \ - int i; \ - for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - unsigned int mask = ((1 << (3 + (sizeof (a->element[0]) >> 1))) - 1); \ - unsigned int shift = b->element[i] & mask; \ - r->element[i] = a->element[i] << shift; \ - } \ - } -VSL(b, u8) -VSL(h, u16) -VSL(w, u32) -#undef VSL - -void helper_vsldoi (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t shift) -{ - int sh = shift & 0xf; - int i; - ppc_avr_t result; - -#if defined(HOST_WORDS_BIGENDIAN) - for (i = 0; i < ARRAY_SIZE(r->u8); i++) { - int index = sh + i; - if (index > 0xf) { - result.u8[i] = b->u8[index-0x10]; - } else { - result.u8[i] = a->u8[index]; - } - } -#else - for (i = 0; i < ARRAY_SIZE(r->u8); i++) { - int index = (16 - sh) + i; - if (index > 0xf) { - result.u8[i] = a->u8[index-0x10]; - } else { - result.u8[i] = b->u8[index]; - } - } -#endif - *r = result; -} - -void helper_vslo (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) -{ - int sh = (b->u8[LO_IDX*0xf] >> 3) & 0xf; - -#if defined (HOST_WORDS_BIGENDIAN) - memmove (&r->u8[0], &a->u8[sh], 16-sh); - memset (&r->u8[16-sh], 0, sh); -#else - memmove (&r->u8[sh], &a->u8[0], 16-sh); - memset (&r->u8[0], 0, sh); -#endif -} - -/* Experimental testing shows that hardware masks the immediate. */ -#define _SPLAT_MASKED(element) (splat & (ARRAY_SIZE(r->element) - 1)) -#if defined(HOST_WORDS_BIGENDIAN) -#define SPLAT_ELEMENT(element) _SPLAT_MASKED(element) -#else -#define SPLAT_ELEMENT(element) (ARRAY_SIZE(r->element)-1 - _SPLAT_MASKED(element)) -#endif -#define VSPLT(suffix, element) \ - void helper_vsplt##suffix (ppc_avr_t *r, ppc_avr_t *b, uint32_t splat) \ - { \ - uint32_t s = b->element[SPLAT_ELEMENT(element)]; \ - int i; \ - for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - r->element[i] = s; \ - } \ - } -VSPLT(b, u8) -VSPLT(h, u16) -VSPLT(w, u32) -#undef VSPLT -#undef SPLAT_ELEMENT -#undef _SPLAT_MASKED - -#define VSPLTI(suffix, element, splat_type) \ - void helper_vspltis##suffix (ppc_avr_t *r, uint32_t splat) \ - { \ - splat_type x = (int8_t)(splat << 3) >> 3; \ - int i; \ - for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - r->element[i] = x; \ - } \ - } -VSPLTI(b, s8, int8_t) -VSPLTI(h, s16, int16_t) -VSPLTI(w, s32, int32_t) -#undef VSPLTI - -#define VSR(suffix, element) \ - void helper_vsr##suffix (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ - { \ - int i; \ - for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - unsigned int mask = ((1 << (3 + (sizeof (a->element[0]) >> 1))) - 1); \ - unsigned int shift = b->element[i] & mask; \ - r->element[i] = a->element[i] >> shift; \ - } \ - } -VSR(ab, s8) -VSR(ah, s16) -VSR(aw, s32) -VSR(b, u8) -VSR(h, u16) -VSR(w, u32) -#undef VSR - -void helper_vsro (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) -{ - int sh = (b->u8[LO_IDX*0xf] >> 3) & 0xf; - -#if defined (HOST_WORDS_BIGENDIAN) - memmove (&r->u8[sh], &a->u8[0], 16-sh); - memset (&r->u8[0], 0, sh); -#else - memmove (&r->u8[0], &a->u8[sh], 16-sh); - memset (&r->u8[16-sh], 0, sh); -#endif -} - -void helper_vsubcuw (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) -{ - int i; - for (i = 0; i < ARRAY_SIZE(r->u32); i++) { - r->u32[i] = a->u32[i] >= b->u32[i]; - } -} - -void helper_vsumsws (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) -{ - int64_t t; - int i, upper; - ppc_avr_t result; - int sat = 0; - -#if defined(HOST_WORDS_BIGENDIAN) - upper = ARRAY_SIZE(r->s32)-1; -#else - upper = 0; -#endif - t = (int64_t)b->s32[upper]; - for (i = 0; i < ARRAY_SIZE(r->s32); i++) { - t += a->s32[i]; - result.s32[i] = 0; - } - result.s32[upper] = cvtsdsw(t, &sat); - *r = result; - - if (sat) { - env->vscr |= (1 << VSCR_SAT); - } -} - -void helper_vsum2sws (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) -{ - int i, j, upper; - ppc_avr_t result; - int sat = 0; - -#if defined(HOST_WORDS_BIGENDIAN) - upper = 1; -#else - upper = 0; -#endif - for (i = 0; i < ARRAY_SIZE(r->u64); i++) { - int64_t t = (int64_t)b->s32[upper+i*2]; - result.u64[i] = 0; - for (j = 0; j < ARRAY_SIZE(r->u64); j++) { - t += a->s32[2*i+j]; - } - result.s32[upper+i*2] = cvtsdsw(t, &sat); - } - - *r = result; - if (sat) { - env->vscr |= (1 << VSCR_SAT); - } -} - -void helper_vsum4sbs (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) -{ - int i, j; - int sat = 0; - - for (i = 0; i < ARRAY_SIZE(r->s32); i++) { - int64_t t = (int64_t)b->s32[i]; - for (j = 0; j < ARRAY_SIZE(r->s32); j++) { - t += a->s8[4*i+j]; - } - r->s32[i] = cvtsdsw(t, &sat); - } - - if (sat) { - env->vscr |= (1 << VSCR_SAT); - } -} - -void helper_vsum4shs (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) -{ - int sat = 0; - int i; - - for (i = 0; i < ARRAY_SIZE(r->s32); i++) { - int64_t t = (int64_t)b->s32[i]; - t += a->s16[2*i] + a->s16[2*i+1]; - r->s32[i] = cvtsdsw(t, &sat); - } - - if (sat) { - env->vscr |= (1 << VSCR_SAT); - } -} - -void helper_vsum4ubs (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) -{ - int i, j; - int sat = 0; - - for (i = 0; i < ARRAY_SIZE(r->u32); i++) { - uint64_t t = (uint64_t)b->u32[i]; - for (j = 0; j < ARRAY_SIZE(r->u32); j++) { - t += a->u8[4*i+j]; - } - r->u32[i] = cvtuduw(t, &sat); - } - - if (sat) { - env->vscr |= (1 << VSCR_SAT); - } -} - -#if defined(HOST_WORDS_BIGENDIAN) -#define UPKHI 1 -#define UPKLO 0 -#else -#define UPKHI 0 -#define UPKLO 1 -#endif -#define VUPKPX(suffix, hi) \ - void helper_vupk##suffix (ppc_avr_t *r, ppc_avr_t *b) \ - { \ - int i; \ - ppc_avr_t result; \ - for (i = 0; i < ARRAY_SIZE(r->u32); i++) { \ - uint16_t e = b->u16[hi ? i : i+4]; \ - uint8_t a = (e >> 15) ? 0xff : 0; \ - uint8_t r = (e >> 10) & 0x1f; \ - uint8_t g = (e >> 5) & 0x1f; \ - uint8_t b = e & 0x1f; \ - result.u32[i] = (a << 24) | (r << 16) | (g << 8) | b; \ - } \ - *r = result; \ - } -VUPKPX(lpx, UPKLO) -VUPKPX(hpx, UPKHI) -#undef VUPKPX - -#define VUPK(suffix, unpacked, packee, hi) \ - void helper_vupk##suffix (ppc_avr_t *r, ppc_avr_t *b) \ - { \ - int i; \ - ppc_avr_t result; \ - if (hi) { \ - for (i = 0; i < ARRAY_SIZE(r->unpacked); i++) { \ - result.unpacked[i] = b->packee[i]; \ - } \ - } else { \ - for (i = ARRAY_SIZE(r->unpacked); i < ARRAY_SIZE(r->packee); i++) { \ - result.unpacked[i-ARRAY_SIZE(r->unpacked)] = b->packee[i]; \ - } \ - } \ - *r = result; \ - } -VUPK(hsb, s16, s8, UPKHI) -VUPK(hsh, s32, s16, UPKHI) -VUPK(lsb, s16, s8, UPKLO) -VUPK(lsh, s32, s16, UPKLO) -#undef VUPK -#undef UPKHI -#undef UPKLO - -#undef DO_HANDLE_NAN -#undef HANDLE_NAN1 -#undef HANDLE_NAN2 -#undef HANDLE_NAN3 -#undef VECTOR_FOR_INORDER_I -#undef HI_IDX -#undef LO_IDX - -/*****************************************************************************/ -/* SPE extension helpers */ -/* Use a table to make this quicker */ -static uint8_t hbrev[16] = { - 0x0, 0x8, 0x4, 0xC, 0x2, 0xA, 0x6, 0xE, - 0x1, 0x9, 0x5, 0xD, 0x3, 0xB, 0x7, 0xF, -}; - -static inline uint8_t byte_reverse(uint8_t val) -{ - return hbrev[val >> 4] | (hbrev[val & 0xF] << 4); -} - -static inline uint32_t word_reverse(uint32_t val) -{ - return byte_reverse(val >> 24) | (byte_reverse(val >> 16) << 8) | - (byte_reverse(val >> 8) << 16) | (byte_reverse(val) << 24); -} - -#define MASKBITS 16 // Random value - to be fixed (implementation dependent) -target_ulong helper_brinc (target_ulong arg1, target_ulong arg2) -{ - uint32_t a, b, d, mask; - - mask = UINT32_MAX >> (32 - MASKBITS); - a = arg1 & mask; - b = arg2 & mask; - d = word_reverse(1 + word_reverse(a | ~b)); - return (arg1 & ~mask) | (d & b); -} - -uint32_t helper_cntlsw32 (uint32_t val) -{ - if (val & 0x80000000) - return clz32(~val); - else - return clz32(val); -} - -uint32_t helper_cntlzw32 (uint32_t val) -{ - return clz32(val); -} - -/* Single-precision floating-point conversions */ -static inline uint32_t efscfsi(uint32_t val) -{ - CPU_FloatU u; - - u.f = int32_to_float32(val, &env->vec_status); - - return u.l; -} - -static inline uint32_t efscfui(uint32_t val) -{ - CPU_FloatU u; - - u.f = uint32_to_float32(val, &env->vec_status); - - return u.l; -} - -static inline int32_t efsctsi(uint32_t val) -{ - CPU_FloatU u; - - u.l = val; - /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float32_is_quiet_nan(u.f))) - return 0; - - return float32_to_int32(u.f, &env->vec_status); -} - -static inline uint32_t efsctui(uint32_t val) -{ - CPU_FloatU u; - - u.l = val; - /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float32_is_quiet_nan(u.f))) - return 0; - - return float32_to_uint32(u.f, &env->vec_status); -} - -static inline uint32_t efsctsiz(uint32_t val) -{ - CPU_FloatU u; - - u.l = val; - /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float32_is_quiet_nan(u.f))) - return 0; - - return float32_to_int32_round_to_zero(u.f, &env->vec_status); -} - -static inline uint32_t efsctuiz(uint32_t val) -{ - CPU_FloatU u; - - u.l = val; - /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float32_is_quiet_nan(u.f))) - return 0; - - return float32_to_uint32_round_to_zero(u.f, &env->vec_status); -} - -static inline uint32_t efscfsf(uint32_t val) -{ - CPU_FloatU u; - float32 tmp; - - u.f = int32_to_float32(val, &env->vec_status); - tmp = int64_to_float32(1ULL << 32, &env->vec_status); - u.f = float32_div(u.f, tmp, &env->vec_status); - - return u.l; -} - -static inline uint32_t efscfuf(uint32_t val) -{ - CPU_FloatU u; - float32 tmp; - - u.f = uint32_to_float32(val, &env->vec_status); - tmp = uint64_to_float32(1ULL << 32, &env->vec_status); - u.f = float32_div(u.f, tmp, &env->vec_status); - - return u.l; -} - -static inline uint32_t efsctsf(uint32_t val) -{ - CPU_FloatU u; - float32 tmp; - - u.l = val; - /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float32_is_quiet_nan(u.f))) - return 0; - tmp = uint64_to_float32(1ULL << 32, &env->vec_status); - u.f = float32_mul(u.f, tmp, &env->vec_status); - - return float32_to_int32(u.f, &env->vec_status); -} - -static inline uint32_t efsctuf(uint32_t val) -{ - CPU_FloatU u; - float32 tmp; - - u.l = val; - /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float32_is_quiet_nan(u.f))) - return 0; - tmp = uint64_to_float32(1ULL << 32, &env->vec_status); - u.f = float32_mul(u.f, tmp, &env->vec_status); - - return float32_to_uint32(u.f, &env->vec_status); -} - -#define HELPER_SPE_SINGLE_CONV(name) \ -uint32_t helper_e##name (uint32_t val) \ -{ \ - return e##name(val); \ -} -/* efscfsi */ -HELPER_SPE_SINGLE_CONV(fscfsi); -/* efscfui */ -HELPER_SPE_SINGLE_CONV(fscfui); -/* efscfuf */ -HELPER_SPE_SINGLE_CONV(fscfuf); -/* efscfsf */ -HELPER_SPE_SINGLE_CONV(fscfsf); -/* efsctsi */ -HELPER_SPE_SINGLE_CONV(fsctsi); -/* efsctui */ -HELPER_SPE_SINGLE_CONV(fsctui); -/* efsctsiz */ -HELPER_SPE_SINGLE_CONV(fsctsiz); -/* efsctuiz */ -HELPER_SPE_SINGLE_CONV(fsctuiz); -/* efsctsf */ -HELPER_SPE_SINGLE_CONV(fsctsf); -/* efsctuf */ -HELPER_SPE_SINGLE_CONV(fsctuf); - -#define HELPER_SPE_VECTOR_CONV(name) \ -uint64_t helper_ev##name (uint64_t val) \ -{ \ - return ((uint64_t)e##name(val >> 32) << 32) | \ - (uint64_t)e##name(val); \ -} -/* evfscfsi */ -HELPER_SPE_VECTOR_CONV(fscfsi); -/* evfscfui */ -HELPER_SPE_VECTOR_CONV(fscfui); -/* evfscfuf */ -HELPER_SPE_VECTOR_CONV(fscfuf); -/* evfscfsf */ -HELPER_SPE_VECTOR_CONV(fscfsf); -/* evfsctsi */ -HELPER_SPE_VECTOR_CONV(fsctsi); -/* evfsctui */ -HELPER_SPE_VECTOR_CONV(fsctui); -/* evfsctsiz */ -HELPER_SPE_VECTOR_CONV(fsctsiz); -/* evfsctuiz */ -HELPER_SPE_VECTOR_CONV(fsctuiz); -/* evfsctsf */ -HELPER_SPE_VECTOR_CONV(fsctsf); -/* evfsctuf */ -HELPER_SPE_VECTOR_CONV(fsctuf); - -/* Single-precision floating-point arithmetic */ -static inline uint32_t efsadd(uint32_t op1, uint32_t op2) -{ - CPU_FloatU u1, u2; - u1.l = op1; - u2.l = op2; - u1.f = float32_add(u1.f, u2.f, &env->vec_status); - return u1.l; -} - -static inline uint32_t efssub(uint32_t op1, uint32_t op2) -{ - CPU_FloatU u1, u2; - u1.l = op1; - u2.l = op2; - u1.f = float32_sub(u1.f, u2.f, &env->vec_status); - return u1.l; -} - -static inline uint32_t efsmul(uint32_t op1, uint32_t op2) -{ - CPU_FloatU u1, u2; - u1.l = op1; - u2.l = op2; - u1.f = float32_mul(u1.f, u2.f, &env->vec_status); - return u1.l; -} - -static inline uint32_t efsdiv(uint32_t op1, uint32_t op2) -{ - CPU_FloatU u1, u2; - u1.l = op1; - u2.l = op2; - u1.f = float32_div(u1.f, u2.f, &env->vec_status); - return u1.l; -} - -#define HELPER_SPE_SINGLE_ARITH(name) \ -uint32_t helper_e##name (uint32_t op1, uint32_t op2) \ -{ \ - return e##name(op1, op2); \ -} -/* efsadd */ -HELPER_SPE_SINGLE_ARITH(fsadd); -/* efssub */ -HELPER_SPE_SINGLE_ARITH(fssub); -/* efsmul */ -HELPER_SPE_SINGLE_ARITH(fsmul); -/* efsdiv */ -HELPER_SPE_SINGLE_ARITH(fsdiv); - -#define HELPER_SPE_VECTOR_ARITH(name) \ -uint64_t helper_ev##name (uint64_t op1, uint64_t op2) \ -{ \ - return ((uint64_t)e##name(op1 >> 32, op2 >> 32) << 32) | \ - (uint64_t)e##name(op1, op2); \ -} -/* evfsadd */ -HELPER_SPE_VECTOR_ARITH(fsadd); -/* evfssub */ -HELPER_SPE_VECTOR_ARITH(fssub); -/* evfsmul */ -HELPER_SPE_VECTOR_ARITH(fsmul); -/* evfsdiv */ -HELPER_SPE_VECTOR_ARITH(fsdiv); - -/* Single-precision floating-point comparisons */ -static inline uint32_t efscmplt(uint32_t op1, uint32_t op2) -{ - CPU_FloatU u1, u2; - u1.l = op1; - u2.l = op2; - return float32_lt(u1.f, u2.f, &env->vec_status) ? 4 : 0; -} - -static inline uint32_t efscmpgt(uint32_t op1, uint32_t op2) -{ - CPU_FloatU u1, u2; - u1.l = op1; - u2.l = op2; - return float32_le(u1.f, u2.f, &env->vec_status) ? 0 : 4; -} - -static inline uint32_t efscmpeq(uint32_t op1, uint32_t op2) -{ - CPU_FloatU u1, u2; - u1.l = op1; - u2.l = op2; - return float32_eq(u1.f, u2.f, &env->vec_status) ? 4 : 0; -} - -static inline uint32_t efststlt(uint32_t op1, uint32_t op2) -{ - /* XXX: TODO: ignore special values (NaN, infinites, ...) */ - return efscmplt(op1, op2); -} - -static inline uint32_t efststgt(uint32_t op1, uint32_t op2) -{ - /* XXX: TODO: ignore special values (NaN, infinites, ...) */ - return efscmpgt(op1, op2); -} - -static inline uint32_t efststeq(uint32_t op1, uint32_t op2) -{ - /* XXX: TODO: ignore special values (NaN, infinites, ...) */ - return efscmpeq(op1, op2); -} - -#define HELPER_SINGLE_SPE_CMP(name) \ -uint32_t helper_e##name (uint32_t op1, uint32_t op2) \ -{ \ - return e##name(op1, op2) << 2; \ -} -/* efststlt */ -HELPER_SINGLE_SPE_CMP(fststlt); -/* efststgt */ -HELPER_SINGLE_SPE_CMP(fststgt); -/* efststeq */ -HELPER_SINGLE_SPE_CMP(fststeq); -/* efscmplt */ -HELPER_SINGLE_SPE_CMP(fscmplt); -/* efscmpgt */ -HELPER_SINGLE_SPE_CMP(fscmpgt); -/* efscmpeq */ -HELPER_SINGLE_SPE_CMP(fscmpeq); - -static inline uint32_t evcmp_merge(int t0, int t1) -{ - return (t0 << 3) | (t1 << 2) | ((t0 | t1) << 1) | (t0 & t1); -} - -#define HELPER_VECTOR_SPE_CMP(name) \ -uint32_t helper_ev##name (uint64_t op1, uint64_t op2) \ -{ \ - return evcmp_merge(e##name(op1 >> 32, op2 >> 32), e##name(op1, op2)); \ -} -/* evfststlt */ -HELPER_VECTOR_SPE_CMP(fststlt); -/* evfststgt */ -HELPER_VECTOR_SPE_CMP(fststgt); -/* evfststeq */ -HELPER_VECTOR_SPE_CMP(fststeq); -/* evfscmplt */ -HELPER_VECTOR_SPE_CMP(fscmplt); -/* evfscmpgt */ -HELPER_VECTOR_SPE_CMP(fscmpgt); -/* evfscmpeq */ -HELPER_VECTOR_SPE_CMP(fscmpeq); - -/* Double-precision floating-point conversion */ -uint64_t helper_efdcfsi (uint32_t val) -{ - CPU_DoubleU u; - - u.d = int32_to_float64(val, &env->vec_status); - - return u.ll; -} - -uint64_t helper_efdcfsid (uint64_t val) -{ - CPU_DoubleU u; - - u.d = int64_to_float64(val, &env->vec_status); - - return u.ll; -} - -uint64_t helper_efdcfui (uint32_t val) -{ - CPU_DoubleU u; - - u.d = uint32_to_float64(val, &env->vec_status); - - return u.ll; -} - -uint64_t helper_efdcfuid (uint64_t val) -{ - CPU_DoubleU u; - - u.d = uint64_to_float64(val, &env->vec_status); - - return u.ll; -} - -uint32_t helper_efdctsi (uint64_t val) -{ - CPU_DoubleU u; - - u.ll = val; - /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float64_is_any_nan(u.d))) { - return 0; - } - - return float64_to_int32(u.d, &env->vec_status); -} - -uint32_t helper_efdctui (uint64_t val) -{ - CPU_DoubleU u; - - u.ll = val; - /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float64_is_any_nan(u.d))) { - return 0; - } - - return float64_to_uint32(u.d, &env->vec_status); -} - -uint32_t helper_efdctsiz (uint64_t val) -{ - CPU_DoubleU u; - - u.ll = val; - /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float64_is_any_nan(u.d))) { - return 0; - } - - return float64_to_int32_round_to_zero(u.d, &env->vec_status); -} - -uint64_t helper_efdctsidz (uint64_t val) -{ - CPU_DoubleU u; - - u.ll = val; - /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float64_is_any_nan(u.d))) { - return 0; - } - - return float64_to_int64_round_to_zero(u.d, &env->vec_status); -} - -uint32_t helper_efdctuiz (uint64_t val) -{ - CPU_DoubleU u; - - u.ll = val; - /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float64_is_any_nan(u.d))) { - return 0; - } - - return float64_to_uint32_round_to_zero(u.d, &env->vec_status); -} - -uint64_t helper_efdctuidz (uint64_t val) -{ - CPU_DoubleU u; - - u.ll = val; - /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float64_is_any_nan(u.d))) { - return 0; - } - - return float64_to_uint64_round_to_zero(u.d, &env->vec_status); -} - -uint64_t helper_efdcfsf (uint32_t val) -{ - CPU_DoubleU u; - float64 tmp; - - u.d = int32_to_float64(val, &env->vec_status); - tmp = int64_to_float64(1ULL << 32, &env->vec_status); - u.d = float64_div(u.d, tmp, &env->vec_status); - - return u.ll; -} - -uint64_t helper_efdcfuf (uint32_t val) -{ - CPU_DoubleU u; - float64 tmp; - - u.d = uint32_to_float64(val, &env->vec_status); - tmp = int64_to_float64(1ULL << 32, &env->vec_status); - u.d = float64_div(u.d, tmp, &env->vec_status); - - return u.ll; -} - -uint32_t helper_efdctsf (uint64_t val) -{ - CPU_DoubleU u; - float64 tmp; - - u.ll = val; - /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float64_is_any_nan(u.d))) { - return 0; - } - tmp = uint64_to_float64(1ULL << 32, &env->vec_status); - u.d = float64_mul(u.d, tmp, &env->vec_status); - - return float64_to_int32(u.d, &env->vec_status); -} - -uint32_t helper_efdctuf (uint64_t val) -{ - CPU_DoubleU u; - float64 tmp; - - u.ll = val; - /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float64_is_any_nan(u.d))) { - return 0; - } - tmp = uint64_to_float64(1ULL << 32, &env->vec_status); - u.d = float64_mul(u.d, tmp, &env->vec_status); - - return float64_to_uint32(u.d, &env->vec_status); -} - -uint32_t helper_efscfd (uint64_t val) -{ - CPU_DoubleU u1; - CPU_FloatU u2; - - u1.ll = val; - u2.f = float64_to_float32(u1.d, &env->vec_status); - - return u2.l; -} - -uint64_t helper_efdcfs (uint32_t val) -{ - CPU_DoubleU u2; - CPU_FloatU u1; - - u1.l = val; - u2.d = float32_to_float64(u1.f, &env->vec_status); - - return u2.ll; -} - -/* Double precision fixed-point arithmetic */ -uint64_t helper_efdadd (uint64_t op1, uint64_t op2) -{ - CPU_DoubleU u1, u2; - u1.ll = op1; - u2.ll = op2; - u1.d = float64_add(u1.d, u2.d, &env->vec_status); - return u1.ll; -} - -uint64_t helper_efdsub (uint64_t op1, uint64_t op2) -{ - CPU_DoubleU u1, u2; - u1.ll = op1; - u2.ll = op2; - u1.d = float64_sub(u1.d, u2.d, &env->vec_status); - return u1.ll; -} - -uint64_t helper_efdmul (uint64_t op1, uint64_t op2) -{ - CPU_DoubleU u1, u2; - u1.ll = op1; - u2.ll = op2; - u1.d = float64_mul(u1.d, u2.d, &env->vec_status); - return u1.ll; -} - -uint64_t helper_efddiv (uint64_t op1, uint64_t op2) -{ - CPU_DoubleU u1, u2; - u1.ll = op1; - u2.ll = op2; - u1.d = float64_div(u1.d, u2.d, &env->vec_status); - return u1.ll; -} - -/* Double precision floating point helpers */ -uint32_t helper_efdtstlt (uint64_t op1, uint64_t op2) -{ - CPU_DoubleU u1, u2; - u1.ll = op1; - u2.ll = op2; - return float64_lt(u1.d, u2.d, &env->vec_status) ? 4 : 0; -} - -uint32_t helper_efdtstgt (uint64_t op1, uint64_t op2) -{ - CPU_DoubleU u1, u2; - u1.ll = op1; - u2.ll = op2; - return float64_le(u1.d, u2.d, &env->vec_status) ? 0 : 4; -} - -uint32_t helper_efdtsteq (uint64_t op1, uint64_t op2) -{ - CPU_DoubleU u1, u2; - u1.ll = op1; - u2.ll = op2; - return float64_eq_quiet(u1.d, u2.d, &env->vec_status) ? 4 : 0; -} - -uint32_t helper_efdcmplt (uint64_t op1, uint64_t op2) -{ - /* XXX: TODO: test special values (NaN, infinites, ...) */ - return helper_efdtstlt(op1, op2); -} - -uint32_t helper_efdcmpgt (uint64_t op1, uint64_t op2) -{ - /* XXX: TODO: test special values (NaN, infinites, ...) */ - return helper_efdtstgt(op1, op2); -} - -uint32_t helper_efdcmpeq (uint64_t op1, uint64_t op2) -{ - /* XXX: TODO: test special values (NaN, infinites, ...) */ - return helper_efdtsteq(op1, op2); -} - -/*****************************************************************************/ -/* Softmmu support */ -#if !defined (CONFIG_USER_ONLY) - -#define MMUSUFFIX _mmu - -#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(CPUPPCState *env1, target_ulong addr, int is_write, int mmu_idx, - uintptr_t retaddr) -{ - TranslationBlock *tb; - CPUPPCState *saved_env; - int ret; - - saved_env = env; - env = env1; - ret = cpu_ppc_handle_mmu_fault(env, addr, is_write, mmu_idx); - if (unlikely(ret != 0)) { - if (likely(retaddr)) { - /* now we have a real cpu fault */ - tb = tb_find_pc(retaddr); - if (likely(tb)) { - /* the PC is inside the translated code. It means that we have - a virtual CPU fault */ - cpu_restore_state(tb, env, retaddr); - } - } - helper_raise_exception_err(env->exception_index, env->error_code); - } - env = saved_env; -} - -/* Segment registers load and store */ -target_ulong helper_load_sr (target_ulong sr_num) -{ -#if defined(TARGET_PPC64) - if (env->mmu_model & POWERPC_MMU_64) - return ppc_load_sr(env, sr_num); -#endif - return env->sr[sr_num]; -} - -void helper_store_sr (target_ulong sr_num, target_ulong val) -{ - ppc_store_sr(env, sr_num, val); -} - -/* SLB management */ -#if defined(TARGET_PPC64) -void helper_store_slb (target_ulong rb, target_ulong rs) -{ - if (ppc_store_slb(env, rb, rs) < 0) { - helper_raise_exception_err(POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL); - } -} - -target_ulong helper_load_slb_esid (target_ulong rb) -{ - target_ulong rt; - - if (ppc_load_slb_esid(env, rb, &rt) < 0) { - helper_raise_exception_err(POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL); - } - return rt; -} - -target_ulong helper_load_slb_vsid (target_ulong rb) -{ - target_ulong rt; - - if (ppc_load_slb_vsid(env, rb, &rt) < 0) { - helper_raise_exception_err(POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL); - } - return rt; -} - -void helper_slbia (void) -{ - ppc_slb_invalidate_all(env); -} - -void helper_slbie (target_ulong addr) -{ - ppc_slb_invalidate_one(env, addr); -} - -#endif /* defined(TARGET_PPC64) */ - -/* TLB management */ -void helper_tlbia (void) -{ - ppc_tlb_invalidate_all(env); -} - -void helper_tlbie (target_ulong addr) -{ - ppc_tlb_invalidate_one(env, addr); -} - -/* Software driven TLBs management */ -/* PowerPC 602/603 software TLB load instructions helpers */ -static void do_6xx_tlb (target_ulong new_EPN, int is_code) -{ - target_ulong RPN, CMP, EPN; - int way; - - RPN = env->spr[SPR_RPA]; - if (is_code) { - CMP = env->spr[SPR_ICMP]; - EPN = env->spr[SPR_IMISS]; - } else { - CMP = env->spr[SPR_DCMP]; - EPN = env->spr[SPR_DMISS]; - } - way = (env->spr[SPR_SRR1] >> 17) & 1; - (void)EPN; /* avoid a compiler warning */ - LOG_SWTLB("%s: EPN " TARGET_FMT_lx " " TARGET_FMT_lx " PTE0 " TARGET_FMT_lx - " PTE1 " TARGET_FMT_lx " way %d\n", __func__, new_EPN, EPN, CMP, - RPN, way); - /* Store this TLB */ - ppc6xx_tlb_store(env, (uint32_t)(new_EPN & TARGET_PAGE_MASK), - way, is_code, CMP, RPN); -} - -void helper_6xx_tlbd (target_ulong EPN) -{ - do_6xx_tlb(EPN, 0); -} - -void helper_6xx_tlbi (target_ulong EPN) -{ - do_6xx_tlb(EPN, 1); -} - -/* PowerPC 74xx software TLB load instructions helpers */ -static void do_74xx_tlb (target_ulong new_EPN, int is_code) -{ - target_ulong RPN, CMP, EPN; - int way; - - RPN = env->spr[SPR_PTELO]; - CMP = env->spr[SPR_PTEHI]; - EPN = env->spr[SPR_TLBMISS] & ~0x3; - way = env->spr[SPR_TLBMISS] & 0x3; - (void)EPN; /* avoid a compiler warning */ - LOG_SWTLB("%s: EPN " TARGET_FMT_lx " " TARGET_FMT_lx " PTE0 " TARGET_FMT_lx - " PTE1 " TARGET_FMT_lx " way %d\n", __func__, new_EPN, EPN, CMP, - RPN, way); - /* Store this TLB */ - ppc6xx_tlb_store(env, (uint32_t)(new_EPN & TARGET_PAGE_MASK), - way, is_code, CMP, RPN); -} - -void helper_74xx_tlbd (target_ulong EPN) -{ - do_74xx_tlb(EPN, 0); -} - -void helper_74xx_tlbi (target_ulong EPN) -{ - do_74xx_tlb(EPN, 1); -} - -static inline target_ulong booke_tlb_to_page_size(int size) -{ - return 1024 << (2 * size); -} - -static inline int booke_page_size_to_tlb(target_ulong page_size) -{ - int size; - - switch (page_size) { - case 0x00000400UL: - size = 0x0; - break; - case 0x00001000UL: - size = 0x1; - break; - case 0x00004000UL: - size = 0x2; - break; - case 0x00010000UL: - size = 0x3; - break; - case 0x00040000UL: - size = 0x4; - break; - case 0x00100000UL: - size = 0x5; - break; - case 0x00400000UL: - size = 0x6; - break; - case 0x01000000UL: - size = 0x7; - break; - case 0x04000000UL: - size = 0x8; - break; - case 0x10000000UL: - size = 0x9; - break; - case 0x40000000UL: - size = 0xA; - break; -#if defined (TARGET_PPC64) - case 0x000100000000ULL: - size = 0xB; - break; - case 0x000400000000ULL: - size = 0xC; - break; - case 0x001000000000ULL: - size = 0xD; - break; - case 0x004000000000ULL: - size = 0xE; - break; - case 0x010000000000ULL: - size = 0xF; - break; -#endif - default: - size = -1; - break; - } - - return size; -} - -/* Helpers for 4xx TLB management */ -#define PPC4XX_TLB_ENTRY_MASK 0x0000003f /* Mask for 64 TLB entries */ - -#define PPC4XX_TLBHI_V 0x00000040 -#define PPC4XX_TLBHI_E 0x00000020 -#define PPC4XX_TLBHI_SIZE_MIN 0 -#define PPC4XX_TLBHI_SIZE_MAX 7 -#define PPC4XX_TLBHI_SIZE_DEFAULT 1 -#define PPC4XX_TLBHI_SIZE_SHIFT 7 -#define PPC4XX_TLBHI_SIZE_MASK 0x00000007 - -#define PPC4XX_TLBLO_EX 0x00000200 -#define PPC4XX_TLBLO_WR 0x00000100 -#define PPC4XX_TLBLO_ATTR_MASK 0x000000FF -#define PPC4XX_TLBLO_RPN_MASK 0xFFFFFC00 - -target_ulong helper_4xx_tlbre_hi (target_ulong entry) -{ - ppcemb_tlb_t *tlb; - target_ulong ret; - int size; - - entry &= PPC4XX_TLB_ENTRY_MASK; - tlb = &env->tlb.tlbe[entry]; - ret = tlb->EPN; - if (tlb->prot & PAGE_VALID) { - ret |= PPC4XX_TLBHI_V; - } - size = booke_page_size_to_tlb(tlb->size); - if (size < PPC4XX_TLBHI_SIZE_MIN || size > PPC4XX_TLBHI_SIZE_MAX) { - size = PPC4XX_TLBHI_SIZE_DEFAULT; - } - ret |= size << PPC4XX_TLBHI_SIZE_SHIFT; - env->spr[SPR_40x_PID] = tlb->PID; - return ret; -} - -target_ulong helper_4xx_tlbre_lo (target_ulong entry) -{ - ppcemb_tlb_t *tlb; - target_ulong ret; - - entry &= PPC4XX_TLB_ENTRY_MASK; - tlb = &env->tlb.tlbe[entry]; - ret = tlb->RPN; - if (tlb->prot & PAGE_EXEC) { - ret |= PPC4XX_TLBLO_EX; - } - if (tlb->prot & PAGE_WRITE) { - ret |= PPC4XX_TLBLO_WR; - } - return ret; -} - -void helper_4xx_tlbwe_hi (target_ulong entry, target_ulong val) -{ - ppcemb_tlb_t *tlb; - target_ulong page, end; - - LOG_SWTLB("%s entry %d val " TARGET_FMT_lx "\n", __func__, (int)entry, - val); - entry &= PPC4XX_TLB_ENTRY_MASK; - tlb = &env->tlb.tlbe[entry]; - /* Invalidate previous TLB (if it's valid) */ - if (tlb->prot & PAGE_VALID) { - end = tlb->EPN + tlb->size; - LOG_SWTLB("%s: invalidate old TLB %d start " TARGET_FMT_lx " end " - TARGET_FMT_lx "\n", __func__, (int)entry, tlb->EPN, end); - for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) { - tlb_flush_page(env, page); - } - } - tlb->size = booke_tlb_to_page_size((val >> PPC4XX_TLBHI_SIZE_SHIFT) - & PPC4XX_TLBHI_SIZE_MASK); - /* We cannot handle TLB size < TARGET_PAGE_SIZE. - * If this ever occurs, one should use the ppcemb target instead - * of the ppc or ppc64 one - */ - if ((val & PPC4XX_TLBHI_V) && tlb->size < TARGET_PAGE_SIZE) { - cpu_abort(env, "TLB size " TARGET_FMT_lu " < %u " - "are not supported (%d)\n", - tlb->size, TARGET_PAGE_SIZE, (int)((val >> 7) & 0x7)); - } - tlb->EPN = val & ~(tlb->size - 1); - if (val & PPC4XX_TLBHI_V) { - tlb->prot |= PAGE_VALID; - if (val & PPC4XX_TLBHI_E) { - /* XXX: TO BE FIXED */ - cpu_abort(env, - "Little-endian TLB entries are not supported by now\n"); - } - } else { - tlb->prot &= ~PAGE_VALID; - } - tlb->PID = env->spr[SPR_40x_PID]; /* PID */ - LOG_SWTLB("%s: set up TLB %d RPN " TARGET_FMT_plx " EPN " TARGET_FMT_lx - " size " TARGET_FMT_lx " prot %c%c%c%c PID %d\n", __func__, - (int)entry, tlb->RPN, tlb->EPN, tlb->size, - tlb->prot & PAGE_READ ? 'r' : '-', - tlb->prot & PAGE_WRITE ? 'w' : '-', - tlb->prot & PAGE_EXEC ? 'x' : '-', - tlb->prot & PAGE_VALID ? 'v' : '-', (int)tlb->PID); - /* Invalidate new TLB (if valid) */ - if (tlb->prot & PAGE_VALID) { - end = tlb->EPN + tlb->size; - LOG_SWTLB("%s: invalidate TLB %d start " TARGET_FMT_lx " end " - TARGET_FMT_lx "\n", __func__, (int)entry, tlb->EPN, end); - for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) { - tlb_flush_page(env, page); - } - } -} - -void helper_4xx_tlbwe_lo (target_ulong entry, target_ulong val) -{ - ppcemb_tlb_t *tlb; - - LOG_SWTLB("%s entry %i val " TARGET_FMT_lx "\n", __func__, (int)entry, - val); - entry &= PPC4XX_TLB_ENTRY_MASK; - tlb = &env->tlb.tlbe[entry]; - tlb->attr = val & PPC4XX_TLBLO_ATTR_MASK; - tlb->RPN = val & PPC4XX_TLBLO_RPN_MASK; - tlb->prot = PAGE_READ; - if (val & PPC4XX_TLBLO_EX) { - tlb->prot |= PAGE_EXEC; - } - if (val & PPC4XX_TLBLO_WR) { - tlb->prot |= PAGE_WRITE; - } - LOG_SWTLB("%s: set up TLB %d RPN " TARGET_FMT_plx " EPN " TARGET_FMT_lx - " size " TARGET_FMT_lx " prot %c%c%c%c PID %d\n", __func__, - (int)entry, tlb->RPN, tlb->EPN, tlb->size, - tlb->prot & PAGE_READ ? 'r' : '-', - tlb->prot & PAGE_WRITE ? 'w' : '-', - tlb->prot & PAGE_EXEC ? 'x' : '-', - tlb->prot & PAGE_VALID ? 'v' : '-', (int)tlb->PID); -} - -target_ulong helper_4xx_tlbsx (target_ulong address) -{ - return ppcemb_tlb_search(env, address, env->spr[SPR_40x_PID]); -} - -/* PowerPC 440 TLB management */ -void helper_440_tlbwe (uint32_t word, target_ulong entry, target_ulong value) -{ - ppcemb_tlb_t *tlb; - target_ulong EPN, RPN, size; - int do_flush_tlbs; - - LOG_SWTLB("%s word %d entry %d value " TARGET_FMT_lx "\n", - __func__, word, (int)entry, value); - do_flush_tlbs = 0; - entry &= 0x3F; - tlb = &env->tlb.tlbe[entry]; - switch (word) { - default: - /* Just here to please gcc */ - case 0: - EPN = value & 0xFFFFFC00; - if ((tlb->prot & PAGE_VALID) && EPN != tlb->EPN) - do_flush_tlbs = 1; - tlb->EPN = EPN; - size = booke_tlb_to_page_size((value >> 4) & 0xF); - if ((tlb->prot & PAGE_VALID) && tlb->size < size) - do_flush_tlbs = 1; - tlb->size = size; - tlb->attr &= ~0x1; - tlb->attr |= (value >> 8) & 1; - if (value & 0x200) { - tlb->prot |= PAGE_VALID; - } else { - if (tlb->prot & PAGE_VALID) { - tlb->prot &= ~PAGE_VALID; - do_flush_tlbs = 1; - } - } - tlb->PID = env->spr[SPR_440_MMUCR] & 0x000000FF; - if (do_flush_tlbs) - tlb_flush(env, 1); - break; - case 1: - RPN = value & 0xFFFFFC0F; - if ((tlb->prot & PAGE_VALID) && tlb->RPN != RPN) - tlb_flush(env, 1); - tlb->RPN = RPN; - break; - case 2: - tlb->attr = (tlb->attr & 0x1) | (value & 0x0000FF00); - tlb->prot = tlb->prot & PAGE_VALID; - if (value & 0x1) - tlb->prot |= PAGE_READ << 4; - if (value & 0x2) - tlb->prot |= PAGE_WRITE << 4; - if (value & 0x4) - tlb->prot |= PAGE_EXEC << 4; - if (value & 0x8) - tlb->prot |= PAGE_READ; - if (value & 0x10) - tlb->prot |= PAGE_WRITE; - if (value & 0x20) - tlb->prot |= PAGE_EXEC; - break; - } -} - -target_ulong helper_440_tlbre (uint32_t word, target_ulong entry) -{ - ppcemb_tlb_t *tlb; - target_ulong ret; - int size; - - entry &= 0x3F; - tlb = &env->tlb.tlbe[entry]; - switch (word) { - default: - /* Just here to please gcc */ - case 0: - ret = tlb->EPN; - size = booke_page_size_to_tlb(tlb->size); - if (size < 0 || size > 0xF) - size = 1; - ret |= size << 4; - if (tlb->attr & 0x1) - ret |= 0x100; - if (tlb->prot & PAGE_VALID) - ret |= 0x200; - env->spr[SPR_440_MMUCR] &= ~0x000000FF; - env->spr[SPR_440_MMUCR] |= tlb->PID; - break; - case 1: - ret = tlb->RPN; - break; - case 2: - ret = tlb->attr & ~0x1; - if (tlb->prot & (PAGE_READ << 4)) - ret |= 0x1; - if (tlb->prot & (PAGE_WRITE << 4)) - ret |= 0x2; - if (tlb->prot & (PAGE_EXEC << 4)) - ret |= 0x4; - if (tlb->prot & PAGE_READ) - ret |= 0x8; - if (tlb->prot & PAGE_WRITE) - ret |= 0x10; - if (tlb->prot & PAGE_EXEC) - ret |= 0x20; - break; - } - return ret; -} - -target_ulong helper_440_tlbsx (target_ulong address) -{ - return ppcemb_tlb_search(env, address, env->spr[SPR_440_MMUCR] & 0xFF); -} - -/* PowerPC BookE 2.06 TLB management */ - -static ppcmas_tlb_t *booke206_cur_tlb(CPUPPCState *env) -{ - uint32_t tlbncfg = 0; - int esel = (env->spr[SPR_BOOKE_MAS0] & MAS0_ESEL_MASK) >> MAS0_ESEL_SHIFT; - int ea = (env->spr[SPR_BOOKE_MAS2] & MAS2_EPN_MASK); - int tlb; - - tlb = (env->spr[SPR_BOOKE_MAS0] & MAS0_TLBSEL_MASK) >> MAS0_TLBSEL_SHIFT; - tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlb]; - - if ((tlbncfg & TLBnCFG_HES) && (env->spr[SPR_BOOKE_MAS0] & MAS0_HES)) { - cpu_abort(env, "we don't support HES yet\n"); - } - - return booke206_get_tlbm(env, tlb, ea, esel); -} - -void helper_booke_setpid(uint32_t pidn, target_ulong pid) -{ - env->spr[pidn] = pid; - /* changing PIDs mean we're in a different address space now */ - tlb_flush(env, 1); -} - -void helper_booke206_tlbwe(void) -{ - uint32_t tlbncfg, tlbn; - ppcmas_tlb_t *tlb; - uint32_t size_tlb, size_ps; - - switch (env->spr[SPR_BOOKE_MAS0] & MAS0_WQ_MASK) { - case MAS0_WQ_ALWAYS: - /* good to go, write that entry */ - break; - case MAS0_WQ_COND: - /* XXX check if reserved */ - if (0) { - return; - } - break; - case MAS0_WQ_CLR_RSRV: - /* XXX clear entry */ - return; - default: - /* no idea what to do */ - return; - } - - if (((env->spr[SPR_BOOKE_MAS0] & MAS0_ATSEL) == MAS0_ATSEL_LRAT) && - !msr_gs) { - /* XXX we don't support direct LRAT setting yet */ - fprintf(stderr, "cpu: don't support LRAT setting yet\n"); - return; - } - - tlbn = (env->spr[SPR_BOOKE_MAS0] & MAS0_TLBSEL_MASK) >> MAS0_TLBSEL_SHIFT; - tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlbn]; - - tlb = booke206_cur_tlb(env); - - if (!tlb) { - helper_raise_exception_err(POWERPC_EXCP_PROGRAM, - POWERPC_EXCP_INVAL | - POWERPC_EXCP_INVAL_INVAL); - } - - /* check that we support the targeted size */ - size_tlb = (env->spr[SPR_BOOKE_MAS1] & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; - size_ps = booke206_tlbnps(env, tlbn); - if ((env->spr[SPR_BOOKE_MAS1] & MAS1_VALID) && (tlbncfg & TLBnCFG_AVAIL) && - !(size_ps & (1 << size_tlb))) { - helper_raise_exception_err(POWERPC_EXCP_PROGRAM, - POWERPC_EXCP_INVAL | - POWERPC_EXCP_INVAL_INVAL); - } - - if (msr_gs) { - cpu_abort(env, "missing HV implementation\n"); - } - tlb->mas7_3 = ((uint64_t)env->spr[SPR_BOOKE_MAS7] << 32) | - env->spr[SPR_BOOKE_MAS3]; - tlb->mas1 = env->spr[SPR_BOOKE_MAS1]; - - /* MAV 1.0 only */ - if (!(tlbncfg & TLBnCFG_AVAIL)) { - /* force !AVAIL TLB entries to correct page size */ - tlb->mas1 &= ~MAS1_TSIZE_MASK; - /* XXX can be configured in MMUCSR0 */ - tlb->mas1 |= (tlbncfg & TLBnCFG_MINSIZE) >> 12; - } - - /* XXX needs to change when supporting 64-bit e500 */ - tlb->mas2 = env->spr[SPR_BOOKE_MAS2] & 0xffffffff; - - if (!(tlbncfg & TLBnCFG_IPROT)) { - /* no IPROT supported by TLB */ - tlb->mas1 &= ~MAS1_IPROT; - } - - if (booke206_tlb_to_page_size(env, tlb) == TARGET_PAGE_SIZE) { - tlb_flush_page(env, tlb->mas2 & MAS2_EPN_MASK); - } else { - tlb_flush(env, 1); - } -} - -static inline void booke206_tlb_to_mas(CPUPPCState *env, ppcmas_tlb_t *tlb) -{ - int tlbn = booke206_tlbm_to_tlbn(env, tlb); - int way = booke206_tlbm_to_way(env, tlb); - - env->spr[SPR_BOOKE_MAS0] = tlbn << MAS0_TLBSEL_SHIFT; - env->spr[SPR_BOOKE_MAS0] |= way << MAS0_ESEL_SHIFT; - env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT; - - env->spr[SPR_BOOKE_MAS1] = tlb->mas1; - env->spr[SPR_BOOKE_MAS2] = tlb->mas2; - env->spr[SPR_BOOKE_MAS3] = tlb->mas7_3; - env->spr[SPR_BOOKE_MAS7] = tlb->mas7_3 >> 32; -} - -void helper_booke206_tlbre(void) -{ - ppcmas_tlb_t *tlb = NULL; - - tlb = booke206_cur_tlb(env); - if (!tlb) { - env->spr[SPR_BOOKE_MAS1] = 0; - } else { - booke206_tlb_to_mas(env, tlb); - } -} - -void helper_booke206_tlbsx(target_ulong address) -{ - ppcmas_tlb_t *tlb = NULL; - int i, j; - target_phys_addr_t raddr; - uint32_t spid, sas; - - spid = (env->spr[SPR_BOOKE_MAS6] & MAS6_SPID_MASK) >> MAS6_SPID_SHIFT; - sas = env->spr[SPR_BOOKE_MAS6] & MAS6_SAS; - - for (i = 0; i < BOOKE206_MAX_TLBN; i++) { - int ways = booke206_tlb_ways(env, i); - - for (j = 0; j < ways; j++) { - tlb = booke206_get_tlbm(env, i, address, j); - - if (!tlb) { - continue; - } - - if (ppcmas_tlb_check(env, tlb, &raddr, address, spid)) { - continue; - } - - if (sas != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { - continue; - } - - booke206_tlb_to_mas(env, tlb); - return; - } - } - - /* no entry found, fill with defaults */ - env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK; - env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK; - env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK; - env->spr[SPR_BOOKE_MAS3] = 0; - env->spr[SPR_BOOKE_MAS7] = 0; - - if (env->spr[SPR_BOOKE_MAS6] & MAS6_SAS) { - env->spr[SPR_BOOKE_MAS1] |= MAS1_TS; - } - - env->spr[SPR_BOOKE_MAS1] |= (env->spr[SPR_BOOKE_MAS6] >> 16) - << MAS1_TID_SHIFT; - - /* next victim logic */ - env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT; - env->last_way++; - env->last_way &= booke206_tlb_ways(env, 0) - 1; - env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT; -} - -static inline void booke206_invalidate_ea_tlb(CPUPPCState *env, int tlbn, - uint32_t ea) -{ - int i; - int ways = booke206_tlb_ways(env, tlbn); - target_ulong mask; - - for (i = 0; i < ways; i++) { - ppcmas_tlb_t *tlb = booke206_get_tlbm(env, tlbn, ea, i); - if (!tlb) { - continue; - } - mask = ~(booke206_tlb_to_page_size(env, tlb) - 1); - if (((tlb->mas2 & MAS2_EPN_MASK) == (ea & mask)) && - !(tlb->mas1 & MAS1_IPROT)) { - tlb->mas1 &= ~MAS1_VALID; - } - } -} - -void helper_booke206_tlbivax(target_ulong address) -{ - if (address & 0x4) { - /* flush all entries */ - if (address & 0x8) { - /* flush all of TLB1 */ - booke206_flush_tlb(env, BOOKE206_FLUSH_TLB1, 1); - } else { - /* flush all of TLB0 */ - booke206_flush_tlb(env, BOOKE206_FLUSH_TLB0, 0); - } - return; - } - - if (address & 0x8) { - /* flush TLB1 entries */ - booke206_invalidate_ea_tlb(env, 1, address); - tlb_flush(env, 1); - } else { - /* flush TLB0 entries */ - booke206_invalidate_ea_tlb(env, 0, address); - tlb_flush_page(env, address & MAS2_EPN_MASK); - } -} - -void helper_booke206_tlbilx0(target_ulong address) -{ - /* XXX missing LPID handling */ - booke206_flush_tlb(env, -1, 1); -} - -void helper_booke206_tlbilx1(target_ulong address) -{ - int i, j; - int tid = (env->spr[SPR_BOOKE_MAS6] & MAS6_SPID); - ppcmas_tlb_t *tlb = env->tlb.tlbm; - int tlb_size; - - /* XXX missing LPID handling */ - for (i = 0; i < BOOKE206_MAX_TLBN; i++) { - tlb_size = booke206_tlb_size(env, i); - for (j = 0; j < tlb_size; j++) { - if (!(tlb[j].mas1 & MAS1_IPROT) && - ((tlb[j].mas1 & MAS1_TID_MASK) == tid)) { - tlb[j].mas1 &= ~MAS1_VALID; - } - } - tlb += booke206_tlb_size(env, i); - } - tlb_flush(env, 1); -} - -void helper_booke206_tlbilx3(target_ulong address) -{ - int i, j; - ppcmas_tlb_t *tlb; - int tid = (env->spr[SPR_BOOKE_MAS6] & MAS6_SPID); - int pid = tid >> MAS6_SPID_SHIFT; - int sgs = env->spr[SPR_BOOKE_MAS5] & MAS5_SGS; - int ind = (env->spr[SPR_BOOKE_MAS6] & MAS6_SIND) ? MAS1_IND : 0; - /* XXX check for unsupported isize and raise an invalid opcode then */ - int size = env->spr[SPR_BOOKE_MAS6] & MAS6_ISIZE_MASK; - /* XXX implement MAV2 handling */ - bool mav2 = false; - - /* XXX missing LPID handling */ - /* flush by pid and ea */ - for (i = 0; i < BOOKE206_MAX_TLBN; i++) { - int ways = booke206_tlb_ways(env, i); - - for (j = 0; j < ways; j++) { - tlb = booke206_get_tlbm(env, i, address, j); - if (!tlb) { - continue; - } - if ((ppcmas_tlb_check(env, tlb, NULL, address, pid) != 0) || - (tlb->mas1 & MAS1_IPROT) || - ((tlb->mas1 & MAS1_IND) != ind) || - ((tlb->mas8 & MAS8_TGS) != sgs)) { - continue; - } - if (mav2 && ((tlb->mas1 & MAS1_TSIZE_MASK) != size)) { - /* XXX only check when MMUCFG[TWC] || TLBnCFG[HES] */ - continue; - } - /* XXX e500mc doesn't match SAS, but other cores might */ - tlb->mas1 &= ~MAS1_VALID; - } - } - tlb_flush(env, 1); -} - -void helper_booke206_tlbflush(uint32_t type) -{ - int flags = 0; - - if (type & 2) { - flags |= BOOKE206_FLUSH_TLB1; - } - - if (type & 4) { - flags |= BOOKE206_FLUSH_TLB0; - } - - booke206_flush_tlb(env, flags, 1); -} - -/* Embedded.Processor Control */ -static int dbell2irq(target_ulong rb) -{ - int msg = rb & DBELL_TYPE_MASK; - int irq = -1; - - switch (msg) { - case DBELL_TYPE_DBELL: - irq = PPC_INTERRUPT_DOORBELL; - break; - case DBELL_TYPE_DBELL_CRIT: - irq = PPC_INTERRUPT_CDOORBELL; - break; - case DBELL_TYPE_G_DBELL: - case DBELL_TYPE_G_DBELL_CRIT: - case DBELL_TYPE_G_DBELL_MC: - /* XXX implement */ - default: - break; - } - - return irq; -} - -void helper_msgclr(target_ulong rb) -{ - int irq = dbell2irq(rb); - - if (irq < 0) { - return; - } - - env->pending_interrupts &= ~(1 << irq); -} - -void helper_msgsnd(target_ulong rb) -{ - int irq = dbell2irq(rb); - int pir = rb & DBELL_PIRTAG_MASK; - CPUPPCState *cenv; - - if (irq < 0) { - return; - } - - for (cenv = first_cpu; cenv != NULL; cenv = cenv->next_cpu) { - if ((rb & DBELL_BRDCAST) || (cenv->spr[SPR_BOOKE_PIR] == pir)) { - cenv->pending_interrupts |= 1 << irq; - cpu_interrupt(cenv, CPU_INTERRUPT_HARD); - } - } -} - -#endif /* !CONFIG_USER_ONLY */ diff --git a/target-ppc/timebase_helper.c b/target-ppc/timebase_helper.c new file mode 100644 index 0000000000..fad738a4af --- /dev/null +++ b/target-ppc/timebase_helper.c @@ -0,0 +1,159 @@ +/* + * PowerPC emulation helpers for QEMU. + * + * Copyright (c) 2003-2007 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, see <http://www.gnu.org/licenses/>. + */ +#include "cpu.h" +#include "helper.h" + +/*****************************************************************************/ +/* SPR accesses */ + +target_ulong helper_load_tbl(CPUPPCState *env) +{ + return (target_ulong)cpu_ppc_load_tbl(env); +} + +target_ulong helper_load_tbu(CPUPPCState *env) +{ + return cpu_ppc_load_tbu(env); +} + +target_ulong helper_load_atbl(CPUPPCState *env) +{ + return (target_ulong)cpu_ppc_load_atbl(env); +} + +target_ulong helper_load_atbu(CPUPPCState *env) +{ + return cpu_ppc_load_atbu(env); +} + +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) +target_ulong helper_load_purr(CPUPPCState *env) +{ + return (target_ulong)cpu_ppc_load_purr(env); +} +#endif + +target_ulong helper_load_601_rtcl(CPUPPCState *env) +{ + return cpu_ppc601_load_rtcl(env); +} + +target_ulong helper_load_601_rtcu(CPUPPCState *env) +{ + return cpu_ppc601_load_rtcu(env); +} + +#if !defined(CONFIG_USER_ONLY) +void helper_store_tbl(CPUPPCState *env, target_ulong val) +{ + cpu_ppc_store_tbl(env, val); +} + +void helper_store_tbu(CPUPPCState *env, target_ulong val) +{ + cpu_ppc_store_tbu(env, val); +} + +void helper_store_atbl(CPUPPCState *env, target_ulong val) +{ + cpu_ppc_store_atbl(env, val); +} + +void helper_store_atbu(CPUPPCState *env, target_ulong val) +{ + cpu_ppc_store_atbu(env, val); +} + +void helper_store_601_rtcl(CPUPPCState *env, target_ulong val) +{ + cpu_ppc601_store_rtcl(env, val); +} + +void helper_store_601_rtcu(CPUPPCState *env, target_ulong val) +{ + cpu_ppc601_store_rtcu(env, val); +} + +target_ulong helper_load_decr(CPUPPCState *env) +{ + return cpu_ppc_load_decr(env); +} + +void helper_store_decr(CPUPPCState *env, target_ulong val) +{ + cpu_ppc_store_decr(env, val); +} + +target_ulong helper_load_40x_pit(CPUPPCState *env) +{ + return load_40x_pit(env); +} + +void helper_store_40x_pit(CPUPPCState *env, target_ulong val) +{ + store_40x_pit(env, val); +} + +void helper_store_booke_tcr(CPUPPCState *env, target_ulong val) +{ + store_booke_tcr(env, val); +} + +void helper_store_booke_tsr(CPUPPCState *env, target_ulong val) +{ + store_booke_tsr(env, val); +} +#endif + +/*****************************************************************************/ +/* Embedded PowerPC specific helpers */ + +/* XXX: to be improved to check access rights when in user-mode */ +target_ulong helper_load_dcr(CPUPPCState *env, target_ulong dcrn) +{ + uint32_t val = 0; + + if (unlikely(env->dcr_env == NULL)) { + qemu_log("No DCR environment\n"); + helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL | + POWERPC_EXCP_INVAL_INVAL); + } else if (unlikely(ppc_dcr_read(env->dcr_env, + (uint32_t)dcrn, &val) != 0)) { + qemu_log("DCR read error %d %03x\n", (uint32_t)dcrn, (uint32_t)dcrn); + helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL | POWERPC_EXCP_PRIV_REG); + } + return val; +} + +void helper_store_dcr(CPUPPCState *env, target_ulong dcrn, target_ulong val) +{ + if (unlikely(env->dcr_env == NULL)) { + qemu_log("No DCR environment\n"); + helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL | + POWERPC_EXCP_INVAL_INVAL); + } else if (unlikely(ppc_dcr_write(env->dcr_env, (uint32_t)dcrn, + (uint32_t)val) != 0)) { + qemu_log("DCR write error %d %03x\n", (uint32_t)dcrn, (uint32_t)dcrn); + helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL | POWERPC_EXCP_PRIV_REG); + } +} diff --git a/target-ppc/translate.c b/target-ppc/translate.c index cf59765405..91eb7a062c 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -219,7 +219,7 @@ struct opc_handler_t { static inline void gen_reset_fpstatus(void) { - gen_helper_reset_fpstatus(); + gen_helper_reset_fpstatus(cpu_env); } static inline void gen_compute_fprf(TCGv_i64 arg, int set_fprf, int set_rc) @@ -229,15 +229,15 @@ static inline void gen_compute_fprf(TCGv_i64 arg, int set_fprf, int set_rc) if (set_fprf != 0) { /* This case might be optimized later */ tcg_gen_movi_i32(t0, 1); - gen_helper_compute_fprf(t0, arg, t0); + gen_helper_compute_fprf(t0, cpu_env, arg, t0); if (unlikely(set_rc)) { tcg_gen_mov_i32(cpu_crf[1], t0); } - gen_helper_float_check_status(); + gen_helper_float_check_status(cpu_env); } else if (unlikely(set_rc)) { /* We always need to compute fpcc */ tcg_gen_movi_i32(t0, 0); - gen_helper_compute_fprf(t0, arg, t0); + gen_helper_compute_fprf(t0, cpu_env, arg, t0); tcg_gen_mov_i32(cpu_crf[1], t0); } @@ -270,7 +270,7 @@ static inline void gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t } t0 = tcg_const_i32(excp); t1 = tcg_const_i32(error); - gen_helper_raise_exception_err(t0, t1); + gen_helper_raise_exception_err(cpu_env, t0, t1); tcg_temp_free_i32(t0); tcg_temp_free_i32(t1); ctx->exception = (excp); @@ -283,7 +283,7 @@ static inline void gen_exception(DisasContext *ctx, uint32_t excp) gen_update_nip(ctx, ctx->nip); } t0 = tcg_const_i32(excp); - gen_helper_raise_exception(t0); + gen_helper_raise_exception(cpu_env, t0); tcg_temp_free_i32(t0); ctx->exception = (excp); } @@ -297,7 +297,7 @@ static inline void gen_debug_exception(DisasContext *ctx) gen_update_nip(ctx, ctx->nip); } t0 = tcg_const_i32(EXCP_DEBUG); - gen_helper_raise_exception(t0); + gen_helper_raise_exception(cpu_env, t0); tcg_temp_free_i32(t0); } @@ -1181,8 +1181,16 @@ static void gen_mulld(DisasContext *ctx) if (unlikely(Rc(ctx->opcode) != 0)) gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); } + /* mulldo mulldo. */ -GEN_INT_ARITH_MUL_HELPER(mulldo, 0x17); +static void gen_mulldo(DisasContext *ctx) +{ + gen_helper_mulldo(cpu_gpr[rD(ctx->opcode)], cpu_env, + cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); + } +} #endif /* neg neg. nego nego. */ @@ -1869,7 +1877,7 @@ static void gen_slw(DisasContext *ctx) /* sraw & sraw. */ static void gen_sraw(DisasContext *ctx) { - gen_helper_sraw(cpu_gpr[rA(ctx->opcode)], + gen_helper_sraw(cpu_gpr[rA(ctx->opcode)], cpu_env, cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); if (unlikely(Rc(ctx->opcode) != 0)) gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); @@ -1953,7 +1961,7 @@ static void gen_sld(DisasContext *ctx) /* srad & srad. */ static void gen_srad(DisasContext *ctx) { - gen_helper_srad(cpu_gpr[rA(ctx->opcode)], + gen_helper_srad(cpu_gpr[rA(ctx->opcode)], cpu_env, cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); if (unlikely(Rc(ctx->opcode) != 0)) gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); @@ -2027,10 +2035,12 @@ static void gen_f##name(DisasContext *ctx) \ /* NIP cannot be restored if the memory exception comes from an helper */ \ gen_update_nip(ctx, ctx->nip - 4); \ gen_reset_fpstatus(); \ - gen_helper_f##op(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rA(ctx->opcode)], \ + gen_helper_f##op(cpu_fpr[rD(ctx->opcode)], cpu_env, \ + cpu_fpr[rA(ctx->opcode)], \ cpu_fpr[rC(ctx->opcode)], cpu_fpr[rB(ctx->opcode)]); \ if (isfloat) { \ - gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rD(ctx->opcode)]); \ + gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env, \ + cpu_fpr[rD(ctx->opcode)]); \ } \ gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], set_fprf, \ Rc(ctx->opcode) != 0); \ @@ -2050,10 +2060,12 @@ static void gen_f##name(DisasContext *ctx) \ /* NIP cannot be restored if the memory exception comes from an helper */ \ gen_update_nip(ctx, ctx->nip - 4); \ gen_reset_fpstatus(); \ - gen_helper_f##op(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rA(ctx->opcode)], \ + gen_helper_f##op(cpu_fpr[rD(ctx->opcode)], cpu_env, \ + cpu_fpr[rA(ctx->opcode)], \ cpu_fpr[rB(ctx->opcode)]); \ if (isfloat) { \ - gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rD(ctx->opcode)]); \ + gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env, \ + cpu_fpr[rD(ctx->opcode)]); \ } \ gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], \ set_fprf, Rc(ctx->opcode) != 0); \ @@ -2072,10 +2084,12 @@ static void gen_f##name(DisasContext *ctx) \ /* NIP cannot be restored if the memory exception comes from an helper */ \ gen_update_nip(ctx, ctx->nip - 4); \ gen_reset_fpstatus(); \ - gen_helper_f##op(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rA(ctx->opcode)], \ - cpu_fpr[rC(ctx->opcode)]); \ + gen_helper_f##op(cpu_fpr[rD(ctx->opcode)], cpu_env, \ + cpu_fpr[rA(ctx->opcode)], \ + cpu_fpr[rC(ctx->opcode)]); \ if (isfloat) { \ - gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rD(ctx->opcode)]); \ + gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env, \ + cpu_fpr[rD(ctx->opcode)]); \ } \ gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], \ set_fprf, Rc(ctx->opcode) != 0); \ @@ -2094,7 +2108,8 @@ static void gen_f##name(DisasContext *ctx) \ /* NIP cannot be restored if the memory exception comes from an helper */ \ gen_update_nip(ctx, ctx->nip - 4); \ gen_reset_fpstatus(); \ - gen_helper_f##name(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)]); \ + gen_helper_f##name(cpu_fpr[rD(ctx->opcode)], cpu_env, \ + cpu_fpr[rB(ctx->opcode)]); \ gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], \ set_fprf, Rc(ctx->opcode) != 0); \ } @@ -2109,7 +2124,8 @@ static void gen_f##name(DisasContext *ctx) \ /* NIP cannot be restored if the memory exception comes from an helper */ \ gen_update_nip(ctx, ctx->nip - 4); \ gen_reset_fpstatus(); \ - gen_helper_f##name(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)]); \ + gen_helper_f##name(cpu_fpr[rD(ctx->opcode)], cpu_env, \ + cpu_fpr[rB(ctx->opcode)]); \ gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], \ set_fprf, Rc(ctx->opcode) != 0); \ } @@ -2140,8 +2156,10 @@ static void gen_frsqrtes(DisasContext *ctx) /* NIP cannot be restored if the memory exception comes from an helper */ gen_update_nip(ctx, ctx->nip - 4); gen_reset_fpstatus(); - gen_helper_frsqrte(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)]); - gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rD(ctx->opcode)]); + gen_helper_frsqrte(cpu_fpr[rD(ctx->opcode)], cpu_env, + cpu_fpr[rB(ctx->opcode)]); + gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env, + cpu_fpr[rD(ctx->opcode)]); gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 1, Rc(ctx->opcode) != 0); } @@ -2161,7 +2179,8 @@ static void gen_fsqrt(DisasContext *ctx) /* NIP cannot be restored if the memory exception comes from an helper */ gen_update_nip(ctx, ctx->nip - 4); gen_reset_fpstatus(); - gen_helper_fsqrt(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)]); + gen_helper_fsqrt(cpu_fpr[rD(ctx->opcode)], cpu_env, + cpu_fpr[rB(ctx->opcode)]); gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 1, Rc(ctx->opcode) != 0); } @@ -2174,8 +2193,10 @@ static void gen_fsqrts(DisasContext *ctx) /* NIP cannot be restored if the memory exception comes from an helper */ gen_update_nip(ctx, ctx->nip - 4); gen_reset_fpstatus(); - gen_helper_fsqrt(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)]); - gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rD(ctx->opcode)]); + gen_helper_fsqrt(cpu_fpr[rD(ctx->opcode)], cpu_env, + cpu_fpr[rB(ctx->opcode)]); + gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env, + cpu_fpr[rD(ctx->opcode)]); gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 1, Rc(ctx->opcode) != 0); } @@ -2228,9 +2249,10 @@ static void gen_fcmpo(DisasContext *ctx) gen_update_nip(ctx, ctx->nip - 4); gen_reset_fpstatus(); crf = tcg_const_i32(crfD(ctx->opcode)); - gen_helper_fcmpo(cpu_fpr[rA(ctx->opcode)], cpu_fpr[rB(ctx->opcode)], crf); + gen_helper_fcmpo(cpu_env, cpu_fpr[rA(ctx->opcode)], + cpu_fpr[rB(ctx->opcode)], crf); tcg_temp_free_i32(crf); - gen_helper_float_check_status(); + gen_helper_float_check_status(cpu_env); } /* fcmpu */ @@ -2245,9 +2267,10 @@ static void gen_fcmpu(DisasContext *ctx) gen_update_nip(ctx, ctx->nip - 4); gen_reset_fpstatus(); crf = tcg_const_i32(crfD(ctx->opcode)); - gen_helper_fcmpu(cpu_fpr[rA(ctx->opcode)], cpu_fpr[rB(ctx->opcode)], crf); + gen_helper_fcmpu(cpu_env, cpu_fpr[rA(ctx->opcode)], + cpu_fpr[rB(ctx->opcode)], crf); tcg_temp_free_i32(crf); - gen_helper_float_check_status(); + gen_helper_float_check_status(cpu_env); } /*** Floating-point move ***/ @@ -2319,7 +2342,7 @@ static void gen_mtfsb0(DisasContext *ctx) /* NIP cannot be restored if the memory exception comes from an helper */ gen_update_nip(ctx, ctx->nip - 4); t0 = tcg_const_i32(crb); - gen_helper_fpscr_clrbit(t0); + gen_helper_fpscr_clrbit(cpu_env, t0); tcg_temp_free_i32(t0); } if (unlikely(Rc(ctx->opcode) != 0)) { @@ -2344,14 +2367,14 @@ static void gen_mtfsb1(DisasContext *ctx) /* NIP cannot be restored if the memory exception comes from an helper */ gen_update_nip(ctx, ctx->nip - 4); t0 = tcg_const_i32(crb); - gen_helper_fpscr_setbit(t0); + gen_helper_fpscr_setbit(cpu_env, t0); tcg_temp_free_i32(t0); } if (unlikely(Rc(ctx->opcode) != 0)) { tcg_gen_shri_i32(cpu_crf[1], cpu_fpscr, FPSCR_OX); } /* We can raise a differed exception */ - gen_helper_float_check_status(); + gen_helper_float_check_status(cpu_env); } /* mtfsf */ @@ -2371,13 +2394,13 @@ static void gen_mtfsf(DisasContext *ctx) t0 = tcg_const_i32(0xff); else t0 = tcg_const_i32(FM(ctx->opcode)); - gen_helper_store_fpscr(cpu_fpr[rB(ctx->opcode)], t0); + gen_helper_store_fpscr(cpu_env, cpu_fpr[rB(ctx->opcode)], t0); tcg_temp_free_i32(t0); if (unlikely(Rc(ctx->opcode) != 0)) { tcg_gen_shri_i32(cpu_crf[1], cpu_fpscr, FPSCR_OX); } /* We can raise a differed exception */ - gen_helper_float_check_status(); + gen_helper_float_check_status(cpu_env); } /* mtfsfi */ @@ -2398,14 +2421,14 @@ static void gen_mtfsfi(DisasContext *ctx) gen_reset_fpstatus(); t0 = tcg_const_i64(FPIMM(ctx->opcode) << (4 * sh)); t1 = tcg_const_i32(1 << sh); - gen_helper_store_fpscr(t0, t1); + gen_helper_store_fpscr(cpu_env, t0, t1); tcg_temp_free_i64(t0); tcg_temp_free_i32(t1); if (unlikely(Rc(ctx->opcode) != 0)) { tcg_gen_shri_i32(cpu_crf[1], cpu_fpscr, FPSCR_OX); } /* We can raise a differed exception */ - gen_helper_float_check_status(); + gen_helper_float_check_status(cpu_env); } /*** Addressing modes ***/ @@ -2495,7 +2518,7 @@ static inline void gen_check_align(DisasContext *ctx, TCGv EA, int mask) tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1); t1 = tcg_const_i32(POWERPC_EXCP_ALIGN); t2 = tcg_const_i32(0); - gen_helper_raise_exception_err(t1, t2); + gen_helper_raise_exception_err(cpu_env, t1, t2); tcg_temp_free_i32(t1); tcg_temp_free_i32(t2); gen_set_label(l1); @@ -2966,7 +2989,7 @@ static void gen_lmw(DisasContext *ctx) t0 = tcg_temp_new(); t1 = tcg_const_i32(rD(ctx->opcode)); gen_addr_imm_index(ctx, t0, 0); - gen_helper_lmw(t0, t1); + gen_helper_lmw(cpu_env, t0, t1); tcg_temp_free(t0); tcg_temp_free_i32(t1); } @@ -2982,7 +3005,7 @@ static void gen_stmw(DisasContext *ctx) t0 = tcg_temp_new(); t1 = tcg_const_i32(rS(ctx->opcode)); gen_addr_imm_index(ctx, t0, 0); - gen_helper_stmw(t0, t1); + gen_helper_stmw(cpu_env, t0, t1); tcg_temp_free(t0); tcg_temp_free_i32(t1); } @@ -3020,7 +3043,7 @@ static void gen_lswi(DisasContext *ctx) gen_addr_register(ctx, t0); t1 = tcg_const_i32(nb); t2 = tcg_const_i32(start); - gen_helper_lsw(t0, t1, t2); + gen_helper_lsw(cpu_env, t0, t1, t2); tcg_temp_free(t0); tcg_temp_free_i32(t1); tcg_temp_free_i32(t2); @@ -3039,7 +3062,7 @@ static void gen_lswx(DisasContext *ctx) t1 = tcg_const_i32(rD(ctx->opcode)); t2 = tcg_const_i32(rA(ctx->opcode)); t3 = tcg_const_i32(rB(ctx->opcode)); - gen_helper_lswx(t0, t1, t2, t3); + gen_helper_lswx(cpu_env, t0, t1, t2, t3); tcg_temp_free(t0); tcg_temp_free_i32(t1); tcg_temp_free_i32(t2); @@ -3061,7 +3084,7 @@ static void gen_stswi(DisasContext *ctx) nb = 32; t1 = tcg_const_i32(nb); t2 = tcg_const_i32(rS(ctx->opcode)); - gen_helper_stsw(t0, t1, t2); + gen_helper_stsw(cpu_env, t0, t1, t2); tcg_temp_free(t0); tcg_temp_free_i32(t1); tcg_temp_free_i32(t2); @@ -3081,7 +3104,7 @@ static void gen_stswx(DisasContext *ctx) tcg_gen_trunc_tl_i32(t1, cpu_xer); tcg_gen_andi_i32(t1, t1, 0x7F); t2 = tcg_const_i32(rS(ctx->opcode)); - gen_helper_stsw(t0, t1, t2); + gen_helper_stsw(cpu_env, t0, t1, t2); tcg_temp_free(t0); tcg_temp_free_i32(t1); tcg_temp_free_i32(t2); @@ -3303,7 +3326,7 @@ static inline void gen_qemu_ld32fs(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2) gen_qemu_ld32u(ctx, t0, arg2); tcg_gen_trunc_tl_i32(t1, t0); tcg_temp_free(t0); - gen_helper_float32_to_float64(arg1, t1); + gen_helper_float32_to_float64(arg1, cpu_env, t1); tcg_temp_free_i32(t1); } @@ -3393,7 +3416,7 @@ static inline void gen_qemu_st32fs(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2) { TCGv_i32 t0 = tcg_temp_new_i32(); TCGv t1 = tcg_temp_new(); - gen_helper_float64_to_float32(t0, arg1); + gen_helper_float64_to_float32(t0, cpu_env, arg1); tcg_gen_extu_i32_tl(t1, t0); tcg_temp_free_i32(t0); gen_qemu_st32(ctx, t1, arg2); @@ -3662,7 +3685,7 @@ static void gen_rfi(DisasContext *ctx) return; } gen_update_cfar(ctx, ctx->nip); - gen_helper_rfi(); + gen_helper_rfi(cpu_env); gen_sync_exception(ctx); #endif } @@ -3679,7 +3702,7 @@ static void gen_rfid(DisasContext *ctx) return; } gen_update_cfar(ctx, ctx->nip); - gen_helper_rfid(); + gen_helper_rfid(cpu_env); gen_sync_exception(ctx); #endif } @@ -3694,7 +3717,7 @@ static void gen_hrfid(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } - gen_helper_hrfid(); + gen_helper_hrfid(cpu_env); gen_sync_exception(ctx); #endif } @@ -3722,7 +3745,8 @@ static void gen_tw(DisasContext *ctx) TCGv_i32 t0 = tcg_const_i32(TO(ctx->opcode)); /* Update the nip since this might generate a trap exception */ gen_update_nip(ctx, ctx->nip); - gen_helper_tw(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], t0); + gen_helper_tw(cpu_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], + t0); tcg_temp_free_i32(t0); } @@ -3733,7 +3757,7 @@ static void gen_twi(DisasContext *ctx) TCGv_i32 t1 = tcg_const_i32(TO(ctx->opcode)); /* Update the nip since this might generate a trap exception */ gen_update_nip(ctx, ctx->nip); - gen_helper_tw(cpu_gpr[rA(ctx->opcode)], t0, t1); + gen_helper_tw(cpu_env, cpu_gpr[rA(ctx->opcode)], t0, t1); tcg_temp_free(t0); tcg_temp_free_i32(t1); } @@ -3745,7 +3769,8 @@ static void gen_td(DisasContext *ctx) TCGv_i32 t0 = tcg_const_i32(TO(ctx->opcode)); /* Update the nip since this might generate a trap exception */ gen_update_nip(ctx, ctx->nip); - gen_helper_td(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], t0); + gen_helper_td(cpu_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], + t0); tcg_temp_free_i32(t0); } @@ -3756,7 +3781,7 @@ static void gen_tdi(DisasContext *ctx) TCGv_i32 t1 = tcg_const_i32(TO(ctx->opcode)); /* Update the nip since this might generate a trap exception */ gen_update_nip(ctx, ctx->nip); - gen_helper_td(cpu_gpr[rA(ctx->opcode)], t0, t1); + gen_helper_td(cpu_env, cpu_gpr[rA(ctx->opcode)], t0, t1); tcg_temp_free(t0); tcg_temp_free_i32(t1); } @@ -3934,7 +3959,7 @@ static void gen_mtmsrd(DisasContext *ctx) * directly from ppc_store_msr */ gen_update_nip(ctx, ctx->nip); - gen_helper_store_msr(cpu_gpr[rS(ctx->opcode)]); + gen_helper_store_msr(cpu_env, cpu_gpr[rS(ctx->opcode)]); /* Must stop the translation as machine state (may have) changed */ /* Note that mtmsr is not always defined as context-synchronizing */ gen_stop_exception(ctx); @@ -3972,7 +3997,7 @@ static void gen_mtmsr(DisasContext *ctx) #else tcg_gen_mov_tl(msr, cpu_gpr[rS(ctx->opcode)]); #endif - gen_helper_store_msr(msr); + gen_helper_store_msr(cpu_env, msr); /* Must stop the translation as machine state (may have) changed */ /* Note that mtmsr is not always defined as context-synchronizing */ gen_stop_exception(ctx); @@ -4091,7 +4116,7 @@ static void gen_dcbz(DisasContext *ctx) gen_update_nip(ctx, ctx->nip - 4); t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); - gen_helper_dcbz(t0); + gen_helper_dcbz(cpu_env, t0); tcg_temp_free(t0); } @@ -4104,9 +4129,9 @@ static void gen_dcbz_970(DisasContext *ctx) t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); if (ctx->opcode & 0x00200000) - gen_helper_dcbz(t0); + gen_helper_dcbz(cpu_env, t0); else - gen_helper_dcbz_970(t0); + gen_helper_dcbz_970(cpu_env, t0); tcg_temp_free(t0); } @@ -4146,7 +4171,7 @@ static void gen_icbi(DisasContext *ctx) gen_update_nip(ctx, ctx->nip - 4); t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); - gen_helper_icbi(t0); + gen_helper_icbi(cpu_env, t0); tcg_temp_free(t0); } @@ -4175,7 +4200,7 @@ static void gen_mfsr(DisasContext *ctx) return; } t0 = tcg_const_tl(SR(ctx->opcode)); - gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], t0); + gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); tcg_temp_free(t0); #endif } @@ -4194,7 +4219,7 @@ static void gen_mfsrin(DisasContext *ctx) t0 = tcg_temp_new(); tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 28); tcg_gen_andi_tl(t0, t0, 0xF); - gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], t0); + gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); tcg_temp_free(t0); #endif } @@ -4211,7 +4236,7 @@ static void gen_mtsr(DisasContext *ctx) return; } t0 = tcg_const_tl(SR(ctx->opcode)); - gen_helper_store_sr(t0, cpu_gpr[rS(ctx->opcode)]); + gen_helper_store_sr(cpu_env, t0, cpu_gpr[rS(ctx->opcode)]); tcg_temp_free(t0); #endif } @@ -4230,7 +4255,7 @@ static void gen_mtsrin(DisasContext *ctx) t0 = tcg_temp_new(); tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 28); tcg_gen_andi_tl(t0, t0, 0xF); - gen_helper_store_sr(t0, cpu_gpr[rD(ctx->opcode)]); + gen_helper_store_sr(cpu_env, t0, cpu_gpr[rD(ctx->opcode)]); tcg_temp_free(t0); #endif } @@ -4250,7 +4275,7 @@ static void gen_mfsr_64b(DisasContext *ctx) return; } t0 = tcg_const_tl(SR(ctx->opcode)); - gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], t0); + gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); tcg_temp_free(t0); #endif } @@ -4269,7 +4294,7 @@ static void gen_mfsrin_64b(DisasContext *ctx) t0 = tcg_temp_new(); tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 28); tcg_gen_andi_tl(t0, t0, 0xF); - gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], t0); + gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); tcg_temp_free(t0); #endif } @@ -4286,7 +4311,7 @@ static void gen_mtsr_64b(DisasContext *ctx) return; } t0 = tcg_const_tl(SR(ctx->opcode)); - gen_helper_store_sr(t0, cpu_gpr[rS(ctx->opcode)]); + gen_helper_store_sr(cpu_env, t0, cpu_gpr[rS(ctx->opcode)]); tcg_temp_free(t0); #endif } @@ -4305,7 +4330,7 @@ static void gen_mtsrin_64b(DisasContext *ctx) t0 = tcg_temp_new(); tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 28); tcg_gen_andi_tl(t0, t0, 0xF); - gen_helper_store_sr(t0, cpu_gpr[rS(ctx->opcode)]); + gen_helper_store_sr(cpu_env, t0, cpu_gpr[rS(ctx->opcode)]); tcg_temp_free(t0); #endif } @@ -4320,7 +4345,8 @@ static void gen_slbmte(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); return; } - gen_helper_store_slb(cpu_gpr[rB(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); + gen_helper_store_slb(cpu_env, cpu_gpr[rB(ctx->opcode)], + cpu_gpr[rS(ctx->opcode)]); #endif } @@ -4333,7 +4359,7 @@ static void gen_slbmfee(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); return; } - gen_helper_load_slb_esid(cpu_gpr[rS(ctx->opcode)], + gen_helper_load_slb_esid(cpu_gpr[rS(ctx->opcode)], cpu_env, cpu_gpr[rB(ctx->opcode)]); #endif } @@ -4347,7 +4373,7 @@ static void gen_slbmfev(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); return; } - gen_helper_load_slb_vsid(cpu_gpr[rS(ctx->opcode)], + gen_helper_load_slb_vsid(cpu_gpr[rS(ctx->opcode)], cpu_env, cpu_gpr[rB(ctx->opcode)]); #endif } @@ -4366,7 +4392,7 @@ static void gen_tlbia(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } - gen_helper_tlbia(); + gen_helper_tlbia(cpu_env); #endif } @@ -4380,7 +4406,7 @@ static void gen_tlbiel(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } - gen_helper_tlbie(cpu_gpr[rB(ctx->opcode)]); + gen_helper_tlbie(cpu_env, cpu_gpr[rB(ctx->opcode)]); #endif } @@ -4398,11 +4424,11 @@ static void gen_tlbie(DisasContext *ctx) if (!ctx->sf_mode) { TCGv t0 = tcg_temp_new(); tcg_gen_ext32u_tl(t0, cpu_gpr[rB(ctx->opcode)]); - gen_helper_tlbie(t0); + gen_helper_tlbie(cpu_env, t0); tcg_temp_free(t0); } else #endif - gen_helper_tlbie(cpu_gpr[rB(ctx->opcode)]); + gen_helper_tlbie(cpu_env, cpu_gpr[rB(ctx->opcode)]); #endif } @@ -4434,7 +4460,7 @@ static void gen_slbia(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } - gen_helper_slbia(); + gen_helper_slbia(cpu_env); #endif } @@ -4448,7 +4474,7 @@ static void gen_slbie(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } - gen_helper_slbie(cpu_gpr[rB(ctx->opcode)]); + gen_helper_slbie(cpu_env, cpu_gpr[rB(ctx->opcode)]); #endif } #endif @@ -4525,7 +4551,7 @@ static void gen_abso(DisasContext *ctx) static void gen_clcs(DisasContext *ctx) { TCGv_i32 t0 = tcg_const_i32(rA(ctx->opcode)); - gen_helper_clcs(cpu_gpr[rD(ctx->opcode)], t0); + gen_helper_clcs(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); tcg_temp_free_i32(t0); /* Rc=1 sets CR0 to an undefined state */ } @@ -4533,7 +4559,8 @@ static void gen_clcs(DisasContext *ctx) /* div - div. */ static void gen_div(DisasContext *ctx) { - gen_helper_div(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); + gen_helper_div(cpu_gpr[rD(ctx->opcode)], cpu_env, cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rB(ctx->opcode)]); if (unlikely(Rc(ctx->opcode) != 0)) gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); } @@ -4541,7 +4568,8 @@ static void gen_div(DisasContext *ctx) /* divo - divo. */ static void gen_divo(DisasContext *ctx) { - gen_helper_divo(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); + gen_helper_divo(cpu_gpr[rD(ctx->opcode)], cpu_env, cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rB(ctx->opcode)]); if (unlikely(Rc(ctx->opcode) != 0)) gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); } @@ -4549,7 +4577,8 @@ static void gen_divo(DisasContext *ctx) /* divs - divs. */ static void gen_divs(DisasContext *ctx) { - gen_helper_divs(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); + gen_helper_divs(cpu_gpr[rD(ctx->opcode)], cpu_env, cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rB(ctx->opcode)]); if (unlikely(Rc(ctx->opcode) != 0)) gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); } @@ -4557,7 +4586,8 @@ static void gen_divs(DisasContext *ctx) /* divso - divso. */ static void gen_divso(DisasContext *ctx) { - gen_helper_divso(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); + gen_helper_divso(cpu_gpr[rD(ctx->opcode)], cpu_env, + cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); if (unlikely(Rc(ctx->opcode) != 0)) gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); } @@ -4633,7 +4663,7 @@ static void gen_lscbx(DisasContext *ctx) gen_addr_reg_index(ctx, t0); /* NIP cannot be restored if the memory exception comes from an helper */ gen_update_nip(ctx, ctx->nip - 4); - gen_helper_lscbx(t0, t0, t1, t2, t3); + gen_helper_lscbx(t0, cpu_env, t0, t1, t2, t3); tcg_temp_free_i32(t1); tcg_temp_free_i32(t2); tcg_temp_free_i32(t3); @@ -5165,7 +5195,7 @@ static void gen_tlbld_6xx(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } - gen_helper_6xx_tlbd(cpu_gpr[rB(ctx->opcode)]); + gen_helper_6xx_tlbd(cpu_env, cpu_gpr[rB(ctx->opcode)]); #endif } @@ -5179,7 +5209,7 @@ static void gen_tlbli_6xx(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } - gen_helper_6xx_tlbi(cpu_gpr[rB(ctx->opcode)]); + gen_helper_6xx_tlbi(cpu_env, cpu_gpr[rB(ctx->opcode)]); #endif } @@ -5195,7 +5225,7 @@ static void gen_tlbld_74xx(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } - gen_helper_74xx_tlbd(cpu_gpr[rB(ctx->opcode)]); + gen_helper_74xx_tlbd(cpu_env, cpu_gpr[rB(ctx->opcode)]); #endif } @@ -5209,7 +5239,7 @@ static void gen_tlbli_74xx(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } - gen_helper_74xx_tlbi(cpu_gpr[rB(ctx->opcode)]); + gen_helper_74xx_tlbi(cpu_env, cpu_gpr[rB(ctx->opcode)]); #endif } @@ -5257,7 +5287,7 @@ static void gen_mfsri(DisasContext *ctx) gen_addr_reg_index(ctx, t0); tcg_gen_shri_tl(t0, t0, 28); tcg_gen_andi_tl(t0, t0, 0xF); - gen_helper_load_sr(cpu_gpr[rd], t0); + gen_helper_load_sr(cpu_gpr[rd], cpu_env, t0); tcg_temp_free(t0); if (ra != 0 && ra != rd) tcg_gen_mov_tl(cpu_gpr[ra], cpu_gpr[rd]); @@ -5276,7 +5306,7 @@ static void gen_rac(DisasContext *ctx) } t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); - gen_helper_rac(cpu_gpr[rD(ctx->opcode)], t0); + gen_helper_rac(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); tcg_temp_free(t0); #endif } @@ -5290,7 +5320,7 @@ static void gen_rfsvc(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } - gen_helper_rfsvc(); + gen_helper_rfsvc(cpu_env); gen_sync_exception(ctx); #endif } @@ -5454,7 +5484,7 @@ static void gen_tlbiva(DisasContext *ctx) } t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); - gen_helper_tlbie(cpu_gpr[rB(ctx->opcode)]); + gen_helper_tlbie(cpu_env, cpu_gpr[rB(ctx->opcode)]); tcg_temp_free(t0); #endif } @@ -5687,7 +5717,7 @@ static void gen_mfdcr(DisasContext *ctx) /* NIP cannot be restored if the memory exception comes from an helper */ gen_update_nip(ctx, ctx->nip - 4); dcrn = tcg_const_tl(SPR(ctx->opcode)); - gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], dcrn); + gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], cpu_env, dcrn); tcg_temp_free(dcrn); #endif } @@ -5706,7 +5736,7 @@ static void gen_mtdcr(DisasContext *ctx) /* NIP cannot be restored if the memory exception comes from an helper */ gen_update_nip(ctx, ctx->nip - 4); dcrn = tcg_const_tl(SPR(ctx->opcode)); - gen_helper_store_dcr(dcrn, cpu_gpr[rS(ctx->opcode)]); + gen_helper_store_dcr(cpu_env, dcrn, cpu_gpr[rS(ctx->opcode)]); tcg_temp_free(dcrn); #endif } @@ -5724,7 +5754,8 @@ static void gen_mfdcrx(DisasContext *ctx) } /* NIP cannot be restored if the memory exception comes from an helper */ gen_update_nip(ctx, ctx->nip - 4); - gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], cpu_env, + cpu_gpr[rA(ctx->opcode)]); /* Note: Rc update flag set leads to undefined state of Rc0 */ #endif } @@ -5742,7 +5773,8 @@ static void gen_mtdcrx(DisasContext *ctx) } /* NIP cannot be restored if the memory exception comes from an helper */ gen_update_nip(ctx, ctx->nip - 4); - gen_helper_store_dcr(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); + gen_helper_store_dcr(cpu_env, cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rS(ctx->opcode)]); /* Note: Rc update flag set leads to undefined state of Rc0 */ #endif } @@ -5752,7 +5784,8 @@ static void gen_mfdcrux(DisasContext *ctx) { /* NIP cannot be restored if the memory exception comes from an helper */ gen_update_nip(ctx, ctx->nip - 4); - gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], cpu_env, + cpu_gpr[rA(ctx->opcode)]); /* Note: Rc update flag set leads to undefined state of Rc0 */ } @@ -5761,7 +5794,8 @@ static void gen_mtdcrux(DisasContext *ctx) { /* NIP cannot be restored if the memory exception comes from an helper */ gen_update_nip(ctx, ctx->nip - 4); - gen_helper_store_dcr(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); + gen_helper_store_dcr(cpu_env, cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rS(ctx->opcode)]); /* Note: Rc update flag set leads to undefined state of Rc0 */ } @@ -5849,7 +5883,7 @@ static void gen_rfci_40x(DisasContext *ctx) return; } /* Restore CPU state */ - gen_helper_40x_rfci(); + gen_helper_40x_rfci(cpu_env); gen_sync_exception(ctx); #endif } @@ -5864,7 +5898,7 @@ static void gen_rfci(DisasContext *ctx) return; } /* Restore CPU state */ - gen_helper_rfci(); + gen_helper_rfci(cpu_env); gen_sync_exception(ctx); #endif } @@ -5882,7 +5916,7 @@ static void gen_rfdi(DisasContext *ctx) return; } /* Restore CPU state */ - gen_helper_rfdi(); + gen_helper_rfdi(cpu_env); gen_sync_exception(ctx); #endif } @@ -5898,7 +5932,7 @@ static void gen_rfmci(DisasContext *ctx) return; } /* Restore CPU state */ - gen_helper_rfmci(); + gen_helper_rfmci(cpu_env); gen_sync_exception(ctx); #endif } @@ -5917,10 +5951,12 @@ static void gen_tlbre_40x(DisasContext *ctx) } switch (rB(ctx->opcode)) { case 0: - gen_helper_4xx_tlbre_hi(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + gen_helper_4xx_tlbre_hi(cpu_gpr[rD(ctx->opcode)], cpu_env, + cpu_gpr[rA(ctx->opcode)]); break; case 1: - gen_helper_4xx_tlbre_lo(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + gen_helper_4xx_tlbre_lo(cpu_gpr[rD(ctx->opcode)], cpu_env, + cpu_gpr[rA(ctx->opcode)]); break; default: gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); @@ -5942,7 +5978,7 @@ static void gen_tlbsx_40x(DisasContext *ctx) } t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); - gen_helper_4xx_tlbsx(cpu_gpr[rD(ctx->opcode)], t0); + gen_helper_4xx_tlbsx(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); tcg_temp_free(t0); if (Rc(ctx->opcode)) { int l1 = gen_new_label(); @@ -5968,10 +6004,12 @@ static void gen_tlbwe_40x(DisasContext *ctx) } switch (rB(ctx->opcode)) { case 0: - gen_helper_4xx_tlbwe_hi(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); + gen_helper_4xx_tlbwe_hi(cpu_env, cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rS(ctx->opcode)]); break; case 1: - gen_helper_4xx_tlbwe_lo(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); + gen_helper_4xx_tlbwe_lo(cpu_env, cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rS(ctx->opcode)]); break; default: gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); @@ -5998,7 +6036,8 @@ static void gen_tlbre_440(DisasContext *ctx) case 2: { TCGv_i32 t0 = tcg_const_i32(rB(ctx->opcode)); - gen_helper_440_tlbre(cpu_gpr[rD(ctx->opcode)], t0, cpu_gpr[rA(ctx->opcode)]); + gen_helper_440_tlbre(cpu_gpr[rD(ctx->opcode)], cpu_env, + t0, cpu_gpr[rA(ctx->opcode)]); tcg_temp_free_i32(t0); } break; @@ -6022,7 +6061,7 @@ static void gen_tlbsx_440(DisasContext *ctx) } t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); - gen_helper_440_tlbsx(cpu_gpr[rD(ctx->opcode)], t0); + gen_helper_440_tlbsx(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); tcg_temp_free(t0); if (Rc(ctx->opcode)) { int l1 = gen_new_label(); @@ -6052,7 +6091,8 @@ static void gen_tlbwe_440(DisasContext *ctx) case 2: { TCGv_i32 t0 = tcg_const_i32(rB(ctx->opcode)); - gen_helper_440_tlbwe(t0, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); + gen_helper_440_tlbwe(cpu_env, t0, cpu_gpr[rA(ctx->opcode)], + cpu_gpr[rS(ctx->opcode)]); tcg_temp_free_i32(t0); } break; @@ -6076,7 +6116,7 @@ static void gen_tlbre_booke206(DisasContext *ctx) return; } - gen_helper_booke206_tlbre(); + gen_helper_booke206_tlbre(cpu_env); #endif } @@ -6100,7 +6140,7 @@ static void gen_tlbsx_booke206(DisasContext *ctx) } tcg_gen_add_tl(t0, t0, cpu_gpr[rB(ctx->opcode)]); - gen_helper_booke206_tlbsx(t0); + gen_helper_booke206_tlbsx(cpu_env, t0); #endif } @@ -6115,7 +6155,7 @@ static void gen_tlbwe_booke206(DisasContext *ctx) return; } gen_update_nip(ctx, ctx->nip - 4); - gen_helper_booke206_tlbwe(); + gen_helper_booke206_tlbwe(cpu_env); #endif } @@ -6133,7 +6173,7 @@ static void gen_tlbivax_booke206(DisasContext *ctx) t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); - gen_helper_booke206_tlbivax(t0); + gen_helper_booke206_tlbivax(cpu_env, t0); #endif } @@ -6153,13 +6193,13 @@ static void gen_tlbilx_booke206(DisasContext *ctx) switch((ctx->opcode >> 21) & 0x3) { case 0: - gen_helper_booke206_tlbilx0(t0); + gen_helper_booke206_tlbilx0(cpu_env, t0); break; case 1: - gen_helper_booke206_tlbilx1(t0); + gen_helper_booke206_tlbilx1(cpu_env, t0); break; case 3: - gen_helper_booke206_tlbilx3(t0); + gen_helper_booke206_tlbilx3(cpu_env, t0); break; default: gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); @@ -6220,8 +6260,8 @@ static void gen_wrteei(DisasContext *ctx) static void gen_dlmzb(DisasContext *ctx) { TCGv_i32 t0 = tcg_const_i32(Rc(ctx->opcode)); - gen_helper_dlmzb(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], - cpu_gpr[rB(ctx->opcode)], t0); + gen_helper_dlmzb(cpu_gpr[rA(ctx->opcode)], cpu_env, + cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], t0); tcg_temp_free_i32(t0); } @@ -6258,7 +6298,7 @@ static void gen_msgclr(DisasContext *ctx) return; } - gen_helper_msgclr(cpu_gpr[rB(ctx->opcode)]); + gen_helper_msgclr(cpu_env, cpu_gpr[rB(ctx->opcode)]); #endif } @@ -6347,7 +6387,7 @@ static void gen_lve##name(DisasContext *ctx) \ EA = tcg_temp_new(); \ gen_addr_reg_index(ctx, EA); \ rs = gen_avr_ptr(rS(ctx->opcode)); \ - gen_helper_lve##name (rs, EA); \ + gen_helper_lve##name(cpu_env, rs, EA); \ tcg_temp_free(EA); \ tcg_temp_free_ptr(rs); \ } @@ -6365,7 +6405,7 @@ static void gen_stve##name(DisasContext *ctx) \ EA = tcg_temp_new(); \ gen_addr_reg_index(ctx, EA); \ rs = gen_avr_ptr(rS(ctx->opcode)); \ - gen_helper_stve##name (rs, EA); \ + gen_helper_stve##name(cpu_env, rs, EA); \ tcg_temp_free(EA); \ tcg_temp_free_ptr(rs); \ } @@ -6440,7 +6480,7 @@ static void gen_mtvscr(DisasContext *ctx) return; } p = gen_avr_ptr(rD(ctx->opcode)); - gen_helper_mtvscr(p); + gen_helper_mtvscr(cpu_env, p); tcg_temp_free_ptr(p); } @@ -6479,6 +6519,23 @@ static void glue(gen_, name)(DisasContext *ctx) tcg_temp_free_ptr(rd); \ } +#define GEN_VXFORM_ENV(name, opc2, opc3) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + TCGv_ptr ra, rb, rd; \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + ra = gen_avr_ptr(rA(ctx->opcode)); \ + rb = gen_avr_ptr(rB(ctx->opcode)); \ + rd = gen_avr_ptr(rD(ctx->opcode)); \ + gen_helper_##name(rd, cpu_env, ra, rb); \ + tcg_temp_free_ptr(ra); \ + tcg_temp_free_ptr(rb); \ + tcg_temp_free_ptr(rd); \ +} + GEN_VXFORM(vaddubm, 0, 0); GEN_VXFORM(vadduhm, 0, 1); GEN_VXFORM(vadduwm, 0, 2); @@ -6530,41 +6587,41 @@ GEN_VXFORM(vslo, 6, 16); GEN_VXFORM(vsro, 6, 17); GEN_VXFORM(vaddcuw, 0, 6); GEN_VXFORM(vsubcuw, 0, 22); -GEN_VXFORM(vaddubs, 0, 8); -GEN_VXFORM(vadduhs, 0, 9); -GEN_VXFORM(vadduws, 0, 10); -GEN_VXFORM(vaddsbs, 0, 12); -GEN_VXFORM(vaddshs, 0, 13); -GEN_VXFORM(vaddsws, 0, 14); -GEN_VXFORM(vsububs, 0, 24); -GEN_VXFORM(vsubuhs, 0, 25); -GEN_VXFORM(vsubuws, 0, 26); -GEN_VXFORM(vsubsbs, 0, 28); -GEN_VXFORM(vsubshs, 0, 29); -GEN_VXFORM(vsubsws, 0, 30); +GEN_VXFORM_ENV(vaddubs, 0, 8); +GEN_VXFORM_ENV(vadduhs, 0, 9); +GEN_VXFORM_ENV(vadduws, 0, 10); +GEN_VXFORM_ENV(vaddsbs, 0, 12); +GEN_VXFORM_ENV(vaddshs, 0, 13); +GEN_VXFORM_ENV(vaddsws, 0, 14); +GEN_VXFORM_ENV(vsububs, 0, 24); +GEN_VXFORM_ENV(vsubuhs, 0, 25); +GEN_VXFORM_ENV(vsubuws, 0, 26); +GEN_VXFORM_ENV(vsubsbs, 0, 28); +GEN_VXFORM_ENV(vsubshs, 0, 29); +GEN_VXFORM_ENV(vsubsws, 0, 30); GEN_VXFORM(vrlb, 2, 0); GEN_VXFORM(vrlh, 2, 1); GEN_VXFORM(vrlw, 2, 2); GEN_VXFORM(vsl, 2, 7); GEN_VXFORM(vsr, 2, 11); -GEN_VXFORM(vpkuhum, 7, 0); -GEN_VXFORM(vpkuwum, 7, 1); -GEN_VXFORM(vpkuhus, 7, 2); -GEN_VXFORM(vpkuwus, 7, 3); -GEN_VXFORM(vpkshus, 7, 4); -GEN_VXFORM(vpkswus, 7, 5); -GEN_VXFORM(vpkshss, 7, 6); -GEN_VXFORM(vpkswss, 7, 7); +GEN_VXFORM_ENV(vpkuhum, 7, 0); +GEN_VXFORM_ENV(vpkuwum, 7, 1); +GEN_VXFORM_ENV(vpkuhus, 7, 2); +GEN_VXFORM_ENV(vpkuwus, 7, 3); +GEN_VXFORM_ENV(vpkshus, 7, 4); +GEN_VXFORM_ENV(vpkswus, 7, 5); +GEN_VXFORM_ENV(vpkshss, 7, 6); +GEN_VXFORM_ENV(vpkswss, 7, 7); GEN_VXFORM(vpkpx, 7, 12); -GEN_VXFORM(vsum4ubs, 4, 24); -GEN_VXFORM(vsum4sbs, 4, 28); -GEN_VXFORM(vsum4shs, 4, 25); -GEN_VXFORM(vsum2sws, 4, 26); -GEN_VXFORM(vsumsws, 4, 30); -GEN_VXFORM(vaddfp, 5, 0); -GEN_VXFORM(vsubfp, 5, 1); -GEN_VXFORM(vmaxfp, 5, 16); -GEN_VXFORM(vminfp, 5, 17); +GEN_VXFORM_ENV(vsum4ubs, 4, 24); +GEN_VXFORM_ENV(vsum4sbs, 4, 28); +GEN_VXFORM_ENV(vsum4shs, 4, 25); +GEN_VXFORM_ENV(vsum2sws, 4, 26); +GEN_VXFORM_ENV(vsumsws, 4, 30); +GEN_VXFORM_ENV(vaddfp, 5, 0); +GEN_VXFORM_ENV(vsubfp, 5, 1); +GEN_VXFORM_ENV(vmaxfp, 5, 16); +GEN_VXFORM_ENV(vminfp, 5, 17); #define GEN_VXRFORM1(opname, name, str, opc2, opc3) \ static void glue(gen_, name)(DisasContext *ctx) \ @@ -6577,7 +6634,7 @@ static void glue(gen_, name)(DisasContext *ctx) \ ra = gen_avr_ptr(rA(ctx->opcode)); \ rb = gen_avr_ptr(rB(ctx->opcode)); \ rd = gen_avr_ptr(rD(ctx->opcode)); \ - gen_helper_##opname (rd, ra, rb); \ + gen_helper_##opname(cpu_env, rd, ra, rb); \ tcg_temp_free_ptr(ra); \ tcg_temp_free_ptr(rb); \ tcg_temp_free_ptr(rd); \ @@ -6636,20 +6693,36 @@ static void glue(gen_, name)(DisasContext *ctx) tcg_temp_free_ptr(rd); \ } +#define GEN_VXFORM_NOA_ENV(name, opc2, opc3) \ +static void glue(gen_, name)(DisasContext *ctx) \ + { \ + TCGv_ptr rb, rd; \ + \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + rb = gen_avr_ptr(rB(ctx->opcode)); \ + rd = gen_avr_ptr(rD(ctx->opcode)); \ + gen_helper_##name(cpu_env, rd, rb); \ + tcg_temp_free_ptr(rb); \ + tcg_temp_free_ptr(rd); \ + } + GEN_VXFORM_NOA(vupkhsb, 7, 8); GEN_VXFORM_NOA(vupkhsh, 7, 9); GEN_VXFORM_NOA(vupklsb, 7, 10); GEN_VXFORM_NOA(vupklsh, 7, 11); GEN_VXFORM_NOA(vupkhpx, 7, 13); GEN_VXFORM_NOA(vupklpx, 7, 15); -GEN_VXFORM_NOA(vrefp, 5, 4); -GEN_VXFORM_NOA(vrsqrtefp, 5, 5); -GEN_VXFORM_NOA(vexptefp, 5, 6); -GEN_VXFORM_NOA(vlogefp, 5, 7); -GEN_VXFORM_NOA(vrfim, 5, 8); -GEN_VXFORM_NOA(vrfin, 5, 9); -GEN_VXFORM_NOA(vrfip, 5, 10); -GEN_VXFORM_NOA(vrfiz, 5, 11); +GEN_VXFORM_NOA_ENV(vrefp, 5, 4); +GEN_VXFORM_NOA_ENV(vrsqrtefp, 5, 5); +GEN_VXFORM_NOA_ENV(vexptefp, 5, 6); +GEN_VXFORM_NOA_ENV(vlogefp, 5, 7); +GEN_VXFORM_NOA_ENV(vrfim, 5, 8); +GEN_VXFORM_NOA_ENV(vrfin, 5, 9); +GEN_VXFORM_NOA_ENV(vrfip, 5, 10); +GEN_VXFORM_NOA_ENV(vrfiz, 5, 11); #define GEN_VXFORM_SIMM(name, opc2, opc3) \ static void glue(gen_, name)(DisasContext *ctx) \ @@ -6685,13 +6758,32 @@ static void glue(gen_, name)(DisasContext *ctx) tcg_temp_free_ptr(rd); \ } +#define GEN_VXFORM_UIMM_ENV(name, opc2, opc3) \ +static void glue(gen_, name)(DisasContext *ctx) \ + { \ + TCGv_ptr rb, rd; \ + TCGv_i32 uimm; \ + \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + uimm = tcg_const_i32(UIMM5(ctx->opcode)); \ + rb = gen_avr_ptr(rB(ctx->opcode)); \ + rd = gen_avr_ptr(rD(ctx->opcode)); \ + gen_helper_##name(cpu_env, rd, rb, uimm); \ + tcg_temp_free_i32(uimm); \ + tcg_temp_free_ptr(rb); \ + tcg_temp_free_ptr(rd); \ + } + GEN_VXFORM_UIMM(vspltb, 6, 8); GEN_VXFORM_UIMM(vsplth, 6, 9); GEN_VXFORM_UIMM(vspltw, 6, 10); -GEN_VXFORM_UIMM(vcfux, 5, 12); -GEN_VXFORM_UIMM(vcfsx, 5, 13); -GEN_VXFORM_UIMM(vctuxs, 5, 14); -GEN_VXFORM_UIMM(vctsxs, 5, 15); +GEN_VXFORM_UIMM_ENV(vcfux, 5, 12); +GEN_VXFORM_UIMM_ENV(vcfsx, 5, 13); +GEN_VXFORM_UIMM_ENV(vctuxs, 5, 14); +GEN_VXFORM_UIMM_ENV(vctsxs, 5, 15); static void gen_vsldoi(DisasContext *ctx) { @@ -6713,7 +6805,7 @@ static void gen_vsldoi(DisasContext *ctx) } #define GEN_VAFORM_PAIRED(name0, name1, opc2) \ -static void glue(gen_, name0##_##name1)(DisasContext *ctx) \ +static void glue(gen_, name0##_##name1)(DisasContext *ctx) \ { \ TCGv_ptr ra, rb, rc, rd; \ if (unlikely(!ctx->altivec_enabled)) { \ @@ -6725,9 +6817,9 @@ static void glue(gen_, name0##_##name1)(DisasContext *ctx) rc = gen_avr_ptr(rC(ctx->opcode)); \ rd = gen_avr_ptr(rD(ctx->opcode)); \ if (Rc(ctx->opcode)) { \ - gen_helper_##name1 (rd, ra, rb, rc); \ + gen_helper_##name1(cpu_env, rd, ra, rb, rc); \ } else { \ - gen_helper_##name0 (rd, ra, rb, rc); \ + gen_helper_##name0(cpu_env, rd, ra, rb, rc); \ } \ tcg_temp_free_ptr(ra); \ tcg_temp_free_ptr(rb); \ @@ -8008,7 +8100,7 @@ static inline void gen_##name(DisasContext *ctx) \ TCGv t1; \ t0 = tcg_temp_new_i32(); \ tcg_gen_trunc_tl_i32(t0, cpu_gpr[rB(ctx->opcode)]); \ - gen_helper_##name(t0, t0); \ + gen_helper_##name(t0, cpu_env, t0); \ t1 = tcg_temp_new(); \ tcg_gen_extu_i32_tl(t1, t0); \ tcg_temp_free_i32(t0); \ @@ -8023,7 +8115,7 @@ static inline void gen_##name(DisasContext *ctx) \ TCGv_i32 t0; \ TCGv t1; \ t0 = tcg_temp_new_i32(); \ - gen_helper_##name(t0, cpu_gpr[rB(ctx->opcode)]); \ + gen_helper_##name(t0, cpu_env, cpu_gpr[rB(ctx->opcode)]); \ t1 = tcg_temp_new(); \ tcg_gen_extu_i32_tl(t1, t0); \ tcg_temp_free_i32(t0); \ @@ -8037,13 +8129,14 @@ static inline void gen_##name(DisasContext *ctx) \ { \ TCGv_i32 t0 = tcg_temp_new_i32(); \ tcg_gen_trunc_tl_i32(t0, cpu_gpr[rB(ctx->opcode)]); \ - gen_helper_##name(cpu_gpr[rD(ctx->opcode)], t0); \ + gen_helper_##name(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); \ tcg_temp_free_i32(t0); \ } #define GEN_SPEFPUOP_CONV_64_64(name) \ static inline void gen_##name(DisasContext *ctx) \ { \ - gen_helper_##name(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); \ + gen_helper_##name(cpu_gpr[rD(ctx->opcode)], cpu_env, \ + cpu_gpr[rB(ctx->opcode)]); \ } #define GEN_SPEFPUOP_ARITH2_32_32(name) \ static inline void gen_##name(DisasContext *ctx) \ @@ -8058,7 +8151,7 @@ static inline void gen_##name(DisasContext *ctx) \ t1 = tcg_temp_new_i32(); \ tcg_gen_trunc_tl_i32(t0, cpu_gpr[rA(ctx->opcode)]); \ tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); \ - gen_helper_##name(t0, t0, t1); \ + gen_helper_##name(t0, cpu_env, t0, t1); \ tcg_temp_free_i32(t1); \ t2 = tcg_temp_new(); \ tcg_gen_extu_i32_tl(t2, t0); \ @@ -8075,8 +8168,8 @@ static inline void gen_##name(DisasContext *ctx) \ gen_exception(ctx, POWERPC_EXCP_SPEU); \ return; \ } \ - gen_helper_##name(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], \ - cpu_gpr[rB(ctx->opcode)]); \ + gen_helper_##name(cpu_gpr[rD(ctx->opcode)], cpu_env, \ + cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); \ } #define GEN_SPEFPUOP_COMP_32(name) \ static inline void gen_##name(DisasContext *ctx) \ @@ -8090,7 +8183,7 @@ static inline void gen_##name(DisasContext *ctx) \ t1 = tcg_temp_new_i32(); \ tcg_gen_trunc_tl_i32(t0, cpu_gpr[rA(ctx->opcode)]); \ tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); \ - gen_helper_##name(cpu_crf[crfD(ctx->opcode)], t0, t1); \ + gen_helper_##name(cpu_crf[crfD(ctx->opcode)], cpu_env, t0, t1); \ tcg_temp_free_i32(t0); \ tcg_temp_free_i32(t1); \ } @@ -8101,28 +8194,29 @@ static inline void gen_##name(DisasContext *ctx) \ gen_exception(ctx, POWERPC_EXCP_SPEU); \ return; \ } \ - gen_helper_##name(cpu_crf[crfD(ctx->opcode)], \ + gen_helper_##name(cpu_crf[crfD(ctx->opcode)], cpu_env, \ cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); \ } #else #define GEN_SPEFPUOP_CONV_32_32(name) \ static inline void gen_##name(DisasContext *ctx) \ { \ - gen_helper_##name(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); \ + gen_helper_##name(cpu_gpr[rD(ctx->opcode)], cpu_env, \ + cpu_gpr[rB(ctx->opcode)]); \ } #define GEN_SPEFPUOP_CONV_32_64(name) \ static inline void gen_##name(DisasContext *ctx) \ { \ TCGv_i64 t0 = tcg_temp_new_i64(); \ gen_load_gpr64(t0, rB(ctx->opcode)); \ - gen_helper_##name(cpu_gpr[rD(ctx->opcode)], t0); \ + gen_helper_##name(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); \ tcg_temp_free_i64(t0); \ } #define GEN_SPEFPUOP_CONV_64_32(name) \ static inline void gen_##name(DisasContext *ctx) \ { \ TCGv_i64 t0 = tcg_temp_new_i64(); \ - gen_helper_##name(t0, cpu_gpr[rB(ctx->opcode)]); \ + gen_helper_##name(t0, cpu_env, cpu_gpr[rB(ctx->opcode)]); \ gen_store_gpr64(rD(ctx->opcode), t0); \ tcg_temp_free_i64(t0); \ } @@ -8131,7 +8225,7 @@ static inline void gen_##name(DisasContext *ctx) \ { \ TCGv_i64 t0 = tcg_temp_new_i64(); \ gen_load_gpr64(t0, rB(ctx->opcode)); \ - gen_helper_##name(t0, t0); \ + gen_helper_##name(t0, cpu_env, t0); \ gen_store_gpr64(rD(ctx->opcode), t0); \ tcg_temp_free_i64(t0); \ } @@ -8142,7 +8236,7 @@ static inline void gen_##name(DisasContext *ctx) \ gen_exception(ctx, POWERPC_EXCP_SPEU); \ return; \ } \ - gen_helper_##name(cpu_gpr[rD(ctx->opcode)], \ + gen_helper_##name(cpu_gpr[rD(ctx->opcode)], cpu_env, \ cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); \ } #define GEN_SPEFPUOP_ARITH2_64_64(name) \ @@ -8157,7 +8251,7 @@ static inline void gen_##name(DisasContext *ctx) \ t1 = tcg_temp_new_i64(); \ gen_load_gpr64(t0, rA(ctx->opcode)); \ gen_load_gpr64(t1, rB(ctx->opcode)); \ - gen_helper_##name(t0, t0, t1); \ + gen_helper_##name(t0, cpu_env, t0, t1); \ gen_store_gpr64(rD(ctx->opcode), t0); \ tcg_temp_free_i64(t0); \ tcg_temp_free_i64(t1); \ @@ -8169,7 +8263,7 @@ static inline void gen_##name(DisasContext *ctx) \ gen_exception(ctx, POWERPC_EXCP_SPEU); \ return; \ } \ - gen_helper_##name(cpu_crf[crfD(ctx->opcode)], \ + gen_helper_##name(cpu_crf[crfD(ctx->opcode)], cpu_env, \ cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); \ } #define GEN_SPEFPUOP_COMP_64(name) \ @@ -8184,7 +8278,7 @@ static inline void gen_##name(DisasContext *ctx) \ t1 = tcg_temp_new_i64(); \ gen_load_gpr64(t0, rA(ctx->opcode)); \ gen_load_gpr64(t1, rB(ctx->opcode)); \ - gen_helper_##name(cpu_crf[crfD(ctx->opcode)], t0, t1); \ + gen_helper_##name(cpu_crf[crfD(ctx->opcode)], cpu_env, t0, t1); \ tcg_temp_free_i64(t0); \ tcg_temp_free_i64(t1); \ } @@ -9532,7 +9626,7 @@ static inline void gen_intermediate_code_internal(CPUPPCState *env, ctx.access_type = -1; ctx.le_mode = env->hflags & (1 << MSR_LE) ? 1 : 0; #if defined(TARGET_PPC64) - ctx.sf_mode = msr_sf; + ctx.sf_mode = msr_is_64bit(env, env->msr); ctx.has_cfar = !!(env->flags & POWERPC_FLAG_CFAR); #endif ctx.fpu_enabled = msr_fp; @@ -9589,9 +9683,9 @@ static inline void gen_intermediate_code_internal(CPUPPCState *env, if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) gen_io_start(); if (unlikely(ctx.le_mode)) { - ctx.opcode = bswap32(ldl_code(ctx.nip)); + ctx.opcode = bswap32(cpu_ldl_code(env, ctx.nip)); } else { - ctx.opcode = ldl_code(ctx.nip); + ctx.opcode = cpu_ldl_code(env, ctx.nip); } LOG_DISAS("translate opcode %08x (%02x %02x %02x) (%s)\n", ctx.opcode, opc1(ctx.opcode), opc2(ctx.opcode), diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 6f61175e7d..5742229197 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -55,31 +55,50 @@ PPC_IRQ_INIT_FN(e500); /* Generic callbacks: * do nothing but store/retrieve spr value */ +static void spr_load_dump_spr(int sprn) +{ +#ifdef PPC_DUMP_SPR_ACCESSES + TCGv_i32 t0 = tcg_const_i32(sprn); + gen_helper_load_dump_spr(t0); + tcg_temp_free_i32(t0); +#endif +} + static void spr_read_generic (void *opaque, int gprn, int sprn) { gen_load_spr(cpu_gpr[gprn], sprn); + spr_load_dump_spr(sprn); +} + +static void spr_store_dump_spr(int sprn) +{ #ifdef PPC_DUMP_SPR_ACCESSES - { - TCGv_i32 t0 = tcg_const_i32(sprn); - gen_helper_load_dump_spr(t0); - tcg_temp_free_i32(t0); - } + TCGv_i32 t0 = tcg_const_i32(sprn); + gen_helper_store_dump_spr(t0); + tcg_temp_free_i32(t0); #endif } static void spr_write_generic (void *opaque, int sprn, int gprn) { gen_store_spr(sprn, cpu_gpr[gprn]); -#ifdef PPC_DUMP_SPR_ACCESSES - { - TCGv_i32 t0 = tcg_const_i32(sprn); - gen_helper_store_dump_spr(t0); - tcg_temp_free_i32(t0); - } -#endif + spr_store_dump_spr(sprn); } #if !defined(CONFIG_USER_ONLY) +static void spr_write_generic32(void *opaque, int sprn, int gprn) +{ +#ifdef TARGET_PPC64 + TCGv t0 = tcg_temp_new(); + tcg_gen_ext32u_tl(t0, cpu_gpr[gprn]); + gen_store_spr(sprn, t0); + tcg_temp_free(t0); + spr_store_dump_spr(sprn); +#else + spr_write_generic(opaque, sprn, gprn); +#endif +} + static void spr_write_clear (void *opaque, int sprn, int gprn) { TCGv t0 = tcg_temp_new(); @@ -159,7 +178,7 @@ static void spr_read_decr (void *opaque, int gprn, int sprn) if (use_icount) { gen_io_start(); } - gen_helper_load_decr(cpu_gpr[gprn]); + gen_helper_load_decr(cpu_gpr[gprn], cpu_env); if (use_icount) { gen_io_end(); gen_stop_exception(opaque); @@ -171,7 +190,7 @@ static void spr_write_decr (void *opaque, int sprn, int gprn) if (use_icount) { gen_io_start(); } - gen_helper_store_decr(cpu_gpr[gprn]); + gen_helper_store_decr(cpu_env, cpu_gpr[gprn]); if (use_icount) { gen_io_end(); gen_stop_exception(opaque); @@ -186,7 +205,7 @@ static void spr_read_tbl (void *opaque, int gprn, int sprn) if (use_icount) { gen_io_start(); } - gen_helper_load_tbl(cpu_gpr[gprn]); + gen_helper_load_tbl(cpu_gpr[gprn], cpu_env); if (use_icount) { gen_io_end(); gen_stop_exception(opaque); @@ -198,7 +217,7 @@ static void spr_read_tbu (void *opaque, int gprn, int sprn) if (use_icount) { gen_io_start(); } - gen_helper_load_tbu(cpu_gpr[gprn]); + gen_helper_load_tbu(cpu_gpr[gprn], cpu_env); if (use_icount) { gen_io_end(); gen_stop_exception(opaque); @@ -208,13 +227,13 @@ static void spr_read_tbu (void *opaque, int gprn, int sprn) __attribute__ (( unused )) static void spr_read_atbl (void *opaque, int gprn, int sprn) { - gen_helper_load_atbl(cpu_gpr[gprn]); + gen_helper_load_atbl(cpu_gpr[gprn], cpu_env); } __attribute__ (( unused )) static void spr_read_atbu (void *opaque, int gprn, int sprn) { - gen_helper_load_atbu(cpu_gpr[gprn]); + gen_helper_load_atbu(cpu_gpr[gprn], cpu_env); } #if !defined(CONFIG_USER_ONLY) @@ -223,7 +242,7 @@ static void spr_write_tbl (void *opaque, int sprn, int gprn) if (use_icount) { gen_io_start(); } - gen_helper_store_tbl(cpu_gpr[gprn]); + gen_helper_store_tbl(cpu_env, cpu_gpr[gprn]); if (use_icount) { gen_io_end(); gen_stop_exception(opaque); @@ -235,7 +254,7 @@ static void spr_write_tbu (void *opaque, int sprn, int gprn) if (use_icount) { gen_io_start(); } - gen_helper_store_tbu(cpu_gpr[gprn]); + gen_helper_store_tbu(cpu_env, cpu_gpr[gprn]); if (use_icount) { gen_io_end(); gen_stop_exception(opaque); @@ -245,20 +264,20 @@ static void spr_write_tbu (void *opaque, int sprn, int gprn) __attribute__ (( unused )) static void spr_write_atbl (void *opaque, int sprn, int gprn) { - gen_helper_store_atbl(cpu_gpr[gprn]); + gen_helper_store_atbl(cpu_env, cpu_gpr[gprn]); } __attribute__ (( unused )) static void spr_write_atbu (void *opaque, int sprn, int gprn) { - gen_helper_store_atbu(cpu_gpr[gprn]); + gen_helper_store_atbu(cpu_env, cpu_gpr[gprn]); } #if defined(TARGET_PPC64) __attribute__ (( unused )) static void spr_read_purr (void *opaque, int gprn, int sprn) { - gen_helper_load_purr(cpu_gpr[gprn]); + gen_helper_load_purr(cpu_gpr[gprn], cpu_env); } #endif #endif @@ -279,28 +298,28 @@ static void spr_read_ibat_h (void *opaque, int gprn, int sprn) static void spr_write_ibatu (void *opaque, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2); - gen_helper_store_ibatu(t0, cpu_gpr[gprn]); + gen_helper_store_ibatu(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } static void spr_write_ibatu_h (void *opaque, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_IBAT4U) / 2) + 4); - gen_helper_store_ibatu(t0, cpu_gpr[gprn]); + gen_helper_store_ibatu(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } static void spr_write_ibatl (void *opaque, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0L) / 2); - gen_helper_store_ibatl(t0, cpu_gpr[gprn]); + gen_helper_store_ibatl(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } static void spr_write_ibatl_h (void *opaque, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_IBAT4L) / 2) + 4); - gen_helper_store_ibatl(t0, cpu_gpr[gprn]); + gen_helper_store_ibatl(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } @@ -319,35 +338,35 @@ static void spr_read_dbat_h (void *opaque, int gprn, int sprn) static void spr_write_dbatu (void *opaque, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32((sprn - SPR_DBAT0U) / 2); - gen_helper_store_dbatu(t0, cpu_gpr[gprn]); + gen_helper_store_dbatu(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } static void spr_write_dbatu_h (void *opaque, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_DBAT4U) / 2) + 4); - gen_helper_store_dbatu(t0, cpu_gpr[gprn]); + gen_helper_store_dbatu(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } static void spr_write_dbatl (void *opaque, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32((sprn - SPR_DBAT0L) / 2); - gen_helper_store_dbatl(t0, cpu_gpr[gprn]); + gen_helper_store_dbatl(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } static void spr_write_dbatl_h (void *opaque, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_DBAT4L) / 2) + 4); - gen_helper_store_dbatl(t0, cpu_gpr[gprn]); + gen_helper_store_dbatl(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } /* SDR1 */ static void spr_write_sdr1 (void *opaque, int sprn, int gprn) { - gen_helper_store_sdr1(cpu_gpr[gprn]); + gen_helper_store_sdr1(cpu_env, cpu_gpr[gprn]); } /* 64 bits PowerPC specific SPRs */ @@ -373,7 +392,7 @@ static void spr_read_asr (void *opaque, int gprn, int sprn) static void spr_write_asr (void *opaque, int sprn, int gprn) { - gen_helper_store_asr(cpu_gpr[gprn]); + gen_helper_store_asr(cpu_env, cpu_gpr[gprn]); } #endif #endif @@ -382,30 +401,30 @@ static void spr_write_asr (void *opaque, int sprn, int gprn) /* RTC */ static void spr_read_601_rtcl (void *opaque, int gprn, int sprn) { - gen_helper_load_601_rtcl(cpu_gpr[gprn]); + gen_helper_load_601_rtcl(cpu_gpr[gprn], cpu_env); } static void spr_read_601_rtcu (void *opaque, int gprn, int sprn) { - gen_helper_load_601_rtcu(cpu_gpr[gprn]); + gen_helper_load_601_rtcu(cpu_gpr[gprn], cpu_env); } #if !defined(CONFIG_USER_ONLY) static void spr_write_601_rtcu (void *opaque, int sprn, int gprn) { - gen_helper_store_601_rtcu(cpu_gpr[gprn]); + gen_helper_store_601_rtcu(cpu_env, cpu_gpr[gprn]); } static void spr_write_601_rtcl (void *opaque, int sprn, int gprn) { - gen_helper_store_601_rtcl(cpu_gpr[gprn]); + gen_helper_store_601_rtcl(cpu_env, cpu_gpr[gprn]); } static void spr_write_hid0_601 (void *opaque, int sprn, int gprn) { DisasContext *ctx = opaque; - gen_helper_store_hid0_601(cpu_gpr[gprn]); + gen_helper_store_hid0_601(cpu_env, cpu_gpr[gprn]); /* Must stop the translation as endianness may have changed */ gen_stop_exception(ctx); } @@ -421,14 +440,14 @@ static void spr_read_601_ubat (void *opaque, int gprn, int sprn) static void spr_write_601_ubatu (void *opaque, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2); - gen_helper_store_601_batl(t0, cpu_gpr[gprn]); + gen_helper_store_601_batl(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } static void spr_write_601_ubatl (void *opaque, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2); - gen_helper_store_601_batu(t0, cpu_gpr[gprn]); + gen_helper_store_601_batu(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } #endif @@ -437,36 +456,36 @@ static void spr_write_601_ubatl (void *opaque, int sprn, int gprn) #if !defined(CONFIG_USER_ONLY) static void spr_read_40x_pit (void *opaque, int gprn, int sprn) { - gen_helper_load_40x_pit(cpu_gpr[gprn]); + gen_helper_load_40x_pit(cpu_gpr[gprn], cpu_env); } static void spr_write_40x_pit (void *opaque, int sprn, int gprn) { - gen_helper_store_40x_pit(cpu_gpr[gprn]); + gen_helper_store_40x_pit(cpu_env, cpu_gpr[gprn]); } static void spr_write_40x_dbcr0 (void *opaque, int sprn, int gprn) { DisasContext *ctx = opaque; - gen_helper_store_40x_dbcr0(cpu_gpr[gprn]); + gen_helper_store_40x_dbcr0(cpu_env, cpu_gpr[gprn]); /* We must stop translation as we may have rebooted */ gen_stop_exception(ctx); } static void spr_write_40x_sler (void *opaque, int sprn, int gprn) { - gen_helper_store_40x_sler(cpu_gpr[gprn]); + gen_helper_store_40x_sler(cpu_env, cpu_gpr[gprn]); } static void spr_write_booke_tcr (void *opaque, int sprn, int gprn) { - gen_helper_store_booke_tcr(cpu_gpr[gprn]); + gen_helper_store_booke_tcr(cpu_env, cpu_gpr[gprn]); } static void spr_write_booke_tsr (void *opaque, int sprn, int gprn) { - gen_helper_store_booke_tsr(cpu_gpr[gprn]); + gen_helper_store_booke_tsr(cpu_env, cpu_gpr[gprn]); } #endif @@ -481,7 +500,7 @@ static void spr_read_403_pbr (void *opaque, int gprn, int sprn) static void spr_write_403_pbr (void *opaque, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32(sprn - SPR_403_PBL1); - gen_helper_store_403_pbr(t0, cpu_gpr[gprn]); + gen_helper_store_403_pbr(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } @@ -1371,14 +1390,14 @@ static void spr_write_e500_l1csr0 (void *opaque, int sprn, int gprn) static void spr_write_booke206_mmucsr0 (void *opaque, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32(sprn); - gen_helper_booke206_tlbflush(t0); + gen_helper_booke206_tlbflush(cpu_env, t0); tcg_temp_free_i32(t0); } static void spr_write_booke_pid (void *opaque, int sprn, int gprn) { TCGv_i32 t0 = tcg_const_i32(sprn); - gen_helper_booke_setpid(t0, cpu_gpr[gprn]); + gen_helper_booke_setpid(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } #endif @@ -1591,10 +1610,14 @@ static void gen_spr_BookE206(CPUPPCState *env, uint32_t mas_mask, /* TLB assist registers */ /* XXX : not implemented */ for (i = 0; i < 8; i++) { + void (*uea_write)(void *o, int sprn, int gprn) = &spr_write_generic32; + if (i == 2 && (mas_mask & (1 << i)) && (env->insns_flags & PPC_64B)) { + uea_write = &spr_write_generic; + } if (mas_mask & (1 << i)) { spr_register(env, mas_sprn[i], mas_names[i], SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, uea_write, 0x00000000); } } @@ -2804,7 +2827,7 @@ static void init_excp_G2 (CPUPPCState *env) #endif } -static void init_excp_e200 (CPUPPCState *env) +static void init_excp_e200(CPUPPCState *env, target_ulong ivpr_mask) { #if !defined(CONFIG_USER_ONLY) env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000FFC; @@ -2829,7 +2852,7 @@ static void init_excp_e200 (CPUPPCState *env) env->excp_vectors[POWERPC_EXCP_EFPRI] = 0x00000000; env->hreset_excp_prefix = 0x00000000UL; env->ivor_mask = 0x0000FFF7UL; - env->ivpr_mask = 0xFFFF0000UL; + env->ivpr_mask = ivpr_mask; /* Hardware reset vector */ env->hreset_vector = 0xFFFFFFFCUL; #endif @@ -4307,7 +4330,7 @@ static void init_proc_e200 (CPUPPCState *env) env->id_tlbs = 0; env->tlb_type = TLB_EMB; #endif - init_excp_e200(env); + init_excp_e200(env, 0xFFFF0000UL); env->dcache_line_size = 32; env->icache_line_size = 32; /* XXX: TODO: allocate internal IRQ controller */ @@ -4424,16 +4447,70 @@ static void init_proc_e300 (CPUPPCState *env) #define check_pow_e500mc check_pow_none #define init_proc_e500mc init_proc_e500mc +/* e5500 core */ +#define POWERPC_INSNS_e5500 (PPC_INSNS_BASE | PPC_ISEL | \ + PPC_WRTEE | PPC_RFDI | PPC_RFMCI | \ + PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | \ + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \ + PPC_FLOAT | PPC_FLOAT_FRES | \ + PPC_FLOAT_FRSQRTE | PPC_FLOAT_FSEL | \ + PPC_FLOAT_STFIWX | PPC_WAIT | \ + PPC_MEM_TLBSYNC | PPC_TLBIVAX | PPC_MEM_SYNC | \ + PPC_64B | PPC_POPCNTB | PPC_POPCNTWD) +#define POWERPC_INSNS2_e5500 (PPC2_BOOKE206 | PPC2_PRCNTL) +#define POWERPC_MSRM_e5500 (0x000000009402FB36ULL) +#define POWERPC_MMU_e5500 (POWERPC_MMU_BOOKE206) +#define POWERPC_EXCP_e5500 (POWERPC_EXCP_BOOKE) +#define POWERPC_INPUT_e5500 (PPC_FLAGS_INPUT_BookE) +/* Fixme: figure out the correct flag for e5500 */ +#define POWERPC_BFDM_e5500 (bfd_mach_ppc_e500) +#define POWERPC_FLAG_e5500 (POWERPC_FLAG_CE | POWERPC_FLAG_DE | \ + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK) +#define check_pow_e5500 check_pow_none +#define init_proc_e5500 init_proc_e5500 + +#if !defined(CONFIG_USER_ONLY) +static void spr_write_mas73(void *opaque, int sprn, int gprn) +{ + TCGv val = tcg_temp_new(); + tcg_gen_ext32u_tl(val, cpu_gpr[gprn]); + gen_store_spr(SPR_BOOKE_MAS3, val); + tcg_gen_shri_tl(val, cpu_gpr[gprn], 32); + gen_store_spr(SPR_BOOKE_MAS7, val); + tcg_temp_free(val); +} + +static void spr_read_mas73(void *opaque, int gprn, int sprn) +{ + TCGv mas7 = tcg_temp_new(); + TCGv mas3 = tcg_temp_new(); + gen_load_spr(mas7, SPR_BOOKE_MAS7); + tcg_gen_shli_tl(mas7, mas7, 32); + gen_load_spr(mas3, SPR_BOOKE_MAS3); + tcg_gen_or_tl(cpu_gpr[gprn], mas3, mas7); + tcg_temp_free(mas3); + tcg_temp_free(mas7); +} + +static void spr_load_epr(void *opaque, int gprn, int sprn) +{ + gen_helper_load_epr(cpu_gpr[gprn], cpu_env); +} + +#endif + enum fsl_e500_version { fsl_e500v1, fsl_e500v2, fsl_e500mc, + fsl_e5500, }; static void init_proc_e500 (CPUPPCState *env, int version) { uint32_t tlbncfg[2]; - uint64_t ivor_mask = 0x0000000F0000FFFFULL; + uint64_t ivor_mask; + uint64_t ivpr_mask = 0xFFFF0000ULL; uint32_t l1cfg0 = 0x3800 /* 8 ways */ | 0x0020; /* 32 kb */ #if !defined(CONFIG_USER_ONLY) @@ -4447,8 +4524,16 @@ static void init_proc_e500 (CPUPPCState *env, int version) * complain when accessing them. * gen_spr_BookE(env, 0x0000000F0000FD7FULL); */ - if (version == fsl_e500mc) { - ivor_mask = 0x000003FE0000FFFFULL; + switch (version) { + case fsl_e500v1: + case fsl_e500v2: + default: + ivor_mask = 0x0000000F0000FFFFULL; + break; + case fsl_e500mc: + case fsl_e5500: + ivor_mask = 0x000003FE0000FFFFULL; + break; } gen_spr_BookE(env, ivor_mask); /* Processor identification */ @@ -4476,6 +4561,7 @@ static void init_proc_e500 (CPUPPCState *env, int version) tlbncfg[1] = gen_tlbncfg(16, 1, 12, TLBnCFG_AVAIL | TLBnCFG_IPROT, 16); break; case fsl_e500mc: + case fsl_e5500: tlbncfg[0] = gen_tlbncfg(4, 1, 1, 0, 512); tlbncfg[1] = gen_tlbncfg(64, 1, 12, TLBnCFG_AVAIL | TLBnCFG_IPROT, 64); break; @@ -4491,6 +4577,7 @@ static void init_proc_e500 (CPUPPCState *env, int version) env->icache_line_size = 32; break; case fsl_e500mc: + case fsl_e5500: env->dcache_line_size = 64; env->icache_line_size = 64; l1cfg0 |= 0x1000000; /* 64 byte cache block size */ @@ -4566,6 +4653,22 @@ static void init_proc_e500 (CPUPPCState *env, int version) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_booke206_mmucsr0, 0x00000000); + spr_register(env, SPR_BOOKE_EPR, "EPR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_load_epr, SPR_NOACCESS, + 0x00000000); + /* XXX better abstract into Emb.xxx features */ + if (version == fsl_e5500) { + spr_register(env, SPR_BOOKE_EPCR, "EPCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_BOOKE_MAS7_MAS3, "MAS7_MAS3", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_mas73, &spr_write_mas73, + 0x00000000); + ivpr_mask = (target_ulong)~0xFFFFULL; + } #if !defined(CONFIG_USER_ONLY) env->nb_tlb = 0; @@ -4575,7 +4678,7 @@ static void init_proc_e500 (CPUPPCState *env, int version) } #endif - init_excp_e200(env); + init_excp_e200(env, ivpr_mask); /* Allocate hardware IRQ controller */ ppce500_irq_init(env); } @@ -4595,6 +4698,13 @@ static void init_proc_e500mc(CPUPPCState *env) init_proc_e500(env, fsl_e500mc); } +#ifdef TARGET_PPC64 +static void init_proc_e5500(CPUPPCState *env) +{ + init_proc_e500(env, fsl_e5500); +} +#endif + /* Non-embedded PowerPC */ /* POWER : same as 601, without mfmsr, mfsr */ @@ -7133,6 +7243,7 @@ enum { CPU_POWERPC_e500v2_v22 = 0x80210022, CPU_POWERPC_e500v2_v30 = 0x80210030, CPU_POWERPC_e500mc = 0x80230020, + CPU_POWERPC_e5500 = 0x80240020, /* MPC85xx microcontrollers */ #define CPU_POWERPC_MPC8533 CPU_POWERPC_MPC8533_v11 #define CPU_POWERPC_MPC8533_v10 CPU_POWERPC_e500v2_v21 @@ -8527,6 +8638,9 @@ static const ppc_def_t ppc_defs[] = { /* PowerPC e500v2 v3.0 core */ POWERPC_DEF("e500v2_v30", CPU_POWERPC_e500v2_v30, e500v2), POWERPC_DEF("e500mc", CPU_POWERPC_e500mc, e500mc), +#ifdef TARGET_PPC64 + POWERPC_DEF("e5500", CPU_POWERPC_e5500, e5500), +#endif /* PowerPC e500 microcontrollers */ /* MPC8533 */ POWERPC_DEF_SVR("MPC8533", @@ -9928,6 +10042,27 @@ int cpu_ppc_register_internal (CPUPPCState *env, const ppc_def_t *def) env->bfd_mach = def->bfd_mach; env->check_pow = def->check_pow; +#if defined(TARGET_PPC64) + if (def->sps) + env->sps = *def->sps; + else if (env->mmu_model & POWERPC_MMU_64) { + /* Use default sets of page sizes */ + static const struct ppc_segment_page_sizes defsps = { + .sps = { + { .page_shift = 12, /* 4K */ + .slb_enc = 0, + .enc = { { .page_shift = 12, .pte_enc = 0 } } + }, + { .page_shift = 24, /* 16M */ + .slb_enc = 0x100, + .enc = { { .page_shift = 24, .pte_enc = 0 } } + }, + }, + }; + env->sps = defsps; + } +#endif /* defined(TARGET_PPC64) */ + if (kvm_enabled()) { if (kvmppc_fixup_cpu(env) != 0) { fprintf(stderr, "Unable to virtualize selected CPU with KVM\n"); diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index 5800fd612c..ec08dd0474 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -314,6 +314,7 @@ static int s390_cpu_initial_reset(CPUS390XState *env) { int i; + s390_del_running_cpu(env); if (kvm_vcpu_ioctl(env, KVM_S390_INITIAL_RESET, NULL) < 0) { perror("cannot init reset vcpu"); } diff --git a/tcg/ppc/tcg-target.c b/tcg/ppc/tcg-target.c index d26569715b..0cff181257 100644 --- a/tcg/ppc/tcg-target.c +++ b/tcg/ppc/tcg-target.c @@ -1865,7 +1865,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, break; default: - tcg_dump_ops (s, stderr); + tcg_dump_ops (s); tcg_abort (); } } diff --git a/tcg/ppc64/tcg-target.c b/tcg/ppc64/tcg-target.c index c800574588..27a0ae88ec 100644 --- a/tcg/ppc64/tcg-target.c +++ b/tcg/ppc64/tcg-target.c @@ -1613,7 +1613,7 @@ static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args, break; default: - tcg_dump_ops (s, stderr); + tcg_dump_ops (s); tcg_abort (); } } diff --git a/tcg/tci/tcg-target.c b/tcg/tci/tcg-target.c index d0a368d99a..ef8580fc8d 100644 --- a/tcg/tci/tcg-target.c +++ b/tcg/tci/tcg-target.c @@ -487,7 +487,7 @@ static void tci_out_label(TCGContext *s, TCGArg arg) assert(label->u.value); } else { tcg_out_reloc(s, s->code_ptr, sizeof(tcg_target_ulong), arg, 0); - tcg_out_i(s, 0); + s->code_ptr += sizeof(tcg_target_ulong); } } diff --git a/trace-events b/trace-events index 5c82b3acf2..c935ba24f4 100644 --- a/trace-events +++ b/trace-events @@ -252,12 +252,13 @@ usb_ehci_qtd_fields(uint32_t addr, int tbytes, int cpage, int cerr, int pid) "QT usb_ehci_qtd_bits(uint32_t addr, int ioc, int active, int halt, int babble, int xacterr) "QTD @ %08x - ioc %d, active %d, halt %d, babble %d, xacterr %d" usb_ehci_itd(uint32_t addr, uint32_t nxt, uint32_t mplen, uint32_t mult, uint32_t ep, uint32_t devaddr) "ITD @ %08x: next %08x - mplen %d, mult %d, ep %d, dev %d" usb_ehci_sitd(uint32_t addr, uint32_t nxt, uint32_t active) "ITD @ %08x: next %08x - active %d" -usb_ehci_port_attach(uint32_t port, const char *device) "attach port #%d - %s" -usb_ehci_port_detach(uint32_t port) "detach port #%d" +usb_ehci_port_attach(uint32_t port, const char *owner, const char *device) "attach port #%d, owner %s, device %s" +usb_ehci_port_detach(uint32_t port, const char *owner) "detach port #%d, owner %s" usb_ehci_port_reset(uint32_t port, int enable) "reset port #%d - %d" usb_ehci_data(int rw, uint32_t cpage, uint32_t offset, uint32_t addr, uint32_t len, uint32_t bufpos) "write %d, cpage %d, offset 0x%03x, addr 0x%08x, len %d, bufpos %d" usb_ehci_queue_action(void *q, const char *action) "q %p: %s" usb_ehci_packet_action(void *q, void *p, const char *action) "q %p p %p: %s" +usb_ehci_interrupt(uint32_t level, uint32_t sts, uint32_t mask) "level %d, sts 0x%x, mask 0x%x" # hw/usb/hcd-uhci.c usb_uhci_reset(void) "=== RESET ===" @@ -1191,3 +1191,15 @@ void xen_register_framebuffer(MemoryRegion *mr) { framebuffer = mr; } + +void xen_shutdown_fatal_error(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "Will destroy the domain.\n"); + /* destroy the domain */ + qemu_system_shutdown_request(); +} |