qdev: move qdev->info to class

Right now, DeviceInfo acts as the class for qdev.  In order to switch to a
proper ObjectClass derivative, we need to ween all of the callers off of
interacting directly with the info pointer.

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c
index bdc55a1..4d0b390 100644
--- a/hw/acpi_piix4.c
+++ b/hw/acpi_piix4.c
@@ -280,7 +280,7 @@
     s->pci0_hotplug_enable = ~0;
 
     QTAILQ_FOREACH_SAFE(qdev, &bus->children, sibling, next) {
-        PCIDeviceInfo *info = container_of(qdev->info, PCIDeviceInfo, qdev);
+        PCIDeviceInfo *info = container_of(qdev_get_info(qdev), PCIDeviceInfo, qdev);
         PCIDevice *pdev = DO_UPCAST(PCIDevice, qdev, qdev);
         int slot = PCI_SLOT(pdev->devfn);
 
@@ -491,7 +491,7 @@
 
     QTAILQ_FOREACH_SAFE(qdev, &bus->children, sibling, next) {
         dev = DO_UPCAST(PCIDevice, qdev, qdev);
-        info = container_of(qdev->info, PCIDeviceInfo, qdev);
+        info = container_of(qdev_get_info(qdev), PCIDeviceInfo, qdev);
         if (PCI_SLOT(dev->devfn) == slot && !info->no_hotplug) {
             qdev_free(qdev);
         }
diff --git a/hw/apic_common.c b/hw/apic_common.c
index e05369c..ac06147 100644
--- a/hw/apic_common.c
+++ b/hw/apic_common.c
@@ -31,7 +31,7 @@
     trace_cpu_set_apic_base(val);
 
     if (s) {
-        info = DO_UPCAST(APICCommonInfo, busdev.qdev, s->busdev.qdev.info);
+        info = DO_UPCAST(APICCommonInfo, busdev.qdev, qdev_get_info(&s->busdev.qdev));
         info->set_base(s, val);
     }
 }
@@ -51,7 +51,7 @@
     APICCommonInfo *info;
 
     if (s) {
-        info = DO_UPCAST(APICCommonInfo, busdev.qdev, s->busdev.qdev.info);
+        info = DO_UPCAST(APICCommonInfo, busdev.qdev, qdev_get_info(&s->busdev.qdev));
         info->set_tpr(s, val);
     }
 }
@@ -89,7 +89,7 @@
     APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
     APICCommonInfo *info;
 
-    info = DO_UPCAST(APICCommonInfo, busdev.qdev, s->busdev.qdev.info);
+    info = DO_UPCAST(APICCommonInfo, busdev.qdev, qdev_get_info(&s->busdev.qdev));
     info->external_nmi(s);
 }
 
@@ -232,7 +232,7 @@
     }
     s->idx = apic_no++;
 
-    info = DO_UPCAST(APICCommonInfo, busdev.qdev, s->busdev.qdev.info);
+    info = DO_UPCAST(APICCommonInfo, busdev.qdev, qdev_get_info(&s->busdev.qdev));
     info->init(s);
 
     sysbus_init_mmio(&s->busdev, &s->io_memory);
@@ -243,7 +243,7 @@
 {
     APICCommonState *s = opaque;
     APICCommonInfo *info =
-        DO_UPCAST(APICCommonInfo, busdev.qdev, s->busdev.qdev.info);
+        DO_UPCAST(APICCommonInfo, busdev.qdev, qdev_get_info(&s->busdev.qdev));
 
     if (info->post_load) {
         info->post_load(s);
diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c
index 7ce35ec..5b22cc3 100644
--- a/hw/cirrus_vga.c
+++ b/hw/cirrus_vga.c
@@ -2921,7 +2921,7 @@
 {
      PCICirrusVGAState *d = DO_UPCAST(PCICirrusVGAState, dev, dev);
      CirrusVGAState *s = &d->cirrus_vga;
-     PCIDeviceInfo *info = DO_UPCAST(PCIDeviceInfo, qdev, dev->qdev.info);
+     PCIDeviceInfo *info = DO_UPCAST(PCIDeviceInfo, qdev, qdev_get_info(&dev->qdev));
      int16_t device_id = info->device_id;
 
      /* setup VGA */
diff --git a/hw/e1000.c b/hw/e1000.c
index 76e736f..e4362ea 100644
--- a/hw/e1000.c
+++ b/hw/e1000.c
@@ -1182,7 +1182,7 @@
     d->eeprom_data[EEPROM_CHECKSUM_REG] = checksum;
 
     d->nic = qemu_new_nic(&net_e1000_info, &d->conf,
-                          d->dev.qdev.info->name, d->dev.qdev.id, d);
+                          qdev_get_info(&d->dev.qdev)->name, d->dev.qdev.id, d);
 
     qemu_format_nic_info_str(&d->nic->nc, macaddr);
 
diff --git a/hw/eepro100.c b/hw/eepro100.c
index 6a162f6..81a32b8 100644
--- a/hw/eepro100.c
+++ b/hw/eepro100.c
@@ -1848,7 +1848,7 @@
 {
     EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev);
     E100PCIDeviceInfo *e100_device = DO_UPCAST(E100PCIDeviceInfo, pci.qdev,
-                                               pci_dev->qdev.info);
+                                               qdev_get_info(&pci_dev->qdev));
 
     TRACE(OTHER, logout("\n"));
 
@@ -1878,7 +1878,7 @@
     nic_reset(s);
 
     s->nic = qemu_new_nic(&net_eepro100_info, &s->conf,
-                          pci_dev->qdev.info->name, pci_dev->qdev.id, s);
+                          qdev_get_info(&pci_dev->qdev)->name, pci_dev->qdev.id, s);
 
     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
     TRACE(OTHER, logout("%s\n", s->nic->nc.info_str));
