summary refs log tree commit diff stats
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/cirrus_vga.c8
-rw-r--r--hw/container.c20
-rw-r--r--hw/mc146818rtc.c27
-rw-r--r--hw/pc.c26
-rw-r--r--hw/pc.h14
-rw-r--r--hw/pc_piix.c58
-rw-r--r--hw/piix_pci.c3
-rw-r--r--hw/qdev.c555
-rw-r--r--hw/qdev.h281
-rw-r--r--hw/usb-bus.c12
-rw-r--r--hw/vga-pci.c5
-rw-r--r--hw/vmware_vga.h6
12 files changed, 980 insertions, 35 deletions
diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c
index 1b216e8813..1388a203eb 100644
--- a/hw/cirrus_vga.c
+++ b/hw/cirrus_vga.c
@@ -2899,7 +2899,7 @@ static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci,
  *
  ***************************************/
 
-void isa_cirrus_vga_init(MemoryRegion *system_memory)
+DeviceState *isa_cirrus_vga_init(MemoryRegion *system_memory)
 {
     CirrusVGAState *s;
 
@@ -2913,6 +2913,8 @@ void isa_cirrus_vga_init(MemoryRegion *system_memory)
     vmstate_register(NULL, 0, &vmstate_cirrus_vga, s);
     rom_add_vga(VGABIOS_CIRRUS_FILENAME);
     /* XXX ISA-LFB support */
+    /* FIXME not qdev yet */
+    return NULL;
 }
 
 /***************************************
@@ -2955,9 +2957,9 @@ static int pci_cirrus_vga_initfn(PCIDevice *dev)
      return 0;
 }
 
-void pci_cirrus_vga_init(PCIBus *bus)
+DeviceState *pci_cirrus_vga_init(PCIBus *bus)
 {
-    pci_create_simple(bus, -1, "cirrus-vga");
+    return &pci_create_simple(bus, -1, "cirrus-vga")->qdev;
 }
 
 static PCIDeviceInfo cirrus_vga_info = {
diff --git a/hw/container.c b/hw/container.c
new file mode 100644
index 0000000000..9cbf3992c4
--- /dev/null
+++ b/hw/container.c
@@ -0,0 +1,20 @@
+#include "sysbus.h"
+
+static int container_initfn(SysBusDevice *dev)
+{
+    return 0;
+}
+
+static SysBusDeviceInfo container_info = {
+    .init = container_initfn,
+    .qdev.name = "container",
+    .qdev.size = sizeof(SysBusDevice),
+    .qdev.no_user = 1,
+};
+
+static void container_init(void)
+{
+    sysbus_register_withprop(&container_info);
+}
+
+device_init(container_init);
diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
index 2aaca2ff41..0c23cb0dba 100644
--- a/hw/mc146818rtc.c
+++ b/hw/mc146818rtc.c
@@ -614,6 +614,29 @@ static const MemoryRegionOps cmos_ops = {
     .old_portio = cmos_portio
 };
 
+// FIXME add int32 visitor
+static void visit_type_int32(Visitor *v, int *value, const char *name, Error **errp)
+{
+    int64_t val = *value;
+    visit_type_int(v, &val, name, errp);
+}
+
+static void rtc_get_date(DeviceState *dev, Visitor *v, void *opaque,
+                         const char *name, Error **errp)
+{
+    ISADevice *isa = DO_UPCAST(ISADevice, qdev, dev);
+    RTCState *s = DO_UPCAST(RTCState, dev, isa);
+
+    visit_start_struct(v, NULL, "struct tm", name, 0, errp);
+    visit_type_int32(v, &s->current_tm.tm_year, "tm_year", errp);
+    visit_type_int32(v, &s->current_tm.tm_mon, "tm_mon", errp);
+    visit_type_int32(v, &s->current_tm.tm_mday, "tm_mday", errp);
+    visit_type_int32(v, &s->current_tm.tm_hour, "tm_hour", errp);
+    visit_type_int32(v, &s->current_tm.tm_min, "tm_min", errp);
+    visit_type_int32(v, &s->current_tm.tm_sec, "tm_sec", errp);
+    visit_end_struct(v, errp);
+}
+
 static int rtc_initfn(ISADevice *dev)
 {
     RTCState *s = DO_UPCAST(RTCState, dev, dev);
@@ -647,6 +670,10 @@ static int rtc_initfn(ISADevice *dev)
 
     qdev_set_legacy_instance_id(&dev->qdev, base, 2);
     qemu_register_reset(rtc_reset, s);
+
+    qdev_property_add(&s->dev.qdev, "date", "struct tm",
+                      rtc_get_date, NULL, NULL, s, NULL);
+
     return 0;
 }
 
diff --git a/hw/pc.c b/hw/pc.c
index b6dcba2067..03466ec8d1 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -1069,38 +1069,44 @@ qemu_irq *pc_allocate_cpu_irq(void)
     return qemu_allocate_irqs(pic_irq_request, NULL, 1);
 }
 
-void pc_vga_init(PCIBus *pci_bus)
+DeviceState *pc_vga_init(PCIBus *pci_bus)
 {
+    DeviceState *dev = NULL;
+
     if (cirrus_vga_enabled) {
         if (pci_bus) {
-            pci_cirrus_vga_init(pci_bus);
+            dev = pci_cirrus_vga_init(pci_bus);
         } else {
-            isa_cirrus_vga_init(get_system_memory());
+            dev = isa_cirrus_vga_init(get_system_memory());
         }
     } else if (vmsvga_enabled) {
         if (pci_bus) {
-            if (!pci_vmsvga_init(pci_bus)) {
+            dev = pci_vmsvga_init(pci_bus);
+            if (!dev) {
                 fprintf(stderr, "Warning: vmware_vga not available,"
                         " using standard VGA instead\n");
-                pci_vga_init(pci_bus);
+                dev = pci_vga_init(pci_bus);
             }
         } else {
             fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __FUNCTION__);
         }
 #ifdef CONFIG_SPICE
     } else if (qxl_enabled) {
-        if (pci_bus)
-            pci_create_simple(pci_bus, -1, "qxl-vga");
-        else
+        if (pci_bus) {
+            dev = &pci_create_simple(pci_bus, -1, "qxl-vga")->qdev;
+        } else {
             fprintf(stderr, "%s: qxl: no PCI bus\n", __FUNCTION__);
+        }
 #endif
     } else if (std_vga_enabled) {
         if (pci_bus) {
-            pci_vga_init(pci_bus);
+            dev = pci_vga_init(pci_bus);
         } else {
-            isa_vga_init();
+            dev = isa_vga_init();
         }
     }
+
+    return dev;
 }
 
 static void cpu_request_exit(void *opaque, int irq, int level)
diff --git a/hw/pc.h b/hw/pc.h
index b7b7e40ce7..b2000e7873 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -140,7 +140,7 @@ void pc_memory_init(MemoryRegion *system_memory,
                     MemoryRegion *rom_memory,
                     MemoryRegion **ram_memory);
 qemu_irq *pc_allocate_cpu_irq(void);
-void pc_vga_init(PCIBus *pci_bus);
+DeviceState *pc_vga_init(PCIBus *pci_bus);
 void pc_basic_device_init(qemu_irq *gsi,
                           ISADevice **rtc_state,
                           ISADevice **floppy,
@@ -205,27 +205,27 @@ enum vga_retrace_method {
 
 extern enum vga_retrace_method vga_retrace_method;
 
-static inline int isa_vga_init(void)
+static inline DeviceState *isa_vga_init(void)
 {
     ISADevice *dev;
 
     dev = isa_try_create("isa-vga");
     if (!dev) {
         fprintf(stderr, "Warning: isa-vga not available\n");
-        return 0;
+        return NULL;
     }
     qdev_init_nofail(&dev->qdev);
-    return 1;
+    return &dev->qdev;
 }
 
-int pci_vga_init(PCIBus *bus);
+DeviceState *pci_vga_init(PCIBus *bus);
 int isa_vga_mm_init(target_phys_addr_t vram_base,
                     target_phys_addr_t ctrl_base, int it_shift,
                     MemoryRegion *address_space);
 
 /* cirrus_vga.c */
