aboutsummaryrefslogtreecommitdiff
path: root/hw/ppc
diff options
context:
space:
mode:
Diffstat (limited to 'hw/ppc')
-rw-r--r--hw/ppc/Makefile.objs2
-rw-r--r--hw/ppc/e500.c20
-rw-r--r--hw/ppc/spapr.c104
-rw-r--r--hw/ppc/spapr_events.c2
-rw-r--r--hw/ppc/spapr_hcall.c2
-rw-r--r--hw/ppc/spapr_iommu.c26
-rw-r--r--hw/ppc/spapr_pci.c321
-rw-r--r--hw/ppc/spapr_pci_vfio.c115
-rw-r--r--hw/ppc/spapr_rtas.c49
-rw-r--r--hw/ppc/spapr_rtc.c212
-rw-r--r--hw/ppc/spapr_vio.c47
11 files changed, 798 insertions, 102 deletions
diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
index 19d99200ae..437955d1d5 100644
--- a/hw/ppc/Makefile.objs
+++ b/hw/ppc/Makefile.objs
@@ -3,7 +3,7 @@ obj-y += ppc.o ppc_booke.o
# IBM pSeries (sPAPR)
obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o
obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
-obj-$(CONFIG_PSERIES) += spapr_pci.o
+obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o
ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
obj-y += spapr_pci_vfio.o
endif
diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c
index 7e17d180c6..fd0d138a1b 100644
--- a/hw/ppc/e500.c
+++ b/hw/ppc/e500.c
@@ -706,17 +706,19 @@ static DeviceState *ppce500_init_mpic_qemu(PPCE500Params *params,
}
static DeviceState *ppce500_init_mpic_kvm(PPCE500Params *params,
- qemu_irq **irqs)
+ qemu_irq **irqs, Error **errp)
{
+ Error *err = NULL;
DeviceState *dev;
CPUState *cs;
- int r;
dev = qdev_create(NULL, TYPE_KVM_OPENPIC);
qdev_prop_set_uint32(dev, "model", params->mpic_version);
- r = qdev_init(dev);
- if (r) {
+ object_property_set_bool(OBJECT(dev), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ object_unparent(OBJECT(dev));
return NULL;
}
@@ -747,15 +749,15 @@ static qemu_irq *ppce500_init_mpic(PPCE500Params *params, MemoryRegion *ccsr,
"kernel_irqchip", true);
bool irqchip_required = qemu_opt_get_bool(machine_opts,
"kernel_irqchip", false);
+ Error *err = NULL;
if (irqchip_allowed) {
- dev = ppce500_init_mpic_kvm(params, irqs);
+ dev = ppce500_init_mpic_kvm(params, irqs, &err);
}
-
if (irqchip_required && !dev) {
- fprintf(stderr, "%s: irqchip requested but unavailable\n",
- __func__);
- abort();
+ error_report("kernel_irqchip requested but unavailable: %s",
+ error_get_pretty(err));
+ exit(1);
}
}
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 23cde20019..4aa979fbbf 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -110,17 +110,20 @@ struct sPAPRMachineState {
sPAPREnvironment *spapr;
static XICSState *try_create_xics(const char *type, int nr_servers,
- int nr_irqs)
+ int nr_irqs, Error **errp)
{
+ Error *err = NULL;
DeviceState *dev;
dev = qdev_create(NULL, type);
qdev_prop_set_uint32(dev, "nr_servers", nr_servers);
qdev_prop_set_uint32(dev, "nr_irqs", nr_irqs);
- if (qdev_init(dev) < 0) {
+ object_property_set_bool(OBJECT(dev), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ object_unparent(OBJECT(dev));
return NULL;
}
-
return XICS_COMMON(dev);
}
@@ -134,23 +137,19 @@ static XICSState *xics_system_init(int nr_servers, int nr_irqs)
"kernel_irqchip", true);
bool irqchip_required = qemu_opt_get_bool(machine_opts,
"kernel_irqchip", false);
+ Error *err = NULL;
+
if (irqchip_allowed) {
- icp = try_create_xics(TYPE_KVM_XICS, nr_servers, nr_irqs);
+ icp = try_create_xics(TYPE_KVM_XICS, nr_servers, nr_irqs, &err);
}
-
if (irqchip_required && !icp) {
- perror("Failed to create in-kernel XICS\n");
- abort();
+ error_report("kernel_irqchip requested but unavailable: %s",
+ error_get_pretty(err));
}
}
if (!icp) {
- icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs);
- }
-
- if (!icp) {
- perror("Failed to create XICS\n");
- abort();
+ icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs, &error_abort);
}
return icp;
@@ -994,6 +993,17 @@ static void spapr_create_nvram(sPAPREnvironment *spapr)
spapr->nvram = (struct sPAPRNVRAM *)dev;
}
+static void spapr_rtc_create(sPAPREnvironment *spapr)
+{
+ DeviceState *dev = qdev_create(NULL, TYPE_SPAPR_RTC);
+
+ qdev_init_nofail(dev);
+ spapr->rtc = dev;
+
+ object_property_add_alias(qdev_get_machine(), "rtc-time",
+ OBJECT(spapr->rtc), "date", NULL);
+}
+
/* Returns whether we want to use VGA or not */
static int spapr_vga_init(PCIBus *pci_bus)
{
@@ -1011,15 +1021,39 @@ static int spapr_vga_init(PCIBus *pci_bus)
}
}
+static int spapr_post_load(void *opaque, int version_id)
+{
+ sPAPREnvironment *spapr = (sPAPREnvironment *)opaque;
+ int err = 0;
+
+ /* In earlier versions, there was no seperate qdev for the PAPR
+ * RTC, so the RTC offset was stored directly in sPAPREnvironment.
+ * So when migrating from those versions, poke the incoming offset
+ * value into the RTC device */
+ if (version_id < 3) {
+ err = spapr_rtc_import_offset(spapr->rtc, spapr->rtc_offset);
+ }
+
+ return err;
+}
+
+static bool version_before_3(void *opaque, int version_id)
+{
+ return version_id < 3;
+}
+
static const VMStateDescription vmstate_spapr = {
.name = "spapr",
- .version_id = 2,
+ .version_id = 3,
.minimum_version_id = 1,
+ .post_load = spapr_post_load,
.fields = (VMStateField[]) {
- VMSTATE_UNUSED(4), /* used to be @next_irq */
+ /* used to be @next_irq */
+ VMSTATE_UNUSED_BUFFER(version_before_3, 0, 4),
/* RTC offset */
- VMSTATE_UINT64(rtc_offset, sPAPREnvironment),
+ VMSTATE_UINT64_TEST(rtc_offset, sPAPREnvironment, version_before_3),
+
VMSTATE_PPC_TIMEBASE_V(tb, sPAPREnvironment, 2),
VMSTATE_END_OF_LIST()
},
@@ -1491,6 +1525,9 @@ static void ppc_spapr_init(MachineState *machine)
/* Set up EPOW events infrastructure */
spapr_events_init(spapr);
+ /* Set up the RTC RTAS interfaces */
+ spapr_rtc_create(spapr);
+
/* Set up VIO bus */
spapr->vio_bus = spapr_vio_bus_init();
@@ -1759,11 +1796,22 @@ static const TypeInfo spapr_machine_info = {
},
};
+#define SPAPR_COMPAT_2_2 \
+ {\
+ .driver = TYPE_SPAPR_PCI_HOST_BRIDGE,\
+ .property = "mem_win_size",\
+ .value = "0x20000000",\
+ }
+
+#define SPAPR_COMPAT_2_1 \
+ SPAPR_COMPAT_2_2
+
static void spapr_machine_2_1_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
static GlobalProperty compat_props[] = {
HW_COMPAT_2_1,
+ SPAPR_COMPAT_2_1,
{ /* end of list */ }
};
@@ -1780,12 +1828,15 @@ static const TypeInfo spapr_machine_2_1_info = {
static void spapr_machine_2_2_class_init(ObjectClass *oc, void *data)
{
+ static GlobalProperty compat_props[] = {
+ SPAPR_COMPAT_2_2,
+ { /* end of list */ }
+ };
MachineClass *mc = MACHINE_CLASS(oc);
mc->name = "pseries-2.2";
mc->desc = "pSeries Logical Partition (PAPR compliant) v2.2";
- mc->alias = "pseries";
- mc->is_default = 1;
+ mc->compat_props = compat_props;
}
static const TypeInfo spapr_machine_2_2_info = {
@@ -1794,11 +1845,28 @@ static const TypeInfo spapr_machine_2_2_info = {
.class_init = spapr_machine_2_2_class_init,
};
+static void spapr_machine_2_3_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->name = "pseries-2.3";
+ mc->desc = "pSeries Logical Partition (PAPR compliant) v2.3";
+ mc->alias = "pseries";
+ mc->is_default = 1;
+}
+
+static const TypeInfo spapr_machine_2_3_info = {
+ .name = TYPE_SPAPR_MACHINE "2.3",
+ .parent = TYPE_SPAPR_MACHINE,
+ .class_init = spapr_machine_2_3_class_init,
+};
+
static void spapr_machine_register_types(void)
{
type_register_static(&spapr_machine_info);
type_register_static(&spapr_machine_2_1_info);
type_register_static(&spapr_machine_2_2_info);
+ type_register_static(&spapr_machine_2_3_info);
}
type_init(spapr_machine_register_types)
diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c
index 1b6157dec4..283e96bca1 100644
--- a/hw/ppc/spapr_events.c
+++ b/hw/ppc/spapr_events.c
@@ -246,7 +246,7 @@ static void spapr_powerdown_req(Notifier *n, void *opaque)
maina->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINA);
maina->hdr.section_length = cpu_to_be16(sizeof(*maina));
/* FIXME: section version, subtype and creator id? */
- qemu_get_timedate(&tm, spapr->rtc_offset);
+ spapr_rtc_read(spapr->rtc, &tm, NULL);
year = tm.tm_year + 1900;
maina->creation_date = cpu_to_be32((to_bcd(year / 100) << 24)
| (to_bcd(year % 100) << 16)
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index 86514472fe..4f76f1cbfe 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -731,12 +731,14 @@ static target_ulong h_set_mode_resource_le(PowerPCCPU *cpu,
CPU_FOREACH(cs) {
set_spr(cs, SPR_LPCR, 0, LPCR_ILE);
}
+ spapr_pci_switch_vga(true);
return H_SUCCESS;
case H_SET_MODE_ENDIAN_LITTLE:
CPU_FOREACH(cs) {
set_spr(cs, SPR_LPCR, LPCR_ILE, LPCR_ILE);
}
+ spapr_pci_switch_vga(false);
return H_SUCCESS;
}
diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c
index ba003da39e..f3990fdc32 100644
--- a/hw/ppc/spapr_iommu.c
+++ b/hw/ppc/spapr_iommu.c
@@ -25,6 +25,7 @@
#include "trace.h"
#include "hw/ppc/spapr.h"
+#include "hw/ppc/spapr_vio.h"
#include <libfdt.h>
@@ -73,9 +74,7 @@ static IOMMUTLBEntry spapr_tce_translate_iommu(MemoryRegion *iommu, hwaddr addr,
.perm = IOMMU_NONE,
};
- if (tcet->bypass) {
- ret.perm = IOMMU_RW;
- } else if ((addr >> tcet->page_shift) < tcet->nb_table) {
+ if ((addr >> tcet->page_shift) < tcet->nb_table) {
/* Check if we are in bound */
hwaddr page_mask = IOMMU_PAGE_MASK(tcet->page_shift);
@@ -91,10 +90,22 @@ static IOMMUTLBEntry spapr_tce_translate_iommu(MemoryRegion *iommu, hwaddr addr,
return ret;
}
+static int spapr_tce_table_post_load(void *opaque, int version_id)
+{
+ sPAPRTCETable *tcet = SPAPR_TCE_TABLE(opaque);
+
+ if (tcet->vdev) {
+ spapr_vio_set_bypass(tcet->vdev, tcet->bypass);
+ }
+
+ return 0;
+}
+
static const VMStateDescription vmstate_spapr_tce_table = {
.name = "spapr_iommu",
.version_id = 2,
.minimum_version_id = 2,
+ .post_load = spapr_tce_table_post_load,
.fields = (VMStateField []) {
/* Sanity check */
VMSTATE_UINT32_EQUAL(liobn, sPAPRTCETable),
@@ -132,7 +143,8 @@ static int spapr_tce_table_realize(DeviceState *dev)
trace_spapr_iommu_new_table(tcet->liobn, tcet, tcet->table, tcet->fd);
memory_region_init_iommu(&tcet->iommu, OBJECT(dev), &spapr_iommu_ops,
- "iommu-spapr", ram_size);
+ "iommu-spapr",
+ (uint64_t)tcet->nb_table << tcet->page_shift);
QLIST_INSERT_HEAD(&spapr_tce_tables, tcet, list);
@@ -192,17 +204,11 @@ MemoryRegion *spapr_tce_get_iommu(sPAPRTCETable *tcet)
return &tcet->iommu;
}
-void spapr_tce_set_bypass(sPAPRTCETable *tcet, bool bypass)
-{
- tcet->bypass = bypass;
-}
-
static void spapr_tce_reset(DeviceState *dev)
{
sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev);
size_t table_size = tcet->nb_table * sizeof(uint64_t);
- tcet->bypass = false;
memset(tcet->table, 0, table_size);
}
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index 21b95b342c..05f4faca6e 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -406,6 +406,258 @@ static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu,
rtas_st(rets, 2, 1);/* 0 == level; 1 == edge */
}
+static void rtas_ibm_set_eeh_option(PowerPCCPU *cpu,
+ sPAPREnvironment *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args, uint32_t nret,
+ target_ulong rets)
+{
+ sPAPRPHBState *sphb;
+ sPAPRPHBClass *spc;
+ uint32_t addr, option;
+ uint64_t buid;
+ int ret;
+
+ if ((nargs != 4) || (nret != 1)) {
+ goto param_error_exit;
+ }
+
+ buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ addr = rtas_ld(args, 0);
+ option = rtas_ld(args, 3);
+
+ sphb = find_phb(spapr, buid);
+ if (!sphb) {
+ goto param_error_exit;
+ }
+
+ spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb);
+ if (!spc->eeh_set_option) {
+ goto param_error_exit;
+ }
+
+ ret = spc->eeh_set_option(sphb, addr, option);
+ rtas_st(rets, 0, ret);
+ return;
+
+param_error_exit:
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+}
+
+static void rtas_ibm_get_config_addr_info2(PowerPCCPU *cpu,
+ sPAPREnvironment *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args, uint32_t nret,
+ target_ulong rets)
+{
+ sPAPRPHBState *sphb;
+ sPAPRPHBClass *spc;
+ PCIDevice *pdev;
+ uint32_t addr, option;
+ uint64_t buid;
+
+ if ((nargs != 4) || (nret != 2)) {
+ goto param_error_exit;
+ }
+
+ buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ sphb = find_phb(spapr, buid);
+ if (!sphb) {
+ goto param_error_exit;
+ }
+
+ spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb);
+ if (!spc->eeh_set_option) {
+ goto param_error_exit;
+ }
+
+ /*
+ * We always have PE address of form "00BB0001". "BB"
+ * represents the bus number of PE's primary bus.
+ */
+ option = rtas_ld(args, 3);
+ switch (option) {
+ case RTAS_GET_PE_ADDR:
+ addr = rtas_ld(args, 0);
+ pdev = find_dev(spapr, buid, addr);
+ if (!pdev) {
+ goto param_error_exit;
+ }
+
+ rtas_st(rets, 1, (pci_bus_num(pdev->bus) << 16) + 1);
+ break;
+ case RTAS_GET_PE_MODE:
+ rtas_st(rets, 1, RTAS_PE_MODE_SHARED);
+ break;
+ default:
+ goto param_error_exit;
+ }
+
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+ return;
+
+param_error_exit:
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+}
+
+static void rtas_ibm_read_slot_reset_state2(PowerPCCPU *cpu,
+ sPAPREnvironment *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args, uint32_t nret,
+ target_ulong rets)
+{
+ sPAPRPHBState *sphb;
+ sPAPRPHBClass *spc;
+ uint64_t buid;
+ int state, ret;
+
+ if ((nargs != 3) || (nret != 4 && nret != 5)) {
+ goto param_error_exit;
+ }
+
+ buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ sphb = find_phb(spapr, buid);
+ if (!sphb) {
+ goto param_error_exit;
+ }
+
+ spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb);
+ if (!spc->eeh_get_state) {
+ goto param_error_exit;
+ }
+
+ ret = spc->eeh_get_state(sphb, &state);
+ rtas_st(rets, 0, ret);
+ if (ret != RTAS_OUT_SUCCESS) {
+ return;
+ }
+
+ rtas_st(rets, 1, state);
+ rtas_st(rets, 2, RTAS_EEH_SUPPORT);
+ rtas_st(rets, 3, RTAS_EEH_PE_UNAVAIL_INFO);
+ if (nret >= 5) {
+ rtas_st(rets, 4, RTAS_EEH_PE_RECOVER_INFO);
+ }
+ return;
+
+param_error_exit:
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+}
+
+static void rtas_ibm_set_slot_reset(PowerPCCPU *cpu,
+ sPAPREnvironment *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args, uint32_t nret,
+ target_ulong rets)
+{
+ sPAPRPHBState *sphb;
+ sPAPRPHBClass *spc;
+ uint32_t option;
+ uint64_t buid;
+ int ret;
+
+ if ((nargs != 4) || (nret != 1)) {
+ goto param_error_exit;
+ }
+
+ buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ option = rtas_ld(args, 3);
+ sphb = find_phb(spapr, buid);
+ if (!sphb) {
+ goto param_error_exit;
+ }
+
+ spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb);
+ if (!spc->eeh_reset) {
+ goto param_error_exit;
+ }
+
+ ret = spc->eeh_reset(sphb, option);
+ rtas_st(rets, 0, ret);
+ return;
+
+param_error_exit:
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+}
+
+static void rtas_ibm_configure_pe(PowerPCCPU *cpu,
+ sPAPREnvironment *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args, uint32_t nret,
+ target_ulong rets)
+{
+ sPAPRPHBState *sphb;
+ sPAPRPHBClass *spc;
+ uint64_t buid;
+ int ret;
+
+ if ((nargs != 3) || (nret != 1)) {
+ goto param_error_exit;
+ }
+
+ buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ sphb = find_phb(spapr, buid);
+ if (!sphb) {
+ goto param_error_exit;
+ }
+
+ spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb);
+ if (!spc->eeh_configure) {
+ goto param_error_exit;
+ }
+
+ ret = spc->eeh_configure(sphb);
+ rtas_st(rets, 0, ret);
+ return;
+
+param_error_exit:
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+}
+
+/* To support it later */
+static void rtas_ibm_slot_error_detail(PowerPCCPU *cpu,
+ sPAPREnvironment *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args, uint32_t nret,
+ target_ulong rets)
+{
+ sPAPRPHBState *sphb;
+ sPAPRPHBClass *spc;
+ int option;
+ uint64_t buid;
+
+ if ((nargs != 8) || (nret != 1)) {
+ goto param_error_exit;
+ }
+
+ buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ sphb = find_phb(spapr, buid);
+ if (!sphb) {
+ goto param_error_exit;
+ }
+
+ spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb);
+ if (!spc->eeh_set_option) {
+ goto param_error_exit;
+ }
+
+ option = rtas_ld(args, 7);
+ switch (option) {
+ case RTAS_SLOT_TEMP_ERR_LOG:
+ case RTAS_SLOT_PERM_ERR_LOG:
+ break;
+ default:
+ goto param_error_exit;
+ }
+
+ /* We don't have error log yet */
+ rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND);
+ return;
+
+param_error_exit:
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+}
+
static int pci_spapr_swizzle(int slot, int pin)
{
return (slot + pin) % PCI_NUM_PINS;
@@ -501,6 +753,12 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
return;
}
+ if (sphb->index > SPAPR_PCI_MAX_INDEX) {
+ error_setg(errp, "\"index\" for PAPR PHB is too large (max %u)",
+ SPAPR_PCI_MAX_INDEX);
+ return;
+ }
+
sphb->buid = SPAPR_PCI_BASE_BUID + sphb->index;
sphb->dma_liobn = SPAPR_PCI_BASE_LIOBN + sphb->index;
@@ -669,7 +927,7 @@ static void spapr_phb_reset(DeviceState *qdev)
}
static Property spapr_phb_properties[] = {
- DEFINE_PROP_INT32("index", sPAPRPHBState, index, -1),
+ DEFINE_PROP_UINT32("index", sPAPRPHBState, index, -1),
DEFINE_PROP_UINT64("buid", sPAPRPHBState, buid, -1),
DEFINE_PROP_UINT32("liobn", sPAPRPHBState, dma_liobn, -1),
DEFINE_PROP_UINT64("mem_win_addr", sPAPRPHBState, mem_win_addr, -1),
@@ -862,6 +1120,10 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb,
int bus_off, i, j;
char nodename[256];
uint32_t bus_range[] = { cpu_to_be32(0), cpu_to_be32(0xff) };
+ const uint64_t mmiosize = memory_region_size(&phb->memwindow);
+ const uint64_t w32max = (1ULL << 32) - SPAPR_PCI_MEM_WIN_BUS_OFFSET;
+ const uint64_t w32size = MIN(w32max, mmiosize);
+ const uint64_t w64size = (mmiosize > w32size) ? (mmiosize - w32size) : 0;
struct {
uint32_t hi;
uint64_t child;
@@ -876,9 +1138,15 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb,
{
cpu_to_be32(b_ss(2)), cpu_to_be64(SPAPR_PCI_MEM_WIN_BUS_OFFSET),
cpu_to_be64(phb->mem_win_addr),
- cpu_to_be64(memory_region_size(&phb->memwindow)),
+ cpu_to_be64(w32size),
+ },
+ {
+ cpu_to_be32(b_ss(3)), cpu_to_be64(1ULL << 32),
+ cpu_to_be64(phb->mem_win_addr + w32size),
+ cpu_to_be64(w64size)
},
};
+ const unsigned sizeof_ranges = (w64size ? 3 : 2) * sizeof(ranges[0]);
uint64_t bus_reg[] = { cpu_to_be64(phb->buid), 0 };
uint32_t interrupt_map_mask[] = {
cpu_to_be32(b_ddddd(-1)|b_fff(0)), 0x0, 0x0, cpu_to_be32(-1)};
@@ -907,7 +1175,7 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb,
_FDT(fdt_setprop_cell(fdt, bus_off, "#interrupt-cells", 0x1));
_FDT(fdt_setprop(fdt, bus_off, "used-by-rtas", NULL, 0));
_FDT(fdt_setprop(fdt, bus_off, "bus-range", &bus_range, sizeof(bus_range)));
- _FDT(fdt_setprop(fdt, bus_off, "ranges", &ranges, sizeof(ranges)));
+ _FDT(fdt_setprop(fdt, bus_off, "ranges", &ranges, sizeof_ranges));
_FDT(fdt_setprop(fdt, bus_off, "reg", &bus_reg, sizeof(bus_reg)));
_FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pci-config-space-type", 0x1));
_FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pe-total-#msi", XICS_IRQS));
@@ -958,6 +1226,25 @@ void spapr_pci_rtas_init(void)
spapr_rtas_register(RTAS_IBM_CHANGE_MSI, "ibm,change-msi",
rtas_ibm_change_msi);
}
+
+ spapr_rtas_register(RTAS_IBM_SET_EEH_OPTION,
+ "ibm,set-eeh-option",
+ rtas_ibm_set_eeh_option);
+ spapr_rtas_register(RTAS_IBM_GET_CONFIG_ADDR_INFO2,
+ "ibm,get-config-addr-info2",
+ rtas_ibm_get_config_addr_info2);
+ spapr_rtas_register(RTAS_IBM_READ_SLOT_RESET_STATE2,
+ "ibm,read-slot-reset-state2",
+ rtas_ibm_read_slot_reset_state2);
+ spapr_rtas_register(RTAS_IBM_SET_SLOT_RESET,
+ "ibm,set-slot-reset",
+ rtas_ibm_set_slot_reset);
+ spapr_rtas_register(RTAS_IBM_CONFIGURE_PE,
+ "ibm,configure-pe",
+ rtas_ibm_configure_pe);
+ spapr_rtas_register(RTAS_IBM_SLOT_ERROR_DETAIL,
+ "ibm,slot-error-detail",
+ rtas_ibm_slot_error_detail);
}
static void spapr_pci_register_types(void)
@@ -966,3 +1253,31 @@ static void spapr_pci_register_types(void)
}
type_init(spapr_pci_register_types)
+
+static int spapr_switch_one_vga(DeviceState *dev, void *opaque)
+{
+ bool be = *(bool *)opaque;
+
+ if (object_dynamic_cast(OBJECT(dev), "VGA")
+ || object_dynamic_cast(OBJECT(dev), "secondary-vga")) {
+ object_property_set_bool(OBJECT(dev), be, "big-endian-framebuffer",
+ &error_abort);
+ }
+ return 0;
+}
+
+void spapr_pci_switch_vga(bool big_endian)
+{
+ sPAPRPHBState *sphb;
+
+ /*
+ * For backward compatibility with existing guests, we switch
+ * the endianness of the VGA controller when changing the guest
+ * interrupt mode
+ */
+ QLIST_FOREACH(sphb, &spapr->phbs, list) {
+ BusState *bus = &PCI_HOST_BRIDGE(sphb)->bus->qbus;
+ qbus_walk_children(bus, spapr_switch_one_vga, NULL, NULL, NULL,
+ &big_endian);
+ }
+}
diff --git a/hw/ppc/spapr_pci_vfio.c b/hw/ppc/spapr_pci_vfio.c
index 144912bf54..99a1be5113 100644
--- a/hw/ppc/spapr_pci_vfio.c
+++ b/hw/ppc/spapr_pci_vfio.c
@@ -76,6 +76,117 @@ static void spapr_phb_vfio_reset(DeviceState *qdev)
/* Do nothing */
}
+static int spapr_phb_vfio_eeh_set_option(sPAPRPHBState *sphb,
+ unsigned int addr, int option)
+{
+ sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb);
+ struct vfio_eeh_pe_op op = { .argsz = sizeof(op) };
+ int ret;
+
+ switch (option) {
+ case RTAS_EEH_DISABLE:
+ op.op = VFIO_EEH_PE_DISABLE;
+ break;
+ case RTAS_EEH_ENABLE: {
+ PCIHostState *phb;
+ PCIDevice *pdev;
+
+ /*
+ * The EEH functionality is enabled on basis of PCI device,
+ * instead of PE. We need check the validity of the PCI
+ * device address.
+ */
+ phb = PCI_HOST_BRIDGE(sphb);
+ pdev = pci_find_device(phb->bus,
+ (addr >> 16) & 0xFF, (addr >> 8) & 0xFF);
+ if (!pdev) {
+ return RTAS_OUT_PARAM_ERROR;
+ }
+
+ op.op = VFIO_EEH_PE_ENABLE;
+ break;
+ }
+ case RTAS_EEH_THAW_IO:
+ op.op = VFIO_EEH_PE_UNFREEZE_IO;
+ break;
+ case RTAS_EEH_THAW_DMA:
+ op.op = VFIO_EEH_PE_UNFREEZE_DMA;
+ break;
+ default:
+ return RTAS_OUT_PARAM_ERROR;
+ }
+
+ ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid,
+ VFIO_EEH_PE_OP, &op);
+ if (ret < 0) {
+ return RTAS_OUT_HW_ERROR;
+ }
+
+ return RTAS_OUT_SUCCESS;
+}
+
+static int spapr_phb_vfio_eeh_get_state(sPAPRPHBState *sphb, int *state)
+{
+ sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb);
+ struct vfio_eeh_pe_op op = { .argsz = sizeof(op) };
+ int ret;
+
+ op.op = VFIO_EEH_PE_GET_STATE;
+ ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid,
+ VFIO_EEH_PE_OP, &op);
+ if (ret < 0) {
+ return RTAS_OUT_PARAM_ERROR;
+ }
+
+ *state = ret;
+ return RTAS_OUT_SUCCESS;
+}
+
+static int spapr_phb_vfio_eeh_reset(sPAPRPHBState *sphb, int option)
+{
+ sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb);
+ struct vfio_eeh_pe_op op = { .argsz = sizeof(op) };
+ int ret;
+
+ switch (option) {
+ case RTAS_SLOT_RESET_DEACTIVATE:
+ op.op = VFIO_EEH_PE_RESET_DEACTIVATE;
+ break;
+ case RTAS_SLOT_RESET_HOT:
+ op.op = VFIO_EEH_PE_RESET_HOT;
+ break;
+ case RTAS_SLOT_RESET_FUNDAMENTAL:
+ op.op = VFIO_EEH_PE_RESET_FUNDAMENTAL;
+ break;
+ default:
+ return RTAS_OUT_PARAM_ERROR;
+ }
+
+ ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid,
+ VFIO_EEH_PE_OP, &op);
+ if (ret < 0) {
+ return RTAS_OUT_HW_ERROR;
+ }
+
+ return RTAS_OUT_SUCCESS;
+}
+
+static int spapr_phb_vfio_eeh_configure(sPAPRPHBState *sphb)
+{
+ sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb);
+ struct vfio_eeh_pe_op op = { .argsz = sizeof(op) };
+ int ret;
+
+ op.op = VFIO_EEH_PE_CONFIGURE;
+ ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid,
+ VFIO_EEH_PE_OP, &op);
+ if (ret < 0) {
+ return RTAS_OUT_PARAM_ERROR;
+ }
+
+ return RTAS_OUT_SUCCESS;
+}
+
static void spapr_phb_vfio_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -84,6 +195,10 @@ static void spapr_phb_vfio_class_init(ObjectClass *klass, void *data)
dc->props = spapr_phb_vfio_properties;
dc->reset = spapr_phb_vfio_reset;
spc->finish_realize = spapr_phb_vfio_finish_realize;
+ spc->eeh_set_option = spapr_phb_vfio_eeh_set_option;
+ spc->eeh_get_state = spapr_phb_vfio_eeh_get_state;
+ spc->eeh_reset = spapr_phb_vfio_eeh_reset;
+ spc->eeh_configure = spapr_phb_vfio_eeh_configure;
}
static const TypeInfo spapr_phb_vfio_info = {
diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c
index 2ec2a8e4d1..0f1ae55828 100644
--- a/hw/ppc/spapr_rtas.c
+++ b/hw/ppc/spapr_rtas.c
@@ -52,51 +52,6 @@ static void rtas_display_character(PowerPCCPU *cpu, sPAPREnvironment *spapr,
}
}
-static void rtas_get_time_of_day(PowerPCCPU *cpu, sPAPREnvironment *spapr,
- uint32_t token, uint32_t nargs,
- target_ulong args,
- uint32_t nret, target_ulong rets)
-{
- struct tm tm;
-
- if (nret != 8) {
- rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
- return;
- }
-
- qemu_get_timedate(&tm, spapr->rtc_offset);
-
- rtas_st(rets, 0, RTAS_OUT_SUCCESS);
- rtas_st(rets, 1, tm.tm_year + 1900);
- rtas_st(rets, 2, tm.tm_mon + 1);
- rtas_st(rets, 3, tm.tm_mday);
- rtas_st(rets, 4, tm.tm_hour);
- rtas_st(rets, 5, tm.tm_min);
- rtas_st(rets, 6, tm.tm_sec);
- rtas_st(rets, 7, 0); /* we don't do nanoseconds */
-}
-
-static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPREnvironment *spapr,
- uint32_t token, uint32_t nargs,
- target_ulong args,
- uint32_t nret, target_ulong rets)
-{
- struct tm tm;
-
- tm.tm_year = rtas_ld(args, 0) - 1900;
- tm.tm_mon = rtas_ld(args, 1) - 1;
- tm.tm_mday = rtas_ld(args, 2);
- tm.tm_hour = rtas_ld(args, 3);
- tm.tm_min = rtas_ld(args, 4);
- tm.tm_sec = rtas_ld(args, 5);
-
- /* Just generate a monitor event for the change */
- qapi_event_send_rtc_change(qemu_timedate_diff(&tm), &error_abort);
- spapr->rtc_offset = qemu_timedate_diff(&tm);
-
- rtas_st(rets, 0, RTAS_OUT_SUCCESS);
-}
-
static void rtas_power_off(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint32_t token, uint32_t nargs, target_ulong args,
uint32_t nret, target_ulong rets)
@@ -400,10 +355,6 @@ static void core_rtas_register_types(void)
{
spapr_rtas_register(RTAS_DISPLAY_CHARACTER, "display-character",
rtas_display_character);
- spapr_rtas_register(RTAS_GET_TIME_OF_DAY, "get-time-of-day",
- rtas_get_time_of_day);
- spapr_rtas_register(RTAS_SET_TIME_OF_DAY, "set-time-of-day",
- rtas_set_time_of_day);
spapr_rtas_register(RTAS_POWER_OFF, "power-off", rtas_power_off);
spapr_rtas_register(RTAS_SYSTEM_REBOOT, "system-reboot",
rtas_system_reboot);
diff --git a/hw/ppc/spapr_rtc.c b/hw/ppc/spapr_rtc.c
new file mode 100644
index 0000000000..83eb7c186f
--- /dev/null
+++ b/hw/ppc/spapr_rtc.c
@@ -0,0 +1,212 @@
+/*
+ * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
+ *
+ * RTAS Real Time Clock
+ *
+ * Copyright (c) 2010-2011 David Gibson, IBM Corporation.
+ * Copyright 2014 David Gibson, Red Hat.
+ *
+ * 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 "cpu.h"
+#include "sysemu/sysemu.h"
+#include "hw/ppc/spapr.h"
+#include "qapi-event.h"
+
+#define SPAPR_RTC(obj) \
+ OBJECT_CHECK(sPAPRRTCState, (obj), TYPE_SPAPR_RTC)
+
+typedef struct sPAPRRTCState sPAPRRTCState;
+struct sPAPRRTCState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ int64_t ns_offset;
+};
+
+#define NSEC_PER_SEC 1000000000LL
+
+void spapr_rtc_read(DeviceState *dev, struct tm *tm, uint32_t *ns)
+{
+ sPAPRRTCState *rtc = SPAPR_RTC(dev);
+ int64_t host_ns = qemu_clock_get_ns(rtc_clock);
+ int64_t guest_ns;
+ time_t guest_s;
+
+ assert(rtc);
+
+ guest_ns = host_ns + rtc->ns_offset;
+ guest_s = guest_ns / NSEC_PER_SEC;
+
+ if (tm) {
+ gmtime_r(&guest_s, tm);
+ }
+ if (ns) {
+ *ns = guest_ns;
+ }
+}
+
+int spapr_rtc_import_offset(DeviceState *dev, int64_t legacy_offset)
+{
+ sPAPRRTCState *rtc;
+
+ if (!dev) {
+ return -ENODEV;
+ }
+
+ rtc = SPAPR_RTC(dev);
+
+ rtc->ns_offset = legacy_offset * NSEC_PER_SEC;
+
+ return 0;
+}
+
+static void rtas_get_time_of_day(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ struct tm tm;
+ uint32_t ns;
+
+ if ((nargs != 0) || (nret != 8)) {
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+
+ if (!spapr->rtc) {
+ rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
+ return;
+ }
+
+ spapr_rtc_read(spapr->rtc, &tm, &ns);
+
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+ rtas_st(rets, 1, tm.tm_year + 1900);
+ rtas_st(rets, 2, tm.tm_mon + 1);
+ rtas_st(rets, 3, tm.tm_mday);
+ rtas_st(rets, 4, tm.tm_hour);
+ rtas_st(rets, 5, tm.tm_min);
+ rtas_st(rets, 6, tm.tm_sec);
+ rtas_st(rets, 7, ns);
+}
+
+static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ sPAPRRTCState *rtc;
+ struct tm tm;
+ time_t new_s;
+ int64_t host_ns;
+
+ if ((nargs != 7) || (nret != 1)) {
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+
+ if (!spapr->rtc) {
+ rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
+ return;
+ }
+
+ tm.tm_year = rtas_ld(args, 0) - 1900;
+ tm.tm_mon = rtas_ld(args, 1) - 1;
+ tm.tm_mday = rtas_ld(args, 2);
+ tm.tm_hour = rtas_ld(args, 3);
+ tm.tm_min = rtas_ld(args, 4);
+ tm.tm_sec = rtas_ld(args, 5);
+
+ new_s = mktimegm(&tm);
+ if (new_s == -1) {
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+
+ /* Generate a monitor event for the change */
+ qapi_event_send_rtc_change(qemu_timedate_diff(&tm), &error_abort);
+
+ rtc = SPAPR_RTC(spapr->rtc);
+
+ host_ns = qemu_clock_get_ns(rtc_clock);
+
+ rtc->ns_offset = (new_s * NSEC_PER_SEC) - host_ns;
+
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+}
+
+static void spapr_rtc_qom_date(Object *obj, struct tm *current_tm, Error **errp)
+{
+ spapr_rtc_read(DEVICE(obj), current_tm, NULL);
+}
+
+static void spapr_rtc_realize(DeviceState *dev, Error **errp)
+{
+ sPAPRRTCState *rtc = SPAPR_RTC(dev);
+ struct tm tm;
+ time_t host_s;
+ int64_t rtc_ns;
+
+ /* Initialize the RTAS RTC from host time */
+
+ qemu_get_timedate(&tm, 0);
+ host_s = mktimegm(&tm);
+ rtc_ns = qemu_clock_get_ns(rtc_clock);
+ rtc->ns_offset = host_s * NSEC_PER_SEC - rtc_ns;
+
+ object_property_add_tm(OBJECT(rtc), "date", spapr_rtc_qom_date, NULL);
+}
+
+static const VMStateDescription vmstate_spapr_rtc = {
+ .name = "spapr/rtc",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT64(ns_offset, sPAPRRTCState),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static void spapr_rtc_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = spapr_rtc_realize;
+ dc->vmsd = &vmstate_spapr_rtc;
+
+ spapr_rtas_register(RTAS_GET_TIME_OF_DAY, "get-time-of-day",
+ rtas_get_time_of_day);
+ spapr_rtas_register(RTAS_SET_TIME_OF_DAY, "set-time-of-day",
+ rtas_set_time_of_day);
+}
+
+static const TypeInfo spapr_rtc_info = {
+ .name = TYPE_SPAPR_RTC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(sPAPRRTCState),
+ .class_size = sizeof(XICSStateClass),
+ .class_init = spapr_rtc_class_init,
+};
+
+static void spapr_rtc_register_types(void)
+{
+ type_register_static(&spapr_rtc_info);
+}
+type_init(spapr_rtc_register_types)
diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c
index 032ee1a5f1..1360b97ab0 100644
--- a/hw/ppc/spapr_vio.c
+++ b/hw/ppc/spapr_vio.c
@@ -322,6 +322,18 @@ static void spapr_vio_quiesce_one(VIOsPAPRDevice *dev)
free_crq(dev);
}
+void spapr_vio_set_bypass(VIOsPAPRDevice *dev, bool bypass)
+{
+ if (!dev->tcet) {
+ return;
+ }
+
+ memory_region_set_enabled(&dev->mrbypass, bypass);
+ memory_region_set_enabled(spapr_tce_get_iommu(dev->tcet), !bypass);
+
+ dev->tcet->bypass = bypass;
+}
+
static void rtas_set_tce_bypass(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint32_t token,
uint32_t nargs, target_ulong args,
@@ -348,7 +360,7 @@ static void rtas_set_tce_bypass(PowerPCCPU *cpu, sPAPREnvironment *spapr,
return;
}
- spapr_tce_set_bypass(dev->tcet, !!enable);
+ spapr_vio_set_bypass(dev, !!enable);
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
}
@@ -407,12 +419,13 @@ static void spapr_vio_busdev_reset(DeviceState *qdev)
dev->signal_state = 0;
+ spapr_vio_set_bypass(dev, false);
if (pc->reset) {
pc->reset(dev);
}
}
-static int spapr_vio_busdev_init(DeviceState *qdev)
+static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp)
{
VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev;
VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
@@ -428,11 +441,11 @@ static int spapr_vio_busdev_init(DeviceState *qdev)
VIOsPAPRDevice *other = reg_conflict(dev);
if (other) {
- fprintf(stderr, "vio: %s and %s devices conflict at address %#x\n",
- object_get_typename(OBJECT(qdev)),
- object_get_typename(OBJECT(&other->qdev)),
- dev->reg);
- return -1;
+ error_setg(errp, "%s and %s devices conflict at address %#x",
+ object_get_typename(OBJECT(qdev)),
+ object_get_typename(OBJECT(&other->qdev)),
+ dev->reg);
+ return;
}
} else {
/* Need to assign an address */
@@ -451,20 +464,32 @@ static int spapr_vio_busdev_init(DeviceState *qdev)
dev->irq = xics_alloc(spapr->icp, 0, dev->irq, false);
if (!dev->irq) {
- return -1;
+ error_setg(errp, "can't allocate IRQ");
+ return;
}
if (pc->rtce_window_size) {
uint32_t liobn = SPAPR_VIO_BASE_LIOBN | dev->reg;
+
+ memory_region_init(&dev->mrroot, OBJECT(dev), "iommu-spapr-root",
+ ram_size);
+ memory_region_init_alias(&dev->mrbypass, OBJECT(dev),
+ "iommu-spapr-bypass", get_system_memory(),
+ 0, ram_size);
+ memory_region_add_subregion_overlap(&dev->mrroot, 0, &dev->mrbypass, 1);
+ address_space_init(&dev->as, &dev->mrroot, qdev->id);
+
dev->tcet = spapr_tce_new_table(qdev, liobn,
0,
SPAPR_TCE_PAGE_SHIFT,
pc->rtce_window_size >>
SPAPR_TCE_PAGE_SHIFT, false);
- address_space_init(&dev->as, spapr_tce_get_iommu(dev->tcet), qdev->id);
+ dev->tcet->vdev = dev;
+ memory_region_add_subregion_overlap(&dev->mrroot, 0,
+ spapr_tce_get_iommu(dev->tcet), 2);
}
- return pc->init(dev);
+ pc->realize(dev, errp);
}
static target_ulong h_vio_signal(PowerPCCPU *cpu, sPAPREnvironment *spapr,
@@ -570,7 +595,7 @@ const VMStateDescription vmstate_spapr_vio = {
static void vio_spapr_device_class_init(ObjectClass *klass, void *data)
{
DeviceClass *k = DEVICE_CLASS(klass);
- k->init = spapr_vio_busdev_init;
+ k->realize = spapr_vio_busdev_realize;
k->reset = spapr_vio_busdev_reset;
k->bus_type = TYPE_SPAPR_VIO_BUS;
k->props = spapr_vio_props;