diff --git a/hw/hda-audio.c b/hw/hda-audio.c
index 9b089e6..0bc0a25 100644
--- a/hw/hda-audio.c
+++ b/hw/hda-audio.c
@@ -777,7 +777,7 @@
     uint32_t i, type;
 
     a->desc = desc;
-    a->name = a->hda.qdev.info->name;
+    a->name = qdev_get_info(&a->hda.qdev)->name;
     dprint(a, 1, "%s: cad %d\n", __FUNCTION__, a->hda.cad);
 
     AUD_register_card("hda", &a->card);
diff --git a/hw/i8259_common.c b/hw/i8259_common.c
index e515876..7536897 100644
--- a/hw/i8259_common.c
+++ b/hw/i8259_common.c
@@ -49,7 +49,7 @@
 {
     PICCommonState *s = opaque;
     PICCommonInfo *info =
-        DO_UPCAST(PICCommonInfo, isadev.qdev, s->dev.qdev.info);
+        DO_UPCAST(PICCommonInfo, isadev.qdev, qdev_get_info(&s->dev.qdev));
 
     if (info->pre_save) {
         info->pre_save(s);
@@ -60,7 +60,7 @@
 {
     PICCommonState *s = opaque;
     PICCommonInfo *info =
-        DO_UPCAST(PICCommonInfo, isadev.qdev, s->dev.qdev.info);
+        DO_UPCAST(PICCommonInfo, isadev.qdev, qdev_get_info(&s->dev.qdev));
 
     if (info->post_load) {
         info->post_load(s);
@@ -72,7 +72,7 @@
 {
     PICCommonState *s = DO_UPCAST(PICCommonState, dev, dev);
     PICCommonInfo *info =
-        DO_UPCAST(PICCommonInfo, isadev.qdev, dev->qdev.info);
+        DO_UPCAST(PICCommonInfo, isadev.qdev, qdev_get_info(&dev->qdev));
 
     info->init(s);
 
diff --git a/hw/ide/piix.c b/hw/ide/piix.c
index 3473345..c0e3450 100644
--- a/hw/ide/piix.c
+++ b/hw/ide/piix.c
@@ -195,7 +195,6 @@
     PCIDevice *dev;
 
     dev = pci_create_simple(bus, devfn, "piix3-ide-xen");
-    dev->qdev.info->unplug = pci_piix3_xen_ide_unplug;
     pci_ide_create_devs(dev, hd_table);
     return dev;
 }
@@ -253,6 +252,7 @@
         .qdev.name    = "piix3-ide-xen",
         .qdev.size    = sizeof(PCIIDEState),
         .qdev.no_user = 1,
+        .qdev.unplug  = pci_piix3_xen_ide_unplug,
         .init         = pci_piix_ide_initfn,
         .vendor_id    = PCI_VENDOR_ID_INTEL,
         .device_id    = PCI_DEVICE_ID_INTEL_82371SB_1,
diff --git a/hw/intel-hda.c b/hw/intel-hda.c
index 10769e0..12dcc84 100644
--- a/hw/intel-hda.c
+++ b/hw/intel-hda.c
@@ -1116,8 +1116,8 @@
     /* reset codecs */
     QTAILQ_FOREACH(qdev, &d->codecs.qbus.children, sibling) {
         cdev = DO_UPCAST(HDACodecDevice, qdev, qdev);
-        if (qdev->info->reset) {
-            qdev->info->reset(qdev);
+        if (qdev_get_info(qdev)->reset) {
+            qdev_get_info(qdev)->reset(qdev);
         }
         d->state_sts |= (1 << cdev->cad);
     }
@@ -1129,7 +1129,7 @@
     IntelHDAState *d = DO_UPCAST(IntelHDAState, pci, pci);
     uint8_t *conf = d->pci.config;
 
-    d->name = d->pci.qdev.info->name;
+    d->name = qdev_get_info(&d->pci.qdev)->name;
 
     pci_config_set_interrupt_pin(conf, 1);
 
diff --git a/hw/ioapic_common.c b/hw/ioapic_common.c
index 3aa9a1c..4a7624c 100644
--- a/hw/ioapic_common.c
+++ b/hw/ioapic_common.c
@@ -40,7 +40,7 @@
 {
     IOAPICCommonState *s = opaque;
     IOAPICCommonInfo *info =
-        DO_UPCAST(IOAPICCommonInfo, busdev.qdev, s->busdev.qdev.info);
+        DO_UPCAST(IOAPICCommonInfo, busdev.qdev, qdev_get_info(&s->busdev.qdev));
 
     if (info->pre_save) {
         info->pre_save(s);
@@ -51,7 +51,7 @@
 {
     IOAPICCommonState *s = opaque;
     IOAPICCommonInfo *info =
-        DO_UPCAST(IOAPICCommonInfo, busdev.qdev, s->busdev.qdev.info);
+        DO_UPCAST(IOAPICCommonInfo, busdev.qdev, qdev_get_info(&s->busdev.qdev));
 
     if (info->post_load) {
         info->post_load(s);
@@ -69,7 +69,7 @@
         return -1;
     }
 
-    info = DO_UPCAST(IOAPICCommonInfo, busdev.qdev, s->busdev.qdev.info);
+    info = DO_UPCAST(IOAPICCommonInfo, busdev.qdev, qdev_get_info(&s->busdev.qdev));
     info->init(s, ioapic_no);
 
     sysbus_init_mmio(&s->busdev, &s->io_memory);
diff --git a/hw/lan9118.c b/hw/lan9118.c
index 9b199d0..d1b244e 100644
--- a/hw/lan9118.c
+++ b/hw/lan9118.c
@@ -1221,7 +1221,7 @@
     qemu_macaddr_default_if_unset(&s->conf.macaddr);
 
     s->nic = qemu_new_nic(&net_lan9118_info, &s->conf,
-                          dev->qdev.info->name, dev->qdev.id, s);
+                          qdev_get_info(&dev->qdev)->name, dev->qdev.id, s);
     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
     s->eeprom[0] = 0xa5;
     for (i = 0; i < 6; i++) {
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index 0d3a101..9f475e0 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -1681,7 +1681,7 @@
                 DeviceState *dev;
 
                 QTAILQ_FOREACH(dev, &s->bus.qbus.children, sibling) {
-                    dev->info->reset(dev);
+                    qdev_get_info(dev)->reset(dev);
                 }
                 s->sstat0 |= LSI_SSTAT0_RST;
                 lsi_script_scsi_interrupt(s, LSI_SIST0_RST, 0);
diff --git a/hw/mipsnet.c b/hw/mipsnet.c
index b1234b8..1486833 100644
--- a/hw/mipsnet.c
+++ b/hw/mipsnet.c
@@ -240,7 +240,7 @@
     sysbus_init_irq(dev, &s->irq);
 
     s->nic = qemu_new_nic(&net_mipsnet_info, &s->conf,
-                          dev->qdev.info->name, dev->qdev.id, s);
+                          qdev_get_info(&dev->qdev)->name, dev->qdev.id, s);
     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
 
     return 0;
diff --git a/hw/ne2000-isa.c b/hw/ne2000-isa.c
index 11ffee7..60dc333 100644
--- a/hw/ne2000-isa.c
+++ b/hw/ne2000-isa.c
@@ -76,7 +76,7 @@
     ne2000_reset(s);
 
     s->nic = qemu_new_nic(&net_ne2000_isa_info, &s->c,
-                          dev->qdev.info->name, dev->qdev.id, s);
+                          qdev_get_info(&dev->qdev)->name, dev->qdev.id, s);
     qemu_format_nic_info_str(&s->nic->nc, s->c.macaddr.a);
 
     return 0;
diff --git a/hw/ne2000.c b/hw/ne2000.c
index 62e082f..d016b8e 100644
--- a/hw/ne2000.c
+++ b/hw/ne2000.c
@@ -760,7 +760,7 @@
     ne2000_reset(s);
 
     s->nic = qemu_new_nic(&net_ne2000_info, &s->c,
-                          pci_dev->qdev.info->name, pci_dev->qdev.id, s);
+                          qdev_get_info(&pci_dev->qdev)->name, pci_dev->qdev.id, s);
     qemu_format_nic_info_str(&s->nic->nc, s->c.macaddr.a);
 
     if (!pci_dev->qdev.hotplugged) {
diff --git a/hw/opencores_eth.c b/hw/opencores_eth.c
index f7cc1b4..d4d3529 100644
--- a/hw/opencores_eth.c
+++ b/hw/opencores_eth.c
@@ -717,7 +717,7 @@
     sysbus_init_irq(dev, &s->irq);
 
     s->nic = qemu_new_nic(&net_open_eth_info, &s->conf,
-                          s->dev.qdev.info->name, s->dev.qdev.id, s);
+                          qdev_get_info(&s->dev.qdev)->name, s->dev.qdev.id, s);
     return 0;
 }
 
diff --git a/hw/pci.c b/hw/pci.c
index ab3b53d..5345079 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -841,7 +841,7 @@
 static int pci_unregister_device(DeviceState *dev)
 {
     PCIDevice *pci_dev = DO_UPCAST(PCIDevice, qdev, dev);
-    PCIDeviceInfo *info = DO_UPCAST(PCIDeviceInfo, qdev, dev->info);
+    PCIDeviceInfo *info = DO_UPCAST(PCIDeviceInfo, qdev, qdev_get_info(dev));
     int ret = 0;
 
     if (info->exit)
@@ -1531,7 +1531,7 @@
 static int pci_unplug_device(DeviceState *qdev)
 {
     PCIDevice *dev = DO_UPCAST(PCIDevice, qdev, qdev);
-    PCIDeviceInfo *info = container_of(qdev->info, PCIDeviceInfo, qdev);
+    PCIDeviceInfo *info = container_of(qdev_get_info(qdev), PCIDeviceInfo, qdev);
 
     if (info->no_hotplug) {
         qerror_report(QERR_DEVICE_NO_HOTPLUG, info->qdev.name);
@@ -1544,7 +1544,9 @@
 void pci_qdev_register(PCIDeviceInfo *info)
 {
     info->qdev.init = pci_qdev_init;
-    info->qdev.unplug = pci_unplug_device;
+    if (!info->qdev.unplug) {
+        info->qdev.unplug = pci_unplug_device;
+    }
     info->qdev.exit = pci_unregister_device;
     info->qdev.bus_info = &pci_bus_info;
     qdev_register(&info->qdev);
@@ -1737,10 +1739,10 @@
         size = 1 << qemu_fls(size);
     }
 
-    if (pdev->qdev.info->vmsd)
-        snprintf(name, sizeof(name), "%s.rom", pdev->qdev.info->vmsd->name);
+    if (qdev_get_info(&pdev->qdev)->vmsd)
+        snprintf(name, sizeof(name), "%s.rom", qdev_get_info(&pdev->qdev)->vmsd->name);
     else
-        snprintf(name, sizeof(name), "%s.rom", pdev->qdev.info->name);
+        snprintf(name, sizeof(name), "%s.rom", qdev_get_info(&pdev->qdev)->name);
     pdev->has_rom = true;
     memory_region_init_ram(&pdev->rom, name, size);
     vmstate_register_ram(&pdev->rom, &pdev->qdev);
@@ -1981,7 +1983,7 @@
     }
 
     /* roughly check if given qdev is pci device */
-    if (qdev->info->init == &pci_qdev_init &&
+    if (qdev_get_info(qdev)->init == &pci_qdev_init &&
         qdev->parent_bus->info == &pci_bus_info) {
         *pdev = DO_UPCAST(PCIDevice, qdev, qdev);
         return 0;
diff --git a/hw/pcnet.c b/hw/pcnet.c
index 6aa48e0..54f14ee 100644
--- a/hw/pcnet.c
+++ b/hw/pcnet.c
@@ -1718,7 +1718,7 @@
     s->poll_timer = qemu_new_timer_ns(vm_clock, pcnet_poll_timer, s);
 
     qemu_macaddr_default_if_unset(&s->conf.macaddr);
-    s->nic = qemu_new_nic(info, &s->conf, dev->info->name, dev->id, s);
+    s->nic = qemu_new_nic(info, &s->conf, qdev_get_info(dev)->name, dev->id, s);
     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
 
     add_boot_device_path(s->conf.bootindex, dev, "/ethernet-phy@0");
diff --git a/hw/qdev-properties.c b/hw/qdev-properties.c
index ea3b2df..d71dfda 100644
--- a/hw/qdev-properties.c
+++ b/hw/qdev-properties.c
@@ -966,7 +966,7 @@
     Property *prop;
 
     /* device properties */
-    prop = qdev_prop_walk(dev->info->props, name);
+    prop = qdev_prop_walk(qdev_get_info(dev)->props, name);
     if (prop)
         return prop;
 
@@ -989,16 +989,16 @@
     switch (ret) {
     case -EEXIST:
         error_set(errp, QERR_PROPERTY_VALUE_IN_USE,
-                  dev->info->name, prop->name, value);
+                  qdev_get_info(dev)->name, prop->name, value);
         break;
     default:
     case -EINVAL:
         error_set(errp, QERR_PROPERTY_VALUE_BAD,
-                  dev->info->name, prop->name, value);
+                  qdev_get_info(dev)->name, prop->name, value);
         break;
     case -ENOENT:
         error_set(errp, QERR_PROPERTY_VALUE_NOT_FOUND,
-                  dev->info->name, prop->name, value);
+                  qdev_get_info(dev)->name, prop->name, value);
         break;
     case 0:
         break;
@@ -1018,7 +1018,7 @@
      * removed along with it.
      */
     if (!prop || !prop->info->parse) {
-        qerror_report(QERR_PROPERTY_NOT_FOUND, dev->info->name, name);
+        qerror_report(QERR_PROPERTY_NOT_FOUND, qdev_get_info(dev)->name, name);
         return -1;
     }
     ret = prop->info->parse(dev, prop, value);
@@ -1039,12 +1039,12 @@
     prop = qdev_prop_find(dev, name);
     if (!prop) {
         fprintf(stderr, "%s: property \"%s.%s\" not found\n",
-                __FUNCTION__, dev->info->name, name);
+                __FUNCTION__, qdev_get_info(dev)->name, name);
         abort();
     }
     if (prop->info->type != type) {
         fprintf(stderr, "%s: property \"%s.%s\" type mismatch\n",
-                __FUNCTION__, dev->info->name, name);
+                __FUNCTION__, qdev_get_info(dev)->name, name);
         abort();
     }
     qdev_prop_cpy(dev, prop, src);
@@ -1093,7 +1093,7 @@
     if (res < 0) {
         error_report("Can't attach drive %s to %s.%s: %s",
                      bdrv_get_device_name(value),
-                     dev->id ? dev->id : dev->info->name,
+                     dev->id ? dev->id : qdev_get_info(dev)->name,
                      name, strerror(-res));
         return -1;
     }
@@ -1165,8 +1165,8 @@
     GlobalProperty *prop;
 
     QTAILQ_FOREACH(prop, &global_props, next) {
-        if (strcmp(dev->info->name, prop->driver) != 0 &&
-            strcmp(dev->info->bus_info->name, prop->driver) != 0) {
+        if (strcmp(qdev_get_info(dev)->name, prop->driver) != 0 &&
+            strcmp(qdev_get_info(dev)->bus_info->name, prop->driver) != 0) {
             continue;
         }
         if (qdev_prop_parse(dev, prop->property, prop->value) != 0) {
diff --git a/hw/qdev.c b/hw/qdev.c
index 3bc9166..49d26a2 100644
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -45,6 +45,17 @@
 static BusState *qbus_find(const char *path);
 
 /* Register a new device type.  */
+static void qdev_subclass_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    dc->info = data;
+}
+
+DeviceInfo *qdev_get_info(DeviceState *dev)
+{
+    return DEVICE_GET_CLASS(dev)->info;
+}
+
 void qdev_register(DeviceInfo *info)
 {
     TypeInfo type_info = {};
@@ -55,6 +66,8 @@
     type_info.name = info->name;
     type_info.parent = TYPE_DEVICE;
     type_info.instance_size = info->size;
+    type_info.class_init = qdev_subclass_init;
+    type_info.class_data = info;
 
     type_register_static(&type_info);
 
@@ -102,9 +115,8 @@
 
     assert(bus->info == info->bus_info);
     dev = DEVICE(object_new(info->name));
-    dev->info = info;
     dev->parent_bus = bus;
-    qdev_prop_set_defaults(dev, dev->info->props);
+    qdev_prop_set_defaults(dev, qdev_get_info(dev)->props);
     qdev_prop_set_defaults(dev, dev->parent_bus->info->props);
     qdev_prop_set_globals(dev);
     QTAILQ_INSERT_HEAD(&bus->children, dev, sibling);
@@ -117,12 +129,12 @@
     QTAILQ_INIT(&dev->properties);
     dev->state = DEV_STATE_CREATED;
 
-    for (prop = dev->info->props; prop && prop->name; prop++) {
+    for (prop = qdev_get_info(dev)->props; prop && prop->name; prop++) {
         qdev_property_add_legacy(dev, prop, NULL);
         qdev_property_add_static(dev, prop, NULL);
     }
 
-    for (prop = dev->info->bus_info->props; prop && prop->name; prop++) {
+    for (prop = qdev_get_info(dev)->bus_info->props; prop && prop->name; prop++) {
         qdev_property_add_legacy(dev, prop, NULL);
         qdev_property_add_static(dev, prop, NULL);
     }
@@ -355,19 +367,19 @@
     int rc;
 
     assert(dev->state == DEV_STATE_CREATED);
-    rc = dev->info->init(dev, dev->info);
+    rc = qdev_get_info(dev)->init(dev, qdev_get_info(dev));
     if (rc < 0) {
         qdev_free(dev);
         return rc;
     }
-    if (dev->info->vmsd) {
-        vmstate_register_with_alias_id(dev, -1, dev->info->vmsd, dev,
+    if (qdev_get_info(dev)->vmsd) {
+        vmstate_register_with_alias_id(dev, -1, qdev_get_info(dev)->vmsd, dev,
                                        dev->instance_id_alias,
                                        dev->alias_required_for_version);
     }
     dev->state = DEV_STATE_INITIALIZED;
-    if (dev->hotplugged && dev->info->reset) {
-        dev->info->reset(dev);
+    if (dev->hotplugged && qdev_get_info(dev)->reset) {
+        qdev_get_info(dev)->reset(dev);
     }
     return 0;
 }
@@ -386,17 +398,17 @@
         qerror_report(QERR_BUS_NO_HOTPLUG, dev->parent_bus->name);
         return -1;
     }
-    assert(dev->info->unplug != NULL);
+    assert(qdev_get_info(dev)->unplug != NULL);
 
     qdev_hot_removed = true;
 
-    return dev->info->unplug(dev);
+    return qdev_get_info(dev)->unplug(dev);
 }
 
 static int qdev_reset_one(DeviceState *dev, void *opaque)
 {
-    if (dev->info->reset) {
-        dev->info->reset(dev);
+    if (qdev_get_info(dev)->reset) {
+        qdev_get_info(dev)->reset(dev);
     }
 
     return 0;
@@ -447,7 +459,7 @@
    way is somewhat unclean, and best avoided.  */
 void qdev_init_nofail(DeviceState *dev)
 {
-    DeviceInfo *info = dev->info;
+    DeviceInfo *info = qdev_get_info(dev);
 
     if (qdev_init(dev) < 0) {
         error_report("Initialization of device %s failed", info->name);
@@ -508,15 +520,15 @@
             bus = QLIST_FIRST(&dev->child_bus);
             qbus_free(bus);
         }
-        if (dev->info->vmsd)
-            vmstate_unregister(dev, dev->info->vmsd, dev);
-        if (dev->info->exit)
-            dev->info->exit(dev);
+        if (qdev_get_info(dev)->vmsd)
+            vmstate_unregister(dev, qdev_get_info(dev)->vmsd, dev);
+        if (qdev_get_info(dev)->exit)
+            qdev_get_info(dev)->exit(dev);
         if (dev->opts)
             qemu_opts_del(dev->opts);
     }
     QTAILQ_REMOVE(&dev->parent_bus->children, dev, sibling);
-    for (prop = dev->info->props; prop && prop->name; prop++) {
+    for (prop = qdev_get_info(dev)->props; prop && prop->name; prop++) {
         if (prop->info->free) {
             prop->info->free(dev, prop);
         }
@@ -708,7 +720,7 @@
     const char *sep = " ";
 
     error_printf("child busses at \"%s\":",
-                 dev->id ? dev->id : dev->info->name);
+                 dev->id ? dev->id : qdev_get_info(dev)->name);
     QLIST_FOREACH(child, &dev->child_bus, sibling) {
         error_printf("%s\"%s\"", sep, child->name);
         sep = ", ";
@@ -723,7 +735,7 @@
 
     error_printf("devices at \"%s\":", bus->name);
     QTAILQ_FOREACH(dev, &bus->children, sibling) {
-        error_printf("%s\"%s\"", sep, dev->info->name);
+        error_printf("%s\"%s\"", sep, qdev_get_info(dev)->name);
         if (dev->id)
             error_printf("/\"%s\"", dev->id);
         sep = ", ";
@@ -759,12 +771,12 @@
         }
     }
     QTAILQ_FOREACH(dev, &bus->children, sibling) {
-        if (strcmp(dev->info->name, elem) == 0) {
+        if (strcmp(qdev_get_info(dev)->name, elem) == 0) {
             return dev;
         }
     }
     QTAILQ_FOREACH(dev, &bus->children, sibling) {
-        if (dev->info->alias && strcmp(dev->info->alias, elem) == 0) {
+        if (qdev_get_info(dev)->alias && strcmp(qdev_get_info(dev)->alias, elem) == 0) {
             return dev;
         }
     }
@@ -966,7 +978,7 @@
 static void qdev_print(Monitor *mon, DeviceState *dev, int indent)
 {
     BusState *child;
-    qdev_printf("dev: %s, id \"%s\"\n", dev->info->name,
+    qdev_printf("dev: %s, id \"%s\"\n", qdev_get_info(dev)->name,
                 dev->id ? dev->id : "");
     indent += 2;
     if (dev->num_gpio_in) {
@@ -975,7 +987,7 @@
     if (dev->num_gpio_out) {
         qdev_printf("gpio-out %d\n", dev->num_gpio_out);
     }
-    qdev_print_props(mon, dev, dev->info->props, "dev", indent);
+    qdev_print_props(mon, dev, qdev_get_info(dev)->props, "dev", indent);
     qdev_print_props(mon, dev, dev->parent_bus->info->props, "bus", indent);
     if (dev->parent_bus->info->print_dev)
         dev->parent_bus->info->print_dev(mon, dev, indent);
@@ -1056,7 +1068,7 @@
             l += snprintf(p + l, size - l, "%s", d);
             g_free(d);
         } else {
-            l += snprintf(p + l, size - l, "%s", dev->info->name);
+            l += snprintf(p + l, size - l, "%s", qdev_get_info(dev)->name);
         }
     }
     l += snprintf(p + l , size - l, "/");
@@ -1078,7 +1090,7 @@
 
 char *qdev_get_type(DeviceState *dev, Error **errp)
 {
-    return g_strdup(dev->info->name);
+    return g_strdup(qdev_get_info(dev)->name);
 }
 
 void qdev_ref(DeviceState *dev)
@@ -1288,7 +1300,7 @@
 {
     gchar *type;
 
-    type = g_strdup_printf("child<%s>", child->info->name);
+    type = g_strdup_printf("child<%s>", qdev_get_info(child)->name);
 
     qdev_property_add(dev, name, type, qdev_get_child_property,
                       NULL, qdev_release_child_property,
@@ -1340,7 +1352,7 @@
         if (target) {
             gchar *target_type;
 
-            target_type = g_strdup_printf("link<%s>", target->info->name);
+            target_type = g_strdup_printf("link<%s>", qdev_get_info(target)->name);
             if (strcmp(target_type, type) == 0) {
                 *child = target;
                 qdev_ref(target);
diff --git a/hw/qdev.h b/hw/qdev.h
index 1e01a04..e13111f 100644
--- a/hw/qdev.h
+++ b/hw/qdev.h
@@ -69,9 +69,12 @@
 
 #define TYPE_DEVICE "device"
 #define DEVICE(obj) OBJECT_CHECK(DeviceState, (obj), TYPE_DEVICE)
+#define DEVICE_CLASS(klass) OBJECT_CLASS_CHECK(DeviceClass, (klass), TYPE_DEVICE)
+#define DEVICE_GET_CLASS(obj) OBJECT_GET_CLASS(DeviceClass, (obj), TYPE_DEVICE)
 
 typedef struct DeviceClass {
     ObjectClass parent_class;
+    DeviceInfo *info;
 } DeviceClass;
 
 /* This structure should not be accessed directly.  We declare it here
@@ -83,7 +86,6 @@
     enum DevState state;
     QemuOpts *opts;
     int hotplugged;
-    DeviceInfo *info;
     BusState *parent_bus;
     int num_gpio_out;
     qemu_irq *gpio_out;
@@ -389,9 +391,19 @@
 void error_set_from_qdev_prop_error(Error **errp, int ret, DeviceState *dev,
                                     Property *prop, const char *value);
 
+DeviceInfo *qdev_get_info(DeviceState *dev);
+
 static inline const char *qdev_fw_name(DeviceState *dev)
 {
-    return dev->info->fw_name ? : dev->info->alias ? : dev->info->name;
+    DeviceInfo *info = qdev_get_info(dev);
+
+    if (info->fw_name) {
+        return info->fw_name;
+    } else if (info->alias) {
+        return info->alias;
+    }
+
+    return info->name;
 }
 
 char *qdev_get_fw_dev_path(DeviceState *dev);
diff --git a/hw/rtl8139.c b/hw/rtl8139.c
index 0ae9f57..16d948e 100644
--- a/hw/rtl8139.c
+++ b/hw/rtl8139.c
@@ -3478,7 +3478,7 @@
     s->eeprom.contents[9] = s->conf.macaddr.a[4] | s->conf.macaddr.a[5] << 8;
 
     s->nic = qemu_new_nic(&net_rtl8139_info, &s->conf,
-                          dev->qdev.info->name, dev->qdev.id, s);
+                          qdev_get_info(&dev->qdev)->name, dev->qdev.id, s);
     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
 
     s->cplus_txbuffer = NULL;
diff --git a/hw/smc91c111.c b/hw/smc91c111.c
index 82b8811..b83a893 100644
--- a/hw/smc91c111.c
+++ b/hw/smc91c111.c
@@ -752,7 +752,7 @@
     sysbus_init_irq(dev, &s->irq);
     qemu_macaddr_default_if_unset(&s->conf.macaddr);
     s->nic = qemu_new_nic(&net_smc91c111_info, &s->conf,
-                          dev->qdev.info->name, dev->qdev.id, s);
+                          qdev_get_info(&dev->qdev)->name, dev->qdev.id, s);
     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
     /* ??? Save/restore.  */
     return 0;
diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c
index 7a86dc8..0159413 100644
--- a/hw/spapr_vio.c
+++ b/hw/spapr_vio.c
@@ -75,7 +75,7 @@
 
 static char *vio_format_dev_name(VIOsPAPRDevice *dev)
 {
-    VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)dev->qdev.info;
+    VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)qdev_get_info(&dev->qdev);
     char *name;
 
     /* Device tree style name device@reg */
@@ -90,7 +90,7 @@
 static int vio_make_devnode(VIOsPAPRDevice *dev,
                             void *fdt)
 {
-    VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)dev->qdev.info;
+    VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)qdev_get_info(&dev->qdev);
     int vdevice_off, node_off, ret;
     char *dt_name;
 
@@ -700,7 +700,7 @@
         return H_PARAMETER;
     }
 
-    info = (VIOsPAPRDeviceInfo *)dev->qdev.info;
+    info = (VIOsPAPRDeviceInfo *)qdev_get_info(&dev->qdev);
 
     if (mode & ~info->signal_mask) {
         return H_PARAMETER;
diff --git a/hw/spapr_vty.c b/hw/spapr_vty.c
index c5fb096..181dd0e 100644
--- a/hw/spapr_vty.c
+++ b/hw/spapr_vty.c
@@ -163,7 +163,7 @@
     selected = NULL;
     QTAILQ_FOREACH(iter, &bus->bus.children, sibling) {
         /* Only look at VTY devices */
-        if (iter->info != &spapr_vty.qdev) {
+        if (qdev_get_info(iter) != &spapr_vty.qdev) {
             continue;
         }
 
diff --git a/hw/usb-bus.c b/hw/usb-bus.c
index 016a3f2..3b80d5e 100644
--- a/hw/usb-bus.c
+++ b/hw/usb-bus.c
@@ -250,7 +250,7 @@
             return -1;
         }
     } else {
-        if (bus->nfree == 1 && strcmp(dev->qdev.info->name, "usb-hub") != 0) {
+        if (bus->nfree == 1 && strcmp(qdev_get_info(&dev->qdev)->name, "usb-hub") != 0) {
             /* Create a new hub and chain it on */
             usb_create_simple(bus, "usb-hub");
         }
diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c
index e9935ad..3b7f95d 100644
--- a/hw/usb-ccid.c
+++ b/hw/usb-ccid.c
@@ -1114,7 +1114,7 @@
 {
     int ret = 0;
     CCIDCardState *card = DO_UPCAST(CCIDCardState, qdev, qdev);
-    CCIDCardInfo *info = DO_UPCAST(CCIDCardInfo, qdev, qdev->info);
+    CCIDCardInfo *info = DO_UPCAST(CCIDCardInfo, qdev, qdev_get_info(qdev));
     USBCCIDState *s =
         DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
 
diff --git a/hw/usb-net.c b/hw/usb-net.c
index 2f527a8..086aa7e 100644
--- a/hw/usb-net.c
+++ b/hw/usb-net.c
@@ -1337,7 +1337,7 @@
 
     qemu_macaddr_default_if_unset(&s->conf.macaddr);
     s->nic = qemu_new_nic(&net_usbnet_info, &s->conf,
-                          s->dev.qdev.info->name, s->dev.qdev.id, s);
+                          qdev_get_info(&s->dev.qdev)->name, s->dev.qdev.id, s);
     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
     snprintf(s->usbstring_mac, sizeof(s->usbstring_mac),
              "%02x%02x%02x%02x%02x%02x",
diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c
index 69463d2..afa3274 100644
--- a/hw/usb-ohci.c
+++ b/hw/usb-ohci.c
@@ -1777,7 +1777,7 @@
     memory_region_init_io(&ohci->mem, &ohci_mem_ops, ohci, "ohci", 256);
     ohci->localmem_base = localmem_base;
 
-    ohci->name = dev->info->name;
+    ohci->name = qdev_get_info(dev)->name;
     usb_packet_init(&ohci->usb_packet);
 
     ohci->async_td = 0;
diff --git a/hw/virtio-console.c b/hw/virtio-console.c
index 0b28a30..f25adc4 100644
--- a/hw/virtio-console.c
+++ b/hw/virtio-console.c
@@ -110,7 +110,7 @@
 {
     VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
     VirtIOSerialPortInfo *info = DO_UPCAST(VirtIOSerialPortInfo, qdev,
-                                           vcon->port.dev.info);
+                                           qdev_get_info(&vcon->port.dev));
 
     if (port->id == 0 && !info->is_console) {
         error_report("Port number 0 on virtio-serial devices reserved for virtconsole devices for backward compatibility.");
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index 8c2f460..6c785d0 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -1030,7 +1030,7 @@
     memcpy(&n->mac[0], &conf->macaddr, sizeof(n->mac));
     n->status = VIRTIO_NET_S_LINK_UP;
 
-    n->nic = qemu_new_nic(&net_virtio_info, conf, dev->info->name, dev->id, n);
+    n->nic = qemu_new_nic(&net_virtio_info, conf, qdev_get_info(dev)->name, dev->id, n);
 
     qemu_format_nic_info_str(&n->nic->nc, conf->macaddr.a);
 
diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c
index 32e46e9..982ffbf 100644
--- a/hw/virtio-serial-bus.c
+++ b/hw/virtio-serial-bus.c
@@ -138,7 +138,7 @@
     assert(port);
     assert(virtio_queue_ready(vq));
 
-    info = DO_UPCAST(VirtIOSerialPortInfo, qdev, port->dev.info);
+    info = DO_UPCAST(VirtIOSerialPortInfo, qdev, qdev_get_info(&port->dev));
 
     while (!port->throttled) {
         unsigned int i;
@@ -373,7 +373,7 @@
 
     trace_virtio_serial_handle_control_message_port(port->id);
 
-    info = DO_UPCAST(VirtIOSerialPortInfo, qdev, port->dev.info);
+    info = DO_UPCAST(VirtIOSerialPortInfo, qdev, qdev_get_info(&port->dev));
 
     switch(cpkt.event) {
     case VIRTIO_CONSOLE_PORT_READY:
@@ -824,7 +824,7 @@
 {
     VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, qdev);
     VirtIOSerialPortInfo *info = DO_UPCAST(VirtIOSerialPortInfo, qdev,
-                                           port->dev.info);
+                                           qdev_get_info(&port->dev));
     VirtIOSerial *vser = port->vser;
 
     qemu_bh_delete(port->bh);
diff --git a/hw/xgmac.c b/hw/xgmac.c
index be63a7d..ccb9759 100644
--- a/hw/xgmac.c
+++ b/hw/xgmac.c
@@ -390,7 +390,7 @@
 
     qemu_macaddr_default_if_unset(&s->conf.macaddr);
     s->nic = qemu_new_nic(&net_xgmac_enet_info, &s->conf,
-                          dev->qdev.info->name, dev->qdev.id, s);
+                          qdev_get_info(&dev->qdev)->name, dev->qdev.id, s);
     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
 
     s->regs[XGMAC_ADDR_HIGH(0)] = (s->conf.macaddr.a[5] << 8) |