summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS2
-rw-r--r--hw/intc/Kconfig17
-rw-r--r--hw/intc/meson.build13
-rw-r--r--hw/intc/ppc-uic.c321
-rw-r--r--hw/intc/spapr_xive.c2
-rw-r--r--hw/pci-host/Kconfig5
-rw-r--r--hw/pci-host/meson.build2
-rw-r--r--hw/ppc/Kconfig29
-rw-r--r--hw/ppc/ppc440_bamboo.c38
-rw-r--r--hw/ppc/ppc440_pcix.c50
-rw-r--r--hw/ppc/ppc4xx_devs.c262
-rw-r--r--hw/ppc/spapr.c44
-rw-r--r--hw/ppc/spapr_drc.c63
-rw-r--r--hw/ppc/spapr_events.c3
-rw-r--r--hw/ppc/spapr_hcall.c33
-rw-r--r--hw/ppc/trace-events1
-rw-r--r--hw/ppc/virtex_ml507.c21
-rw-r--r--include/hw/intc/ppc-uic.h73
-rw-r--r--include/hw/pci-host/spapr.h2
-rw-r--r--include/hw/ppc/spapr.h6
-rw-r--r--include/hw/ppc/spapr_drc.h10
-rw-r--r--include/hw/ppc/spapr_xive.h2
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);