Cover image for QOM

QOM

Timeline

Timeline

2025-11-21

  1. init
This article introduces the object-oriented modeling mechanism and operation process of the QEMU Object Model (QOM). Taking the edu device modeling source code as an example, it deeply analyzes the three core stages: type registration, type initialization, and object instantiation. It elaborates on how type information constructs a linked list during the loading phase and completes global registration in the main function, as well as the inheritance and interface handling logic during class initialization.

Environment

Source Code

1
2
3
4
5
6
wget https://download.qemu.org/qemu-10.1.2.tar.xz
tar xvJf qemu-10.1.2.tar.xz
cd qemu-10.1.2
mkdir -p output
./configure --prefix=$PWD/output --target-list=aarch64-softmmu,riscv64-softmmu --enable-debug
bear -- make -j$(nproc)

Create .clangd

1
2
3
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 isQEMU Object Model, which is an abstraction layer implemented by QEMU using object-oriented concepts to organize various components in QEMU (such as device simulation, backend components like MemoryRegion, Machine, etc.). It is similar to C++ classes, but QOM is implemented in pure C language.

The object-oriented features supported by QOM include:Inheritance, Encapsulation, Polymorphism

The operation process of QOM consists of three parts:

  • Type Registration
  • Type Initialization
  • Object Initialization
1
2
3
4
5
6
7
    |--类型注册     ---> type_init()
| register_module_init()
| type_register()
QOM-|--类型的初始化 ---> type_initialize()
|--对象的初始化 ---> object_new()
| object_initialize()
| object_initialize_with_type()

Based on object-oriented modeling concepts, QEMU provides a highly formatted and standardized hardware modeling process. For beginners, it is sufficient 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 devices are instantiated during the QEMU loading phase;
  3. How different devices are connected (communicate).

Source code analysis of edu device modeling

QEMU version 10.1.2 source code

Type registration

EduStateDefinition

hw/miscs/edu.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#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 4096

struct 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)Such a macro, used to safely convertQObjecttoEduState *
  • TYPE_PCI_EDU_DEVICEDefine a QOM (QEMU Object Model) type name"edu"
  • EduStateis the state structure of this device

This is the standard way to create a device type in QEMU.

DECLARE_INSTANCE_CHECKER

DECLARE_INSTANCE_CHECKER implements something similar to the C language version of dynamic_cast<EduState *>(obj)

include/qom/object.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* 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:

1
2
3
4
static inline EduState *EDU(const void *obj)
{
return OBJECT_CHECK(EduState, obj, "edu");
}
  • It generates atype-safe cast function
  • The function’s name 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<EduState *>(obj)

TypeInfo

Define the information for the edu type

