diff options
Diffstat (limited to 'hw/core/qdev.c')
-rw-r--r-- | hw/core/qdev.c | 147 |
1 files changed, 83 insertions, 64 deletions
diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 9e5538aeae..2131c7f951 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -97,6 +97,9 @@ static void bus_add_child(BusState *bus, DeviceState *child) void qdev_set_parent_bus(DeviceState *dev, BusState *bus) { BusState *old_parent_bus = dev->parent_bus; + DeviceClass *dc = DEVICE_GET_CLASS(dev); + + assert(dc->bus_type && object_dynamic_cast(OBJECT(bus), dc->bus_type)); if (old_parent_bus) { trace_qdev_update_parent_bus(dev, object_get_typename(OBJECT(dev)), @@ -125,52 +128,30 @@ void qdev_set_parent_bus(DeviceState *dev, BusState *bus) } } -/* Create a new device. This only initializes the device state - structure and allows properties to be set. The device still needs - to be realized. See qdev-core.h. */ -DeviceState *qdev_create(BusState *bus, const char *name) +/* + * Create a device on the heap. + * A type @name must exist. + * This only initializes the device state structure and allows + * properties to be set. The device still needs to be realized. See + * qdev-core.h. + */ +DeviceState *qdev_new(const char *name) { - DeviceState *dev; - - dev = qdev_try_create(bus, name); - if (!dev) { - if (bus) { - error_report("Unknown device '%s' for bus '%s'", name, - object_get_typename(OBJECT(bus))); - } else { - error_report("Unknown device '%s' for default sysbus", name); - } - abort(); - } - - return dev; + return DEVICE(object_new(name)); } -DeviceState *qdev_try_create(BusState *bus, const char *type) +/* + * Try to create a device on the heap. + * This is like qdev_new(), except it returns %NULL when type @name + * does not exist. + */ +DeviceState *qdev_try_new(const char *name) { - DeviceState *dev; - - if (object_class_by_name(type) == NULL) { + if (!object_class_by_name(name)) { return NULL; } - dev = DEVICE(object_new(type)); - if (!dev) { - return NULL; - } - - if (!bus) { - /* Assert that the device really is a SysBusDevice before - * we put it onto the sysbus. Non-sysbus devices which aren't - * being put onto a bus should be created with object_new(TYPE_FOO), - * not qdev_create(NULL, TYPE_FOO). - */ - g_assert(object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)); - bus = sysbus_get_default(); - } - qdev_set_parent_bus(dev, bus); - object_unref(OBJECT(dev)); - return dev; + return DEVICE(object_new(name)); } static QTAILQ_HEAD(, DeviceListener) device_listeners @@ -392,36 +373,71 @@ static void device_reset_child_foreach(Object *obj, ResettableChildCallback cb, void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - object_property_set_bool(OBJECT(dev), false, "realized", &error_abort); + qdev_unrealize(dev); } /* * Realize @dev. - * Device properties should be set before calling this function. IRQs - * and MMIO regions should be connected/mapped after calling this - * function. - * On failure, report an error with error_report() and terminate the - * program. This is okay during machine creation. Don't use for - * hotplug, because there callers need to recover from failure. - * Exception: if you know the device's init() callback can't fail, - * then qdev_init_nofail() can't fail either, and is therefore usable - * even then. But relying on the device implementation that way is - * somewhat unclean, and best avoided. + * @dev must not be plugged into a bus. + * If @bus, plug @dev into @bus. This takes a reference to @dev. + * If @dev has no QOM parent, make one up, taking another reference. + * On success, return true. + * On failure, store an error through @errp and return false. */ -void qdev_init_nofail(DeviceState *dev) +bool qdev_realize(DeviceState *dev, BusState *bus, Error **errp) { Error *err = NULL; - assert(!dev->realized); + assert(!dev->realized && !dev->parent_bus); + + if (bus) { + qdev_set_parent_bus(dev, bus); + } else { + assert(!DEVICE_GET_CLASS(dev)->bus_type); + } - object_ref(OBJECT(dev)); object_property_set_bool(OBJECT(dev), true, "realized", &err); if (err) { - error_reportf_err(err, "Initialization of device %s failed: ", - object_get_typename(OBJECT(dev))); - exit(1); + error_propagate(errp, err); } + return !err; +} + +/* + * Realize @dev and drop a reference. + * This is like qdev_realize(), except the caller must hold a + * (private) reference, which is dropped on return regardless of + * success or failure. Intended use: + * dev = qdev_new(); + * [...] + * qdev_realize_and_unref(dev, bus, errp); + * Now @dev can go away without further ado. + */ +bool qdev_realize_and_unref(DeviceState *dev, BusState *bus, Error **errp) +{ + bool ret; + + ret = qdev_realize(dev, bus, errp); object_unref(OBJECT(dev)); + return ret; +} + +void qdev_unrealize(DeviceState *dev) +{ + object_property_set_bool(OBJECT(dev), false, "realized", &error_abort); +} + +static int qdev_assert_realized_properly(Object *obj, void *opaque) +{ + DeviceState *dev = DEVICE(object_dynamic_cast(obj, TYPE_DEVICE)); + DeviceClass *dc; + + if (dev) { + dc = DEVICE_GET_CLASS(dev); + assert(dev->realized); + assert(dev->parent_bus || !dc->bus_type); + } + return 0; } void qdev_machine_creation_done(void) @@ -431,6 +447,9 @@ void qdev_machine_creation_done(void) * only create hotpluggable devices */ qdev_hotplug = true; + + object_child_foreach_recursive(object_get_root(), + qdev_assert_realized_properly, NULL); } bool qdev_machine_modified(void) @@ -919,9 +938,7 @@ static void device_set_realized(Object *obj, bool value, Error **errp) resettable_state_clear(&dev->reset); QLIST_FOREACH(bus, &dev->child_bus, sibling) { - object_property_set_bool(OBJECT(bus), true, "realized", - &local_err); - if (local_err != NULL) { + if (!qbus_realize(bus, errp)) { goto child_realize_fail; } } @@ -946,8 +963,7 @@ static void device_set_realized(Object *obj, bool value, Error **errp) } else if (!value && dev->realized) { QLIST_FOREACH(bus, &dev->child_bus, sibling) { - object_property_set_bool(OBJECT(bus), false, "realized", - &error_abort); + qbus_unrealize(bus); } if (qdev_get_vmsd(dev)) { vmstate_unregister(VMSTATE_IF(dev), qdev_get_vmsd(dev), dev); @@ -965,8 +981,7 @@ static void device_set_realized(Object *obj, bool value, Error **errp) child_realize_fail: QLIST_FOREACH(bus, &dev->child_bus, sibling) { - object_property_set_bool(OBJECT(bus), false, "realized", - &error_abort); + qbus_unrealize(bus); } if (qdev_get_vmsd(dev)) { @@ -983,6 +998,10 @@ post_realize_fail: fail: error_propagate(errp, local_err); if (unattached_parent) { + /* + * Beware, this doesn't just revert + * object_property_add_child(), it also runs bus_remove()! + */ object_unparent(OBJECT(dev)); unattached_count--; } @@ -1078,7 +1097,7 @@ static void device_unparent(Object *obj) BusState *bus; if (dev->realized) { - object_property_set_bool(obj, false, "realized", &error_abort); + qdev_unrealize(dev); } while (dev->num_child_bus) { bus = QLIST_FIRST(&dev->child_bus); |