diff --git a/hw/qdev-monitor.c b/hw/qdev-monitor.c
index 81d6548..0345f6f 100644
--- a/hw/qdev-monitor.c
+++ b/hw/qdev-monitor.c
@@ -118,17 +118,44 @@ static const char *find_typename_by_alias(const char *alias)
     return NULL;
 }
 
+static void display_property(Object *obj, const char *name,
+                             const char *typename, bool read_only,
+                             void *opaque)
+{
+    char *legacy_typename;
+
+    if (!strstart(typename, "legacy<", NULL)) {
+        return;
+    }
+
+    legacy_typename = g_strdup(&typename[7]);
+    legacy_typename[strlen(legacy_typename) - 1] = 0;
+
+    /*
+     * TODO Properties without a parser are just for dirty hacks.
+     * qdev_prop_ptr is the only such PropertyInfo.  It's marked
+     * for removal.  This conditional should be removed along with
+     * it.
+     */
+    if (!read_only) {
+        error_printf("%s.%s=%s\n", object_get_typename(obj),
+                     &name[7], legacy_typename);
+    }
+
+    g_free(legacy_typename);
+}
+
 int qdev_device_help(QemuOpts *opts)
 {
     const char *driver;
-    Property *prop;
     ObjectClass *klass;
-    DeviceClass *info;
+    Object *obj;
 
     driver = qemu_opt_get(opts, "driver");
     if (driver && !strcmp(driver, "?")) {
         bool show_no_user = false;
-        object_class_foreach(qdev_print_devinfo, TYPE_DEVICE, false, &show_no_user);
+        object_class_foreach(qdev_print_devinfo, TYPE_DEVICE,
+                             false, &show_no_user);
         return 1;
     }
 
@@ -149,30 +176,11 @@ int qdev_device_help(QemuOpts *opts)
     if (!klass) {
         return 0;
     }
-    info = DEVICE_CLASS(klass);
-
-    for (prop = info->props; prop && prop->name; prop++) {
-        /*
-         * TODO Properties without a parser are just for dirty hacks.
-         * qdev_prop_ptr is the only such PropertyInfo.  It's marked
-         * for removal.  This conditional should be removed along with
-         * it.
-         */
-        if (!prop->info->parse) {
-            continue;           /* no way to set it, don't show */
-        }
-        error_printf("%s.%s=%s\n", driver, prop->name,
-                     prop->info->legacy_name ?: prop->info->name);
-    }
-    if (info->bus_info) {
-        for (prop = info->bus_info->props; prop && prop->name; prop++) {
-            if (!prop->info->parse) {
-                continue;           /* no way to set it, don't show */
-            }
-            error_printf("%s.%s=%s\n", driver, prop->name,
-                         prop->info->legacy_name ?: prop->info->name);
-        }
-    }
+
+    obj = object_new(driver);
+    object_property_foreach(obj, display_property, NULL);
+    object_delete(obj);
+
     return 1;
 }
 
@@ -481,50 +489,56 @@ DeviceState *qdev_device_add(QemuOpts *opts)
 #define qdev_printf(fmt, ...) monitor_printf(mon, "%*s" fmt, indent, "", ## __VA_ARGS__)
 static void qbus_print(Monitor *mon, BusState *bus, int indent);
 
-static void qdev_print_props(Monitor *mon, DeviceState *dev, Property *props,
-                             const char *prefix, int indent)
+typedef struct QTreePrinter
 {
-    if (!props)
-        return;
-    for (; props->name; props++) {
-        Error *err = NULL;
-        char *value;
-        char *legacy_name = g_strdup_printf("legacy-%s", props->name);
-        if (object_property_get_type(OBJECT(dev), legacy_name, NULL)) {
-            value = object_property_get_str(OBJECT(dev), legacy_name, &err);
-        } else {
-            value = object_property_get_str(OBJECT(dev), props->name, &err);
-        }
-        g_free(legacy_name);
+    Visitor visitor;
+    Monitor *mon;
+    int indent;
+    const char *prefix;
+} QTreePrinter;
+
+static void qtree_printer_visit_str(Visitor *v, char **obj,
+                                    const char *name, Error **errp)
+{
+    QTreePrinter *p = (QTreePrinter *)v;
 
-        if (err) {
-            error_free(err);
-            continue;
-        }
-        qdev_printf("%s-prop: %s = %s\n", prefix, props->name,
-                    value && *value ? value : "<null>");
-        g_free(value);
+    monitor_printf(p->mon, "%*sdev-prop: %s = %s\n",
+                   p->indent, "", &name[7], *obj);
+}
+
+static void qdev_print_prop(Object *obj, const char *name,
+                             const char *typename, bool read_only,
+                             void *opaque)
+{
+    QTreePrinter *printer = opaque;
+
+    if (strstart(typename, "legacy<", NULL)) {
+        object_property_get(obj, &printer->visitor, name, NULL);
     }
 }
 
 static void qdev_print(Monitor *mon, DeviceState *dev, int indent)
 {
+    QTreePrinter printer = {
+        .visitor.type_str = qtree_printer_visit_str,
+        .mon = mon,
+        .indent = indent + 2,
+    };
     BusState *child;
+
     qdev_printf("dev: %s, id \"%s\"\n", object_get_typename(OBJECT(dev)),
                 dev->id ? dev->id : "");
-    indent += 2;
     if (dev->num_gpio_in) {
         qdev_printf("gpio-in %d\n", dev->num_gpio_in);
     }
     if (dev->num_gpio_out) {
         qdev_printf("gpio-out %d\n", dev->num_gpio_out);
     }
-    qdev_print_props(mon, dev, qdev_get_props(dev), "dev", indent);
-    qdev_print_props(mon, dev, dev->parent_bus->info->props, "bus", indent);
+    object_property_foreach(OBJECT(dev), qdev_print_prop, &printer);
     if (dev->parent_bus->info->print_dev)
-        dev->parent_bus->info->print_dev(mon, dev, indent);
+        dev->parent_bus->info->print_dev(mon, dev, indent + 2);
     QLIST_FOREACH(child, &dev->child_bus, sibling) {
-        qbus_print(mon, child, indent);
+        qbus_print(mon, child, indent + 2);
     }
 }
 
diff --git a/hw/qdev-properties.c b/hw/qdev-properties.c
index 894fa79..0c6dade 100644
--- a/hw/qdev-properties.c
+++ b/hw/qdev-properties.c
@@ -925,31 +925,17 @@ PropertyInfo qdev_prop_blocksize = {
 
 /* --- public helpers --- */
 
-static Property *qdev_prop_walk(Property *props, const char *name)
-{
-    if (!props)
-        return NULL;
-    while (props->name) {
-        if (strcmp(props->name, name) == 0)
-            return props;
-        props++;
-    }
-    return NULL;
-}
-
 static Property *qdev_prop_find(DeviceState *dev, const char *name)
 {
-    Property *prop;
-
-    /* device properties */
-    prop = qdev_prop_walk(qdev_get_props(dev), name);
-    if (prop)
-        return prop;
+    Object *obj = OBJECT(dev);
+    ObjectProperty *prop;
 
-    /* bus properties */
-    prop = qdev_prop_walk(dev->parent_bus->info->props, name);
-    if (prop)
-        return prop;
+    QTAILQ_FOREACH(prop, &obj->properties, node) {
+        if (strstart(prop->type, "legacy<", NULL) &&
+            strcmp(&prop->name[7], name) == 0) {
+            return prop->opaque;
+        }
+    }
 
     return NULL;
 }
diff --git a/hw/qdev.c b/hw/qdev.c
index d265963..6afbbb6 100644
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -45,18 +45,6 @@ const VMStateDescription *qdev_get_vmsd(DeviceState *dev)
     return dc->vmsd;
 }
 
-BusInfo *qdev_get_bus_info(DeviceState *dev)
-{
-    DeviceClass *dc = DEVICE_GET_CLASS(dev);
-    return dc->bus_info;
-}
-
-Property *qdev_get_props(DeviceState *dev)
-{
-    DeviceClass *dc = DEVICE_GET_CLASS(dev);
-    return dc->props;
-}
-
 const char *qdev_fw_name(DeviceState *dev)
 {
     DeviceClass *dc = DEVICE_GET_CLASS(dev);
@@ -95,7 +83,6 @@ void qdev_set_parent_bus(DeviceState *dev, BusState *bus)
 
     dev->parent_bus = bus;
     QTAILQ_INSERT_HEAD(&bus->children, dev, sibling);
-    qdev_add_properties(dev, dev->parent_bus->info->props);
 }
 
 /* Create a new device.  This only initializes the device state structure
@@ -608,7 +595,7 @@ void qdev_property_add_legacy(DeviceState *dev, Property *prop,
     object_property_add(OBJECT(dev), name, type,
                         prop->info->print ? qdev_get_legacy_property : prop->info->get,
                         prop->info->parse ? qdev_set_legacy_property : prop->info->set,
-                        NULL,
+                        prop->info->release,
                         prop, errp);
 
     g_free(type);
@@ -642,7 +629,7 @@ void qdev_property_add_static(DeviceState *dev, Property *prop,
 static void device_initfn(Object *obj)
 {
     DeviceState *dev = DEVICE(obj);
-    Property *prop;
+    DeviceClass *dc = DEVICE_GET_CLASS(dev);
 
     if (qdev_hotplug) {
         dev->hotplugged = 1;
@@ -652,7 +639,8 @@ static void device_initfn(Object *obj)
     dev->instance_id_alias = -1;
     dev->state = DEV_STATE_CREATED;
 
-    qdev_add_properties(dev, qdev_get_props(dev));
+    qdev_add_properties(dev, dc->props);
+
     object_property_add_str(OBJECT(dev), "type", qdev_get_type, NULL, NULL);
 }
 
@@ -660,8 +648,8 @@ static void device_initfn(Object *obj)
 static void device_finalize(Object *obj)
 {
     DeviceState *dev = DEVICE(obj);
-    BusState *bus;
     DeviceClass *dc = DEVICE_GET_CLASS(dev);
+    BusState *bus;
 
     if (dev->state == DEV_STATE_INITIALIZED) {
         while (dev->num_child_bus) {
@@ -678,7 +666,9 @@ static void device_finalize(Object *obj)
             qemu_opts_del(dev->opts);
         }
     }
-    QTAILQ_REMOVE(&dev->parent_bus->children, dev, sibling);
+    if (dev->parent_bus) {
+        QTAILQ_REMOVE(&dev->parent_bus->children, dev, sibling);
+    }
 }
 
 void device_reset(DeviceState *dev)
diff --git a/hw/qdev.h b/hw/qdev.h
index fc3b50f..6d2261f 100644
--- a/hw/qdev.h
+++ b/hw/qdev.h
@@ -96,7 +96,6 @@ struct BusInfo {
     bus_get_dev_path get_dev_path;
     bus_get_fw_dev_path get_fw_dev_path;
     qbus_resetfn *reset;
-    Property *props;
 };
 
 struct BusState {
