diff options
Diffstat (limited to 'hw/ppc')
-rw-r--r-- | hw/ppc/Makefile.objs | 2 | ||||
-rw-r--r-- | hw/ppc/e500.c | 20 | ||||
-rw-r--r-- | hw/ppc/spapr.c | 104 | ||||
-rw-r--r-- | hw/ppc/spapr_events.c | 2 | ||||
-rw-r--r-- | hw/ppc/spapr_hcall.c | 2 | ||||
-rw-r--r-- | hw/ppc/spapr_iommu.c | 26 | ||||
-rw-r--r-- | hw/ppc/spapr_pci.c | 321 | ||||
-rw-r--r-- | hw/ppc/spapr_pci_vfio.c | 115 | ||||
-rw-r--r-- | hw/ppc/spapr_rtas.c | 49 | ||||
-rw-r--r-- | hw/ppc/spapr_rtc.c | 212 | ||||
-rw-r--r-- | hw/ppc/spapr_vio.c | 47 |
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; |