diff --git a/MAINTAINERS b/MAINTAINERS index 9e9f489a41..4e77d03651 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1245,6 +1245,7 @@ Bamboo L: qemu-ppc@nongnu.org S: Orphan F: hw/ppc/ppc440_bamboo.c +F: tests/acceptance/ppc_bamboo.py e500 L: qemu-ppc@nongnu.org diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index 89cfa018f5..4ec659b93e 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -185,7 +185,7 @@ static void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon) xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI", pq & XIVE_ESB_VAL_P ? 'P' : '-', pq & XIVE_ESB_VAL_Q ? 'Q' : '-', - xsrc->status[i] & XIVE_STATUS_ASSERTED ? 'A' : ' ', + xive_source_is_asserted(xsrc, i) ? 'A' : ' ', xive_eas_is_masked(eas) ? "M" : " ", (int) xive_get_field64(EAS_END_DATA, eas->w)); diff --git a/hw/intc/spapr_xive_kvm.c b/hw/intc/spapr_xive_kvm.c index 6d4909d0a8..61fe7bd2d3 100644 --- a/hw/intc/spapr_xive_kvm.c +++ b/hw/intc/spapr_xive_kvm.c @@ -242,7 +242,7 @@ int kvmppc_xive_source_reset_one(XiveSource *xsrc, int srcno, Error **errp) if (xive_source_irq_is_lsi(xsrc, srcno)) { state |= KVM_XIVE_LEVEL_SENSITIVE; - if (xsrc->status[srcno] & XIVE_STATUS_ASSERTED) { + if (xive_source_is_asserted(xsrc, srcno)) { state |= KVM_XIVE_LEVEL_ASSERTED; } } @@ -301,9 +301,7 @@ static uint8_t xive_esb_read(XiveSource *xsrc, int srcno, uint32_t offset) static void kvmppc_xive_esb_trigger(XiveSource *xsrc, int srcno) { - uint64_t *addr = xsrc->esb_mmap + xive_source_esb_page(xsrc, srcno); - - *addr = 0x0; + xive_esb_rw(xsrc, srcno, 0, 0, true); } uint64_t kvmppc_xive_esb_rw(XiveSource *xsrc, int srcno, uint32_t offset, @@ -321,7 +319,7 @@ uint64_t kvmppc_xive_esb_rw(XiveSource *xsrc, int srcno, uint32_t offset, if (xive_source_irq_is_lsi(xsrc, srcno) && offset == XIVE_ESB_LOAD_EOI) { xive_esb_read(xsrc, srcno, XIVE_ESB_SET_PQ_00); - if (xsrc->status[srcno] & XIVE_STATUS_ASSERTED) { + if (xive_source_is_asserted(xsrc, srcno)) { kvmppc_xive_esb_trigger(xsrc, srcno); } return 0; @@ -359,11 +357,7 @@ void kvmppc_xive_source_set_irq(void *opaque, int srcno, int val) return; } } else { - if (val) { - xsrc->status[srcno] |= XIVE_STATUS_ASSERTED; - } else { - xsrc->status[srcno] &= ~XIVE_STATUS_ASSERTED; - } + xive_source_set_asserted(xsrc, srcno, val); } kvmppc_xive_esb_trigger(xsrc, srcno); diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 6c82326ec7..190194d27f 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -875,7 +875,7 @@ static bool xive_source_lsi_trigger(XiveSource *xsrc, uint32_t srcno) { uint8_t old_pq = xive_source_esb_get(xsrc, srcno); - xsrc->status[srcno] |= XIVE_STATUS_ASSERTED; + xive_source_set_asserted(xsrc, srcno, true); switch (old_pq) { case XIVE_ESB_RESET: @@ -923,7 +923,7 @@ static bool xive_source_esb_eoi(XiveSource *xsrc, uint32_t srcno) * notification */ if (xive_source_irq_is_lsi(xsrc, srcno) && - xsrc->status[srcno] & XIVE_STATUS_ASSERTED) { + xive_source_is_asserted(xsrc, srcno)) { ret = xive_source_lsi_trigger(xsrc, srcno); } @@ -1104,7 +1104,7 @@ void xive_source_set_irq(void *opaque, int srcno, int val) if (val) { notify = xive_source_lsi_trigger(xsrc, srcno); } else { - xsrc->status[srcno] &= ~XIVE_STATUS_ASSERTED; + xive_source_set_asserted(xsrc, srcno, false); } } else { if (val) { @@ -1133,7 +1133,7 @@ void xive_source_pic_print_info(XiveSource *xsrc, uint32_t offset, Monitor *mon) xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI", pq & XIVE_ESB_VAL_P ? 'P' : '-', pq & XIVE_ESB_VAL_Q ? 'Q' : '-', - xsrc->status[i] & XIVE_STATUS_ASSERTED ? 'A' : ' '); + xive_source_is_asserted(xsrc, i) ? 'A' : ' '); } } diff --git a/hw/pci-host/mv64361.c b/hw/pci-host/mv64361.c index 92b0f5d047..00b3ff7d90 100644 --- a/hw/pci-host/mv64361.c +++ b/hw/pci-host/mv64361.c @@ -869,6 +869,7 @@ static void mv64361_realize(DeviceState *dev, Error **errp) s->base_addr_enable = 0x1fffff; memory_region_init_io(&s->regs, OBJECT(s), &mv64361_ops, s, TYPE_MV64361, 0x10000); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->regs); for (i = 0; i < 2; i++) { g_autofree char *name = g_strdup_printf("pcihost%d", i); object_initialize_child(OBJECT(dev), name, &s->pci[i], diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index b8ce859f1a..e427ac2fe0 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -22,6 +22,7 @@ #include "hw/i2c/smbus_eeprom.h" #include "hw/qdev-properties.h" #include "sysemu/reset.h" +#include "sysemu/runstate.h" #include "hw/boards.h" #include "hw/loader.h" #include "hw/fw-path-provider.h" @@ -31,6 +32,8 @@ #include "sysemu/kvm.h" #include "kvm_ppc.h" #include "exec/address-spaces.h" +#include "qom/qom-qobject.h" +#include "qapi/qmp/qdict.h" #include "trace.h" #include "qemu/datadir.h" #include "sysemu/device_tree.h" @@ -52,11 +55,13 @@ #define BUS_FREQ_HZ 133333333 +#define PCI0_CFG_ADDR 0xcf8 #define PCI0_MEM_BASE 0xc0000000 #define PCI0_MEM_SIZE 0x20000000 #define PCI0_IO_BASE 0xf8000000 #define PCI0_IO_SIZE 0x10000 +#define PCI1_CFG_ADDR 0xc78 #define PCI1_MEM_BASE 0x80000000 #define PCI1_MEM_SIZE 0x40000000 #define PCI1_IO_BASE 0xfe000000 @@ -117,6 +122,10 @@ static void pegasos2_init(MachineState *machine) qemu_register_reset(pegasos2_cpu_reset, pm->cpu); /* RAM */ + if (machine->ram_size > 2 * GiB) { + error_report("RAM size more than 2 GiB is not supported"); + exit(1); + } memory_region_add_subregion(get_system_memory(), 0, machine->ram); /* allocate and load firmware */ @@ -190,62 +199,58 @@ static void pegasos2_init(MachineState *machine) if (!pm->vof) { warn_report("Option -kernel may be ineffective with -bios."); } + } else if (pm->vof) { + warn_report("Using Virtual OpenFirmware but no -kernel option."); } + if (!pm->vof && machine->kernel_cmdline && machine->kernel_cmdline[0]) { warn_report("Option -append may be ineffective with -bios."); } } -static uint32_t pegasos2_pci_config_read(AddressSpace *as, int bus, +static uint32_t pegasos2_mv_reg_read(Pegasos2MachineState *pm, + uint32_t addr, uint32_t len) +{ + MemoryRegion *r = sysbus_mmio_get_region(SYS_BUS_DEVICE(pm->mv), 0); + uint64_t val = 0xffffffffULL; + memory_region_dispatch_read(r, addr, &val, size_memop(len) | MO_LE, + MEMTXATTRS_UNSPECIFIED); + return val; +} + +static void pegasos2_mv_reg_write(Pegasos2MachineState *pm, uint32_t addr, + uint32_t len, uint32_t val) +{ + MemoryRegion *r = sysbus_mmio_get_region(SYS_BUS_DEVICE(pm->mv), 0); + memory_region_dispatch_write(r, addr, val, size_memop(len) | MO_LE, + MEMTXATTRS_UNSPECIFIED); +} + +static uint32_t pegasos2_pci_config_read(Pegasos2MachineState *pm, int bus, uint32_t addr, uint32_t len) { - hwaddr pcicfg = (bus ? 0xf1000c78 : 0xf1000cf8); - uint32_t val = 0xffffffff; + hwaddr pcicfg = bus ? PCI1_CFG_ADDR : PCI0_CFG_ADDR; + uint64_t val = 0xffffffffULL; - stl_le_phys(as, pcicfg, addr | BIT(31)); - switch (len) { - case 4: - val = ldl_le_phys(as, pcicfg + 4); - break; - case 2: - val = lduw_le_phys(as, pcicfg + 4); - break; - case 1: - val = ldub_phys(as, pcicfg + 4); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid length\n", __func__); - break; + if (len <= 4) { + pegasos2_mv_reg_write(pm, pcicfg, 4, addr | BIT(31)); + val = pegasos2_mv_reg_read(pm, pcicfg + 4, len); } return val; } -static void pegasos2_pci_config_write(AddressSpace *as, int bus, uint32_t addr, - uint32_t len, uint32_t val) +static void pegasos2_pci_config_write(Pegasos2MachineState *pm, int bus, + uint32_t addr, uint32_t len, uint32_t val) { - hwaddr pcicfg = (bus ? 0xf1000c78 : 0xf1000cf8); + hwaddr pcicfg = bus ? PCI1_CFG_ADDR : PCI0_CFG_ADDR; - stl_le_phys(as, pcicfg, addr | BIT(31)); - switch (len) { - case 4: - stl_le_phys(as, pcicfg + 4, val); - break; - case 2: - stw_le_phys(as, pcicfg + 4, val); - break; - case 1: - stb_phys(as, pcicfg + 4, val); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid length\n", __func__); - break; - } + pegasos2_mv_reg_write(pm, pcicfg, 4, addr | BIT(31)); + pegasos2_mv_reg_write(pm, pcicfg + 4, len, val); } static void pegasos2_machine_reset(MachineState *machine) { Pegasos2MachineState *pm = PEGASOS2_MACHINE(machine); - AddressSpace *as = CPU(pm->cpu)->as; void *fdt; uint64_t d[2]; int sz; @@ -256,51 +261,51 @@ static void pegasos2_machine_reset(MachineState *machine) } /* Otherwise, set up devices that board firmware would normally do */ - stl_le_phys(as, 0xf1000000, 0x28020ff); - stl_le_phys(as, 0xf1000278, 0xa31fc); - stl_le_phys(as, 0xf100f300, 0x11ff0400); - stl_le_phys(as, 0xf100f10c, 0x80000000); - stl_le_phys(as, 0xf100001c, 0x8000000); - pegasos2_pci_config_write(as, 0, PCI_COMMAND, 2, PCI_COMMAND_IO | + pegasos2_mv_reg_write(pm, 0, 4, 0x28020ff); + pegasos2_mv_reg_write(pm, 0x278, 4, 0xa31fc); + pegasos2_mv_reg_write(pm, 0xf300, 4, 0x11ff0400); + pegasos2_mv_reg_write(pm, 0xf10c, 4, 0x80000000); + pegasos2_mv_reg_write(pm, 0x1c, 4, 0x8000000); + pegasos2_pci_config_write(pm, 0, PCI_COMMAND, 2, PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); - pegasos2_pci_config_write(as, 1, PCI_COMMAND, 2, PCI_COMMAND_IO | + pegasos2_pci_config_write(pm, 1, PCI_COMMAND, 2, PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 0) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 0) << 8) | PCI_INTERRUPT_LINE, 2, 0x9); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 0) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 0) << 8) | 0x50, 1, 0x2); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) | PCI_INTERRUPT_LINE, 2, 0x109); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) | PCI_CLASS_PROG, 1, 0xf); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) | 0x40, 1, 0xb); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) | 0x50, 4, 0x17171717); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) | PCI_COMMAND, 2, 0x87); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 2) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 2) << 8) | PCI_INTERRUPT_LINE, 2, 0x409); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 3) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 3) << 8) | PCI_INTERRUPT_LINE, 2, 0x409); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 4) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 4) << 8) | PCI_INTERRUPT_LINE, 2, 0x9); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 4) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 4) << 8) | 0x48, 4, 0xf00); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 4) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 4) << 8) | 0x40, 4, 0x558020); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 4) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 4) << 8) | 0x90, 4, 0xd00); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 5) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 5) << 8) | PCI_INTERRUPT_LINE, 2, 0x309); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 6) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 6) << 8) | PCI_INTERRUPT_LINE, 2, 0x309); /* Device tree and VOF set up */ @@ -362,6 +367,29 @@ static target_ulong pegasos2_rtas(PowerPCCPU *cpu, Pegasos2MachineState *pm, return H_PARAMETER; } switch (token) { + case RTAS_GET_TIME_OF_DAY: + { + QObject *qo = object_property_get_qobject(qdev_get_machine(), + "rtc-time", &error_fatal); + QDict *qd = qobject_to(QDict, qo); + + if (nargs != 0 || nrets != 8 || !qd) { + stl_be_phys(as, rets, -1); + qobject_unref(qo); + return H_PARAMETER; + } + + stl_be_phys(as, rets, 0); + stl_be_phys(as, rets + 4, qdict_get_int(qd, "tm_year") + 1900); + stl_be_phys(as, rets + 8, qdict_get_int(qd, "tm_mon") + 1); + stl_be_phys(as, rets + 12, qdict_get_int(qd, "tm_mday")); + stl_be_phys(as, rets + 16, qdict_get_int(qd, "tm_hour")); + stl_be_phys(as, rets + 20, qdict_get_int(qd, "tm_min")); + stl_be_phys(as, rets + 24, qdict_get_int(qd, "tm_sec")); + stl_be_phys(as, rets + 28, 0); + qobject_unref(qo); + return H_SUCCESS; + } case RTAS_READ_PCI_CONFIG: { uint32_t addr, len, val; @@ -372,7 +400,7 @@ static target_ulong pegasos2_rtas(PowerPCCPU *cpu, Pegasos2MachineState *pm, } addr = ldl_be_phys(as, args); len = ldl_be_phys(as, args + 4); - val = pegasos2_pci_config_read(as, !(addr >> 24), + val = pegasos2_pci_config_read(pm, !(addr >> 24), addr & 0x0fffffff, len); stl_be_phys(as, rets, 0); stl_be_phys(as, rets + 4, val); @@ -389,7 +417,7 @@ static target_ulong pegasos2_rtas(PowerPCCPU *cpu, Pegasos2MachineState *pm, addr = ldl_be_phys(as, args); len = ldl_be_phys(as, args + 4); val = ldl_be_phys(as, args + 8); - pegasos2_pci_config_write(as, !(addr >> 24), + pegasos2_pci_config_write(pm, !(addr >> 24), addr & 0x0fffffff, len, val); stl_be_phys(as, rets, 0); return H_SUCCESS; @@ -402,6 +430,16 @@ static target_ulong pegasos2_rtas(PowerPCCPU *cpu, Pegasos2MachineState *pm, qemu_log_mask(LOG_UNIMP, "%c", ldl_be_phys(as, args)); stl_be_phys(as, rets, 0); return H_SUCCESS; + case RTAS_POWER_OFF: + { + if (nargs != 2 || nrets != 1) { + stl_be_phys(as, rets, -1); + return H_PARAMETER; + } + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + stl_be_phys(as, rets, 0); + return H_SUCCESS; + } default: qemu_log_mask(LOG_UNIMP, "Unknown RTAS token %u (args=%u, rets=%u)\n", token, nargs, nrets); diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index f5d012f860..e8127599c9 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -336,6 +336,8 @@ void store_40x_dbcr0(CPUPPCState *env, uint32_t val) { PowerPCCPU *cpu = env_archcpu(env); + qemu_mutex_lock_iothread(); + switch ((val >> 28) & 0x3) { case 0x0: /* No action */ @@ -353,6 +355,8 @@ void store_40x_dbcr0(CPUPPCState *env, uint32_t val) ppc40x_system_reset(cpu); break; } + + qemu_mutex_unlock_iothread(); } /* PowerPC 40x internal IRQ controller */ @@ -848,7 +852,7 @@ static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp, * On MSB edge based DEC implementations the MSB going from 0 -> 1 triggers * an edge interrupt, so raise it here too. */ - if ((signed_value < 3) || + if ((value < 3) || ((tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL) && signed_value < 0) || ((tb_env->flags & PPC_DECR_UNDERFLOW_TRIGGERED) && signed_value < 0 && signed_decr >= 0)) { diff --git a/hw/ppc/ppc4xx_pci.c b/hw/ppc/ppc4xx_pci.c index 8147ba6f94..304a29349c 100644 --- a/hw/ppc/ppc4xx_pci.c +++ b/hw/ppc/ppc4xx_pci.c @@ -48,12 +48,14 @@ OBJECT_DECLARE_SIMPLE_TYPE(PPC4xxPCIState, PPC4xx_PCI_HOST_BRIDGE) #define PPC4xx_PCI_NR_PMMS 3 #define PPC4xx_PCI_NR_PTMS 2 +#define PPC4xx_PCI_NUM_DEVS 5 + struct PPC4xxPCIState { PCIHostState parent_obj; struct PCIMasterMap pmm[PPC4xx_PCI_NR_PMMS]; struct PCITargetMap ptm[PPC4xx_PCI_NR_PTMS]; - qemu_irq irq[PCI_NUM_PINS]; + qemu_irq irq[PPC4xx_PCI_NUM_DEVS]; MemoryRegion container; MemoryRegion iomem; @@ -246,7 +248,7 @@ static int ppc4xx_pci_map_irq(PCIDevice *pci_dev, int irq_num) trace_ppc4xx_pci_map_irq(pci_dev->devfn, irq_num, slot); - return slot - 1; + return slot > 0 ? slot - 1 : PPC4xx_PCI_NUM_DEVS - 1; } static void ppc4xx_pci_set_irq(void *opaque, int irq_num, int level) @@ -254,7 +256,7 @@ static void ppc4xx_pci_set_irq(void *opaque, int irq_num, int level) qemu_irq *pci_irqs = opaque; trace_ppc4xx_pci_set_irq(irq_num); - assert(irq_num >= 0); + assert(irq_num >= 0 && irq_num < PPC4xx_PCI_NUM_DEVS); qemu_set_irq(pci_irqs[irq_num], level); } diff --git a/hw/ppc/spapr_softmmu.c b/hw/ppc/spapr_softmmu.c index 6c6b86dd3c..f8924270ef 100644 --- a/hw/ppc/spapr_softmmu.c +++ b/hw/ppc/spapr_softmmu.c @@ -1,25 +1,10 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" -#include "qapi/error.h" -#include "sysemu/hw_accel.h" -#include "sysemu/runstate.h" -#include "qemu/log.h" -#include "qemu/main-loop.h" -#include "qemu/module.h" -#include "qemu/error-report.h" #include "cpu.h" -#include "exec/exec-all.h" #include "helper_regs.h" #include "hw/ppc/spapr.h" -#include "hw/ppc/spapr_cpu_core.h" #include "mmu-hash64.h" -#include "cpu-models.h" -#include "trace.h" -#include "kvm_ppc.h" -#include "hw/ppc/fdt.h" -#include "hw/ppc/spapr_ovec.h" #include "mmu-book3s-v3.h" -#include "hw/mem/memory-device.h" static inline bool valid_ptex(PowerPCCPU *cpu, target_ulong ptex) { diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index 252c58a1d6..b8ab0bf749 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -286,6 +286,30 @@ uint8_t xive_esb_set(uint8_t *pq, uint8_t value); uint8_t xive_source_esb_get(XiveSource *xsrc, uint32_t srcno); uint8_t xive_source_esb_set(XiveSource *xsrc, uint32_t srcno, uint8_t pq); +/* + * Source status helpers + */ +static inline void xive_source_set_status(XiveSource *xsrc, uint32_t srcno, + uint8_t status, bool enable) +{ + if (enable) { + xsrc->status[srcno] |= status; + } else { + xsrc->status[srcno] &= ~status; + } +} + +static inline void xive_source_set_asserted(XiveSource *xsrc, uint32_t srcno, + bool enable) +{ + xive_source_set_status(xsrc, srcno, XIVE_STATUS_ASSERTED, enable); +} + +static inline bool xive_source_is_asserted(XiveSource *xsrc, uint32_t srcno) +{ + return xsrc->status[srcno] & XIVE_STATUS_ASSERTED; +} + void xive_source_pic_print_info(XiveSource *xsrc, uint32_t offset, Monitor *mon); diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 2404d482ba..eb32f3e2cb 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -901,7 +901,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *en (*regs)[33] = tswapreg(env->msr); (*regs)[35] = tswapreg(env->ctr); (*regs)[36] = tswapreg(env->lr); - (*regs)[37] = tswapreg(env->xer); + (*regs)[37] = tswapreg(cpu_read_xer(env)); for (i = 0; i < ARRAY_SIZE(env->crf); i++) { ccr |= env->crf[i] << (32 - ((i + 1) * 4)); diff --git a/linux-user/ppc/signal.c b/linux-user/ppc/signal.c index c37744c8fc..90a0369632 100644 --- a/linux-user/ppc/signal.c +++ b/linux-user/ppc/signal.c @@ -242,7 +242,7 @@ static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame) __put_user(env->nip, &frame->mc_gregs[TARGET_PT_NIP]); __put_user(env->ctr, &frame->mc_gregs[TARGET_PT_CTR]); __put_user(env->lr, &frame->mc_gregs[TARGET_PT_LNK]); - __put_user(env->xer, &frame->mc_gregs[TARGET_PT_XER]); + __put_user(cpu_read_xer(env), &frame->mc_gregs[TARGET_PT_XER]); for (i = 0; i < ARRAY_SIZE(env->crf); i++) { ccr |= env->crf[i] << (32 - ((i + 1) * 4)); @@ -315,6 +315,7 @@ static void restore_user_regs(CPUPPCState *env, { target_ulong save_r2 = 0; target_ulong msr; + target_ulong xer; target_ulong ccr; int i; @@ -330,9 +331,11 @@ static void restore_user_regs(CPUPPCState *env, __get_user(env->nip, &frame->mc_gregs[TARGET_PT_NIP]); __get_user(env->ctr, &frame->mc_gregs[TARGET_PT_CTR]); __get_user(env->lr, &frame->mc_gregs[TARGET_PT_LNK]); - __get_user(env->xer, &frame->mc_gregs[TARGET_PT_XER]); - __get_user(ccr, &frame->mc_gregs[TARGET_PT_CCR]); + __get_user(xer, &frame->mc_gregs[TARGET_PT_XER]); + cpu_write_xer(env, xer); + + __get_user(ccr, &frame->mc_gregs[TARGET_PT_CCR]); for (i = 0; i < ARRAY_SIZE(env->crf); i++) { env->crf[i] = (ccr >> (32 - ((i + 1) * 4))) & 0xf; } diff --git a/target/ppc/cpu.c b/target/ppc/cpu.c index 7ad9bd6044..f933d9f2bd 100644 --- a/target/ppc/cpu.c +++ b/target/ppc/cpu.c @@ -27,7 +27,7 @@ #include "helper_regs.h" #include "sysemu/tcg.h" -target_ulong cpu_read_xer(CPUPPCState *env) +target_ulong cpu_read_xer(const CPUPPCState *env) { if (is_isa300(env)) { return env->xer | (env->so << XER_SO) | diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index baa4e7c34d..0472ec9154 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -314,6 +314,7 @@ typedef struct ppc_v3_pate_t { #define MSR_AP 23 /* Access privilege state on 602 hflags */ #define MSR_VSX 23 /* Vector Scalar Extension (ISA 2.06 and later) x hflags */ #define MSR_SA 22 /* Supervisor access mode on 602 hflags */ +#define MSR_S 22 /* Secure state */ #define MSR_KEY 19 /* key bit on 603e */ #define MSR_POW 18 /* Power management */ #define MSR_TGPR 17 /* TGPR usage on 602/603 x */ @@ -342,6 +343,26 @@ typedef struct ppc_v3_pate_t { #define MSR_RI 1 /* Recoverable interrupt 1 */ #define MSR_LE 0 /* Little-endian mode 1 hflags */ +/* PMU bits */ +#define MMCR0_FC PPC_BIT(32) /* Freeze Counters */ +#define MMCR0_PMAO PPC_BIT(56) /* Perf Monitor Alert Ocurred */ +#define MMCR0_PMAE PPC_BIT(37) /* Perf Monitor Alert Enable */ +#define MMCR0_EBE PPC_BIT(43) /* Perf Monitor EBB Enable */ +#define MMCR0_FCECE PPC_BIT(38) /* FC on Enabled Cond or Event */ +#define MMCR0_PMCC0 PPC_BIT(44) /* PMC Control bit 0 */ +#define MMCR0_PMCC1 PPC_BIT(45) /* PMC Control bit 1 */ +/* MMCR0 userspace r/w mask */ +#define MMCR0_UREG_MASK (MMCR0_FC | MMCR0_PMAO | MMCR0_PMAE) +/* MMCR2 userspace r/w mask */ +#define MMCR2_FC1P0 PPC_BIT(1) /* MMCR2 FCnP0 for PMC1 */ +#define MMCR2_FC2P0 PPC_BIT(10) /* MMCR2 FCnP0 for PMC2 */ +#define MMCR2_FC3P0 PPC_BIT(19) /* MMCR2 FCnP0 for PMC3 */ +#define MMCR2_FC4P0 PPC_BIT(28) /* MMCR2 FCnP0 for PMC4 */ +#define MMCR2_FC5P0 PPC_BIT(37) /* MMCR2 FCnP0 for PMC5 */ +#define MMCR2_FC6P0 PPC_BIT(46) /* MMCR2 FCnP0 for PMC6 */ +#define MMCR2_UREG_MASK (MMCR2_FC1P0 | MMCR2_FC2P0 | MMCR2_FC3P0 | \ + MMCR2_FC4P0 | MMCR2_FC5P0 | MMCR2_FC6P0) + /* LPCR bits */ #define LPCR_VPM0 PPC_BIT(0) #define LPCR_VPM1 PPC_BIT(1) @@ -607,6 +628,8 @@ enum { HFLAGS_SE = 10, /* MSR_SE -- from elsewhere on embedded ppc */ HFLAGS_FP = 13, /* MSR_FP */ HFLAGS_PR = 14, /* MSR_PR */ + HFLAGS_PMCC0 = 15, /* MMCR0 PMCC bit 0 */ + HFLAGS_PMCC1 = 16, /* MMCR0 PMCC bit 1 */ HFLAGS_VSX = 23, /* MSR_VSX if cpu has VSX */ HFLAGS_VR = 25, /* MSR_VR if cpu has VRE */ @@ -2412,7 +2435,7 @@ enum { /*****************************************************************************/ #define is_isa300(ctx) (!!(ctx->insns_flags2 & PPC2_ISA300)) -target_ulong cpu_read_xer(CPUPPCState *env); +target_ulong cpu_read_xer(const CPUPPCState *env); void cpu_write_xer(CPUPPCState *env, target_ulong xer); /* diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 6aad01d1d3..65545ba9ca 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -6867,7 +6867,7 @@ static void register_book3s_pmu_sup_sprs(CPUPPCState *env) static void register_book3s_pmu_user_sprs(CPUPPCState *env) { spr_register(env, SPR_POWER_UMMCR0, "UMMCR0", - &spr_read_ureg, SPR_NOACCESS, + &spr_read_MMCR0_ureg, &spr_write_MMCR0_ureg, &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_UMMCR1, "UMMCR1", @@ -6879,27 +6879,27 @@ static void register_book3s_pmu_user_sprs(CPUPPCState *env) &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_UPMC1, "UPMC1", - &spr_read_ureg, SPR_NOACCESS, + &spr_read_PMC14_ureg, &spr_write_PMC14_ureg, &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_UPMC2, "UPMC2", - &spr_read_ureg, SPR_NOACCESS, + &spr_read_PMC14_ureg, &spr_write_PMC14_ureg, &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_UPMC3, "UPMC3", - &spr_read_ureg, SPR_NOACCESS, + &spr_read_PMC14_ureg, &spr_write_PMC14_ureg, &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_UPMC4, "UPMC4", - &spr_read_ureg, SPR_NOACCESS, + &spr_read_PMC14_ureg, &spr_write_PMC14_ureg, &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_UPMC5, "UPMC5", - &spr_read_ureg, SPR_NOACCESS, + &spr_read_PMC56_ureg, &spr_write_PMC56_ureg, &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_UPMC6, "UPMC6", - &spr_read_ureg, SPR_NOACCESS, + &spr_read_PMC56_ureg, &spr_write_PMC56_ureg, &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_USIAR, "USIAR", @@ -6975,7 +6975,7 @@ static void register_power8_pmu_sup_sprs(CPUPPCState *env) static void register_power8_pmu_user_sprs(CPUPPCState *env) { spr_register(env, SPR_POWER_UMMCR2, "UMMCR2", - &spr_read_ureg, SPR_NOACCESS, + &spr_read_MMCR2_ureg, &spr_write_MMCR2_ureg, &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_USIER, "USIER", diff --git a/target/ppc/gdbstub.c b/target/ppc/gdbstub.c index 1808a150e4..105c2f7dd1 100644 --- a/target/ppc/gdbstub.c +++ b/target/ppc/gdbstub.c @@ -159,7 +159,7 @@ int ppc_cpu_gdb_read_register(CPUState *cs, GByteArray *buf, int n) gdb_get_regl(buf, env->ctr); break; case 69: - gdb_get_reg32(buf, env->xer); + gdb_get_reg32(buf, cpu_read_xer(env)); break; case 70: gdb_get_reg32(buf, env->fpscr); @@ -217,7 +217,7 @@ int ppc_cpu_gdb_read_register_apple(CPUState *cs, GByteArray *buf, int n) gdb_get_reg64(buf, env->ctr); break; case 69 + 32: - gdb_get_reg32(buf, env->xer); + gdb_get_reg32(buf, cpu_read_xer(env)); break; case 70 + 32: gdb_get_reg64(buf, env->fpscr); @@ -269,7 +269,7 @@ int ppc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) env->ctr = ldtul_p(mem_buf); break; case 69: - env->xer = ldl_p(mem_buf); + cpu_write_xer(env, ldl_p(mem_buf)); break; case 70: /* fpscr */ @@ -319,7 +319,7 @@ int ppc_cpu_gdb_write_register_apple(CPUState *cs, uint8_t *mem_buf, int n) env->ctr = ldq_p(mem_buf); break; case 69 + 32: - env->xer = ldl_p(mem_buf); + cpu_write_xer(env, ldl_p(mem_buf)); break; case 70 + 32: /* fpscr */ diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index 1bfb480ecf..99562edd57 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -109,6 +109,12 @@ static uint32_t hreg_compute_hflags_value(CPUPPCState *env) if (env->spr[SPR_LPCR] & LPCR_HR) { hflags |= 1 << HFLAGS_HR; } + if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC0) { + hflags |= 1 << HFLAGS_PMCC0; + } + if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC1) { + hflags |= 1 << HFLAGS_PMCC1; + } #ifndef CONFIG_USER_ONLY if (!env->has_hv_mode || (msr & (1ull << MSR_HV))) { diff --git a/target/ppc/monitor.c b/target/ppc/monitor.c index a475108b2d..0b805ef6e9 100644 --- a/target/ppc/monitor.c +++ b/target/ppc/monitor.c @@ -44,6 +44,13 @@ static target_long monitor_get_ccr(Monitor *mon, const struct MonitorDef *md, return u; } +static target_long monitor_get_xer(Monitor *mon, const struct MonitorDef *md, + int val) +{ + CPUArchState *env = mon_get_cpu_env(mon); + return cpu_read_xer(env); +} + static target_long monitor_get_decr(Monitor *mon, const struct MonitorDef *md, int val) { @@ -85,7 +92,7 @@ const MonitorDef monitor_defs[] = { { "decr", 0, &monitor_get_decr, }, { "ccr|cr", 0, &monitor_get_ccr, }, /* Machine state register */ - { "xer", offsetof(CPUPPCState, xer) }, + { "xer", 0, &monitor_get_xer }, { "msr", offsetof(CPUPPCState, msr) }, { "tbu", 0, &monitor_get_tbu, }, { "tbl", 0, &monitor_get_tbl, }, diff --git a/target/ppc/power8-pmu-regs.c.inc b/target/ppc/power8-pmu-regs.c.inc new file mode 100644 index 0000000000..7391851238 --- /dev/null +++ b/target/ppc/power8-pmu-regs.c.inc @@ -0,0 +1,262 @@ +/* + * PMU register read/write functions for TCG IBM POWER chips + * + * Copyright IBM Corp. 2021 + * + * Authors: + * Daniel Henrique Barboza + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) + +/* + * Checks whether the Group A SPR (MMCR0, MMCR2, MMCRA, and the + * PMCs) has problem state read access. + * + * Read acccess is granted for all PMCC values but 0b01, where a + * Facility Unavailable Interrupt will occur. + */ +static bool spr_groupA_read_allowed(DisasContext *ctx) +{ + if (!ctx->mmcr0_pmcc0 && ctx->mmcr0_pmcc1) { + gen_hvpriv_exception(ctx, POWERPC_EXCP_FU); + return false; + } + + return true; +} + +/* + * Checks whether the Group A SPR (MMCR0, MMCR2, MMCRA, and the + * PMCs) has problem state write access. + * + * Write acccess is granted for PMCC values 0b10 and 0b11. Userspace + * writing with PMCC 0b00 will generate a Hypervisor Emulation + * Assistance Interrupt. Userspace writing with PMCC 0b01 will + * generate a Facility Unavailable Interrupt. + */ +static bool spr_groupA_write_allowed(DisasContext *ctx) +{ + if (ctx->mmcr0_pmcc0) { + return true; + } + + if (ctx->mmcr0_pmcc1) { + /* PMCC = 0b01 */ + gen_hvpriv_exception(ctx, POWERPC_EXCP_FU); + } else { + /* PMCC = 0b00 */ + gen_hvpriv_exception(ctx, POWERPC_EXCP_INVAL_SPR); + } + + return false; +} + +/* + * Helper function to avoid code repetition between MMCR0 and + * MMCR2 problem state write functions. + * + * 'ret' must be tcg_temp_freed() by the caller. + */ +static TCGv masked_gprn_for_spr_write(int gprn, int sprn, + uint64_t spr_mask) +{ + TCGv ret = tcg_temp_new(); + TCGv t0 = tcg_temp_new(); + + /* 'ret' starts with all mask bits cleared */ + gen_load_spr(ret, sprn); + tcg_gen_andi_tl(ret, ret, ~(spr_mask)); + + /* Apply the mask into 'gprn' in a temp var */ + tcg_gen_andi_tl(t0, cpu_gpr[gprn], spr_mask); + + /* Add the masked gprn bits into 'ret' */ + tcg_gen_or_tl(ret, ret, t0); + + tcg_temp_free(t0); + + return ret; +} + +void spr_read_MMCR0_ureg(DisasContext *ctx, int gprn, int sprn) +{ + TCGv t0; + + if (!spr_groupA_read_allowed(ctx)) { + return; + } + + t0 = tcg_temp_new(); + + /* + * Filter out all bits but FC, PMAO, and PMAE, according + * to ISA v3.1, in 10.4.4 Monitor Mode Control Register 0, + * fourth paragraph. + */ + gen_load_spr(t0, SPR_POWER_MMCR0); + tcg_gen_andi_tl(t0, t0, MMCR0_UREG_MASK); + tcg_gen_mov_tl(cpu_gpr[gprn], t0); + + tcg_temp_free(t0); +} + +void spr_write_MMCR0_ureg(DisasContext *ctx, int sprn, int gprn) +{ + TCGv masked_gprn; + + if (!spr_groupA_write_allowed(ctx)) { + return; + } + + /* + * Filter out all bits but FC, PMAO, and PMAE, according + * to ISA v3.1, in 10.4.4 Monitor Mode Control Register 0, + * fourth paragraph. + */ + masked_gprn = masked_gprn_for_spr_write(gprn, SPR_POWER_MMCR0, + MMCR0_UREG_MASK); + gen_store_spr(SPR_POWER_MMCR0, masked_gprn); + + tcg_temp_free(masked_gprn); +} + +void spr_read_MMCR2_ureg(DisasContext *ctx, int gprn, int sprn) +{ + TCGv t0; + + if (!spr_groupA_read_allowed(ctx)) { + return; + } + + t0 = tcg_temp_new(); + + /* + * On read, filter out all bits that are not FCnP0 bits. + * When MMCR0[PMCC] is set to 0b10 or 0b11, providing + * problem state programs read/write access to MMCR2, + * only the FCnP0 bits can be accessed. All other bits are + * not changed when mtspr is executed in problem state, and + * all other bits return 0s when mfspr is executed in problem + * state, according to ISA v3.1, section 10.4.6 Monitor Mode + * Control Register 2, p. 1316, third paragraph. + */ + gen_load_spr(t0, SPR_POWER_MMCR2); + tcg_gen_andi_tl(t0, t0, MMCR2_UREG_MASK); + tcg_gen_mov_tl(cpu_gpr[gprn], t0); + + tcg_temp_free(t0); +} + +void spr_write_MMCR2_ureg(DisasContext *ctx, int sprn, int gprn) +{ + TCGv masked_gprn; + + if (!spr_groupA_write_allowed(ctx)) { + return; + } + + /* + * Filter the bits that can be written using MMCR2_UREG_MASK, + * similar to what is done in spr_write_MMCR0_ureg(). + */ + masked_gprn = masked_gprn_for_spr_write(gprn, SPR_POWER_MMCR2, + MMCR2_UREG_MASK); + gen_store_spr(SPR_POWER_MMCR2, masked_gprn); + + tcg_temp_free(masked_gprn); +} + +void spr_read_PMC14_ureg(DisasContext *ctx, int gprn, int sprn) +{ + if (!spr_groupA_read_allowed(ctx)) { + return; + } + + spr_read_ureg(ctx, gprn, sprn); +} + +void spr_read_PMC56_ureg(DisasContext *ctx, int gprn, int sprn) +{ + /* + * If PMCC = 0b11, PMC5 and PMC6 aren't included in the Performance + * Monitor, and a read attempt results in a Facility Unavailable + * Interrupt. + */ + if (ctx->mmcr0_pmcc0 && ctx->mmcr0_pmcc1) { + gen_hvpriv_exception(ctx, POWERPC_EXCP_FU); + return; + } + + /* The remaining steps are similar to PMCs 1-4 userspace read */ + spr_read_PMC14_ureg(ctx, gprn, sprn); +} + +void spr_write_PMC14_ureg(DisasContext *ctx, int sprn, int gprn) +{ + if (!spr_groupA_write_allowed(ctx)) { + return; + } + + spr_write_ureg(ctx, sprn, gprn); +} + +void spr_write_PMC56_ureg(DisasContext *ctx, int sprn, int gprn) +{ + /* + * If PMCC = 0b11, PMC5 and PMC6 aren't included in the Performance + * Monitor, and a write attempt results in a Facility Unavailable + * Interrupt. + */ + if (ctx->mmcr0_pmcc0 && ctx->mmcr0_pmcc1) { + gen_hvpriv_exception(ctx, POWERPC_EXCP_FU); + return; + } + + /* The remaining steps are similar to PMCs 1-4 userspace write */ + spr_write_PMC14_ureg(ctx, sprn, gprn); +} +#else +void spr_read_MMCR0_ureg(DisasContext *ctx, int gprn, int sprn) +{ + spr_read_ureg(ctx, gprn, sprn); +} + +void spr_write_MMCR0_ureg(DisasContext *ctx, int sprn, int gprn) +{ + spr_noaccess(ctx, gprn, sprn); +} + +void spr_read_MMCR2_ureg(DisasContext *ctx, int gprn, int sprn) +{ + spr_read_ureg(ctx, gprn, sprn); +} + +void spr_write_MMCR2_ureg(DisasContext *ctx, int sprn, int gprn) +{ + spr_noaccess(ctx, gprn, sprn); +} + +void spr_read_PMC14_ureg(DisasContext *ctx, int gprn, int sprn) +{ + spr_read_ureg(ctx, gprn, sprn); +} + +void spr_read_PMC56_ureg(DisasContext *ctx, int gprn, int sprn) +{ + spr_read_ureg(ctx, gprn, sprn); +} + +void spr_write_PMC14_ureg(DisasContext *ctx, int sprn, int gprn) +{ + spr_noaccess(ctx, gprn, sprn); +} + +void spr_write_PMC56_ureg(DisasContext *ctx, int sprn, int gprn) +{ + spr_noaccess(ctx, gprn, sprn); +} +#endif /* defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) */ diff --git a/target/ppc/spr_tcg.h b/target/ppc/spr_tcg.h index 0be5f347d5..520f1ef233 100644 --- a/target/ppc/spr_tcg.h +++ b/target/ppc/spr_tcg.h @@ -32,6 +32,10 @@ void spr_write_lr(DisasContext *ctx, int sprn, int gprn); void spr_read_ctr(DisasContext *ctx, int gprn, int sprn); void spr_write_ctr(DisasContext *ctx, int sprn, int gprn); void spr_read_ureg(DisasContext *ctx, int gprn, int sprn); +void spr_read_MMCR0_ureg(DisasContext *ctx, int gprn, int sprn); +void spr_read_MMCR2_ureg(DisasContext *ctx, int gprn, int sprn); +void spr_read_PMC14_ureg(DisasContext *ctx, int gprn, int sprn); +void spr_read_PMC56_ureg(DisasContext *ctx, int gprn, int sprn); void spr_read_tbl(DisasContext *ctx, int gprn, int sprn); void spr_read_tbu(DisasContext *ctx, int gprn, int sprn); void spr_read_atbl(DisasContext *ctx, int gprn, int sprn); @@ -40,6 +44,10 @@ void spr_read_601_rtcl(DisasContext *ctx, int gprn, int sprn); void spr_read_601_rtcu(DisasContext *ctx, int gprn, int sprn); void spr_read_spefscr(DisasContext *ctx, int gprn, int sprn); void spr_write_spefscr(DisasContext *ctx, int sprn, int gprn); +void spr_write_MMCR0_ureg(DisasContext *ctx, int sprn, int gprn); +void spr_write_MMCR2_ureg(DisasContext *ctx, int sprn, int gprn); +void spr_write_PMC14_ureg(DisasContext *ctx, int sprn, int gprn); +void spr_write_PMC56_ureg(DisasContext *ctx, int sprn, int gprn); #ifndef CONFIG_USER_ONLY void spr_write_generic32(DisasContext *ctx, int sprn, int gprn); diff --git a/target/ppc/translate.c b/target/ppc/translate.c index c3c6cb9589..518337bcb7 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -175,6 +175,8 @@ struct DisasContext { bool tm_enabled; bool gtse; bool hr; + bool mmcr0_pmcc0; + bool mmcr0_pmcc1; ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */ int singlestep_enabled; uint32_t flags; @@ -4934,32 +4936,40 @@ static void gen_mtmsrd(DisasContext *ctx) CHK_SV; #if !defined(CONFIG_USER_ONLY) + TCGv t0, t1; + target_ulong mask; + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + gen_icount_io_start(ctx); + if (ctx->opcode & 0x00010000) { /* L=1 form only updates EE and RI */ - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], - (1 << MSR_RI) | (1 << MSR_EE)); - tcg_gen_andi_tl(t1, cpu_msr, - ~(target_ulong)((1 << MSR_RI) | (1 << MSR_EE))); - tcg_gen_or_tl(t1, t1, t0); - - gen_helper_store_msr(cpu_env, t1); - tcg_temp_free(t0); - tcg_temp_free(t1); - + mask = (1ULL << MSR_RI) | (1ULL << MSR_EE); } else { + /* mtmsrd does not alter HV, S, ME, or LE */ + mask = ~((1ULL << MSR_LE) | (1ULL << MSR_ME) | (1ULL << MSR_S) | + (1ULL << MSR_HV)); /* * XXX: we need to update nip before the store if we enter * power saving mode, we will exit the loop directly from * ppc_store_msr */ gen_update_nip(ctx, ctx->base.pc_next); - gen_helper_store_msr(cpu_env, cpu_gpr[rS(ctx->opcode)]); } + + tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], mask); + tcg_gen_andi_tl(t1, cpu_msr, ~mask); + tcg_gen_or_tl(t0, t0, t1); + + gen_helper_store_msr(cpu_env, t0); + /* Must stop the translation as machine state (may have) changed */ ctx->base.is_jmp = DISAS_EXIT_UPDATE; + + tcg_temp_free(t0); + tcg_temp_free(t1); #endif /* !defined(CONFIG_USER_ONLY) */ } #endif /* defined(TARGET_PPC64) */ @@ -4969,23 +4979,19 @@ static void gen_mtmsr(DisasContext *ctx) CHK_SV; #if !defined(CONFIG_USER_ONLY) + TCGv t0, t1; + target_ulong mask = 0xFFFFFFFF; + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + gen_icount_io_start(ctx); if (ctx->opcode & 0x00010000) { /* L=1 form only updates EE and RI */ - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], - (1 << MSR_RI) | (1 << MSR_EE)); - tcg_gen_andi_tl(t1, cpu_msr, - ~(target_ulong)((1 << MSR_RI) | (1 << MSR_EE))); - tcg_gen_or_tl(t1, t1, t0); - - gen_helper_store_msr(cpu_env, t1); - tcg_temp_free(t0); - tcg_temp_free(t1); - + mask &= (1ULL << MSR_RI) | (1ULL << MSR_EE); } else { - TCGv msr = tcg_temp_new(); + /* mtmsr does not alter S, ME, or LE */ + mask &= ~((1ULL << MSR_LE) | (1ULL << MSR_ME) | (1ULL << MSR_S)); /* * XXX: we need to update nip before the store if we enter @@ -4993,16 +4999,19 @@ static void gen_mtmsr(DisasContext *ctx) * ppc_store_msr */ gen_update_nip(ctx, ctx->base.pc_next); -#if defined(TARGET_PPC64) - tcg_gen_deposit_tl(msr, cpu_msr, cpu_gpr[rS(ctx->opcode)], 0, 32); -#else - tcg_gen_mov_tl(msr, cpu_gpr[rS(ctx->opcode)]); -#endif - gen_helper_store_msr(cpu_env, msr); - tcg_temp_free(msr); } + + tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], mask); + tcg_gen_andi_tl(t1, cpu_msr, ~mask); + tcg_gen_or_tl(t0, t0, t1); + + gen_helper_store_msr(cpu_env, t0); + /* Must stop the translation as machine state (may have) changed */ ctx->base.is_jmp = DISAS_EXIT_UPDATE; + + tcg_temp_free(t0); + tcg_temp_free(t1); #endif } @@ -5068,19 +5077,15 @@ static void gen_mtspr(DisasContext *ctx) static void gen_setb(DisasContext *ctx) { TCGv_i32 t0 = tcg_temp_new_i32(); - TCGv_i32 t8 = tcg_temp_new_i32(); - TCGv_i32 tm1 = tcg_temp_new_i32(); + TCGv_i32 t8 = tcg_constant_i32(8); + TCGv_i32 tm1 = tcg_constant_i32(-1); int crf = crfS(ctx->opcode); tcg_gen_setcondi_i32(TCG_COND_GEU, t0, cpu_crf[crf], 4); - tcg_gen_movi_i32(t8, 8); - tcg_gen_movi_i32(tm1, -1); tcg_gen_movcond_i32(TCG_COND_GEU, t0, cpu_crf[crf], t8, tm1, t0); tcg_gen_ext_i32_tl(cpu_gpr[rD(ctx->opcode)], t0); tcg_temp_free_i32(t0); - tcg_temp_free_i32(t8); - tcg_temp_free_i32(tm1); } #endif @@ -7481,6 +7486,8 @@ static int times_4(DisasContext *ctx, int x) #include "decode-insn32.c.inc" #include "decode-insn64.c.inc" +#include "power8-pmu-regs.c.inc" + #include "translate/fixedpoint-impl.c.inc" #include "translate/fp-impl.c.inc" @@ -7573,18 +7580,16 @@ static void gen_brw(DisasContext *ctx) /* brh */ static void gen_brh(DisasContext *ctx) { - TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 mask = tcg_constant_i64(0x00ff00ff00ff00ffull); TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); - tcg_gen_movi_i64(t0, 0x00ff00ff00ff00ffull); tcg_gen_shri_i64(t1, cpu_gpr[rS(ctx->opcode)], 8); - tcg_gen_and_i64(t2, t1, t0); - tcg_gen_and_i64(t1, cpu_gpr[rS(ctx->opcode)], t0); + tcg_gen_and_i64(t2, t1, mask); + tcg_gen_and_i64(t1, cpu_gpr[rS(ctx->opcode)], mask); tcg_gen_shli_i64(t1, t1, 8); tcg_gen_or_i64(cpu_gpr[rA(ctx->opcode)], t1, t2); - tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); tcg_temp_free_i64(t2); } @@ -8551,6 +8556,8 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->tm_enabled = (hflags >> HFLAGS_TM) & 1; ctx->gtse = (hflags >> HFLAGS_GTSE) & 1; ctx->hr = (hflags >> HFLAGS_HR) & 1; + ctx->mmcr0_pmcc0 = (hflags >> HFLAGS_PMCC0) & 1; + ctx->mmcr0_pmcc1 = (hflags >> HFLAGS_PMCC1) & 1; ctx->singlestep_enabled = 0; if ((hflags >> HFLAGS_SE) & 1) { diff --git a/tests/acceptance/ppc_405.py b/tests/acceptance/ppc_405.py new file mode 100644 index 0000000000..c534d5d32f --- /dev/null +++ b/tests/acceptance/ppc_405.py @@ -0,0 +1,42 @@ +# Test that the U-Boot firmware boots on ppc 405 machines and check the console +# +# Copyright (c) 2021 Red Hat, Inc. +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from avocado.utils import archive +from avocado_qemu import Test +from avocado_qemu import wait_for_console_pattern +from avocado_qemu import exec_command_and_wait_for_pattern + +class Ppc405Machine(Test): + + timeout = 90 + + def do_test_ppc405(self): + uboot_url = ('https://gitlab.com/huth/u-boot/-/raw/' + 'taihu-2021-10-09/u-boot-taihu.bin') + uboot_hash = ('3208940e908a5edc7c03eab072c60f0dcfadc2ab'); + file_path = self.fetch_asset(uboot_url, asset_hash=uboot_hash) + self.vm.set_console(console_index=1) + self.vm.add_args('-bios', file_path) + self.vm.launch() + wait_for_console_pattern(self, 'AMCC PPC405EP Evaluation Board') + exec_command_and_wait_for_pattern(self, 'reset', 'AMCC PowerPC 405EP') + + def test_ppc_taihu(self): + """ + :avocado: tags=arch:ppc + :avocado: tags=machine:taihu + :avocado: tags=cpu:405ep + """ + self.do_test_ppc405() + + def test_ppc_ref405ep(self): + """ + :avocado: tags=arch:ppc + :avocado: tags=machine:ref405ep + :avocado: tags=cpu:405ep + """ + self.do_test_ppc405() diff --git a/tests/acceptance/ppc_bamboo.py b/tests/acceptance/ppc_bamboo.py new file mode 100644 index 0000000000..dd33bf66f3 --- /dev/null +++ b/tests/acceptance/ppc_bamboo.py @@ -0,0 +1,39 @@ +# Test that Linux kernel boots on the ppc bamboo board and check the console +# +# Copyright (c) 2021 Red Hat +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from avocado.utils import archive +from avocado_qemu import Test +from avocado_qemu import wait_for_console_pattern +from avocado_qemu import exec_command_and_wait_for_pattern + +class BambooMachine(Test): + + timeout = 90 + + def test_ppc_bamboo(self): + """ + :avocado: tags=arch:ppc + :avocado: tags=machine:bamboo + :avocado: tags=cpu:440epb + :avocado: tags=device:rtl8139 + """ + tar_url = ('http://landley.net/aboriginal/downloads/binaries/' + 'system-image-powerpc-440fp.tar.gz') + tar_hash = '53e5f16414b195b82d2c70272f81c2eedb39bad9' + file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) + archive.extract(file_path, self.workdir) + self.vm.set_console() + self.vm.add_args('-kernel', self.workdir + + '/system-image-powerpc-440fp/linux', + '-initrd', self.workdir + + '/system-image-powerpc-440fp/rootfs.cpio.gz', + '-nic', 'user,model=rtl8139,restrict=on') + self.vm.launch() + wait_for_console_pattern(self, 'Type exit when done') + exec_command_and_wait_for_pattern(self, 'ping 10.0.2.2', + '10.0.2.2 is alive!') + exec_command_and_wait_for_pattern(self, 'halt', 'System Halted') diff --git a/tests/tcg/ppc64/Makefile.target b/tests/tcg/ppc64/Makefile.target index a6a4ddaeca..6ab7934fdf 100644 --- a/tests/tcg/ppc64/Makefile.target +++ b/tests/tcg/ppc64/Makefile.target @@ -23,4 +23,6 @@ run-plugin-byte_reverse-with-%: $(call skip-test, "RUN of byte_reverse ($*)", "not built") endif +PPC64_TESTS += signal_save_restore_xer + TESTS += $(PPC64_TESTS) diff --git a/tests/tcg/ppc64le/Makefile.target b/tests/tcg/ppc64le/Makefile.target index c0c14ffbad..5e65b1590d 100644 --- a/tests/tcg/ppc64le/Makefile.target +++ b/tests/tcg/ppc64le/Makefile.target @@ -22,4 +22,6 @@ run-plugin-byte_reverse-with-%: $(call skip-test, "RUN of byte_reverse ($*)", "not built") endif +PPC64LE_TESTS += signal_save_restore_xer + TESTS += $(PPC64LE_TESTS) diff --git a/tests/tcg/ppc64le/signal_save_restore_xer.c b/tests/tcg/ppc64le/signal_save_restore_xer.c new file mode 100644 index 0000000000..e4f8a07dd7 --- /dev/null +++ b/tests/tcg/ppc64le/signal_save_restore_xer.c @@ -0,0 +1,42 @@ +#include +#include +#include +#include + +#define XER_SO (1 << 31) +#define XER_OV (1 << 30) +#define XER_CA (1 << 29) +#define XER_OV32 (1 << 19) +#define XER_CA32 (1 << 18) + +uint64_t saved; + +void sigill_handler(int sig, siginfo_t *si, void *ucontext) +{ + ucontext_t *uc = ucontext; + uc->uc_mcontext.regs->nip += 4; + saved = uc->uc_mcontext.regs->xer; + uc->uc_mcontext.regs->xer |= XER_OV | XER_OV32; +} + +int main(void) +{ + uint64_t initial = XER_CA | XER_CA32, restored; + struct sigaction sa = { + .sa_sigaction = sigill_handler, + .sa_flags = SA_SIGINFO + }; + + sigaction(SIGILL, &sa, NULL); + + asm("mtspr 1, %1\n\t" + ".long 0x0\n\t" + "mfspr %0, 1\n\t" + : "=r" (restored) + : "r" (initial)); + + assert(saved == initial); + assert(restored == (XER_OV | XER_OV32 | XER_CA | XER_CA32)); + + return 0; +}