hw/misc/edu.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static const TypeInfo edu_types[] = {
{
.name = TYPE_PCI_EDU_DEVICE,//Device type name
.parent = TYPE_PCI_DEVICE,//It inherits from PCI device
.instance_size = sizeof(EduState),//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 implementsConventionalPciDevicethe interface and can be recognized by the PCI bus.

DEFINE_TYPES

DEFINE_TYPESIt registers this type, and its macro definition is as follows:

include/qom/object.h

1
2
3
4
5
6
#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

At the same time,type_initis also a macro that defines a function.

type_init

Defined ininclude/qemu/module.h, it will call module_init(function, MODULE_INIT_QOM)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

// type init definition
typedef 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_The function generated by the TYPE macro 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

1
2
3
4
5
6
7
8
// 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 feature indicates:Automatically execute this function before main() is called

register_module_init

register_module_init is defined as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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 initialization function of the type, along with its belonging type (MODULE_INIT_QOM for QOM types), constructs a ModuleEntry, then inserts it into the linked list of the corresponding module. The linked lists of all modules are stored in an init_type_list array.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
               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, all types used by QEMU are uniformly registered into theinit_type_list[MODULE_INIT_QOM]linked list before the main function executes.

type_register_static_array

DEFINE_The type defined by TYPES_init ultimately calls type_register_static_array(type_array, ARRAY_SIZE(type_array)), it calls type for each TypeInfo_register_static, and type_register_static calls type_register_internal, meaning type registration ultimately calls the core functiontype_register_internal()

qom/object.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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 is type_table_add(ti), which adds the new type to the QOM global type hash table, and later can be used via object_new(“edu”) or OBJECT_CHECK, returning TypeImpl* for upper-layer use.

TypeImpl

TypeImpl stores all information about the type, defined as follows

qom/object.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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, it uses MODULE_INIT_QOM as a parameter to call the function module_call_init, this function executedinit_type_list[MODULE_INIT_QOM]the init function of each ModuleEntry on the linked list.

utils/module.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 between the following two concepts:

ConceptTimingFunction
RegisterELF loading phase (constructor execution)Add the init function corresponding to the type to theinit_type_listlinked list
Initialization (init)main() callqemu_init_subsystems()module_call_init(MODULE_INIT_QOM)Traverse the linked list, call each init function, complete the actual TypeInfo registration (type_register_static_array), add each TypeImpl to the global hash table

do_qemu_init_edu_types call stack
do_qemu_init_edu_types call stack

Type initialization

The type registration has been completed earlier, and we have all TypeImpl of QOM registered in the global hash table, but the initialization of the class (type) has not been completed.

The initialization of a class is completed using type_initialize(), and the function’s input is the TypeImpl type ti representing type information.

type_initialize actually executes the class initialization logic, while type_register_internal only creates TypeImpl and adds it to the global table.

The specific function is as follows:

type_initialize

qom/object.c

type_initializeIts main task is toTypeImplthe object (TypeImpl created during the registration phase)truly initialize it into a usable class object, and handle inheritance relationships and interfaces.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
static void type_initialize(TypeImpl *ti)
{
TypeImpl *parent;

if (ti->class) {//Avoid duplicate 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 with 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 and instance-related attributes
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, storing virtual function table, property table, interface list, etc.

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 QOM object property information (e.g., 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 edu.c_class_init:

1
2
3
4
5
6
7
8
9
10
11
12
13
static const TypeInfo edu_types[] = {
{
.name = TYPE_PCI_EDU_DEVICE,//Device type name
.parent = TYPE_PCI_DEVICE,//It inherits from PCI device
.instance_size = sizeof(EduState),//Instance size is EduState
.instance_init = edu_instance_init,//Instance initialization function
.class_init = edu_class_init,//Class initialization function
.interfaces = (const InterfaceInfo[]) {//Implement PCI device interface
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
{ },
},
}
};

type_initialize call stack
type_initialize call stack

Type hierarchy

From type_initialize, it can be seen 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 is based on the edu device as an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// hw/misc/edu.c
static const TypeInfo edu_info = {
.name = TYPE_PCI_EDU_DEVICE,
.parent = TYPE_PCI_DEVICE,
...
};
// hw/pci/pci.c
static const TypeInfo pci_device_type_info = {
.name = TYPE_PCI_DEVICE,
.parent = TYPE_DEVICE,
...
};
// hw/core/qdev.c
static const TypeInfo device_type_info = {
.name = TYPE_DEVICE,
.parent = TYPE_OBJECT,
.class_init = device_class_init,
.abstract = true,
...
};
// qom/object.c
static 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 perspective of data structures:

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’s information, similar to a class defined in C++.

class_size is a field of TypeImpl. If this type does not specify it, the parent class’s class_size is used for initialization.

The edu device type itself does not define it, so its class_size is TYPE_The value defined in DEVICE, i.e.,sizeof(PCIDeviceClass)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// include/hw/pci/pci_device.h (qemu v9.2.0)
struct PCIDeviceClass {
// First field: some attributes that belong to the "device type" type.
DeviceClass parent_class; // Its parent class 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

Below is the relationship diagram among ObjectClass, DeviceClass, and PCIDeviceClass:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
                     +----------------+                   
+-- | | --+
| | 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 arises: when are the member fields of the parent class initialized?

qom/object.c, type_initialize()

1
memcpy(ti->class, parent->class, parent->class_size);

Summary

  1. First, each type specifies a TypeInfo to register into the system;
  2. Then, during system initialization, the TypeInfo is converted into a TypeImpl and placed into a hash table;
  3. The system initializes each type in this hash table;
  4. Next, based on the QEMU command-line parameters, the corresponding instance objects are created.

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

1
2
3
4
5
6
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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, in the previous gdb debugging analysis, g_hash_table_foreach has already been called, so why call it again here? The reason is as follows:

  • Althoughg_hash_table_foreach()can initialize all types in advance, butnot all types are traversed before machine initialization
  • object_new may create a type that has not yet beenobject_class_foreach()traversed
  • For safety, object_new must ensure the type has been initialized
  • type_initialize is idempotent
1
2
3
if (ti->class) {//to avoid repeated initialization
return;
}

object_initialize_with_type

qom/object.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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

1
2
3
4
5
6
7
8
9
10
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 ofedu_instance_init, then here it will call ti->instance_init(obj) is edu_instance_init(obj)

1
2
3
4
5
6
7
8
9
10
11
12
13
static const TypeInfo edu_types[] = {
{
.name = TYPE_PCI_EDU_DEVICE,//device type name
.parent = TYPE_PCI_DEVICE,//it inherits from PCI device
.instance_size = sizeof(EduState),//instance size is EduState
.instance_init = edu_instance_init,//instance initialization function
.class_init = edu_class_init,//class initialization function
.interfaces = (const InterfaceInfo[]) {//implement PCI device interface
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
{ },
},
}
};

fill the data content of EduState

types and objects are linked through the class field of Object:obj->class=type->class

hierarchical relationship of object types:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// hw/misc/edu.c
struct 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.h
struct DeviceState {
/* private: */
Object parent_obj;
/* public: */
};

the object construction of QOM can be divided into 3 parts:

  1. type construction: build a hash table of TypeImpl via TypeInfo, completed before main;
  2. type initialization: performed in main, the first two are global and called by all compiled QOM objects;
  3. class object construction: construct specific instance objects, only creating objects for specified devices.

now the object has been constructed and initialized, but the data content of EduState has not been filled yet.

At this point, the edu device is still unavailable. For the device, it is also necessary to set its realized property (ObjectProperty) to true.

system/qdev-monitor.c

qdev_device_addwill callqdev_device_add_from_qdict

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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’s property

QOM implements class-based polymorphism similar to C++, an object can be Object, DeviceState, PCIDevice, etc., according to the inheritance hierarchy. 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, which is constructed in object_initialize_with_type;

Both are a hash table(mapping from property names to ObjectProperty).

ObjectProperty

Properties are represented by ObjectProperty:

include/qom/object.h

1
2
3
4
5
6
7
8
9
10
11
12
13
struct ObjectProperty
{
char *name;
char *type;
char *description;
ObjectPropertyAccessor *get;
ObjectPropertyAccessor *set;
ObjectPropertyResolve *resolve;
ObjectPropertyRelease *release;
ObjectPropertyInit *init;
void *opaque;
QObject *defval;
};

Each specific property has a structure to describe it. Here is an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// qom/object.c
typedef 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;

The addition of attributes is divided into class attribute addition and object attribute addition. Taking object attribute addition as an example, its attribute addition is done through the object_property_add interface.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
+----------------+                                                                 
| ... |
+----------------+
| 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 child attribute of the parent object points to the child object. The function to add the child attribute is object_property_add_child:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// qom/object.c
ObjectProperty *
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 to add the link attribute is object_property_add_link :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// qom/object.c
ObjectProperty *
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 qdevs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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 qdevs
(Object **)&pins[i],
object_property_allow_set_link,
OBJ_PROP_LINK_STRONG);
g_free(propname);
}
gpio_list->num_out += n;
}

Reference: