| /* | |
| * 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 "qobject/qdict.h" | |
| #include "qapi/visitor.h" | |
| #include "qemu/error-report.h" | |
| #include "qemu/option.h" | |
| #include "qom/compat-properties.h" | |
| #include "hw/core/irq.h" | |
| #include "hw/core/qdev-properties.h" | |
| #include "hw/core/boards.h" | |
| #include "hw/core/sysbus.h" | |
| #include "hw/core/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) | |
| { | |
| return DEVICE(object_new(name)); | |
| } | |
| DeviceState *qdev_try_new(const char *name) | |
| { | |
| ObjectClass *oc = module_object_class_by_name(name); | |
| if (!oc) { | |
| return NULL; | |
| } | |
| return DEVICE(object_new_with_class(oc)); | |
| } | |
| 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; | |
| } | |
| 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) | |
| { | |
| static int unattached_count; | |
| bool unattached_parent = false; | |
| assert(!dev->parent_bus); | |
| if (!OBJECT(dev)->parent) { | |
| gchar *name = g_strdup_printf("device[%d]", unattached_count++); | |
| object_property_add_child(machine_get_container("unattached"), | |
| name, OBJECT(dev)); | |
| unattached_parent = true; | |
| g_free(name); | |
| } | |
| if (bus) { | |
| if (!qdev_set_parent_bus(dev, bus, errp)) { | |
| goto fail; | |
| } | |
| } else { | |
| assert(!DEVICE_GET_CLASS(dev)->bus_type); | |
| } | |
| if (object_property_set_bool(OBJECT(dev), "realized", true, errp)) { | |
| return true; | |
| } | |
| fail: | |
| if (unattached_parent) { | |
| /* | |
| * Beware, this doesn't just revert | |
| * object_property_add_child(), it also runs bus_remove()! | |
| */ | |
| object_unparent(OBJECT(dev)); | |
| unattached_count--; | |
| } | |
| return false; | |
| } | |
| 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(const 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; | |
| } | |
| char *qdev_get_human_name(DeviceState *dev) | |
| { | |
| if (dev->id) { | |
| return g_strdup(dev->id); | |
| } | |
| /* | |
| * Fall back to a bus-specific device path, if the bus | |
| * provides one (e.g. "PCI device 0000:00:04.0"). | |
| */ | |
| g_autofree char *path = qdev_get_dev_path(dev); | |
| if (path) { | |
| const char *bus_type = object_get_typename(OBJECT(dev->parent_bus)); | |
| char *name = g_strdup_printf("%s device %s", bus_type, path); | |
| return name; | |
| } | |
| return object_get_canonical_path(OBJECT(dev)); | |
| } | |
| void qdev_add_unplug_blocker(DeviceState *dev, Error *reason) | |
| { | |
| dev->unplug_blockers = g_slist_prepend(dev->unplug_blockers, reason); | |
| } | |
| void qdev_del_unplug_blocker(DeviceState *dev, Error *reason) | |
| { | |
| dev->unplug_blockers = g_slist_remove(dev->unplug_blockers, reason); | |
| } | |
| bool qdev_unplug_blocked(DeviceState *dev, Error **errp) | |
| { | |
| if (dev->unplug_blockers) { | |
| error_propagate(errp, error_copy(dev->unplug_blockers->data)); | |
| return true; | |
| } | |
| return false; | |
| } | |
| 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); | |
| const HotplugHandler *hotplug_ctrl; | |
| BusState *bus; | |
| NamedClockList *ncl; | |
| Error *local_err = NULL; | |
| if (dev->hotplugged && !dc->hotpluggable) { | |
| error_setg(errp, "Device '%s' does not support hotplugging", | |
| object_get_typename(obj)); | |
| return; | |
| } | |
| if (value && !dev->realized) { | |
| if (!check_only_migratable(obj, errp)) { | |
| goto fail; | |
| } | |
| 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); | |
| } | |
| 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); | |
| g_assert(!dev->unplug_blockers); | |
| 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->canonical_path); | |
| g_free(dev->canonical_path); | |
| dev->canonical_path = NULL; | |
| } | |
| g_free(dev->id); | |
| } | |
| static void device_class_base_init(ObjectClass *class, const 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; | |
| klass->props_count_ = 0; | |
| } | |
| 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); | |
| } | |
| static void device_class_init(ObjectClass *class, const 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; | |
| /* | |
| * A NULL legacy_reset implies a three-phase reset device. Devices can | |
| * only be reset using three-phase aware mechanisms, but we still support | |
| * for transitional purposes leaf classes which set the old legacy_reset | |
| * method via device_class_set_legacy_reset(). | |
| */ | |
| dc->legacy_reset = NULL; | |
| 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); | |
| } | |
| static void do_legacy_reset(Object *obj, ResetType type) | |
| { | |
| DeviceClass *dc = DEVICE_GET_CLASS(obj); | |
| dc->legacy_reset(DEVICE(obj)); | |
| } | |
| void device_class_set_legacy_reset(DeviceClass *dc, DeviceReset dev_reset) | |
| { | |
| /* | |
| * A legacy DeviceClass::reset has identical semantics to the | |
| * three-phase "hold" method, with no "enter" or "exit" | |
| * behaviour. Classes that use this legacy function must be leaf | |
| * classes that do not chain up to their parent class reset. | |
| * There is no mechanism for resetting a device that does not | |
| * use the three-phase APIs, so the only place which calls | |
| * the legacy_reset hook is do_legacy_reset(). | |
| */ | |
| ResettableClass *rc = RESETTABLE_CLASS(dc); | |
| rc->phases.enter = NULL; | |
| rc->phases.hold = do_legacy_reset; | |
| rc->phases.exit = NULL; | |
| dc->legacy_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; | |
| } | |
| Object *qdev_get_machine(void) | |
| { | |
| static Object *dev; | |
| if (dev == NULL) { | |
| dev = object_resolve_path_component(object_get_root(), "machine"); | |
| /* | |
| * Any call to this function before machine is created is treated | |
| * as a programming error as of now. | |
| */ | |
| assert(dev); | |
| } | |
| return dev; | |
| } | |
| Object *machine_get_container(const char *name) | |
| { | |
| Object *container, *machine; | |
| machine = qdev_get_machine(); | |
| container = object_resolve_path_component(machine, name); | |
| assert(object_dynamic_cast(container, TYPE_CONTAINER)); | |
| return container; | |
| } | |
| 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 = (const InterfaceInfo[]) { | |
| { TYPE_VMSTATE_IF }, | |
| { TYPE_RESETTABLE_INTERFACE }, | |
| { } | |
| } | |
| }; | |
| static void qdev_register_types(void) | |
| { | |
| type_register_static(&device_type_info); | |
| } | |
| type_init(qdev_register_types) |