aboutsummaryrefslogtreecommitdiffstats
path: root/hw/core/qdev.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/core/qdev.c')
-rw-r--r--hw/core/qdev.c947
1 files changed, 947 insertions, 0 deletions
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
new file mode 100644
index 000000000..84f301944
--- /dev/null
+++ b/hw/core/qdev.c
@@ -0,0 +1,947 @@
+/*
+ * Dynamic device configuration and creation.
+ *
+ * Copyright (c) 2009 CodeSourcery
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* The theory here is that it should be possible to create a machine without
+ knowledge of specific devices. Historically board init routines have
+ passed a bunch of arguments to each device, requiring the board know
+ exactly which device it is dealing with. This file provides an abstract
+ API for device configuration and initialization. Devices will generally
+ inherit from a particular bus (e.g. PCI or I2C) rather than
+ this API directly. */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qapi/qapi-events-qdev.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qmp/qerror.h"
+#include "qapi/visitor.h"
+#include "qemu/error-report.h"
+#include "qemu/option.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "hw/boards.h"
+#include "hw/sysbus.h"
+#include "hw/qdev-clock.h"
+#include "migration/vmstate.h"
+#include "trace.h"
+
+static bool qdev_hot_added = false;
+bool qdev_hot_removed = false;
+
+const VMStateDescription *qdev_get_vmsd(DeviceState *dev)
+{
+ DeviceClass *dc = DEVICE_GET_CLASS(dev);
+ return dc->vmsd;
+}
+
+static void bus_free_bus_child(BusChild *kid)
+{
+ object_unref(OBJECT(kid->child));
+ g_free(kid);
+}
+
+static void bus_remove_child(BusState *bus, DeviceState *child)
+{
+ BusChild *kid;
+
+ QTAILQ_FOREACH(kid, &bus->children, sibling) {
+ if (kid->child == child) {
+ char name[32];
+
+ snprintf(name, sizeof(name), "child[%d]", kid->index);
+ QTAILQ_REMOVE_RCU(&bus->children, kid, sibling);
+
+ bus->num_children--;
+
+ /* This gives back ownership of kid->child back to us. */
+ object_property_del(OBJECT(bus), name);
+
+ /* free the bus kid, when it is safe to do so*/
+ call_rcu(kid, bus_free_bus_child, rcu);
+ break;
+ }
+ }
+}
+
+static void bus_add_child(BusState *bus, DeviceState *child)
+{
+ char name[32];
+ BusChild *kid = g_malloc0(sizeof(*kid));
+
+ bus->num_children++;
+ kid->index = bus->max_index++;
+ kid->child = child;
+ object_ref(OBJECT(kid->child));
+
+ QTAILQ_INSERT_HEAD_RCU(&bus->children, kid, sibling);
+
+ /* This transfers ownership of kid->child to the property. */
+ snprintf(name, sizeof(name), "child[%d]", kid->index);
+ object_property_add_link(OBJECT(bus), name,
+ object_get_typename(OBJECT(child)),
+ (Object **)&kid->child,
+ NULL, /* read-only property */
+ 0);
+}
+
+static bool bus_check_address(BusState *bus, DeviceState *child, Error **errp)
+{
+ BusClass *bc = BUS_GET_CLASS(bus);
+ return !bc->check_address || bc->check_address(bus, child, errp);
+}
+
+bool qdev_set_parent_bus(DeviceState *dev, BusState *bus, Error **errp)
+{
+ 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 (!bus_check_address(bus, dev, errp)) {
+ return false;
+ }
+
+ if (old_parent_bus) {
+ trace_qdev_update_parent_bus(dev, object_get_typename(OBJECT(dev)),
+ old_parent_bus, object_get_typename(OBJECT(old_parent_bus)),
+ OBJECT(bus), object_get_typename(OBJECT(bus)));
+ /*
+ * Keep a reference to the device while it's not plugged into
+ * any bus, to avoid it potentially evaporating when it is
+ * dereffed in bus_remove_child().
+ * Also keep the ref of the parent bus until the end, so that
+ * we can safely call resettable_change_parent() below.
+ */
+ object_ref(OBJECT(dev));
+ bus_remove_child(dev->parent_bus, dev);
+ }
+ dev->parent_bus = bus;
+ object_ref(OBJECT(bus));
+ bus_add_child(bus, dev);
+ if (dev->realized) {
+ resettable_change_parent(OBJECT(dev), OBJECT(bus),
+ OBJECT(old_parent_bus));
+ }
+ if (old_parent_bus) {
+ object_unref(OBJECT(old_parent_bus));
+ object_unref(OBJECT(dev));
+ }
+ return true;
+}
+
+DeviceState *qdev_new(const char *name)
+{
+ if (!object_class_by_name(name)) {
+ module_load_qom_one(name);
+ }
+ return DEVICE(object_new(name));
+}
+
+DeviceState *qdev_try_new(const char *name)
+{
+ if (!module_object_class_by_name(name)) {
+ return NULL;
+ }
+ return DEVICE(object_new(name));
+}
+
+static QTAILQ_HEAD(, DeviceListener) device_listeners
+ = QTAILQ_HEAD_INITIALIZER(device_listeners);
+
+enum ListenerDirection { Forward, Reverse };
+
+#define DEVICE_LISTENER_CALL(_callback, _direction, _args...) \
+ do { \
+ DeviceListener *_listener; \
+ \
+ switch (_direction) { \
+ case Forward: \
+ QTAILQ_FOREACH(_listener, &device_listeners, link) { \
+ if (_listener->_callback) { \
+ _listener->_callback(_listener, ##_args); \
+ } \
+ } \
+ break; \
+ case Reverse: \
+ QTAILQ_FOREACH_REVERSE(_listener, &device_listeners, \
+ link) { \
+ if (_listener->_callback) { \
+ _listener->_callback(_listener, ##_args); \
+ } \
+ } \
+ break; \
+ default: \
+ abort(); \
+ } \
+ } while (0)
+
+static int device_listener_add(DeviceState *dev, void *opaque)
+{
+ DEVICE_LISTENER_CALL(realize, Forward, dev);
+
+ return 0;
+}
+
+void device_listener_register(DeviceListener *listener)
+{
+ QTAILQ_INSERT_TAIL(&device_listeners, listener, link);
+
+ qbus_walk_children(sysbus_get_default(), NULL, NULL, device_listener_add,
+ NULL, NULL);
+}
+
+void device_listener_unregister(DeviceListener *listener)
+{
+ QTAILQ_REMOVE(&device_listeners, listener, link);
+}
+
+bool qdev_should_hide_device(const QDict *opts, bool from_json, Error **errp)
+{
+ ERRP_GUARD();
+ DeviceListener *listener;
+
+ QTAILQ_FOREACH(listener, &device_listeners, link) {
+ if (listener->hide_device) {
+ if (listener->hide_device(listener, opts, from_json, errp)) {
+ return true;
+ } else if (*errp) {
+ return false;
+ }
+ }
+ }
+
+ return false;
+}
+
+void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id,
+ int required_for_version)
+{
+ assert(!dev->realized);
+ dev->instance_id_alias = alias_id;
+ dev->alias_required_for_version = required_for_version;
+}
+
+static int qdev_prereset(DeviceState *dev, void *opaque)
+{
+ trace_qdev_reset_tree(dev, object_get_typename(OBJECT(dev)));
+ return 0;
+}
+
+static int qbus_prereset(BusState *bus, void *opaque)
+{
+ trace_qbus_reset_tree(bus, object_get_typename(OBJECT(bus)));
+ return 0;
+}
+
+static int qdev_reset_one(DeviceState *dev, void *opaque)
+{
+ device_legacy_reset(dev);
+
+ return 0;
+}
+
+static int qbus_reset_one(BusState *bus, void *opaque)
+{
+ BusClass *bc = BUS_GET_CLASS(bus);
+ trace_qbus_reset(bus, object_get_typename(OBJECT(bus)));
+ if (bc->reset) {
+ bc->reset(bus);
+ }
+ return 0;
+}
+
+void qdev_reset_all(DeviceState *dev)
+{
+ trace_qdev_reset_all(dev, object_get_typename(OBJECT(dev)));
+ qdev_walk_children(dev, qdev_prereset, qbus_prereset,
+ qdev_reset_one, qbus_reset_one, NULL);
+}
+
+void qdev_reset_all_fn(void *opaque)
+{
+ qdev_reset_all(DEVICE(opaque));
+}
+
+void qbus_reset_all(BusState *bus)
+{
+ trace_qbus_reset_all(bus, object_get_typename(OBJECT(bus)));
+ qbus_walk_children(bus, qdev_prereset, qbus_prereset,
+ qdev_reset_one, qbus_reset_one, NULL);
+}
+
+void qbus_reset_all_fn(void *opaque)
+{
+ BusState *bus = opaque;
+ qbus_reset_all(bus);
+}
+
+void device_cold_reset(DeviceState *dev)
+{
+ resettable_reset(OBJECT(dev), RESET_TYPE_COLD);
+}
+
+bool device_is_in_reset(DeviceState *dev)
+{
+ return resettable_is_in_reset(OBJECT(dev));
+}
+
+static ResettableState *device_get_reset_state(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+ return &dev->reset;
+}
+
+static void device_reset_child_foreach(Object *obj, ResettableChildCallback cb,
+ void *opaque, ResetType type)
+{
+ DeviceState *dev = DEVICE(obj);
+ BusState *bus;
+
+ QLIST_FOREACH(bus, &dev->child_bus, sibling) {
+ cb(OBJECT(bus), opaque, type);
+ }
+}
+
+bool qdev_realize(DeviceState *dev, BusState *bus, Error **errp)
+{
+ assert(!dev->realized && !dev->parent_bus);
+
+ if (bus) {
+ if (!qdev_set_parent_bus(dev, bus, errp)) {
+ return false;
+ }
+ } else {
+ assert(!DEVICE_GET_CLASS(dev)->bus_type);
+ }
+
+ return object_property_set_bool(OBJECT(dev), "realized", true, errp);
+}
+
+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), "realized", false, &error_abort);
+}
+
+static int qdev_assert_realized_properly_cb(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_assert_realized_properly(void)
+{
+ object_child_foreach_recursive(object_get_root(),
+ qdev_assert_realized_properly_cb, NULL);
+}
+
+bool qdev_machine_modified(void)
+{
+ return qdev_hot_added || qdev_hot_removed;
+}
+
+BusState *qdev_get_parent_bus(DeviceState *dev)
+{
+ return dev->parent_bus;
+}
+
+BusState *qdev_get_child_bus(DeviceState *dev, const char *name)
+{
+ BusState *bus;
+ Object *child = object_resolve_path_component(OBJECT(dev), name);
+
+ bus = (BusState *)object_dynamic_cast(child, TYPE_BUS);
+ if (bus) {
+ return bus;
+ }
+
+ QLIST_FOREACH(bus, &dev->child_bus, sibling) {
+ if (strcmp(name, bus->name) == 0) {
+ return bus;
+ }
+ }
+ return NULL;
+}
+
+int qdev_walk_children(DeviceState *dev,
+ qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
+ qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
+ void *opaque)
+{
+ BusState *bus;
+ int err;
+
+ if (pre_devfn) {
+ err = pre_devfn(dev, opaque);
+ if (err) {
+ return err;
+ }
+ }
+
+ QLIST_FOREACH(bus, &dev->child_bus, sibling) {
+ err = qbus_walk_children(bus, pre_devfn, pre_busfn,
+ post_devfn, post_busfn, opaque);
+ if (err < 0) {
+ return err;
+ }
+ }
+
+ if (post_devfn) {
+ err = post_devfn(dev, opaque);
+ if (err) {
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+DeviceState *qdev_find_recursive(BusState *bus, const char *id)
+{
+ BusChild *kid;
+ DeviceState *ret;
+ BusState *child;
+
+ WITH_RCU_READ_LOCK_GUARD() {
+ QTAILQ_FOREACH_RCU(kid, &bus->children, sibling) {
+ DeviceState *dev = kid->child;
+
+ if (dev->id && strcmp(dev->id, id) == 0) {
+ return dev;
+ }
+
+ QLIST_FOREACH(child, &dev->child_bus, sibling) {
+ ret = qdev_find_recursive(child, id);
+ if (ret) {
+ return ret;
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+char *qdev_get_dev_path(DeviceState *dev)
+{
+ BusClass *bc;
+
+ if (!dev || !dev->parent_bus) {
+ return NULL;
+ }
+
+ bc = BUS_GET_CLASS(dev->parent_bus);
+ if (bc->get_dev_path) {
+ return bc->get_dev_path(dev);
+ }
+
+ return NULL;
+}
+
+static bool device_get_realized(Object *obj, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ return dev->realized;
+}
+
+static bool check_only_migratable(Object *obj, Error **errp)
+{
+ DeviceClass *dc = DEVICE_GET_CLASS(obj);
+
+ if (!vmstate_check_only_migratable(dc->vmsd)) {
+ error_setg(errp, "Device %s is not migratable, but "
+ "--only-migratable was specified",
+ object_get_typename(obj));
+ return false;
+ }
+
+ return true;
+}
+
+static void device_set_realized(Object *obj, bool value, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ DeviceClass *dc = DEVICE_GET_CLASS(dev);
+ HotplugHandler *hotplug_ctrl;
+ BusState *bus;
+ NamedClockList *ncl;
+ Error *local_err = NULL;
+ bool unattached_parent = false;
+ static int unattached_count;
+
+ if (dev->hotplugged && !dc->hotpluggable) {
+ error_setg(errp, QERR_DEVICE_NO_HOTPLUG, object_get_typename(obj));
+ return;
+ }
+
+ if (value && !dev->realized) {
+ if (!check_only_migratable(obj, errp)) {
+ goto fail;
+ }
+
+ if (!obj->parent) {
+ gchar *name = g_strdup_printf("device[%d]", unattached_count++);
+
+ object_property_add_child(container_get(qdev_get_machine(),
+ "/unattached"),
+ name, obj);
+ unattached_parent = true;
+ g_free(name);
+ }
+
+ hotplug_ctrl = qdev_get_hotplug_handler(dev);
+ if (hotplug_ctrl) {
+ hotplug_handler_pre_plug(hotplug_ctrl, dev, &local_err);
+ if (local_err != NULL) {
+ goto fail;
+ }
+ }
+
+ if (dc->realize) {
+ dc->realize(dev, &local_err);
+ if (local_err != NULL) {
+ goto fail;
+ }
+ }
+
+ DEVICE_LISTENER_CALL(realize, Forward, dev);
+
+ /*
+ * always free/re-initialize here since the value cannot be cleaned up
+ * in device_unrealize due to its usage later on in the unplug path
+ */
+ g_free(dev->canonical_path);
+ dev->canonical_path = object_get_canonical_path(OBJECT(dev));
+ QLIST_FOREACH(ncl, &dev->clocks, node) {
+ if (ncl->alias) {
+ continue;
+ } else {
+ clock_setup_canonical_path(ncl->clock);
+ }
+ }
+
+ if (qdev_get_vmsd(dev)) {
+ if (vmstate_register_with_alias_id(VMSTATE_IF(dev),
+ VMSTATE_INSTANCE_ID_ANY,
+ qdev_get_vmsd(dev), dev,
+ dev->instance_id_alias,
+ dev->alias_required_for_version,
+ &local_err) < 0) {
+ goto post_realize_fail;
+ }
+ }
+
+ /*
+ * Clear the reset state, in case the object was previously unrealized
+ * with a dirty state.
+ */
+ resettable_state_clear(&dev->reset);
+
+ QLIST_FOREACH(bus, &dev->child_bus, sibling) {
+ if (!qbus_realize(bus, errp)) {
+ goto child_realize_fail;
+ }
+ }
+ if (dev->hotplugged) {
+ /*
+ * Reset the device, as well as its subtree which, at this point,
+ * should be realized too.
+ */
+ resettable_assert_reset(OBJECT(dev), RESET_TYPE_COLD);
+ resettable_change_parent(OBJECT(dev), OBJECT(dev->parent_bus),
+ NULL);
+ resettable_release_reset(OBJECT(dev), RESET_TYPE_COLD);
+ }
+ dev->pending_deleted_event = false;
+
+ if (hotplug_ctrl) {
+ hotplug_handler_plug(hotplug_ctrl, dev, &local_err);
+ if (local_err != NULL) {
+ goto child_realize_fail;
+ }
+ }
+
+ qatomic_store_release(&dev->realized, value);
+
+ } else if (!value && dev->realized) {
+
+ /*
+ * Change the value so that any concurrent users are aware
+ * that the device is going to be unrealized
+ *
+ * TODO: change .realized property to enum that states
+ * each phase of the device realization/unrealization
+ */
+
+ qatomic_set(&dev->realized, value);
+ /*
+ * Ensure that concurrent users see this update prior to
+ * any other changes done by unrealize.
+ */
+ smp_wmb();
+
+ QLIST_FOREACH(bus, &dev->child_bus, sibling) {
+ qbus_unrealize(bus);
+ }
+ if (qdev_get_vmsd(dev)) {
+ vmstate_unregister(VMSTATE_IF(dev), qdev_get_vmsd(dev), dev);
+ }
+ if (dc->unrealize) {
+ dc->unrealize(dev);
+ }
+ dev->pending_deleted_event = true;
+ DEVICE_LISTENER_CALL(unrealize, Reverse, dev);
+ }
+
+ assert(local_err == NULL);
+ return;
+
+child_realize_fail:
+ QLIST_FOREACH(bus, &dev->child_bus, sibling) {
+ qbus_unrealize(bus);
+ }
+
+ if (qdev_get_vmsd(dev)) {
+ vmstate_unregister(VMSTATE_IF(dev), qdev_get_vmsd(dev), dev);
+ }
+
+post_realize_fail:
+ g_free(dev->canonical_path);
+ dev->canonical_path = NULL;
+ if (dc->unrealize) {
+ dc->unrealize(dev);
+ }
+
+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--;
+ }
+}
+
+static bool device_get_hotpluggable(Object *obj, Error **errp)
+{
+ DeviceClass *dc = DEVICE_GET_CLASS(obj);
+ DeviceState *dev = DEVICE(obj);
+
+ return dc->hotpluggable && (dev->parent_bus == NULL ||
+ qbus_is_hotpluggable(dev->parent_bus));
+}
+
+static bool device_get_hotplugged(Object *obj, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+
+ return dev->hotplugged;
+}
+
+static void device_initfn(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+
+ if (phase_check(PHASE_MACHINE_READY)) {
+ dev->hotplugged = 1;
+ qdev_hot_added = true;
+ }
+
+ dev->instance_id_alias = -1;
+ dev->realized = false;
+ dev->allow_unplug_during_migration = false;
+
+ QLIST_INIT(&dev->gpios);
+ QLIST_INIT(&dev->clocks);
+}
+
+static void device_post_init(Object *obj)
+{
+ /*
+ * Note: ordered so that the user's global properties take
+ * precedence.
+ */
+ object_apply_compat_props(obj);
+ qdev_prop_set_globals(DEVICE(obj));
+}
+
+/* Unlink device from bus and free the structure. */
+static void device_finalize(Object *obj)
+{
+ NamedGPIOList *ngl, *next;
+
+ DeviceState *dev = DEVICE(obj);
+
+ QLIST_FOREACH_SAFE(ngl, &dev->gpios, node, next) {
+ QLIST_REMOVE(ngl, node);
+ qemu_free_irqs(ngl->in, ngl->num_in);
+ g_free(ngl->name);
+ g_free(ngl);
+ /* ngl->out irqs are owned by the other end and should not be freed
+ * here
+ */
+ }
+
+ qdev_finalize_clocklist(dev);
+
+ /* Only send event if the device had been completely realized */
+ if (dev->pending_deleted_event) {
+ g_assert(dev->canonical_path);
+
+ qapi_event_send_device_deleted(!!dev->id, dev->id, dev->canonical_path);
+ g_free(dev->canonical_path);
+ dev->canonical_path = NULL;
+ }
+
+ qobject_unref(dev->opts);
+ g_free(dev->id);
+}
+
+static void device_class_base_init(ObjectClass *class, void *data)
+{
+ DeviceClass *klass = DEVICE_CLASS(class);
+
+ /* We explicitly look up properties in the superclasses,
+ * so do not propagate them to the subclasses.
+ */
+ klass->props_ = NULL;
+}
+
+static void device_unparent(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+ BusState *bus;
+
+ if (dev->realized) {
+ qdev_unrealize(dev);
+ }
+ while (dev->num_child_bus) {
+ bus = QLIST_FIRST(&dev->child_bus);
+ object_unparent(OBJECT(bus));
+ }
+ if (dev->parent_bus) {
+ bus_remove_child(dev->parent_bus, dev);
+ object_unref(OBJECT(dev->parent_bus));
+ dev->parent_bus = NULL;
+ }
+}
+
+static char *
+device_vmstate_if_get_id(VMStateIf *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+
+ return qdev_get_dev_path(dev);
+}
+
+/**
+ * device_phases_reset:
+ * Transition reset method for devices to allow moving
+ * smoothly from legacy reset method to multi-phases
+ */
+static void device_phases_reset(DeviceState *dev)
+{
+ ResettableClass *rc = RESETTABLE_GET_CLASS(dev);
+
+ if (rc->phases.enter) {
+ rc->phases.enter(OBJECT(dev), RESET_TYPE_COLD);
+ }
+ if (rc->phases.hold) {
+ rc->phases.hold(OBJECT(dev));
+ }
+ if (rc->phases.exit) {
+ rc->phases.exit(OBJECT(dev));
+ }
+}
+
+static void device_transitional_reset(Object *obj)
+{
+ DeviceClass *dc = DEVICE_GET_CLASS(obj);
+
+ /*
+ * This will call either @device_phases_reset (for multi-phases transitioned
+ * devices) or a device's specific method for not-yet transitioned devices.
+ * In both case, it does not reset children.
+ */
+ if (dc->reset) {
+ dc->reset(DEVICE(obj));
+ }
+}
+
+/**
+ * device_get_transitional_reset:
+ * check if the device's class is ready for multi-phase
+ */
+static ResettableTrFunction device_get_transitional_reset(Object *obj)
+{
+ DeviceClass *dc = DEVICE_GET_CLASS(obj);
+ if (dc->reset != device_phases_reset) {
+ /*
+ * dc->reset has been overridden by a subclass,
+ * the device is not ready for multi phase yet.
+ */
+ return device_transitional_reset;
+ }
+ return NULL;
+}
+
+static void device_class_init(ObjectClass *class, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(class);
+ VMStateIfClass *vc = VMSTATE_IF_CLASS(class);
+ ResettableClass *rc = RESETTABLE_CLASS(class);
+
+ class->unparent = device_unparent;
+
+ /* by default all devices were considered as hotpluggable,
+ * so with intent to check it in generic qdev_unplug() /
+ * device_set_realized() functions make every device
+ * hotpluggable. Devices that shouldn't be hotpluggable,
+ * should override it in their class_init()
+ */
+ dc->hotpluggable = true;
+ dc->user_creatable = true;
+ vc->get_id = device_vmstate_if_get_id;
+ rc->get_state = device_get_reset_state;
+ rc->child_foreach = device_reset_child_foreach;
+
+ /*
+ * @device_phases_reset is put as the default reset method below, allowing
+ * to do the multi-phase transition from base classes to leaf classes. It
+ * allows a legacy-reset Device class to extend a multi-phases-reset
+ * Device class for the following reason:
+ * + If a base class B has been moved to multi-phase, then it does not
+ * override this default reset method and may have defined phase methods.
+ * + A child class C (extending class B) which uses
+ * device_class_set_parent_reset() (or similar means) to override the
+ * reset method will still work as expected. @device_phases_reset function
+ * will be registered as the parent reset method and effectively call
+ * parent reset phases.
+ */
+ dc->reset = device_phases_reset;
+ rc->get_transitional_function = device_get_transitional_reset;
+
+ object_class_property_add_bool(class, "realized",
+ device_get_realized, device_set_realized);
+ object_class_property_add_bool(class, "hotpluggable",
+ device_get_hotpluggable, NULL);
+ object_class_property_add_bool(class, "hotplugged",
+ device_get_hotplugged, NULL);
+ object_class_property_add_link(class, "parent_bus", TYPE_BUS,
+ offsetof(DeviceState, parent_bus), NULL, 0);
+}
+
+void device_class_set_parent_reset(DeviceClass *dc,
+ DeviceReset dev_reset,
+ DeviceReset *parent_reset)
+{
+ *parent_reset = dc->reset;
+ dc->reset = dev_reset;
+}
+
+void device_class_set_parent_realize(DeviceClass *dc,
+ DeviceRealize dev_realize,
+ DeviceRealize *parent_realize)
+{
+ *parent_realize = dc->realize;
+ dc->realize = dev_realize;
+}
+
+void device_class_set_parent_unrealize(DeviceClass *dc,
+ DeviceUnrealize dev_unrealize,
+ DeviceUnrealize *parent_unrealize)
+{
+ *parent_unrealize = dc->unrealize;
+ dc->unrealize = dev_unrealize;
+}
+
+void device_legacy_reset(DeviceState *dev)
+{
+ DeviceClass *klass = DEVICE_GET_CLASS(dev);
+
+ trace_qdev_reset(dev, object_get_typename(OBJECT(dev)));
+ if (klass->reset) {
+ klass->reset(dev);
+ }
+}
+
+Object *qdev_get_machine(void)
+{
+ static Object *dev;
+
+ if (dev == NULL) {
+ dev = container_get(object_get_root(), "/machine");
+ }
+
+ return dev;
+}
+
+static MachineInitPhase machine_phase;
+
+bool phase_check(MachineInitPhase phase)
+{
+ return machine_phase >= phase;
+}
+
+void phase_advance(MachineInitPhase phase)
+{
+ assert(machine_phase == phase - 1);
+ machine_phase = phase;
+}
+
+static const TypeInfo device_type_info = {
+ .name = TYPE_DEVICE,
+ .parent = TYPE_OBJECT,
+ .instance_size = sizeof(DeviceState),
+ .instance_init = device_initfn,
+ .instance_post_init = device_post_init,
+ .instance_finalize = device_finalize,
+ .class_base_init = device_class_base_init,
+ .class_init = device_class_init,
+ .abstract = true,
+ .class_size = sizeof(DeviceClass),
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_VMSTATE_IF },
+ { TYPE_RESETTABLE_INTERFACE },
+ { }
+ }
+};
+
+static void qdev_register_types(void)
+{
+ type_register_static(&device_type_info);
+}
+
+type_init(qdev_register_types)