From da6ccee4184482b45a2cb562c7373639792fc58d Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Tue, 27 May 2014 15:36:31 +1000 Subject: spapr_pci: Introduce a finish_realize() callback The spapr-pci PHB initializes IOMMU for emulated devices only. The upcoming VFIO support will do it different. However both emulated and VFIO PHB types share most of the initialization code. For the type specific things a new finish_realize() callback is introduced. This introduces sPAPRPHBClass derived from PCIHostBridgeClass and adds the callback pointer. This implements finish_realize() for emulated devices. Signed-off-by: Alexey Kardashevskiy [agraf: Fix compilation] Signed-off-by: Alexander Graf --- include/hw/pci-host/spapr.h | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'include/hw/pci-host') diff --git a/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h index 970b4a9e4a..0f428a1af9 100644 --- a/include/hw/pci-host/spapr.h +++ b/include/hw/pci-host/spapr.h @@ -34,7 +34,21 @@ #define SPAPR_PCI_HOST_BRIDGE(obj) \ OBJECT_CHECK(sPAPRPHBState, (obj), TYPE_SPAPR_PCI_HOST_BRIDGE) -typedef struct sPAPRPHBState { +#define SPAPR_PCI_HOST_BRIDGE_CLASS(klass) \ + OBJECT_CLASS_CHECK(sPAPRPHBClass, (klass), TYPE_SPAPR_PCI_HOST_BRIDGE) +#define SPAPR_PCI_HOST_BRIDGE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(sPAPRPHBClass, (obj), TYPE_SPAPR_PCI_HOST_BRIDGE) + +typedef struct sPAPRPHBClass sPAPRPHBClass; +typedef struct sPAPRPHBState sPAPRPHBState; + +struct sPAPRPHBClass { + PCIHostBridgeClass parent_class; + + void (*finish_realize)(sPAPRPHBState *sphb, Error **errp); +}; + +struct sPAPRPHBState { PCIHostState parent_obj; int32_t index; @@ -62,7 +76,7 @@ typedef struct sPAPRPHBState { } msi_table[SPAPR_MSIX_MAX_DEVS]; QLIST_ENTRY(sPAPRPHBState) list; -} sPAPRPHBState; +}; #define SPAPR_PCI_BASE_BUID 0x800000020000000ULL -- cgit 1.4.1 From cca7fad5765251fece44cd230156a101867522dd Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Tue, 27 May 2014 15:36:32 +1000 Subject: spapr_pci: spapr_iommu: Make DMA window a subregion Currently the default DMA window is represented by a single MemoryRegion. However there can be more than just one window so we need a "root" memory region to be separated from the actual DMA window(s). This introduces a "root" IOMMU memory region and adds a subregion for the default DMA 32bit window. Following patches will add other subregion(s). This initializes a default DMA window subregion size to the guest RAM size as this window can be switched into "bypass" mode which implements direct DMA mapping. Signed-off-by: Alexey Kardashevskiy Signed-off-by: Alexander Graf --- hw/ppc/spapr_iommu.c | 2 +- hw/ppc/spapr_pci.c | 20 ++++++++++++++++++-- include/hw/pci-host/spapr.h | 1 + 3 files changed, 20 insertions(+), 3 deletions(-) (limited to 'include/hw/pci-host') diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index b855e7ca6b..2d50faf698 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -135,7 +135,7 @@ 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", UINT64_MAX); + "iommu-spapr", ram_size); QLIST_INSERT_HEAD(&spapr_tce_tables, tcet, list); diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 56944c1902..ba020320eb 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -610,6 +610,20 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) PCI_DEVFN(0, 0), PCI_NUM_PINS, TYPE_PCI_BUS); phb->bus = bus; + /* + * Initialize PHB address space. + * By default there will be at least one subregion for default + * 32bit DMA window. + * Later the guest might want to create another DMA window + * which will become another memory subregion. + */ + sprintf(namebuf, "%s.iommu-root", sphb->dtbusname); + + memory_region_init(&sphb->iommu_root, OBJECT(sphb), + namebuf, UINT64_MAX); + address_space_init(&sphb->iommu_as, &sphb->iommu_root, + sphb->dtbusname); + pci_setup_iommu(bus, spapr_pci_dma_iommu, sphb); pci_bus_set_route_irq_fn(bus, spapr_route_intx_pin_to_irq); @@ -648,8 +662,10 @@ static void spapr_phb_finish_realize(sPAPRPHBState *sphb, Error **errp) sphb->dtbusname); return ; } - address_space_init(&sphb->iommu_as, spapr_tce_get_iommu(sphb->tcet), - sphb->dtbusname); + + /* Register default 32bit DMA window */ + memory_region_add_subregion(&sphb->iommu_root, 0, + spapr_tce_get_iommu(sphb->tcet)); } static void spapr_phb_reset(DeviceState *qdev) diff --git a/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h index 0f428a1af9..fcb62137a2 100644 --- a/include/hw/pci-host/spapr.h +++ b/include/hw/pci-host/spapr.h @@ -64,6 +64,7 @@ struct sPAPRPHBState { uint64_t dma_window_size; sPAPRTCETable *tcet; AddressSpace iommu_as; + MemoryRegion iommu_root; struct spapr_pci_lsi { uint32_t irq; -- cgit 1.4.1 From e28c16f61feefa197e04e83662f32bfc1d607723 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Tue, 27 May 2014 15:36:33 +1000 Subject: spapr_pci: Allow multiple TCE tables per PHB At the moment sPAPRPHBState contains a @tcet pointer to the only TCE table. However sPAPR spec allows having more than one DMA window. Since the TCE object is already a child of SPAPR PHB object, there is no need to keep an additional pointer to it in sPAPRPHBState so remove it. This changes the way sPAPRPHBState::reset performs reset of sPAPRTCETable objects. This changes the default DMA window properties calculation. Signed-off-by: Alexey Kardashevskiy Signed-off-by: Alexander Graf --- hw/ppc/spapr_pci.c | 54 ++++++++++++++++++++++++++++++++++++--------- include/hw/pci-host/spapr.h | 1 - 2 files changed, 43 insertions(+), 12 deletions(-) (limited to 'include/hw/pci-host') diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index ba020320eb..bc23614956 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -653,11 +653,13 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) static void spapr_phb_finish_realize(sPAPRPHBState *sphb, Error **errp) { + sPAPRTCETable *tcet; + sphb->dma_window_start = 0; sphb->dma_window_size = 0x40000000; - sphb->tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn, - sphb->dma_window_size); - if (!sphb->tcet) { + tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn, + sphb->dma_window_size); + if (!tcet) { error_setg(errp, "Unable to create TCE table for %s", sphb->dtbusname); return ; @@ -665,16 +667,24 @@ static void spapr_phb_finish_realize(sPAPRPHBState *sphb, Error **errp) /* Register default 32bit DMA window */ memory_region_add_subregion(&sphb->iommu_root, 0, - spapr_tce_get_iommu(sphb->tcet)); + spapr_tce_get_iommu(tcet)); } -static void spapr_phb_reset(DeviceState *qdev) +static int spapr_phb_children_reset(Object *child, void *opaque) { - SysBusDevice *s = SYS_BUS_DEVICE(qdev); - sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(s); + DeviceState *dev = (DeviceState *) object_dynamic_cast(child, TYPE_DEVICE); + + if (dev) { + device_reset(dev); + } + return 0; +} + +static void spapr_phb_reset(DeviceState *qdev) +{ /* Reset the IOMMU state */ - device_reset(DEVICE(sphb->tcet)); + object_child_foreach(OBJECT(qdev), spapr_phb_children_reset, NULL); } static Property spapr_phb_properties[] = { @@ -788,6 +798,29 @@ PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index) #define b_fff(x) b_x((x), 8, 3) /* function number */ #define b_rrrrrrrr(x) b_x((x), 0, 8) /* register number */ +typedef struct sPAPRTCEDT { + void *fdt; + int node_off; +} sPAPRTCEDT; + +static int spapr_phb_children_dt(Object *child, void *opaque) +{ + sPAPRTCEDT *p = opaque; + sPAPRTCETable *tcet; + + tcet = (sPAPRTCETable *) object_dynamic_cast(child, TYPE_SPAPR_TCE_TABLE); + if (!tcet) { + return 0; + } + + spapr_dma_dt(p->fdt, p->node_off, "ibm,dma-window", + tcet->liobn, 0, + tcet->window_size); + /* Stop after the first window */ + + return 1; +} + int spapr_populate_pci_dt(sPAPRPHBState *phb, uint32_t xics_phandle, void *fdt) @@ -867,9 +900,8 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, _FDT(fdt_setprop(fdt, bus_off, "interrupt-map", &interrupt_map, sizeof(interrupt_map))); - spapr_dma_dt(fdt, bus_off, "ibm,dma-window", - phb->dma_liobn, phb->dma_window_start, - phb->dma_window_size); + object_child_foreach(OBJECT(phb), spapr_phb_children_dt, + &((sPAPRTCEDT){ .fdt = fdt, .node_off = bus_off })); return 0; } diff --git a/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h index fcb62137a2..9ce83ea1aa 100644 --- a/include/hw/pci-host/spapr.h +++ b/include/hw/pci-host/spapr.h @@ -62,7 +62,6 @@ struct sPAPRPHBState { uint32_t dma_liobn; uint64_t dma_window_start; uint64_t dma_window_size; - sPAPRTCETable *tcet; AddressSpace iommu_as; MemoryRegion iommu_root; -- cgit 1.4.1 From 523e7b8ab818ec3fab14cfd7a20d51cb3aa33a9d Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Tue, 27 May 2014 15:36:35 +1000 Subject: spapr_iommu: Get rid of window_size in sPAPRTCETable This removes window_size as it is basically a copy of nb_table shifted by SPAPR_TCE_PAGE_SHIFT. As new dynamic DMA windows are going to support windows as big as the entire RAM and this number will be bigger that 32 capacity, we will have to do something about @window_size anyway and removal seems to be the right way to go. This removes dma_window_start/dma_window_size from sPAPRPHBState as they are no longer used. Signed-off-by: Alexey Kardashevskiy Signed-off-by: Alexander Graf --- hw/ppc/spapr_iommu.c | 45 +++++++++++++++++---------------------------- hw/ppc/spapr_pci.c | 6 ++---- hw/ppc/spapr_vio.c | 4 +++- include/hw/pci-host/spapr.h | 2 -- include/hw/ppc/spapr.h | 3 +-- target-ppc/kvm.c | 4 ++-- target-ppc/kvm_ppc.h | 2 +- 7 files changed, 26 insertions(+), 40 deletions(-) (limited to 'include/hw/pci-host') diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index e5fd0882f9..738b936489 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -70,7 +70,7 @@ static IOMMUTLBEntry spapr_tce_translate_iommu(MemoryRegion *iommu, hwaddr addr) if (tcet->bypass) { ret.perm = IOMMU_RW; - } else if (addr < tcet->window_size) { + } else if ((addr >> SPAPR_TCE_PAGE_SHIFT) < tcet->nb_table) { /* Check if we are in bound */ tce = tcet->table[addr >> SPAPR_TCE_PAGE_SHIFT]; ret.iova = addr & ~SPAPR_TCE_PAGE_MASK; @@ -84,24 +84,14 @@ static IOMMUTLBEntry spapr_tce_translate_iommu(MemoryRegion *iommu, hwaddr addr) return ret; } -static int spapr_tce_table_pre_load(void *opaque) -{ - sPAPRTCETable *tcet = SPAPR_TCE_TABLE(opaque); - - tcet->nb_table = tcet->window_size >> SPAPR_TCE_PAGE_SHIFT; - - return 0; -} - static const VMStateDescription vmstate_spapr_tce_table = { .name = "spapr_iommu", - .version_id = 1, - .minimum_version_id = 1, - .pre_load = spapr_tce_table_pre_load, - .fields = (VMStateField[]) { + .version_id = 2, + .minimum_version_id = 2, + .fields = (VMStateField []) { /* Sanity check */ VMSTATE_UINT32_EQUAL(liobn, sPAPRTCETable), - VMSTATE_UINT32_EQUAL(window_size, sPAPRTCETable), + VMSTATE_UINT32_EQUAL(nb_table, sPAPRTCETable), /* IOMMU state */ VMSTATE_BOOL(bypass, sPAPRTCETable), @@ -121,16 +111,15 @@ static int spapr_tce_table_realize(DeviceState *dev) if (kvm_enabled()) { tcet->table = kvmppc_create_spapr_tce(tcet->liobn, - tcet->window_size, + tcet->nb_table << + SPAPR_TCE_PAGE_SHIFT, &tcet->fd); } if (!tcet->table) { - size_t table_size = (tcet->window_size >> SPAPR_TCE_PAGE_SHIFT) - * sizeof(uint64_t); + size_t table_size = tcet->nb_table * sizeof(uint64_t); tcet->table = g_malloc0(table_size); } - tcet->nb_table = tcet->window_size >> SPAPR_TCE_PAGE_SHIFT; trace_spapr_iommu_new_table(tcet->liobn, tcet, tcet->table, tcet->fd); @@ -145,7 +134,8 @@ static int spapr_tce_table_realize(DeviceState *dev) return 0; } -sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, size_t window_size) +sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, + uint32_t nb_table) { sPAPRTCETable *tcet; @@ -155,13 +145,13 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, size_t wi return NULL; } - if (!window_size) { + if (!nb_table) { return NULL; } tcet = SPAPR_TCE_TABLE(object_new(TYPE_SPAPR_TCE_TABLE)); tcet->liobn = liobn; - tcet->window_size = window_size; + tcet->nb_table = nb_table; object_property_add_child(OBJECT(owner), "tce-table", OBJECT(tcet), NULL); @@ -178,7 +168,7 @@ static void spapr_tce_table_finalize(Object *obj) if (!kvm_enabled() || (kvmppc_remove_spapr_tce(tcet->table, tcet->fd, - tcet->window_size) != 0)) { + tcet->nb_table) != 0)) { g_free(tcet->table); } } @@ -196,8 +186,7 @@ void spapr_tce_set_bypass(sPAPRTCETable *tcet, bool bypass) static void spapr_tce_reset(DeviceState *dev) { sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev); - size_t table_size = (tcet->window_size >> SPAPR_TCE_PAGE_SHIFT) - * sizeof(uint64_t); + size_t table_size = tcet->nb_table * sizeof(uint64_t); tcet->bypass = false; memset(tcet->table, 0, table_size); @@ -208,7 +197,7 @@ static target_ulong put_tce_emu(sPAPRTCETable *tcet, target_ulong ioba, { IOMMUTLBEntry entry; - if (ioba >= tcet->window_size) { + if ((ioba >> SPAPR_TCE_PAGE_SHIFT) >= tcet->nb_table) { hcall_dprintf("spapr_vio_put_tce on out-of-bounds IOBA 0x" TARGET_FMT_lx "\n", ioba); return H_PARAMETER; @@ -324,7 +313,7 @@ static target_ulong h_put_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr, static target_ulong get_tce_emu(sPAPRTCETable *tcet, target_ulong ioba, target_ulong *tce) { - if (ioba >= tcet->window_size) { + if ((ioba >> SPAPR_TCE_PAGE_SHIFT) >= tcet->nb_table) { hcall_dprintf("spapr_iommu_get_tce on out-of-bounds IOBA 0x" TARGET_FMT_lx "\n", ioba); return H_PARAMETER; @@ -395,7 +384,7 @@ int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname, } return spapr_dma_dt(fdt, node_off, propname, - tcet->liobn, 0, tcet->window_size); + tcet->liobn, 0, tcet->nb_table << SPAPR_TCE_PAGE_SHIFT); } static void spapr_tce_table_class_init(ObjectClass *klass, void *data) diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index bc23614956..fea8fb147f 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -655,10 +655,8 @@ static void spapr_phb_finish_realize(sPAPRPHBState *sphb, Error **errp) { sPAPRTCETable *tcet; - sphb->dma_window_start = 0; - sphb->dma_window_size = 0x40000000; tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn, - sphb->dma_window_size); + 0x40000000 >> SPAPR_TCE_PAGE_SHIFT); if (!tcet) { error_setg(errp, "Unable to create TCE table for %s", sphb->dtbusname); @@ -815,7 +813,7 @@ static int spapr_phb_children_dt(Object *child, void *opaque) spapr_dma_dt(p->fdt, p->node_off, "ibm,dma-window", tcet->liobn, 0, - tcet->window_size); + tcet->nb_table << SPAPR_TCE_PAGE_SHIFT); /* Stop after the first window */ return 1; diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index bce8d7f4e0..ffd434870a 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -456,7 +456,9 @@ static int spapr_vio_busdev_init(DeviceState *qdev) if (pc->rtce_window_size) { uint32_t liobn = SPAPR_VIO_BASE_LIOBN | dev->reg; - dev->tcet = spapr_tce_new_table(qdev, liobn, pc->rtce_window_size); + dev->tcet = spapr_tce_new_table(qdev, liobn, + pc->rtce_window_size >> + SPAPR_TCE_PAGE_SHIFT); address_space_init(&dev->as, spapr_tce_get_iommu(dev->tcet), qdev->id); } diff --git a/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h index 9ce83ea1aa..0934518bbd 100644 --- a/include/hw/pci-host/spapr.h +++ b/include/hw/pci-host/spapr.h @@ -60,8 +60,6 @@ struct sPAPRPHBState { MemoryRegion memwindow, iowindow; uint32_t dma_liobn; - uint64_t dma_window_start; - uint64_t dma_window_size; AddressSpace iommu_as; MemoryRegion iommu_root; diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index b8c2ba4a4d..7a0bcc53ff 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -397,7 +397,6 @@ typedef struct sPAPRTCETable sPAPRTCETable; struct sPAPRTCETable { DeviceState parent; uint32_t liobn; - uint32_t window_size; uint32_t nb_table; uint64_t *table; bool bypass; @@ -410,7 +409,7 @@ void spapr_events_init(sPAPREnvironment *spapr); void spapr_events_fdt_skel(void *fdt, uint32_t epow_irq); int spapr_h_cas_compose_response(target_ulong addr, target_ulong size); sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, - size_t window_size); + uint32_t nb_table); MemoryRegion *spapr_tce_get_iommu(sPAPRTCETable *tcet); void spapr_tce_set_bypass(sPAPRTCETable *tcet, bool bypass); int spapr_dma_dt(void *fdt, int node_off, const char *propname, diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index 2b2e35f22f..e6a1625b6b 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -1660,7 +1660,7 @@ void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t window_size, int *pfd) return table; } -int kvmppc_remove_spapr_tce(void *table, int fd, uint32_t window_size) +int kvmppc_remove_spapr_tce(void *table, int fd, uint32_t nb_table) { long len; @@ -1668,7 +1668,7 @@ int kvmppc_remove_spapr_tce(void *table, int fd, uint32_t window_size) return -1; } - len = (window_size / SPAPR_TCE_PAGE_SIZE)*sizeof(uint64_t); + len = nb_table * sizeof(uint64_t); if ((munmap(table, len) < 0) || (close(fd) < 0)) { fprintf(stderr, "KVM: Unexpected error removing TCE table: %s", diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h index 98aa641d8f..5ea209a81b 100644 --- a/target-ppc/kvm_ppc.h +++ b/target-ppc/kvm_ppc.h @@ -149,7 +149,7 @@ static inline void *kvmppc_create_spapr_tce(uint32_t liobn, } static inline int kvmppc_remove_spapr_tce(void *table, int pfd, - uint32_t window_size) + uint32_t nb_table) { return -1; } -- cgit 1.4.1