Timeline
Timeline
2025-11-21
- init
This article introduces the basic concepts and implementation principles of the QEMU Object Model (QOM), and analyzes its mechanism for implementing object-oriented features (inheritance, encapsulation, polymorphism) based on pure C language. Taking the edu device in QEMU version 10.1.2 as an example, it explains in detail the operation process of QOM, including three stages: type registration, type initialization, and object initialization. In the type registration stage, the device type is defined through TypeInfo, with the help of DECLARE_INSTANCE_CHECKER macro to implement safe type conversion, and through DEFINE_TYPES and type_The init macro mounts the type registration function into the module initialization linked list; before the main function executes, the GCC constructor feature is used to automatically perform registration, and then in main, through module_call_init uniformly calls the registration function to complete the registration of TypeImpl in the global hash table. In the type initialization stage, the article explains how the type_initialize function initializes TypeImpl into a usable class object, and handles parent type inheritance and interface implementation, and finally
Environment
Source code
123456 | wget https://download.qemu.org/qemu-10.1.2.tar.xztar xvJf qemu-10.1.2.tar.xzcd qemu-10.1.2mkdir -p output./configure --prefix=$PWD/output --target-list=aarch64-softmmu,riscv64-softmmu --enable-debugbear -- make -j$(nproc) |
Create .clangd
123 | CompileFlags: Add: -Wno-unknown-warning-option Remove: [-m*, -f*] |
gdb
1 | gdb -args ./build/qemu-system-riscv64 -M virt -device edu,id=edu1 -nographic |
QOM
The full name of QOM is QEMU Object Model, which is an abstraction layer implemented by QEMU using object-oriented thinking, used to organize various components in QEMU (such as device emulation, backend components MemoryRegion, Machine, etc.). It is similar to C++ classes, but QOM is implemented in pure C language.
The object-oriented features supported by QOM are:inheritance, encapsulation, polymorphism。
The operation process of QOM consists of three parts:
- Type registration
- Type initialization
- Object initialization
1234567 | |--类型注册 ---> type_init() | register_module_init() | type_register()QOM-|--类型的初始化 ---> type_initialize() |--对象的初始化 ---> object_new() | object_initialize() | object_initialize_with_type() |
Based on object-oriented modeling ideas, QEMU provides a very formatted and routine hardware modeling process. For beginners, it is only necessary to master the common basic interfaces of QOM to smoothly carry out modeling work, without needing to delve into internal principles.
We need to understand three points:
- How the device model is defined;
- How QEMU instantiates devices during the loading phase;
- How different devices are connected (communicate) with each other.
Source code analysis of edu device modeling
QEMU 10.1.2 version source code
Type registration
EduStateDefinition
hw/misc/edu.c
12345678910111213141516171819202122232425262728293031323334353637383940414243 | typedef struct EduState EduState;DECLARE_INSTANCE_CHECKER(EduState, EDU, TYPE_PCI_EDU_DEVICE)struct EduState { PCIDevice pdev; MemoryRegion mmio; QemuThread thread; QemuMutex thr_mutex; QemuCond thr_cond; bool stopping; uint32_t addr4; uint32_t fact; uint32_t status; uint32_t irq_status; struct dma_state { dma_addr_t src; dma_addr_t dst; dma_addr_t cnt; dma_addr_t cmd; } dma; QEMUTimer dma_timer; char dma_buf[DMA_SIZE]; uint64_t dma_mask;}; |
DECLARE_INSTANCE_CHECKERGenerateEDU(obj)a macro like this, used to safely convertQObjectconverted toEduState *TYPE_PCI_EDU_DEVICEDefine a QOM (QEMU Object Model) type name"edu"EduStateis the state structure of this device
This is the standard way QEMU creates a device type.
DECLARE_INSTANCE_CHECKER
DECLARE_INSTANCE_CHECKER implements a type-safe conversion function similar to dynamic_cast<EduState *>(obj) in C++.
include/qom/object.h
12345678910111213141516171819202122232425262728293031323334 | /** * DECLARE_INSTANCE_CHECKER: * @InstanceType: instance struct name * @OBJ_NAME: the object name in uppercase with underscore separators * @TYPENAME: type name * * Direct usage of this macro should be avoided, and the complete * OBJECT_DECLARE_TYPE macro is recommended instead. * * This macro will provide the instance type cast functions for a * QOM type. *//** * OBJECT_CHECK: * @type: The C type to use for the return value. * @obj: A derivative of @type to cast. * @name: The QOM typename of @type * * A type safe version of @object_dynamic_cast_assert. Typically each class * will define a macro based on this type to perform type safe dynamic_casts to * this object type. * * If an invalid object is passed to this function, a run time assert will be * generated. */ |
Therefore,
DECLARE_INSTANCE_CHECKER(EduState, EDU, TYPE_PCI_EDU_DEVICE)
The macro will generate:
1234 | static inline EduState *EDU(const void *obj){ return OBJECT_CHECK(EduState, obj, "edu");} |
- It generates a type-safe cast function
- The name of the function is the second parameter:
EDU() - Its function is: convert any
QObjectto your instance typeEduState *, and perform type checking. - OBJECT_CHECK is similar to the C language version of dynamic_cast_cast<EduState *>(obj)
TypeInfo
Define the information for the edu type.
hw/misc/edu.c
123456789101112131415 | static const TypeInfo edu_types[] = { { .name = TYPE_PCI_EDU_DEVICE,//the device's type name .parent = TYPE_PCI_DEVICE,//It inherits from the PCI device. .instance_size = sizeof(EduState),//The instance size is EduState. .instance_init = edu_instance_init,//instance initialization function .class_init = edu_class_init,//class initialization function .interfaces = (const InterfaceInfo[]) {//Implement the PCI device interface { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, }};DEFINE_TYPES(edu_types) |
The definition in interfaces indicates that EDU implementsConventionalPciDeviceinterface, and can be recognized by the PCI bus.
DEFINE_TYPES
DEFINE_TYPESThis registers the type, and its macro definition is as follows:
include/qom/object.h
123456 |
It definesdo_qemu_init_##type_arrayThis function will calltype_register_static_array
simultaneouslytype_initIt is also a macro that defines a function.
type_init
defined ininclude/qemu/module.h, it will call module_init(function, MODULE_INIT_QOM)
123456789101112131415161718192021222324252627 | // type init definitiontypedef enum { MODULE_INIT_MIGRATION, MODULE_INIT_BLOCK, MODULE_INIT_OPTS, MODULE_INIT_QOM, MODULE_INIT_TRACE, MODULE_INIT_XEN_BACKEND, MODULE_INIT_LIBQOS, MODULE_INIT_FUZZ_TARGET, MODULE_INIT_MAX} module_init_type; |
module_init
and type_init calls module_init, whose first parameter isdo_qemu_init_edu_types(the above DEFINE_TYPES macro-generated function will call type_register_static_array), the second parameter isMODULE_INIT_QOM, indicating the QOM initialization type
include/qemu/module.hThe module_init macro in it is as follows
12345678 | // module_init |
_attribute__((constructor))is the key
This GCC attribute means:This function is automatically executed before main() is called.。
register_module_init
register_module_The definition of init is as follows:
12345678910111213141516171819202122232425262728293031323334 | typedef struct ModuleEntry{ void (*init)(void); QTAILQ_ENTRY(ModuleEntry) node; module_init_type type;} ModuleEntry;typedef QTAILQ_HEAD(, ModuleEntry) ModuleTypeList;static ModuleTypeList init_type_list[MODULE_INIT_MAX];static bool modules_init_done[MODULE_INIT_MAX];static ModuleTypeList dso_init_list;static ModuleTypeList *find_type(module_init_type type){ init_lists(); return &init_type_list[type];}void register_module_init(void (*fn)(void), module_init_type type){ ModuleEntry *e; ModuleTypeList *l; e = g_malloc0(sizeof(*e)); e->init = fn; e->type = type; l = find_type(type); QTAILQ_INSERT_TAIL(l, e, node);} |
register_module_init() takesthe type’s initialization function, and the owning type (for QOM types, MODULE_INIT_QOM) to construct a ModuleEntry, then insert it into the linked list to which the corresponding module belongs; the linked lists of all modules are stored in ainit_type_listarray.
1234567891011121314151617 | pci_edu_register_types ^ | vmxnet3_register_types | ^ +---+ | intc_register_types init_type_list | | ^ +--------------------+ | +--------+ +--------------+| MODULE_INIT_BLOCK | | | |+--------------------+ | +------+ | +------+ | +------+| MODULE_INIT_OPTS | +-----+ init | +-----+ init | +--+ init |+--------------------+ +------+ +------+ +------+| MODULE_INIT_QOM +-------->+ node +-------->+ node +------>+ node |+--------------------+ +------+ +------+ +------+| MODULE_INIT_TRACE | | type | | type | | type |+--------------------+ +------+ +------+ +------+| ... |+--------------------+ |
Therefore, before the main function is executed, the initialization functions of the various types used by QEMU are uniformly registered ininit_type_list[MODULE_INIT_QOM]this linked list (through the__attribute__((constructor))mechanism).
type_register_static_array
DEFINE_The type defined by TYPES_init ultimately calls type_register_static_array(type_array, ARRAY_SIZE(type_array)), it will call type for each TypeInfo_register_static, and type_register_static will call type_register_internal, that is, type registration ultimately calls the core function
type_register_internal()。
qom/object.c
1234567891011121314151617181920212223242526272829303132333435 | void type_register_static_array(const TypeInfo *infos, int nr_infos){ int i; for (i = 0; i < nr_infos; i++) { type_register_static(&infos[i]); }}TypeImpl *type_register_static(const TypeInfo *info){ assert(info->parent); return type_register_internal(info);}static TypeImpl *type_register_internal(const TypeInfo *info){ TypeImpl *ti; if (!type_name_is_valid(info->name)) { fprintf(stderr, "Registering '%s' with illegal type name\n", info->name); abort(); } ti = type_new(info); type_table_add(ti); return ti;}static void type_table_add(TypeImpl *ti){ assert(!enumerating_types); g_hash_table_insert(type_table_get(), (void *)ti->name, ti);} |
The key one is type_table_add(ti), which adds the new type to the QOM global type hash table, and later can be used through object_new(“edu”) or OBJECT_CHECK, returning TypeImpl* to the upper layer for use.
TypeImpl
TypeImpl stores all the information about the type, defined as follows
qom/object.c
123456789101112131415161718192021222324252627282930313233343536 | typedef struct InterfaceImpl InterfaceImpl;typedef struct TypeImpl TypeImpl;struct InterfaceImpl{ const char *typename;};struct TypeImpl{ const char *name; size_t class_size; size_t instance_size; size_t instance_align; void (*class_init)(ObjectClass *klass, const void *data); void (*class_base_init)(ObjectClass *klass, const void *data); const void *class_data; void (*instance_init)(Object *obj); void (*instance_post_init)(Object *obj); void (*instance_finalize)(Object *obj); bool abstract; const char *parent; TypeImpl *parent_type; ObjectClass *class; int num_interfaces; InterfaceImpl interfaces[MAX_INTERFACES];}; |
Register TypeImpl in the main function
Soon after entering the main function, with MODULE_INIT_QOM as the parameter, it calls the function module_call_init, which executesinit_type_list[MODULE_INIT_QOM]the init function of each ModuleEntry on the linked list.
utils/module.c
123456789101112131415161718 | void module_call_init(module_init_type type){ ModuleTypeList *l; ModuleEntry *e; if (modules_init_done[type]) { return; } l = find_type(type); QTAILQ_FOREACH(e, l, node) { e->init(); } modules_init_done[type] = true;} |
system/main.c
1234567891011121314151617 | int main(int argc, char **argv){ qemu_init(argc, argv); bql_unlock(); replay_mutex_unlock(); if (qemu_main) { QemuThread main_loop_thread; qemu_thread_create(&main_loop_thread, "qemu_main", qemu_default_main, NULL, QEMU_THREAD_DETACHED); return qemu_main(); } else { qemu_default_main(NULL); g_assert_not_reached(); }} |
Distinguish the following two concepts:
| Concept | When it occurs | Function |
|---|---|---|
| Registration (register) | ELF loading phase (constructor execution) | Add the init function corresponding to the type toinit_type_listlinked list |
| Initialization (init) | main() callqemu_init_subsystems()→module_call_init(MODULE_INIT_QOM) | Traverse the linked list, call each init function, and complete the actual TypeInfo registration (type_register_static_array), adding each TypeImpl to the global hash table. |

Type initialization
Earlier, type registration has been completed. We have all TypeImpls of QOM registered in the global hash table, but the initialization of classes (types) is not yet complete.
Class initialization is accomplished by type_initialize(). The function’s input is a TypeImpl pointer ti representing type information.
type_initialize actually executes the class initialization logic, while type_register_internal just creates a TypeImpl and adds it to the global table.
The specific functions are as follows:
type_initialize
qom/object.c
type_initializeIts main task is toTypeImplobject (TypeImpl created during the registration phase)actually initialize it into a usable class object, and handle inheritance relationships and interfaces.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788 | static void type_initialize(TypeImpl *ti){ TypeImpl *parent; if (ti->class) {//to avoid repeated initialization return; } // Calculate the size of the class object class_size ti->class_size = type_class_get_size(ti); // Calculate the size of the instance object instance_size ti->instance_size = type_object_get_size(ti); // Calculate the instance alignment instance_align ti->instance_align = type_object_get_align(ti); /* Any type with zero instance_size is implicitly abstract. * This means interface types are all abstract. */ if (ti->instance_size == 0) {// If instance_size is 0, it indicates an abstract type; interface types are also abstract types and have no instances. ti->abstract = true; } if (type_is_ancestor(ti, type_interface)) { // Perform strict checks on interface types to ensure there are no instance initialization functions or instance-related properties. assert(ti->instance_size == 0); assert(ti->abstract); assert(!ti->instance_init); assert(!ti->instance_post_init); assert(!ti->instance_finalize); assert(!ti->num_interfaces); } ti->class = g_malloc0(ti->class_size);//Allocate memory for the class object (class) to store information such as the virtual function table, property table, and interface list. parent = type_get_parent(ti); if (parent) { type_initialize(parent);//Recursively initialize the parent class GSList *e; int i; g_assert(parent->class_size <= ti->class_size); g_assert(parent->instance_size <= ti->instance_size); memcpy(ti->class, parent->class, parent->class_size);//Inherit parent class fields ti->class->interfaces = NULL; for (e = parent->class->interfaces; e; e = e->next) {//Initialize parent class interfaces InterfaceClass *iface = e->data; ObjectClass *klass = OBJECT_CLASS(iface); type_initialize_interface(ti, iface->interface_type, klass->type); } for (i = 0; i < ti->num_interfaces; i++) {//Initialize current class interfaces: TypeImpl *t = type_get_by_name_noload(ti->interfaces[i].typename); if (!t) { error_report("missing interface '%s' for object '%s'", ti->interfaces[i].typename, parent->name); abort(); } for (e = ti->class->interfaces; e; e = e->next) { TypeImpl *target_type = OBJECT_CLASS(e->data)->type; if (type_is_ancestor(target_type, t)) { break; } } if (e) { continue; } type_initialize_interface(ti, t, t); } } ti->class->properties = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, object_property_free);//Allocate a property hash table for the class to store property information of QOM objects (such as name, size, etc.) ti->class->type = ti;// The class object stores a pointer to its TypeImpl while (parent) { if (parent->class_base_init) { parent->class_base_init(ti->class, ti->class_data);//Call the parent class base initialization function } parent = type_get_parent(parent); } if (ti->class_init) { ti->class_init(ti->class, ti->class_data);//Call the current class's class_init }} |
Finally, it will callti->class_init, it will call the edu defined in our edu.c_class_init:
12345678910111213 | static const TypeInfo edu_types[] = { { .name = TYPE_PCI_EDU_DEVICE,//the device's type name .parent = TYPE_PCI_DEVICE,//It inherits from the PCI device. .instance_size = sizeof(EduState),//The instance size is EduState. .instance_init = edu_instance_init,//instance initialization function .class_init = edu_class_init,//class initialization function .interfaces = (const InterfaceInfo[]) {//Implement the PCI device interface { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, }}; |

Type hierarchy
From type_initialize, we can see that when a type is initialized, its parent type is also initialized. QOM implements a concept similar to inheritance in C++ through this hierarchy.
The following analysis uses the edu device as an example:
123456789101112131415161718192021222324252627 | // hw/misc/edu.c static const TypeInfo edu_info = { .name = TYPE_PCI_EDU_DEVICE, .parent = TYPE_PCI_DEVICE, ... };// hw/pci/pci.cstatic const TypeInfo pci_device_type_info = { .name = TYPE_PCI_DEVICE, .parent = TYPE_DEVICE, ...};// hw/core/qdev.cstatic const TypeInfo device_type_info = { .name = TYPE_DEVICE, .parent = TYPE_OBJECT, .class_init = device_class_init, .abstract = true, ...};// qom/object.cstatic const TypeInfo object_info = { .name = TYPE_OBJECT, .instance_size = sizeof(Object), .class_init = object_class_init, .abstract = true,}; |
The hierarchy of this edu type:
1 | TYPE_PCI_DEVICE -> TYPE_DEVICE -> TYPE_OBJECT |
From the data structure perspective:
In the type initialization function type_initialize, it calls ti->class = g_The malloc0(ti->class_size) statement allocates the class structure of the type, which actually represents the type information. It is similar to a class defined in C++.
class_size is a field of TypeImpl. If this type does not specify it, it will use the parent class’s class_size is used for initialization.
The edu device type itself does not define class_size, so it inherits the parent class’s class_size, that is,sizeof(PCIDeviceClass)。
123456789101112131415161718 | // include/hw/pci/pci_device.h (qemu v9.2.0)struct PCIDeviceClass { DeviceClass parent_class; // The first field: inherited from DeviceClass (the parent of DeviceClass is ObjectClass, the base of all types). void (*realize)(PCIDevice *dev, Error **errp); PCIUnregisterFunc *exit; PCIConfigReadFunc *config_read; PCIConfigWriteFunc *config_write; uint16_t vendor_id; uint16_t device_id; uint8_t revision; uint16_t class_id; uint16_t subsystem_vendor_id; /* only for header type = 0 */ uint16_t subsystem_id; /* only for header type = 0 */ const char *romfile; /* rom bar */}; |
Initialization of the parent class
The following is a diagram of the relationships among ObjectClass, DeviceClass, and PCIDeviceClass:
123456789101112131415 | +----------------+ +-- | | --+ | | ObjectClass | | | | | | | +----------------+ +--- DeviceClass | | | | PCIDeviceClass --+ | DeviceClass | | | | other fileds | | | | | --+ | +----------------+ | | | | | PCIDeviceClass | | | other fileds | +-- | | +----------------+ |
It can be seen that they have a containment relationship. In fact, the memory layout compiled by the compiler for C++ inheritance structures is similar to this.
The question is, when are the parent class’s member fields initialized?
qom/object.c, type_initialize()
1 | memcpy(ti->class, parent->class, parent->class_size); |
The parent class’s member fields are through thismemcpycopied into the subclass’s class.
Summary
- First, each type specifies a TypeInfo to register into the system;
- Then, when the system runtime initializes, it converts TypeInfo into TypeImpl and puts it into a hash table;
- The system initializes each type in this hash table;
- Next, according to QEMU command-line parameters, it creates corresponding instance objects.
Object Construction and Initialization
Here we analyze the object construction process, mainly implemented through the object_new function, with the call chain as follows:
1 | object_new() -> object_new_with_type() -> object_initialize_with_type() -> object_init_with_type() -> edu_instance_init() |

object_new
qom/object.c
123456 | Object *object_new(const char *typename){ TypeImpl *ti = type_get_or_load_by_name(typename, &error_fatal); return object_new_with_type(ti);} |
type_get_or_load_by_nameGet type information by the type string name, then callobject_new_with_type
object_new_with_type
qom/object.c
1234567891011121314151617181920212223242526272829 | static Object *object_new_with_type(Type type){ Object *obj; size_t size, align; void (*obj_free)(void *); g_assert(type != NULL); type_initialize(type); size = type->instance_size; align = type->instance_align; /* * Do not use qemu_memalign unless required. Depending on the * implementation, extra alignment implies extra overhead. */ if (likely(align <= __alignof__(qemu_max_align_t))) { obj = g_malloc(size); obj_free = g_free; } else { obj = qemu_memalign(align, size); obj_free = qemu_vfree; } object_initialize_with_type(obj, size, type); obj->free = obj_free; return obj;} |
Here it also callstype_initialize, previously in the gdb debugging analysisg_hash_table_foreachit was already called, so why call it again here? The reasons are as follows:
- Although
g_hash_table_foreach()It can initialize all types in advance, but not all types are traversed before machine initialization - object_new may create a type that has not yet been
object_class_foreach()traversed type - For safety, object_new must ensure that the type has been initialized
- type_initialize is idempotent:
123 | if (ti->class) {//to avoid repeated initialization return;} |
object_initialize_with_type
qom/object.c
1234567891011121314151617 | static void object_initialize_with_type(Object *obj, size_t size, TypeImpl *type){ type_initialize(type); g_assert(type->instance_size >= sizeof(Object)); g_assert(type->abstract == false); g_assert(size >= type->instance_size); memset(obj, 0, type->instance_size); obj->class = type->class; object_ref(obj); object_class_property_init_all(obj); obj->properties = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, object_property_free); object_init_with_type(obj, type); object_post_init_with_type(obj, type);} |
object_init_with_type
qom/object.c
12345678910 | static void object_init_with_type(Object *obj, TypeImpl *ti){ if (type_has_parent(ti)) { object_init_with_type(obj, type_get_parent(ti)); } if (ti->instance_init) { ti->instance_init(obj); }} |
We defined in edu.cinstance_initthe value isedu_instance_init, then here it will call ti->instance_init(obj), i.e., edu_instance_init(obj)
12345678910111213 | static const TypeInfo edu_types[] = { { .name = TYPE_PCI_EDU_DEVICE,//the device's type name .parent = TYPE_PCI_DEVICE,//It inherits from the PCI device. .instance_size = sizeof(EduState),//The instance size is EduState. .instance_init = edu_instance_init,//instance initialization function .class_init = edu_class_init,//class initialization function .interfaces = (const InterfaceInfo[]) {//Implement the PCI device interface { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, }}; |
Populate the data content of EduState
Types and objects are linked through the class field of Object:obj->class = type->class。
Object type hierarchy:
1234567891011121314151617181920 | // hw/misc/edu.cstruct EduState { PCIDevice pdev; MemoryRegion mmio;...} EduState;// include/hw/pci/pci_device.h (qemu v9.2.0)struct PCIDevice { DeviceState qdev; bool partially_hotplugged;...};// include/hw/qdev-core.hstruct DeviceState { /* private: */ Object parent_obj; /* public: */}; |
QOM object construction can be divided into 3 parts:
- Type registration: construct a TypeImpl via TypeInfo and add it to the global hash table, in main via
module_call_init()completed (the registration function itself is added to the linked list before main through the constructor mechanism); - Type initialization is performed in main; the first two stages are global, and all compiled-in QOM objects will be called;
- Object construction: construct specific instance objects; objects are created only for devices specified on the command line.
At this point, the object has only been constructed and initialized, but the data content of EduState has not yet been populated.
At this time, the edu device is still unavailable. For the device, its realized property (ObjectProperty) must also be set to true.
system/qdev-monitor.c
qdev_device_addwill callqdev_device_add_from_qdict
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 | DeviceState *qdev_device_add(QemuOpts *opts, Error **errp){ QDict *qdict = qemu_opts_to_qdict(opts, NULL); DeviceState *ret; ret = qdev_device_add_from_qdict(qdict, false, errp); if (ret) { qemu_opts_del(opts); } qobject_unref(qdict); return ret;}DeviceState *qdev_device_add_from_qdict(const QDict *opts, bool from_json, Error **errp){ ERRP_GUARD(); DeviceClass *dc; const char *driver, *path; char *id; DeviceState *dev; BusState *bus = NULL; QDict *properties; driver = qdict_get_try_str(opts, "driver"); if (!driver) { error_setg(errp, QERR_MISSING_PARAMETER, "driver"); return NULL; } /* find driver */ dc = qdev_get_device_class(&driver, errp); if (!dc) { return NULL; } /* find bus */ path = qdict_get_try_str(opts, "bus"); if (path != NULL) { bus = qbus_find(path, errp); if (!bus) { return NULL; } if (!object_dynamic_cast(OBJECT(bus), dc->bus_type)) { error_setg(errp, "Device '%s' can't go on %s bus", driver, object_get_typename(OBJECT(bus))); return NULL; } } else if (dc->bus_type != NULL) { bus = qbus_find_recursive(sysbus_get_default(), NULL, dc->bus_type); if (!bus || qbus_is_full(bus)) { error_setg(errp, "No '%s' bus found for device '%s'", dc->bus_type, driver); return NULL; } } if (qdev_should_hide_device(opts, from_json, errp)) { if (bus && !qbus_is_hotpluggable(bus)) { error_setg(errp, "Bus '%s' does not support hotplugging", bus->name); } return NULL; } else if (*errp) { return NULL; } if (migration_is_running()) { error_setg(errp, "device_add not allowed while migrating"); return NULL; } /* create device */ dev = qdev_new(driver); /* Check whether the hotplug is allowed by the machine */ if (phase_check(PHASE_MACHINE_READY) && !qdev_hotplug_allowed(dev, bus, errp)) { goto err_del_dev; } /* * set dev's parent and register its id. * If it fails it means the id is already taken. */ id = g_strdup(qdict_get_try_str(opts, "id")); if (!qdev_set_id(dev, id, errp)) { goto err_del_dev; } /* set properties */ properties = qdict_clone_shallow(opts); qdict_del(properties, "driver"); qdict_del(properties, "bus"); qdict_del(properties, "id"); object_set_properties_from_keyval(&dev->parent_obj, properties, from_json, errp); qobject_unref(properties); if (*errp) { goto err_del_dev; } if (!qdev_realize(dev, bus, errp)) { goto err_del_dev; } return dev;err_del_dev: object_unparent(OBJECT(dev)); object_unref(OBJECT(dev)); return NULL;} |
qdev_device_add_from_qdictCallqdev_realize
hw/core/qdev.c
123456789101112131415 | 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);} |
Object properties
QOM implements class-based polymorphism similar to C++, an object according to the inheritance hierarchy can be Object, DeviceState, PCIDevice, etc. In QOM, to facilitate object management, it alsoadds properties to each type and object.. Among them:
- Class properties exist in the properties field of ObjectClass and are constructed in type_initialize;
- Object properties exist in the properties field of Object; this field is in object_initialize_with_type constructed;
Both are a hash table(a mapping from property names to ObjectProperty).
ObjectProperty
Properties are represented by ObjectProperty:
include/qom/object.h
12345678910111213 | struct ObjectProperty{ char *name; char *type; char *description; ObjectPropertyAccessor *get; ObjectPropertyAccessor *set; ObjectPropertyResolve *resolve; ObjectPropertyRelease *release; ObjectPropertyInit *init; void *opaque; QObject *defval;}; |
Each specific attribute is described by a structure. Here is an example:
12345678910111213141516171819202122 | // qom/object.ctypedef struct { union { Object **targetp; Object *target; /* if OBJ_PROP_LINK_DIRECT, when holding the pointer */ ptrdiff_t offset; /* if OBJ_PROP_LINK_CLASS */ }; void (*check)(const Object *, const char *, Object *, Error **); ObjectPropertyLinkFlags flags;} LinkProperty;typedef struct StringProperty{ char *(*get)(Object *, Error **); void (*set)(Object *, const char *, Error **);} StringProperty;typedef struct BoolProperty{ bool (*get)(Object *, Error **); void (*set)(Object *, bool, Error **);} BoolProperty; |
Adding attributes is divided into adding class attributes and adding object attributes. Taking object attribute addition as an example, it is done throughobject_property_addan interface.
1234567891011121314151617181920 | +----------------+ | ... | +----------------+ | properties +-------------+------------------------------------------------- +----------------+ | | ... | +---+----+ | | | name | | | +--------+ | | | type | +----------------+ +--------+ Object | set +---> property_set_bool +--------+ | get +---> property_get_bool +--------+ | opaque +---------> +-------+ +--------+ | get +--> memfd_backend_get_seal ObjectProperty +-------+ | set +--> memfd_backend_set_seal +-------+ BoolProperty |
Introduce two special attributes:
child attribute
Describes the subordinate relationship between objects. The parent object’s child attribute points to the child object. The function for adding the child attribute is object_property_add_child:
1234567891011121314151617181920212223242526272829 | // qom/object.cObjectProperty *object_property_add_child(Object *obj, const char *name, Object *child){ return object_property_try_add_child(obj, name, child, &error_abort);}ObjectProperty *object_property_try_add_child(Object *obj, const char *name, Object *child, Error **errp){ g_autofree char *type = NULL; ObjectProperty *op; assert(!child->parent); type = g_strdup_printf("child<%s>", object_get_typename(child)); op = object_property_try_add(obj, name, type, object_get_child_property, NULL, object_finalize_child_property, child, errp); if (!op) { return NULL; } op->resolve = object_resolve_child_property; object_ref(child); child->parent = obj; return op;} |
link attribute
Represents a connection relationship, indicating that one device references another device. The function for adding the link attribute is object_property_add_link :
123456789101112131415161718192021222324252627282930313233343536373839 | // qom/object.cObjectProperty *object_property_add_link(Object *obj, const char *name, const char *type, Object **targetp, void (*check)(const Object *, const char *, Object *, Error **), ObjectPropertyLinkFlags flags){ return object_add_link_prop(obj, name, type, targetp, check, flags);}static ObjectProperty *object_add_link_prop(Object *obj, const char *name, const char *type, void *ptr, void (*check)(const Object *, const char *, Object *, Error **), ObjectPropertyLinkFlags flags){ LinkProperty *prop = g_malloc(sizeof(*prop)); g_autofree char *full_type = NULL; ObjectProperty *op; if (flags & OBJ_PROP_LINK_DIRECT) { prop->target = ptr; } else { prop->targetp = ptr; } prop->check = check; prop->flags = flags; full_type = g_strdup_printf("link<%s>", type); op = object_property_add(obj, name, full_type, object_get_link_property, check ? object_set_link_property : NULL, object_release_link_property, prop); op->resolve = object_resolve_link_property; return op;} |
The most intuitive implementation is gpio_irq, throughobject_property_add_linkto connect two qdev
123456789101112131415161718192021222324 | void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins, const char *name, int n){ int i; NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name); assert(gpio_list->num_in == 0 || !name); if (!name) { name = "unnamed-gpio-out"; } memset(pins, 0, sizeof(*pins) * n); for (i = 0; i < n; ++i) { gchar *propname = g_strdup_printf("%s[%u]", name, gpio_list->num_out + i); object_property_add_link(OBJECT(dev), propname, TYPE_IRQ, // link to connect two qdev (Object **)&pins[i], object_property_allow_set_link, OBJ_PROP_LINK_STRONG); g_free(propname); } gpio_list->num_out += n;} |
Reference:
