diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 48ae09283d..e340708371 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -848,9 +848,6 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) /* Set up EPOW events infrastructure */ spapr_events_init(spapr); - /* Set up IOMMU */ - spapr_iommu_init(); - /* Set up VIO bus */ spapr->vio_bus = spapr_vio_bus_init(); diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index 89b33a5478..3d4a1fcfe1 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -36,17 +36,6 @@ enum sPAPRTCEAccess { SPAPR_TCE_RW = 3, }; -struct sPAPRTCETable { - uint32_t liobn; - uint32_t window_size; - sPAPRTCE *table; - bool bypass; - int fd; - MemoryRegion iommu; - QLIST_ENTRY(sPAPRTCETable) list; -}; - - QLIST_HEAD(spapr_tce_tables, sPAPRTCETable) spapr_tce_tables; static sPAPRTCETable *spapr_tce_find_by_liobn(uint32_t liobn) @@ -96,7 +85,7 @@ static IOMMUTLBEntry spapr_tce_translate_iommu(MemoryRegion *iommu, hwaddr addr) return (IOMMUTLBEntry) { .perm = IOMMU_NONE }; } - tce = tcet->table[addr >> SPAPR_TCE_PAGE_SHIFT].tce; + tce = tcet->table[addr >> SPAPR_TCE_PAGE_SHIFT]; #ifdef DEBUG_TCE fprintf(stderr, " -> *paddr=0x%llx, *len=0x%llx\n", @@ -112,10 +101,68 @@ static IOMMUTLBEntry spapr_tce_translate_iommu(MemoryRegion *iommu, hwaddr addr) }; } +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, + .minimum_version_id_old = 1, + .pre_load = spapr_tce_table_pre_load, + .fields = (VMStateField []) { + /* Sanity check */ + VMSTATE_UINT32_EQUAL(liobn, sPAPRTCETable), + VMSTATE_UINT32_EQUAL(window_size, sPAPRTCETable), + + /* IOMMU state */ + VMSTATE_BOOL(bypass, sPAPRTCETable), + VMSTATE_VARRAY_UINT32(table, sPAPRTCETable, nb_table, 0, vmstate_info_uint64, uint64_t), + + VMSTATE_END_OF_LIST() + }, +}; + static MemoryRegionIOMMUOps spapr_iommu_ops = { .translate = spapr_tce_translate_iommu, }; +static int spapr_tce_table_realize(DeviceState *dev) +{ + sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev); + + if (kvm_enabled()) { + tcet->table = kvmppc_create_spapr_tce(tcet->liobn, + tcet->window_size, + &tcet->fd); + } + + if (!tcet->table) { + size_t table_size = (tcet->window_size >> SPAPR_TCE_PAGE_SHIFT) + * sizeof(uint64_t); + tcet->table = g_malloc0(table_size); + } + tcet->nb_table = tcet->window_size >> SPAPR_TCE_PAGE_SHIFT; + +#ifdef DEBUG_TCE + fprintf(stderr, "spapr_iommu: New TCE table @ %p, liobn=0x%x, " + "table @ %p, fd=%d\n", tcet, liobn, tcet->table, tcet->fd); +#endif + + memory_region_init_iommu(&tcet->iommu, OBJECT(dev), &spapr_iommu_ops, + "iommu-spapr", UINT64_MAX); + + QLIST_INSERT_HEAD(&spapr_tce_tables, tcet, list); + + return 0; +} + sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, size_t window_size) { sPAPRTCETable *tcet; @@ -130,37 +177,21 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, size_t wi return NULL; } - tcet = g_malloc0(sizeof(*tcet)); + tcet = SPAPR_TCE_TABLE(object_new(TYPE_SPAPR_TCE_TABLE)); tcet->liobn = liobn; tcet->window_size = window_size; - if (kvm_enabled()) { - tcet->table = kvmppc_create_spapr_tce(liobn, - window_size, - &tcet->fd); - } + object_property_add_child(OBJECT(owner), "tce-table", OBJECT(tcet), NULL); - if (!tcet->table) { - size_t table_size = (window_size >> SPAPR_TCE_PAGE_SHIFT) - * sizeof(sPAPRTCE); - tcet->table = g_malloc0(table_size); - } - -#ifdef DEBUG_TCE - fprintf(stderr, "spapr_iommu: New TCE table @ %p, liobn=0x%x, " - "table @ %p, fd=%d\n", tcet, liobn, tcet->table, tcet->fd); -#endif - - memory_region_init_iommu(&tcet->iommu, OBJECT(owner), &spapr_iommu_ops, - "iommu-spapr", UINT64_MAX); - - QLIST_INSERT_HEAD(&spapr_tce_tables, tcet, list); + qdev_init_nofail(DEVICE(tcet)); return tcet; } -void spapr_tce_free(sPAPRTCETable *tcet) +static void spapr_tce_table_finalize(Object *obj) { + sPAPRTCETable *tcet = SPAPR_TCE_TABLE(obj); + QLIST_REMOVE(tcet, list); if (!kvm_enabled() || @@ -168,8 +199,6 @@ void spapr_tce_free(sPAPRTCETable *tcet) tcet->window_size) != 0)) { g_free(tcet->table); } - - g_free(tcet); } MemoryRegion *spapr_tce_get_iommu(sPAPRTCETable *tcet) @@ -182,10 +211,11 @@ void spapr_tce_set_bypass(sPAPRTCETable *tcet, bool bypass) tcet->bypass = bypass; } -void spapr_tce_reset(sPAPRTCETable *tcet) +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(sPAPRTCE); + * sizeof(uint64_t); tcet->bypass = false; memset(tcet->table, 0, table_size); @@ -194,7 +224,6 @@ void spapr_tce_reset(sPAPRTCETable *tcet) static target_ulong put_tce_emu(sPAPRTCETable *tcet, target_ulong ioba, target_ulong tce) { - sPAPRTCE *tcep; IOMMUTLBEntry entry; if (ioba >= tcet->window_size) { @@ -203,8 +232,7 @@ static target_ulong put_tce_emu(sPAPRTCETable *tcet, target_ulong ioba, return H_PARAMETER; } - tcep = tcet->table + (ioba >> SPAPR_TCE_PAGE_SHIFT); - tcep->tce = tce; + tcet->table[ioba >> SPAPR_TCE_PAGE_SHIFT] = tce; entry.target_as = &address_space_memory, entry.iova = ioba & ~SPAPR_TCE_PAGE_MASK; @@ -238,14 +266,6 @@ static target_ulong h_put_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr, return H_PARAMETER; } -void spapr_iommu_init(void) -{ - QLIST_INIT(&spapr_tce_tables); - - /* hcall-tce */ - spapr_register_hypercall(H_PUT_TCE, h_put_tce); -} - int spapr_dma_dt(void *fdt, int node_off, const char *propname, uint32_t liobn, uint64_t window, uint32_t size) { @@ -286,3 +306,31 @@ 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); } + +static void spapr_tce_table_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + dc->vmsd = &vmstate_spapr_tce_table; + dc->init = spapr_tce_table_realize; + dc->reset = spapr_tce_reset; + + QLIST_INIT(&spapr_tce_tables); + + /* hcall-tce */ + spapr_register_hypercall(H_PUT_TCE, h_put_tce); +} + +static TypeInfo spapr_tce_table_info = { + .name = TYPE_SPAPR_TCE_TABLE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(sPAPRTCETable), + .class_init = spapr_tce_table_class_init, + .instance_finalize = spapr_tce_table_finalize, +}; + +static void register_types(void) +{ + type_register_static(&spapr_tce_table_info); +} + +type_init(register_types); diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index c880a757c8..9dc6ca31db 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -645,7 +645,7 @@ static void spapr_phb_reset(DeviceState *qdev) sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(s); /* Reset the IOMMU state */ - spapr_tce_reset(sphb->tcet); + device_reset(DEVICE(sphb->tcet)); } static Property spapr_phb_properties[] = { diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index 75ce19f0e3..c3f85bf078 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -316,7 +316,7 @@ int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq) static void spapr_vio_quiesce_one(VIOsPAPRDevice *dev) { if (dev->tcet) { - spapr_tce_reset(dev->tcet); + device_reset(DEVICE(dev->tcet)); } free_crq(dev); } diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index de95480734..82ad7c0068 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -334,10 +334,6 @@ int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr, #define SPAPR_TCE_PAGE_SIZE (1ULL << SPAPR_TCE_PAGE_SHIFT) #define SPAPR_TCE_PAGE_MASK (SPAPR_TCE_PAGE_SIZE - 1) -typedef struct sPAPRTCE { - uint64_t tce; -} sPAPRTCE; - #define SPAPR_VIO_BASE_LIOBN 0x00000000 #define SPAPR_PCI_BASE_LIOBN 0x80000000 @@ -345,14 +341,27 @@ typedef struct sPAPRTCE { typedef struct sPAPRTCETable sPAPRTCETable; -void spapr_iommu_init(void); +#define TYPE_SPAPR_TCE_TABLE "spapr-tce-table" +#define SPAPR_TCE_TABLE(obj) \ + OBJECT_CHECK(sPAPRTCETable, (obj), TYPE_SPAPR_TCE_TABLE) + +struct sPAPRTCETable { + DeviceState parent; + uint32_t liobn; + uint32_t window_size; + uint32_t nb_table; + uint64_t *table; + bool bypass; + int fd; + MemoryRegion iommu; + QLIST_ENTRY(sPAPRTCETable) list; +}; + void spapr_events_init(sPAPREnvironment *spapr); void spapr_events_fdt_skel(void *fdt, uint32_t epow_irq); sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, size_t window_size); MemoryRegion *spapr_tce_get_iommu(sPAPRTCETable *tcet); -void spapr_tce_free(sPAPRTCETable *tcet); -void spapr_tce_reset(sPAPRTCETable *tcet); void spapr_tce_set_bypass(sPAPRTCETable *tcet, bool bypass); int spapr_dma_dt(void *fdt, int node_off, const char *propname, uint32_t liobn, uint64_t window, uint32_t size); diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index b0099e122f..9f2e4f0457 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -1626,7 +1626,7 @@ void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t window_size, int *pfd) return NULL; } - len = (window_size / SPAPR_TCE_PAGE_SIZE) * sizeof(sPAPRTCE); + len = (window_size / SPAPR_TCE_PAGE_SIZE) * sizeof(uint64_t); /* FIXME: round this up to page size */ table = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); @@ -1649,7 +1649,7 @@ int kvmppc_remove_spapr_tce(void *table, int fd, uint32_t window_size) return -1; } - len = (window_size / SPAPR_TCE_PAGE_SIZE)*sizeof(sPAPRTCE); + len = (window_size / SPAPR_TCE_PAGE_SIZE)*sizeof(uint64_t); if ((munmap(table, len) < 0) || (close(fd) < 0)) { fprintf(stderr, "KVM: Unexpected error removing TCE table: %s",