Patchwork [05/11] spapr-tce: make sPAPRTCETable a proper device

login
register
mail settings
Submitter Anthony Liguori
Date July 15, 2013, 3:11 p.m.
Message ID <1373901083-18730-6-git-send-email-aliguori@us.ibm.com>
Download mbox | patch
Permalink /patch/259079/
State New
Headers show

Comments

Anthony Liguori - July 15, 2013, 3:11 p.m.
Model TCE tables as a device that's hooked up as a child object to
the owner.  Besides the code cleanup, we get a few nice benefits:

1) free actually works now (it was dead code before)

2) the TCE information is visible in the device tree

3) we can expose table information as properties such that if we
   change the window_size, we can use globals to keep migration
   working.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
[dwg: pseries: savevm support for PAPR TCE tables]
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
---
 hw/ppc/spapr.c         |   3 -
 hw/ppc/spapr_iommu.c   | 145 ++++++++++++++++++++++++++++++++-----------------
 hw/ppc/spapr_pci.c     |   2 +-
 hw/ppc/spapr_vio.c     |   2 +-
 include/hw/ppc/spapr.h |  23 +++++---
 5 files changed, 114 insertions(+), 61 deletions(-)
Alexey Kardashevskiy - July 17, 2013, 7:01 a.m.
On 07/16/2013 01:11 AM, Anthony Liguori wrote:
> Model TCE tables as a device that's hooked up as a child object to
> the owner.  Besides the code cleanup, we get a few nice benefits:
> 
> 1) free actually works now (it was dead code before)
> 
> 2) the TCE information is visible in the device tree
> 
> 3) we can expose table information as properties such that if we
>    change the window_size, we can use globals to keep migration
>    working.
> 
> Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
> [dwg: pseries: savevm support for PAPR TCE tables]
> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
> ---
>  hw/ppc/spapr.c         |   3 -
>  hw/ppc/spapr_iommu.c   | 145 ++++++++++++++++++++++++++++++++-----------------
>  hw/ppc/spapr_pci.c     |   2 +-
>  hw/ppc/spapr_vio.c     |   2 +-
>  include/hw/ppc/spapr.h |  23 +++++---
>  5 files changed, 114 insertions(+), 61 deletions(-)
> 
> diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
> index 48ae092..e340708 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 89b33a5..709cc34 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,37 +101,51 @@ 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,
>  };
>  
> -sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, size_t window_size)
> +static int spapr_tce_table_realize(DeviceState *dev)
>  {
> -    sPAPRTCETable *tcet;
> -
> -    if (spapr_tce_find_by_liobn(liobn)) {
> -        fprintf(stderr, "Attempted to create TCE table with duplicate"
> -                " LIOBN 0x%x\n", liobn);
> -        return NULL;
> -    }
> -
> -    if (!window_size) {
> -        return NULL;
> -    }
> -
> -    tcet = g_malloc0(sizeof(*tcet));
> -    tcet->liobn = liobn;
> -    tcet->window_size = window_size;
> +    sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev);
>  
>      if (kvm_enabled()) {
> -        tcet->table = kvmppc_create_spapr_tce(liobn,
> -                                              window_size,
> +        tcet->table = kvmppc_create_spapr_tce(tcet->liobn,
> +                                              tcet->window_size,
>                                                &tcet->fd);
>      }
>  
>      if (!tcet->table) {
> -        size_t table_size = (window_size >> SPAPR_TCE_PAGE_SHIFT)
> -            * sizeof(sPAPRTCE);
> +        size_t table_size = (tcet->window_size >> SPAPR_TCE_PAGE_SHIFT)
> +            * sizeof(uint64_t);
>          tcet->table = g_malloc0(table_size);
>      }
>  
> @@ -151,16 +154,43 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, size_t wi
>              "table @ %p, fd=%d\n", tcet, liobn, tcet->table, tcet->fd);
>  #endif
>  
> -    memory_region_init_iommu(&tcet->iommu, OBJECT(owner), &spapr_iommu_ops,
> +    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;
> +
> +    if (spapr_tce_find_by_liobn(liobn)) {
> +        fprintf(stderr, "Attempted to create TCE table with duplicate"
> +                " LIOBN 0x%x\n", liobn);
> +        return NULL;
> +    }
> +
> +    if (!window_size) {
> +        return NULL;
> +    }
> +
> +    tcet = SPAPR_TCE_TABLE(object_new(TYPE_SPAPR_TCE_TABLE));
> +    tcet->liobn = liobn;
> +    tcet->window_size = window_size;



I am trying to understand the QOM. How do you understand what
initialization should go to .realize and what should stay in
spapr_tce_new_table?

In this particular case having the .realize implementation does not make
much sense to me. If you made .liobn and .window_size members of
sPAPRTCETable then it would be ok but you did not. What do I miss? Thanks.
Anthony Liguori - July 17, 2013, 1:05 p.m.
Alexey Kardashevskiy <aik@ozlabs.ru> writes:

> On 07/16/2013 01:11 AM, Anthony Liguori wrote:
>> Model TCE tables as a device that's hooked up as a child object to
>> the owner.  Besides the code cleanup, we get a few nice benefits:
>> 
>> 1) free actually works now (it was dead code before)
>> 
>> 2) the TCE information is visible in the device tree
>> 
>> 3) we can expose table information as properties such that if we
>>    change the window_size, we can use globals to keep migration
>>    working.
>> 
>> Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
>> [dwg: pseries: savevm support for PAPR TCE tables]
>> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
>> ---
>>  hw/ppc/spapr.c         |   3 -
>>  hw/ppc/spapr_iommu.c   | 145 ++++++++++++++++++++++++++++++++-----------------
>>  hw/ppc/spapr_pci.c     |   2 +-
>>  hw/ppc/spapr_vio.c     |   2 +-
>>  include/hw/ppc/spapr.h |  23 +++++---
>>  5 files changed, 114 insertions(+), 61 deletions(-)
>> 
>> diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
>> index 48ae092..e340708 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 89b33a5..709cc34 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,37 +101,51 @@ 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,
>>  };
>>  
>> -sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, size_t window_size)
>> +static int spapr_tce_table_realize(DeviceState *dev)
>>  {
>> -    sPAPRTCETable *tcet;
>> -
>> -    if (spapr_tce_find_by_liobn(liobn)) {
>> -        fprintf(stderr, "Attempted to create TCE table with duplicate"
>> -                " LIOBN 0x%x\n", liobn);
>> -        return NULL;
>> -    }
>> -
>> -    if (!window_size) {
>> -        return NULL;
>> -    }
>> -
>> -    tcet = g_malloc0(sizeof(*tcet));
>> -    tcet->liobn = liobn;
>> -    tcet->window_size = window_size;
>> +    sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev);
>>  
>>      if (kvm_enabled()) {
>> -        tcet->table = kvmppc_create_spapr_tce(liobn,
>> -                                              window_size,
>> +        tcet->table = kvmppc_create_spapr_tce(tcet->liobn,
>> +                                              tcet->window_size,
>>                                                &tcet->fd);
>>      }
>>  
>>      if (!tcet->table) {
>> -        size_t table_size = (window_size >> SPAPR_TCE_PAGE_SHIFT)
>> -            * sizeof(sPAPRTCE);
>> +        size_t table_size = (tcet->window_size >> SPAPR_TCE_PAGE_SHIFT)
>> +            * sizeof(uint64_t);
>>          tcet->table = g_malloc0(table_size);
>>      }
>>  
>> @@ -151,16 +154,43 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, size_t wi
>>              "table @ %p, fd=%d\n", tcet, liobn, tcet->table, tcet->fd);
>>  #endif
>>  
>> -    memory_region_init_iommu(&tcet->iommu, OBJECT(owner), &spapr_iommu_ops,
>> +    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;
>> +
>> +    if (spapr_tce_find_by_liobn(liobn)) {
>> +        fprintf(stderr, "Attempted to create TCE table with duplicate"
>> +                " LIOBN 0x%x\n", liobn);
>> +        return NULL;
>> +    }
>> +
>> +    if (!window_size) {
>> +        return NULL;
>> +    }
>> +
>> +    tcet = SPAPR_TCE_TABLE(object_new(TYPE_SPAPR_TCE_TABLE));
>> +    tcet->liobn = liobn;
>> +    tcet->window_size = window_size;
>
>
>
> I am trying to understand the QOM. How do you understand what
> initialization should go to .realize and what should stay in
> spapr_tce_new_table?

Really nothing should be in spapr_tce_new_table().   It should strictly
be an helper function to create a device and set properties (via
qdev_prop_set..).  We really should make liobn and window_size proper
properties.

Regards,

Anthony Liguori

>
> In this particular case having the .realize implementation does not make
> much sense to me. If you made .liobn and .window_size members of
> sPAPRTCETable then it would be ok but you did not. What do I miss?
> Thanks.



>
>
>
> -- 
> Alexey

Patch

diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 48ae092..e340708 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 89b33a5..709cc34 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,37 +101,51 @@  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,
 };
 
-sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, size_t window_size)
+static int spapr_tce_table_realize(DeviceState *dev)
 {
-    sPAPRTCETable *tcet;
-
-    if (spapr_tce_find_by_liobn(liobn)) {
-        fprintf(stderr, "Attempted to create TCE table with duplicate"
-                " LIOBN 0x%x\n", liobn);
-        return NULL;
-    }
-
-    if (!window_size) {
-        return NULL;
-    }
-
-    tcet = g_malloc0(sizeof(*tcet));
-    tcet->liobn = liobn;
-    tcet->window_size = window_size;
+    sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev);
 
     if (kvm_enabled()) {
-        tcet->table = kvmppc_create_spapr_tce(liobn,
-                                              window_size,
+        tcet->table = kvmppc_create_spapr_tce(tcet->liobn,
+                                              tcet->window_size,
                                               &tcet->fd);
     }
 
     if (!tcet->table) {
-        size_t table_size = (window_size >> SPAPR_TCE_PAGE_SHIFT)
-            * sizeof(sPAPRTCE);
+        size_t table_size = (tcet->window_size >> SPAPR_TCE_PAGE_SHIFT)
+            * sizeof(uint64_t);
         tcet->table = g_malloc0(table_size);
     }
 
@@ -151,16 +154,43 @@  sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, size_t wi
             "table @ %p, fd=%d\n", tcet, liobn, tcet->table, tcet->fd);
 #endif
 
-    memory_region_init_iommu(&tcet->iommu, OBJECT(owner), &spapr_iommu_ops,
+    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;
+
+    if (spapr_tce_find_by_liobn(liobn)) {
+        fprintf(stderr, "Attempted to create TCE table with duplicate"
+                " LIOBN 0x%x\n", liobn);
+        return NULL;
+    }
+
+    if (!window_size) {
+        return NULL;
+    }
+
+    tcet = SPAPR_TCE_TABLE(object_new(TYPE_SPAPR_TCE_TABLE));
+    tcet->liobn = liobn;
+    tcet->window_size = window_size;
+
+    object_property_add_child(OBJECT(owner), "tce-table", OBJECT(tcet), NULL);
+
+    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 +198,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 +210,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 +223,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 +231,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 +265,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 +305,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 318bc9d..dd9e74e 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -682,7 +682,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 75ce19f..c3f85bf 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 de95480..82ad7c0 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);