-void pci_cirrus_vga_init(PCIBus *bus);
-void isa_cirrus_vga_init(MemoryRegion *address_space);
+DeviceState *pci_cirrus_vga_init(PCIBus *bus);
+DeviceState *isa_cirrus_vga_init(MemoryRegion *address_space);
 
 /* ne2000.c */
 static inline bool isa_ne2000_init(int base, int irq, NICInfo *nd)
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index 970f43c99c..b9bb09d1e0 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -99,6 +99,7 @@ static void pc_init1(MemoryRegion *system_memory,
     MemoryRegion *ram_memory;
     MemoryRegion *pci_memory;
     MemoryRegion *rom_memory;
+    DeviceState *dev;
 
     pc_cpus_init(cpu_model);
 
@@ -168,7 +169,10 @@ static void pc_init1(MemoryRegion *system_memory,
 
     pc_register_ferr_irq(gsi[13]);
 
-    pc_vga_init(pci_enabled? pci_bus: NULL);
+    dev = pc_vga_init(pci_enabled? pci_bus: NULL);
+    if (dev) {
+        qdev_property_add_child(qdev_get_root(), "vga", dev, NULL);
+    }
 
     if (xen_enabled()) {
         pci_create_simple(pci_bus, -1, "xen-platform");
@@ -205,6 +209,17 @@ static void pc_init1(MemoryRegion *system_memory,
         }
     }
 
+    /* FIXME there's some major spaghetti here.  Somehow we create the devices
+     * on the PIIX before we actually create it.  We create the PIIX3 deep in
+     * the recess of the i440fx creation too and then lose the DeviceState.
+     *
+     * For now, let's "fix" this by making judicious use of paths.  This is not
+     * generally the right way to do this.
+     */
+
+    qdev_property_add_child(qdev_resolve_path("/i440fx/piix3", NULL),
+                            "rtc", (DeviceState *)rtc_state, NULL);
+
     audio_init(gsi, pci_enabled ? pci_bus : NULL);
 
     pc_cmos_init(below_4g_mem_size, above_4g_mem_size, boot_device,
@@ -306,6 +321,14 @@ static QEMUMachine pc_machine_v1_0 = {
     .is_default = 1,
 };
 
+static QEMUMachine pc_machine_v0_15 = {
+    .name = "pc-0.15",
+    .desc = "Standard PC",
+    .init = pc_init_pci,
+    .max_cpus = 255,
+    .is_default = 1,
+};
+
 static QEMUMachine pc_machine_v0_14 = {
     .name = "pc-0.14",
     .desc = "Standard PC",
@@ -320,6 +343,22 @@ static QEMUMachine pc_machine_v0_14 = {
             .driver   = "qxl-vga",
             .property = "revision",
             .value    = stringify(2),
+        },{
+            .driver   = "virtio-blk-pci",
+            .property = "event_idx",
+            .value    = "off",
+        },{
+            .driver   = "virtio-serial-pci",
+            .property = "event_idx",
+            .value    = "off",
+        },{
+            .driver   = "virtio-net-pci",
+            .property = "event_idx",
+            .value    = "off",
+        },{
+            .driver   = "virtio-balloon-pci",
+            .property = "event_idx",
+            .value    = "off",
         },
         { /* end of list */ }
     },
@@ -360,6 +399,10 @@ static QEMUMachine pc_machine_v0_13 = {
             .property = "event_idx",
             .value    = "off",
         },{
+            .driver   = "virtio-balloon-pci",
+            .property = "event_idx",
+            .value    = "off",
+        },{
             .driver   = "AC97",
             .property = "use_broken_id",
             .value    = stringify(1),
@@ -407,6 +450,10 @@ static QEMUMachine pc_machine_v0_12 = {
             .property = "event_idx",
             .value    = "off",
         },{
+            .driver   = "virtio-balloon-pci",
+            .property = "event_idx",
+            .value    = "off",
+        },{
             .driver   = "AC97",
             .property = "use_broken_id",
             .value    = stringify(1),
@@ -462,6 +509,10 @@ static QEMUMachine pc_machine_v0_11 = {
             .property = "event_idx",
             .value    = "off",
         },{
+            .driver   = "virtio-balloon-pci",
+            .property = "event_idx",
+            .value    = "off",
+        },{
             .driver   = "AC97",
             .property = "use_broken_id",
             .value    = stringify(1),
@@ -529,6 +580,10 @@ static QEMUMachine pc_machine_v0_10 = {
             .property = "event_idx",
             .value    = "off",
         },{
+            .driver   = "virtio-balloon-pci",
+            .property = "event_idx",
+            .value    = "off",
+        },{
             .driver   = "AC97",
             .property = "use_broken_id",
             .value    = stringify(1),
@@ -557,6 +612,7 @@ static QEMUMachine xenfv_machine = {
 static void pc_machine_init(void)
 {
     qemu_register_machine(&pc_machine_v1_0);
+    qemu_register_machine(&pc_machine_v0_15);
     qemu_register_machine(&pc_machine_v0_14);
     qemu_register_machine(&pc_machine_v0_13);
     qemu_register_machine(&pc_machine_v0_12);
diff --git a/hw/piix_pci.c b/hw/piix_pci.c
index d183443b2f..d785d4ba07 100644
--- a/hw/piix_pci.c
+++ b/hw/piix_pci.c
@@ -288,6 +288,7 @@ static PCIBus *i440fx_common_init(const char *device_name,
                     address_space_io, 0);
     s->bus = b;
     qdev_init_nofail(dev);
+    qdev_property_add_child(qdev_get_root(), "i440fx", dev, NULL);
 
     d = pci_create_simple(b, 0, device_name);
     *pi440fx_state = DO_UPCAST(PCII440FXState, dev, d);
@@ -323,6 +324,8 @@ static PCIBus *i440fx_common_init(const char *device_name,
                 pci_create_simple_multifunction(b, -1, true, "PIIX3"));
         pci_bus_irqs(b, piix3_set_irq, pci_slot_get_pirq, piix3,
                 PIIX_NUM_PIRQS);
+
+        qdev_property_add_child(dev, "piix3", &piix3->dev.qdev, NULL);
     }
     piix3->pic = pic;
 
diff --git a/hw/qdev.c b/hw/qdev.c
index 106407f226..83913c72f2 100644
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -83,6 +83,7 @@ static DeviceInfo *qdev_find_info(BusInfo *bus_info, const char *name)
 static DeviceState *qdev_create_from_info(BusState *bus, DeviceInfo *info)
 {
     DeviceState *dev;
+    Property *prop;
 
     assert(bus->info == info->bus_info);
     dev = g_malloc0(info->size);
@@ -98,7 +99,19 @@ static DeviceState *qdev_create_from_info(BusState *bus, DeviceInfo *info)
         qdev_hot_added = true;
     }
     dev->instance_id_alias = -1;
+    QTAILQ_INIT(&dev->properties);
     dev->state = DEV_STATE_CREATED;
+
+    for (prop = dev->info->props; prop && prop->name; prop++) {
+        qdev_property_add_legacy(dev, prop, NULL);
+    }
+
+    for (prop = dev->info->bus_info->props; prop && prop->name; prop++) {
+        qdev_property_add_legacy(dev, prop, NULL);
+    }
+
+    qdev_property_add_str(dev, "type", qdev_get_type, NULL, NULL);
+
     return dev;
 }
 
@@ -216,6 +229,32 @@ int qdev_device_help(QemuOpts *opts)
     return 1;
 }
 
+static DeviceState *qdev_get_peripheral(void)
+{
+    static DeviceState *dev;
+
+    if (dev == NULL) {
+        dev = qdev_create(NULL, "container");
+        qdev_property_add_child(qdev_get_root(), "peripheral", dev, NULL);
+        qdev_init_nofail(dev);
+    }
+
+    return dev;
+}
+
+static DeviceState *qdev_get_peripheral_anon(void)
+{
+    static DeviceState *dev;
+
+    if (dev == NULL) {
+        dev = qdev_create(NULL, "container");
+        qdev_property_add_child(qdev_get_root(), "peripheral-anon", dev, NULL);
+        qdev_init_nofail(dev);
+    }
+
+    return dev;
+}
+
 DeviceState *qdev_device_add(QemuOpts *opts)
 {
     const char *driver, *path, *id;
@@ -267,7 +306,14 @@ DeviceState *qdev_device_add(QemuOpts *opts)
     id = qemu_opts_id(opts);
     if (id) {
         qdev->id = id;
-    }
+        qdev_property_add_child(qdev_get_peripheral(), qdev->id, qdev, NULL);
+    } else {
+        static int anon_count;
+        gchar *name = g_strdup_printf("device[%d]", anon_count++);
+        qdev_property_add_child(qdev_get_peripheral_anon(), name,
+                                qdev, NULL);
+        g_free(name);
+    }        
     if (qemu_opt_foreach(opts, set_property, qdev, 1) != 0) {
         qdev_free(qdev);
         return NULL;
@@ -323,6 +369,11 @@ int qdev_unplug(DeviceState *dev)
     }
     assert(dev->info->unplug != NULL);
 
+    if (dev->ref != 0) {
+        qerror_report(QERR_DEVICE_IN_USE, dev->id?:"");
+        return -1;
+    }
+
     qdev_hot_removed = true;
 
     return dev->info->unplug(dev);
@@ -390,12 +441,31 @@ void qdev_init_nofail(DeviceState *dev)
     }
 }
 
+static void qdev_property_del_all(DeviceState *dev)
+{
+    while (!QTAILQ_EMPTY(&dev->properties)) {
+        DeviceProperty *prop = QTAILQ_FIRST(&dev->properties);
+
+        QTAILQ_REMOVE(&dev->properties, prop, node);
+
+        if (prop->release) {
+            prop->release(dev, prop->name, prop->opaque);
+        }
+
+        g_free(prop->name);
+        g_free(prop->type);
+        g_free(prop);
+    }
+}
+
 /* Unlink device from bus and free the structure.  */
 void qdev_free(DeviceState *dev)
 {
     BusState *bus;
     Property *prop;
 
+    qdev_property_del_all(dev);
+
     if (dev->state == DEV_STATE_INITIALIZED) {
         while (dev->num_child_bus) {
             bus = QLIST_FIRST(&dev->child_bus);
@@ -962,3 +1032,486 @@ char* qdev_get_fw_dev_path(DeviceState *dev)
 
     return strdup(path);
 }
+
+char *qdev_get_type(DeviceState *dev, Error **errp)
+{
+    return g_strdup(dev->info->name);
+}
+
+void qdev_ref(DeviceState *dev)
+{
+    dev->ref++;
+}
+
+void qdev_unref(DeviceState *dev)
+{
+    g_assert(dev->ref > 0);
+    dev->ref--;
+}
+
+void qdev_property_add(DeviceState *dev, const char *name, const char *type,
+                       DevicePropertyAccessor *get, DevicePropertyAccessor *set,
+                       DevicePropertyRelease *release,
+                       void *opaque, Error **errp)
+{
+    DeviceProperty *prop = g_malloc0(sizeof(*prop));
+
+    prop->name = g_strdup(name);
+    prop->type = g_strdup(type);
+
+    prop->get = get;
+    prop->set = set;
+    prop->release = release;
+    prop->opaque = opaque;
+
+    QTAILQ_INSERT_TAIL(&dev->properties, prop, node);
+}
+
+static DeviceProperty *qdev_property_find(DeviceState *dev, const char *name)
+{
+    DeviceProperty *prop;
+
+    QTAILQ_FOREACH(prop, &dev->properties, node) {
+        if (strcmp(prop->name, name) == 0) {
+            return prop;
+        }
+    }
+
+    return NULL;
+}
+
+void qdev_property_get(DeviceState *dev, Visitor *v, const char *name,
+                       Error **errp)
+{
+    DeviceProperty *prop = qdev_property_find(dev, name);
+
+    if (prop == NULL) {
+        error_set(errp, QERR_PROPERTY_NOT_FOUND, dev->id?:"", name);
+        return;
+    }
+
+    if (!prop->get) {
+        error_set(errp, QERR_PERMISSION_DENIED);
+    } else {
+        prop->get(dev, v, prop->opaque, name, errp);
+    }
+}
+
+void qdev_property_set(DeviceState *dev, Visitor *v, const char *name,
+                       Error **errp)
+{
+    DeviceProperty *prop = qdev_property_find(dev, name);
+
+    if (prop == NULL) {
+        error_set(errp, QERR_PROPERTY_NOT_FOUND, dev->id?:"", name);
+        return;
+    }
+
+    if (!prop->set) {
+        error_set(errp, QERR_PERMISSION_DENIED);
+    } else {
+        prop->set(dev, prop->opaque, v, name, errp);
+    }
+}
+
+const char *qdev_property_get_type(DeviceState *dev, const char *name, Error **errp)
+{
+    DeviceProperty *prop = qdev_property_find(dev, name);
+
+    if (prop == NULL) {
+        error_set(errp, QERR_PROPERTY_NOT_FOUND, dev->id?:"", name);
+        return NULL;
+    }
+
+    return prop->type;
+}
+
+/**
+ * Legacy property handling
+ */
+
+static void qdev_get_legacy_property(DeviceState *dev, Visitor *v, void *opaque,
+                                     const char *name, Error **errp)
+{
+    Property *prop = opaque;
+
+    if (prop->info->print) {
+        char buffer[1024];
+        char *ptr = buffer;
+
+        prop->info->print(dev, prop, buffer, sizeof(buffer));
+        visit_type_str(v, &ptr, name, errp);
+    } else {
+        error_set(errp, QERR_PERMISSION_DENIED);
+    }
+}
+
+static void qdev_set_legacy_property(DeviceState *dev, Visitor *v, void *opaque,
+                                     const char *name, Error **errp)
+{
+    Property *prop = opaque;
+
+    if (dev->state != DEV_STATE_CREATED) {
+        error_set(errp, QERR_PERMISSION_DENIED);
+        return;
+    }
+
+    if (prop->info->parse) {
+        Error *local_err = NULL;
+        char *ptr = NULL;
+
+        visit_type_str(v, &ptr, name, &local_err);
+        if (!local_err) {
+            int ret;
+            ret = prop->info->parse(dev, prop, ptr);
+            if (ret != 0) {
+                error_set(errp, QERR_INVALID_PARAMETER_VALUE,
+                          name, prop->info->name);
+            }
+            g_free(ptr);
+        } else {
+            error_propagate(errp, local_err);
+        }
+    } else {
+        error_set(errp, QERR_PERMISSION_DENIED);
+    }
+}
+
+/**
+ * @qdev_add_legacy_property - adds a legacy property
+ *
+ * Do not use this is new code!  Properties added through this interface will
+ * be given types in the "legacy<>" type namespace.
+ *
+ * Legacy properties are always processed as strings.  The format of the string
+ * depends on the property type.
+ */
+void qdev_property_add_legacy(DeviceState *dev, Property *prop,
+                              Error **errp)
+{
+    gchar *type;
+
+    type = g_strdup_printf("legacy<%s>", prop->info->name);
+
+    qdev_property_add(dev, prop->name, type,
+                      qdev_get_legacy_property,
+                      qdev_set_legacy_property,
+                      NULL,
+                      prop, errp);
+
+    g_free(type);
+}
+
+DeviceState *qdev_get_root(void)
+{
+    static DeviceState *qdev_root;
+
+    if (!qdev_root) {
+        qdev_root = qdev_create(NULL, "container");
+        qdev_init_nofail(qdev_root);
+    }
+
+    return qdev_root;
+}
+
+static void qdev_get_child_property(DeviceState *dev, Visitor *v, void *opaque,
+                                    const char *name, Error **errp)
+{
+    DeviceState *child = opaque;
+    gchar *path;
+
+    path = qdev_get_canonical_path(child);
+    visit_type_str(v, &path, name, errp);
+    g_free(path);
+}
+
+void qdev_property_add_child(DeviceState *dev, const char *name,
+                             DeviceState *child, Error **errp)
+{
+    gchar *type;
+
+    type = g_strdup_printf("child<%s>", child->info->name);
+
+    qdev_property_add(dev, name, type, qdev_get_child_property,
+                      NULL, NULL, child, errp);
+
+    qdev_ref(child);
+    g_assert(child->parent == NULL);
+    child->parent = dev;
+
+    g_free(type);
+}
+
+static void qdev_get_link_property(DeviceState *dev, Visitor *v, void *opaque,
+                                   const char *name, Error **errp)
+{
+    DeviceState **child = opaque;
+    gchar *path;
+
+    if (*child) {
+        path = qdev_get_canonical_path(*child);
+        visit_type_str(v, &path, name, errp);
+        g_free(path);
+    } else {
+        path = (gchar *)"";
+        visit_type_str(v, &path, name, errp);
+    }
+}
+
+static void qdev_set_link_property(DeviceState *dev, Visitor *v, void *opaque,
+                                   const char *name, Error **errp)
+{
+    DeviceState **child = opaque;
+    bool ambiguous = false;
+    const char *type;
+    char *path;
+
+    type = qdev_property_get_type(dev, name, NULL);
+
+    visit_type_str(v, &path, name, errp);
+
+    if (*child) {
+        qdev_unref(*child);
+    }
+
+    if (strcmp(path, "") != 0) {
+        DeviceState *target;
+
+        target = qdev_resolve_path(path, &ambiguous);
+        if (target) {
+            gchar *target_type;
+
+            target_type = g_strdup_printf("link<%s>", target->info->name);
+            if (strcmp(target_type, type) == 0) {
+                *child = target;
+                qdev_ref(target);
+            } else {
+                error_set(errp, QERR_INVALID_PARAMETER_TYPE, name, type);
+            }
+
+            g_free(target_type);
+        } else {
+            error_set(errp, QERR_DEVICE_NOT_FOUND, path);
+        }
+    } else {
+        *child = NULL;
+    }
+
+    g_free(path);
+}
+
+void qdev_property_add_link(DeviceState *dev, const char *name,
+                            const char *type, DeviceState **child,
+                            Error **errp)
+{
+    gchar *full_type;
+
+    full_type = g_strdup_printf("link<%s>", type);
+
+    qdev_property_add(dev, name, full_type,
+                      qdev_get_link_property,
+                      qdev_set_link_property,
+                      NULL, child, errp);
+
+    g_free(full_type);
+}
+
+gchar *qdev_get_canonical_path(DeviceState *dev)
+{
+    DeviceState *root = qdev_get_root();
+    char *newpath = NULL, *path = NULL;
+
+    while (dev != root) {
+        DeviceProperty *prop = NULL;
+
+        g_assert(dev->parent != NULL);
+
+        QTAILQ_FOREACH(prop, &dev->parent->properties, node) {
+            if (!strstart(prop->type, "child<", NULL)) {
+                continue;
+            }
+
+            if (prop->opaque == dev) {
+                if (path) {
+                    newpath = g_strdup_printf("%s/%s", prop->name, path);
+                    g_free(path);
+                    path = newpath;
+                } else {
+                    path = g_strdup(prop->name);
+                }
+                break;
+            }
+        }
+
+        g_assert(prop != NULL);
+
+        dev = dev->parent;
+    }
+
+    newpath = g_strdup_printf("/%s", path);
+    g_free(path);
+
+    return newpath;
+}
+
+static DeviceState *qdev_resolve_abs_path(DeviceState *parent,
+                                          gchar **parts,
+                                          int index)
+{
+    DeviceProperty *prop;
+    DeviceState *child;
+
+    if (parts[index] == NULL) {
+        return parent;
+    }
+
+    if (strcmp(parts[index], "") == 0) {
+        return qdev_resolve_abs_path(parent, parts, index + 1);
+    }
+
+    prop = qdev_property_find(parent, parts[index]);
+    if (prop == NULL) {
+        return NULL;
+    }
+
+    child = NULL;
+    if (strstart(prop->type, "link<", NULL)) {
+        DeviceState **pchild = prop->opaque;
+        if (*pchild) {
+            child = *pchild;
+        }
+    } else if (strstart(prop->type, "child<", NULL)) {
+        child = prop->opaque;
+    }
+
+    if (!child) {
+        return NULL;
+    }
+
+    return qdev_resolve_abs_path(child, parts, index + 1);
+}
+
+static DeviceState *qdev_resolve_partial_path(DeviceState *parent,
+                                              gchar **parts,
+                                              bool *ambiguous)
+{
+    DeviceState *dev;
+    DeviceProperty *prop;
+
+    dev = qdev_resolve_abs_path(parent, parts, 0);
+
+    QTAILQ_FOREACH(prop, &parent->properties, node) {
+        DeviceState *found;
+
+        if (!strstart(prop->type, "child<", NULL)) {
+            continue;
+        }
+
+        found = qdev_resolve_partial_path(prop->opaque, parts, ambiguous);
+        if (found) {
+            if (dev) {
+                if (ambiguous) {
+                    *ambiguous = true;
+                }
+                return NULL;
+            }
+            dev = found;
+        }
+
+        if (ambiguous && *ambiguous) {
+            return NULL;
+        }
+    }
+
+    return dev;
+}
+
+DeviceState *qdev_resolve_path(const char *path, bool *ambiguous)
+{
+    bool partial_path = true;
+    DeviceState *dev;
+    gchar **parts;
+
+    parts = g_strsplit(path, "/", 0);
+    if (parts == NULL || parts[0] == NULL) {
+        g_strfreev(parts);
+        return qdev_get_root();
+    }
+
+    if (strcmp(parts[0], "") == 0) {
+        partial_path = false;
+    }
+
+    if (partial_path) {
+        if (ambiguous) {
+            *ambiguous = false;
+        }
+        dev = qdev_resolve_partial_path(qdev_get_root(), parts, ambiguous);
+    } else {
+        dev = qdev_resolve_abs_path(qdev_get_root(), parts, 1);
+    }
+
+    g_strfreev(parts);
+
+    return dev;
+}
+
+typedef struct StringProperty
+{
+    char *(*get)(DeviceState *, Error **);
+    void (*set)(DeviceState *, const char *, Error **);
+} StringProperty;
+
+static void qdev_property_get_str(DeviceState *dev, Visitor *v, void *opaque,
+                                  const char *name, Error **errp)
+{
+    StringProperty *prop = opaque;
+    char *value;
+
+    value = prop->get(dev, errp);
+    if (value) {
+        visit_type_str(v, &value, name, errp);
+        g_free(value);
+    }
+}
+
+static void qdev_property_set_str(DeviceState *dev, Visitor *v, void *opaque,
+                                  const char *name, Error **errp)
+{
+    StringProperty *prop = opaque;
+    char *value;
+    Error *local_err = NULL;
+
+    visit_type_str(v, &value, name, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    prop->set(dev, value, errp);
+    g_free(value);
+}
+
+static void qdev_property_release_str(DeviceState *dev, const char *name,
+                                      void *opaque)
+{
+    StringProperty *prop = opaque;
+    g_free(prop);
+}
+
+void qdev_property_add_str(DeviceState *dev, const char *name,
+                           char *(*get)(DeviceState *, Error **),
+                           void (*set)(DeviceState *, const char *, Error **),
+                           Error **errp)
+{
+    StringProperty *prop = g_malloc0(sizeof(*prop));
+
+    prop->get = get;
+    prop->set = set;
+
+    qdev_property_add(dev, name, "string",
+                      get ? qdev_property_get_str : NULL,
+                      set ? qdev_property_set_str : NULL,
+                      qdev_property_release_str,
+                      prop, errp);
+}
diff --git a/hw/qdev.h b/hw/qdev.h
index 36a4198c89..6e184278f4 100644
--- a/hw/qdev.h
+++ b/hw/qdev.h
@@ -5,6 +5,7 @@
 #include "qemu-queue.h"
 #include "qemu-char.h"
 #include "qemu-option.h"
+#include "qapi/qapi-visit-core.h"
 
 typedef struct Property Property;
 
@@ -27,6 +28,44 @@ enum {
     DEV_NVECTORS_UNSPECIFIED = -1,
 };
 
+/**
+ * @DevicePropertyAccessor - called when trying to get/set a property
+ *
+ * @dev the device that owns the property
+ * @v the visitor that contains the property data
+ * @opaque the device property opaque
+ * @name the name of the property
+ * @errp a pointer to an Error that is filled if getting/setting fails.
+ */
+typedef void (DevicePropertyAccessor)(DeviceState *dev,
+                                      Visitor *v,
+                                      void *opaque,
+                                      const char *name,
+                                      Error **errp);
+
+/**
+ * @DevicePropertyRelease - called when a property is removed from a device
+ *
+ * @dev the device that owns the property
+ * @name the name of the property
+ * @opaque the opaque registered with the property
+ */
+typedef void (DevicePropertyRelease)(DeviceState *dev,
+                                     const char *name,
+                                     void *opaque);
+
+typedef struct DeviceProperty
+{
+    gchar *name;
+    gchar *type;
+    DevicePropertyAccessor *get;
+    DevicePropertyAccessor *set;
+    DevicePropertyRelease *release;
+    void *opaque;
+
+    QTAILQ_ENTRY(DeviceProperty) node;
+} DeviceProperty;
+
 /* This structure should not be accessed directly.  We declare it here
    so that it can be embedded in individual device state structures.  */
 struct DeviceState {
@@ -45,6 +84,18 @@ struct DeviceState {
     QTAILQ_ENTRY(DeviceState) sibling;
     int instance_id_alias;
     int alias_required_for_version;
+
+    /**
+     * This tracks the number of references between devices.  See @qdev_ref for
+     * more information.
+     */
+    uint32_t ref;
+
+    QTAILQ_HEAD(, DeviceProperty) properties;
+
+    /* Do not, under any circumstance, use this parent link below anywhere
+     * outside of qdev.c.  You have been warned. */
+    DeviceState *parent;
 };
 
 typedef void (*bus_dev_printfn)(Monitor *mon, DeviceState *dev, int indent);
@@ -329,4 +380,234 @@ char *qdev_get_fw_dev_path(DeviceState *dev);
 /* This is a nasty hack to allow passing a NULL bus to qdev_create.  */
 extern struct BusInfo system_bus_info;
 
+/**
+ * @qdev_ref
+ *
+ * Increase the reference count of a device.  A device cannot be freed as long
+ * as its reference count is greater than zero.
+ *
+ * @dev - the device
+ */
+void qdev_ref(DeviceState *dev);
+
+/**
+ * @qdef_unref
+ *
+ * Decrease the reference count of a device.  A device cannot be freed as long
+ * as its reference count is greater than zero.
+ *
+ * @dev - the device
+ */
+void qdev_unref(DeviceState *dev);
+
+/**
+ * @qdev_property_add - add a new property to a device
+ *
+ * @dev - the device to add a property to
+ *
+ * @name - the name of the property.  This can contain any character except for
+ *         a forward slash.  In general, you should use hyphens '-' instead of
+ *         underscores '_' when naming properties.
+ *
+ * @type - the type name of the property.  This namespace is pretty loosely
+ *         defined.  Sub namespaces are constructed by using a prefix and then
+ *         to angle brackets.  For instance, the type 'virtio-net-pci' in the
+ *         'link' namespace would be 'link<virtio-net-pci>'.
+ *
+ * @get - the getter to be called to read a property.  If this is NULL, then
+ *        the property cannot be read.
+ *
+ * @set - the setter to be called to write a property.  If this is NULL,
+ *        then the property cannot be written.
+ *
+ * @release - called when the property is removed from the device.  This is
+ *            meant to allow a property to free its opaque upon device
+ *            destruction.  This may be NULL.
+ *
+ * @opaque - an opaque pointer to pass to the callbacks for the property
+ *
+ * @errp - returns an error if this function fails
+ */
+void qdev_property_add(DeviceState *dev, const char *name, const char *type,
+                       DevicePropertyAccessor *get, DevicePropertyAccessor *set,
+                       DevicePropertyRelease *release,
+                       void *opaque, Error **errp);
+
+/**
+ * @qdev_property_get - reads a property from a device
+ *
+ * @dev - the device
+ *
+ * @v - the visitor that will receive the property value.  This should be an
+ *      Output visitor and the data will be written with @name as the name.
+ *
+ * @name - the name of the property
+ *
+ * @errp - returns an error if this function fails
+ */
+void qdev_property_get(DeviceState *dev, Visitor *v, const char *name,
+                       Error **errp);
+
+/**
+ * @qdev_property_set - writes a property to a device
+ *
+ * @dev - the device
+ *
+ * @v - the visitor that will be used to write the property value.  This should
+ *      be an Input visitor and the data will be first read with @name as the
+ *      name and then written as the property value.
+ *
+ * @name - the name of the property
+ *
+ * @errp - returns an error if this function fails
+ */
+void qdev_property_set(DeviceState *dev, Visitor *v, const char *name,
+                       Error **errp);
+
+/**
+ * @qdev_property_get_type - returns the type of a property
+ *
+ * @dev - the device
+ *
+ * @name - the name of the property
+ *
+ * @errp - returns an error if this function fails
+ *
+ * Returns:
+ *   The type name of the property.
+ */
+const char *qdev_property_get_type(DeviceState *dev, const char *name,
+                                   Error **errp);
+
+/**
+ * @qdev_property_add_legacy - add a legacy @Property to a device
+ *
+ * DO NOT USE THIS IN NEW CODE!
+ */
+void qdev_property_add_legacy(DeviceState *dev, Property *prop, Error **errp);
+
+/**
+ * @qdev_get_root - returns the root device of the composition tree
+ *
+ * Returns:
+ *   The root of the composition tree.
+ */
+DeviceState *qdev_get_root(void);
+
+/**
+ * @qdev_get_canonical_path - returns the canonical path for a device.  This
+ * is the path within the composition tree starting from the root.
+ *
+ * Returns:
+ *   The canonical path in the composition tree.
+ */
+gchar *qdev_get_canonical_path(DeviceState *dev);
+
+/**
+ * @qdev_resolve_path - resolves a path returning a device
+ *
+ * There are two types of supported paths--absolute paths and partial paths.
+ * 
+ * Absolute paths are derived from the root device and can follow child<> or
+ * link<> properties.  Since they can follow link<> properties, they can be
+ * arbitrarily long.  Absolute paths look like absolute filenames and are
+ * prefixed with a leading slash.
+ * 
+ * Partial paths look like relative filenames.  They do not begin with a
+ * prefix.  The matching rules for partial paths are subtle but designed to make
+ * specifying devices easy.  At each level of the composition tree, the partial
+ * path is matched as an absolute path.  The first match is not returned.  At
+ * least two matches are searched for.  A successful result is only returned if
+ * only one match is founded.  If more than one match is found, a flag is
+ * return to indicate that the match was ambiguous.
+ *
+ * @path - the path to resolve
+ *
+ * @ambiguous - returns true if the path resolution failed because of an
+ *              ambiguous match
+ *
+ * Returns:
+ *   The matched device or NULL on path lookup failure.
+ */
+DeviceState *qdev_resolve_path(const char *path, bool *ambiguous);
+
+/**
+ * @qdev_property_add_child - Add a child property to a device
+ *
+ * Child properties form the composition tree.  All devices need to be a child
+ * of another device.  Devices can only be a child of one device.
+ *
+ * There is no way for a child to determine what its parent is.  It is not
+ * a bidirectional relationship.  This is by design.
+ *
+ * @dev - the device to add a property to
+ *
+ * @name - the name of the property
+ *
+ * @child - the child device
+ *
+ * @errp - if an error occurs, a pointer to an area to store the area
+ */
+void qdev_property_add_child(DeviceState *dev, const char *name,
+                             DeviceState *child, Error **errp);
+
+/**
+ * @qdev_property_add_link - Add a link property to a device
+ *
+ * Links establish relationships between devices.  Links are unidirectional
+ * although two links can be combined to form a bidirectional relationship
+ * between devices.
+ *
+ * Links form the graph in the device model.
+ *
+ * @dev - the device to add a property to
+ *
+ * @name - the name of the property
+ *
+ * @type - the qdev type of the link
+ *
+ * @child - a pointer to where the link device reference is stored
+ *
+ * @errp - if an error occurs, a pointer to an area to store the area
+ */
+void qdev_property_add_link(DeviceState *dev, const char *name,
+                            const char *type, DeviceState **child,
+                            Error **errp);
+
+/**
+ * @qdev_property_add_str
+ *
+ * Add a string property using getters/setters.  This function will add a
+ * property of type 'string'.
+ *
+ * @dev - the device to add a property to
+ *
+ * @name - the name of the property
+ *
+ * @get - the getter or NULL if the property is write-only.  This function must
+ *        return a string to be freed by @g_free().
+ *
+ * @set - the setter or NULL if the property is read-only
+ *
+ * @errp - if an error occurs, a pointer to an area to store the error
+ */
+void qdev_property_add_str(DeviceState *dev, const char *name,
+                           char *(*get)(DeviceState *, Error **),
+                           void (*set)(DeviceState *, const char *, Error **),
+                           Error **errp);
+
+/**
+ * @qdev_get_type
+ *
+ * Returns the string representation of the type of this object.
+ *
+ * @dev - the device
+ *
+ * @errp - if an error occurs, a pointer to an area to store the error
+ *
+ * Returns: a string representing the type.  This must be freed by the caller
+ *          with g_free().
+ */
+char *qdev_get_type(DeviceState *dev, Error **errp);
+
 #endif
diff --git a/hw/usb-bus.c b/hw/usb-bus.c
index 8cafb76fff..8203390929 100644
--- a/hw/usb-bus.c
+++ b/hw/usb-bus.c
@@ -77,23 +77,21 @@ static int usb_qdev_init(DeviceState *qdev, DeviceInfo *base)
     QLIST_INIT(&dev->strings);
     rc = usb_claim_port(dev);
     if (rc != 0) {
-        goto err;
+        return rc;
     }
     rc = dev->info->init(dev);
     if (rc != 0) {
-        goto err;
+        usb_release_port(dev);
+        return rc;
     }
     if (dev->auto_attach) {
         rc = usb_device_attach(dev);
         if (rc != 0) {
-            goto err;
+            usb_qdev_exit(qdev);
+            return rc;
         }
     }
     return 0;
-
-err:
-    usb_qdev_exit(qdev);
-    return rc;
 }
 
 static int usb_qdev_exit(DeviceState *qdev)
diff --git a/hw/vga-pci.c b/hw/vga-pci.c
index 14bfadbfcf..a75dbf3974 100644
--- a/hw/vga-pci.c
+++ b/hw/vga-pci.c
@@ -70,10 +70,9 @@ static int pci_vga_initfn(PCIDevice *dev)
      return 0;
 }
 
-int pci_vga_init(PCIBus *bus)
+DeviceState *pci_vga_init(PCIBus *bus)
 {
-    pci_create_simple(bus, -1, "VGA");
-    return 0;
+    return &pci_create_simple(bus, -1, "VGA")->qdev;
 }
 
 static PCIDeviceInfo vga_info = {
diff --git a/hw/vmware_vga.h b/hw/vmware_vga.h
index 5132573a56..db11cbfac8 100644
--- a/hw/vmware_vga.h
+++ b/hw/vmware_vga.h
@@ -4,15 +4,15 @@
 #include "qemu-common.h"
 
 /* vmware_vga.c */
-static inline bool pci_vmsvga_init(PCIBus *bus)
+static inline DeviceState *pci_vmsvga_init(PCIBus *bus)
 {
     PCIDevice *dev;
 
     dev = pci_try_create(bus, -1, "vmware-svga");
     if (!dev || qdev_init(&dev->qdev) < 0) {
-        return false;
+        return NULL;
     } else {
-        return true;
+        return &dev->qdev;
     }
 }