diff options
-rw-r--r-- | MAINTAINERS | 2 | ||||
-rw-r--r-- | hw/intc/Kconfig | 17 | ||||
-rw-r--r-- | hw/intc/meson.build | 13 | ||||
-rw-r--r-- | hw/intc/ppc-uic.c | 321 | ||||
-rw-r--r-- | hw/intc/spapr_xive.c | 2 | ||||
-rw-r--r-- | hw/pci-host/Kconfig | 5 | ||||
-rw-r--r-- | hw/pci-host/meson.build | 2 | ||||
-rw-r--r-- | hw/ppc/Kconfig | 29 | ||||
-rw-r--r-- | hw/ppc/ppc440_bamboo.c | 38 | ||||
-rw-r--r-- | hw/ppc/ppc440_pcix.c | 50 | ||||
-rw-r--r-- | hw/ppc/ppc4xx_devs.c | 262 | ||||
-rw-r--r-- | hw/ppc/spapr.c | 44 | ||||
-rw-r--r-- | hw/ppc/spapr_drc.c | 63 | ||||
-rw-r--r-- | hw/ppc/spapr_events.c | 3 | ||||
-rw-r--r-- | hw/ppc/spapr_hcall.c | 33 | ||||
-rw-r--r-- | hw/ppc/trace-events | 1 | ||||
-rw-r--r-- | hw/ppc/virtex_ml507.c | 21 | ||||
-rw-r--r-- | include/hw/intc/ppc-uic.h | 73 | ||||
-rw-r--r-- | include/hw/pci-host/spapr.h | 2 | ||||
-rw-r--r-- | include/hw/ppc/spapr.h | 6 | ||||
-rw-r--r-- | include/hw/ppc/spapr_drc.h | 10 | ||||
-rw-r--r-- | include/hw/ppc/spapr_xive.h | 2 |
22 files changed, 616 insertions, 383 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index da3bd3f752..171e7047aa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1683,6 +1683,8 @@ F: hw/ppc/ppc4*.c F: hw/i2c/ppc4xx_i2c.c F: include/hw/ppc/ppc4xx.h F: include/hw/i2c/ppc4xx_i2c.h +F: hw/intc/ppc-uic.c +F: include/hw/intc/ppc-uic.h Character devices M: Marc-André Lureau <marcandre.lureau@redhat.com> diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index d07954086a..c18d11142a 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -30,23 +30,11 @@ config ARM_GIC_KVM default y depends on ARM_GIC && KVM -config OPENPIC_KVM - bool - default y - depends on OPENPIC && KVM - config XICS bool - depends on POWERNV || PSERIES - -config XICS_SPAPR - bool - select XICS -config XICS_KVM +config XIVE bool - default y - depends on XICS && KVM config ALLWINNER_A10_PIC bool @@ -62,6 +50,9 @@ config S390_FLIC_KVM config OMPIC bool +config PPC_UIC + bool + config RX_ICU bool diff --git a/hw/intc/meson.build b/hw/intc/meson.build index 7c3e9daf58..53cba11569 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -39,8 +39,10 @@ specific_ss.add(when: 'CONFIG_LOONGSON_LIOINTC', if_true: files('loongson_lioint specific_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('mips_gic.c')) specific_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_intc.c')) specific_ss.add(when: 'CONFIG_OMPIC', if_true: files('ompic.c')) -specific_ss.add(when: 'CONFIG_OPENPIC_KVM', if_true: files('openpic_kvm.c')) +specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_OPENPIC'], + if_true: files('openpic_kvm.c')) specific_ss.add(when: 'CONFIG_POWERNV', if_true: files('xics_pnv.c', 'pnv_xive.c')) +specific_ss.add(when: 'CONFIG_PPC_UIC', if_true: files('ppc-uic.c')) specific_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_ic.c', 'bcm2836_control.c')) specific_ss.add(when: 'CONFIG_RX_ICU', if_true: files('rx_icu.c')) specific_ss.add(when: 'CONFIG_S390_FLIC', if_true: files('s390_flic.c')) @@ -49,8 +51,9 @@ specific_ss.add(when: 'CONFIG_SH4', if_true: files('sh_intc.c')) specific_ss.add(when: 'CONFIG_SIFIVE_CLINT', if_true: files('sifive_clint.c')) specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true: files('sifive_plic.c')) specific_ss.add(when: 'CONFIG_XICS', if_true: files('xics.c')) -specific_ss.add(when: 'CONFIG_XICS_KVM', if_true: files('xics_kvm.c')) -specific_ss.add(when: 'CONFIG_XICS_SPAPR', if_true: files('xics_spapr.c')) +specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XICS'], + if_true: files('xics_kvm.c')) +specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('xics_spapr.c', 'spapr_xive.c')) specific_ss.add(when: 'CONFIG_XIVE', if_true: files('xive.c')) -specific_ss.add(when: 'CONFIG_XIVE_KVM', if_true: files('spapr_xive_kvm.c')) -specific_ss.add(when: 'CONFIG_XIVE_SPAPR', if_true: files('spapr_xive.c')) +specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'], + if_true: files('spapr_xive_kvm.c')) diff --git a/hw/intc/ppc-uic.c b/hw/intc/ppc-uic.c new file mode 100644 index 0000000000..b21951eea8 --- /dev/null +++ b/hw/intc/ppc-uic.c @@ -0,0 +1,321 @@ +/* + * "Universal" Interrupt Controller for PowerPPC 4xx embedded processors + * + * Copyright (c) 2007 Jocelyn Mayer + * + * 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 "include/hw/intc/ppc-uic.h" +#include "hw/irq.h" +#include "cpu.h" +#include "hw/ppc/ppc.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" +#include "qapi/error.h" + +enum { + DCR_UICSR = 0x000, + DCR_UICSRS = 0x001, + DCR_UICER = 0x002, + DCR_UICCR = 0x003, + DCR_UICPR = 0x004, + DCR_UICTR = 0x005, + DCR_UICMSR = 0x006, + DCR_UICVR = 0x007, + DCR_UICVCR = 0x008, + DCR_UICMAX = 0x009, +}; + +/*#define DEBUG_UIC*/ + +#ifdef DEBUG_UIC +# define LOG_UIC(...) qemu_log_mask(CPU_LOG_INT, ## __VA_ARGS__) +#else +# define LOG_UIC(...) do { } while (0) +#endif + +static void ppcuic_trigger_irq(PPCUIC *uic) +{ + uint32_t ir, cr; + int start, end, inc, i; + + /* Trigger interrupt if any is pending */ + ir = uic->uicsr & uic->uicer & (~uic->uiccr); + cr = uic->uicsr & uic->uicer & uic->uiccr; + LOG_UIC("%s: uicsr %08" PRIx32 " uicer %08" PRIx32 + " uiccr %08" PRIx32 "\n" + " %08" PRIx32 " ir %08" PRIx32 " cr %08" PRIx32 "\n", + __func__, uic->uicsr, uic->uicer, uic->uiccr, + uic->uicsr & uic->uicer, ir, cr); + if (ir != 0x0000000) { + LOG_UIC("Raise UIC interrupt\n"); + qemu_irq_raise(uic->output_int); + } else { + LOG_UIC("Lower UIC interrupt\n"); + qemu_irq_lower(uic->output_int); + } + /* Trigger critical interrupt if any is pending and update vector */ + if (cr != 0x0000000) { + qemu_irq_raise(uic->output_cint); + if (uic->use_vectors) { + /* Compute critical IRQ vector */ + if (uic->uicvcr & 1) { + start = 31; + end = 0; + inc = -1; + } else { + start = 0; + end = 31; + inc = 1; + } + uic->uicvr = uic->uicvcr & 0xFFFFFFFC; + for (i = start; i <= end; i += inc) { + if (cr & (1 << i)) { + uic->uicvr += (i - start) * 512 * inc; + break; + } + } + } + LOG_UIC("Raise UIC critical interrupt - " + "vector %08" PRIx32 "\n", uic->uicvr); + } else { + LOG_UIC("Lower UIC critical interrupt\n"); + qemu_irq_lower(uic->output_cint); + uic->uicvr = 0x00000000; + } +} + +static void ppcuic_set_irq(void *opaque, int irq_num, int level) +{ + PPCUIC *uic; + uint32_t mask, sr; + + uic = opaque; + mask = 1U << (31 - irq_num); + LOG_UIC("%s: irq %d level %d uicsr %08" PRIx32 + " mask %08" PRIx32 " => %08" PRIx32 " %08" PRIx32 "\n", + __func__, irq_num, level, + uic->uicsr, mask, uic->uicsr & mask, level << irq_num); + if (irq_num < 0 || irq_num > 31) { + return; + } + sr = uic->uicsr; + + /* Update status register */ + if (uic->uictr & mask) { + /* Edge sensitive interrupt */ + if (level == 1) { + uic->uicsr |= mask; + } + } else { + /* Level sensitive interrupt */ + if (level == 1) { + uic->uicsr |= mask; + uic->level |= mask; + } else { + uic->uicsr &= ~mask; + uic->level &= ~mask; + } + } + LOG_UIC("%s: irq %d level %d sr %" PRIx32 " => " + "%08" PRIx32 "\n", __func__, irq_num, level, uic->uicsr, sr); + if (sr != uic->uicsr) { + ppcuic_trigger_irq(uic); + } +} + +static uint32_t dcr_read_uic(void *opaque, int dcrn) +{ + PPCUIC *uic; + uint32_t ret; + + uic = opaque; + dcrn -= uic->dcr_base; + switch (dcrn) { + case DCR_UICSR: + case DCR_UICSRS: + ret = uic->uicsr; + break; + case DCR_UICER: + ret = uic->uicer; + break; + case DCR_UICCR: + ret = uic->uiccr; + break; + case DCR_UICPR: + ret = uic->uicpr; + break; + case DCR_UICTR: + ret = uic->uictr; + break; + case DCR_UICMSR: + ret = uic->uicsr & uic->uicer; + break; + case DCR_UICVR: + if (!uic->use_vectors) { + goto no_read; + } + ret = uic->uicvr; + break; + case DCR_UICVCR: + if (!uic->use_vectors) { + goto no_read; + } + ret = uic->uicvcr; + break; + default: + no_read: + ret = 0x00000000; + break; + } + + return ret; +} + +static void dcr_write_uic(void *opaque, int dcrn, uint32_t val) +{ + PPCUIC *uic; + + uic = opaque; + dcrn -= uic->dcr_base; + LOG_UIC("%s: dcr %d val 0x%x\n", __func__, dcrn, val); + switch (dcrn) { + case DCR_UICSR: + uic->uicsr &= ~val; + uic->uicsr |= uic->level; + ppcuic_trigger_irq(uic); + break; + case DCR_UICSRS: + uic->uicsr |= val; + ppcuic_trigger_irq(uic); + break; + case DCR_UICER: + uic->uicer = val; + ppcuic_trigger_irq(uic); + break; + case DCR_UICCR: + uic->uiccr = val; + ppcuic_trigger_irq(uic); + break; + case DCR_UICPR: + uic->uicpr = val; + break; + case DCR_UICTR: + uic->uictr = val; + ppcuic_trigger_irq(uic); + break; + case DCR_UICMSR: + break; + case DCR_UICVR: + break; + case DCR_UICVCR: + uic->uicvcr = val & 0xFFFFFFFD; + ppcuic_trigger_irq(uic); + break; + } +} + +static void ppc_uic_reset(DeviceState *dev) +{ + PPCUIC *uic = PPC_UIC(dev); + + uic->uiccr = 0x00000000; + uic->uicer = 0x00000000; + uic->uicpr = 0x00000000; + uic->uicsr = 0x00000000; + uic->uictr = 0x00000000; + if (uic->use_vectors) { + uic->uicvcr = 0x00000000; + uic->uicvr = 0x0000000; + } +} + +static void ppc_uic_realize(DeviceState *dev, Error **errp) +{ + PPCUIC *uic = PPC_UIC(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + PowerPCCPU *cpu; + int i; + + if (!uic->cpu) { + /* This is a programming error in the code using this device */ + error_setg(errp, "ppc-uic 'cpu' link property was not set"); + return; + } + + cpu = POWERPC_CPU(uic->cpu); + for (i = 0; i < DCR_UICMAX; i++) { + ppc_dcr_register(&cpu->env, uic->dcr_base + i, uic, + &dcr_read_uic, &dcr_write_uic); + } + + sysbus_init_irq(sbd, &uic->output_int); + sysbus_init_irq(sbd, &uic->output_cint); + qdev_init_gpio_in(dev, ppcuic_set_irq, UIC_MAX_IRQ); +} + +static Property ppc_uic_properties[] = { + DEFINE_PROP_LINK("cpu", PPCUIC, cpu, TYPE_CPU, CPUState *), + DEFINE_PROP_UINT32("dcr-base", PPCUIC, dcr_base, 0x30), + DEFINE_PROP_BOOL("use-vectors", PPCUIC, use_vectors, true), + DEFINE_PROP_END_OF_LIST() +}; + +static const VMStateDescription ppc_uic_vmstate = { + .name = "ppc-uic", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(level, PPCUIC), + VMSTATE_UINT32(uicsr, PPCUIC), + VMSTATE_UINT32(uicer, PPCUIC), + VMSTATE_UINT32(uiccr, PPCUIC), + VMSTATE_UINT32(uicpr, PPCUIC), + VMSTATE_UINT32(uictr, PPCUIC), + VMSTATE_UINT32(uicvcr, PPCUIC), + VMSTATE_UINT32(uicvr, PPCUIC), + VMSTATE_END_OF_LIST() + }, +}; + +static void ppc_uic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = ppc_uic_reset; + dc->realize = ppc_uic_realize; + dc->vmsd = &ppc_uic_vmstate; + device_class_set_props(dc, ppc_uic_properties); +} + +static const TypeInfo ppc_uic_info = { + .name = TYPE_PPC_UIC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PPCUIC), + .class_init = ppc_uic_class_init, +}; + +static void ppc_uic_register_types(void) +{ + type_register_static(&ppc_uic_info); +} + +type_init(ppc_uic_register_types); diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index caedd312d7..801bc19341 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -156,7 +156,7 @@ static void spapr_xive_end_pic_print_info(SpaprXive *xive, XiveEND *end, #define spapr_xive_in_kernel(xive) \ (kvm_irqchip_in_kernel() && (xive)->fd != -1) -void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon) +static void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon) { XiveSource *xsrc = &xive->source; int i; diff --git a/hw/pci-host/Kconfig b/hw/pci-host/Kconfig index 036a61877a..eb03f0489d 100644 --- a/hw/pci-host/Kconfig +++ b/hw/pci-host/Kconfig @@ -60,3 +60,8 @@ config PCI_BONITO select PCI select UNIMP bool + +config PCI_POWERNV + select PCI_EXPRESS + select MSI_NONBROKEN + select PCIE_PORT diff --git a/hw/pci-host/meson.build b/hw/pci-host/meson.build index e6d1b89684..da9d1a9964 100644 --- a/hw/pci-host/meson.build +++ b/hw/pci-host/meson.build @@ -23,7 +23,7 @@ pci_ss.add(when: 'CONFIG_VERSATILE_PCI', if_true: files('versatile.c')) softmmu_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss) -specific_ss.add(when: 'CONFIG_POWERNV', if_true: files( +specific_ss.add(when: 'CONFIG_PCI_POWERNV', if_true: files( 'pnv_phb3.c', 'pnv_phb3_msi.c', 'pnv_phb3_pbcq.c', diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig index dd86e664d2..7e267d94a1 100644 --- a/hw/ppc/Kconfig +++ b/hw/ppc/Kconfig @@ -7,8 +7,8 @@ config PSERIES select PCI select SPAPR_VSCSI select VFIO if LINUX # needed by spapr_pci_vfio.c - select XICS_SPAPR - select XIVE_SPAPR + select XICS + select XIVE select MSI_NONBROKEN select FDT_PPC select CHRP_NVRAM @@ -29,15 +29,13 @@ config POWERNV select XICS select XIVE select FDT_PPC - select PCI_EXPRESS - select MSI_NONBROKEN + select PCI_POWERNV config PPC405 bool select M48T59 select PFLASH_CFI02 select PPC4XX - select SERIAL config PPC440 bool @@ -46,13 +44,14 @@ config PPC440 imply E1000_PCI select PCI_EXPRESS select PPC4XX - select SERIAL select FDT_PPC config PPC4XX bool select BITBANG_I2C select PCI + select PPC_UIC + select SERIAL config SAM460EX bool @@ -61,12 +60,10 @@ config SAM460EX select IDE_SII3112 select M41T80 select PPC440 - select SERIAL select SM501 select SMBUS_EEPROM select USB_EHCI_SYSBUS select USB_OHCI - select FDT_PPC config PREP bool @@ -123,26 +120,10 @@ config VIRTEX bool select PPC4XX select PFLASH_CFI01 - select SERIAL select XILINX select XILINX_ETHLITE select FDT_PPC -config XIVE - bool - depends on POWERNV || PSERIES - -config XIVE_SPAPR - bool - default y - depends on PSERIES - select XIVE - -config XIVE_KVM - bool - default y - depends on XIVE_SPAPR && KVM - # Only used by 64-bit targets config FW_CFG_PPC bool diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index 665bc1784e..b156bcb999 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -33,6 +33,9 @@ #include "sysemu/qtest.h" #include "sysemu/reset.h" #include "hw/sysbus.h" +#include "hw/intc/ppc-uic.h" +#include "hw/qdev-properties.h" +#include "qapi/error.h" #define BINARY_DEVICE_TREE_FILE "bamboo.dtb" @@ -168,13 +171,13 @@ static void bamboo_init(MachineState *machine) MemoryRegion *ram_memories = g_new(MemoryRegion, PPC440EP_SDRAM_NR_BANKS); hwaddr ram_bases[PPC440EP_SDRAM_NR_BANKS]; hwaddr ram_sizes[PPC440EP_SDRAM_NR_BANKS]; - qemu_irq *pic; - qemu_irq *irqs; PCIBus *pcibus; PowerPCCPU *cpu; CPUPPCState *env; target_long initrd_size = 0; DeviceState *dev; + DeviceState *uicdev; + SysBusDevice *uicsbd; int success; int i; @@ -192,10 +195,17 @@ static void bamboo_init(MachineState *machine) ppc_dcr_init(env, NULL, NULL); /* interrupt controller */ - irqs = g_new0(qemu_irq, PPCUIC_OUTPUT_NB); - irqs[PPCUIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]; - irqs[PPCUIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]; - pic = ppcuic_init(env, irqs, 0x0C0, 0, 1); + uicdev = qdev_new(TYPE_PPC_UIC); + uicsbd = SYS_BUS_DEVICE(uicdev); + + object_property_set_link(OBJECT(uicdev), "cpu", OBJECT(cpu), + &error_fatal); + sysbus_realize_and_unref(uicsbd, &error_fatal); + + sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_INT, + ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]); + sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_CINT, + ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]); /* SDRAM controller */ memset(ram_bases, 0, sizeof(ram_bases)); @@ -203,14 +213,18 @@ static void bamboo_init(MachineState *machine) ppc4xx_sdram_banks(machine->ram, PPC440EP_SDRAM_NR_BANKS, ram_memories, ram_bases, ram_sizes, ppc440ep_sdram_bank_sizes); /* XXX 440EP's ECC interrupts are on UIC1, but we've only created UIC0. */ - ppc4xx_sdram_init(env, pic[14], PPC440EP_SDRAM_NR_BANKS, ram_memories, + ppc4xx_sdram_init(env, + qdev_get_gpio_in(uicdev, 14), + PPC440EP_SDRAM_NR_BANKS, ram_memories, ram_bases, ram_sizes, 1); /* PCI */ dev = sysbus_create_varargs(TYPE_PPC4xx_PCI_HOST_BRIDGE, PPC440EP_PCI_CONFIG, - pic[pci_irq_nrs[0]], pic[pci_irq_nrs[1]], - pic[pci_irq_nrs[2]], pic[pci_irq_nrs[3]], + qdev_get_gpio_in(uicdev, pci_irq_nrs[0]), + qdev_get_gpio_in(uicdev, pci_irq_nrs[1]), + qdev_get_gpio_in(uicdev, pci_irq_nrs[2]), + qdev_get_gpio_in(uicdev, pci_irq_nrs[3]), NULL); pcibus = (PCIBus *)qdev_get_child_bus(dev, "pci.0"); if (!pcibus) { @@ -223,12 +237,14 @@ static void bamboo_init(MachineState *machine) memory_region_add_subregion(get_system_memory(), PPC440EP_PCI_IO, isa); if (serial_hd(0) != NULL) { - serial_mm_init(address_space_mem, 0xef600300, 0, pic[0], + serial_mm_init(address_space_mem, 0xef600300, 0, + qdev_get_gpio_in(uicdev, 0), PPC_SERIAL_MM_BAUDBASE, serial_hd(0), DEVICE_BIG_ENDIAN); } if (serial_hd(1) != NULL) { - serial_mm_init(address_space_mem, 0xef600400, 0, pic[1], + serial_mm_init(address_space_mem, 0xef600400, 0, + qdev_get_gpio_in(uicdev, 1), PPC_SERIAL_MM_BAUDBASE, serial_hd(1), DEVICE_BIG_ENDIAN); } diff --git a/hw/ppc/ppc440_pcix.c b/hw/ppc/ppc440_pcix.c index ee952314c8..91cbcd0504 100644 --- a/hw/ppc/ppc440_pcix.c +++ b/hw/ppc/ppc440_pcix.c @@ -169,7 +169,7 @@ static void ppc440_pcix_reg_write4(void *opaque, hwaddr addr, { struct PPC440PCIXState *s = opaque; - trace_ppc440_pcix_reg_read(addr, val); + trace_ppc440_pcix_reg_write(addr, val, size); switch (addr) { case PCI_VENDOR_ID ... PCI_MAX_LAT: stl_le_p(s->dev->config + addr, val); @@ -415,8 +415,15 @@ static void ppc440_pcix_reset(DeviceState *dev) s->sts = 0; } -/* All pins from each slot are tied to a single board IRQ. - * This may need further refactoring for other boards. */ +/* + * All four IRQ[ABCD] pins from all slots are tied to a single board + * IRQ, so our mapping function here maps everything to IRQ 0. + * The code in pci_change_irq_level() tracks the number of times + * the mapped IRQ is asserted and deasserted, so if multiple devices + * assert an IRQ at the same time the behaviour is correct. + * + * This may need further refactoring for boards that use multiple IRQ lines. + */ static int ppc440_pcix_map_irq(PCIDevice *pci_dev, int irq_num) { trace_ppc440_pcix_map_irq(pci_dev->devfn, irq_num, 0); @@ -442,28 +449,35 @@ static AddressSpace *ppc440_pcix_set_iommu(PCIBus *b, void *opaque, int devfn) return &s->bm_as; } -/* The default pci_host_data_{read,write} functions in pci/pci_host.c - * deny access to registers without bit 31 set but our clients want - * this to work so we have to override these here */ -static void pci_host_data_write(void *opaque, hwaddr addr, - uint64_t val, unsigned len) +/* + * Some guests on sam460ex write all kinds of garbage here such as + * missing enable bit and low bits set and still expect this to work + * (apparently it does on real hardware because these boot there) so + * we have to override these ops here and fix it up + */ +static void pci_host_config_write(void *opaque, hwaddr addr, + uint64_t val, unsigned len) { PCIHostState *s = opaque; - pci_data_write(s->bus, s->config_reg | (addr & 3), val, len); + + if (addr != 0 || len != 4) { + return; + } + s->config_reg = (val & 0xfffffffcULL) | (1UL << 31); } -static uint64_t pci_host_data_read(void *opaque, - hwaddr addr, unsigned len) +static uint64_t pci_host_config_read(void *opaque, hwaddr addr, + unsigned len) { PCIHostState *s = opaque; - uint32_t val; - val = pci_data_read(s->bus, s->config_reg | (addr & 3), len); + uint32_t val = s->config_reg; + return val; } -const MemoryRegionOps ppc440_pcix_host_data_ops = { - .read = pci_host_data_read, - .write = pci_host_data_write, +const MemoryRegionOps ppc440_pcix_host_conf_ops = { + .read = pci_host_config_read, + .write = pci_host_config_write, .endianness = DEVICE_LITTLE_ENDIAN, }; @@ -490,9 +504,9 @@ static void ppc440_pcix_realize(DeviceState *dev, Error **errp) pci_setup_iommu(h->bus, ppc440_pcix_set_iommu, s); memory_region_init(&s->container, OBJECT(s), "pci-container", PCI_ALL_SIZE); - memory_region_init_io(&h->conf_mem, OBJECT(s), &pci_host_conf_le_ops, + memory_region_init_io(&h->conf_mem, OBJECT(s), &ppc440_pcix_host_conf_ops, h, "pci-conf-idx", 4); - memory_region_init_io(&h->data_mem, OBJECT(s), &ppc440_pcix_host_data_ops, + memory_region_init_io(&h->data_mem, OBJECT(s), &pci_host_data_le_ops, h, "pci-conf-data", 4); memory_region_init_io(&s->iomem, OBJECT(s), &pci_reg_ops, s, "pci.reg", PPC440_REG_SIZE); diff --git a/hw/ppc/ppc4xx_devs.c b/hw/ppc/ppc4xx_devs.c index f1651e04d9..ffe4cf43e8 100644 --- a/hw/ppc/ppc4xx_devs.c +++ b/hw/ppc/ppc4xx_devs.c @@ -30,9 +30,12 @@ #include "hw/ppc/ppc.h" #include "hw/ppc/ppc4xx.h" #include "hw/boards.h" +#include "hw/intc/ppc-uic.h" +#include "hw/qdev-properties.h" #include "qemu/log.h" #include "exec/address-spaces.h" #include "qemu/error-report.h" +#include "qapi/error.h" /*#define DEBUG_UIC*/ @@ -76,245 +79,40 @@ PowerPCCPU *ppc4xx_init(const char *cpu_type, /*****************************************************************************/ /* "Universal" Interrupt controller */ -enum { - DCR_UICSR = 0x000, - DCR_UICSRS = 0x001, - DCR_UICER = 0x002, - DCR_UICCR = 0x003, - DCR_UICPR = 0x004, - DCR_UICTR = 0x005, - DCR_UICMSR = 0x006, - DCR_UICVR = 0x007, - DCR_UICVCR = 0x008, - DCR_UICMAX = 0x009, -}; - -#define UIC_MAX_IRQ 32 -typedef struct ppcuic_t ppcuic_t; -struct ppcuic_t { - uint32_t dcr_base; - int use_vectors; - uint32_t level; /* Remembers the state of level-triggered interrupts. */ - uint32_t uicsr; /* Status register */ - uint32_t uicer; /* Enable register */ - uint32_t uiccr; /* Critical register */ - uint32_t uicpr; /* Polarity register */ - uint32_t uictr; /* Triggering register */ - uint32_t uicvcr; /* Vector configuration register */ - uint32_t uicvr; - qemu_irq *irqs; -}; - -static void ppcuic_trigger_irq (ppcuic_t *uic) -{ - uint32_t ir, cr; - int start, end, inc, i; - - /* Trigger interrupt if any is pending */ - ir = uic->uicsr & uic->uicer & (~uic->uiccr); - cr = uic->uicsr & uic->uicer & uic->uiccr; - LOG_UIC("%s: uicsr %08" PRIx32 " uicer %08" PRIx32 - " uiccr %08" PRIx32 "\n" - " %08" PRIx32 " ir %08" PRIx32 " cr %08" PRIx32 "\n", - __func__, uic->uicsr, uic->uicer, uic->uiccr, - uic->uicsr & uic->uicer, ir, cr); - if (ir != 0x0000000) { - LOG_UIC("Raise UIC interrupt\n"); - qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_INT]); - } else { - LOG_UIC("Lower UIC interrupt\n"); - qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_INT]); - } - /* Trigger critical interrupt if any is pending and update vector */ - if (cr != 0x0000000) { - qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_CINT]); - if (uic->use_vectors) { - /* Compute critical IRQ vector */ - if (uic->uicvcr & 1) { - start = 31; - end = 0; - inc = -1; - } else { - start = 0; - end = 31; - inc = 1; - } - uic->uicvr = uic->uicvcr & 0xFFFFFFFC; - for (i = start; i <= end; i += inc) { - if (cr & (1 << i)) { - uic->uicvr += (i - start) * 512 * inc; - break; - } - } - } - LOG_UIC("Raise UIC critical interrupt - " - "vector %08" PRIx32 "\n", uic->uicvr); - } else { - LOG_UIC("Lower UIC critical interrupt\n"); - qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_CINT]); - uic->uicvr = 0x00000000; - } -} - -static void ppcuic_set_irq (void *opaque, int irq_num, int level) -{ - ppcuic_t *uic; - uint32_t mask, sr; - - uic = opaque; - mask = 1U << (31-irq_num); - LOG_UIC("%s: irq %d level %d uicsr %08" PRIx32 - " mask %08" PRIx32 " => %08" PRIx32 " %08" PRIx32 "\n", - __func__, irq_num, level, - uic->uicsr, mask, uic->uicsr & mask, level << irq_num); - if (irq_num < 0 || irq_num > 31) - return; - sr = uic->uicsr; - - /* Update status register */ - if (uic->uictr & mask) { - /* Edge sensitive interrupt */ - if (level == 1) - uic->uicsr |= mask; - } else { - /* Level sensitive interrupt */ - if (level == 1) { - uic->uicsr |= mask; - uic->level |= mask; - } else { - uic->uicsr &= ~mask; - uic->level &= ~mask; - } - } - LOG_UIC("%s: irq %d level %d sr %" PRIx32 " => " - "%08" PRIx32 "\n", __func__, irq_num, level, uic->uicsr, sr); - if (sr != uic->uicsr) - ppcuic_trigger_irq(uic); -} - -static uint32_t dcr_read_uic (void *opaque, int dcrn) -{ - ppcuic_t *uic; - uint32_t ret; - - uic = opaque; - dcrn -= uic->dcr_base; - switch (dcrn) { - case DCR_UICSR: - case DCR_UICSRS: - ret = uic->uicsr; - break; - case DCR_UICER: - ret = uic->uicer; - break; - case DCR_UICCR: - ret = uic->uiccr; - break; - case DCR_UICPR: - ret = uic->uicpr; - break; - case DCR_UICTR: - ret = uic->uictr; - break; - case DCR_UICMSR: - ret = uic->uicsr & uic->uicer; - break; - case DCR_UICVR: - if (!uic->use_vectors) - goto no_read; - ret = uic->uicvr; - break; - case DCR_UICVCR: - if (!uic->use_vectors) - goto no_read; - ret = uic->uicvcr; - break; - default: - no_read: - ret = 0x00000000; - break; - } - - return ret; -} - -static void dcr_write_uic (void *opaque, int dcrn, uint32_t val) -{ - ppcuic_t *uic; - - uic = opaque; - dcrn -= uic->dcr_base; - LOG_UIC("%s: dcr %d val 0x%x\n", __func__, dcrn, val); - switch (dcrn) { - case DCR_UICSR: - uic->uicsr &= ~val; - uic->uicsr |= uic->level; - ppcuic_trigger_irq(uic); - break; - case DCR_UICSRS: - uic->uicsr |= val; - ppcuic_trigger_irq(uic); - break; - case DCR_UICER: - uic->uicer = val; - ppcuic_trigger_irq(uic); - break; - case DCR_UICCR: - uic->uiccr = val; - ppcuic_trigger_irq(uic); - break; - case DCR_UICPR: - uic->uicpr = val; - break; - case DCR_UICTR: - uic->uictr = val; - ppcuic_trigger_irq(uic); - break; - case DCR_UICMSR: - break; - case DCR_UICVR: - break; - case DCR_UICVCR: - uic->uicvcr = val & 0xFFFFFFFD; - ppcuic_trigger_irq(uic); - break; - } -} - -static void ppcuic_reset (void *opaque) -{ - ppcuic_t *uic; - - uic = opaque; - uic->uiccr = 0x00000000; - uic->uicer = 0x00000000; - uic->uicpr = 0x00000000; - uic->uicsr = 0x00000000; - uic->uictr = 0x00000000; - if (uic->use_vectors) { - uic->uicvcr = 0x00000000; - uic->uicvr = 0x0000000; - } -} qemu_irq *ppcuic_init (CPUPPCState *env, qemu_irq *irqs, uint32_t dcr_base, int has_ssr, int has_vr) { - ppcuic_t *uic; + DeviceState *uicdev = qdev_new(TYPE_PPC_UIC); + SysBusDevice *uicsbd = SYS_BUS_DEVICE(uicdev); + qemu_irq *uic_irqs; int i; - uic = g_malloc0(sizeof(ppcuic_t)); - uic->dcr_base = dcr_base; - uic->irqs = irqs; - if (has_vr) - uic->use_vectors = 1; - for (i = 0; i < DCR_UICMAX; i++) { - ppc_dcr_register(env, dcr_base + i, uic, - &dcr_read_uic, &dcr_write_uic); + qdev_prop_set_uint32(uicdev, "dcr-base", dcr_base); + qdev_prop_set_bit(uicdev, "use-vectors", has_vr); + object_property_set_link(OBJECT(uicdev), "cpu", OBJECT(env_cpu(env)), + &error_fatal); + sysbus_realize_and_unref(uicsbd, &error_fatal); + + sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_INT, irqs[PPCUIC_OUTPUT_INT]); + sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_CINT, irqs[PPCUIC_OUTPUT_CINT]); + + /* + * Return an allocated array of the UIC's input IRQ lines. + * This is an ugly temporary API to retain compatibility with + * the ppcuic_init() interface from the pre-QOM-conversion UIC. + * None of the callers free this array, so it is leaked -- but + * so was the array allocated by qemu_allocate_irqs() in the + * old code. + * + * The callers should just instantiate the UIC and wire it up + * themselves rather than passing qemu_irq* in and out of this function. + */ + uic_irqs = g_new0(qemu_irq, UIC_MAX_IRQ); + for (i = 0; i < UIC_MAX_IRQ; i++) { + uic_irqs[i] = qdev_get_gpio_in(uicdev, i); } - qemu_register_reset(ppcuic_reset, uic); - - return qemu_allocate_irqs(&ppcuic_set_irq, uic, UIC_MAX_IRQ); + return uic_irqs; } /*****************************************************************************/ diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 489cefcb81..2c403b574e 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1120,6 +1120,7 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space) MachineState *machine = MACHINE(spapr); MachineClass *mc = MACHINE_GET_CLASS(machine); SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); + uint32_t root_drc_type_mask = 0; int ret; void *fdt; SpaprPhbState *phb; @@ -1194,8 +1195,18 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space) spapr_dt_cpus(fdt, spapr); + /* ibm,drc-indexes and friends */ if (smc->dr_lmb_enabled) { - _FDT(spapr_dt_drc(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_LMB)); + root_drc_type_mask |= SPAPR_DR_CONNECTOR_TYPE_LMB; + } + if (smc->dr_phb_enabled) { + root_drc_type_mask |= SPAPR_DR_CONNECTOR_TYPE_PHB; + } + if (mc->nvdimm_supported) { + root_drc_type_mask |= SPAPR_DR_CONNECTOR_TYPE_PMEM; + } + if (root_drc_type_mask) { + _FDT(spapr_dt_drc(fdt, 0, NULL, root_drc_type_mask)); } if (mc->has_hotpluggable_cpus) { @@ -1233,14 +1244,6 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space) } } - if (smc->dr_phb_enabled) { - ret = spapr_dt_drc(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_PHB); - if (ret < 0) { - error_report("Couldn't set up PHB DR device tree properties"); - exit(1); - } - } - /* NVDIMM devices */ if (mc->nvdimm_supported) { spapr_dt_persistent_memory(spapr, fdt); @@ -1563,19 +1566,6 @@ void spapr_setup_hpt(SpaprMachineState *spapr) } } -static int spapr_reset_drcs(Object *child, void *opaque) -{ - SpaprDrc *drc = - (SpaprDrc *) object_dynamic_cast(child, - TYPE_SPAPR_DR_CONNECTOR); - - if (drc) { - spapr_drc_reset(drc); - } - - return 0; -} - static void spapr_machine_reset(MachineState *machine) { SpaprMachineState *spapr = SPAPR_MACHINE(machine); @@ -1630,7 +1620,7 @@ static void spapr_machine_reset(MachineState *machine) * will crash QEMU if the DIMM holding the vring goes away). To avoid such * situations, we reset DRCs after all devices have been reset. */ - object_child_foreach_recursive(object_get_root(), spapr_reset_drcs, NULL); + spapr_drc_reset_all(spapr); spapr_clear_pending_events(spapr); @@ -3437,6 +3427,7 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size, if (dedicated_hp_event_source) { drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, addr_start / SPAPR_MEMORY_BLOCK_SIZE); + g_assert(drc); spapr_hotplug_req_add_by_count_indexed(SPAPR_DR_CONNECTOR_TYPE_LMB, nr_lmbs, spapr_drc_index(drc)); @@ -3677,6 +3668,7 @@ static void spapr_memory_unplug_request(HotplugHandler *hotplug_dev, drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, addr_start / SPAPR_MEMORY_BLOCK_SIZE); + g_assert(drc); spapr_hotplug_req_remove_by_count_indexed(SPAPR_DR_CONNECTOR_TYPE_LMB, nr_lmbs, spapr_drc_index(drc)); } @@ -4064,7 +4056,8 @@ static void spapr_machine_device_unplug_request(HotplugHandler *hotplug_dev, SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - if (spapr_ovec_test(sms->ov5_cas, OV5_HP_EVT)) { + if (!smc->pre_6_0_memory_unplug || + spapr_ovec_test(sms->ov5_cas, OV5_HP_EVT)) { spapr_memory_unplug_request(hotplug_dev, dev, errp); } else { /* NOTE: this means there is a window after guest reset, prior to @@ -4550,8 +4543,11 @@ DEFINE_SPAPR_MACHINE(6_0, "6.0", true); */ static void spapr_machine_5_2_class_options(MachineClass *mc) { + SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + spapr_machine_6_0_class_options(mc); compat_props_add(mc->compat_props, hw_compat_5_2, hw_compat_5_2_len); + smc->pre_6_0_memory_unplug = true; } DEFINE_SPAPR_MACHINE(5_2, "5.2", false); diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index f991cf89a0..8571d5bafe 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -417,9 +417,10 @@ void spapr_drc_detach(SpaprDrc *drc) spapr_drc_release(drc); } -void spapr_drc_reset(SpaprDrc *drc) +bool spapr_drc_reset(SpaprDrc *drc) { SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + bool unplug_completed = false; trace_spapr_drc_reset(spapr_drc_index(drc)); @@ -428,6 +429,7 @@ void spapr_drc_reset(SpaprDrc *drc) */ if (drc->unplug_requested) { spapr_drc_release(drc); + unplug_completed = true; } if (drc->dev) { @@ -444,6 +446,8 @@ void spapr_drc_reset(SpaprDrc *drc) drc->ccs_offset = -1; drc->ccs_depth = -1; } + + return unplug_completed; } static bool spapr_drc_unplug_requested_needed(void *opaque) @@ -462,8 +466,9 @@ static const VMStateDescription vmstate_spapr_drc_unplug_requested = { } }; -bool spapr_drc_transient(SpaprDrc *drc) +static bool spapr_drc_needed(void *opaque) { + SpaprDrc *drc = opaque; SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); /* @@ -483,11 +488,6 @@ bool spapr_drc_transient(SpaprDrc *drc) spapr_drc_unplug_requested(drc); } -static bool spapr_drc_needed(void *opaque) -{ - return spapr_drc_transient(opaque); -} - static const VMStateDescription vmstate_spapr_drc = { .name = "spapr_drc", .version_id = 1, @@ -503,7 +503,7 @@ static const VMStateDescription vmstate_spapr_drc = { } }; -static void realize(DeviceState *d, Error **errp) +static void drc_realize(DeviceState *d, Error **errp) { SpaprDrc *drc = SPAPR_DR_CONNECTOR(d); Object *root_container; @@ -530,7 +530,7 @@ static void realize(DeviceState *d, Error **errp) trace_spapr_drc_realize_complete(spapr_drc_index(drc)); } -static void unrealize(DeviceState *d) +static void drc_unrealize(DeviceState *d) { SpaprDrc *drc = SPAPR_DR_CONNECTOR(d); Object *root_container; @@ -579,8 +579,8 @@ static void spapr_dr_connector_class_init(ObjectClass *k, void *data) { DeviceClass *dk = DEVICE_CLASS(k); - dk->realize = realize; - dk->unrealize = unrealize; + dk->realize = drc_realize; + dk->unrealize = drc_unrealize; /* * Reason: DR connector needs to be wired to either the machine or to a * PHB in spapr_dr_connector_new(). @@ -628,7 +628,7 @@ static void realize_physical(DeviceState *d, Error **errp) SpaprDrcPhysical *drcp = SPAPR_DRC_PHYSICAL(d); Error *local_err = NULL; - realize(d, &local_err); + drc_realize(d, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -644,7 +644,7 @@ static void unrealize_physical(DeviceState *d) { SpaprDrcPhysical *drcp = SPAPR_DRC_PHYSICAL(d); - unrealize(d); + drc_unrealize(d); vmstate_unregister(VMSTATE_IF(drcp), &vmstate_spapr_drc_physical, drcp); qemu_unregister_reset(drc_physical_reset, drcp); } @@ -832,6 +832,12 @@ int spapr_dt_drc(void *fdt, int offset, Object *owner, uint32_t drc_type_mask) GString *drc_names, *drc_types; int ret; + /* + * This should really be only called once per node since it overwrites + * the OF properties if they already exist. + */ + g_assert(!fdt_get_property(fdt, offset, "ibm,drc-indexes", NULL)); + /* the first entry of each properties is a 32-bit integer encoding * the number of elements in the array. we won't know this until * we complete the iteration through all the matching DRCs, but @@ -943,6 +949,37 @@ out: return ret; } +void spapr_drc_reset_all(SpaprMachineState *spapr) +{ + Object *drc_container; + ObjectProperty *prop; + ObjectPropertyIterator iter; + + drc_container = container_get(object_get_root(), DRC_CONTAINER_PATH); +restart: + object_property_iter_init(&iter, drc_container); + while ((prop = object_property_iter_next(&iter))) { + SpaprDrc *drc; + + if (!strstart(prop->type, "link<", NULL)) { + continue; + } + drc = SPAPR_DR_CONNECTOR(object_property_get_link(drc_container, + prop->name, + &error_abort)); + + /* + * This will complete any pending plug/unplug requests. + * In case of a unplugged PHB or PCI bridge, this will + * cause some DRCs to be destroyed and thus potentially + * invalidate the iterator. + */ + if (spapr_drc_reset(drc)) { + goto restart; + } + } +} + /* * RTAS calls */ diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index 3f37b49fd8..6aedd988b3 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -658,7 +658,8 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action, /* we should not be using count_indexed value unless the guest * supports dedicated hotplug event source */ - g_assert(spapr_ovec_test(spapr->ov5_cas, OV5_HP_EVT)); + g_assert(!SPAPR_MACHINE_GET_CLASS(spapr)->pre_6_0_memory_unplug || + spapr_ovec_test(spapr->ov5_cas, OV5_HP_EVT)); hp->drc_id.count_indexed.count = cpu_to_be32(drc_id->count_indexed.count); hp->drc_id.count_indexed.index = diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index c0ea0bd579..e5dfc1ba7a 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -1632,32 +1632,6 @@ static uint32_t cas_check_pvr(PowerPCCPU *cpu, uint32_t max_compat, return best_compat; } -static void spapr_handle_transient_dev_before_cas(SpaprMachineState *spapr) -{ - Object *drc_container; - ObjectProperty *prop; - ObjectPropertyIterator iter; - - drc_container = container_get(object_get_root(), "/dr-connector"); - object_property_iter_init(&iter, drc_container); - while ((prop = object_property_iter_next(&iter))) { - SpaprDrc *drc; - - if (!strstart(prop->type, "link<", NULL)) { - continue; - } - drc = SPAPR_DR_CONNECTOR(object_property_get_link(drc_container, - prop->name, - &error_abort)); - - if (spapr_drc_transient(drc)) { - spapr_drc_reset(drc); - } - } - - spapr_clear_pending_hotplug_events(spapr); -} - target_ulong do_client_architecture_support(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong vec, @@ -1815,7 +1789,12 @@ target_ulong do_client_architecture_support(PowerPCCPU *cpu, spapr_irq_update_active_intc(spapr); - spapr_handle_transient_dev_before_cas(spapr); + /* + * Process all pending hot-plug/unplug requests now. An updated full + * rendered FDT will be returned to the guest. + */ + spapr_drc_reset_all(spapr); + spapr_clear_pending_hotplug_events(spapr); /* * If spapr_machine_reset() did not set up a HPT but one is necessary diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events index 6d8d095aa2..1e91984526 100644 --- a/hw/ppc/trace-events +++ b/hw/ppc/trace-events @@ -96,3 +96,4 @@ ppc440_pcix_set_irq(int irq_num) "PCI irq %d" ppc440_pcix_update_pim(int idx, uint64_t size, uint64_t la) "Added window %d of size=0x%" PRIx64 " to CPU=0x%" PRIx64 ppc440_pcix_update_pom(int idx, uint32_t size, uint64_t la, uint64_t pcia) "Added window %d of size=0x%x from CPU=0x%" PRIx64 " to PCI=0x%" PRIx64 ppc440_pcix_reg_read(uint64_t addr, uint32_t val) "addr 0x%" PRIx64 " = 0x%" PRIx32 +ppc440_pcix_reg_write(uint64_t addr, uint32_t val, uint32_t size) "addr 0x%" PRIx64 " = 0x%" PRIx32 " size 0x%" PRIx32 diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index 07fe49da0d..b26ff17767 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -43,6 +43,7 @@ #include "qemu/option.h" #include "exec/address-spaces.h" +#include "hw/intc/ppc-uic.h" #include "hw/ppc/ppc.h" #include "hw/ppc/ppc4xx.h" #include "hw/qdev-properties.h" @@ -95,7 +96,8 @@ static PowerPCCPU *ppc440_init_xilinx(const char *cpu_type, uint32_t sysclk) { PowerPCCPU *cpu; CPUPPCState *env; - qemu_irq *irqs; + DeviceState *uicdev; + SysBusDevice *uicsbd; cpu = POWERPC_CPU(cpu_create(cpu_type)); env = &cpu->env; @@ -105,10 +107,19 @@ static PowerPCCPU *ppc440_init_xilinx(const char *cpu_type, uint32_t sysclk) ppc_dcr_init(env, NULL, NULL); /* interrupt controller */ - irqs = g_new0(qemu_irq, PPCUIC_OUTPUT_NB); - irqs[PPCUIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]; - irqs[PPCUIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]; - ppcuic_init(env, irqs, 0x0C0, 0, 1); + uicdev = qdev_new(TYPE_PPC_UIC); + uicsbd = SYS_BUS_DEVICE(uicdev); + + object_property_set_link(OBJECT(uicdev), "cpu", OBJECT(cpu), + &error_fatal); + sysbus_realize_and_unref(uicsbd, &error_fatal); + + sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_INT, + ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]); + sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_CINT, + ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]); + + /* This board doesn't wire anything up to the inputs of the UIC. */ return cpu; } diff --git a/include/hw/intc/ppc-uic.h b/include/hw/intc/ppc-uic.h new file mode 100644 index 0000000000..e614e2ffd8 --- /dev/null +++ b/include/hw/intc/ppc-uic.h @@ -0,0 +1,73 @@ +/* + * "Universal" Interrupt Controller for PowerPPC 4xx embedded processors + * + * Copyright (c) 2007 Jocelyn Mayer + * + * 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. + */ + +#ifndef HW_INTC_PPC_UIC_H +#define HW_INTC_PPC_UIC_H + +#include "hw/sysbus.h" +#include "qom/object.h" + +#define TYPE_PPC_UIC "ppc-uic" +OBJECT_DECLARE_SIMPLE_TYPE(PPCUIC, PPC_UIC) + +/* + * QEMU interface: + * QOM property "cpu": link to the PPC CPU + * (no default, must be set) + * QOM property "dcr-base": base of the bank of DCR registers for the UIC + * (default 0x30) + * QOM property "use-vectors": true if the UIC has vector registers + * (default true) + * unnamed GPIO inputs 0..UIC_MAX_IRQ: input IRQ lines + * sysbus IRQs: + * 0 (PPCUIC_OUTPUT_INT): output INT line to the CPU + * 1 (PPCUIC_OUTPUT_CINT): output CINT line to the CPU + */ + +#define UIC_MAX_IRQ 32 + +struct PPCUIC { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + qemu_irq output_int; + qemu_irq output_cint; + + /* properties */ + CPUState *cpu; + uint32_t dcr_base; + bool use_vectors; + + uint32_t level; /* Remembers the state of level-triggered interrupts. */ + uint32_t uicsr; /* Status register */ + uint32_t uicer; /* Enable register */ + uint32_t uiccr; /* Critical register */ + uint32_t uicpr; /* Polarity register */ + uint32_t uictr; /* Triggering register */ + uint32_t uicvcr; /* Vector configuration register */ + uint32_t uicvr; +}; + +#endif diff --git a/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h index 4f58f0223b..bd014823a9 100644 --- a/include/hw/pci-host/spapr.h +++ b/include/hw/pci-host/spapr.h @@ -115,8 +115,6 @@ struct SpaprPhbState { #define SPAPR_PCI_NV2RAM64_WIN_BASE SPAPR_PCI_LIMIT #define SPAPR_PCI_NV2RAM64_WIN_SIZE (2 * TiB) /* For up to 6 GPUs 256GB each */ -/* Max number of these GPUsper a physical box */ -#define NVGPU_MAX_NUM 6 /* Max number of NVLinks per GPU in any physical box */ #define NVGPU_MAX_LINKS 3 diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index e0f10f252c..1cc19575f5 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -112,6 +112,9 @@ typedef enum { #define NUMA_ASSOC_SIZE (MAX_DISTANCE_REF_POINTS + 1) #define VCPU_ASSOC_SIZE (NUMA_ASSOC_SIZE + 1) +/* Max number of these GPUsper a physical box */ +#define NVGPU_MAX_NUM 6 + typedef struct SpaprCapabilities SpaprCapabilities; struct SpaprCapabilities { uint8_t caps[SPAPR_CAP_NUM]; @@ -139,6 +142,7 @@ struct SpaprMachineClass { hwaddr rma_limit; /* clamp the RMA to this size */ bool pre_5_1_assoc_refpoints; bool pre_5_2_numa_associativity; + bool pre_6_0_memory_unplug; bool (*phb_placement)(SpaprMachineState *spapr, uint32_t index, uint64_t *buid, hwaddr *pio, @@ -239,7 +243,7 @@ struct SpaprMachineState { unsigned gpu_numa_id; SpaprTpmProxy *tpm_proxy; - uint32_t numa_assoc_array[MAX_NODES][NUMA_ASSOC_SIZE]; + uint32_t numa_assoc_array[MAX_NODES + NVGPU_MAX_NUM][NUMA_ASSOC_SIZE]; Error *fwnmi_migration_blocker; }; diff --git a/include/hw/ppc/spapr_drc.h b/include/hw/ppc/spapr_drc.h index def3593adc..8982927d5c 100644 --- a/include/hw/ppc/spapr_drc.h +++ b/include/hw/ppc/spapr_drc.h @@ -224,7 +224,8 @@ static inline bool spapr_drc_hotplugged(DeviceState *dev) return dev->hotplugged && !runstate_check(RUN_STATE_INMIGRATE); } -void spapr_drc_reset(SpaprDrc *drc); +/* Returns true if an unplug request completed */ +bool spapr_drc_reset(SpaprDrc *drc); uint32_t spapr_drc_index(SpaprDrc *drc); SpaprDrcType spapr_drc_type(SpaprDrc *drc); @@ -244,8 +245,11 @@ int spapr_dt_drc(void *fdt, int offset, Object *owner, uint32_t drc_type_mask); void spapr_drc_attach(SpaprDrc *drc, DeviceState *d); void spapr_drc_detach(SpaprDrc *drc); -/* Returns true if a hot plug/unplug request is pending */ -bool spapr_drc_transient(SpaprDrc *drc); +/* + * Reset all DRCs, causing pending hot-plug/unplug requests to complete. + * Safely handles potential DRC removal (eg. PHBs or PCI bridges). + */ +void spapr_drc_reset_all(struct SpaprMachineState *spapr); static inline bool spapr_drc_unplug_requested(SpaprDrc *drc) { diff --git a/include/hw/ppc/spapr_xive.h b/include/hw/ppc/spapr_xive.h index 26c8d90d71..b282960ad9 100644 --- a/include/hw/ppc/spapr_xive.h +++ b/include/hw/ppc/spapr_xive.h @@ -66,8 +66,6 @@ typedef struct SpaprXiveClass { */ #define SPAPR_XIVE_BLOCK_ID 0x0 -void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon); - struct SpaprMachineState; void spapr_xive_hcall_init(struct SpaprMachineState *spapr); void spapr_xive_mmio_set_enabled(SpaprXive *xive, bool enable); |