diff --git a/MAINTAINERS b/MAINTAINERS index 497ca54627..00b81f1432 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -671,10 +671,13 @@ F: hw/misc/macio/ F: hw/intc/heathrow_pic.c PReP +M: Hervé Poussineau L: qemu-devel@nongnu.org L: qemu-ppc@nongnu.org -S: Odd Fixes +S: Maintained F: hw/ppc/prep.c +F: hw/ppc/prep_systemio.c +F: hw/ppc/rs6000_mc.c F: hw/pci-host/prep.[hc] F: hw/isa/pc87312.[hc] F: pc-bios/ppc_rom.bin diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index 9288838e32..384cefb612 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -57,3 +57,4 @@ CONFIG_IOH3420=y CONFIG_I82801B11=y CONFIG_SMBIOS=y CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM) +CONFIG_PXB=y diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak index d4d0f9b192..7dd004e36e 100644 --- a/default-configs/ppc-softmmu.mak +++ b/default-configs/ppc-softmmu.mak @@ -18,6 +18,7 @@ CONFIG_I82378=y CONFIG_PC87312=y CONFIG_MACIO=y CONFIG_PCSPK=y +CONFIG_CS4231A=y CONFIG_CUDA=y CONFIG_ADB=y CONFIG_MAC_NVRAM=y @@ -47,3 +48,4 @@ CONFIG_LIBDECNUMBER=y # For PReP CONFIG_MC146818RTC=y CONFIG_ISA_TESTDEV=y +CONFIG_RS6000_MC=y diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak index 67a9bcaa67..9ae6563c82 100644 --- a/default-configs/ppc64-softmmu.mak +++ b/default-configs/ppc64-softmmu.mak @@ -55,3 +55,4 @@ CONFIG_XICS_KVM=$(and $(CONFIG_PSERIES),$(CONFIG_KVM)) CONFIG_MC146818RTC=y CONFIG_ISA_TESTDEV=y CONFIG_MEM_HOTPLUG=y +CONFIG_RS6000_MC=y diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak index 7d2c2d4d3a..491a191b9d 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -57,3 +57,4 @@ CONFIG_IOH3420=y CONFIG_I82801B11=y CONFIG_SMBIOS=y CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM) +CONFIG_PXB=y diff --git a/disas/ppc.c b/disas/ppc.c index ed7e0d0b9c..5ab9c35a84 100644 --- a/disas/ppc.c +++ b/disas/ppc.c @@ -1653,11 +1653,11 @@ extract_tbr (unsigned long insn, #define BBOYBI_MASK (BBOYCB_MASK | BI_MASK) #define BBOATBI_MASK (BBOAT2CB_MASK | BI_MASK) -/* An Context form instruction. */ +/* A Context form instruction. */ #define CTX(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x7)) #define CTX_MASK CTX(0x3f, 0x7) -/* An User Context form instruction. */ +/* A User Context form instruction. */ #define UCTX(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x1f)) #define UCTX_MASK UCTX(0x3f, 0x1f) @@ -1710,19 +1710,19 @@ extract_tbr (unsigned long insn, #define SC(op, sa, lk) (OP (op) | ((((unsigned long)(sa)) & 1) << 1) | ((lk) & 1)) #define SC_MASK (OP_MASK | (((unsigned long)0x3ff) << 16) | (((unsigned long)1) << 1) | 1) -/* An VX form instruction. */ +/* A VX form instruction. */ #define VX(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x7ff)) /* The mask for an VX form instruction. */ #define VX_MASK VX(0x3f, 0x7ff) -/* An VA form instruction. */ +/* A VA form instruction. */ #define VXA(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x03f)) -/* The mask for an VA form instruction. */ +/* The mask for a VA form instruction. */ #define VXA_MASK VXA(0x3f, 0x3f) -/* An VXR form instruction. */ +/* A VXR form instruction. */ #define VXR(op, xop, rc) (OP (op) | (((rc) & 1) << 10) | (((unsigned long)(xop)) & 0x3ff)) /* The mask for a VXR form instruction. */ diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index f05c8658c1..100c8a98bf 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -185,7 +185,7 @@ float128 float128_default_nan(float_status *status) r.high = LIT64(0x7FFF7FFFFFFFFFFF); } else { r.low = LIT64(0x0000000000000000); -#if defined(TARGET_S390X) +#if defined(TARGET_S390X) || defined(TARGET_PPC) r.high = LIT64(0x7FFF800000000000); #else r.high = LIT64(0xFFFF800000000000); diff --git a/hw/gpio/mpc8xxx.c b/hw/gpio/mpc8xxx.c index d149719469..e12edb4933 100644 --- a/hw/gpio/mpc8xxx.c +++ b/hw/gpio/mpc8xxx.c @@ -143,8 +143,10 @@ static void mpc8xxx_gpio_write(void *opaque, hwaddr offset, mpc8xxx_gpio_update(s); } -static void mpc8xxx_gpio_reset(MPC8XXXGPIOState *s) +static void mpc8xxx_gpio_reset(DeviceState *dev) { + MPC8XXXGPIOState *s = MPC8XXX_GPIO(dev); + s->dir = 0; s->odr = 0; s->dat = 0; @@ -180,33 +182,33 @@ static const MemoryRegionOps mpc8xxx_gpio_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static int mpc8xxx_gpio_initfn(SysBusDevice *sbd) +static void mpc8xxx_gpio_initfn(Object *obj) { - DeviceState *dev = DEVICE(sbd); - MPC8XXXGPIOState *s = MPC8XXX_GPIO(dev); + DeviceState *dev = DEVICE(obj); + MPC8XXXGPIOState *s = MPC8XXX_GPIO(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - memory_region_init_io(&s->iomem, OBJECT(s), &mpc8xxx_gpio_ops, s, "mpc8xxx_gpio", 0x1000); + memory_region_init_io(&s->iomem, obj, &mpc8xxx_gpio_ops, + s, "mpc8xxx_gpio", 0x1000); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq); qdev_init_gpio_in(dev, mpc8xxx_gpio_set_irq, 32); qdev_init_gpio_out(dev, s->out, 32); - mpc8xxx_gpio_reset(s); - return 0; } static void mpc8xxx_gpio_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = mpc8xxx_gpio_initfn; dc->vmsd = &vmstate_mpc8xxx_gpio; + dc->reset = mpc8xxx_gpio_reset; } static const TypeInfo mpc8xxx_gpio_info = { .name = TYPE_MPC8XXX_GPIO, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MPC8XXXGPIOState), + .instance_init = mpc8xxx_gpio_initfn, .class_init = mpc8xxx_gpio_class_init, }; diff --git a/hw/pci-bridge/Makefile.objs b/hw/pci-bridge/Makefile.objs index 85e8e3990f..c4683cf5c1 100644 --- a/hw/pci-bridge/Makefile.objs +++ b/hw/pci-bridge/Makefile.objs @@ -1,6 +1,6 @@ common-obj-y += pci_bridge_dev.o -common-obj-y += pci_expander_bridge.o common-obj-$(CONFIG_PCIE_PORT) += pcie_root_port.o gen_pcie_root_port.o +common-obj-$(CONFIG_PXB) += pci_expander_bridge.o common-obj-$(CONFIG_XIO3130) += xio3130_upstream.o xio3130_downstream.o common-obj-$(CONFIG_IOH3420) += ioh3420.o common-obj-$(CONFIG_I82801B11) += i82801b11.o diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 8025129377..001293423c 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -16,6 +16,8 @@ obj-y += ppc405_boards.o ppc4xx_devs.o ppc405_uc.o ppc440_bamboo.o obj-y += ppc4xx_pci.o # PReP obj-$(CONFIG_PREP) += prep.o +obj-$(CONFIG_PREP) += prep_systemio.o +obj-${CONFIG_RS6000_MC} += rs6000_mc.o # OldWorld PowerMac obj-$(CONFIG_MAC) += mac_oldworld.o # NewWorld PowerMac diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index cf8b122afe..f7df2388c1 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -827,6 +827,12 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) env = &cpu->env; cs = CPU(cpu); + if (env->mmu_model != POWERPC_MMU_BOOKE206) { + fprintf(stderr, "MMU model %i not supported by this machine.\n", + env->mmu_model); + exit(1); + } + if (!firstenv) { firstenv = env; } @@ -1049,27 +1055,18 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) boot_info->dt_size = dt_size; } -static int e500_ccsr_initfn(SysBusDevice *dev) +static void e500_ccsr_initfn(Object *obj) { - PPCE500CCSRState *ccsr; - - ccsr = CCSR(dev); - memory_region_init(&ccsr->ccsr_space, OBJECT(ccsr), "e500-ccsr", + PPCE500CCSRState *ccsr = CCSR(obj); + memory_region_init(&ccsr->ccsr_space, obj, "e500-ccsr", MPC8544_CCSRBAR_SIZE); - return 0; -} - -static void e500_ccsr_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = e500_ccsr_initfn; } static const TypeInfo e500_ccsr_info = { .name = TYPE_CCSR, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PPCE500CCSRState), - .class_init = e500_ccsr_class_init, + .instance_init = e500_ccsr_initfn, }; static void e500_register_types(void) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 83597fe92b..4fab5c0ae7 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -381,7 +381,7 @@ static void ppc_powernv_init(MachineState *machine) fw_size = load_image_targphys(fw_filename, FW_LOAD_ADDR, FW_MAX_SIZE); if (fw_size < 0) { - hw_error("qemu: could not load OPAL '%s'\n", fw_filename); + error_report("qemu: could not load OPAL '%s'", fw_filename); exit(1); } g_free(fw_filename); @@ -393,8 +393,8 @@ static void ppc_powernv_init(MachineState *machine) kernel_size = load_image_targphys(machine->kernel_filename, KERNEL_LOAD_ADDR, 0x2000000); if (kernel_size < 0) { - hw_error("qemu: could not load kernel'%s'\n", - machine->kernel_filename); + error_report("qemu: could not load kernel'%s'", + machine->kernel_filename); exit(1); } } diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index 8945869009..d171e60b5c 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -847,9 +847,8 @@ static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq) cpu_ppc_store_purr(cpu, 0x0000000000000000ULL); } -static void timebase_pre_save(void *opaque) +static void timebase_save(PPCTimebase *tb) { - PPCTimebase *tb = opaque; uint64_t ticks = cpu_get_host_ticks(); PowerPCCPU *first_ppc_cpu = POWERPC_CPU(first_cpu); @@ -858,43 +857,30 @@ static void timebase_pre_save(void *opaque) return; } + /* not used anymore, we keep it for compatibility */ tb->time_of_the_day_ns = qemu_clock_get_ns(QEMU_CLOCK_HOST); /* - * tb_offset is only expected to be changed by migration so + * tb_offset is only expected to be changed by QEMU so * there is no need to update it from KVM here */ tb->guest_timebase = ticks + first_ppc_cpu->env.tb_env->tb_offset; } -static int timebase_post_load(void *opaque, int version_id) +static void timebase_load(PPCTimebase *tb) { - PPCTimebase *tb_remote = opaque; CPUState *cpu; PowerPCCPU *first_ppc_cpu = POWERPC_CPU(first_cpu); - int64_t tb_off_adj, tb_off, ns_diff; - int64_t migration_duration_ns, migration_duration_tb, guest_tb, host_ns; + int64_t tb_off_adj, tb_off; unsigned long freq; if (!first_ppc_cpu->env.tb_env) { error_report("No timebase object"); - return -1; + return; } freq = first_ppc_cpu->env.tb_env->tb_freq; - /* - * Calculate timebase on the destination side of migration. - * The destination timebase must be not less than the source timebase. - * We try to adjust timebase by downtime if host clocks are not - * too much out of sync (1 second for now). - */ - host_ns = qemu_clock_get_ns(QEMU_CLOCK_HOST); - ns_diff = MAX(0, host_ns - tb_remote->time_of_the_day_ns); - migration_duration_ns = MIN(NANOSECONDS_PER_SECOND, ns_diff); - migration_duration_tb = muldiv64(freq, migration_duration_ns, - NANOSECONDS_PER_SECOND); - guest_tb = tb_remote->guest_timebase + MIN(0, migration_duration_tb); - tb_off_adj = guest_tb - cpu_get_host_ticks(); + tb_off_adj = tb->guest_timebase - cpu_get_host_ticks(); tb_off = first_ppc_cpu->env.tb_env->tb_offset; trace_ppc_tb_adjust(tb_off, tb_off_adj, tb_off_adj - tb_off, @@ -904,9 +890,44 @@ static int timebase_post_load(void *opaque, int version_id) CPU_FOREACH(cpu) { PowerPCCPU *pcpu = POWERPC_CPU(cpu); pcpu->env.tb_env->tb_offset = tb_off_adj; +#if defined(CONFIG_KVM) + kvm_set_one_reg(cpu, KVM_REG_PPC_TB_OFFSET, + &pcpu->env.tb_env->tb_offset); +#endif } +} - return 0; +void cpu_ppc_clock_vm_state_change(void *opaque, int running, + RunState state) +{ + PPCTimebase *tb = opaque; + + if (running) { + timebase_load(tb); + } else { + timebase_save(tb); + } +} + +/* + * When migrating, read the clock just before migration, + * so that the guest clock counts during the events + * between: + * + * * vm_stop() + * * + * * pre_save() + * + * This reduces clock difference on migration from 5s + * to 0.1s (when max_downtime == 5s), because sending the + * final pages of memory (which happens between vm_stop() + * and pre_save()) takes max_downtime. + */ +static void timebase_pre_save(void *opaque) +{ + PPCTimebase *tb = opaque; + + timebase_save(tb); } const VMStateDescription vmstate_ppc_timebase = { @@ -915,7 +936,6 @@ const VMStateDescription vmstate_ppc_timebase = { .minimum_version_id = 1, .minimum_version_id_old = 1, .pre_save = timebase_pre_save, - .post_load = timebase_post_load, .fields = (VMStateField []) { VMSTATE_UINT64(guest_timebase, PPCTimebase), VMSTATE_INT64(time_of_the_day_ns, PPCTimebase), @@ -950,13 +970,6 @@ clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq) } /* Specific helpers for POWER & PowerPC 601 RTC */ -#if 0 -static clk_setup_cb cpu_ppc601_rtc_init (CPUPPCState *env) -{ - return cpu_ppc_tb_init(env, 7812500); -} -#endif - void cpu_ppc601_store_rtcu (CPUPPCState *env, uint32_t value) { _cpu_ppc_store_tbu(env, value); diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index 5c535b18a2..9d997bf743 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -193,6 +193,12 @@ static void bamboo_init(MachineState *machine) } env = &cpu->env; + if (env->mmu_model != POWERPC_MMU_BOOKE) { + fprintf(stderr, "MMU model %i not supported by this machine.\n", + env->mmu_model); + exit(1); + } + qemu_register_reset(main_cpu_reset, cpu); ppc_booke_timers_init(cpu, 400000000, 0); ppc_dcr_init(env, NULL, NULL); diff --git a/hw/ppc/ppc_booke.c b/hw/ppc/ppc_booke.c index ab8d026c32..60baffaf1d 100644 --- a/hw/ppc/ppc_booke.c +++ b/hw/ppc/ppc_booke.c @@ -198,8 +198,12 @@ static void booke_decr_cb(void *opaque) booke_update_irq(cpu); if (env->spr[SPR_BOOKE_TCR] & TCR_ARE) { - /* Auto Reload */ - cpu_ppc_store_decr(env, env->spr[SPR_BOOKE_DECAR]); + /* Do not reload 0, it is already there. It would just trigger + * the timer again and lead to infinite loop */ + if (env->spr[SPR_BOOKE_DECAR] != 0) { + /* Auto Reload */ + cpu_ppc_store_decr(env, env->spr[SPR_BOOKE_DECAR]); + } } } diff --git a/hw/ppc/ppce500_spin.c b/hw/ppc/ppce500_spin.c index eb219abdff..69ca2d0e42 100644 --- a/hw/ppc/ppce500_spin.c +++ b/hw/ppc/ppce500_spin.c @@ -54,9 +54,9 @@ typedef struct SpinState { SpinInfo spin[MAX_CPUS]; } SpinState; -static void spin_reset(void *opaque) +static void spin_reset(DeviceState *dev) { - SpinState *s = opaque; + SpinState *s = E500_SPIN(dev); int i; for (i = 0; i < MAX_CPUS; i++) { @@ -174,30 +174,28 @@ static const MemoryRegionOps spin_rw_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static int ppce500_spin_initfn(SysBusDevice *dev) +static void ppce500_spin_initfn(Object *obj) { + SysBusDevice *dev = SYS_BUS_DEVICE(obj); SpinState *s = E500_SPIN(dev); - memory_region_init_io(&s->iomem, OBJECT(s), &spin_rw_ops, s, + memory_region_init_io(&s->iomem, obj, &spin_rw_ops, s, "e500 spin pv device", sizeof(SpinInfo) * MAX_CPUS); sysbus_init_mmio(dev, &s->iomem); - - qemu_register_reset(spin_reset, s); - - return 0; } static void ppce500_spin_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); - k->init = ppce500_spin_initfn; + dc->reset = spin_reset; } static const TypeInfo ppce500_spin_info = { .name = TYPE_E500_SPIN, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SpinState), + .instance_init = ppce500_spin_initfn, .class_init = ppce500_spin_class_init, }; diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 054af1e8b4..ca7959c126 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -2,6 +2,7 @@ * QEMU PPC PREP hardware System Emulator * * Copyright (c) 2003-2007 Jocelyn Mayer + * Copyright (c) 2017 Hervé Poussineau * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -43,17 +44,21 @@ #include "hw/isa/pc87312.h" #include "sysemu/block-backend.h" #include "sysemu/arch_init.h" +#include "sysemu/kvm.h" #include "sysemu/qtest.h" #include "exec/address-spaces.h" #include "trace.h" #include "elf.h" #include "qemu/cutils.h" +#include "kvm_ppc.h" /* SMP is not enabled, for now */ #define MAX_CPUS 1 #define MAX_IDE_BUS 2 +#define CFG_ADDR 0xf0000510 + #define BIOS_SIZE (1024 * 1024) #define BIOS_FILENAME "ppc_rom.bin" #define KERNEL_LOAD_ADDR 0x01000000 @@ -316,6 +321,12 @@ static uint32_t PREP_io_800_readb (void *opaque, uint32_t addr) #define NVRAM_SIZE 0x2000 +static void fw_cfg_boot_set(void *opaque, const char *boot_device, + Error **errp) +{ + fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); +} + static void ppc_prep_reset(void *opaque) { PowerPCCPU *cpu = opaque; @@ -339,13 +350,13 @@ static PortioList prep_port_list; /* NVRAM helpers */ static inline uint32_t nvram_read(Nvram *nvram, uint32_t addr) { - NvramClass *k = NVRAM_GET_CLASS(sysctrl->nvram); + NvramClass *k = NVRAM_GET_CLASS(nvram); return (k->read)(nvram, addr); } static inline void nvram_write(Nvram *nvram, uint32_t addr, uint32_t val) { - NvramClass *k = NVRAM_GET_CLASS(sysctrl->nvram); + NvramClass *k = NVRAM_GET_CLASS(nvram); (k->write)(nvram, addr, val); } @@ -677,4 +688,223 @@ static void prep_machine_init(MachineClass *mc) mc->default_boot_order = "cad"; } +static int prep_set_cmos_checksum(DeviceState *dev, void *opaque) +{ + uint16_t checksum = *(uint16_t *)opaque; + ISADevice *rtc; + + if (object_dynamic_cast(OBJECT(dev), "mc146818rtc")) { + rtc = ISA_DEVICE(dev); + rtc_set_memory(rtc, 0x2e, checksum & 0xff); + rtc_set_memory(rtc, 0x3e, checksum & 0xff); + rtc_set_memory(rtc, 0x2f, checksum >> 8); + rtc_set_memory(rtc, 0x3f, checksum >> 8); + } + return 0; +} + +static void ibm_40p_init(MachineState *machine) +{ + CPUPPCState *env = NULL; + uint16_t cmos_checksum; + PowerPCCPU *cpu; + DeviceState *dev; + SysBusDevice *pcihost; + Nvram *m48t59 = NULL; + PCIBus *pci_bus; + ISABus *isa_bus; + void *fw_cfg; + int i; + uint32_t kernel_base = 0, initrd_base = 0; + long kernel_size = 0, initrd_size = 0; + char boot_device; + + /* init CPU */ + if (!machine->cpu_model) { + machine->cpu_model = "604"; + } + cpu = cpu_ppc_init(machine->cpu_model); + if (!cpu) { + error_report("could not initialize CPU '%s'", + machine->cpu_model); + exit(1); + } + env = &cpu->env; + if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) { + error_report("only 6xx bus is supported on this machine"); + exit(1); + } + + if (env->flags & POWERPC_FLAG_RTC_CLK) { + /* POWER / PowerPC 601 RTC clock frequency is 7.8125 MHz */ + cpu_ppc_tb_init(env, 7812500UL); + } else { + /* Set time-base frequency to 100 Mhz */ + cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL); + } + qemu_register_reset(ppc_prep_reset, cpu); + + /* PCI host */ + dev = qdev_create(NULL, "raven-pcihost"); + if (!bios_name) { + bios_name = BIOS_FILENAME; + } + qdev_prop_set_string(dev, "bios-name", bios_name); + qdev_prop_set_uint32(dev, "elf-machine", PPC_ELF_MACHINE); + pcihost = SYS_BUS_DEVICE(dev); + object_property_add_child(qdev_get_machine(), "raven", OBJECT(dev), NULL); + qdev_init_nofail(dev); + pci_bus = PCI_BUS(qdev_get_child_bus(dev, "pci.0")); + if (!pci_bus) { + error_report("could not create PCI host controller"); + exit(1); + } + + /* PCI -> ISA bridge */ + dev = DEVICE(pci_create_simple(pci_bus, PCI_DEVFN(11, 0), "i82378")); + qdev_connect_gpio_out(dev, 0, + cpu->env.irq_inputs[PPC6xx_INPUT_INT]); + sysbus_connect_irq(pcihost, 0, qdev_get_gpio_in(dev, 15)); + sysbus_connect_irq(pcihost, 1, qdev_get_gpio_in(dev, 13)); + sysbus_connect_irq(pcihost, 2, qdev_get_gpio_in(dev, 15)); + sysbus_connect_irq(pcihost, 3, qdev_get_gpio_in(dev, 13)); + isa_bus = ISA_BUS(qdev_get_child_bus(dev, "isa.0")); + + /* Memory controller */ + dev = DEVICE(isa_create(isa_bus, "rs6000-mc")); + qdev_prop_set_uint32(dev, "ram-size", machine->ram_size); + qdev_init_nofail(dev); + + /* initialize CMOS checksums */ + cmos_checksum = 0x6aa9; + qbus_walk_children(BUS(isa_bus), prep_set_cmos_checksum, NULL, NULL, NULL, + &cmos_checksum); + + /* initialize audio subsystem */ + audio_init(); + + /* add some more devices */ + if (defaults_enabled()) { + isa_create_simple(isa_bus, "i8042"); + m48t59 = NVRAM(isa_create_simple(isa_bus, "isa-m48t59")); + + dev = DEVICE(isa_create(isa_bus, "cs4231a")); + qdev_prop_set_uint32(dev, "iobase", 0x830); + qdev_prop_set_uint32(dev, "irq", 10); + qdev_init_nofail(dev); + + dev = DEVICE(isa_create(isa_bus, "pc87312")); + qdev_prop_set_uint32(dev, "config", 12); + qdev_init_nofail(dev); + + dev = DEVICE(isa_create(isa_bus, "prep-systemio")); + qdev_prop_set_uint32(dev, "ibm-planar-id", 0xfc); + qdev_prop_set_uint32(dev, "equipment", 0xc0); + qdev_init_nofail(dev); + + pci_create_simple(pci_bus, PCI_DEVFN(1, 0), "lsi53c810"); + + /* XXX: s3-trio at PCI_DEVFN(2, 0) */ + pci_vga_init(pci_bus); + + for (i = 0; i < nb_nics; i++) { + pci_nic_init_nofail(&nd_table[i], pci_bus, "pcnet", + i == 0 ? "3" : NULL); + } + } + + /* Prepare firmware configuration for OpenBIOS */ + fw_cfg = fw_cfg_init_mem(CFG_ADDR, CFG_ADDR + 2); + + if (machine->kernel_filename) { + /* load kernel */ + kernel_base = KERNEL_LOAD_ADDR; + kernel_size = load_image_targphys(machine->kernel_filename, + kernel_base, + machine->ram_size - kernel_base); + if (kernel_size < 0) { + error_report("could not load kernel '%s'", + machine->kernel_filename); + exit(1); + } + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, kernel_base); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); + /* load initrd */ + if (machine->initrd_filename) { + initrd_base = INITRD_LOAD_ADDR; + initrd_size = load_image_targphys(machine->initrd_filename, + initrd_base, + machine->ram_size - initrd_base); + if (initrd_size < 0) { + error_report("could not load initial ram disk '%s'", + machine->initrd_filename); + exit(1); + } + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_base); + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); + } + if (machine->kernel_cmdline && *machine->kernel_cmdline) { + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, CMDLINE_ADDR); + pstrcpy_targphys("cmdline", CMDLINE_ADDR, TARGET_PAGE_SIZE, + machine->kernel_cmdline); + fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, + machine->kernel_cmdline); + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, + strlen(machine->kernel_cmdline) + 1); + } + boot_device = 'm'; + } else { + boot_device = machine->boot_order[0]; + } + + fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); + fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)machine->ram_size); + fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, ARCH_PREP); + + fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_WIDTH, graphic_width); + fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_HEIGHT, graphic_height); + fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_DEPTH, graphic_depth); + + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled()); + if (kvm_enabled()) { +#ifdef CONFIG_KVM + uint8_t *hypercall; + + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, kvmppc_get_tbfreq()); + hypercall = g_malloc(16); + kvmppc_get_hypercall(env, hypercall, 16); + fw_cfg_add_bytes(fw_cfg, FW_CFG_PPC_KVM_HC, hypercall, 16); + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid()); +#endif + } else { + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, NANOSECONDS_PER_SECOND); + } + fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, boot_device); + qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); + + /* Prepare firmware configuration for Open Hack'Ware */ + if (m48t59) { + PPC_NVRAM_set_params(m48t59, NVRAM_SIZE, "PREP", ram_size, + boot_device, + kernel_base, kernel_size, + machine->kernel_cmdline, + initrd_base, initrd_size, + /* XXX: need an option to load a NVRAM image */ + 0, + graphic_width, graphic_height, graphic_depth); + } +} + +static void ibm_40p_machine_init(MachineClass *mc) +{ + mc->desc = "IBM RS/6000 7020 (40p)", + mc->init = ibm_40p_init; + mc->max_cpus = 1; + mc->pci_allow_0_address = true; + mc->default_ram_size = 128 * M_BYTE; + mc->block_default_type = IF_SCSI; + mc->default_boot_order = "c"; +} + +DEFINE_MACHINE("40p", ibm_40p_machine_init) DEFINE_MACHINE("prep", prep_machine_init) diff --git a/hw/ppc/prep_systemio.c b/hw/ppc/prep_systemio.c new file mode 100644 index 0000000000..50893ec529 --- /dev/null +++ b/hw/ppc/prep_systemio.c @@ -0,0 +1,303 @@ +/* + * QEMU PReP System I/O emulation + * + * Copyright (c) 2017 Hervé Poussineau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "hw/isa/isa.h" +#include "exec/address-spaces.h" +#include "qemu/error-report.h" /* for error_report() */ +#include "sysemu/sysemu.h" /* for vm_stop() */ +#include "cpu.h" +#include "trace.h" + +#define TYPE_PREP_SYSTEMIO "prep-systemio" +#define PREP_SYSTEMIO(obj) \ + OBJECT_CHECK(PrepSystemIoState, (obj), TYPE_PREP_SYSTEMIO) + +/* Bit as defined in PowerPC Reference Plaform v1.1, sect. 6.1.5, p. 132 */ +#define PREP_BIT(n) (1 << (7 - (n))) + +typedef struct PrepSystemIoState { + ISADevice parent_obj; + MemoryRegion ppc_parity_mem; + + qemu_irq non_contiguous_io_map_irq; + uint8_t sreset; /* 0x0092 */ + uint8_t equipment; /* 0x080c */ + uint8_t system_control; /* 0x081c */ + uint8_t iomap_type; /* 0x0850 */ + uint8_t ibm_planar_id; /* 0x0852 */ + qemu_irq softreset_irq; + PortioList portio; +} PrepSystemIoState; + +/* PORT 0092 -- Special Port 92 (Read/Write) */ + +enum { + PORT0092_SOFTRESET = PREP_BIT(7), + PORT0092_LE_MODE = PREP_BIT(6), +}; + +static void prep_port0092_write(void *opaque, uint32_t addr, uint32_t val) +{ + PrepSystemIoState *s = opaque; + + trace_prep_systemio_write(addr, val); + + s->sreset = val & PORT0092_SOFTRESET; + qemu_set_irq(s->softreset_irq, s->sreset); + + if ((val & PORT0092_LE_MODE) != 0) { + /* XXX Not supported yet */ + error_report("little-endian mode not supported"); + vm_stop(RUN_STATE_PAUSED); + } else { + /* Nothing to do */ + } +} + +static uint32_t prep_port0092_read(void *opaque, uint32_t addr) +{ + PrepSystemIoState *s = opaque; + trace_prep_systemio_read(addr, s->sreset); + return s->sreset; +} + +/* PORT 0808 -- Hardfile Light Register (Write Only) */ + +enum { + PORT0808_HARDFILE_LIGHT_ON = PREP_BIT(7), +}; + +static void prep_port0808_write(void *opaque, uint32_t addr, uint32_t val) +{ + trace_prep_systemio_write(addr, val); +} + +/* PORT 0810 -- Password Protect 1 Register (Write Only) */ + +/* reset by port 0x4D in the SIO */ +static void prep_port0810_write(void *opaque, uint32_t addr, uint32_t val) +{ + trace_prep_systemio_write(addr, val); +} + +/* PORT 0812 -- Password Protect 2 Register (Write Only) */ + +/* reset by port 0x4D in the SIO */ +static void prep_port0812_write(void *opaque, uint32_t addr, uint32_t val) +{ + trace_prep_systemio_write(addr, val); +} + +/* PORT 0814 -- L2 Invalidate Register (Write Only) */ + +static void prep_port0814_write(void *opaque, uint32_t addr, uint32_t val) +{ + trace_prep_systemio_write(addr, val); +} + +/* PORT 0818 -- Reserved for Keylock (Read Only) */ + +enum { + PORT0818_KEYLOCK_SIGNAL_HIGH = PREP_BIT(7), +}; + +static uint32_t prep_port0818_read(void *opaque, uint32_t addr) +{ + uint32_t val = 0; + trace_prep_systemio_read(addr, val); + return val; +} + +/* PORT 080C -- Equipment */ + +enum { + PORT080C_SCSIFUSE = PREP_BIT(1), + PORT080C_L2_COPYBACK = PREP_BIT(4), + PORT080C_L2_256 = PREP_BIT(5), + PORT080C_UPGRADE_CPU = PREP_BIT(6), + PORT080C_L2 = PREP_BIT(7), +}; + +static uint32_t prep_port080c_read(void *opaque, uint32_t addr) +{ + PrepSystemIoState *s = opaque; + trace_prep_systemio_read(addr, s->equipment); + return s->equipment; +} + +/* PORT 081C -- System Control Register (Read/Write) */ + +enum { + PORT081C_FLOPPY_MOTOR_INHIBIT = PREP_BIT(3), + PORT081C_MASK_TEA = PREP_BIT(2), + PORT081C_L2_UPDATE_INHIBIT = PREP_BIT(1), + PORT081C_L2_CACHEMISS_INHIBIT = PREP_BIT(0), +}; + +static void prep_port081c_write(void *opaque, uint32_t addr, uint32_t val) +{ + static const uint8_t mask = PORT081C_FLOPPY_MOTOR_INHIBIT | + PORT081C_MASK_TEA | + PORT081C_L2_UPDATE_INHIBIT | + PORT081C_L2_CACHEMISS_INHIBIT; + PrepSystemIoState *s = opaque; + trace_prep_systemio_write(addr, val); + s->system_control = val & mask; +} + +static uint32_t prep_port081c_read(void *opaque, uint32_t addr) +{ + PrepSystemIoState *s = opaque; + trace_prep_systemio_read(addr, s->system_control); + return s->system_control; +} + +/* System Board Identification */ + +static uint32_t prep_port0852_read(void *opaque, uint32_t addr) +{ + PrepSystemIoState *s = opaque; + trace_prep_systemio_read(addr, s->ibm_planar_id); + return s->ibm_planar_id; +} + +/* PORT 0850 -- I/O Map Type Register (Read/Write) */ + +enum { + PORT0850_IOMAP_NONCONTIGUOUS = PREP_BIT(7), +}; + +static uint32_t prep_port0850_read(void *opaque, uint32_t addr) +{ + PrepSystemIoState *s = opaque; + trace_prep_systemio_read(addr, s->iomap_type); + return s->iomap_type; +} + +static void prep_port0850_write(void *opaque, uint32_t addr, uint32_t val) +{ + PrepSystemIoState *s = opaque; + + trace_prep_systemio_write(addr, val); + qemu_set_irq(s->non_contiguous_io_map_irq, + val & PORT0850_IOMAP_NONCONTIGUOUS); + s->iomap_type = val & PORT0850_IOMAP_NONCONTIGUOUS; +} + +static const MemoryRegionPortio ppc_io800_port_list[] = { + { 0x092, 1, 1, .read = prep_port0092_read, + .write = prep_port0092_write, }, + { 0x808, 1, 1, .write = prep_port0808_write, }, + { 0x80c, 1, 1, .read = prep_port080c_read, }, + { 0x810, 1, 1, .write = prep_port0810_write, }, + { 0x812, 1, 1, .write = prep_port0812_write, }, + { 0x814, 1, 1, .write = prep_port0814_write, }, + { 0x818, 1, 1, .read = prep_port0818_read }, + { 0x81c, 1, 1, .read = prep_port081c_read, + .write = prep_port081c_write, }, + { 0x850, 1, 1, .read = prep_port0850_read, + .write = prep_port0850_write, }, + { 0x852, 1, 1, .read = prep_port0852_read, }, + PORTIO_END_OF_LIST() +}; + +static uint64_t ppc_parity_error_readl(void *opaque, hwaddr addr, + unsigned int size) +{ + uint32_t val = 0; + trace_prep_systemio_read((unsigned int)addr, val); + return val; +} + +static const MemoryRegionOps ppc_parity_error_ops = { + .read = ppc_parity_error_readl, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void prep_systemio_realize(DeviceState *dev, Error **errp) +{ + ISADevice *isa = ISA_DEVICE(dev); + PrepSystemIoState *s = PREP_SYSTEMIO(dev); + PowerPCCPU *cpu; + + qdev_init_gpio_out(dev, &s->non_contiguous_io_map_irq, 1); + s->iomap_type = PORT0850_IOMAP_NONCONTIGUOUS; + qemu_set_irq(s->non_contiguous_io_map_irq, + s->iomap_type & PORT0850_IOMAP_NONCONTIGUOUS); + cpu = POWERPC_CPU(first_cpu); + s->softreset_irq = cpu->env.irq_inputs[PPC6xx_INPUT_HRESET]; + + isa_register_portio_list(isa, &s->portio, 0x0, ppc_io800_port_list, s, + "systemio800"); + + memory_region_init_io(&s->ppc_parity_mem, OBJECT(dev), + &ppc_parity_error_ops, s, "ppc-parity", 0x4); + memory_region_add_subregion(get_system_memory(), 0xbfffeff0, + &s->ppc_parity_mem); +} + +static const VMStateDescription vmstate_prep_systemio = { + .name = "prep_systemio", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(sreset, PrepSystemIoState), + VMSTATE_UINT8(system_control, PrepSystemIoState), + VMSTATE_UINT8(iomap_type, PrepSystemIoState), + VMSTATE_END_OF_LIST() + }, +}; + +static Property prep_systemio_properties[] = { + DEFINE_PROP_UINT8("ibm-planar-id", PrepSystemIoState, ibm_planar_id, 0), + DEFINE_PROP_UINT8("equipment", PrepSystemIoState, equipment, 0), + DEFINE_PROP_END_OF_LIST() +}; + +static void prep_systemio_class_initfn(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = prep_systemio_realize; + dc->vmsd = &vmstate_prep_systemio; + dc->props = prep_systemio_properties; +} + +static TypeInfo prep_systemio800_info = { + .name = TYPE_PREP_SYSTEMIO, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(PrepSystemIoState), + .class_init = prep_systemio_class_initfn, +}; + +static void prep_systemio_register_types(void) +{ + type_register_static(&prep_systemio800_info); +} + +type_init(prep_systemio_register_types) diff --git a/hw/ppc/rs6000_mc.c b/hw/ppc/rs6000_mc.c new file mode 100644 index 0000000000..b6135650bd --- /dev/null +++ b/hw/ppc/rs6000_mc.c @@ -0,0 +1,232 @@ +/* + * QEMU RS/6000 memory controller + * + * Copyright (c) 2017 Hervé Poussineau + * + * 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) version 3 or 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 . + */ + +#include "qemu/osdep.h" +#include "hw/isa/isa.h" +#include "exec/address-spaces.h" +#include "hw/boards.h" +#include "qapi/error.h" +#include "trace.h" + +#define TYPE_RS6000MC "rs6000-mc" +#define RS6000MC_DEVICE(obj) \ + OBJECT_CHECK(RS6000MCState, (obj), TYPE_RS6000MC) + +typedef struct RS6000MCState { + ISADevice parent_obj; + /* see US patent 5,684,979 for details (expired 2001-11-04) */ + uint32_t ram_size; + bool autoconfigure; + MemoryRegion simm[6]; + unsigned int simm_size[6]; + uint32_t end_address[8]; + uint8_t port0820_index; + PortioList portio; +} RS6000MCState; + +/* P0RT 0803 -- SIMM ID Register (32/8 MB) (Read Only) */ + +static uint32_t rs6000mc_port0803_read(void *opaque, uint32_t addr) +{ + RS6000MCState *s = opaque; + uint32_t val = 0; + int socket; + + /* (1 << socket) indicates 32 MB SIMM at given socket */ + for (socket = 0; socket < 6; socket++) { + if (s->simm_size[socket] == 32) { + val |= (1 << socket); + } + } + + trace_rs6000mc_id_read(addr, val); + return val; +} + +/* PORT 0804 -- SIMM Presence Register (Read Only) */ + +static uint32_t rs6000mc_port0804_read(void *opaque, uint32_t addr) +{ + RS6000MCState *s = opaque; + uint32_t val = 0xff; + int socket; + + /* (1 << socket) indicates SIMM absence at given socket */ + for (socket = 0; socket < 6; socket++) { + if (s->simm_size[socket]) { + val &= ~(1 << socket); + } + } + s->port0820_index = 0; + + trace_rs6000mc_presence_read(addr, val); + return val; +} + +/* Memory Controller Size Programming Register */ + +static uint32_t rs6000mc_port0820_read(void *opaque, uint32_t addr) +{ + RS6000MCState *s = opaque; + uint32_t val = s->end_address[s->port0820_index] & 0x1f; + s->port0820_index = (s->port0820_index + 1) & 7; + trace_rs6000mc_size_read(addr, val); + return val; +} + +static void rs6000mc_port0820_write(void *opaque, uint32_t addr, uint32_t val) +{ + RS6000MCState *s = opaque; + uint8_t socket = val >> 5; + uint32_t end_address = val & 0x1f; + + trace_rs6000mc_size_write(addr, val); + s->end_address[socket] = end_address; + if (socket > 0 && socket < 7) { + if (s->simm_size[socket - 1]) { + uint32_t size; + uint32_t start_address = 0; + if (socket > 1) { + start_address = s->end_address[socket - 1]; + } + + size = end_address - start_address; + memory_region_set_enabled(&s->simm[socket - 1], size != 0); + memory_region_set_address(&s->simm[socket - 1], + start_address * 8 * 1024 * 1024); + } + } +} + +/* Read Memory Parity Error */ + +enum { + PORT0841_NO_ERROR_DETECTED = 0x01, +}; + +static uint32_t rs6000mc_port0841_read(void *opaque, uint32_t addr) +{ + uint32_t val = PORT0841_NO_ERROR_DETECTED; + trace_rs6000mc_parity_read(addr, val); + return val; +} + +static const MemoryRegionPortio rs6000mc_port_list[] = { + { 0x803, 1, 1, .read = rs6000mc_port0803_read }, + { 0x804, 1, 1, .read = rs6000mc_port0804_read }, + { 0x820, 1, 1, .read = rs6000mc_port0820_read, + .write = rs6000mc_port0820_write, }, + { 0x841, 1, 1, .read = rs6000mc_port0841_read }, + PORTIO_END_OF_LIST() +}; + +static void rs6000mc_realize(DeviceState *dev, Error **errp) +{ + RS6000MCState *s = RS6000MC_DEVICE(dev); + int socket = 0; + unsigned int ram_size = s->ram_size / (1024 * 1024); + + while (socket < 6) { + if (ram_size >= 64) { + s->simm_size[socket] = 32; + s->simm_size[socket + 1] = 32; + ram_size -= 64; + } else if (ram_size >= 16) { + s->simm_size[socket] = 8; + s->simm_size[socket + 1] = 8; + ram_size -= 16; + } else { + /* Not enough memory */ + break; + } + socket += 2; + } + + for (socket = 0; socket < 6; socket++) { + if (s->simm_size[socket]) { + char name[] = "simm.?"; + name[5] = socket + '0'; + memory_region_allocate_system_memory(&s->simm[socket], OBJECT(dev), + name, s->simm_size[socket] + * 1024 * 1024); + memory_region_add_subregion_overlap(get_system_memory(), 0, + &s->simm[socket], socket); + } + } + if (ram_size) { + /* unable to push all requested RAM in SIMMs */ + error_setg(errp, "RAM size incompatible with this board. " + "Try again with something else, like %d MB", + s->ram_size / 1024 / 1024 - ram_size); + return; + } + + if (s->autoconfigure) { + uint32_t start_address = 0; + for (socket = 0; socket < 6; socket++) { + if (s->simm_size[socket]) { + memory_region_set_enabled(&s->simm[socket], true); + memory_region_set_address(&s->simm[socket], start_address); + start_address += memory_region_size(&s->simm[socket]); + } + } + } + + isa_register_portio_list(ISA_DEVICE(dev), &s->portio, 0x0, + rs6000mc_port_list, s, "rs6000mc"); +} + +static const VMStateDescription vmstate_rs6000mc = { + .name = "rs6000-mc", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(port0820_index, RS6000MCState), + VMSTATE_END_OF_LIST() + }, +}; + +static Property rs6000mc_properties[] = { + DEFINE_PROP_UINT32("ram-size", RS6000MCState, ram_size, 0), + DEFINE_PROP_BOOL("auto-configure", RS6000MCState, autoconfigure, true), + DEFINE_PROP_END_OF_LIST() +}; + +static void rs6000mc_class_initfn(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = rs6000mc_realize; + dc->vmsd = &vmstate_rs6000mc; + dc->props = rs6000mc_properties; +} + +static const TypeInfo rs6000mc_info = { + .name = TYPE_RS6000MC, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(RS6000MCState), + .class_init = rs6000mc_class_initfn, +}; + +static void rs6000mc_types(void) +{ + type_register_static(&rs6000mc_info); +} + +type_init(rs6000mc_types) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 45b7d99821..e465d7ac98 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -148,8 +148,8 @@ static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu, uint32_t gservers_prop[smt_threads * 2]; int index = ppc_get_vcpu_dt_id(cpu); - if (cpu->cpu_version) { - ret = fdt_setprop_cell(fdt, offset, "cpu-version", cpu->cpu_version); + if (cpu->compat_pvr) { + ret = fdt_setprop_cell(fdt, offset, "cpu-version", cpu->compat_pvr); if (ret < 0) { return ret; } @@ -206,6 +206,7 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr) PowerPCCPU *cpu = POWERPC_CPU(cs); DeviceClass *dc = DEVICE_GET_CLASS(cs); int index = ppc_get_vcpu_dt_id(cpu); + int compat_smt = MIN(smp_threads, ppc_compat_max_threads(cpu)); if ((index % smt) != 0) { continue; @@ -240,8 +241,7 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr) return ret; } - ret = spapr_fixup_cpu_smt_dt(fdt, offset, cpu, - ppc_get_compat_smt_threads(cpu)); + ret = spapr_fixup_cpu_smt_dt(fdt, offset, cpu, compat_smt); if (ret < 0) { return ret; } @@ -407,6 +407,7 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, size_t page_sizes_prop_size; uint32_t vcpus_per_socket = smp_threads * smp_cores; uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)}; + int compat_smt = MIN(smp_threads, ppc_compat_max_threads(cpu)); sPAPRDRConnector *drc; sPAPRDRConnectorClass *drck; int drc_index; @@ -494,8 +495,7 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, _FDT(spapr_fixup_cpu_numa_dt(fdt, offset, cs)); - _FDT(spapr_fixup_cpu_smt_dt(fdt, offset, cpu, - ppc_get_compat_smt_threads(cpu))); + _FDT(spapr_fixup_cpu_smt_dt(fdt, offset, cpu, compat_smt)); } static void spapr_populate_cpus_dt_node(void *fdt, sPAPRMachineState *spapr) @@ -685,7 +685,6 @@ out: int spapr_h_cas_compose_response(sPAPRMachineState *spapr, target_ulong addr, target_ulong size, - bool cpu_update, sPAPROptionVector *ov5_updates) { void *fdt, *fdt_skel; @@ -704,9 +703,7 @@ int spapr_h_cas_compose_response(sPAPRMachineState *spapr, g_free(fdt_skel); /* Fixup cpu nodes */ - if (cpu_update) { - _FDT((spapr_fixup_cpu_dt(fdt, spapr))); - } + _FDT((spapr_fixup_cpu_dt(fdt, spapr))); if (spapr_dt_cas_updates(spapr, fdt, ov5_updates)) { return -1; @@ -1008,7 +1005,8 @@ static uint64_t translate_kernel_address(void *opaque, uint64_t addr) return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR; } -static void emulate_spapr_hypercall(PowerPCCPU *cpu) +static void emulate_spapr_hypercall(PPCVirtualHypervisor *vhyp, + PowerPCCPU *cpu) { CPUPPCState *env = &cpu->env; @@ -1753,11 +1751,80 @@ static void spapr_validate_node_memory(MachineState *machine, Error **errp) } } +static void spapr_init_cpus(sPAPRMachineState *spapr) +{ + MachineState *machine = MACHINE(spapr); + MachineClass *mc = MACHINE_GET_CLASS(machine); + char *type = spapr_get_cpu_core_type(machine->cpu_model); + int smt = kvmppc_smt_threads(); + int spapr_max_cores, spapr_cores; + int i; + + if (!type) { + error_report("Unable to find sPAPR CPU Core definition"); + exit(1); + } + + if (mc->query_hotpluggable_cpus) { + if (smp_cpus % smp_threads) { + error_report("smp_cpus (%u) must be multiple of threads (%u)", + smp_cpus, smp_threads); + exit(1); + } + if (max_cpus % smp_threads) { + error_report("max_cpus (%u) must be multiple of threads (%u)", + max_cpus, smp_threads); + exit(1); + } + + spapr_max_cores = max_cpus / smp_threads; + spapr_cores = smp_cpus / smp_threads; + } else { + if (max_cpus != smp_cpus) { + error_report("This machine version does not support CPU hotplug"); + exit(1); + } + + spapr_max_cores = QEMU_ALIGN_UP(smp_cpus, smp_threads) / smp_threads; + spapr_cores = spapr_max_cores; + } + + spapr->cores = g_new0(Object *, spapr_max_cores); + for (i = 0; i < spapr_max_cores; i++) { + int core_id = i * smp_threads; + + if (mc->query_hotpluggable_cpus) { + sPAPRDRConnector *drc = + spapr_dr_connector_new(OBJECT(spapr), + SPAPR_DR_CONNECTOR_TYPE_CPU, + (core_id / smp_threads) * smt); + + qemu_register_reset(spapr_drc_reset, drc); + } + + if (i < spapr_cores) { + Object *core = object_new(type); + int nr_threads = smp_threads; + + /* Handle the partially filled core for older machine types */ + if ((i + 1) * smp_threads >= smp_cpus) { + nr_threads = smp_cpus - i * smp_threads; + } + + object_property_set_int(core, nr_threads, "nr-threads", + &error_fatal); + object_property_set_int(core, core_id, CPU_CORE_PROP_CORE_ID, + &error_fatal); + object_property_set_bool(core, true, "realized", &error_fatal); + } + } + g_free(type); +} + /* pSeries LPAR / sPAPR hardware init */ static void ppc_spapr_init(MachineState *machine) { sPAPRMachineState *spapr = SPAPR_MACHINE(machine); - MachineClass *mc = MACHINE_GET_CLASS(machine); sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); const char *kernel_filename = machine->kernel_filename; const char *initrd_filename = machine->initrd_filename; @@ -1772,28 +1839,11 @@ static void ppc_spapr_init(MachineState *machine) long load_limit, fw_size; char *filename; int smt = kvmppc_smt_threads(); - int spapr_cores = smp_cpus / smp_threads; - int spapr_max_cores = max_cpus / smp_threads; - - if (mc->query_hotpluggable_cpus) { - if (smp_cpus % smp_threads) { - error_report("smp_cpus (%u) must be multiple of threads (%u)", - smp_cpus, smp_threads); - exit(1); - } - if (max_cpus % smp_threads) { - error_report("max_cpus (%u) must be multiple of threads (%u)", - max_cpus, smp_threads); - exit(1); - } - } msi_nonbroken = true; QLIST_INIT(&spapr->phbs); - cpu_ppc_hypercall = emulate_spapr_hypercall; - /* Allocate RMA if necessary */ rma_alloc_size = kvmppc_alloc_rma(&rma); @@ -1866,44 +1916,7 @@ static void ppc_spapr_init(MachineState *machine) ppc_cpu_parse_features(machine->cpu_model); - if (mc->query_hotpluggable_cpus) { - char *type = spapr_get_cpu_core_type(machine->cpu_model); - - if (type == NULL) { - error_report("Unable to find sPAPR CPU Core definition"); - exit(1); - } - - spapr->cores = g_new0(Object *, spapr_max_cores); - for (i = 0; i < spapr_max_cores; i++) { - int core_id = i * smp_threads; - sPAPRDRConnector *drc = - spapr_dr_connector_new(OBJECT(spapr), - SPAPR_DR_CONNECTOR_TYPE_CPU, - (core_id / smp_threads) * smt); - - qemu_register_reset(spapr_drc_reset, drc); - - if (i < spapr_cores) { - Object *core = object_new(type); - object_property_set_int(core, smp_threads, "nr-threads", - &error_fatal); - object_property_set_int(core, core_id, CPU_CORE_PROP_CORE_ID, - &error_fatal); - object_property_set_bool(core, true, "realized", &error_fatal); - } - } - g_free(type); - } else { - for (i = 0; i < smp_cpus; i++) { - PowerPCCPU *cpu = cpu_ppc_init(machine->cpu_model); - if (cpu == NULL) { - error_report("Unable to find PowerPC CPU definition"); - exit(1); - } - spapr_cpu_init(spapr, cpu, &error_fatal); - } - } + spapr_init_cpus(spapr); if (kvm_enabled()) { /* Enable H_LOGICAL_CI_* so SLOF can talk to in-kernel devices */ @@ -2116,6 +2129,12 @@ static void ppc_spapr_init(MachineState *machine) qemu_register_reset(spapr_ccs_reset_hook, spapr); qemu_register_boot_set(spapr_boot_set, spapr); + + /* to stop and start vmclock */ + if (kvm_enabled()) { + qemu_add_vm_change_state_handler(cpu_ppc_clock_vm_state_change, + &spapr->tb); + } } static int spapr_kvm_type(const char *vm_type) @@ -2185,6 +2204,19 @@ static char *spapr_get_fw_dev_path(FWPathProvider *p, BusState *bus, } } + /* + * SLOF probes the USB devices, and if it recognizes that the device is a + * storage device, it changes its name to "storage" instead of "usb-host", + * and additionally adds a child node for the SCSI LUN, so the correct + * boot path in SLOF is something like .../storage@1/disk@xxx" instead. + */ + if (strcmp("usb-host", qdev_fw_name(dev)) == 0) { + USBDevice *usbdev = CAST(USBDevice, dev, TYPE_USB_DEVICE); + if (usb_host_dev_is_scsi_storage(usbdev)) { + return g_strdup_printf("storage@%s/disk", usbdev->port->path); + } + } + if (phb) { /* Replace "pci" with "pci@800000020000000" */ return g_strdup_printf("pci@%"PRIX64, phb->buid); @@ -2252,7 +2284,7 @@ static void spapr_machine_finalizefn(Object *obj) g_free(spapr->kvm_type); } -static void ppc_cpu_do_nmi_on_cpu(CPUState *cs, run_on_cpu_data arg) +void spapr_do_system_reset_on_cpu(CPUState *cs, run_on_cpu_data arg) { cpu_synchronize_state(cs); ppc_cpu_do_system_reset(cs); @@ -2263,7 +2295,7 @@ static void spapr_nmi(NMIState *n, int cpu_index, Error **errp) CPUState *cs; CPU_FOREACH(cs) { - async_run_on_cpu(cs, ppc_cpu_do_nmi_on_cpu, RUN_ON_CPU_NULL); + async_run_on_cpu(cs, spapr_do_system_reset_on_cpu, RUN_ON_CPU_NULL); } } @@ -2668,6 +2700,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc); NMIClass *nc = NMI_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); + PPCVirtualHypervisorClass *vhc = PPC_VIRTUAL_HYPERVISOR_CLASS(oc); mc->desc = "pSeries Logical Partition (PAPR compliant)"; @@ -2699,6 +2732,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) fwc->get_dev_path = spapr_get_fw_dev_path; nc->nmi_monitor_handler = spapr_nmi; smc->phb_placement = spapr_phb_placement; + vhc->hypercall = emulate_spapr_hypercall; } static const TypeInfo spapr_machine_info = { @@ -2714,6 +2748,7 @@ static const TypeInfo spapr_machine_info = { { TYPE_FW_PATH_PROVIDER }, { TYPE_NMI }, { TYPE_HOTPLUG_HANDLER }, + { TYPE_PPC_VIRTUAL_HYPERVISOR }, { } }, }; @@ -2747,18 +2782,37 @@ static const TypeInfo spapr_machine_info = { type_init(spapr_machine_register_##suffix) /* - * pseries-2.8 + * pseries-2.9 */ -static void spapr_machine_2_8_instance_options(MachineState *machine) +static void spapr_machine_2_9_instance_options(MachineState *machine) { } -static void spapr_machine_2_8_class_options(MachineClass *mc) +static void spapr_machine_2_9_class_options(MachineClass *mc) { /* Defaults for the latest behaviour inherited from the base class */ } -DEFINE_SPAPR_MACHINE(2_8, "2.8", true); +DEFINE_SPAPR_MACHINE(2_9, "2.9", true); + +/* + * pseries-2.8 + */ +#define SPAPR_COMPAT_2_8 \ + HW_COMPAT_2_8 + +static void spapr_machine_2_8_instance_options(MachineState *machine) +{ + spapr_machine_2_9_instance_options(machine); +} + +static void spapr_machine_2_8_class_options(MachineClass *mc) +{ + spapr_machine_2_9_class_options(mc); + SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_8); +} + +DEFINE_SPAPR_MACHINE(2_8, "2.8", false); /* * pseries-2.7 diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index c18632bbff..9dddaeb3fa 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -46,7 +46,8 @@ static void spapr_cpu_destroy(PowerPCCPU *cpu) qemu_unregister_reset(spapr_cpu_reset, cpu); } -void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, Error **errp) +static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, + Error **errp) { CPUPPCState *env = &cpu->env; CPUState *cs = CPU(cpu); @@ -56,6 +57,7 @@ void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, Error **errp) cpu_ppc_tb_init(env, SPAPR_TIMEBASE_FREQ); /* Enable PAPR mode in TCG or KVM */ + cpu_ppc_set_vhyp(cpu, PPC_VIRTUAL_HYPERVISOR(spapr)); cpu_ppc_set_papr(cpu); if (cpu->max_compat) { @@ -166,11 +168,11 @@ void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { sPAPRMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); + MachineClass *mc = MACHINE_GET_CLASS(spapr); sPAPRCPUCore *core = SPAPR_CPU_CORE(OBJECT(dev)); CPUCore *cc = CPU_CORE(dev); CPUState *cs = CPU(core->threads); sPAPRDRConnector *drc; - sPAPRDRConnectorClass *drck; Error *local_err = NULL; void *fdt = NULL; int fdt_offset = 0; @@ -180,7 +182,7 @@ void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev, drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_CPU, index * smt); spapr->cores[index] = OBJECT(dev); - g_assert(drc); + g_assert(drc || !mc->query_hotpluggable_cpus); /* * Setup CPU DT entries only for hotplugged CPUs. For boot time or @@ -190,13 +192,15 @@ void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev, fdt = spapr_populate_hotplug_cpu_dt(cs, &fdt_offset, spapr); } - drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - drck->attach(drc, dev, fdt, fdt_offset, !dev->hotplugged, &local_err); - if (local_err) { - g_free(fdt); - spapr->cores[index] = NULL; - error_propagate(errp, local_err); - return; + if (drc) { + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + drck->attach(drc, dev, fdt, fdt_offset, !dev->hotplugged, &local_err); + if (local_err) { + g_free(fdt); + spapr->cores[index] = NULL; + error_propagate(errp, local_err); + return; + } } if (dev->hotplugged) { @@ -209,8 +213,11 @@ void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev, /* * Set the right DRC states for cold plugged CPU. */ - drck->set_allocation_state(drc, SPAPR_DR_ALLOCATION_STATE_USABLE); - drck->set_isolation_state(drc, SPAPR_DR_ISOLATION_STATE_UNISOLATED); + if (drc) { + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + drck->set_allocation_state(drc, SPAPR_DR_ALLOCATION_STATE_USABLE); + drck->set_isolation_state(drc, SPAPR_DR_ISOLATION_STATE_UNISOLATED); + } } } @@ -227,7 +234,7 @@ void spapr_core_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, char *base_core_type = spapr_get_cpu_core_type(machine->cpu_model); const char *type = object_get_typename(OBJECT(dev)); - if (!mc->query_hotpluggable_cpus) { + if (dev->hotplugged && !mc->query_hotpluggable_cpus) { error_setg(&local_err, "CPU hotplug not supported for this machine"); goto out; } @@ -237,11 +244,6 @@ void spapr_core_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, goto out; } - if (cc->nr_threads != smp_threads) { - error_setg(&local_err, "threads must be %d", smp_threads); - goto out; - } - if (cc->core_id % smp_threads) { error_setg(&local_err, "invalid core id %d", cc->core_id); goto out; diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index b2a8e48569..42d20e0b92 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -881,126 +881,103 @@ static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPRMachineState *spapr, return ret; } -typedef struct { - uint32_t cpu_version; - Error *err; -} SetCompatState; +#define H_SIGNAL_SYS_RESET_ALL -1 +#define H_SIGNAL_SYS_RESET_ALLBUTSELF -2 -static void do_set_compat(CPUState *cs, run_on_cpu_data arg) +static target_ulong h_signal_sys_reset(PowerPCCPU *cpu, + sPAPRMachineState *spapr, + target_ulong opcode, target_ulong *args) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - SetCompatState *s = arg.host_ptr; + target_long target = args[0]; + CPUState *cs; - cpu_synchronize_state(cs); - ppc_set_compat(cpu, s->cpu_version, &s->err); -} - -#define get_compat_level(cpuver) ( \ - ((cpuver) == CPU_POWERPC_LOGICAL_2_05) ? 2050 : \ - ((cpuver) == CPU_POWERPC_LOGICAL_2_06) ? 2060 : \ - ((cpuver) == CPU_POWERPC_LOGICAL_2_06_PLUS) ? 2061 : \ - ((cpuver) == CPU_POWERPC_LOGICAL_2_07) ? 2070 : 0) - -static void cas_handle_compat_cpu(PowerPCCPUClass *pcc, uint32_t pvr, - unsigned max_lvl, unsigned *compat_lvl, - unsigned *cpu_version) -{ - unsigned lvl = get_compat_level(pvr); - bool is205, is206, is207; - - if (!lvl) { - return; - } - - /* If it is a logical PVR, try to determine the highest level */ - is205 = (pcc->pcr_supported & PCR_COMPAT_2_05) && - (lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_05)); - is206 = (pcc->pcr_supported & PCR_COMPAT_2_06) && - ((lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_06)) || - (lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_06_PLUS))); - is207 = (pcc->pcr_supported & PCR_COMPAT_2_07) && - (lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_07)); - - if (is205 || is206 || is207) { - if (!max_lvl) { - /* User did not set the level, choose the highest */ - if (*compat_lvl <= lvl) { - *compat_lvl = lvl; - *cpu_version = pvr; - } - } else if (max_lvl >= lvl) { - /* User chose the level, don't set higher than this */ - *compat_lvl = lvl; - *cpu_version = pvr; + if (target < 0) { + /* Broadcast */ + if (target < H_SIGNAL_SYS_RESET_ALLBUTSELF) { + return H_PARAMETER; } + + CPU_FOREACH(cs) { + PowerPCCPU *c = POWERPC_CPU(cs); + + if (target == H_SIGNAL_SYS_RESET_ALLBUTSELF) { + if (c == cpu) { + continue; + } + } + run_on_cpu(cs, spapr_do_system_reset_on_cpu, RUN_ON_CPU_NULL); + } + return H_SUCCESS; + + } else { + /* Unicast */ + CPU_FOREACH(cs) { + if (cpu->cpu_dt_id == target) { + run_on_cpu(cs, spapr_do_system_reset_on_cpu, RUN_ON_CPU_NULL); + return H_SUCCESS; + } + } + return H_PARAMETER; } } -static target_ulong h_client_architecture_support(PowerPCCPU *cpu_, +static target_ulong h_client_architecture_support(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong list = ppc64_phys_to_real(args[0]); target_ulong ov_table; - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu_); - CPUState *cs; - bool cpu_match = false, cpu_update = true; - unsigned old_cpu_version = cpu_->cpu_version; - unsigned compat_lvl = 0, cpu_version = 0; - unsigned max_lvl = get_compat_level(cpu_->max_compat); - int counter; + bool explicit_match = false; /* Matched the CPU's real PVR */ + uint32_t max_compat = cpu->max_compat; + uint32_t best_compat = 0; + int i; sPAPROptionVector *ov5_guest, *ov5_cas_old, *ov5_updates; - /* Parse PVR list */ - for (counter = 0; counter < 512; ++counter) { + /* + * We scan the supplied table of PVRs looking for two things + * 1. Is our real CPU PVR in the list? + * 2. What's the "best" listed logical PVR + */ + for (i = 0; i < 512; ++i) { uint32_t pvr, pvr_mask; pvr_mask = ldl_be_phys(&address_space_memory, list); - list += 4; - pvr = ldl_be_phys(&address_space_memory, list); - list += 4; + pvr = ldl_be_phys(&address_space_memory, list + 4); + list += 8; - trace_spapr_cas_pvr_try(pvr); - if (!max_lvl && - ((cpu_->env.spr[SPR_PVR] & pvr_mask) == (pvr & pvr_mask))) { - cpu_match = true; - cpu_version = 0; - } else if (pvr == cpu_->cpu_version) { - cpu_match = true; - cpu_version = cpu_->cpu_version; - } else if (!cpu_match) { - cas_handle_compat_cpu(pcc, pvr, max_lvl, &compat_lvl, &cpu_version); - } - /* Terminator record */ if (~pvr_mask & pvr) { - break; + break; /* Terminator record */ } - } - /* Parsing finished */ - trace_spapr_cas_pvr(cpu_->cpu_version, cpu_match, - cpu_version, pcc->pcr_mask); - - /* Update CPUs */ - if (old_cpu_version != cpu_version) { - CPU_FOREACH(cs) { - SetCompatState s = { - .cpu_version = cpu_version, - .err = NULL, - }; - - run_on_cpu(cs, do_set_compat, RUN_ON_CPU_HOST_PTR(&s)); - - if (s.err) { - error_report_err(s.err); - return H_HARDWARE; + if ((cpu->env.spr[SPR_PVR] & pvr_mask) == (pvr & pvr_mask)) { + explicit_match = true; + } else { + if (ppc_check_compat(cpu, pvr, best_compat, max_compat)) { + best_compat = pvr; } } } - if (!cpu_version) { - cpu_update = false; + if ((best_compat == 0) && (!explicit_match || max_compat)) { + /* We couldn't find a suitable compatibility mode, and either + * the guest doesn't support "raw" mode for this CPU, or raw + * mode is disabled because a maximum compat mode is set */ + return H_HARDWARE; + } + + /* Parsing finished */ + trace_spapr_cas_pvr(cpu->compat_pvr, explicit_match, best_compat); + + /* Update CPUs */ + if (cpu->compat_pvr != best_compat) { + Error *local_err = NULL; + + ppc_set_compat_all(best_compat, &local_err); + if (local_err) { + error_report_err(local_err); + return H_HARDWARE; + } } /* For the future use: here @ov_table points to the first option vector */ @@ -1028,7 +1005,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu_, if (!spapr->cas_reboot) { spapr->cas_reboot = - (spapr_h_cas_compose_response(spapr, args[1], args[2], cpu_update, + (spapr_h_cas_compose_response(spapr, args[1], args[2], ov5_updates) != 0); } spapr_ovec_cleanup(ov5_updates); @@ -1101,6 +1078,7 @@ static void hypercall_register_types(void) /* hcall-splpar */ spapr_register_hypercall(H_REGISTER_VPA, h_register_vpa); spapr_register_hypercall(H_CEDE, h_cede); + spapr_register_hypercall(H_SIGNAL_SYS_RESET, h_signal_sys_reset); /* processor register resource access h-calls */ spapr_register_hypercall(H_SET_SPRG0, h_set_sprg0); diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index cc1e09c568..8bfc5f971f 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -538,21 +538,11 @@ VIOsPAPRBus *spapr_vio_bus_init(void) return bus; } -/* Represents sPAPR hcall VIO devices */ - -static int spapr_vio_bridge_init(SysBusDevice *dev) -{ - /* nothing */ - return 0; -} - static void spapr_vio_bridge_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); dc->fw_name = "vdevice"; - k->init = spapr_vio_bridge_init; } static const TypeInfo spapr_vio_bridge_info = { diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events index 2297ead11e..f46995cdb2 100644 --- a/hw/ppc/trace-events +++ b/hw/ppc/trace-events @@ -15,7 +15,7 @@ spapr_cas_continue(unsigned long n) "Copy changes to the guest: %ld bytes" # hw/ppc/spapr_hcall.c spapr_cas_pvr_try(uint32_t pvr) "%x" -spapr_cas_pvr(uint32_t cur_pvr, bool cpu_match, uint32_t new_pvr, uint64_t pcr) "current=%x, cpu_match=%u, new=%x, compat flags=%"PRIx64 +spapr_cas_pvr(uint32_t cur_pvr, bool explicit_match, uint32_t new_pvr) "current=%x, explicit_match=%u, new=%x" # hw/ppc/spapr_iommu.c spapr_iommu_put(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tce=0x%"PRIx64" ret=%"PRId64 @@ -74,3 +74,14 @@ ppc_tb_adjust(uint64_t offs1, uint64_t offs2, int64_t diff, int64_t seconds) "ad # hw/ppc/prep.c prep_io_800_writeb(uint32_t addr, uint32_t val) "0x%08" PRIx32 " => 0x%02" PRIx32 prep_io_800_readb(uint32_t addr, uint32_t retval) "0x%08" PRIx32 " <= 0x%02" PRIx32 + +# hw/ppc/prep_systemio.c +prep_systemio_read(uint32_t addr, uint32_t val) "read addr=%x val=%x" +prep_systemio_write(uint32_t addr, uint32_t val) "write addr=%x val=%x" + +# hw/ppc/rs6000_mc.c +rs6000mc_id_read(uint32_t addr, uint32_t val) "read addr=%x val=%x" +rs6000mc_presence_read(uint32_t addr, uint32_t val) "read addr=%x val=%x" +rs6000mc_size_read(uint32_t addr, uint32_t val) "read addr=%x val=%x" +rs6000mc_size_write(uint32_t addr, uint32_t val) "write addr=%x val=%x" +rs6000mc_parity_read(uint32_t addr, uint32_t val) "read addr=%x val=%x" diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index b97d96685c..fdbcf22a0c 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -221,6 +221,13 @@ static void virtex_init(MachineState *machine) cpu = ppc440_init_xilinx(&ram_size, 1, machine->cpu_model, 400000000); env = &cpu->env; + + if (env->mmu_model != POWERPC_MMU_BOOKE) { + fprintf(stderr, "MMU model %i not supported by this machine.\n", + env->mmu_model); + exit(1); + } + qemu_register_reset(main_cpu_reset, cpu); memory_region_allocate_system_memory(phys_ram, NULL, "ram", ram_size); diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index bd81d71a98..7791c6d520 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -1707,6 +1707,35 @@ static void usb_host_auto_check(void *unused) timer_mod(usb_auto_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 2000); } +/** + * Check whether USB host device has a USB mass storage SCSI interface + */ +bool usb_host_dev_is_scsi_storage(USBDevice *ud) +{ + USBHostDevice *uhd = USB_HOST_DEVICE(ud); + struct libusb_config_descriptor *conf; + const struct libusb_interface_descriptor *intf; + bool is_scsi_storage = false; + int i; + + if (!uhd || libusb_get_active_config_descriptor(uhd->dev, &conf) != 0) { + return false; + } + + for (i = 0; i < conf->bNumInterfaces; i++) { + intf = &conf->interface[i].altsetting[ud->altsetting[i]]; + if (intf->bInterfaceClass == LIBUSB_CLASS_MASS_STORAGE && + intf->bInterfaceSubClass == 6) { /* 6 means SCSI */ + is_scsi_storage = true; + break; + } + } + + libusb_free_config_descriptor(conf); + + return is_scsi_storage; +} + void hmp_info_usbhost(Monitor *mon, const QDict *qdict) { libusb_device **devs = NULL; diff --git a/hw/usb/host-stub.c b/hw/usb/host-stub.c index 6ba65a1f6d..d0268baaa0 100644 --- a/hw/usb/host-stub.c +++ b/hw/usb/host-stub.c @@ -46,3 +46,8 @@ USBDevice *usb_host_device_open(USBBus *bus, const char *devname) { return NULL; } + +bool usb_host_dev_is_scsi_storage(USBDevice *ud) +{ + return false; +} diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index 14f8383686..842ec6b22a 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -356,6 +356,26 @@ static inline int float16_is_any_nan(float16 a) return ((float16_val(a) & ~0x8000) > 0x7c00); } +static inline int float16_is_neg(float16 a) +{ + return float16_val(a) >> 15; +} + +static inline int float16_is_infinity(float16 a) +{ + return (float16_val(a) & 0x7fff) == 0x7c00; +} + +static inline int float16_is_zero(float16 a) +{ + return (float16_val(a) & 0x7fff) == 0; +} + +static inline int float16_is_zero_or_denormal(float16 a) +{ + return (float16_val(a) & 0x7c00) == 0; +} + /*---------------------------------------------------------------------------- | The pattern for a default generated half-precision NaN. *----------------------------------------------------------------------------*/ diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index bd5bcf70de..a2d8964f7e 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -347,7 +347,8 @@ struct sPAPRMachineState { #define H_XIRR_X 0x2FC #define H_RANDOM 0x300 #define H_SET_MODE 0x31C -#define MAX_HCALL_OPCODE H_SET_MODE +#define H_SIGNAL_SYS_RESET 0x380 +#define MAX_HCALL_OPCODE H_SIGNAL_SYS_RESET /* The hcalls above are standardized in PAPR and implemented by pHyp * as well. @@ -589,7 +590,6 @@ void spapr_events_init(sPAPRMachineState *sm); void spapr_dt_events(sPAPRMachineState *sm, void *fdt); int spapr_h_cas_compose_response(sPAPRMachineState *sm, target_ulong addr, target_ulong size, - bool cpu_update, sPAPROptionVector *ov5_updates); sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn); void spapr_tce_table_enable(sPAPRTCETable *tcet, @@ -614,7 +614,6 @@ void spapr_hotplug_req_add_by_count_indexed(sPAPRDRConnectorType drc_type, uint32_t count, uint32_t index); void spapr_hotplug_req_remove_by_count_indexed(sPAPRDRConnectorType drc_type, uint32_t count, uint32_t index); -void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, Error **errp); void *spapr_populate_hotplug_cpu_dt(CPUState *cs, int *fdt_offset, sPAPRMachineState *spapr); @@ -662,4 +661,6 @@ int spapr_rng_populate_dt(void *fdt); #define SPAPR_LMB_FLAGS_DRC_INVALID 0x00000020 #define SPAPR_LMB_FLAGS_RESERVED 0x00000080 +void spapr_do_system_reset_on_cpu(CPUState *cs, run_on_cpu_data arg); + #endif /* HW_SPAPR_H */ diff --git a/include/hw/usb.h b/include/hw/usb.h index 847c9dec7f..43838c9f5d 100644 --- a/include/hw/usb.h +++ b/include/hw/usb.h @@ -471,6 +471,7 @@ void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p); /* usb-linux.c */ USBDevice *usb_host_device_open(USBBus *bus, const char *devname); void hmp_info_usbhost(Monitor *mon, const QDict *qdict); +bool usb_host_dev_is_scsi_storage(USBDevice *usbdev); /* usb ports of the VM */ diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h index 96288d0bce..a38be42253 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -513,4 +513,31 @@ static inline uint64_t pow2ceil(uint64_t value) return 1ULL << (64 - nlz); } +/** + * urshift - 128-bit Unsigned Right Shift. + * @plow: in/out - lower 64-bit integer. + * @phigh: in/out - higher 64-bit integer. + * @shift: in - bytes to shift, between 0 and 127. + * + * Result is zero-extended and stored in plow/phigh, which are + * input/output variables. Shift values outside the range will + * be mod to 128. In other words, the caller is responsible to + * verify/assert both the shift range and plow/phigh pointers. + */ +void urshift(uint64_t *plow, uint64_t *phigh, int32_t shift); + +/** + * ulshift - 128-bit Unsigned Left Shift. + * @plow: in/out - lower 64-bit integer. + * @phigh: in/out - higher 64-bit integer. + * @shift: in - bytes to shift, between 0 and 127. + * @overflow: out - true if any 1-bit is shifted out. + * + * Result is zero-extended and stored in plow/phigh, which are + * input/output variables. Shift values outside the range will + * be mod to 128. In other words, the caller is responsible to + * verify/assert both the shift range and plow/phigh pointers. + */ +void ulshift(uint64_t *plow, uint64_t *phigh, int32_t shift, bool *overflow); + #endif diff --git a/target/ppc/Makefile.objs b/target/ppc/Makefile.objs index e667e69701..a8c7a30cde 100644 --- a/target/ppc/Makefile.objs +++ b/target/ppc/Makefile.objs @@ -2,7 +2,7 @@ obj-y += cpu-models.o obj-y += translate.o ifeq ($(CONFIG_SOFTMMU),y) obj-y += machine.o mmu_helper.o mmu-hash32.o monitor.o -obj-$(TARGET_PPC64) += mmu-hash64.o arch_dump.o +obj-$(TARGET_PPC64) += mmu-hash64.o arch_dump.o compat.o endif obj-$(CONFIG_KVM) += kvm.o obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o diff --git a/target/ppc/compat.c b/target/ppc/compat.c new file mode 100644 index 0000000000..458da262be --- /dev/null +++ b/target/ppc/compat.c @@ -0,0 +1,185 @@ +/* + * PowerPC CPU initialization for qemu. + * + * Copyright 2016, David Gibson, Red Hat Inc. + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "sysemu/hw_accel.h" +#include "sysemu/kvm.h" +#include "kvm_ppc.h" +#include "sysemu/cpus.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "cpu-models.h" + +typedef struct { + uint32_t pvr; + uint64_t pcr; + uint64_t pcr_level; + int max_threads; +} CompatInfo; + +static const CompatInfo compat_table[] = { + /* + * Ordered from oldest to newest - the code relies on this + */ + { /* POWER6, ISA2.05 */ + .pvr = CPU_POWERPC_LOGICAL_2_05, + .pcr = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_COMPAT_2_05 + | PCR_TM_DIS | PCR_VSX_DIS, + .pcr_level = PCR_COMPAT_2_05, + .max_threads = 2, + }, + { /* POWER7, ISA2.06 */ + .pvr = CPU_POWERPC_LOGICAL_2_06, + .pcr = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_TM_DIS, + .pcr_level = PCR_COMPAT_2_06, + .max_threads = 4, + }, + { + .pvr = CPU_POWERPC_LOGICAL_2_06_PLUS, + .pcr = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_TM_DIS, + .pcr_level = PCR_COMPAT_2_06, + .max_threads = 4, + }, + { /* POWER8, ISA2.07 */ + .pvr = CPU_POWERPC_LOGICAL_2_07, + .pcr = PCR_COMPAT_2_07, + .pcr_level = PCR_COMPAT_2_07, + .max_threads = 8, + }, +}; + +static const CompatInfo *compat_by_pvr(uint32_t pvr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(compat_table); i++) { + if (compat_table[i].pvr == pvr) { + return &compat_table[i]; + } + } + return NULL; +} + +bool ppc_check_compat(PowerPCCPU *cpu, uint32_t compat_pvr, + uint32_t min_compat_pvr, uint32_t max_compat_pvr) +{ + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + const CompatInfo *compat = compat_by_pvr(compat_pvr); + const CompatInfo *min = compat_by_pvr(min_compat_pvr); + const CompatInfo *max = compat_by_pvr(max_compat_pvr); + +#if !defined(CONFIG_USER_ONLY) + g_assert(cpu->vhyp); +#endif + g_assert(!min_compat_pvr || min); + g_assert(!max_compat_pvr || max); + + if (!compat) { + /* Not a recognized logical PVR */ + return false; + } + if ((min && (compat < min)) || (max && (compat > max))) { + /* Outside specified range */ + return false; + } + if (!(pcc->pcr_supported & compat->pcr_level)) { + /* Not supported by this CPU */ + return false; + } + return true; +} + +void ppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr, Error **errp) +{ + const CompatInfo *compat = compat_by_pvr(compat_pvr); + CPUPPCState *env = &cpu->env; + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + uint64_t pcr; + + if (!compat_pvr) { + pcr = 0; + } else if (!compat) { + error_setg(errp, "Unknown compatibility PVR 0x%08"PRIx32, compat_pvr); + return; + } else if (!ppc_check_compat(cpu, compat_pvr, 0, 0)) { + error_setg(errp, "Compatibility PVR 0x%08"PRIx32" not valid for CPU", + compat_pvr); + return; + } else { + pcr = compat->pcr; + } + + cpu_synchronize_state(CPU(cpu)); + + cpu->compat_pvr = compat_pvr; + env->spr[SPR_PCR] = pcr & pcc->pcr_mask; + + if (kvm_enabled()) { + int ret = kvmppc_set_compat(cpu, cpu->compat_pvr); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Unable to set CPU compatibility mode in KVM"); + } + } +} + +typedef struct { + uint32_t compat_pvr; + Error *err; +} SetCompatState; + +static void do_set_compat(CPUState *cs, run_on_cpu_data arg) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + SetCompatState *s = arg.host_ptr; + + ppc_set_compat(cpu, s->compat_pvr, &s->err); +} + +void ppc_set_compat_all(uint32_t compat_pvr, Error **errp) +{ + CPUState *cs; + + CPU_FOREACH(cs) { + SetCompatState s = { + .compat_pvr = compat_pvr, + .err = NULL, + }; + + run_on_cpu(cs, do_set_compat, RUN_ON_CPU_HOST_PTR(&s)); + + if (s.err) { + error_propagate(errp, s.err); + return; + } + } +} + +int ppc_compat_max_threads(PowerPCCPU *cpu) +{ + const CompatInfo *compat = compat_by_pvr(cpu->compat_pvr); + int n_threads = CPU(cpu)->nr_threads; + + if (cpu->compat_pvr) { + g_assert(compat); + n_threads = MIN(n_threads, compat->max_threads); + } + + return n_threads; +} diff --git a/target/ppc/cpu-models.c b/target/ppc/cpu-models.c index 506dee1ee8..4d3e6354cf 100644 --- a/target/ppc/cpu-models.c +++ b/target/ppc/cpu-models.c @@ -1375,19 +1375,15 @@ PowerPCCPUAlias ppc_cpu_aliases[] = { { "7445", "7445_v3.2" }, { "7455", "7455_v3.2" }, { "Apollo6", "7455" }, - { "7447", "7447_v1.2" }, + { "7447", "7447_v1.1" }, { "7457", "7457_v1.2" }, { "Apollo7", "7457" }, { "7447A", "7447A_v1.2" }, { "7457A", "7457A_v1.2" }, { "Apollo7PM", "7457A_v1.0" }, #if defined(TARGET_PPC64) - { "Trident", "620" }, { "POWER3", "630" }, - { "Boxer", "POWER3" }, - { "Dino", "POWER3" }, { "POWER3+", "631" }, - { "POWER5gr", "POWER5" }, { "POWER5+", "POWER5+_v2.1" }, { "POWER5gs", "POWER5+_v2.1" }, { "POWER7", "POWER7_v2.3" }, @@ -1399,21 +1395,7 @@ PowerPCCPUAlias ppc_cpu_aliases[] = { { "970", "970_v2.2" }, { "970fx", "970fx_v3.1" }, { "970mp", "970mp_v1.1" }, - { "Apache", "RS64" }, - { "A35", "RS64" }, - { "NorthStar", "RS64-II" }, - { "A50", "RS64-II" }, - { "Pulsar", "RS64-III" }, - { "IceStar", "RS64-IV" }, - { "IStar", "RS64-IV" }, - { "SStar", "RS64-IV" }, #endif - { "RIOS", "POWER" }, - { "RSC", "POWER" }, - { "RSC3308", "POWER" }, - { "RSC4608", "POWER" }, - { "RSC2", "POWER2" }, - { "P2SC", "POWER2" }, /* Generic PowerPCs */ #if defined(TARGET_PPC64) diff --git a/target/ppc/cpu-models.h b/target/ppc/cpu-models.h index aafbbd7d5d..d587e69bbc 100644 --- a/target/ppc/cpu-models.h +++ b/target/ppc/cpu-models.h @@ -601,7 +601,7 @@ enum { CPU_POWERPC_LOGICAL_2_06 = 0x0F000003, CPU_POWERPC_LOGICAL_2_06_PLUS = 0x0F100003, CPU_POWERPC_LOGICAL_2_07 = 0x0F000004, - CPU_POWERPC_LOGICAL_2_08 = 0x0F000005, + CPU_POWERPC_LOGICAL_3_00 = 0x0F000005, }; /* System version register (used on MPC 8xxx) */ diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index d46c31a15d..b7977bad18 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -214,6 +214,9 @@ extern const struct VMStateDescription vmstate_ppc_timebase; .flags = VMS_STRUCT, \ .offset = vmstate_offset_value(_state, _field, PPCTimebase), \ } + +void cpu_ppc_clock_vm_state_change(void *opaque, int running, + RunState state); #endif #endif diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 2a50c43689..bc2a2ce431 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -21,6 +21,7 @@ #define PPC_CPU_H #include "qemu-common.h" +#include "qemu/int128.h" //#define PPC_EMULATE_32BITS_HYPV @@ -262,6 +263,7 @@ union ppc_avr_t { #ifdef CONFIG_INT128 __uint128_t u128; #endif + Int128 s128; }; #if !defined(CONFIG_USER_ONLY) @@ -1148,12 +1150,15 @@ do { \ env->wdt_period[3] = (d_); \ } while (0) +typedef struct PPCVirtualHypervisor PPCVirtualHypervisor; +typedef struct PPCVirtualHypervisorClass PPCVirtualHypervisorClass; + /** * PowerPCCPU: * @env: #CPUPPCState * @cpu_dt_id: CPU index used in the device tree. KVM uses this index too * @max_compat: Maximal supported logical PVR from the command line - * @cpu_version: Current logical PVR, zero if in "raw" mode + * @compat_pvr: Current logical PVR, zero if in "raw" mode * * A PowerPC CPU. */ @@ -1165,7 +1170,8 @@ struct PowerPCCPU { CPUPPCState env; int cpu_dt_id; uint32_t max_compat; - uint32_t cpu_version; + uint32_t compat_pvr; + PPCVirtualHypervisor *vhyp; /* Fields related to migration compatibility hacks */ bool pre_2_8_migration; @@ -1187,6 +1193,25 @@ static inline PowerPCCPU *ppc_env_get_cpu(CPUPPCState *env) PowerPCCPUClass *ppc_cpu_class_by_pvr(uint32_t pvr); PowerPCCPUClass *ppc_cpu_class_by_pvr_mask(uint32_t pvr); +struct PPCVirtualHypervisor { + Object parent; +}; + +struct PPCVirtualHypervisorClass { + InterfaceClass parent; + void (*hypercall)(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu); +}; + +#define TYPE_PPC_VIRTUAL_HYPERVISOR "ppc-virtual-hypervisor" +#define PPC_VIRTUAL_HYPERVISOR(obj) \ + OBJECT_CHECK(PPCVirtualHypervisor, (obj), TYPE_PPC_VIRTUAL_HYPERVISOR) +#define PPC_VIRTUAL_HYPERVISOR_CLASS(klass) \ + OBJECT_CLASS_CHECK(PPCVirtualHypervisorClass, (klass), \ + TYPE_PPC_VIRTUAL_HYPERVISOR) +#define PPC_VIRTUAL_HYPERVISOR_GET_CLASS(obj) \ + OBJECT_GET_CLASS(PPCVirtualHypervisorClass, (obj), \ + TYPE_PPC_VIRTUAL_HYPERVISOR) + void ppc_cpu_do_interrupt(CPUState *cpu); bool ppc_cpu_exec_interrupt(CPUState *cpu, int int_req); void ppc_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, @@ -1225,9 +1250,7 @@ void ppc_store_sdr1 (CPUPPCState *env, target_ulong value); void ppc_store_msr (CPUPPCState *env, target_ulong value); void ppc_cpu_list (FILE *f, fprintf_function cpu_fprintf); -int ppc_get_compat_smt_threads(PowerPCCPU *cpu); #if defined(TARGET_PPC64) -void ppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version, Error **errp); #endif /* Time-base and decrementer management */ @@ -1259,6 +1282,7 @@ void store_booke_tcr (CPUPPCState *env, target_ulong val); void store_booke_tsr (CPUPPCState *env, target_ulong val); void ppc_tlb_invalidate_all (CPUPPCState *env); void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr); +void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp); void cpu_ppc_set_papr(PowerPCCPU *cpu); #endif #endif @@ -1297,18 +1321,34 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch) return ifetch ? env->immu_idx : env->dmmu_idx; } +/* Compatibility modes */ +#if defined(TARGET_PPC64) +bool ppc_check_compat(PowerPCCPU *cpu, uint32_t compat_pvr, + uint32_t min_compat_pvr, uint32_t max_compat_pvr); +void ppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr, Error **errp); +#if !defined(CONFIG_USER_ONLY) +void ppc_set_compat_all(uint32_t compat_pvr, Error **errp); +#endif +int ppc_compat_max_threads(PowerPCCPU *cpu); +#endif /* defined(TARGET_PPC64) */ + #include "exec/cpu-all.h" /*****************************************************************************/ /* CRF definitions */ -#define CRF_LT 3 -#define CRF_GT 2 -#define CRF_EQ 1 -#define CRF_SO 0 -#define CRF_CH (1 << CRF_LT) -#define CRF_CL (1 << CRF_GT) -#define CRF_CH_OR_CL (1 << CRF_EQ) -#define CRF_CH_AND_CL (1 << CRF_SO) +#define CRF_LT_BIT 3 +#define CRF_GT_BIT 2 +#define CRF_EQ_BIT 1 +#define CRF_SO_BIT 0 +#define CRF_LT (1 << CRF_LT_BIT) +#define CRF_GT (1 << CRF_GT_BIT) +#define CRF_EQ (1 << CRF_EQ_BIT) +#define CRF_SO (1 << CRF_SO_BIT) +/* For SPE extensions */ +#define CRF_CH (1 << CRF_LT_BIT) +#define CRF_CL (1 << CRF_GT_BIT) +#define CRF_CH_OR_CL (1 << CRF_EQ_BIT) +#define CRF_CH_AND_CL (1 << CRF_SO_BIT) /* XER definitions */ #define XER_SO 31 @@ -2250,6 +2290,7 @@ enum { PCR_COMPAT_2_05 = 1ull << (63-62), PCR_COMPAT_2_06 = 1ull << (63-61), PCR_COMPAT_2_07 = 1ull << (63-60), + PCR_COMPAT_3_00 = 1ull << (63-59), PCR_VEC_DIS = 1ull << (63-0), /* Vec. disable (bit NA since POWER8) */ PCR_VSX_DIS = 1ull << (63-1), /* VSX disable (bit NA since POWER8) */ PCR_TM_DIS = 1ull << (63-2), /* Trans. memory disable (POWER8) */ @@ -2428,8 +2469,6 @@ static inline bool lsw_reg_in_range(int start, int nregs, int rx) (start + nregs > 32 && (rx >= start || rx < start + nregs - 32)); } -extern void (*cpu_ppc_hypercall)(PowerPCCPU *); - void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env); /** diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 93369d4fe5..f4ee7aacd2 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -34,11 +34,6 @@ # define LOG_EXCP(...) do { } while (0) #endif -/*****************************************************************************/ -/* PowerPC Hypercall emulation */ - -void (*cpu_ppc_hypercall)(PowerPCCPU *); - /*****************************************************************************/ /* Exception processing */ #if defined(CONFIG_USER_ONLY) @@ -318,8 +313,10 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) env->nip += 4; /* "PAPR mode" built-in hypercall emulation */ - if ((lev == 1) && cpu_ppc_hypercall) { - cpu_ppc_hypercall(cpu); + if ((lev == 1) && cpu->vhyp) { + PPCVirtualHypervisorClass *vhc = + PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); + vhc->hypercall(cpu->vhyp, cpu); return; } if (lev == 1) { diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index 8a389e19af..9f5cafd5ba 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -20,9 +20,20 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" +#include "internal.h" + +static inline float128 float128_snan_to_qnan(float128 x) +{ + float128 r; + + r.high = x.high | 0x0000800000000000; + r.low = x.low; + return r; +} #define float64_snan_to_qnan(x) ((x) | 0x0008000000000000ULL) #define float32_snan_to_qnan(x) ((x) | 0x00400000) +#define float16_snan_to_qnan(x) ((x) | 0x0200) /*****************************************************************************/ /* Floating point operations helpers */ @@ -46,15 +57,6 @@ uint32_t helper_float64_to_float32(CPUPPCState *env, uint64_t arg) return f.l; } -static inline int isden(float64 d) -{ - CPU_DoubleU u; - - u.d = d; - - return ((u.ll >> 52) & 0x7FF) == 0; -} - static inline int ppc_float32_get_unbiased_exp(float32 f) { return ((f >> 23) & 0xFF) - 127; @@ -65,57 +67,61 @@ static inline int ppc_float64_get_unbiased_exp(float64 f) return ((f >> 52) & 0x7FF) - 1023; } -void helper_compute_fprf(CPUPPCState *env, uint64_t arg) -{ - CPU_DoubleU farg; - int isneg; - int fprf; - - farg.ll = arg; - isneg = float64_is_neg(farg.d); - if (unlikely(float64_is_any_nan(farg.d))) { - if (float64_is_signaling_nan(farg.d, &env->fp_status)) { - /* Signaling NaN: flags are undefined */ - fprf = 0x00; - } else { - /* Quiet NaN */ - fprf = 0x11; - } - } else if (unlikely(float64_is_infinity(farg.d))) { - /* +/- infinity */ - if (isneg) { - fprf = 0x09; - } else { - fprf = 0x05; - } - } else { - if (float64_is_zero(farg.d)) { - /* +/- zero */ - if (isneg) { - fprf = 0x12; - } else { - fprf = 0x02; - } - } else { - if (isden(farg.d)) { - /* Denormalized numbers */ - fprf = 0x10; - } else { - /* Normalized numbers */ - fprf = 0x00; - } - if (isneg) { - fprf |= 0x08; - } else { - fprf |= 0x04; - } - } - } - /* We update FPSCR_FPRF */ - env->fpscr &= ~(0x1F << FPSCR_FPRF); - env->fpscr |= fprf << FPSCR_FPRF; +#define COMPUTE_FPRF(tp) \ +void helper_compute_fprf_##tp(CPUPPCState *env, tp arg) \ +{ \ + int isneg; \ + int fprf; \ + \ + isneg = tp##_is_neg(arg); \ + if (unlikely(tp##_is_any_nan(arg))) { \ + if (tp##_is_signaling_nan(arg, &env->fp_status)) { \ + /* Signaling NaN: flags are undefined */ \ + fprf = 0x00; \ + } else { \ + /* Quiet NaN */ \ + fprf = 0x11; \ + } \ + } else if (unlikely(tp##_is_infinity(arg))) { \ + /* +/- infinity */ \ + if (isneg) { \ + fprf = 0x09; \ + } else { \ + fprf = 0x05; \ + } \ + } else { \ + if (tp##_is_zero(arg)) { \ + /* +/- zero */ \ + if (isneg) { \ + fprf = 0x12; \ + } else { \ + fprf = 0x02; \ + } \ + } else { \ + if (tp##_is_zero_or_denormal(arg)) { \ + /* Denormalized numbers */ \ + fprf = 0x10; \ + } else { \ + /* Normalized numbers */ \ + fprf = 0x00; \ + } \ + if (isneg) { \ + fprf |= 0x08; \ + } else { \ + fprf |= 0x04; \ + } \ + } \ + } \ + /* We update FPSCR_FPRF */ \ + env->fpscr &= ~(0x1F << FPSCR_FPRF); \ + env->fpscr |= fprf << FPSCR_FPRF; \ } +COMPUTE_FPRF(float16) +COMPUTE_FPRF(float32) +COMPUTE_FPRF(float64) +COMPUTE_FPRF(float128) + /* Floating-point invalid operations exception */ static inline __attribute__((__always_inline__)) uint64_t float_invalid_op_excp(CPUPPCState *env, int op, int set_fpcc) @@ -1776,53 +1782,6 @@ uint32_t helper_efdcmpeq(CPUPPCState *env, uint64_t op1, uint64_t op2) return helper_efdtsteq(env, op1, op2); } -#define DECODE_SPLIT(opcode, shift1, nb1, shift2, nb2) \ - (((((opcode) >> (shift1)) & ((1 << (nb1)) - 1)) << nb2) | \ - (((opcode) >> (shift2)) & ((1 << (nb2)) - 1))) - -#define xT(opcode) DECODE_SPLIT(opcode, 0, 1, 21, 5) -#define xA(opcode) DECODE_SPLIT(opcode, 2, 1, 16, 5) -#define xB(opcode) DECODE_SPLIT(opcode, 1, 1, 11, 5) -#define xC(opcode) DECODE_SPLIT(opcode, 3, 1, 6, 5) -#define BF(opcode) (((opcode) >> (31-8)) & 7) - -typedef union _ppc_vsr_t { - uint64_t u64[2]; - uint32_t u32[4]; - float32 f32[4]; - float64 f64[2]; -} ppc_vsr_t; - -#if defined(HOST_WORDS_BIGENDIAN) -#define VsrW(i) u32[i] -#define VsrD(i) u64[i] -#else -#define VsrW(i) u32[3-(i)] -#define VsrD(i) u64[1-(i)] -#endif - -static void getVSR(int n, ppc_vsr_t *vsr, CPUPPCState *env) -{ - if (n < 32) { - vsr->VsrD(0) = env->fpr[n]; - vsr->VsrD(1) = env->vsr[n]; - } else { - vsr->u64[0] = env->avr[n-32].u64[0]; - vsr->u64[1] = env->avr[n-32].u64[1]; - } -} - -static void putVSR(int n, ppc_vsr_t *vsr, CPUPPCState *env) -{ - if (n < 32) { - env->fpr[n] = vsr->VsrD(0); - env->vsr[n] = vsr->VsrD(1); - } else { - env->avr[n-32].u64[0] = vsr->u64[0]; - env->avr[n-32].u64[1] = vsr->u64[1]; - } -} - #define float64_to_float64(x, env) x @@ -1865,7 +1824,7 @@ void helper_##name(CPUPPCState *env, uint32_t opcode) \ } \ \ if (sfprf) { \ - helper_compute_fprf(env, xt.fld); \ + helper_compute_fprf_float64(env, xt.fld); \ } \ } \ putVSR(xT(opcode), &xt, env); \ @@ -1881,6 +1840,41 @@ VSX_ADD_SUB(xssubsp, sub, 1, float64, VsrD(0), 1, 1) VSX_ADD_SUB(xvsubdp, sub, 2, float64, VsrD(i), 0, 0) VSX_ADD_SUB(xvsubsp, sub, 4, float32, VsrW(i), 0, 0) +void helper_xsaddqp(CPUPPCState *env, uint32_t opcode) +{ + ppc_vsr_t xt, xa, xb; + float_status tstat; + + getVSR(rA(opcode) + 32, &xa, env); + getVSR(rB(opcode) + 32, &xb, env); + getVSR(rD(opcode) + 32, &xt, env); + helper_reset_fpstatus(env); + + if (unlikely(Rc(opcode) != 0)) { + /* TODO: Support xsadddpo after round-to-odd is implemented */ + abort(); + } + + tstat = env->fp_status; + set_float_exception_flags(0, &tstat); + xt.f128 = float128_add(xa.f128, xb.f128, &tstat); + env->fp_status.float_exception_flags |= tstat.float_exception_flags; + + if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { + if (float128_is_infinity(xa.f128) && float128_is_infinity(xb.f128)) { + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); + } else if (float128_is_signaling_nan(xa.f128, &tstat) || + float128_is_signaling_nan(xb.f128, &tstat)) { + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + } + } + + helper_compute_fprf_float128(env, xt.f128); + + putVSR(rD(opcode) + 32, &xt, env); + float_check_status(env); +} + /* VSX_MUL - VSX floating point multiply * op - instruction mnemonic * nels - number of elements (1, 2 or 4) @@ -1920,7 +1914,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ \ if (sfprf) { \ - helper_compute_fprf(env, xt.fld); \ + helper_compute_fprf_float64(env, xt.fld); \ } \ } \ \ @@ -1933,6 +1927,41 @@ VSX_MUL(xsmulsp, 1, float64, VsrD(0), 1, 1) VSX_MUL(xvmuldp, 2, float64, VsrD(i), 0, 0) VSX_MUL(xvmulsp, 4, float32, VsrW(i), 0, 0) +void helper_xsmulqp(CPUPPCState *env, uint32_t opcode) +{ + ppc_vsr_t xt, xa, xb; + + getVSR(rA(opcode) + 32, &xa, env); + getVSR(rB(opcode) + 32, &xb, env); + getVSR(rD(opcode) + 32, &xt, env); + + if (unlikely(Rc(opcode) != 0)) { + /* TODO: Support xsmulpo after round-to-odd is implemented */ + abort(); + } + + helper_reset_fpstatus(env); + + float_status tstat = env->fp_status; + set_float_exception_flags(0, &tstat); + xt.f128 = float128_mul(xa.f128, xb.f128, &tstat); + env->fp_status.float_exception_flags |= tstat.float_exception_flags; + + if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { + if ((float128_is_infinity(xa.f128) && float128_is_zero(xb.f128)) || + (float128_is_infinity(xb.f128) && float128_is_zero(xa.f128))) { + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1); + } else if (float128_is_signaling_nan(xa.f128, &tstat) || + float128_is_signaling_nan(xb.f128, &tstat)) { + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + } + } + helper_compute_fprf_float128(env, xt.f128); + + putVSR(rD(opcode) + 32, &xt, env); + float_check_status(env); +} + /* VSX_DIV - VSX floating point divide * op - instruction mnemonic * nels - number of elements (1, 2 or 4) @@ -1974,7 +2003,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ \ if (sfprf) { \ - helper_compute_fprf(env, xt.fld); \ + helper_compute_fprf_float64(env, xt.fld); \ } \ } \ \ @@ -1987,6 +2016,42 @@ VSX_DIV(xsdivsp, 1, float64, VsrD(0), 1, 1) VSX_DIV(xvdivdp, 2, float64, VsrD(i), 0, 0) VSX_DIV(xvdivsp, 4, float32, VsrW(i), 0, 0) +void helper_xsdivqp(CPUPPCState *env, uint32_t opcode) +{ + ppc_vsr_t xt, xa, xb; + + getVSR(rA(opcode) + 32, &xa, env); + getVSR(rB(opcode) + 32, &xb, env); + getVSR(rD(opcode) + 32, &xt, env); + + if (unlikely(Rc(opcode) != 0)) { + /* TODO: Support xsdivqpo after round-to-odd is implemented */ + abort(); + } + + helper_reset_fpstatus(env); + float_status tstat = env->fp_status; + set_float_exception_flags(0, &tstat); + xt.f128 = float128_div(xa.f128, xb.f128, &tstat); + env->fp_status.float_exception_flags |= tstat.float_exception_flags; + + if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { + if (float128_is_infinity(xa.f128) && float128_is_infinity(xb.f128)) { + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIDI, 1); + } else if (float128_is_zero(xa.f128) && + float128_is_zero(xb.f128)) { + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXZDZ, 1); + } else if (float128_is_signaling_nan(xa.f128, &tstat) || + float128_is_signaling_nan(xb.f128, &tstat)) { + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + } + } + + helper_compute_fprf_float128(env, xt.f128); + putVSR(rD(opcode) + 32, &xt, env); + float_check_status(env); +} + /* VSX_RE - VSX floating point reciprocal estimate * op - instruction mnemonic * nels - number of elements (1, 2 or 4) @@ -2015,7 +2080,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ \ if (sfprf) { \ - helper_compute_fprf(env, xt.fld); \ + helper_compute_fprf_float64(env, xt.fld); \ } \ } \ \ @@ -2064,7 +2129,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ \ if (sfprf) { \ - helper_compute_fprf(env, xt.fld); \ + helper_compute_fprf_float64(env, xt.fld); \ } \ } \ \ @@ -2114,7 +2179,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ \ if (sfprf) { \ - helper_compute_fprf(env, xt.fld); \ + helper_compute_fprf_float64(env, xt.fld); \ } \ } \ \ @@ -2314,7 +2379,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ \ if (sfprf) { \ - helper_compute_fprf(env, xt_out.fld); \ + helper_compute_fprf_float64(env, xt_out.fld); \ } \ } \ putVSR(xT(opcode), &xt_out, env); \ @@ -2414,34 +2479,108 @@ VSX_SCALAR_CMP_DP(xscmpgedp, le, 1, 1) VSX_SCALAR_CMP_DP(xscmpgtdp, lt, 1, 1) VSX_SCALAR_CMP_DP(xscmpnedp, eq, 0, 0) +void helper_xscmpexpdp(CPUPPCState *env, uint32_t opcode) +{ + ppc_vsr_t xa, xb; + int64_t exp_a, exp_b; + uint32_t cc; + + getVSR(xA(opcode), &xa, env); + getVSR(xB(opcode), &xb, env); + + exp_a = extract64(xa.VsrD(0), 52, 11); + exp_b = extract64(xb.VsrD(0), 52, 11); + + if (unlikely(float64_is_any_nan(xa.VsrD(0)) || + float64_is_any_nan(xb.VsrD(0)))) { + cc = CRF_SO; + } else { + if (exp_a < exp_b) { + cc = CRF_LT; + } else if (exp_a > exp_b) { + cc = CRF_GT; + } else { + cc = CRF_EQ; + } + } + + env->fpscr &= ~(0x0F << FPSCR_FPRF); + env->fpscr |= cc << FPSCR_FPRF; + env->crf[BF(opcode)] = cc; + + helper_float_check_status(env); +} + +void helper_xscmpexpqp(CPUPPCState *env, uint32_t opcode) +{ + ppc_vsr_t xa, xb; + int64_t exp_a, exp_b; + uint32_t cc; + + getVSR(rA(opcode) + 32, &xa, env); + getVSR(rB(opcode) + 32, &xb, env); + + exp_a = extract64(xa.VsrD(0), 48, 15); + exp_b = extract64(xb.VsrD(0), 48, 15); + + if (unlikely(float128_is_any_nan(xa.f128) || + float128_is_any_nan(xb.f128))) { + cc = CRF_SO; + } else { + if (exp_a < exp_b) { + cc = CRF_LT; + } else if (exp_a > exp_b) { + cc = CRF_GT; + } else { + cc = CRF_EQ; + } + } + + env->fpscr &= ~(0x0F << FPSCR_FPRF); + env->fpscr |= cc << FPSCR_FPRF; + env->crf[BF(opcode)] = cc; + + helper_float_check_status(env); +} + #define VSX_SCALAR_CMP(op, ordered) \ void helper_##op(CPUPPCState *env, uint32_t opcode) \ { \ ppc_vsr_t xa, xb; \ uint32_t cc = 0; \ + bool vxsnan_flag = false, vxvc_flag = false; \ \ + helper_reset_fpstatus(env); \ getVSR(xA(opcode), &xa, env); \ getVSR(xB(opcode), &xb, env); \ \ - if (unlikely(float64_is_any_nan(xa.VsrD(0)) || \ - float64_is_any_nan(xb.VsrD(0)))) { \ - if (float64_is_signaling_nan(xa.VsrD(0), &env->fp_status) || \ - float64_is_signaling_nan(xb.VsrD(0), &env->fp_status)) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + if (float64_is_signaling_nan(xa.VsrD(0), &env->fp_status) || \ + float64_is_signaling_nan(xb.VsrD(0), &env->fp_status)) { \ + vxsnan_flag = true; \ + cc = CRF_SO; \ + if (fpscr_ve == 0 && ordered) { \ + vxvc_flag = true; \ } \ + } else if (float64_is_quiet_nan(xa.VsrD(0), &env->fp_status) || \ + float64_is_quiet_nan(xb.VsrD(0), &env->fp_status)) { \ + cc = CRF_SO; \ if (ordered) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC, 0); \ + vxvc_flag = true; \ } \ - cc = 1; \ + } \ + if (vxsnan_flag) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + } \ + if (vxvc_flag) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC, 0); \ + } \ + \ + if (float64_lt(xa.VsrD(0), xb.VsrD(0), &env->fp_status)) { \ + cc |= CRF_LT; \ + } else if (!float64_le(xa.VsrD(0), xb.VsrD(0), &env->fp_status)) { \ + cc |= CRF_GT; \ } else { \ - if (float64_lt(xa.VsrD(0), xb.VsrD(0), &env->fp_status)) { \ - cc = 8; \ - } else if (!float64_le(xa.VsrD(0), xb.VsrD(0), \ - &env->fp_status)) { \ - cc = 4; \ - } else { \ - cc = 2; \ - } \ + cc |= CRF_EQ; \ } \ \ env->fpscr &= ~(0x0F << FPSCR_FPRF); \ @@ -2454,6 +2593,56 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ VSX_SCALAR_CMP(xscmpodp, 1) VSX_SCALAR_CMP(xscmpudp, 0) +#define VSX_SCALAR_CMPQ(op, ordered) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xa, xb; \ + uint32_t cc = 0; \ + bool vxsnan_flag = false, vxvc_flag = false; \ + \ + helper_reset_fpstatus(env); \ + getVSR(rA(opcode) + 32, &xa, env); \ + getVSR(rB(opcode) + 32, &xb, env); \ + \ + if (float128_is_signaling_nan(xa.f128, &env->fp_status) || \ + float128_is_signaling_nan(xb.f128, &env->fp_status)) { \ + vxsnan_flag = true; \ + cc = CRF_SO; \ + if (fpscr_ve == 0 && ordered) { \ + vxvc_flag = true; \ + } \ + } else if (float128_is_quiet_nan(xa.f128, &env->fp_status) || \ + float128_is_quiet_nan(xb.f128, &env->fp_status)) { \ + cc = CRF_SO; \ + if (ordered) { \ + vxvc_flag = true; \ + } \ + } \ + if (vxsnan_flag) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + } \ + if (vxvc_flag) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC, 0); \ + } \ + \ + if (float128_lt(xa.f128, xb.f128, &env->fp_status)) { \ + cc |= CRF_LT; \ + } else if (!float128_le(xa.f128, xb.f128, &env->fp_status)) { \ + cc |= CRF_GT; \ + } else { \ + cc |= CRF_EQ; \ + } \ + \ + env->fpscr &= ~(0x0F << FPSCR_FPRF); \ + env->fpscr |= cc << FPSCR_FPRF; \ + env->crf[BF(opcode)] = cc; \ + \ + float_check_status(env); \ +} + +VSX_SCALAR_CMPQ(xscmpoqp, 1) +VSX_SCALAR_CMPQ(xscmpuqp, 0) + /* VSX_MAX_MIN - VSX floating point maximum/minimum * name - instruction mnemonic * op - operation (max or min) @@ -2576,8 +2765,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ xt.tfld = ttp##_snan_to_qnan(xt.tfld); \ } \ if (sfprf) { \ - helper_compute_fprf(env, ttp##_to_float64(xt.tfld, \ - &env->fp_status)); \ + helper_compute_fprf_##ttp(env, xt.tfld); \ } \ } \ \ @@ -2590,6 +2778,110 @@ VSX_CVT_FP_TO_FP(xscvspdp, 1, float32, float64, VsrW(0), VsrD(0), 1) VSX_CVT_FP_TO_FP(xvcvdpsp, 2, float64, float32, VsrD(i), VsrW(2*i), 0) VSX_CVT_FP_TO_FP(xvcvspdp, 2, float32, float64, VsrW(2*i), VsrD(i), 0) +/* VSX_CVT_FP_TO_FP_VECTOR - VSX floating point/floating point conversion + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * stp - source type (float32 or float64) + * ttp - target type (float32 or float64) + * sfld - source vsr_t field + * tfld - target vsr_t field (f32 or f64) + * sfprf - set FPRF + */ +#define VSX_CVT_FP_TO_FP_VECTOR(op, nels, stp, ttp, sfld, tfld, sfprf) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + int i; \ + \ + getVSR(rB(opcode) + 32, &xb, env); \ + getVSR(rD(opcode) + 32, &xt, env); \ + \ + for (i = 0; i < nels; i++) { \ + xt.tfld = stp##_to_##ttp(xb.sfld, &env->fp_status); \ + if (unlikely(stp##_is_signaling_nan(xb.sfld, \ + &env->fp_status))) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + xt.tfld = ttp##_snan_to_qnan(xt.tfld); \ + } \ + if (sfprf) { \ + helper_compute_fprf_##ttp(env, xt.tfld); \ + } \ + } \ + \ + putVSR(rD(opcode) + 32, &xt, env); \ + float_check_status(env); \ +} + +VSX_CVT_FP_TO_FP_VECTOR(xscvdpqp, 1, float64, float128, VsrD(0), f128, 1) + +/* VSX_CVT_FP_TO_FP_HP - VSX floating point/floating point conversion + * involving one half precision value + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * stp - source type + * ttp - target type + * sfld - source vsr_t field + * tfld - target vsr_t field + * sfprf - set FPRF + */ +#define VSX_CVT_FP_TO_FP_HP(op, nels, stp, ttp, sfld, tfld, sfprf) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + int i; \ + \ + getVSR(xB(opcode), &xb, env); \ + memset(&xt, 0, sizeof(xt)); \ + \ + for (i = 0; i < nels; i++) { \ + xt.tfld = stp##_to_##ttp(xb.sfld, 1, &env->fp_status); \ + if (unlikely(stp##_is_signaling_nan(xb.sfld, \ + &env->fp_status))) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + xt.tfld = ttp##_snan_to_qnan(xt.tfld); \ + } \ + if (sfprf) { \ + helper_compute_fprf_##ttp(env, xt.tfld); \ + } \ + } \ + \ + putVSR(xT(opcode), &xt, env); \ + float_check_status(env); \ +} + +VSX_CVT_FP_TO_FP_HP(xscvdphp, 1, float64, float16, VsrD(0), VsrH(3), 1) +VSX_CVT_FP_TO_FP_HP(xscvhpdp, 1, float16, float64, VsrH(3), VsrD(0), 1) +VSX_CVT_FP_TO_FP_HP(xvcvsphp, 4, float32, float16, VsrW(i), VsrH(2 * i + 1), 0) +VSX_CVT_FP_TO_FP_HP(xvcvhpsp, 4, float16, float32, VsrH(2 * i + 1), VsrW(i), 0) + +/* + * xscvqpdp isn't using VSX_CVT_FP_TO_FP() because xscvqpdpo will be + * added to this later. + */ +void helper_xscvqpdp(CPUPPCState *env, uint32_t opcode) +{ + ppc_vsr_t xt, xb; + + getVSR(rB(opcode) + 32, &xb, env); + memset(&xt, 0, sizeof(xt)); + + if (unlikely(Rc(opcode) != 0)) { + /* TODO: Support xscvqpdpo after round-to-odd is implemented */ + abort(); + } + + xt.VsrD(0) = float128_to_float64(xb.f128, &env->fp_status); + if (unlikely(float128_is_signaling_nan(xb.f128, + &env->fp_status))) { + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); + xt.VsrD(0) = float64_snan_to_qnan(xt.VsrD(0)); + } + helper_compute_fprf_float64(env, xt.VsrD(0)); + + putVSR(rD(opcode) + 32, &xt, env); + float_check_status(env); +} + uint64_t helper_xscvdpspn(CPUPPCState *env, uint64_t xb) { float_status tstat = env->fp_status; @@ -2662,6 +2954,46 @@ VSX_CVT_FP_TO_INT(xvcvspsxws, 4, float32, int32, VsrW(i), VsrW(i), 0x80000000U) VSX_CVT_FP_TO_INT(xvcvspuxds, 2, float32, uint64, VsrW(2*i), VsrD(i), 0ULL) VSX_CVT_FP_TO_INT(xvcvspuxws, 4, float32, uint32, VsrW(i), VsrW(i), 0U) +/* VSX_CVT_FP_TO_INT_VECTOR - VSX floating point to integer conversion + * op - instruction mnemonic + * stp - source type (float32 or float64) + * ttp - target type (int32, uint32, int64 or uint64) + * sfld - source vsr_t field + * tfld - target vsr_t field + * rnan - resulting NaN + */ +#define VSX_CVT_FP_TO_INT_VECTOR(op, stp, ttp, sfld, tfld, rnan) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + \ + getVSR(rB(opcode) + 32, &xb, env); \ + memset(&xt, 0, sizeof(xt)); \ + \ + if (unlikely(stp##_is_any_nan(xb.sfld))) { \ + if (stp##_is_signaling_nan(xb.sfld, &env->fp_status)) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + } \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI, 0); \ + xt.tfld = rnan; \ + } else { \ + xt.tfld = stp##_to_##ttp##_round_to_zero(xb.sfld, \ + &env->fp_status); \ + if (env->fp_status.float_exception_flags & float_flag_invalid) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI, 0); \ + } \ + } \ + \ + putVSR(rD(opcode) + 32, &xt, env); \ + float_check_status(env); \ +} + +VSX_CVT_FP_TO_INT_VECTOR(xscvqpsdz, float128, int64, f128, VsrD(0), \ + 0x8000000000000000ULL) + +VSX_CVT_FP_TO_INT_VECTOR(xscvqpswz, float128, int32, f128, VsrD(0), \ + 0xffffffff80000000ULL) + /* VSX_CVT_INT_TO_FP - VSX integer to floating point conversion * op - instruction mnemonic * nels - number of elements (1, 2 or 4) @@ -2687,7 +3019,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ xt.tfld = helper_frsp(env, xt.tfld); \ } \ if (sfprf) { \ - helper_compute_fprf(env, xt.tfld); \ + helper_compute_fprf_float64(env, xt.tfld); \ } \ } \ \ @@ -2708,6 +3040,31 @@ VSX_CVT_INT_TO_FP(xvcvuxdsp, 2, uint64, float32, VsrD(i), VsrW(2*i), 0, 0) VSX_CVT_INT_TO_FP(xvcvsxwsp, 4, int32, float32, VsrW(i), VsrW(i), 0, 0) VSX_CVT_INT_TO_FP(xvcvuxwsp, 4, uint32, float32, VsrW(i), VsrW(i), 0, 0) +/* VSX_CVT_INT_TO_FP_VECTOR - VSX integer to floating point conversion + * op - instruction mnemonic + * stp - source type (int32, uint32, int64 or uint64) + * ttp - target type (float32 or float64) + * sfld - source vsr_t field + * tfld - target vsr_t field + */ +#define VSX_CVT_INT_TO_FP_VECTOR(op, stp, ttp, sfld, tfld) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + \ + getVSR(rB(opcode) + 32, &xb, env); \ + getVSR(rD(opcode) + 32, &xt, env); \ + \ + xt.tfld = stp##_to_##ttp(xb.sfld, &env->fp_status); \ + helper_compute_fprf_##ttp(env, xt.tfld); \ + \ + putVSR(xT(opcode) + 32, &xt, env); \ + float_check_status(env); \ +} + +VSX_CVT_INT_TO_FP_VECTOR(xscvsdqp, int64, float128, VsrD(0), f128) +VSX_CVT_INT_TO_FP_VECTOR(xscvudqp, uint64, float128, VsrD(0), f128) + /* For "use current rounding mode", define a value that will not be one of * the existing rounding model enums. */ @@ -2743,7 +3100,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ xt.fld = tp##_round_to_int(xb.fld, &env->fp_status); \ } \ if (sfprf) { \ - helper_compute_fprf(env, xt.fld); \ + helper_compute_fprf_float64(env, xt.fld); \ } \ } \ \ @@ -2783,7 +3140,140 @@ uint64_t helper_xsrsp(CPUPPCState *env, uint64_t xb) uint64_t xt = helper_frsp(env, xb); - helper_compute_fprf(env, xt); + helper_compute_fprf_float64(env, xt); float_check_status(env); return xt; } + +#define VSX_XXPERM(op, indexed) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xa, pcv, xto; \ + int i, idx; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xT(opcode), &xt, env); \ + getVSR(xB(opcode), &pcv, env); \ + \ + for (i = 0; i < 16; i++) { \ + idx = pcv.VsrB(i) & 0x1F; \ + if (indexed) { \ + idx = 31 - idx; \ + } \ + xto.VsrB(i) = (idx <= 15) ? xa.VsrB(idx) : xt.VsrB(idx - 16); \ + } \ + putVSR(xT(opcode), &xto, env); \ +} + +VSX_XXPERM(xxperm, 0) +VSX_XXPERM(xxpermr, 1) + +void helper_xvxsigsp(CPUPPCState *env, uint32_t opcode) +{ + ppc_vsr_t xt, xb; + uint32_t exp, i, fraction; + + getVSR(xB(opcode), &xb, env); + memset(&xt, 0, sizeof(xt)); + + for (i = 0; i < 4; i++) { + exp = (xb.VsrW(i) >> 23) & 0xFF; + fraction = xb.VsrW(i) & 0x7FFFFF; + if (exp != 0 && exp != 255) { + xt.VsrW(i) = fraction | 0x00800000; + } else { + xt.VsrW(i) = fraction; + } + } + putVSR(xT(opcode), &xt, env); +} + +/* VSX_TEST_DC - VSX floating point test data class + * op - instruction mnemonic + * nels - number of elements (1, 2 or 4) + * xbn - VSR register number + * tp - type (float32 or float64) + * fld - vsr_t field (VsrD(*) or VsrW(*)) + * tfld - target vsr_t field (VsrD(*) or VsrW(*)) + * fld_max - target field max + * scrf - set result in CR and FPCC + */ +#define VSX_TEST_DC(op, nels, xbn, tp, fld, tfld, fld_max, scrf) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xb; \ + uint32_t i, sign, dcmx; \ + uint32_t cc, match = 0; \ + \ + getVSR(xbn, &xb, env); \ + if (!scrf) { \ + memset(&xt, 0, sizeof(xt)); \ + dcmx = DCMX_XV(opcode); \ + } else { \ + dcmx = DCMX(opcode); \ + } \ + \ + for (i = 0; i < nels; i++) { \ + sign = tp##_is_neg(xb.fld); \ + if (tp##_is_any_nan(xb.fld)) { \ + match = extract32(dcmx, 6, 1); \ + } else if (tp##_is_infinity(xb.fld)) { \ + match = extract32(dcmx, 4 + !sign, 1); \ + } else if (tp##_is_zero(xb.fld)) { \ + match = extract32(dcmx, 2 + !sign, 1); \ + } else if (tp##_is_zero_or_denormal(xb.fld)) { \ + match = extract32(dcmx, 0 + !sign, 1); \ + } \ + \ + if (scrf) { \ + cc = sign << CRF_LT_BIT | match << CRF_EQ_BIT; \ + env->fpscr &= ~(0x0F << FPSCR_FPRF); \ + env->fpscr |= cc << FPSCR_FPRF; \ + env->crf[BF(opcode)] = cc; \ + } else { \ + xt.tfld = match ? fld_max : 0; \ + } \ + match = 0; \ + } \ + if (!scrf) { \ + putVSR(xT(opcode), &xt, env); \ + } \ +} + +VSX_TEST_DC(xvtstdcdp, 2, xB(opcode), float64, VsrD(i), VsrD(i), UINT64_MAX, 0) +VSX_TEST_DC(xvtstdcsp, 4, xB(opcode), float32, VsrW(i), VsrW(i), UINT32_MAX, 0) +VSX_TEST_DC(xststdcdp, 1, xB(opcode), float64, VsrD(0), VsrD(0), 0, 1) +VSX_TEST_DC(xststdcqp, 1, (rB(opcode) + 32), float128, f128, VsrD(0), 0, 1) + +void helper_xststdcsp(CPUPPCState *env, uint32_t opcode) +{ + ppc_vsr_t xb; + uint32_t dcmx, sign, exp; + uint32_t cc, match = 0, not_sp = 0; + + getVSR(xB(opcode), &xb, env); + dcmx = DCMX(opcode); + exp = (xb.VsrD(0) >> 52) & 0x7FF; + + sign = float64_is_neg(xb.VsrD(0)); + if (float64_is_any_nan(xb.VsrD(0))) { + match = extract32(dcmx, 6, 1); + } else if (float64_is_infinity(xb.VsrD(0))) { + match = extract32(dcmx, 4 + !sign, 1); + } else if (float64_is_zero(xb.VsrD(0))) { + match = extract32(dcmx, 2 + !sign, 1); + } else if (float64_is_zero_or_denormal(xb.VsrD(0)) || + (exp > 0 && exp < 0x381)) { + match = extract32(dcmx, 0 + !sign, 1); + } + + not_sp = !float64_eq(xb.VsrD(0), + float32_to_float64( + float64_to_float32(xb.VsrD(0), &env->fp_status), + &env->fp_status), &env->fp_status); + + cc = sign << CRF_LT_BIT | match << CRF_EQ_BIT | not_sp << CRF_SO_BIT; + env->fpscr &= ~(0x0F << FPSCR_FPRF); + env->fpscr |= cc << FPSCR_FPRF; + env->crf[BF(opcode)] = cc; +} diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 0a8fbba3c5..85af9df36d 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -56,7 +56,7 @@ DEF_HELPER_FLAGS_2(brinc, TCG_CALL_NO_RWG_SE, tl, tl, tl) DEF_HELPER_1(float_check_status, void, env) DEF_HELPER_1(reset_fpstatus, void, env) -DEF_HELPER_2(compute_fprf, void, env, i64) +DEF_HELPER_2(compute_fprf_float64, void, env, i64) 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) @@ -312,6 +312,12 @@ 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) +#if defined(TARGET_PPC64) +DEF_HELPER_4(lxvl, void, env, tl, tl, tl) +DEF_HELPER_4(lxvll, void, env, tl, tl, tl) +DEF_HELPER_4(stxvl, void, env, tl, tl, tl) +DEF_HELPER_4(stxvll, void, env, tl, tl, tl) +#endif 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) @@ -361,6 +367,12 @@ DEF_HELPER_3(vpmsumb, void, avr, avr, avr) DEF_HELPER_3(vpmsumh, void, avr, avr, avr) DEF_HELPER_3(vpmsumw, void, avr, avr, avr) DEF_HELPER_3(vpmsumd, void, avr, avr, avr) +DEF_HELPER_2(vextublx, tl, tl, avr) +DEF_HELPER_2(vextuhlx, tl, tl, avr) +DEF_HELPER_2(vextuwlx, tl, tl, avr) +DEF_HELPER_2(vextubrx, tl, tl, avr) +DEF_HELPER_2(vextuhrx, tl, tl, avr) +DEF_HELPER_2(vextuwrx, tl, tl, avr) DEF_HELPER_2(vsbox, void, avr, avr) DEF_HELPER_3(vcipher, void, avr, avr, avr) @@ -377,11 +389,23 @@ DEF_HELPER_3(bcdcfn, i32, avr, avr, i32) DEF_HELPER_3(bcdctn, i32, avr, avr, i32) DEF_HELPER_3(bcdcfz, i32, avr, avr, i32) DEF_HELPER_3(bcdctz, i32, avr, avr, i32) +DEF_HELPER_3(bcdcfsq, i32, avr, avr, i32) +DEF_HELPER_3(bcdctsq, i32, avr, avr, i32) +DEF_HELPER_4(bcdcpsgn, i32, avr, avr, avr, i32) +DEF_HELPER_3(bcdsetsgn, i32, avr, avr, i32) +DEF_HELPER_4(bcds, i32, avr, avr, avr, i32) +DEF_HELPER_4(bcdus, i32, avr, avr, avr, i32) +DEF_HELPER_4(bcdsr, i32, avr, avr, avr, i32) +DEF_HELPER_4(bcdtrunc, i32, avr, avr, avr, i32) +DEF_HELPER_4(bcdutrunc, i32, avr, avr, avr, i32) DEF_HELPER_2(xsadddp, void, env, i32) +DEF_HELPER_2(xsaddqp, void, env, i32) DEF_HELPER_2(xssubdp, void, env, i32) DEF_HELPER_2(xsmuldp, void, env, i32) +DEF_HELPER_2(xsmulqp, void, env, i32) DEF_HELPER_2(xsdivdp, void, env, i32) +DEF_HELPER_2(xsdivqp, void, env, i32) DEF_HELPER_2(xsredp, void, env, i32) DEF_HELPER_2(xssqrtdp, void, env, i32) DEF_HELPER_2(xsrsqrtedp, void, env, i32) @@ -399,12 +423,23 @@ DEF_HELPER_2(xscmpeqdp, void, env, i32) DEF_HELPER_2(xscmpgtdp, void, env, i32) DEF_HELPER_2(xscmpgedp, void, env, i32) DEF_HELPER_2(xscmpnedp, void, env, i32) +DEF_HELPER_2(xscmpexpdp, void, env, i32) +DEF_HELPER_2(xscmpexpqp, void, env, i32) DEF_HELPER_2(xscmpodp, void, env, i32) DEF_HELPER_2(xscmpudp, void, env, i32) +DEF_HELPER_2(xscmpoqp, void, env, i32) +DEF_HELPER_2(xscmpuqp, void, env, i32) DEF_HELPER_2(xsmaxdp, void, env, i32) DEF_HELPER_2(xsmindp, void, env, i32) +DEF_HELPER_2(xscvdphp, void, env, i32) +DEF_HELPER_2(xscvdpqp, void, env, i32) DEF_HELPER_2(xscvdpsp, void, env, i32) DEF_HELPER_2(xscvdpspn, i64, env, i64) +DEF_HELPER_2(xscvqpdp, void, env, i32) +DEF_HELPER_2(xscvqpsdz, void, env, i32) +DEF_HELPER_2(xscvqpswz, void, env, i32) +DEF_HELPER_2(xscvhpdp, void, env, i32) +DEF_HELPER_2(xscvsdqp, void, env, i32) DEF_HELPER_2(xscvspdp, void, env, i32) DEF_HELPER_2(xscvspdpn, i64, env, i64) DEF_HELPER_2(xscvdpsxds, void, env, i32) @@ -414,7 +449,11 @@ DEF_HELPER_2(xscvdpuxws, void, env, i32) DEF_HELPER_2(xscvsxddp, void, env, i32) DEF_HELPER_2(xscvuxdsp, void, env, i32) DEF_HELPER_2(xscvsxdsp, void, env, i32) +DEF_HELPER_2(xscvudqp, void, env, i32) DEF_HELPER_2(xscvuxddp, void, env, i32) +DEF_HELPER_2(xststdcsp, void, env, i32) +DEF_HELPER_2(xststdcdp, void, env, i32) +DEF_HELPER_2(xststdcqp, void, env, i32) DEF_HELPER_2(xsrdpi, void, env, i32) DEF_HELPER_2(xsrdpic, void, env, i32) DEF_HELPER_2(xsrdpim, void, env, i32) @@ -500,6 +539,8 @@ DEF_HELPER_2(xvcmpgesp, void, env, i32) DEF_HELPER_2(xvcmpgtsp, void, env, i32) DEF_HELPER_2(xvcmpnesp, void, env, i32) DEF_HELPER_2(xvcvspdp, void, env, i32) +DEF_HELPER_2(xvcvsphp, void, env, i32) +DEF_HELPER_2(xvcvhpsp, void, env, i32) DEF_HELPER_2(xvcvspsxds, void, env, i32) DEF_HELPER_2(xvcvspsxws, void, env, i32) DEF_HELPER_2(xvcvspuxds, void, env, i32) @@ -508,11 +549,18 @@ DEF_HELPER_2(xvcvsxdsp, void, env, i32) DEF_HELPER_2(xvcvuxdsp, void, env, i32) DEF_HELPER_2(xvcvsxwsp, void, env, i32) DEF_HELPER_2(xvcvuxwsp, void, env, i32) +DEF_HELPER_2(xvtstdcsp, void, env, i32) +DEF_HELPER_2(xvtstdcdp, void, env, i32) DEF_HELPER_2(xvrspi, void, env, i32) DEF_HELPER_2(xvrspic, void, env, i32) DEF_HELPER_2(xvrspim, void, env, i32) DEF_HELPER_2(xvrspip, void, env, i32) DEF_HELPER_2(xvrspiz, void, env, i32) +DEF_HELPER_2(xxperm, void, env, i32) +DEF_HELPER_2(xxpermr, void, env, i32) +DEF_HELPER_4(xxextractuw, void, env, tl, tl, i32) +DEF_HELPER_4(xxinsertw, void, env, tl, tl, i32) +DEF_HELPER_2(xvxsigsp, void, env, i32) DEF_HELPER_2(efscfsi, i32, env, i32) DEF_HELPER_2(efscfui, i32, env, i32) diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index 1871792ff6..dd0a8929b3 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -157,7 +157,7 @@ uint64_t helper_divde(CPUPPCState *env, uint64_t rau, uint64_t rbu, uint32_t oe) uint32_t helper_cmpeqb(target_ulong ra, target_ulong rb) { - return hasvalue(rb, ra) ? 1 << CRF_GT : 0; + return hasvalue(rb, ra) ? CRF_GT : 0; } #undef pattern @@ -1773,6 +1773,42 @@ void helper_vlogefp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *b) } } +#if defined(HOST_WORDS_BIGENDIAN) +#define VEXTU_X_DO(name, size, left) \ + target_ulong glue(helper_, name)(target_ulong a, ppc_avr_t *b) \ + { \ + int index; \ + if (left) { \ + index = (a & 0xf) * 8; \ + } else { \ + index = ((15 - (a & 0xf) + 1) * 8) - size; \ + } \ + return int128_getlo(int128_rshift(b->s128, index)) & \ + MAKE_64BIT_MASK(0, size); \ + } +#else +#define VEXTU_X_DO(name, size, left) \ + target_ulong glue(helper_, name)(target_ulong a, ppc_avr_t *b) \ + { \ + int index; \ + if (left) { \ + index = ((15 - (a & 0xf) + 1) * 8) - size; \ + } else { \ + index = (a & 0xf) * 8; \ + } \ + return int128_getlo(int128_rshift(b->s128, index)) & \ + MAKE_64BIT_MASK(0, size); \ + } +#endif + +VEXTU_X_DO(vextublx, 8, 1) +VEXTU_X_DO(vextuhlx, 16, 1) +VEXTU_X_DO(vextuwlx, 32, 1) +VEXTU_X_DO(vextubrx, 8, 0) +VEXTU_X_DO(vextuhrx, 16, 0) +VEXTU_X_DO(vextuwrx, 32, 0) +#undef VEXTU_X_DO + /* 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. */ @@ -1965,6 +2001,57 @@ VEXTRACT(uw, u32) VEXTRACT(d, u64) #undef VEXTRACT +void helper_xxextractuw(CPUPPCState *env, target_ulong xtn, + target_ulong xbn, uint32_t index) +{ + ppc_vsr_t xt, xb; + size_t es = sizeof(uint32_t); + uint32_t ext_index; + int i; + + getVSR(xbn, &xb, env); + memset(&xt, 0, sizeof(xt)); + +#if defined(HOST_WORDS_BIGENDIAN) + ext_index = index; + for (i = 0; i < es; i++, ext_index++) { + xt.u8[8 - es + i] = xb.u8[ext_index % 16]; + } +#else + ext_index = 15 - index; + for (i = es - 1; i >= 0; i--, ext_index--) { + xt.u8[8 + i] = xb.u8[ext_index % 16]; + } +#endif + + putVSR(xtn, &xt, env); +} + +void helper_xxinsertw(CPUPPCState *env, target_ulong xtn, + target_ulong xbn, uint32_t index) +{ + ppc_vsr_t xt, xb; + size_t es = sizeof(uint32_t); + int ins_index, i = 0; + + getVSR(xbn, &xb, env); + getVSR(xtn, &xt, env); + +#if defined(HOST_WORDS_BIGENDIAN) + ins_index = index; + for (i = 0; i < es && ins_index < 16; i++, ins_index++) { + xt.u8[ins_index] = xb.u8[8 - es + i]; + } +#else + ins_index = 15 - index; + for (i = es - 1; i >= 0 && ins_index >= 0; i--, ins_index--) { + xt.u8[ins_index] = xb.u8[8 + i]; + } +#endif + + putVSR(xtn, &xt, env); +} + #define VEXT_SIGNED(name, element, mask, cast, recast) \ void helper_##name(ppc_avr_t *r, ppc_avr_t *b) \ { \ @@ -2464,9 +2551,9 @@ void helper_vsubecuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) #define NATIONAL_NEG 0x2D #if defined(HOST_WORDS_BIGENDIAN) -#define BCD_DIG_BYTE(n) (15 - (n/2)) +#define BCD_DIG_BYTE(n) (15 - ((n) / 2)) #else -#define BCD_DIG_BYTE(n) (n/2) +#define BCD_DIG_BYTE(n) ((n) / 2) #endif static int bcd_get_sgn(ppc_avr_t *bcd) @@ -2528,12 +2615,30 @@ static void bcd_put_digit(ppc_avr_t *bcd, uint8_t digit, int n) } } +static bool bcd_is_valid(ppc_avr_t *bcd) +{ + int i; + int invalid = 0; + + if (bcd_get_sgn(bcd) == 0) { + return false; + } + + for (i = 1; i < 32; i++) { + bcd_get_digit(bcd, i, &invalid); + if (unlikely(invalid)) { + return false; + } + } + return true; +} + static int bcd_cmp_zero(ppc_avr_t *bcd) { if (bcd->u64[HI_IDX] == 0 && (bcd->u64[LO_IDX] >> 4) == 0) { - return 1 << CRF_EQ; + return CRF_EQ; } else { - return (bcd_get_sgn(bcd) == 1) ? 1 << CRF_GT : 1 << CRF_LT; + return (bcd_get_sgn(bcd) == 1) ? CRF_GT : CRF_LT; } } @@ -2645,25 +2750,25 @@ uint32_t helper_bcdadd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) if (sgna == sgnb) { result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgna, ps); zero = bcd_add_mag(&result, a, b, &invalid, &overflow); - cr = (sgna > 0) ? 1 << CRF_GT : 1 << CRF_LT; + cr = (sgna > 0) ? CRF_GT : CRF_LT; } else if (bcd_cmp_mag(a, b) > 0) { result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgna, ps); zero = bcd_sub_mag(&result, a, b, &invalid, &overflow); - cr = (sgna > 0) ? 1 << CRF_GT : 1 << CRF_LT; + cr = (sgna > 0) ? CRF_GT : CRF_LT; } else { result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgnb, ps); zero = bcd_sub_mag(&result, b, a, &invalid, &overflow); - cr = (sgnb > 0) ? 1 << CRF_GT : 1 << CRF_LT; + cr = (sgnb > 0) ? CRF_GT : CRF_LT; } } if (unlikely(invalid)) { result.u64[HI_IDX] = result.u64[LO_IDX] = -1; - cr = 1 << CRF_SO; + cr = CRF_SO; } else if (overflow) { - cr |= 1 << CRF_SO; + cr |= CRF_SO; } else if (zero) { - cr = 1 << CRF_EQ; + cr = CRF_EQ; } *r = result; @@ -2713,7 +2818,7 @@ uint32_t helper_bcdcfn(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps) cr = bcd_cmp_zero(&ret); if (unlikely(invalid)) { - cr = 1 << CRF_SO; + cr = CRF_SO; } *r = ret; @@ -2743,11 +2848,11 @@ uint32_t helper_bcdctn(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps) cr = bcd_cmp_zero(b); if (ox_flag) { - cr |= 1 << CRF_SO; + cr |= CRF_SO; } if (unlikely(invalid)) { - cr = 1 << CRF_SO; + cr = CRF_SO; } *r = ret; @@ -2771,7 +2876,7 @@ uint32_t helper_bcdcfz(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps) } for (i = 0; i < 16; i++) { - zone_digit = (i * 2) ? b->u8[BCD_DIG_BYTE(i * 2)] >> 4 : zone_lead; + zone_digit = i ? b->u8[BCD_DIG_BYTE(i * 2)] >> 4 : zone_lead; digit = b->u8[BCD_DIG_BYTE(i * 2)] & 0xF; if (unlikely(zone_digit != zone_lead || digit > 0x9)) { invalid = 1; @@ -2791,7 +2896,7 @@ uint32_t helper_bcdcfz(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps) cr = bcd_cmp_zero(&ret); if (unlikely(invalid)) { - cr = 1 << CRF_SO; + cr = CRF_SO; } *r = ret; @@ -2830,11 +2935,11 @@ uint32_t helper_bcdctz(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps) cr = bcd_cmp_zero(b); if (ox_flag) { - cr |= 1 << CRF_SO; + cr |= CRF_SO; } if (unlikely(invalid)) { - cr = 1 << CRF_SO; + cr = CRF_SO; } *r = ret; @@ -2842,6 +2947,338 @@ uint32_t helper_bcdctz(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps) return cr; } +uint32_t helper_bcdcfsq(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps) +{ + int i; + int cr = 0; + uint64_t lo_value; + uint64_t hi_value; + ppc_avr_t ret = { .u64 = { 0, 0 } }; + + if (b->s64[HI_IDX] < 0) { + lo_value = -b->s64[LO_IDX]; + hi_value = ~b->u64[HI_IDX] + !lo_value; + bcd_put_digit(&ret, 0xD, 0); + } else { + lo_value = b->u64[LO_IDX]; + hi_value = b->u64[HI_IDX]; + bcd_put_digit(&ret, bcd_preferred_sgn(0, ps), 0); + } + + if (divu128(&lo_value, &hi_value, 1000000000000000ULL) || + lo_value > 9999999999999999ULL) { + cr = CRF_SO; + } + + for (i = 1; i < 16; hi_value /= 10, i++) { + bcd_put_digit(&ret, hi_value % 10, i); + } + + for (; i < 32; lo_value /= 10, i++) { + bcd_put_digit(&ret, lo_value % 10, i); + } + + cr |= bcd_cmp_zero(&ret); + + *r = ret; + + return cr; +} + +uint32_t helper_bcdctsq(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps) +{ + uint8_t i; + int cr; + uint64_t carry; + uint64_t unused; + uint64_t lo_value; + uint64_t hi_value = 0; + int sgnb = bcd_get_sgn(b); + int invalid = (sgnb == 0); + + lo_value = bcd_get_digit(b, 31, &invalid); + for (i = 30; i > 0; i--) { + mulu64(&lo_value, &carry, lo_value, 10ULL); + mulu64(&hi_value, &unused, hi_value, 10ULL); + lo_value += bcd_get_digit(b, i, &invalid); + hi_value += carry; + + if (unlikely(invalid)) { + break; + } + } + + if (sgnb == -1) { + r->s64[LO_IDX] = -lo_value; + r->s64[HI_IDX] = ~hi_value + !r->s64[LO_IDX]; + } else { + r->s64[LO_IDX] = lo_value; + r->s64[HI_IDX] = hi_value; + } + + cr = bcd_cmp_zero(b); + + if (unlikely(invalid)) { + cr = CRF_SO; + } + + return cr; +} + +uint32_t helper_bcdcpsgn(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) +{ + int i; + int invalid = 0; + + if (bcd_get_sgn(a) == 0 || bcd_get_sgn(b) == 0) { + return CRF_SO; + } + + *r = *a; + bcd_put_digit(r, b->u8[BCD_DIG_BYTE(0)] & 0xF, 0); + + for (i = 1; i < 32; i++) { + bcd_get_digit(a, i, &invalid); + bcd_get_digit(b, i, &invalid); + if (unlikely(invalid)) { + return CRF_SO; + } + } + + return bcd_cmp_zero(r); +} + +uint32_t helper_bcdsetsgn(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps) +{ + int sgnb = bcd_get_sgn(b); + + *r = *b; + bcd_put_digit(r, bcd_preferred_sgn(sgnb, ps), 0); + + if (bcd_is_valid(b) == false) { + return CRF_SO; + } + + return bcd_cmp_zero(r); +} + +uint32_t helper_bcds(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) +{ + int cr; +#if defined(HOST_WORDS_BIGENDIAN) + int i = a->s8[7]; +#else + int i = a->s8[8]; +#endif + bool ox_flag = false; + int sgnb = bcd_get_sgn(b); + ppc_avr_t ret = *b; + ret.u64[LO_IDX] &= ~0xf; + + if (bcd_is_valid(b) == false) { + return CRF_SO; + } + + if (unlikely(i > 31)) { + i = 31; + } else if (unlikely(i < -31)) { + i = -31; + } + + if (i > 0) { + ulshift(&ret.u64[LO_IDX], &ret.u64[HI_IDX], i * 4, &ox_flag); + } else { + urshift(&ret.u64[LO_IDX], &ret.u64[HI_IDX], -i * 4); + } + bcd_put_digit(&ret, bcd_preferred_sgn(sgnb, ps), 0); + + *r = ret; + + cr = bcd_cmp_zero(r); + if (ox_flag) { + cr |= CRF_SO; + } + + return cr; +} + +uint32_t helper_bcdus(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) +{ + int cr; + int i; + int invalid = 0; + bool ox_flag = false; + ppc_avr_t ret = *b; + + for (i = 0; i < 32; i++) { + bcd_get_digit(b, i, &invalid); + + if (unlikely(invalid)) { + return CRF_SO; + } + } + +#if defined(HOST_WORDS_BIGENDIAN) + i = a->s8[7]; +#else + i = a->s8[8]; +#endif + if (i >= 32) { + ox_flag = true; + ret.u64[LO_IDX] = ret.u64[HI_IDX] = 0; + } else if (i <= -32) { + ret.u64[LO_IDX] = ret.u64[HI_IDX] = 0; + } else if (i > 0) { + ulshift(&ret.u64[LO_IDX], &ret.u64[HI_IDX], i * 4, &ox_flag); + } else { + urshift(&ret.u64[LO_IDX], &ret.u64[HI_IDX], -i * 4); + } + *r = ret; + + cr = bcd_cmp_zero(r); + if (ox_flag) { + cr |= CRF_SO; + } + + return cr; +} + +uint32_t helper_bcdsr(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) +{ + int cr; + int unused = 0; + int invalid = 0; + bool ox_flag = false; + int sgnb = bcd_get_sgn(b); + ppc_avr_t ret = *b; + ret.u64[LO_IDX] &= ~0xf; + +#if defined(HOST_WORDS_BIGENDIAN) + int i = a->s8[7]; + ppc_avr_t bcd_one = { .u64 = { 0, 0x10 } }; +#else + int i = a->s8[8]; + ppc_avr_t bcd_one = { .u64 = { 0x10, 0 } }; +#endif + + if (bcd_is_valid(b) == false) { + return CRF_SO; + } + + if (unlikely(i > 31)) { + i = 31; + } else if (unlikely(i < -31)) { + i = -31; + } + + if (i > 0) { + ulshift(&ret.u64[LO_IDX], &ret.u64[HI_IDX], i * 4, &ox_flag); + } else { + urshift(&ret.u64[LO_IDX], &ret.u64[HI_IDX], -i * 4); + + if (bcd_get_digit(&ret, 0, &invalid) >= 5) { + bcd_add_mag(&ret, &ret, &bcd_one, &invalid, &unused); + } + } + bcd_put_digit(&ret, bcd_preferred_sgn(sgnb, ps), 0); + + cr = bcd_cmp_zero(&ret); + if (ox_flag) { + cr |= CRF_SO; + } + *r = ret; + + return cr; +} + +uint32_t helper_bcdtrunc(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) +{ + uint64_t mask; + uint32_t ox_flag = 0; +#if defined(HOST_WORDS_BIGENDIAN) + int i = a->s16[3] + 1; +#else + int i = a->s16[4] + 1; +#endif + ppc_avr_t ret = *b; + + if (bcd_is_valid(b) == false) { + return CRF_SO; + } + + if (i > 16 && i < 32) { + mask = (uint64_t)-1 >> (128 - i * 4); + if (ret.u64[HI_IDX] & ~mask) { + ox_flag = CRF_SO; + } + + ret.u64[HI_IDX] &= mask; + } else if (i >= 0 && i <= 16) { + mask = (uint64_t)-1 >> (64 - i * 4); + if (ret.u64[HI_IDX] || (ret.u64[LO_IDX] & ~mask)) { + ox_flag = CRF_SO; + } + + ret.u64[LO_IDX] &= mask; + ret.u64[HI_IDX] = 0; + } + bcd_put_digit(&ret, bcd_preferred_sgn(bcd_get_sgn(b), ps), 0); + *r = ret; + + return bcd_cmp_zero(&ret) | ox_flag; +} + +uint32_t helper_bcdutrunc(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) +{ + int i; + uint64_t mask; + uint32_t ox_flag = 0; + int invalid = 0; + ppc_avr_t ret = *b; + + for (i = 0; i < 32; i++) { + bcd_get_digit(b, i, &invalid); + + if (unlikely(invalid)) { + return CRF_SO; + } + } + +#if defined(HOST_WORDS_BIGENDIAN) + i = a->s16[3]; +#else + i = a->s16[4]; +#endif + if (i > 16 && i < 33) { + mask = (uint64_t)-1 >> (128 - i * 4); + if (ret.u64[HI_IDX] & ~mask) { + ox_flag = CRF_SO; + } + + ret.u64[HI_IDX] &= mask; + } else if (i > 0 && i <= 16) { + mask = (uint64_t)-1 >> (64 - i * 4); + if (ret.u64[HI_IDX] || (ret.u64[LO_IDX] & ~mask)) { + ox_flag = CRF_SO; + } + + ret.u64[LO_IDX] &= mask; + ret.u64[HI_IDX] = 0; + } else if (i == 0) { + if (ret.u64[HI_IDX] || ret.u64[LO_IDX]) { + ox_flag = CRF_SO; + } + ret.u64[HI_IDX] = ret.u64[LO_IDX] = 0; + } + + *r = ret; + if (r->u64[HI_IDX] == 0 && r->u64[LO_IDX] == 0) { + return ox_flag | CRF_EQ; + } + + return ox_flag | CRF_GT; +} + void helper_vsbox(ppc_avr_t *r, ppc_avr_t *a) { int i; diff --git a/target/ppc/internal.h b/target/ppc/internal.h index 1ff4896c45..5a2fd68427 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -47,4 +47,206 @@ FUNC_MASK(MASK, target_ulong, 32, UINT32_MAX); FUNC_MASK(mask_u32, uint32_t, 32, UINT32_MAX); FUNC_MASK(mask_u64, uint64_t, 64, UINT64_MAX); +/*****************************************************************************/ +/*** Instruction decoding ***/ +#define EXTRACT_HELPER(name, shift, nb) \ +static inline uint32_t name(uint32_t opcode) \ +{ \ + return (opcode >> (shift)) & ((1 << (nb)) - 1); \ +} + +#define EXTRACT_SHELPER(name, shift, nb) \ +static inline int32_t name(uint32_t opcode) \ +{ \ + return (int16_t)((opcode >> (shift)) & ((1 << (nb)) - 1)); \ +} + +#define EXTRACT_HELPER_SPLIT(name, shift1, nb1, shift2, nb2) \ +static inline uint32_t name(uint32_t opcode) \ +{ \ + return (((opcode >> (shift1)) & ((1 << (nb1)) - 1)) << nb2) | \ + ((opcode >> (shift2)) & ((1 << (nb2)) - 1)); \ +} + +#define EXTRACT_HELPER_SPLIT_3(name, \ + d0_bits, shift_op_d0, shift_d0, \ + d1_bits, shift_op_d1, shift_d1, \ + d2_bits, shift_op_d2, shift_d2) \ +static inline int16_t name(uint32_t opcode) \ +{ \ + return \ + (((opcode >> (shift_op_d0)) & ((1 << (d0_bits)) - 1)) << (shift_d0)) | \ + (((opcode >> (shift_op_d1)) & ((1 << (d1_bits)) - 1)) << (shift_d1)) | \ + (((opcode >> (shift_op_d2)) & ((1 << (d2_bits)) - 1)) << (shift_d2)); \ +} + + +/* Opcode part 1 */ +EXTRACT_HELPER(opc1, 26, 6); +/* Opcode part 2 */ +EXTRACT_HELPER(opc2, 1, 5); +/* Opcode part 3 */ +EXTRACT_HELPER(opc3, 6, 5); +/* Opcode part 4 */ +EXTRACT_HELPER(opc4, 16, 5); +/* Update Cr0 flags */ +EXTRACT_HELPER(Rc, 0, 1); +/* Update Cr6 flags (Altivec) */ +EXTRACT_HELPER(Rc21, 10, 1); +/* Destination */ +EXTRACT_HELPER(rD, 21, 5); +/* Source */ +EXTRACT_HELPER(rS, 21, 5); +/* First operand */ +EXTRACT_HELPER(rA, 16, 5); +/* Second operand */ +EXTRACT_HELPER(rB, 11, 5); +/* Third operand */ +EXTRACT_HELPER(rC, 6, 5); +/*** Get CRn ***/ +EXTRACT_HELPER(crfD, 23, 3); +EXTRACT_HELPER(BF, 23, 3); +EXTRACT_HELPER(crfS, 18, 3); +EXTRACT_HELPER(crbD, 21, 5); +EXTRACT_HELPER(crbA, 16, 5); +EXTRACT_HELPER(crbB, 11, 5); +/* SPR / TBL */ +EXTRACT_HELPER(_SPR, 11, 10); +static inline uint32_t SPR(uint32_t opcode) +{ + uint32_t sprn = _SPR(opcode); + + return ((sprn >> 5) & 0x1F) | ((sprn & 0x1F) << 5); +} +/*** Get constants ***/ +/* 16 bits signed immediate value */ +EXTRACT_SHELPER(SIMM, 0, 16); +/* 16 bits unsigned immediate value */ +EXTRACT_HELPER(UIMM, 0, 16); +/* 5 bits signed immediate value */ +EXTRACT_HELPER(SIMM5, 16, 5); +/* 5 bits signed immediate value */ +EXTRACT_HELPER(UIMM5, 16, 5); +/* 4 bits unsigned immediate value */ +EXTRACT_HELPER(UIMM4, 16, 4); +/* Bit count */ +EXTRACT_HELPER(NB, 11, 5); +/* Shift count */ +EXTRACT_HELPER(SH, 11, 5); +/* Vector shift count */ +EXTRACT_HELPER(VSH, 6, 4); +/* Mask start */ +EXTRACT_HELPER(MB, 6, 5); +/* Mask end */ +EXTRACT_HELPER(ME, 1, 5); +/* Trap operand */ +EXTRACT_HELPER(TO, 21, 5); + +EXTRACT_HELPER(CRM, 12, 8); + +#ifndef CONFIG_USER_ONLY +EXTRACT_HELPER(SR, 16, 4); +#endif + +/* mtfsf/mtfsfi */ +EXTRACT_HELPER(FPBF, 23, 3); +EXTRACT_HELPER(FPIMM, 12, 4); +EXTRACT_HELPER(FPL, 25, 1); +EXTRACT_HELPER(FPFLM, 17, 8); +EXTRACT_HELPER(FPW, 16, 1); + +/* addpcis */ +EXTRACT_HELPER_SPLIT_3(DX, 10, 6, 6, 5, 16, 1, 1, 0, 0) +#if defined(TARGET_PPC64) +/* darn */ +EXTRACT_HELPER(L, 16, 2); +#endif + +/*** Jump target decoding ***/ +/* Immediate address */ +static inline target_ulong LI(uint32_t opcode) +{ + return (opcode >> 0) & 0x03FFFFFC; +} + +static inline uint32_t BD(uint32_t opcode) +{ + return (opcode >> 0) & 0xFFFC; +} + +EXTRACT_HELPER(BO, 21, 5); +EXTRACT_HELPER(BI, 16, 5); +/* Absolute/relative address */ +EXTRACT_HELPER(AA, 1, 1); +/* Link */ +EXTRACT_HELPER(LK, 0, 1); + +/* DFP Z22-form */ +EXTRACT_HELPER(DCM, 10, 6) + +/* DFP Z23-form */ +EXTRACT_HELPER(RMC, 9, 2) + +EXTRACT_HELPER_SPLIT(DQxT, 3, 1, 21, 5); +EXTRACT_HELPER_SPLIT(xT, 0, 1, 21, 5); +EXTRACT_HELPER_SPLIT(xS, 0, 1, 21, 5); +EXTRACT_HELPER_SPLIT(xA, 2, 1, 16, 5); +EXTRACT_HELPER_SPLIT(xB, 1, 1, 11, 5); +EXTRACT_HELPER_SPLIT(xC, 3, 1, 6, 5); +EXTRACT_HELPER(DM, 8, 2); +EXTRACT_HELPER(UIM, 16, 2); +EXTRACT_HELPER(SHW, 8, 2); +EXTRACT_HELPER(SP, 19, 2); +EXTRACT_HELPER(IMM8, 11, 8); +EXTRACT_HELPER(DCMX, 16, 7); +EXTRACT_HELPER_SPLIT_3(DCMX_XV, 5, 16, 0, 1, 2, 5, 1, 6, 6); + +typedef union _ppc_vsr_t { + uint8_t u8[16]; + uint16_t u16[8]; + uint32_t u32[4]; + uint64_t u64[2]; + float32 f32[4]; + float64 f64[2]; + float128 f128; + Int128 s128; +} ppc_vsr_t; + +#if defined(HOST_WORDS_BIGENDIAN) +#define VsrB(i) u8[i] +#define VsrH(i) u16[i] +#define VsrW(i) u32[i] +#define VsrD(i) u64[i] +#else +#define VsrB(i) u8[15 - (i)] +#define VsrH(i) u16[7 - (i)] +#define VsrW(i) u32[3 - (i)] +#define VsrD(i) u64[1 - (i)] +#endif + +static inline void getVSR(int n, ppc_vsr_t *vsr, CPUPPCState *env) +{ + if (n < 32) { + vsr->VsrD(0) = env->fpr[n]; + vsr->VsrD(1) = env->vsr[n]; + } else { + vsr->u64[0] = env->avr[n - 32].u64[0]; + vsr->u64[1] = env->avr[n - 32].u64[1]; + } +} + +static inline void putVSR(int n, ppc_vsr_t *vsr, CPUPPCState *env) +{ + if (n < 32) { + env->fpr[n] = vsr->VsrD(0); + env->vsr[n] = vsr->VsrD(1); + } else { + env->avr[n - 32].u64[0] = vsr->u64[0]; + env->avr[n - 32].u64[1] = vsr->u64[1]; + } +} + +void helper_compute_fprf_float16(CPUPPCState *env, float16 arg); +void helper_compute_fprf_float32(CPUPPCState *env, float32 arg); +void helper_compute_fprf_float128(CPUPPCState *env, float128 arg); #endif /* PPC_INTERNAL_H */ diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index ec92c64159..663d2e79c9 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -24,6 +24,7 @@ #include "qemu-common.h" #include "qemu/error-report.h" #include "cpu.h" +#include "cpu-models.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" #include "sysemu/hw_accel.h" @@ -2108,9 +2109,9 @@ void kvmppc_set_papr(PowerPCCPU *cpu) cap_papr = 1; } -int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version) +int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr) { - return kvm_set_one_reg(CPU(cpu), KVM_REG_PPC_ARCH_COMPAT, &cpu_version); + return kvm_set_one_reg(CPU(cpu), KVM_REG_PPC_ARCH_COMPAT, &compat_pvr); } void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy) @@ -2412,6 +2413,7 @@ static int kvm_ppc_register_host_cpu_type(void) }; PowerPCCPUClass *pvr_pcc; DeviceClass *dc; + int i; pvr_pcc = kvm_ppc_get_host_cpu_class(); if (pvr_pcc == NULL) { @@ -2420,13 +2422,6 @@ static int kvm_ppc_register_host_cpu_type(void) type_info.parent = object_class_get_name(OBJECT_CLASS(pvr_pcc)); type_register(&type_info); - /* Register generic family CPU class for a family */ - pvr_pcc = ppc_cpu_get_family_class(pvr_pcc); - dc = DEVICE_CLASS(pvr_pcc); - type_info.parent = object_class_get_name(OBJECT_CLASS(pvr_pcc)); - type_info.name = g_strdup_printf("%s-"TYPE_POWERPC_CPU, dc->desc); - type_register(&type_info); - #if defined(TARGET_PPC64) type_info.name = g_strdup_printf("%s-"TYPE_SPAPR_CPU_CORE, "host"); type_info.parent = TYPE_SPAPR_CPU_CORE, @@ -2436,14 +2431,29 @@ static int kvm_ppc_register_host_cpu_type(void) type_info.class_data = (void *) "host"; type_register(&type_info); g_free((void *)type_info.name); - - /* Register generic spapr CPU family class for current host CPU type */ - type_info.name = g_strdup_printf("%s-"TYPE_SPAPR_CPU_CORE, dc->desc); - type_info.class_data = (void *) dc->desc; - type_register(&type_info); - g_free((void *)type_info.name); #endif + /* + * Update generic CPU family class alias (e.g. on a POWER8NVL host, + * we want "POWER8" to be a "family" alias that points to the current + * host CPU type, too) + */ + dc = DEVICE_CLASS(ppc_cpu_get_family_class(pvr_pcc)); + for (i = 0; ppc_cpu_aliases[i].alias != NULL; i++) { + if (strcmp(ppc_cpu_aliases[i].alias, dc->desc) == 0) { + ObjectClass *oc = OBJECT_CLASS(pvr_pcc); + char *suffix; + + ppc_cpu_aliases[i].model = g_strdup(object_class_get_name(oc)); + suffix = strstr(ppc_cpu_aliases[i].model, "-"TYPE_POWERPC_CPU); + if (suffix) { + *suffix = 0; + } + ppc_cpu_aliases[i].oc = oc; + break; + } + } + return 0; } diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h index 4b43283913..151c00bac7 100644 --- a/target/ppc/kvm_ppc.h +++ b/target/ppc/kvm_ppc.h @@ -26,7 +26,7 @@ void kvmppc_enable_logical_ci_hcalls(void); void kvmppc_enable_set_mode_hcall(void); void kvmppc_enable_clear_ref_mod_hcalls(void); void kvmppc_set_papr(PowerPCCPU *cpu); -int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version); +int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr); void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy); int kvmppc_smt_threads(void); int kvmppc_clear_tsr_bits(PowerPCCPU *cpu, uint32_t tsr_bits); @@ -123,7 +123,7 @@ static inline void kvmppc_set_papr(PowerPCCPU *cpu) { } -static inline int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version) +static inline int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr) { return 0; } diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index 1ab8a6eab4..e6383c6bfa 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -24,6 +24,7 @@ #include "helper_regs.h" #include "exec/cpu_ldst.h" +#include "internal.h" //#define DEBUG_OP @@ -284,6 +285,71 @@ STVE(stvewx, cpu_stl_data_ra, bswap32, u32) #undef I #undef LVE +#ifdef TARGET_PPC64 +#define GET_NB(rb) ((rb >> 56) & 0xFF) + +#define VSX_LXVL(name, lj) \ +void helper_##name(CPUPPCState *env, target_ulong addr, \ + target_ulong xt_num, target_ulong rb) \ +{ \ + int i; \ + ppc_vsr_t xt; \ + uint64_t nb = GET_NB(rb); \ + \ + xt.s128 = int128_zero(); \ + if (nb) { \ + nb = (nb >= 16) ? 16 : nb; \ + if (msr_le && !lj) { \ + for (i = 16; i > 16 - nb; i--) { \ + xt.VsrB(i - 1) = cpu_ldub_data_ra(env, addr, GETPC()); \ + addr = addr_add(env, addr, 1); \ + } \ + } else { \ + for (i = 0; i < nb; i++) { \ + xt.VsrB(i) = cpu_ldub_data_ra(env, addr, GETPC()); \ + addr = addr_add(env, addr, 1); \ + } \ + } \ + } \ + putVSR(xt_num, &xt, env); \ +} + +VSX_LXVL(lxvl, 0) +VSX_LXVL(lxvll, 1) +#undef VSX_LXVL + +#define VSX_STXVL(name, lj) \ +void helper_##name(CPUPPCState *env, target_ulong addr, \ + target_ulong xt_num, target_ulong rb) \ +{ \ + int i; \ + ppc_vsr_t xt; \ + target_ulong nb = GET_NB(rb); \ + \ + if (!nb) { \ + return; \ + } \ + getVSR(xt_num, &xt, env); \ + nb = (nb >= 16) ? 16 : nb; \ + if (msr_le && !lj) { \ + for (i = 16; i > 16 - nb; i--) { \ + cpu_stb_data_ra(env, addr, xt.VsrB(i - 1), GETPC()); \ + addr = addr_add(env, addr, 1); \ + } \ + } else { \ + for (i = 0; i < nb; i++) { \ + cpu_stb_data_ra(env, addr, xt.VsrB(i), GETPC()); \ + addr = addr_add(env, addr, 1); \ + } \ + } \ +} + +VSX_STXVL(stxvl, 0) +VSX_STXVL(stxvll, 1) +#undef VSX_STXVL +#undef GET_NB +#endif /* TARGET_PPC64 */ + #undef HI_IDX #undef LO_IDX diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index 0efc8c63fa..bb78fb5497 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -181,8 +181,8 @@ int ppc_store_slb(PowerPCCPU *cpu, target_ulong slot, slb->vsid = vsid; slb->sps = sps; - LOG_SLB("%s: %d " TARGET_FMT_lx " - " TARGET_FMT_lx " => %016" PRIx64 - " %016" PRIx64 "\n", __func__, slot, esid, vsid, + LOG_SLB("%s: " TARGET_FMT_lu " " TARGET_FMT_lx " - " TARGET_FMT_lx + " => %016" PRIx64 " %016" PRIx64 "\n", __func__, slot, esid, vsid, slb->esid, slb->vsid); return 0; diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h index ab5d347a53..7a0b7fca41 100644 --- a/target/ppc/mmu-hash64.h +++ b/target/ppc/mmu-hash64.h @@ -85,7 +85,7 @@ void ppc_hash64_update_rmls(CPUPPCState *env); #define HPTE64_R_C 0x0000000000000080ULL #define HPTE64_R_R 0x0000000000000100ULL #define HPTE64_R_KEY_LO 0x0000000000000e00ULL -#define HPTE64_R_KEY(x) ((((x) & HPTE64_R_KEY_HI) >> 60) | \ +#define HPTE64_R_KEY(x) ((((x) & HPTE64_R_KEY_HI) >> 57) | \ (((x) & HPTE64_R_KEY_LO) >> 9)) #define HPTE64_V_1TB_SEG 0x4000000000000000ULL diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 121218087f..b48abaedfb 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -422,157 +422,6 @@ typedef struct opcode_t { #define CHK_NONE - -/*****************************************************************************/ -/*** Instruction decoding ***/ -#define EXTRACT_HELPER(name, shift, nb) \ -static inline uint32_t name(uint32_t opcode) \ -{ \ - return (opcode >> (shift)) & ((1 << (nb)) - 1); \ -} - -#define EXTRACT_SHELPER(name, shift, nb) \ -static inline int32_t name(uint32_t opcode) \ -{ \ - return (int16_t)((opcode >> (shift)) & ((1 << (nb)) - 1)); \ -} - -#define EXTRACT_HELPER_SPLIT(name, shift1, nb1, shift2, nb2) \ -static inline uint32_t name(uint32_t opcode) \ -{ \ - return (((opcode >> (shift1)) & ((1 << (nb1)) - 1)) << nb2) | \ - ((opcode >> (shift2)) & ((1 << (nb2)) - 1)); \ -} - -#define EXTRACT_HELPER_DXFORM(name, \ - d0_bits, shift_op_d0, shift_d0, \ - d1_bits, shift_op_d1, shift_d1, \ - d2_bits, shift_op_d2, shift_d2) \ -static inline int16_t name(uint32_t opcode) \ -{ \ - return \ - (((opcode >> (shift_op_d0)) & ((1 << (d0_bits)) - 1)) << (shift_d0)) | \ - (((opcode >> (shift_op_d1)) & ((1 << (d1_bits)) - 1)) << (shift_d1)) | \ - (((opcode >> (shift_op_d2)) & ((1 << (d2_bits)) - 1)) << (shift_d2)); \ -} - - -/* Opcode part 1 */ -EXTRACT_HELPER(opc1, 26, 6); -/* Opcode part 2 */ -EXTRACT_HELPER(opc2, 1, 5); -/* Opcode part 3 */ -EXTRACT_HELPER(opc3, 6, 5); -/* Opcode part 4 */ -EXTRACT_HELPER(opc4, 16, 5); -/* Update Cr0 flags */ -EXTRACT_HELPER(Rc, 0, 1); -/* Update Cr6 flags (Altivec) */ -EXTRACT_HELPER(Rc21, 10, 1); -/* Destination */ -EXTRACT_HELPER(rD, 21, 5); -/* Source */ -EXTRACT_HELPER(rS, 21, 5); -/* First operand */ -EXTRACT_HELPER(rA, 16, 5); -/* Second operand */ -EXTRACT_HELPER(rB, 11, 5); -/* Third operand */ -EXTRACT_HELPER(rC, 6, 5); -/*** Get CRn ***/ -EXTRACT_HELPER(crfD, 23, 3); -EXTRACT_HELPER(crfS, 18, 3); -EXTRACT_HELPER(crbD, 21, 5); -EXTRACT_HELPER(crbA, 16, 5); -EXTRACT_HELPER(crbB, 11, 5); -/* SPR / TBL */ -EXTRACT_HELPER(_SPR, 11, 10); -static inline uint32_t SPR(uint32_t opcode) -{ - uint32_t sprn = _SPR(opcode); - - return ((sprn >> 5) & 0x1F) | ((sprn & 0x1F) << 5); -} -/*** Get constants ***/ -/* 16 bits signed immediate value */ -EXTRACT_SHELPER(SIMM, 0, 16); -/* 16 bits unsigned immediate value */ -EXTRACT_HELPER(UIMM, 0, 16); -/* 5 bits signed immediate value */ -EXTRACT_HELPER(SIMM5, 16, 5); -/* 5 bits signed immediate value */ -EXTRACT_HELPER(UIMM5, 16, 5); -/* 4 bits unsigned immediate value */ -EXTRACT_HELPER(UIMM4, 16, 4); -/* Bit count */ -EXTRACT_HELPER(NB, 11, 5); -/* Shift count */ -EXTRACT_HELPER(SH, 11, 5); -/* Vector shift count */ -EXTRACT_HELPER(VSH, 6, 4); -/* Mask start */ -EXTRACT_HELPER(MB, 6, 5); -/* Mask end */ -EXTRACT_HELPER(ME, 1, 5); -/* Trap operand */ -EXTRACT_HELPER(TO, 21, 5); - -EXTRACT_HELPER(CRM, 12, 8); - -#ifndef CONFIG_USER_ONLY -EXTRACT_HELPER(SR, 16, 4); -#endif - -/* mtfsf/mtfsfi */ -EXTRACT_HELPER(FPBF, 23, 3); -EXTRACT_HELPER(FPIMM, 12, 4); -EXTRACT_HELPER(FPL, 25, 1); -EXTRACT_HELPER(FPFLM, 17, 8); -EXTRACT_HELPER(FPW, 16, 1); - -/* addpcis */ -EXTRACT_HELPER_DXFORM(DX, 10, 6, 6, 5, 16, 1, 1, 0, 0) -#if defined(TARGET_PPC64) -/* darn */ -EXTRACT_HELPER(L, 16, 2); -#endif - -/*** Jump target decoding ***/ -/* Immediate address */ -static inline target_ulong LI(uint32_t opcode) -{ - return (opcode >> 0) & 0x03FFFFFC; -} - -static inline uint32_t BD(uint32_t opcode) -{ - return (opcode >> 0) & 0xFFFC; -} - -EXTRACT_HELPER(BO, 21, 5); -EXTRACT_HELPER(BI, 16, 5); -/* Absolute/relative address */ -EXTRACT_HELPER(AA, 1, 1); -/* Link */ -EXTRACT_HELPER(LK, 0, 1); - -/* DFP Z22-form */ -EXTRACT_HELPER(DCM, 10, 6) - -/* DFP Z23-form */ -EXTRACT_HELPER(RMC, 9, 2) - -EXTRACT_HELPER_SPLIT(xT, 0, 1, 21, 5); -EXTRACT_HELPER_SPLIT(xS, 0, 1, 21, 5); -EXTRACT_HELPER_SPLIT(xA, 2, 1, 16, 5); -EXTRACT_HELPER_SPLIT(xB, 1, 1, 11, 5); -EXTRACT_HELPER_SPLIT(xC, 3, 1, 6, 5); -EXTRACT_HELPER(DM, 8, 2); -EXTRACT_HELPER(UIM, 16, 2); -EXTRACT_HELPER(SHW, 8, 2); -EXTRACT_HELPER(SP, 19, 2); -EXTRACT_HELPER(IMM8, 11, 8); - /*****************************************************************************/ /* PowerPC instructions table */ @@ -763,17 +612,17 @@ static inline void gen_op_cmp(TCGv arg0, TCGv arg1, int s, int crf) tcg_gen_setcond_tl((s ? TCG_COND_LT: TCG_COND_LTU), t0, arg0, arg1); tcg_gen_trunc_tl_i32(t1, t0); - tcg_gen_shli_i32(t1, t1, CRF_LT); + tcg_gen_shli_i32(t1, t1, CRF_LT_BIT); tcg_gen_or_i32(cpu_crf[crf], cpu_crf[crf], t1); tcg_gen_setcond_tl((s ? TCG_COND_GT: TCG_COND_GTU), t0, arg0, arg1); tcg_gen_trunc_tl_i32(t1, t0); - tcg_gen_shli_i32(t1, t1, CRF_GT); + tcg_gen_shli_i32(t1, t1, CRF_GT_BIT); tcg_gen_or_i32(cpu_crf[crf], cpu_crf[crf], t1); tcg_gen_setcond_tl(TCG_COND_EQ, t0, arg0, arg1); tcg_gen_trunc_tl_i32(t1, t0); - tcg_gen_shli_i32(t1, t1, CRF_EQ); + tcg_gen_shli_i32(t1, t1, CRF_EQ_BIT); tcg_gen_or_i32(cpu_crf[crf], cpu_crf[crf], t1); tcg_temp_free(t0); @@ -899,7 +748,7 @@ static void gen_cmprb(DisasContext *ctx) tcg_gen_and_i32(src2lo, src2lo, src2hi); tcg_gen_or_i32(crf, crf, src2lo); } - tcg_gen_shli_i32(crf, crf, CRF_GT); + tcg_gen_shli_i32(crf, crf, CRF_GT_BIT); tcg_temp_free_i32(src1); tcg_temp_free_i32(src2); tcg_temp_free_i32(src2lo); @@ -3148,7 +2997,7 @@ static void gen_conditional_store(DisasContext *ctx, TCGv EA, tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); l1 = gen_new_label(); tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, l1); - tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 1 << CRF_EQ); + tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], CRF_EQ); tcg_gen_qemu_st_tl(cpu_gpr[reg], EA, ctx->mem_idx, memop); gen_set_label(l1); tcg_gen_movi_tl(cpu_reserve, -1); @@ -3242,7 +3091,7 @@ static void gen_stqcx_(DisasContext *ctx) tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); l1 = gen_new_label(); tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, l1); - tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 1 << CRF_EQ); + tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], CRF_EQ); if (unlikely(ctx->le_mode)) { gpr1 = cpu_gpr[reg + 1]; @@ -3323,6 +3172,11 @@ static void gen_nap(DisasContext *ctx) #endif /* defined(CONFIG_USER_ONLY) */ } +static void gen_stop(DisasContext *ctx) +{ + gen_nap(ctx); +} + static void gen_sleep(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) @@ -4423,7 +4277,7 @@ static void gen_slbfee_(DisasContext *ctx) l2 = gen_new_label(); tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[rS(ctx->opcode)], -1, l1); - tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 1 << CRF_EQ); + tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], CRF_EQ); tcg_gen_br(l2); gen_set_label(l1); tcg_gen_movi_tl(cpu_gpr[rS(ctx->opcode)], 0); @@ -6166,6 +6020,10 @@ GEN_TM_NOOP(tabortwci); GEN_TM_NOOP(tabortdc); GEN_TM_NOOP(tabortdci); GEN_TM_NOOP(tsr); +static inline void gen_cp_abort(DisasContext *ctx) +{ + // Do Nothing +} static void gen_tcheck(DisasContext *ctx) { @@ -6223,6 +6081,67 @@ GEN_TM_PRIV_NOOP(trechkpt); #include "translate/spe-impl.inc.c" +/* Handles lfdp, lxsd, lxssp */ +static void gen_dform39(DisasContext *ctx) +{ + switch (ctx->opcode & 0x3) { + case 0: /* lfdp */ + if (ctx->insns_flags2 & PPC2_ISA205) { + return gen_lfdp(ctx); + } + break; + case 2: /* lxsd */ + if (ctx->insns_flags2 & PPC2_ISA300) { + return gen_lxsd(ctx); + } + break; + case 3: /* lxssp */ + if (ctx->insns_flags2 & PPC2_ISA300) { + return gen_lxssp(ctx); + } + break; + } + return gen_invalid(ctx); +} + +/* handles stfdp, lxv, stxsd, stxssp lxvx */ +static void gen_dform3D(DisasContext *ctx) +{ + if ((ctx->opcode & 3) == 1) { /* DQ-FORM */ + switch (ctx->opcode & 0x7) { + case 1: /* lxv */ + if (ctx->insns_flags2 & PPC2_ISA300) { + return gen_lxv(ctx); + } + break; + case 5: /* stxv */ + if (ctx->insns_flags2 & PPC2_ISA300) { + return gen_stxv(ctx); + } + break; + } + } else { /* DS-FORM */ + switch (ctx->opcode & 0x3) { + case 0: /* stfdp */ + if (ctx->insns_flags2 & PPC2_ISA205) { + return gen_stfdp(ctx); + } + break; + case 2: /* stxsd */ + if (ctx->insns_flags2 & PPC2_ISA300) { + return gen_stxsd(ctx); + } + break; + case 3: /* stxssp */ + if (ctx->insns_flags2 & PPC2_ISA300) { + return gen_stxssp(ctx); + } + break; + } + } + return gen_invalid(ctx); +} + static opcode_t opcodes[] = { GEN_HANDLER(invalid, 0x00, 0x00, 0x00, 0xFFFFFFFF, PPC_NONE), GEN_HANDLER(cmp, 0x1F, 0x00, 0x00, 0x00400000, PPC_INTEGER), @@ -6255,6 +6174,7 @@ GEN_HANDLER2(andi_, "andi.", 0x1C, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER2(andis_, "andis.", 0x1D, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER(cntlzw, 0x1F, 0x1A, 0x00, 0x00000000, PPC_INTEGER), GEN_HANDLER_E(cnttzw, 0x1F, 0x1A, 0x10, 0x00000000, PPC_NONE, PPC2_ISA300), +GEN_HANDLER_E(cp_abort, 0x1F, 0x06, 0x1A, 0x03FFF801, PPC_NONE, PPC2_ISA300), GEN_HANDLER(or, 0x1F, 0x1C, 0x0D, 0x00000000, PPC_INTEGER), GEN_HANDLER(xor, 0x1F, 0x1C, 0x09, 0x00000000, PPC_INTEGER), GEN_HANDLER(ori, 0x18, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), @@ -6295,6 +6215,10 @@ GEN_HANDLER(ld, 0x3A, 0xFF, 0xFF, 0x00000000, PPC_64B), GEN_HANDLER(lq, 0x38, 0xFF, 0xFF, 0x00000000, PPC_64BX), GEN_HANDLER(std, 0x3E, 0xFF, 0xFF, 0x00000000, PPC_64B), #endif +/* handles lfdp, lxsd, lxssp */ +GEN_HANDLER_E(dform39, 0x39, 0xFF, 0xFF, 0x00000000, PPC_NONE, PPC2_ISA205), +/* handles stfdp, lxv, stxsd, stxssp, stxv */ +GEN_HANDLER_E(dform3D, 0x3D, 0xFF, 0xFF, 0x00000000, PPC_NONE, PPC2_ISA205), GEN_HANDLER(lmw, 0x2E, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER(stmw, 0x2F, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER(lswi, 0x1F, 0x15, 0x12, 0x00000001, PPC_STRING), @@ -6326,6 +6250,7 @@ GEN_HANDLER(mcrf, 0x13, 0x00, 0xFF, 0x00000001, PPC_INTEGER), GEN_HANDLER(rfi, 0x13, 0x12, 0x01, 0x03FF8001, PPC_FLOW), #if defined(TARGET_PPC64) GEN_HANDLER(rfid, 0x13, 0x12, 0x00, 0x03FF8001, PPC_64B), +GEN_HANDLER_E(stop, 0x13, 0x12, 0x0b, 0x03FFF801, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(doze, 0x13, 0x12, 0x0c, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206), GEN_HANDLER_E(nap, 0x13, 0x12, 0x0d, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206), GEN_HANDLER_E(sleep, 0x13, 0x12, 0x0e, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206), @@ -6910,6 +6835,9 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, } #endif + if (env->spr_cb[SPR_LPCR].name) + cpu_fprintf(f, " LPCR " TARGET_FMT_lx "\n", env->spr[SPR_LPCR]); + switch (env->mmu_model) { case POWERPC_MMU_32B: case POWERPC_MMU_601: diff --git a/target/ppc/translate/fp-impl.inc.c b/target/ppc/translate/fp-impl.inc.c index 872af7b56f..2fbd4d4f38 100644 --- a/target/ppc/translate/fp-impl.inc.c +++ b/target/ppc/translate/fp-impl.inc.c @@ -9,9 +9,9 @@ static inline void gen_reset_fpstatus(void) gen_helper_reset_fpstatus(cpu_env); } -static inline void gen_compute_fprf(TCGv_i64 arg) +static inline void gen_compute_fprf_float64(TCGv_i64 arg) { - gen_helper_compute_fprf(cpu_env, arg); + gen_helper_compute_fprf_float64(cpu_env, arg); gen_helper_float_check_status(cpu_env); } @@ -47,7 +47,7 @@ static void gen_f##name(DisasContext *ctx) \ cpu_fpr[rD(ctx->opcode)]); \ } \ if (set_fprf) { \ - gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); \ + gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]); \ } \ if (unlikely(Rc(ctx->opcode) != 0)) { \ gen_set_cr1_from_fpscr(ctx); \ @@ -74,7 +74,7 @@ static void gen_f##name(DisasContext *ctx) \ cpu_fpr[rD(ctx->opcode)]); \ } \ if (set_fprf) { \ - gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); \ + gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]); \ } \ if (unlikely(Rc(ctx->opcode) != 0)) { \ gen_set_cr1_from_fpscr(ctx); \ @@ -100,7 +100,7 @@ static void gen_f##name(DisasContext *ctx) \ cpu_fpr[rD(ctx->opcode)]); \ } \ if (set_fprf) { \ - gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); \ + gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]); \ } \ if (unlikely(Rc(ctx->opcode) != 0)) { \ gen_set_cr1_from_fpscr(ctx); \ @@ -121,7 +121,7 @@ static void gen_f##name(DisasContext *ctx) \ gen_helper_f##name(cpu_fpr[rD(ctx->opcode)], cpu_env, \ cpu_fpr[rB(ctx->opcode)]); \ if (set_fprf) { \ - gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); \ + gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]); \ } \ if (unlikely(Rc(ctx->opcode) != 0)) { \ gen_set_cr1_from_fpscr(ctx); \ @@ -139,7 +139,7 @@ static void gen_f##name(DisasContext *ctx) \ gen_helper_f##name(cpu_fpr[rD(ctx->opcode)], cpu_env, \ cpu_fpr[rB(ctx->opcode)]); \ if (set_fprf) { \ - gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); \ + gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]); \ } \ if (unlikely(Rc(ctx->opcode) != 0)) { \ gen_set_cr1_from_fpscr(ctx); \ @@ -174,7 +174,7 @@ static void gen_frsqrtes(DisasContext *ctx) 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)]); + gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]); if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_cr1_from_fpscr(ctx); } @@ -196,7 +196,7 @@ static void gen_fsqrt(DisasContext *ctx) gen_reset_fpstatus(); gen_helper_fsqrt(cpu_fpr[rD(ctx->opcode)], cpu_env, cpu_fpr[rB(ctx->opcode)]); - gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); + gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]); if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_cr1_from_fpscr(ctx); } @@ -213,7 +213,7 @@ static void gen_fsqrts(DisasContext *ctx) 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)]); + gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]); if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_cr1_from_fpscr(ctx); } diff --git a/target/ppc/translate/fp-ops.inc.c b/target/ppc/translate/fp-ops.inc.c index d36ab4ecc3..3c6d05a074 100644 --- a/target/ppc/translate/fp-ops.inc.c +++ b/target/ppc/translate/fp-ops.inc.c @@ -68,7 +68,6 @@ GEN_LDFS(lfd, ld64, 0x12, PPC_FLOAT) GEN_LDFS(lfs, ld32fs, 0x10, PPC_FLOAT) GEN_HANDLER_E(lfiwax, 0x1f, 0x17, 0x1a, 0x00000001, PPC_NONE, PPC2_ISA205), GEN_HANDLER_E(lfiwzx, 0x1f, 0x17, 0x1b, 0x1, PPC_NONE, PPC2_FP_CVT_ISA206), -GEN_HANDLER_E(lfdp, 0x39, 0xFF, 0xFF, 0x00200003, PPC_NONE, PPC2_ISA205), GEN_HANDLER_E(lfdpx, 0x1F, 0x17, 0x18, 0x00200001, PPC_NONE, PPC2_ISA205), #define GEN_STF(name, stop, opc, type) \ @@ -88,7 +87,6 @@ GEN_STXF(name, stop, 0x17, op | 0x00, type) GEN_STFS(stfd, st64_i64, 0x16, PPC_FLOAT) GEN_STFS(stfs, st32fs, 0x14, PPC_FLOAT) GEN_STXF(stfiw, st32fiw, 0x17, 0x1E, PPC_FLOAT_STFIWX) -GEN_HANDLER_E(stfdp, 0x3D, 0xFF, 0xFF, 0x00200003, PPC_NONE, PPC2_ISA205), GEN_HANDLER_E(stfdpx, 0x1F, 0x17, 0x1C, 0x00200001, PPC_NONE, PPC2_ISA205), GEN_HANDLER(frsqrtes, 0x3B, 0x1A, 0xFF, 0x001F07C0, PPC_FLOAT_FRSQRTES), diff --git a/target/ppc/translate/vmx-impl.inc.c b/target/ppc/translate/vmx-impl.inc.c index 7143eb3a39..3cb6fc2926 100644 --- a/target/ppc/translate/vmx-impl.inc.c +++ b/target/ppc/translate/vmx-impl.inc.c @@ -340,6 +340,19 @@ static void glue(gen_, name0##_##name1)(DisasContext *ctx) \ } \ } +#define GEN_VXFORM_HETRO(name, opc2, opc3) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + TCGv_ptr rb; \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + rb = gen_avr_ptr(rB(ctx->opcode)); \ + gen_helper_##name(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], rb); \ + tcg_temp_free_ptr(rb); \ +} + GEN_VXFORM(vaddubm, 0, 0); GEN_VXFORM_DUAL_EXT(vaddubm, PPC_ALTIVEC, PPC_NONE, 0, \ vmul10cuq, PPC_NONE, PPC2_ISA300, 0x0000F800) @@ -525,6 +538,16 @@ GEN_VXFORM_ENV(vaddfp, 5, 0); GEN_VXFORM_ENV(vsubfp, 5, 1); GEN_VXFORM_ENV(vmaxfp, 5, 16); GEN_VXFORM_ENV(vminfp, 5, 17); +GEN_VXFORM_HETRO(vextublx, 6, 24) +GEN_VXFORM_HETRO(vextuhlx, 6, 25) +GEN_VXFORM_HETRO(vextuwlx, 6, 26) +GEN_VXFORM_DUAL(vmrgow, PPC_NONE, PPC2_ALTIVEC_207, + vextuwlx, PPC_NONE, PPC2_ISA300) +GEN_VXFORM_HETRO(vextubrx, 6, 28) +GEN_VXFORM_HETRO(vextuhrx, 6, 29) +GEN_VXFORM_HETRO(vextuwrx, 6, 30) +GEN_VXFORM_DUAL(vmrgew, PPC_NONE, PPC2_ALTIVEC_207, \ + vextuwrx, PPC_NONE, PPC2_ISA300) #define GEN_VXRFORM1(opname, name, str, opc2, opc3) \ static void glue(gen_, name)(DisasContext *ctx) \ @@ -989,10 +1012,25 @@ GEN_BCD2(bcdcfn) GEN_BCD2(bcdctn) GEN_BCD2(bcdcfz) GEN_BCD2(bcdctz) +GEN_BCD2(bcdcfsq) +GEN_BCD2(bcdctsq) +GEN_BCD2(bcdsetsgn) +GEN_BCD(bcdcpsgn); +GEN_BCD(bcds); +GEN_BCD(bcdus); +GEN_BCD(bcdsr); +GEN_BCD(bcdtrunc); +GEN_BCD(bcdutrunc); static void gen_xpnd04_1(DisasContext *ctx) { switch (opc4(ctx->opcode)) { + case 0: + gen_bcdctsq(ctx); + break; + case 2: + gen_bcdcfsq(ctx); + break; case 4: gen_bcdctz(ctx); break; @@ -1005,6 +1043,9 @@ static void gen_xpnd04_1(DisasContext *ctx) case 7: gen_bcdcfn(ctx); break; + case 31: + gen_bcdsetsgn(ctx); + break; default: gen_invalid(ctx); break; @@ -1014,6 +1055,12 @@ static void gen_xpnd04_1(DisasContext *ctx) static void gen_xpnd04_2(DisasContext *ctx) { switch (opc4(ctx->opcode)) { + case 0: + gen_bcdctsq(ctx); + break; + case 2: + gen_bcdcfsq(ctx); + break; case 4: gen_bcdctz(ctx); break; @@ -1023,12 +1070,16 @@ static void gen_xpnd04_2(DisasContext *ctx) case 7: gen_bcdcfn(ctx); break; + case 31: + gen_bcdsetsgn(ctx); + break; default: gen_invalid(ctx); break; } } + GEN_VXFORM_DUAL(vsubcuw, PPC_ALTIVEC, PPC_NONE, \ xpnd04_1, PPC_NONE, PPC2_ISA300) GEN_VXFORM_DUAL(vsubsws, PPC_ALTIVEC, PPC_NONE, \ @@ -1042,6 +1093,19 @@ GEN_VXFORM_DUAL(vsubuhm, PPC_ALTIVEC, PPC_NONE, \ bcdsub, PPC_NONE, PPC2_ALTIVEC_207) GEN_VXFORM_DUAL(vsubuhs, PPC_ALTIVEC, PPC_NONE, \ bcdsub, PPC_NONE, PPC2_ALTIVEC_207) +GEN_VXFORM_DUAL(vaddshs, PPC_ALTIVEC, PPC_NONE, \ + bcdcpsgn, PPC_NONE, PPC2_ISA300) +GEN_VXFORM_DUAL(vsubudm, PPC2_ALTIVEC_207, PPC_NONE, \ + bcds, PPC_NONE, PPC2_ISA300) +GEN_VXFORM_DUAL(vsubuwm, PPC_ALTIVEC, PPC_NONE, \ + bcdus, PPC_NONE, PPC2_ISA300) +GEN_VXFORM_DUAL(vsubsbs, PPC_ALTIVEC, PPC_NONE, \ + bcdtrunc, PPC_NONE, PPC2_ISA300) +GEN_VXFORM_DUAL(vsubuqm, PPC2_ALTIVEC_207, PPC_NONE, \ + bcdtrunc, PPC_NONE, PPC2_ISA300) +GEN_VXFORM_DUAL(vsubcuq, PPC2_ALTIVEC_207, PPC_NONE, \ + bcdutrunc, PPC_NONE, PPC2_ISA300) + static void gen_vsbox(DisasContext *ctx) { diff --git a/target/ppc/translate/vmx-ops.inc.c b/target/ppc/translate/vmx-ops.inc.c index f02b3bed50..139f80cb24 100644 --- a/target/ppc/translate/vmx-ops.inc.c +++ b/target/ppc/translate/vmx-ops.inc.c @@ -61,8 +61,9 @@ GEN_VXFORM(vadduwm, 0, 2), GEN_VXFORM_207(vaddudm, 0, 3), GEN_VXFORM_DUAL(vsububm, bcdadd, 0, 16, PPC_ALTIVEC, PPC_NONE), GEN_VXFORM_DUAL(vsubuhm, bcdsub, 0, 17, PPC_ALTIVEC, PPC_NONE), -GEN_VXFORM(vsubuwm, 0, 18), -GEN_VXFORM_207(vsubudm, 0, 19), +GEN_VXFORM_DUAL(vsubuwm, bcdus, 0, 18, PPC_ALTIVEC, PPC2_ISA300), +GEN_VXFORM_DUAL(vsubudm, bcds, 0, 19, PPC2_ALTIVEC_207, PPC2_ISA300), +GEN_VXFORM_300(bcds, 0, 27), GEN_VXFORM(vmaxub, 1, 0), GEN_VXFORM(vmaxuh, 1, 1), GEN_VXFORM(vmaxuw, 1, 2), @@ -91,8 +92,12 @@ GEN_VXFORM(vmrghw, 6, 2), GEN_VXFORM(vmrglb, 6, 4), GEN_VXFORM(vmrglh, 6, 5), GEN_VXFORM(vmrglw, 6, 6), -GEN_VXFORM_207(vmrgew, 6, 30), -GEN_VXFORM_207(vmrgow, 6, 26), +GEN_VXFORM_300(vextublx, 6, 24), +GEN_VXFORM_300(vextuhlx, 6, 25), +GEN_VXFORM_DUAL(vmrgow, vextuwlx, 6, 26, PPC_NONE, PPC2_ALTIVEC_207), +GEN_VXFORM_300(vextubrx, 6, 28), +GEN_VXFORM_300(vextuhrx, 6, 29), +GEN_VXFORM_DUAL(vmrgew, vextuwrx, 6, 30, PPC_NONE, PPC2_ALTIVEC_207), GEN_VXFORM(vmuloub, 4, 0), GEN_VXFORM(vmulouh, 4, 1), GEN_VXFORM_DUAL(vmulouw, vmuluwm, 4, 2, PPC_ALTIVEC, PPC_NONE), @@ -127,23 +132,25 @@ GEN_HANDLER_E_2(vprtybd, 0x4, 0x1, 0x18, 9, 0, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E_2(vprtybq, 0x4, 0x1, 0x18, 10, 0, PPC_NONE, PPC2_ISA300), GEN_VXFORM_DUAL(vsubcuw, xpnd04_1, 0, 22, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM_300(bcdsr, 0, 23), +GEN_VXFORM_300(bcdsr, 0, 31), GEN_VXFORM_DUAL(vaddubs, vmul10uq, 0, 8, PPC_ALTIVEC, PPC_NONE), GEN_VXFORM_DUAL(vadduhs, vmul10euq, 0, 9, PPC_ALTIVEC, PPC_NONE), GEN_VXFORM(vadduws, 0, 10), GEN_VXFORM(vaddsbs, 0, 12), -GEN_VXFORM(vaddshs, 0, 13), +GEN_VXFORM_DUAL(vaddshs, bcdcpsgn, 0, 13, PPC_ALTIVEC, PPC_NONE), GEN_VXFORM(vaddsws, 0, 14), GEN_VXFORM_DUAL(vsububs, bcdadd, 0, 24, PPC_ALTIVEC, PPC_NONE), GEN_VXFORM_DUAL(vsubuhs, bcdsub, 0, 25, PPC_ALTIVEC, PPC_NONE), GEN_VXFORM(vsubuws, 0, 26), -GEN_VXFORM(vsubsbs, 0, 28), +GEN_VXFORM_DUAL(vsubsbs, bcdtrunc, 0, 28, PPC_NONE, PPC2_ISA300), GEN_VXFORM(vsubshs, 0, 29), GEN_VXFORM_DUAL(vsubsws, xpnd04_2, 0, 30, PPC_ALTIVEC, PPC_NONE), GEN_VXFORM_207(vadduqm, 0, 4), GEN_VXFORM_207(vaddcuq, 0, 5), GEN_VXFORM_DUAL(vaddeuqm, vaddecuq, 30, 0xFF, PPC_NONE, PPC2_ALTIVEC_207), -GEN_VXFORM_207(vsubuqm, 0, 20), -GEN_VXFORM_207(vsubcuq, 0, 21), +GEN_VXFORM_DUAL(vsubuqm, bcdtrunc, 0, 20, PPC2_ALTIVEC_207, PPC2_ISA300), +GEN_VXFORM_DUAL(vsubcuq, bcdutrunc, 0, 21, PPC2_ALTIVEC_207, PPC2_ISA300), GEN_VXFORM_DUAL(vsubeuqm, vsubecuq, 31, 0xFF, PPC_NONE, PPC2_ALTIVEC_207), GEN_VXFORM(vrlb, 2, 0), GEN_VXFORM(vrlh, 2, 1), diff --git a/target/ppc/translate/vsx-impl.inc.c b/target/ppc/translate/vsx-impl.inc.c index 5a27be4bd4..a44c0034a8 100644 --- a/target/ppc/translate/vsx-impl.inc.c +++ b/target/ppc/translate/vsx-impl.inc.c @@ -190,6 +190,109 @@ static void gen_lxvb16x(DisasContext *ctx) tcg_temp_free(EA); } +#define VSX_VECTOR_LOAD_STORE(name, op, indexed) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + int xt; \ + TCGv EA; \ + TCGv_i64 xth, xtl; \ + \ + if (indexed) { \ + xt = xT(ctx->opcode); \ + } else { \ + xt = DQxT(ctx->opcode); \ + } \ + xth = cpu_vsrh(xt); \ + xtl = cpu_vsrl(xt); \ + \ + if (xt < 32) { \ + if (unlikely(!ctx->vsx_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VSXU); \ + return; \ + } \ + } else { \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + } \ + gen_set_access_type(ctx, ACCESS_INT); \ + EA = tcg_temp_new(); \ + if (indexed) { \ + gen_addr_reg_index(ctx, EA); \ + } else { \ + gen_addr_imm_index(ctx, EA, 0x0F); \ + } \ + if (ctx->le_mode) { \ + tcg_gen_qemu_##op(xtl, EA, ctx->mem_idx, MO_LEQ); \ + tcg_gen_addi_tl(EA, EA, 8); \ + tcg_gen_qemu_##op(xth, EA, ctx->mem_idx, MO_LEQ); \ + } else { \ + tcg_gen_qemu_##op(xth, EA, ctx->mem_idx, MO_BEQ); \ + tcg_gen_addi_tl(EA, EA, 8); \ + tcg_gen_qemu_##op(xtl, EA, ctx->mem_idx, MO_BEQ); \ + } \ + tcg_temp_free(EA); \ +} + +VSX_VECTOR_LOAD_STORE(lxv, ld_i64, 0) +VSX_VECTOR_LOAD_STORE(stxv, st_i64, 0) +VSX_VECTOR_LOAD_STORE(lxvx, ld_i64, 1) +VSX_VECTOR_LOAD_STORE(stxvx, st_i64, 1) + +#ifdef TARGET_PPC64 +#define VSX_VECTOR_LOAD_STORE_LENGTH(name) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv EA, xt; \ + \ + if (xT(ctx->opcode) < 32) { \ + if (unlikely(!ctx->vsx_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VSXU); \ + return; \ + } \ + } else { \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + } \ + EA = tcg_temp_new(); \ + xt = tcg_const_tl(xT(ctx->opcode)); \ + gen_set_access_type(ctx, ACCESS_INT); \ + gen_addr_register(ctx, EA); \ + gen_helper_##name(cpu_env, EA, xt, cpu_gpr[rB(ctx->opcode)]); \ + tcg_temp_free(EA); \ + tcg_temp_free(xt); \ +} + +VSX_VECTOR_LOAD_STORE_LENGTH(lxvl) +VSX_VECTOR_LOAD_STORE_LENGTH(lxvll) +VSX_VECTOR_LOAD_STORE_LENGTH(stxvl) +VSX_VECTOR_LOAD_STORE_LENGTH(stxvll) +#endif + +#define VSX_LOAD_SCALAR_DS(name, operation) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv EA; \ + TCGv_i64 xth = cpu_vsrh(rD(ctx->opcode) + 32); \ + \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_INT); \ + EA = tcg_temp_new(); \ + gen_addr_imm_index(ctx, EA, 0x03); \ + gen_qemu_##operation(ctx, xth, EA); \ + /* NOTE: cpu_vsrl is undefined */ \ + tcg_temp_free(EA); \ +} + +VSX_LOAD_SCALAR_DS(lxsd, ld64_i64) +VSX_LOAD_SCALAR_DS(lxssp, ld32fs) + #define VSX_STORE_SCALAR(name, operation) \ static void gen_##name(DisasContext *ctx) \ { \ @@ -311,6 +414,27 @@ static void gen_stxvb16x(DisasContext *ctx) tcg_temp_free(EA); } +#define VSX_STORE_SCALAR_DS(name, operation) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv EA; \ + TCGv_i64 xth = cpu_vsrh(rD(ctx->opcode) + 32); \ + \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + gen_set_access_type(ctx, ACCESS_INT); \ + EA = tcg_temp_new(); \ + gen_addr_imm_index(ctx, EA, 0x03); \ + gen_qemu_##operation(ctx, xth, EA); \ + /* NOTE: cpu_vsrl is undefined */ \ + tcg_temp_free(EA); \ +} + +VSX_LOAD_SCALAR_DS(stxsd, st64_i64) +VSX_LOAD_SCALAR_DS(stxssp, st32fs) + #define MV_VSRW(name, tcgop1, tcgop2, target, source) \ static void gen_##name(DisasContext *ctx) \ { \ @@ -517,6 +641,55 @@ VSX_SCALAR_MOVE(xsnabsdp, OP_NABS, SGN_MASK_DP) VSX_SCALAR_MOVE(xsnegdp, OP_NEG, SGN_MASK_DP) VSX_SCALAR_MOVE(xscpsgndp, OP_CPSGN, SGN_MASK_DP) +#define VSX_SCALAR_MOVE_QP(name, op, sgn_mask) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + int xa; \ + int xt = rD(ctx->opcode) + 32; \ + int xb = rB(ctx->opcode) + 32; \ + TCGv_i64 xah, xbh, xbl, sgm; \ + \ + if (unlikely(!ctx->vsx_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VSXU); \ + return; \ + } \ + xbh = tcg_temp_new_i64(); \ + xbl = tcg_temp_new_i64(); \ + sgm = tcg_temp_new_i64(); \ + tcg_gen_mov_i64(xbh, cpu_vsrh(xb)); \ + tcg_gen_mov_i64(xbl, cpu_vsrl(xb)); \ + tcg_gen_movi_i64(sgm, sgn_mask); \ + switch (op) { \ + case OP_ABS: \ + tcg_gen_andc_i64(xbh, xbh, sgm); \ + break; \ + case OP_NABS: \ + tcg_gen_or_i64(xbh, xbh, sgm); \ + break; \ + case OP_NEG: \ + tcg_gen_xor_i64(xbh, xbh, sgm); \ + break; \ + case OP_CPSGN: \ + xah = tcg_temp_new_i64(); \ + xa = rA(ctx->opcode) + 32; \ + tcg_gen_and_i64(xah, cpu_vsrh(xa), sgm); \ + tcg_gen_andc_i64(xbh, xbh, sgm); \ + tcg_gen_or_i64(xbh, xbh, xah); \ + tcg_temp_free_i64(xah); \ + break; \ + } \ + tcg_gen_mov_i64(cpu_vsrh(xt), xbh); \ + tcg_gen_mov_i64(cpu_vsrl(xt), xbl); \ + tcg_temp_free_i64(xbl); \ + tcg_temp_free_i64(xbh); \ + tcg_temp_free_i64(sgm); \ +} + +VSX_SCALAR_MOVE_QP(xsabsqp, OP_ABS, SGN_MASK_DP) +VSX_SCALAR_MOVE_QP(xsnabsqp, OP_NABS, SGN_MASK_DP) +VSX_SCALAR_MOVE_QP(xsnegqp, OP_NEG, SGN_MASK_DP) +VSX_SCALAR_MOVE_QP(xscpsgnqp, OP_CPSGN, SGN_MASK_DP) + #define VSX_VECTOR_MOVE(name, op, sgn_mask) \ static void glue(gen_, name)(DisasContext * ctx) \ { \ @@ -604,9 +777,12 @@ static void gen_##name(DisasContext * ctx) \ } GEN_VSX_HELPER_2(xsadddp, 0x00, 0x04, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsaddqp, 0x04, 0x00, 0, PPC2_ISA300) GEN_VSX_HELPER_2(xssubdp, 0x00, 0x05, 0, PPC2_VSX) GEN_VSX_HELPER_2(xsmuldp, 0x00, 0x06, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsmulqp, 0x04, 0x01, 0, PPC2_ISA300) GEN_VSX_HELPER_2(xsdivdp, 0x00, 0x07, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xsdivqp, 0x04, 0x11, 0, PPC2_ISA300) GEN_VSX_HELPER_2(xsredp, 0x14, 0x05, 0, PPC2_VSX) GEN_VSX_HELPER_2(xssqrtdp, 0x16, 0x04, 0, PPC2_VSX) GEN_VSX_HELPER_2(xsrsqrtedp, 0x14, 0x04, 0, PPC2_VSX) @@ -624,12 +800,23 @@ GEN_VSX_HELPER_2(xscmpeqdp, 0x0C, 0x00, 0, PPC2_ISA300) GEN_VSX_HELPER_2(xscmpgtdp, 0x0C, 0x01, 0, PPC2_ISA300) GEN_VSX_HELPER_2(xscmpgedp, 0x0C, 0x02, 0, PPC2_ISA300) GEN_VSX_HELPER_2(xscmpnedp, 0x0C, 0x03, 0, PPC2_ISA300) +GEN_VSX_HELPER_2(xscmpexpdp, 0x0C, 0x07, 0, PPC2_ISA300) +GEN_VSX_HELPER_2(xscmpexpqp, 0x04, 0x05, 0, PPC2_ISA300) GEN_VSX_HELPER_2(xscmpodp, 0x0C, 0x05, 0, PPC2_VSX) GEN_VSX_HELPER_2(xscmpudp, 0x0C, 0x04, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscmpoqp, 0x04, 0x04, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscmpuqp, 0x04, 0x14, 0, PPC2_VSX) GEN_VSX_HELPER_2(xsmaxdp, 0x00, 0x14, 0, PPC2_VSX) GEN_VSX_HELPER_2(xsmindp, 0x00, 0x15, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscvdphp, 0x16, 0x15, 0x11, PPC2_ISA300) GEN_VSX_HELPER_2(xscvdpsp, 0x12, 0x10, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscvdpqp, 0x04, 0x1A, 0x16, PPC2_ISA300) GEN_VSX_HELPER_XT_XB_ENV(xscvdpspn, 0x16, 0x10, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xscvqpdp, 0x04, 0x1A, 0x14, PPC2_ISA300) +GEN_VSX_HELPER_2(xscvqpsdz, 0x04, 0x1A, 0x19, PPC2_ISA300) +GEN_VSX_HELPER_2(xscvqpswz, 0x04, 0x1A, 0x09, PPC2_ISA300) +GEN_VSX_HELPER_2(xscvhpdp, 0x16, 0x15, 0x10, PPC2_ISA300) +GEN_VSX_HELPER_2(xscvsdqp, 0x04, 0x1A, 0x0A, PPC2_ISA300) GEN_VSX_HELPER_2(xscvspdp, 0x12, 0x14, 0, PPC2_VSX) GEN_VSX_HELPER_XT_XB_ENV(xscvspdpn, 0x16, 0x14, 0, PPC2_VSX207) GEN_VSX_HELPER_2(xscvdpsxds, 0x10, 0x15, 0, PPC2_VSX) @@ -637,6 +824,7 @@ GEN_VSX_HELPER_2(xscvdpsxws, 0x10, 0x05, 0, PPC2_VSX) GEN_VSX_HELPER_2(xscvdpuxds, 0x10, 0x14, 0, PPC2_VSX) GEN_VSX_HELPER_2(xscvdpuxws, 0x10, 0x04, 0, PPC2_VSX) GEN_VSX_HELPER_2(xscvsxddp, 0x10, 0x17, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscvudqp, 0x04, 0x1A, 0x02, PPC2_ISA300) GEN_VSX_HELPER_2(xscvuxddp, 0x10, 0x16, 0, PPC2_VSX) GEN_VSX_HELPER_2(xsrdpi, 0x12, 0x04, 0, PPC2_VSX) GEN_VSX_HELPER_2(xsrdpic, 0x16, 0x06, 0, PPC2_VSX) @@ -662,6 +850,9 @@ GEN_VSX_HELPER_2(xsnmsubasp, 0x04, 0x12, 0, PPC2_VSX207) GEN_VSX_HELPER_2(xsnmsubmsp, 0x04, 0x13, 0, PPC2_VSX207) GEN_VSX_HELPER_2(xscvsxdsp, 0x10, 0x13, 0, PPC2_VSX207) GEN_VSX_HELPER_2(xscvuxdsp, 0x10, 0x12, 0, PPC2_VSX207) +GEN_VSX_HELPER_2(xststdcsp, 0x14, 0x12, 0, PPC2_ISA300) +GEN_VSX_HELPER_2(xststdcdp, 0x14, 0x16, 0, PPC2_ISA300) +GEN_VSX_HELPER_2(xststdcqp, 0x04, 0x16, 0, PPC2_ISA300) GEN_VSX_HELPER_2(xvadddp, 0x00, 0x0C, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvsubdp, 0x00, 0x0D, 0, PPC2_VSX) @@ -725,6 +916,8 @@ GEN_VSX_HELPER_2(xvcmpgtsp, 0x0C, 0x09, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcmpgesp, 0x0C, 0x0A, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcmpnesp, 0x0C, 0x0B, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcvspdp, 0x12, 0x1C, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcvhpsp, 0x16, 0x1D, 0x18, PPC2_ISA300) +GEN_VSX_HELPER_2(xvcvsphp, 0x16, 0x1D, 0x19, PPC2_ISA300) GEN_VSX_HELPER_2(xvcvspsxds, 0x10, 0x19, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcvspsxws, 0x10, 0x09, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcvspuxds, 0x10, 0x18, 0, PPC2_VSX) @@ -738,6 +931,10 @@ GEN_VSX_HELPER_2(xvrspic, 0x16, 0x0A, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvrspim, 0x12, 0x0B, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvrspip, 0x12, 0x0A, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvrspiz, 0x12, 0x09, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvtstdcsp, 0x14, 0x1A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvtstdcdp, 0x14, 0x1E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xxperm, 0x08, 0x03, 0, PPC2_ISA300) +GEN_VSX_HELPER_2(xxpermr, 0x08, 0x07, 0, PPC2_ISA300) static void gen_xxbrd(DisasContext *ctx) { @@ -1001,6 +1198,293 @@ static void gen_xxsldwi(DisasContext *ctx) tcg_temp_free_i64(xtl); } +#define VSX_EXTRACT_INSERT(name) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + TCGv xt, xb; \ + TCGv_i32 t0 = tcg_temp_new_i32(); \ + uint8_t uimm = UIMM4(ctx->opcode); \ + \ + if (unlikely(!ctx->vsx_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VSXU); \ + return; \ + } \ + xt = tcg_const_tl(xT(ctx->opcode)); \ + xb = tcg_const_tl(xB(ctx->opcode)); \ + /* uimm > 15 out of bound and for \ + * uimm > 12 handle as per hardware in helper \ + */ \ + if (uimm > 15) { \ + tcg_gen_movi_i64(cpu_vsrh(xT(ctx->opcode)), 0); \ + tcg_gen_movi_i64(cpu_vsrl(xT(ctx->opcode)), 0); \ + return; \ + } \ + tcg_gen_movi_i32(t0, uimm); \ + gen_helper_##name(cpu_env, xt, xb, t0); \ + tcg_temp_free(xb); \ + tcg_temp_free(xt); \ + tcg_temp_free_i32(t0); \ +} + +VSX_EXTRACT_INSERT(xxextractuw) +VSX_EXTRACT_INSERT(xxinsertw) + +#ifdef TARGET_PPC64 +static void gen_xsxexpdp(DisasContext *ctx) +{ + TCGv rt = cpu_gpr[rD(ctx->opcode)]; + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + tcg_gen_shri_i64(rt, cpu_vsrh(xB(ctx->opcode)), 52); + tcg_gen_andi_i64(rt, rt, 0x7FF); +} + +static void gen_xsxexpqp(DisasContext *ctx) +{ + TCGv_i64 xth = cpu_vsrh(rD(ctx->opcode) + 32); + TCGv_i64 xtl = cpu_vsrl(rD(ctx->opcode) + 32); + TCGv_i64 xbh = cpu_vsrh(rB(ctx->opcode) + 32); + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + tcg_gen_shri_i64(xth, xbh, 48); + tcg_gen_andi_i64(xth, xth, 0x7FFF); + tcg_gen_movi_i64(xtl, 0); +} + +static void gen_xsiexpdp(DisasContext *ctx) +{ + TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); + TCGv ra = cpu_gpr[rA(ctx->opcode)]; + TCGv rb = cpu_gpr[rB(ctx->opcode)]; + TCGv_i64 t0; + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + t0 = tcg_temp_new_i64(); + tcg_gen_andi_i64(xth, ra, 0x800FFFFFFFFFFFFF); + tcg_gen_andi_i64(t0, rb, 0x7FF); + tcg_gen_shli_i64(t0, t0, 52); + tcg_gen_or_i64(xth, xth, t0); + /* dword[1] is undefined */ + tcg_temp_free_i64(t0); +} + +static void gen_xsiexpqp(DisasContext *ctx) +{ + TCGv_i64 xth = cpu_vsrh(rD(ctx->opcode) + 32); + TCGv_i64 xtl = cpu_vsrl(rD(ctx->opcode) + 32); + TCGv_i64 xah = cpu_vsrh(rA(ctx->opcode) + 32); + TCGv_i64 xal = cpu_vsrl(rA(ctx->opcode) + 32); + TCGv_i64 xbh = cpu_vsrh(rB(ctx->opcode) + 32); + TCGv_i64 t0; + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + t0 = tcg_temp_new_i64(); + tcg_gen_andi_i64(xth, xah, 0x8000FFFFFFFFFFFF); + tcg_gen_andi_i64(t0, xbh, 0x7FFF); + tcg_gen_shli_i64(t0, t0, 48); + tcg_gen_or_i64(xth, xth, t0); + tcg_gen_mov_i64(xtl, xal); + tcg_temp_free_i64(t0); +} + +static void gen_xsxsigdp(DisasContext *ctx) +{ + TCGv rt = cpu_gpr[rD(ctx->opcode)]; + TCGv_i64 t0, zr, nan, exp; + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + exp = tcg_temp_new_i64(); + t0 = tcg_temp_new_i64(); + zr = tcg_const_i64(0); + nan = tcg_const_i64(2047); + + tcg_gen_shri_i64(exp, cpu_vsrh(xB(ctx->opcode)), 52); + tcg_gen_andi_i64(exp, exp, 0x7FF); + tcg_gen_movi_i64(t0, 0x0010000000000000); + tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, zr, zr, t0); + tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, nan, zr, t0); + tcg_gen_andi_i64(rt, cpu_vsrh(xB(ctx->opcode)), 0x000FFFFFFFFFFFFF); + tcg_gen_or_i64(rt, rt, t0); + + tcg_temp_free_i64(t0); + tcg_temp_free_i64(exp); + tcg_temp_free_i64(zr); + tcg_temp_free_i64(nan); +} + +static void gen_xsxsigqp(DisasContext *ctx) +{ + TCGv_i64 t0, zr, nan, exp; + TCGv_i64 xth = cpu_vsrh(rD(ctx->opcode) + 32); + TCGv_i64 xtl = cpu_vsrl(rD(ctx->opcode) + 32); + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + exp = tcg_temp_new_i64(); + t0 = tcg_temp_new_i64(); + zr = tcg_const_i64(0); + nan = tcg_const_i64(32767); + + tcg_gen_shri_i64(exp, cpu_vsrh(rB(ctx->opcode) + 32), 48); + tcg_gen_andi_i64(exp, exp, 0x7FFF); + tcg_gen_movi_i64(t0, 0x0001000000000000); + tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, zr, zr, t0); + tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, nan, zr, t0); + tcg_gen_andi_i64(xth, cpu_vsrh(rB(ctx->opcode) + 32), 0x0000FFFFFFFFFFFF); + tcg_gen_or_i64(xth, xth, t0); + tcg_gen_mov_i64(xtl, cpu_vsrl(rB(ctx->opcode) + 32)); + + tcg_temp_free_i64(t0); + tcg_temp_free_i64(exp); + tcg_temp_free_i64(zr); + tcg_temp_free_i64(nan); +} +#endif + +static void gen_xviexpsp(DisasContext *ctx) +{ + TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); + TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); + TCGv_i64 xah = cpu_vsrh(xA(ctx->opcode)); + TCGv_i64 xal = cpu_vsrl(xA(ctx->opcode)); + TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); + TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); + TCGv_i64 t0; + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + t0 = tcg_temp_new_i64(); + tcg_gen_andi_i64(xth, xah, 0x807FFFFF807FFFFF); + tcg_gen_andi_i64(t0, xbh, 0xFF000000FF); + tcg_gen_shli_i64(t0, t0, 23); + tcg_gen_or_i64(xth, xth, t0); + tcg_gen_andi_i64(xtl, xal, 0x807FFFFF807FFFFF); + tcg_gen_andi_i64(t0, xbl, 0xFF000000FF); + tcg_gen_shli_i64(t0, t0, 23); + tcg_gen_or_i64(xtl, xtl, t0); + tcg_temp_free_i64(t0); +} + +static void gen_xviexpdp(DisasContext *ctx) +{ + TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); + TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); + TCGv_i64 xah = cpu_vsrh(xA(ctx->opcode)); + TCGv_i64 xal = cpu_vsrl(xA(ctx->opcode)); + TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); + TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); + TCGv_i64 t0; + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + t0 = tcg_temp_new_i64(); + tcg_gen_andi_i64(xth, xah, 0x800FFFFFFFFFFFFF); + tcg_gen_andi_i64(t0, xbh, 0x7FF); + tcg_gen_shli_i64(t0, t0, 52); + tcg_gen_or_i64(xth, xth, t0); + tcg_gen_andi_i64(xtl, xal, 0x800FFFFFFFFFFFFF); + tcg_gen_andi_i64(t0, xbl, 0x7FF); + tcg_gen_shli_i64(t0, t0, 52); + tcg_gen_or_i64(xtl, xtl, t0); + tcg_temp_free_i64(t0); +} + +static void gen_xvxexpsp(DisasContext *ctx) +{ + TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); + TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); + TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); + TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + tcg_gen_shri_i64(xth, xbh, 23); + tcg_gen_andi_i64(xth, xth, 0xFF000000FF); + tcg_gen_shri_i64(xtl, xbl, 23); + tcg_gen_andi_i64(xtl, xtl, 0xFF000000FF); +} + +static void gen_xvxexpdp(DisasContext *ctx) +{ + TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); + TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); + TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); + TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + tcg_gen_shri_i64(xth, xbh, 52); + tcg_gen_andi_i64(xth, xth, 0x7FF); + tcg_gen_shri_i64(xtl, xbl, 52); + tcg_gen_andi_i64(xtl, xtl, 0x7FF); +} + +GEN_VSX_HELPER_2(xvxsigsp, 0x00, 0x04, 0, PPC2_ISA300) + +static void gen_xvxsigdp(DisasContext *ctx) +{ + TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); + TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); + TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); + TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); + + TCGv_i64 t0, zr, nan, exp; + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + exp = tcg_temp_new_i64(); + t0 = tcg_temp_new_i64(); + zr = tcg_const_i64(0); + nan = tcg_const_i64(2047); + + tcg_gen_shri_i64(exp, xbh, 52); + tcg_gen_andi_i64(exp, exp, 0x7FF); + tcg_gen_movi_i64(t0, 0x0010000000000000); + tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, zr, zr, t0); + tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, nan, zr, t0); + tcg_gen_andi_i64(xth, xbh, 0x000FFFFFFFFFFFFF); + tcg_gen_or_i64(xth, xth, t0); + + tcg_gen_shri_i64(exp, xbl, 52); + tcg_gen_andi_i64(exp, exp, 0x7FF); + tcg_gen_movi_i64(t0, 0x0010000000000000); + tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, zr, zr, t0); + tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, nan, zr, t0); + tcg_gen_andi_i64(xtl, xbl, 0x000FFFFFFFFFFFFF); + tcg_gen_or_i64(xtl, xtl, t0); + + tcg_temp_free_i64(t0); + tcg_temp_free_i64(exp); + tcg_temp_free_i64(zr); + tcg_temp_free_i64(nan); +} + #undef GEN_XX2FORM #undef GEN_XX3FORM #undef GEN_XX2IFORM diff --git a/target/ppc/translate/vsx-ops.inc.c b/target/ppc/translate/vsx-ops.inc.c index 3d9104155a..7dc9f6f477 100644 --- a/target/ppc/translate/vsx-ops.inc.c +++ b/target/ppc/translate/vsx-ops.inc.c @@ -9,6 +9,11 @@ GEN_HANDLER_E(lxvdsx, 0x1F, 0x0C, 0x0A, 0, PPC_NONE, PPC2_VSX), GEN_HANDLER_E(lxvw4x, 0x1F, 0x0C, 0x18, 0, PPC_NONE, PPC2_VSX), GEN_HANDLER_E(lxvh8x, 0x1F, 0x0C, 0x19, 0, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(lxvb16x, 0x1F, 0x0C, 0x1B, 0, PPC_NONE, PPC2_ISA300), +GEN_HANDLER_E(lxvx, 0x1F, 0x0C, 0x08, 0x00000040, PPC_NONE, PPC2_ISA300), +#if defined(TARGET_PPC64) +GEN_HANDLER_E(lxvl, 0x1F, 0x0D, 0x08, 0, PPC_NONE, PPC2_ISA300), +GEN_HANDLER_E(lxvll, 0x1F, 0x0D, 0x09, 0, PPC_NONE, PPC2_ISA300), +#endif GEN_HANDLER_E(stxsdx, 0x1F, 0xC, 0x16, 0, PPC_NONE, PPC2_VSX), GEN_HANDLER_E(stxsibx, 0x1F, 0xD, 0x1C, 0, PPC_NONE, PPC2_ISA300), @@ -19,6 +24,11 @@ GEN_HANDLER_E(stxvd2x, 0x1F, 0xC, 0x1E, 0, PPC_NONE, PPC2_VSX), GEN_HANDLER_E(stxvw4x, 0x1F, 0xC, 0x1C, 0, PPC_NONE, PPC2_VSX), GEN_HANDLER_E(stxvh8x, 0x1F, 0x0C, 0x1D, 0, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(stxvb16x, 0x1F, 0x0C, 0x1F, 0, PPC_NONE, PPC2_ISA300), +GEN_HANDLER_E(stxvx, 0x1F, 0x0C, 0x0C, 0, PPC_NONE, PPC2_ISA300), +#if defined(TARGET_PPC64) +GEN_HANDLER_E(stxvl, 0x1F, 0x0D, 0x0C, 0, PPC_NONE, PPC2_ISA300), +GEN_HANDLER_E(stxvll, 0x1F, 0x0D, 0x0D, 0, PPC_NONE, PPC2_ISA300), +#endif GEN_HANDLER_E(mfvsrwz, 0x1F, 0x13, 0x03, 0x0000F800, PPC_NONE, PPC2_VSX207), GEN_HANDLER_E(mtvsrwa, 0x1F, 0x13, 0x06, 0x0000F800, PPC_NONE, PPC2_VSX207), @@ -39,6 +49,10 @@ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 0, PPC_NONE, fl2) GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0, opc3, 0, PPC_NONE, fl2), \ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 0, PPC_NONE, fl2) +#define GEN_XX2FORM_EXT(name, opc2, opc3, fl2) \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0, opc3, 0x00100000, PPC_NONE, fl2), \ +GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 0x00100000, PPC_NONE, fl2) + #define GEN_XX2FORM_EO(name, opc2, opc3, opc4, fl2) \ GEN_HANDLER2_E_2(name, #name, 0x3C, opc2 | 0, opc3, opc4, 0, PPC_NONE, fl2), \ GEN_HANDLER2_E_2(name, #name, 0x3C, opc2 | 1, opc3, opc4, 0, PPC_NONE, fl2) @@ -83,11 +97,54 @@ GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x01, opc3|0x0C, 0, PPC_NONE, PPC2_VSX),\ GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x02, opc3|0x0C, 0, PPC_NONE, PPC2_VSX),\ GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x03, opc3|0x0C, 0, PPC_NONE, PPC2_VSX) +#define GEN_VSX_XFORM_300(name, opc2, opc3, inval) \ +GEN_HANDLER_E(name, 0x3F, opc2, opc3, inval, PPC_NONE, PPC2_ISA300) + +#define GEN_VSX_XFORM_300_EO(name, opc2, opc3, opc4, inval) \ +GEN_HANDLER_E_2(name, 0x3F, opc2, opc3, opc4, inval, PPC_NONE, PPC2_ISA300) + GEN_XX2FORM(xsabsdp, 0x12, 0x15, PPC2_VSX), GEN_XX2FORM(xsnabsdp, 0x12, 0x16, PPC2_VSX), GEN_XX2FORM(xsnegdp, 0x12, 0x17, PPC2_VSX), GEN_XX3FORM(xscpsgndp, 0x00, 0x16, PPC2_VSX), +GEN_VSX_XFORM_300_EO(xsabsqp, 0x04, 0x19, 0x00, 0x00000001), +GEN_VSX_XFORM_300_EO(xsnabsqp, 0x04, 0x19, 0x08, 0x00000001), +GEN_VSX_XFORM_300_EO(xsnegqp, 0x04, 0x19, 0x10, 0x00000001), +GEN_VSX_XFORM_300(xscpsgnqp, 0x04, 0x03, 0x00000001), +GEN_VSX_XFORM_300_EO(xscvdpqp, 0x04, 0x1A, 0x16, 0x00000001), +GEN_VSX_XFORM_300_EO(xscvqpdp, 0x04, 0x1A, 0x14, 0x0), +GEN_VSX_XFORM_300_EO(xscvqpsdz, 0x04, 0x1A, 0x19, 0x00000001), +GEN_VSX_XFORM_300_EO(xscvqpswz, 0x04, 0x1A, 0x09, 0x00000001), + +#ifdef TARGET_PPC64 +GEN_XX2FORM_EO(xsxexpdp, 0x16, 0x15, 0x00, PPC2_ISA300), +GEN_VSX_XFORM_300_EO(xsxexpqp, 0x04, 0x19, 0x02, 0x00000001), +GEN_XX2FORM_EO(xsxsigdp, 0x16, 0x15, 0x01, PPC2_ISA300), +GEN_VSX_XFORM_300_EO(xsxsigqp, 0x04, 0x19, 0x12, 0x00000001), +GEN_HANDLER_E(xsiexpdp, 0x3C, 0x16, 0x1C, 0, PPC_NONE, PPC2_ISA300), +GEN_VSX_XFORM_300(xsiexpqp, 0x4, 0x1B, 0x00000001), +#endif + +GEN_XX2FORM(xststdcdp, 0x14, 0x16, PPC2_ISA300), +GEN_XX2FORM(xststdcsp, 0x14, 0x12, PPC2_ISA300), +GEN_VSX_XFORM_300(xststdcqp, 0x04, 0x16, 0x00000001), + +GEN_XX3FORM(xviexpsp, 0x00, 0x1B, PPC2_ISA300), +GEN_XX3FORM(xviexpdp, 0x00, 0x1F, PPC2_ISA300), +GEN_XX2FORM_EO(xvxexpdp, 0x16, 0x1D, 0x00, PPC2_ISA300), +GEN_XX2FORM_EO(xvxsigdp, 0x16, 0x1D, 0x01, PPC2_ISA300), +GEN_XX2FORM_EO(xvxexpsp, 0x16, 0x1D, 0x08, PPC2_ISA300), +GEN_XX2FORM_EO(xvxsigsp, 0x16, 0x1D, 0x09, PPC2_ISA300), + +/* DCMX = bit[25] << 6 | bit[29] << 5 | bit[11:15] */ +#define GEN_XX2FORM_DCMX(name, opc2, opc3, fl2) \ +GEN_XX3FORM(name, opc2, opc3 | 0, fl2), \ +GEN_XX3FORM(name, opc2, opc3 | 1, fl2) + +GEN_XX2FORM_DCMX(xvtstdcdp, 0x14, 0x1E, PPC2_ISA300), +GEN_XX2FORM_DCMX(xvtstdcsp, 0x14, 0x1A, PPC2_ISA300), + GEN_XX2FORM(xvabsdp, 0x12, 0x1D, PPC2_VSX), GEN_XX2FORM(xvnabsdp, 0x12, 0x1E, PPC2_VSX), GEN_XX2FORM(xvnegdp, 0x12, 0x1F, PPC2_VSX), @@ -98,8 +155,10 @@ GEN_XX2FORM(xvnegsp, 0x12, 0x1B, PPC2_VSX), GEN_XX3FORM(xvcpsgnsp, 0x00, 0x1A, PPC2_VSX), GEN_XX3FORM(xsadddp, 0x00, 0x04, PPC2_VSX), +GEN_VSX_XFORM_300(xsaddqp, 0x04, 0x00, 0x0), GEN_XX3FORM(xssubdp, 0x00, 0x05, PPC2_VSX), GEN_XX3FORM(xsmuldp, 0x00, 0x06, PPC2_VSX), +GEN_VSX_XFORM_300(xsmulqp, 0x04, 0x01, 0x0), GEN_XX3FORM(xsdivdp, 0x00, 0x07, PPC2_VSX), GEN_XX2FORM(xsredp, 0x14, 0x05, PPC2_VSX), GEN_XX2FORM(xssqrtdp, 0x16, 0x04, PPC2_VSX), @@ -118,12 +177,19 @@ GEN_XX3FORM(xscmpeqdp, 0x0C, 0x00, PPC2_ISA300), GEN_XX3FORM(xscmpgtdp, 0x0C, 0x01, PPC2_ISA300), GEN_XX3FORM(xscmpgedp, 0x0C, 0x02, PPC2_ISA300), GEN_XX3FORM(xscmpnedp, 0x0C, 0x03, PPC2_ISA300), +GEN_XX3FORM(xscmpexpdp, 0x0C, 0x07, PPC2_ISA300), +GEN_VSX_XFORM_300(xscmpexpqp, 0x04, 0x05, 0x00600001), GEN_XX2IFORM(xscmpodp, 0x0C, 0x05, PPC2_VSX), GEN_XX2IFORM(xscmpudp, 0x0C, 0x04, PPC2_VSX), +GEN_VSX_XFORM_300(xscmpoqp, 0x04, 0x04, 0x00600001), +GEN_VSX_XFORM_300(xscmpuqp, 0x04, 0x14, 0x00600001), GEN_XX3FORM(xsmaxdp, 0x00, 0x14, PPC2_VSX), GEN_XX3FORM(xsmindp, 0x00, 0x15, PPC2_VSX), +GEN_XX2FORM_EO(xscvdphp, 0x16, 0x15, 0x11, PPC2_ISA300), GEN_XX2FORM(xscvdpsp, 0x12, 0x10, PPC2_VSX), GEN_XX2FORM(xscvdpspn, 0x16, 0x10, PPC2_VSX207), +GEN_XX2FORM_EO(xscvhpdp, 0x16, 0x15, 0x10, PPC2_ISA300), +GEN_VSX_XFORM_300_EO(xscvsdqp, 0x04, 0x1A, 0x0A, 0x00000001), GEN_XX2FORM(xscvspdp, 0x12, 0x14, PPC2_VSX), GEN_XX2FORM(xscvspdpn, 0x16, 0x14, PPC2_VSX207), GEN_XX2FORM(xscvdpsxds, 0x10, 0x15, PPC2_VSX), @@ -131,6 +197,7 @@ GEN_XX2FORM(xscvdpsxws, 0x10, 0x05, PPC2_VSX), GEN_XX2FORM(xscvdpuxds, 0x10, 0x14, PPC2_VSX), GEN_XX2FORM(xscvdpuxws, 0x10, 0x04, PPC2_VSX), GEN_XX2FORM(xscvsxddp, 0x10, 0x17, PPC2_VSX), +GEN_VSX_XFORM_300_EO(xscvudqp, 0x04, 0x1A, 0x02, 0x00000001), GEN_XX2FORM(xscvuxddp, 0x10, 0x16, PPC2_VSX), GEN_XX2FORM(xsrdpi, 0x12, 0x04, PPC2_VSX), GEN_XX2FORM(xsrdpic, 0x16, 0x06, PPC2_VSX), @@ -142,6 +209,7 @@ GEN_XX3FORM(xsaddsp, 0x00, 0x00, PPC2_VSX207), GEN_XX3FORM(xssubsp, 0x00, 0x01, PPC2_VSX207), GEN_XX3FORM(xsmulsp, 0x00, 0x02, PPC2_VSX207), GEN_XX3FORM(xsdivsp, 0x00, 0x03, PPC2_VSX207), +GEN_VSX_XFORM_300(xsdivqp, 0x04, 0x11, 0x0), GEN_XX2FORM(xsresp, 0x14, 0x01, PPC2_VSX207), GEN_XX2FORM(xsrsp, 0x12, 0x11, PPC2_VSX207), GEN_XX2FORM(xssqrtsp, 0x16, 0x00, PPC2_VSX207), @@ -235,6 +303,8 @@ GEN_XX2FORM(xvrspiz, 0x12, 0x09, PPC2_VSX), GEN_XX2FORM_EO(xxbrh, 0x16, 0x1D, 0x07, PPC2_ISA300), GEN_XX2FORM_EO(xxbrw, 0x16, 0x1D, 0x0F, PPC2_ISA300), GEN_XX2FORM_EO(xxbrd, 0x16, 0x1D, 0x17, PPC2_ISA300), +GEN_XX2FORM_EO(xvcvhpsp, 0x16, 0x1D, 0x18, PPC2_ISA300), +GEN_XX2FORM_EO(xvcvsphp, 0x16, 0x1D, 0x19, PPC2_ISA300), GEN_XX2FORM_EO(xxbrq, 0x16, 0x1D, 0x1F, PPC2_ISA300), #define VSX_LOGICAL(name, opc2, opc3, fl2) \ @@ -250,9 +320,13 @@ VSX_LOGICAL(xxlnand, 0x8, 0x16, PPC2_VSX207), VSX_LOGICAL(xxlorc, 0x8, 0x15, PPC2_VSX207), GEN_XX3FORM(xxmrghw, 0x08, 0x02, PPC2_VSX), GEN_XX3FORM(xxmrglw, 0x08, 0x06, PPC2_VSX), +GEN_XX3FORM(xxperm, 0x08, 0x03, PPC2_ISA300), +GEN_XX3FORM(xxpermr, 0x08, 0x07, PPC2_ISA300), GEN_XX2FORM(xxspltw, 0x08, 0x0A, PPC2_VSX), GEN_XX1FORM(xxspltib, 0x08, 0x0B, PPC2_ISA300), GEN_XX3FORM_DM(xxsldwi, 0x08, 0x00), +GEN_XX2FORM_EXT(xxextractuw, 0x0A, 0x0A, PPC2_ISA300), +GEN_XX2FORM_EXT(xxinsertw, 0x0A, 0x0B, PPC2_ISA300), #define GEN_XXSEL_ROW(opc3) \ GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x18, opc3, 0, PPC_NONE, PPC2_VSX), \ diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c index 94dfcd7afc..76f79fa77b 100644 --- a/target/ppc/translate_init.c +++ b/target/ppc/translate_init.c @@ -5217,28 +5217,6 @@ POWERPC_FAMILY(e5500)(ObjectClass *oc, void *data) /* Non-embedded PowerPC */ -/* POWER : same as 601, without mfmsr, mfsr */ -POWERPC_FAMILY(POWER)(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); - - dc->desc = "POWER"; - /* pcc->insns_flags = XXX_TODO; */ - /* POWER RSC (from RAD6000) */ - pcc->msr_mask = (1ull << MSR_EE) | - (1ull << MSR_PR) | - (1ull << MSR_FP) | - (1ull << MSR_ME) | - (1ull << MSR_FE0) | - (1ull << MSR_SE) | - (1ull << MSR_DE) | - (1ull << MSR_AL) | - (1ull << MSR_EP) | - (1ull << MSR_IR) | - (1ull << MSR_DR); -} - #define POWERPC_MSRR_601 (0x0000000000001040ULL) static void init_proc_601 (CPUPPCState *env) @@ -8797,6 +8775,8 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) dc->props = powerpc_servercpu_properties; pcc->pvr_match = ppc_pvr_match_power9; pcc->pcr_mask = PCR_COMPAT_2_05 | PCR_COMPAT_2_06 | PCR_COMPAT_2_07; + pcc->pcr_supported = PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | + PCR_COMPAT_2_05; pcc->init_proc = init_proc_POWER9; pcc->check_pow = check_pow_nocheck; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB | @@ -8857,6 +8837,11 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) #if !defined(CONFIG_USER_ONLY) +void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp) +{ + cpu->vhyp = vhyp; +} + void cpu_ppc_set_papr(PowerPCCPU *cpu) { CPUPPCState *env = &cpu->env; @@ -9947,67 +9932,6 @@ static void ppc_cpu_unrealizefn(DeviceState *dev, Error **errp) } } -int ppc_get_compat_smt_threads(PowerPCCPU *cpu) -{ - CPUState *cs = CPU(cpu); - int ret = MIN(cs->nr_threads, kvmppc_smt_threads()); - - switch (cpu->cpu_version) { - case CPU_POWERPC_LOGICAL_2_05: - ret = MIN(ret, 2); - break; - case CPU_POWERPC_LOGICAL_2_06: - ret = MIN(ret, 4); - break; - case CPU_POWERPC_LOGICAL_2_07: - ret = MIN(ret, 8); - break; - } - - return ret; -} - -#ifdef TARGET_PPC64 -void ppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version, Error **errp) -{ - int ret = 0; - CPUPPCState *env = &cpu->env; - PowerPCCPUClass *host_pcc; - - cpu->cpu_version = cpu_version; - - switch (cpu_version) { - case CPU_POWERPC_LOGICAL_2_05: - env->spr[SPR_PCR] = PCR_TM_DIS | PCR_VSX_DIS | PCR_COMPAT_2_07 | - PCR_COMPAT_2_06 | PCR_COMPAT_2_05; - break; - case CPU_POWERPC_LOGICAL_2_06: - case CPU_POWERPC_LOGICAL_2_06_PLUS: - env->spr[SPR_PCR] = PCR_TM_DIS | PCR_COMPAT_2_07 | PCR_COMPAT_2_06; - break; - case CPU_POWERPC_LOGICAL_2_07: - env->spr[SPR_PCR] = PCR_COMPAT_2_07; - break; - default: - env->spr[SPR_PCR] = 0; - break; - } - - host_pcc = kvm_ppc_get_host_cpu_class(); - if (host_pcc) { - env->spr[SPR_PCR] &= host_pcc->pcr_mask; - } - - if (kvm_enabled()) { - ret = kvmppc_set_compat(cpu, cpu->cpu_version); - if (ret < 0) { - error_setg_errno(errp, -ret, - "Unable to set CPU compatibility mode in KVM"); - } - } -} -#endif - static gint ppc_cpu_compare_class_pvr(gconstpointer a, gconstpointer b) { ObjectClass *oc = (ObjectClass *)a; @@ -10591,9 +10515,16 @@ static const TypeInfo ppc_cpu_type_info = { .class_init = ppc_cpu_class_init, }; +static const TypeInfo ppc_vhyp_type_info = { + .name = TYPE_PPC_VIRTUAL_HYPERVISOR, + .parent = TYPE_INTERFACE, + .class_size = sizeof(PPCVirtualHypervisorClass), +}; + static void ppc_cpu_register_types(void) { type_register_static(&ppc_cpu_type_info); + type_register_static(&ppc_vhyp_type_info); } type_init(ppc_cpu_register_types) diff --git a/tests/.gitignore b/tests/.gitignore index 7357d0a0d4..dc37519421 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -69,6 +69,7 @@ test-qmp-marshal.c test-qobject-output-visitor test-rcu-list test-replication +test-shift128 test-string-input-visitor test-string-output-visitor test-thread-pool diff --git a/tests/Makefile.include b/tests/Makefile.include index a36a436755..634394aecf 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -65,6 +65,8 @@ check-unit-$(CONFIG_POSIX) += tests/test-vmstate$(EXESUF) endif check-unit-y += tests/test-cutils$(EXESUF) gcov-files-test-cutils-y += util/cutils.c +check-unit-y += tests/test-shift128$(EXESUF) +gcov-files-test-shift128-y = util/host-utils.c check-unit-y += tests/test-mul64$(EXESUF) gcov-files-test-mul64-y = util/host-utils.c check-unit-y += tests/test-int128$(EXESUF) @@ -285,6 +287,11 @@ gcov-files-ppc64-y += hw/usb/hcd-uhci.c check-qtest-ppc64-y += tests/usb-hcd-xhci-test$(EXESUF) gcov-files-ppc64-y += hw/usb/hcd-xhci.c check-qtest-ppc64-y += $(check-qtest-virtio-y) +check-qtest-ppc64-y += tests/test-netfilter$(EXESUF) +check-qtest-ppc64-y += tests/test-filter-mirror$(EXESUF) +check-qtest-ppc64-y += tests/test-filter-redirector$(EXESUF) +check-qtest-ppc64-y += tests/display-vga-test$(EXESUF) +check-qtest-ppc64-$(CONFIG_EVENTFD) += tests/ivshmem-test$(EXESUF) check-qtest-sh4-y = tests/endianness-test$(EXESUF) @@ -482,7 +489,7 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \ tests/test-x86-cpuid.o tests/test-mul64.o tests/test-int128.o \ tests/test-opts-visitor.o tests/test-qmp-event.o \ tests/rcutorture.o tests/test-rcu-list.o \ - tests/test-qdist.o \ + tests/test-qdist.o tests/test-shift128.o \ tests/test-qht.o tests/qht-bench.o tests/test-qht-par.o \ tests/atomic_add-bench.o @@ -592,6 +599,7 @@ tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marsh tests/test-visitor-serialization$(EXESUF): tests/test-visitor-serialization.o $(test-qapi-obj-y) tests/test-opts-visitor$(EXESUF): tests/test-opts-visitor.o $(test-qapi-obj-y) +tests/test-shift128$(EXESUF): tests/test-shift128.o $(test-util-obj-y) tests/test-mul64$(EXESUF): tests/test-mul64.o $(test-util-obj-y) tests/test-bitops$(EXESUF): tests/test-bitops.o $(test-util-obj-y) tests/test-bitcnt$(EXESUF): tests/test-bitcnt.o $(test-util-obj-y) @@ -714,7 +722,7 @@ tests/test-netfilter$(EXESUF): tests/test-netfilter.o $(qtest-obj-y) tests/test-filter-mirror$(EXESUF): tests/test-filter-mirror.o $(qtest-obj-y) tests/test-filter-redirector$(EXESUF): tests/test-filter-redirector.o $(qtest-obj-y) tests/test-x86-cpuid-compat$(EXESUF): tests/test-x86-cpuid-compat.o $(qtest-obj-y) -tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y) +tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y) $(libqos-spapr-obj-y) tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o contrib/libvhost-user/libvhost-user.o $(test-util-obj-y) tests/test-uuid$(EXESUF): tests/test-uuid.o $(test-util-obj-y) tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o diff --git a/tests/display-vga-test.c b/tests/display-vga-test.c index 91460215cc..2d7d24eee0 100644 --- a/tests/display-vga-test.c +++ b/tests/display-vga-test.c @@ -50,9 +50,14 @@ static void pci_virtio_vga(void) int main(int argc, char **argv) { + const char *arch = qtest_get_arch(); + g_test_init(&argc, &argv, NULL); - qtest_add_func("/display/pci/cirrus", pci_cirrus); + if (strcmp(arch, "alpha") == 0 || strcmp(arch, "i386") == 0 || + strcmp(arch, "mips") == 0 || strcmp(arch, "x86_64") == 0) { + qtest_add_func("/display/pci/cirrus", pci_cirrus); + } qtest_add_func("/display/pci/stdvga", pci_stdvga); qtest_add_func("/display/pci/secondary", pci_secondary); qtest_add_func("/display/pci/multihead", pci_multihead); diff --git a/tests/ivshmem-test.c b/tests/ivshmem-test.c index 04a5c5dc7d..37763425ee 100644 --- a/tests/ivshmem-test.c +++ b/tests/ivshmem-test.c @@ -11,7 +11,8 @@ #include "qemu/osdep.h" #include #include "contrib/ivshmem-server/ivshmem-server.h" -#include "libqos/pci-pc.h" +#include "libqos/libqos-pc.h" +#include "libqos/libqos-spapr.h" #include "libqtest.h" #include "qemu-common.h" @@ -40,9 +41,8 @@ static QPCIDevice *get_device(QPCIBus *pcibus) } typedef struct _IVState { - QTestState *qtest; + QOSState *qs; QPCIBar reg_bar, mem_bar; - QPCIBus *pcibus; QPCIDevice *dev; } IVState; @@ -74,7 +74,7 @@ static inline unsigned in_reg(IVState *s, enum Reg reg) QTestState *qtest = global_qtest; unsigned res; - global_qtest = s->qtest; + global_qtest = s->qs->qts; res = qpci_io_readl(s->dev, s->reg_bar, reg); g_test_message("*%s -> %x\n", name, res); global_qtest = qtest; @@ -87,7 +87,7 @@ static inline void out_reg(IVState *s, enum Reg reg, unsigned v) const char *name = reg2str(reg); QTestState *qtest = global_qtest; - global_qtest = s->qtest; + global_qtest = s->qs->qts; g_test_message("%x -> *%s\n", v, name); qpci_io_writel(s->dev, s->reg_bar, reg, v); global_qtest = qtest; @@ -97,7 +97,7 @@ static inline void read_mem(IVState *s, uint64_t off, void *buf, size_t len) { QTestState *qtest = global_qtest; - global_qtest = s->qtest; + global_qtest = s->qs->qts; qpci_memread(s->dev, s->mem_bar, off, buf, len); global_qtest = qtest; } @@ -107,7 +107,7 @@ static inline void write_mem(IVState *s, uint64_t off, { QTestState *qtest = global_qtest; - global_qtest = s->qtest; + global_qtest = s->qs->qts; qpci_memwrite(s->dev, s->mem_bar, off, buf, len); global_qtest = qtest; } @@ -115,17 +115,23 @@ static inline void write_mem(IVState *s, uint64_t off, static void cleanup_vm(IVState *s) { g_free(s->dev); - qpci_free_pc(s->pcibus); - qtest_quit(s->qtest); + qtest_shutdown(s->qs); } static void setup_vm_cmd(IVState *s, const char *cmd, bool msix) { uint64_t barsize; + const char *arch = qtest_get_arch(); - s->qtest = qtest_start(cmd); - s->pcibus = qpci_init_pc(NULL); - s->dev = get_device(s->pcibus); + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + s->qs = qtest_pc_boot(cmd); + } else if (strcmp(arch, "ppc64") == 0) { + s->qs = qtest_spapr_boot(cmd); + } else { + g_printerr("ivshmem-test tests are only available on x86 or ppc64\n"); + exit(EXIT_FAILURE); + } + s->dev = get_device(s->qs->pcibus); s->reg_bar = qpci_iomap(s->dev, 0, &barsize); g_assert_cmpuint(barsize, ==, 256); @@ -347,7 +353,7 @@ static void test_ivshmem_server(bool msi) g_assert_cmpint(vm1, !=, vm2); /* check number of MSI-X vectors */ - global_qtest = s1->qtest; + global_qtest = s1->qs->qts; if (msi) { ret = qpci_msix_table_size(s1->dev); g_assert_cmpuint(ret, ==, nvectors); @@ -370,7 +376,7 @@ static void test_ivshmem_server(bool msi) g_assert_cmpuint(ret, !=, 0); /* ping vm1 -> vm2 on vector 1 */ - global_qtest = s2->qtest; + global_qtest = s2->qs->qts; if (msi) { ret = qpci_msix_pending(s2->dev, 1); g_assert_cmpuint(ret, ==, 0); @@ -412,6 +418,7 @@ static void test_ivshmem_server_irq(void) static void test_ivshmem_hotplug(void) { + const char *arch = qtest_get_arch(); gchar *opts; qtest_start(""); @@ -419,7 +426,9 @@ static void test_ivshmem_hotplug(void) opts = g_strdup_printf("'shm': '%s', 'size': '1M'", tmpshm); qpci_plug_device_test("ivshmem", "iv1", PCI_SLOT_HP, opts); - qpci_unplug_acpi_device_test("iv1", PCI_SLOT_HP); + if (strcmp(arch, "ppc64") != 0) { + qpci_unplug_acpi_device_test("iv1", PCI_SLOT_HP); + } qtest_end(); g_free(opts); @@ -491,6 +500,7 @@ static gchar *mktempshm(int size, int *fd) int main(int argc, char **argv) { int ret, fd; + const char *arch = qtest_get_arch(); gchar dir[] = "/tmp/ivshmem-test.XXXXXX"; #if !GLIB_CHECK_VERSION(2, 31, 0) @@ -521,8 +531,10 @@ int main(int argc, char **argv) qtest_add_func("/ivshmem/memdev", test_ivshmem_memdev); if (g_test_slow()) { qtest_add_func("/ivshmem/pair", test_ivshmem_pair); - qtest_add_func("/ivshmem/server-msi", test_ivshmem_server_msi); - qtest_add_func("/ivshmem/server-irq", test_ivshmem_server_irq); + if (strcmp(arch, "ppc64") != 0) { + qtest_add_func("/ivshmem/server-msi", test_ivshmem_server_msi); + qtest_add_func("/ivshmem/server-irq", test_ivshmem_server_irq); + } } ret = g_test_run(); diff --git a/tests/libqos/pci-spapr.c b/tests/libqos/pci-spapr.c index 1e5d015bd4..2043f1e123 100644 --- a/tests/libqos/pci-spapr.c +++ b/tests/libqos/pci-spapr.c @@ -193,8 +193,8 @@ QPCIBus *qpci_init_spapr(QGuestAllocator *alloc) ret->pio.size = SPAPR_PCI_IO_WIN_SIZE; /* 32-bit portion of the MMIO window is at PCI address 2..4 GiB */ - ret->mmio32_cpu_base = SPAPR_PCI_BASE + SPAPR_PCI_MMIO32_WIN_SIZE; - ret->mmio32.pci_base = 0x80000000; /* 2 GiB */ + ret->mmio32_cpu_base = SPAPR_PCI_BASE; + ret->mmio32.pci_base = SPAPR_PCI_MMIO32_WIN_SIZE; ret->mmio32.size = SPAPR_PCI_MMIO32_WIN_SIZE; ret->bus.pio_alloc_ptr = 0xc000; diff --git a/tests/test-shift128.c b/tests/test-shift128.c new file mode 100644 index 0000000000..f3ff736e5c --- /dev/null +++ b/tests/test-shift128.c @@ -0,0 +1,139 @@ +/* + * Test unsigned left and right shift + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu/host-utils.h" + +typedef struct { + uint64_t low; + uint64_t high; + uint64_t rlow; + uint64_t rhigh; + int32_t shift; + bool overflow; +} test_data; + +static const test_data test_ltable[] = { + { 0x4C7ULL, 0x0ULL, 0x00000000000004C7ULL, + 0x0000000000000000ULL, 0, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000002ULL, + 0x0000000000000000ULL, 1, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000004ULL, + 0x0000000000000000ULL, 2, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000010ULL, + 0x0000000000000000ULL, 4, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000100ULL, + 0x0000000000000000ULL, 8, false }, + { 0x001ULL, 0x0ULL, 0x0000000000010000ULL, + 0x0000000000000000ULL, 16, false }, + { 0x001ULL, 0x0ULL, 0x0000000080000000ULL, + 0x0000000000000000ULL, 31, false }, + { 0x001ULL, 0x0ULL, 0x0000200000000000ULL, + 0x0000000000000000ULL, 45, false }, + { 0x001ULL, 0x0ULL, 0x1000000000000000ULL, + 0x0000000000000000ULL, 60, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, + 0x0000000000000001ULL, 64, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, + 0x0000000000010000ULL, 80, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, + 0x8000000000000000ULL, 127, false }, + { 0x000ULL, 0x1ULL, 0x0000000000000000ULL, + 0x0000000000000000ULL, 64, true }, + { 0x008ULL, 0x0ULL, 0x0000000000000000ULL, + 0x0000000000000008ULL, 64, false }, + { 0x008ULL, 0x0ULL, 0x0000000000000000ULL, + 0x8000000000000000ULL, 124, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, + 0x4000000000000000ULL, 126, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, + 0x8000000000000000ULL, 127, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000001ULL, + 0x0000000000000000ULL, 128, false }, + { 0x000ULL, 0x0ULL, 0x0000000000000000ULL, + 0x0000000000000000ULL, 200, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, + 0x0000000000000100ULL, 200, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, + 0x8000000000000000ULL, -1, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, + 0x8000000000000000ULL, INT32_MAX, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, + 0x4000000000000000ULL, -2, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, + 0x4000000000000000ULL, INT32_MAX - 1, false }, + { 0x8888888888888888ULL, 0x9999999999999999ULL, + 0x8000000000000000ULL, 0x9888888888888888ULL, 60, true }, + { 0x8888888888888888ULL, 0x9999999999999999ULL, + 0x0000000000000000ULL, 0x8888888888888888ULL, 64, true }, +}; + +static const test_data test_rtable[] = { + { 0x00000000000004C7ULL, 0x0ULL, 0x00000000000004C7ULL, 0x0ULL, 0, false }, + { 0x0800000000000000ULL, 0x0ULL, 0x0400000000000000ULL, 0x0ULL, 1, false }, + { 0x0800000000000000ULL, 0x0ULL, 0x0200000000000000ULL, 0x0ULL, 2, false }, + { 0x0800000000000000ULL, 0x0ULL, 0x0008000000000000ULL, 0x0ULL, 8, false }, + { 0x0800000000000000ULL, 0x0ULL, 0x0000080000000000ULL, 0x0ULL, 16, false }, + { 0x0800000000000000ULL, 0x0ULL, 0x0000000008000000ULL, 0x0ULL, 32, false }, + { 0x8000000000000000ULL, 0x0ULL, 0x0000000000000001ULL, 0x0ULL, 63, false }, + { 0x8000000000000000ULL, 0x0ULL, 0x0000000000000000ULL, 0x0ULL, 64, false }, + { 0x0000000000000000ULL, 0x8000000000000000ULL, + 0x0000000000000000ULL, 0x8000000000000000ULL, 128, false }, + { 0x0000000000000000ULL, 0x8000000000000000ULL, + 0x0080000000000000ULL, 0x0000000000000000ULL, 200, false }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, + 0x0000000000000000ULL, 0x0000000000000000ULL, 200, false }, + { 0x0000000000000000ULL, 0x8000000000000000ULL, + 0x0000000000000000ULL, 0x0000000000000080ULL, -200, false }, + { 0x8000000000000000ULL, 0x8000000000000000ULL, + 0x0000000080000000ULL, 0x0000000080000000ULL, 32, false }, + { 0x0800000000000000ULL, 0x0800000000000000ULL, + 0x0800000000000000ULL, 0x0000000000000000ULL, 64, false }, + { 0x0800000000000000ULL, 0x0800000000000000ULL, + 0x0008000000000000ULL, 0x0000000000000000ULL, 72, false }, + { 0x8000000000000000ULL, 0x8000000000000000ULL, + 0x0000000000000001ULL, 0x0000000000000000ULL, 127, false }, + { 0x0000000000000000ULL, 0x8000000000000000ULL, + 0x0000000000000001ULL, 0x0000000000000000ULL, -1, false }, + { 0x0000000000000000ULL, 0x8000000000000000ULL, + 0x0000000000000002ULL, 0x0000000000000000ULL, -2, false }, +}; + +static void test_lshift(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(test_ltable); ++i) { + bool overflow = false; + test_data tmp = test_ltable[i]; + ulshift(&tmp.low, &tmp.high, tmp.shift, &overflow); + g_assert_cmpuint(tmp.low, ==, tmp.rlow); + g_assert_cmpuint(tmp.high, ==, tmp.rhigh); + g_assert_cmpuint(tmp.overflow, ==, overflow); + } +} + +static void test_rshift(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(test_rtable); ++i) { + test_data tmp = test_rtable[i]; + urshift(&tmp.low, &tmp.high, tmp.shift); + g_assert_cmpuint(tmp.low, ==, tmp.rlow); + g_assert_cmpuint(tmp.high, ==, tmp.rhigh); + } +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/host-utils/test_lshift", test_lshift); + g_test_add_func("/host-utils/test_rshift", test_rshift); + return g_test_run(); +} diff --git a/util/Makefile.objs b/util/Makefile.objs index c1f247d675..56c8c23c7d 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -12,7 +12,7 @@ util-obj-$(CONFIG_POSIX) += memfd.o util-obj-$(CONFIG_WIN32) += oslib-win32.o util-obj-$(CONFIG_WIN32) += qemu-thread-win32.o util-obj-y += envlist.o path.o module.o -util-obj-$(call lnot,$(CONFIG_INT128)) += host-utils.o +util-obj-y += host-utils.o util-obj-y += bitmap.o bitops.o hbitmap.o util-obj-y += fifo8.o util-obj-y += acl.o diff --git a/util/host-utils.c b/util/host-utils.c index b166e57586..7b9322071d 100644 --- a/util/host-utils.c +++ b/util/host-utils.c @@ -26,6 +26,7 @@ #include "qemu/osdep.h" #include "qemu/host-utils.h" +#ifndef CONFIG_INT128 /* Long integer helpers */ static inline void mul64(uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b) @@ -158,4 +159,69 @@ int divs128(int64_t *plow, int64_t *phigh, int64_t divisor) return overflow; } +#endif +/** + * urshift - 128-bit Unsigned Right Shift. + * @plow: in/out - lower 64-bit integer. + * @phigh: in/out - higher 64-bit integer. + * @shift: in - bytes to shift, between 0 and 127. + * + * Result is zero-extended and stored in plow/phigh, which are + * input/output variables. Shift values outside the range will + * be mod to 128. In other words, the caller is responsible to + * verify/assert both the shift range and plow/phigh pointers. + */ +void urshift(uint64_t *plow, uint64_t *phigh, int32_t shift) +{ + shift &= 127; + if (shift == 0) { + return; + } + + uint64_t h = *phigh >> (shift & 63); + if (shift >= 64) { + *plow = h; + *phigh = 0; + } else { + *plow = (*plow >> (shift & 63)) | (*phigh << (64 - (shift & 63))); + *phigh = h; + } +} + +/** + * ulshift - 128-bit Unsigned Left Shift. + * @plow: in/out - lower 64-bit integer. + * @phigh: in/out - higher 64-bit integer. + * @shift: in - bytes to shift, between 0 and 127. + * @overflow: out - true if any 1-bit is shifted out. + * + * Result is zero-extended and stored in plow/phigh, which are + * input/output variables. Shift values outside the range will + * be mod to 128. In other words, the caller is responsible to + * verify/assert both the shift range and plow/phigh pointers. + */ +void ulshift(uint64_t *plow, uint64_t *phigh, int32_t shift, bool *overflow) +{ + uint64_t low = *plow; + uint64_t high = *phigh; + + shift &= 127; + if (shift == 0) { + return; + } + + /* check if any bit will be shifted out */ + urshift(&low, &high, 128 - shift); + if (low | high) { + *overflow = true; + } + + if (shift >= 64) { + *phigh = *plow << (shift & 63); + *plow = 0; + } else { + *phigh = (*plow >> (64 - (shift & 63))) | (*phigh << (shift & 63)); + *plow = *plow << shift; + } +}