This implements DDW for emulated PHB. This advertises the query/create/remove RTAS tokens in device tree. This does not advertise the reset RTAS token though, will be added later.
The "ddw" property is enabled by default on a PHB but for compatibility pseries-2.1 machine disables it. Since QEMU does not implement any 64bit DMA capable device, this hack has been used to enable 64bit DMA on E1000: diff --git a/hw/net/e1000.c b/hw/net/e1000.c index 0fc29a0..131f80a 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -240,6 +240,7 @@ static const uint32_t mac_reg_init[] = { [STATUS] = 0x80000000 | E1000_STATUS_GIO_MASTER_ENABLE | E1000_STATUS_ASDV | E1000_STATUS_MTXCKOK | E1000_STATUS_SPEED_1000 | E1000_STATUS_FD | + E1000_STATUS_PCIX_MODE | E1000_STATUS_LU, [MANC] = E1000_MANC_EN_MNG2HOST | E1000_MANC_RCV_TCO_EN | E1000_MANC_ARP_EN | E1000_MANC_0298_EN | Signed-off-by: Alexey Kardashevskiy <a...@ozlabs.ru> --- Changes: v3: * removed reset * windows_num is now 1 or bigger rather than 0-based value and it is only changed in PHB code, not in RTAS * added page mask check in create() v2: * tested on hacked emulated E1000 * implemented DDW reset on the PHB reset * spapr_pci_ddw_remove/spapr_pci_ddw_reset are public for reuse by VFIO --- hw/ppc/spapr.c | 9 +++++ hw/ppc/spapr_pci.c | 94 +++++++++++++++++++++++++++++++++++++++++++++ include/hw/pci-host/spapr.h | 7 ++++ 3 files changed, 110 insertions(+) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index d2d3c27..663cb75 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1670,11 +1670,20 @@ static const TypeInfo spapr_machine_info = { static void spapr_machine_2_1_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + static GlobalProperty compat_props[] = { + { + .driver = TYPE_SPAPR_PCI_HOST_BRIDGE, + .property = "ddw", + .value = stringify(off), + }, + { /* end of list */ } + }; mc->name = "pseries-2.1"; mc->desc = "pSeries Logical Partition (PAPR compliant) v2.1"; mc->alias = "pseries"; mc->is_default = 1; + mc->compat_props = compat_props; } static const TypeInfo spapr_machine_2_1_info = { diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 2968b39..04ee1dc 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -470,6 +470,76 @@ static const MemoryRegionOps spapr_msi_ops = { }; /* + * Dynamic DMA windows + */ +static int spapr_pci_ddw_query(sPAPRPHBState *sphb, + uint32_t *windows_available, + uint32_t *page_size_mask) +{ + *windows_available = 1; + *page_size_mask = DDW_PGSIZE_64K | DDW_PGSIZE_16M; + + return 0; +} + +static int spapr_pci_ddw_create(sPAPRPHBState *sphb, uint32_t page_shift, + uint32_t window_shift, uint32_t liobn, + sPAPRTCETable **ptcet) +{ + if ((page_shift != 16) && (page_shift != 24)) { + return -1; + } + + *ptcet = spapr_tce_new_table(DEVICE(sphb), liobn, + SPAPR_PCI_TCE64_START, page_shift, + 1ULL << (window_shift - page_shift), + true); + if (!*ptcet) { + return -1; + } + memory_region_add_subregion(&sphb->iommu_root, (*ptcet)->bus_offset, + spapr_tce_get_iommu(*ptcet)); + + ++sphb->windows_num; + + return 0; +} + +int spapr_pci_ddw_remove(sPAPRPHBState *sphb, sPAPRTCETable *tcet) +{ + memory_region_del_subregion(&sphb->iommu_root, + spapr_tce_get_iommu(tcet)); + spapr_tce_free_table(tcet); + + return 0; +} + +static int spapr_pci_remove_ddw_cb(Object *child, void *opaque) +{ + sPAPRTCETable *tcet; + + tcet = (sPAPRTCETable *) object_dynamic_cast(child, TYPE_SPAPR_TCE_TABLE); + + /* Delete all dynamic windows, i.e. every except the default one with #0 */ + if (tcet && SPAPR_PCI_DMA_WINDOW_NUM(tcet->liobn)) { + sPAPRPHBState *sphb = opaque; + sPAPRPHBClass *spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb); + + spc->ddw_remove(sphb, tcet); + } + + return 0; +} + +int spapr_pci_ddw_reset(sPAPRPHBState *sphb) +{ + object_child_foreach(OBJECT(sphb), spapr_pci_remove_ddw_cb, sphb); + sphb->windows_num = 1; + + return 0; +} + +/* * PHB PCI device */ static AddressSpace *spapr_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) @@ -668,6 +738,12 @@ static int spapr_phb_children_reset(Object *child, void *opaque) static void spapr_phb_reset(DeviceState *qdev) { + sPAPRPHBClass *spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(qdev); + + if (spc->ddw_reset) { + spc->ddw_reset(SPAPR_PCI_HOST_BRIDGE(qdev)); + } + /* Reset the IOMMU state */ object_child_foreach(OBJECT(qdev), spapr_phb_children_reset, NULL); } @@ -682,6 +758,7 @@ static Property spapr_phb_properties[] = { DEFINE_PROP_UINT64("io_win_addr", sPAPRPHBState, io_win_addr, -1), DEFINE_PROP_UINT64("io_win_size", sPAPRPHBState, io_win_size, SPAPR_PCI_IO_WIN_SIZE), + DEFINE_PROP_BOOL("ddw", sPAPRPHBState, ddw_enabled, true), DEFINE_PROP_END_OF_LIST(), }; @@ -799,6 +876,10 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->cannot_instantiate_with_device_add_yet = false; spc->finish_realize = spapr_phb_finish_realize; + spc->ddw_query = spapr_pci_ddw_query; + spc->ddw_create = spapr_pci_ddw_create; + spc->ddw_remove = spapr_pci_ddw_remove; + spc->ddw_reset = spapr_pci_ddw_reset; } static const TypeInfo spapr_phb_info = { @@ -882,6 +963,12 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, uint32_t interrupt_map_mask[] = { cpu_to_be32(b_ddddd(-1)|b_fff(0)), 0x0, 0x0, cpu_to_be32(-1)}; uint32_t interrupt_map[PCI_SLOT_MAX * PCI_NUM_PINS][7]; + uint32_t ddw_applicable[] = { + RTAS_IBM_QUERY_PE_DMA_WINDOW, + RTAS_IBM_CREATE_PE_DMA_WINDOW, + RTAS_IBM_REMOVE_PE_DMA_WINDOW + }; + sPAPRPHBClass *spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(phb); /* Start populating the FDT */ sprintf(nodename, "pci@%" PRIx64, phb->buid); @@ -911,6 +998,13 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, _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)); + /* Dynamic DMA window */ + if (phb->ddw_enabled && + spc->ddw_query && spc->ddw_create && spc->ddw_remove) { + _FDT(fdt_setprop(fdt, bus_off, "ibm,ddw-applicable", &ddw_applicable, + sizeof(ddw_applicable))); + } + /* Build the interrupt-map, this must matches what is done * in pci_spapr_map_irq */ diff --git a/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h index 07b60c3..01b3d25 100644 --- a/include/hw/pci-host/spapr.h +++ b/include/hw/pci-host/spapr.h @@ -104,6 +104,8 @@ struct sPAPRPHBState { int32_t msi_devs_num; spapr_pci_msi_mig *msi_devs; + bool ddw_enabled; + QLIST_ENTRY(sPAPRPHBState) list; }; @@ -126,6 +128,9 @@ struct sPAPRPHBVFIOState { #define SPAPR_PCI_MEM_WIN_BUS_OFFSET 0x80000000ULL +/* Default 64bit dynamic window offset */ +#define SPAPR_PCI_TCE64_START 0x8000000000000000ULL + static inline qemu_irq spapr_phb_lsi_qirq(struct sPAPRPHBState *phb, int pin) { return xics_get_qirq(spapr->icp, phb->lsi_table[pin].irq); @@ -144,5 +149,7 @@ void spapr_pci_rtas_init(void); sPAPRPHBState *spapr_pci_find_phb(sPAPREnvironment *spapr, uint64_t buid); PCIDevice *spapr_pci_find_dev(sPAPREnvironment *spapr, uint64_t buid, uint32_t config_addr); +int spapr_pci_ddw_remove(sPAPRPHBState *sphb, sPAPRTCETable *tcet); +int spapr_pci_ddw_reset(sPAPRPHBState *sphb); #endif /* __HW_SPAPR_PCI_H__ */ -- 2.0.0