Cover image for QOM

QOM

Words 4.4k
Views
Visitors
Timeline

Timeline

2025-11-21

  1. 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:

  1. How the device model is defined;
  2. How QEMU instantiates devices during the loading phase;
  3. 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
#define TYPE_PCI_EDU_DEVICE "edu"typedef struct EduState EduState;DECLARE_INSTANCE_CHECKER(EduState, EDU,                         TYPE_PCI_EDU_DEVICE)#define FACT_IRQ        0x00000001#define DMA_IRQ         0x00000100#define DMA_START       0x40000#define DMA_SIZE        4096struct EduState {    PCIDevice pdev;    MemoryRegion mmio;    QemuThread thread;    QemuMutex thr_mutex;    QemuCond thr_cond;    bool stopping;    uint32_t addr4;    uint32_t fact;#define EDU_STATUS_COMPUTING    0x01#define EDU_STATUS_IRQFACT      0x80    uint32_t status;    uint32_t irq_status;#define EDU_DMA_RUN             0x1#define EDU_DMA_DIR(cmd)        (((cmd) & 0x2) >> 1)# define EDU_DMA_FROM_PCI       0# define EDU_DMA_TO_PCI         1#define EDU_DMA_IRQ             0x4    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. */#define DECLARE_INSTANCE_CHECKER(InstanceType, OBJ_NAME, TYPENAME) \    static inline G_GNUC_UNUSED InstanceType * \    OBJ_NAME(const void *obj) \    { return OBJECT_CHECK(InstanceType, obj, TYPENAME); }/** * 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. */#define OBJECT_CHECK(type, obj, name) \    ((type *)object_dynamic_cast_assert(OBJECT(obj), (name), \                                        __FILE__, __LINE__, __func__))

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 anyQObjectto 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
#define DEFINE_TYPES(type_array)                                            \static void do_qemu_init_ ## type_array(void)                               \{                                                                           \    type_register_static_array(type_array, ARRAY_SIZE(type_array));         \}                                                                           \type_init(do_qemu_init_ ## type_array)

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;#define block_init(function) module_init(function, MODULE_INIT_BLOCK)#define opts_init(function) module_init(function, MODULE_INIT_OPTS)#define type_init(function) module_init(function, MODULE_INIT_QOM)#define trace_init(function) module_init(function, MODULE_INIT_TRACE)#define xen_backend_init(function) module_init(function, \                                               MODULE_INIT_XEN_BACKEND)#define libqos_init(function) module_init(function, MODULE_INIT_LIBQOS)#define fuzz_target_init(function) module_init(function, \                                               MODULE_INIT_FUZZ_TARGET)#define migration_init(function) module_init(function, MODULE_INIT_MIGRATION)#define block_module_load(lib, errp) module_load("block-", lib, errp)#define ui_module_load(lib, errp) module_load("ui-", lib, errp)#define audio_module_load(lib, errp) module_load("audio-", lib, errp)
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#define module_init(function, type)                                         \static void __attribute__((constructor)) do_qemu_init_ ## function(void)    \{                                                                           \    register_module_init(function, type);                                   \}#endif

_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 functiontype_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:

ConceptWhen it occursFunction
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.

do_qemu_init_edu_types call stack
do_qemu_init_edu_types call stack

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_The initialize call stack
type_The initialize call stack

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

  1. First, each type specifies a TypeInfo to register into the system;
  2. Then, when the system runtime initializes, it converts TypeInfo into TypeImpl and puts it into a hash table;
  3. The system initializes each type in this hash table;
  4. 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()

edu_instance_init call stack
edu_instance_init call stack

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:

  • Althoughg_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 beenobject_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:

  1. Type registration: construct a TypeImpl via TypeInfo and add it to the global hash table, in main viamodule_call_init()completed (the registration function itself is added to the linked list before main through the constructor mechanism);
  2. Type initialization is performed in main; the first two stages are global, and all compiled-in QOM objects will be called;
  3. 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:

  1. Class properties exist in the properties field of ObjectClass and are constructed in type_initialize;
  2. 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;}

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:

Loading comments…