时间轴

2025-11-21

  1. init

环境

源码

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)

创建.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

QOM 的全称是 QEMU Object Model,是 QEMU 使用面向对象思想实现的抽象层,用来组织 QEMU 中的各种组件(比如设备模拟、后端组件 MemoryRegion、Machine 等)。类似于 C++ 的类,但是 QOM 是用纯 C 语言来实现的。

QOM 支持的面向对象特性有:继承、封装、多态

QOM 的运作过程包含三个部分:

  • 类型的注册
  • 类型的初始化
  • 对象的初始化
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()

基于面向对象的建模思想,QEMU 提供了一套非常格式化、套路化的硬件建模流程,对于初学者而言,只需要掌握 QOM 常用基本接口即可顺利的开展建模工作,无需深究内部原理。

我们需要了解三点:

  1. 设备模型是如何被定义的;
  2. QEMU 加载阶段是如何对设备进行实例化的;
  3. 不同设备之间是如何连接(通信)的。

edu设备建模源码分析

qemu-10.1.2版本源码

类型注册

EduState定义

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_CHECKER 生成 EDU(obj) 这样的宏,用于安全地把 QObject 转为 EduState *
  • TYPE_PCI_EDU_DEVICE 定义一个 QOM(QEMU Object Model)类型名字 "edu"
  • EduState 是这个设备的状态结构体

这是 QEMU 创建设备类型时的标准写法。

DECLARE_INSTANCE_CHECKER

DECLARE_INSTANCE_CHECKER实现了类似C语言版本的dynamic_cast(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__))

因此

DECLARE_INSTANCE_CHECKER(EduState, EDU, TYPE_PCI_EDU_DEVICE)

宏会生成:

1
2
3
4
static inline EduState *EDU(const void *obj)
{
return OBJECT_CHECK(EduState, obj, "edu");
}
  • 它生成了一个 类型安全的强制类型转换函数
  • 函数的名字就是第二个参数:EDU()
  • 功能是:把任意 QObject 转成你的实例类型 EduState *,并做类型检查。
  • OBJECT_CHECK类似C语言版本的dynamic_cast(obj)

TypeInfo

对edu这个类型的信息进行定义

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,//设备的类型名
.parent = TYPE_PCI_DEVICE,//它继承自 PCI 设备
.instance_size = sizeof(EduState),//实例大小是 EduState
.instance_init = edu_instance_init,//实例初始化函数
.class_init = edu_class_init,//类初始化函数
.interfaces = (const InterfaceInfo[]) {//实现 PCI 设备接口
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
{ },
},
}
};

DEFINE_TYPES(edu_types)

interfaces中的定义表示 EDU 实现了 ConventionalPciDevice 接口,可以被 PCI 总线识别。

DEFINE_TYPES

DEFINE_TYPES就是将该类型注册,其宏定义如下:

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)

其中定义了do_qemu_init_##type_array这个函数它会调用type_register_static_array

同时type_init也是一个宏,定义了一个函数。

type_init

定义在include/qemu/module.h,它会调用 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定义
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

而typeinit是调用module_init,其第一个个参数是**do_qemu_init edu_types(上面DEFINE_TYPE宏生成的函数,会调用type_register_static_array), 第二个参数是MODULE_INIT_QOM**,表示QOM初始化类型

include/qemu/module.h中的module_init宏如下

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)) 是关键

这个 GCC 特性表示:在 main() 调用之前自动执行这个函数

register_module_init

register_module_init定义如下:

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() 以类型的初始化函数,以及所属类型(对 QOM 类型来说是 MODULE_INIT_QOM)构建出一个 ModuleEntry,然后插入到对应 module 所属的链表中,所有 module 的链表存放在一个 init_type_list 数组中。

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 |
+--------------------+ +------+ +------+ +------+
| ... |
+--------------------+

因此QEMU 使用的各个类型在 main 函数执行之前就统一注册到了 init_type_list[MODULE_INIT_QOM] 这个链表中。

type_register_static_array

DEFINE_TYPES定义的type_init最终调用type_register_static_array(type_array, ARRAY_SIZE(type_array)),它会为每个TypeInfo调用type_register_static,而type_register_static会调用到 type_register_internal,即类型注册最终调用核心函数 type_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);
}

其中关键的是type_table_add(ti),它把新类型加入 QOM 全局类型哈希表,后续可以通过 object_new(“edu”) 或者 OBJECT_CHECK 使用,返回 TypeImpl* 给上层使用。

TypeImpl

TypeImpl 中存放了类型的所有信息,定义如下

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];
};

main函数中注册TypeImpl

进入 main 函数不久以后,就以 MODULE_INIT_QOM 为参数调用了函数 module_call_init, 这个函数执行了init_type_list[MODULE_INIT_QOM] 链表上每一个 ModuleEntry 的 init 函数。

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();
}
}

区分下面这两个概念:

概念 发生时机 作用
注册(register) ELF 加载阶段(constructor 执行) 把类型对应的 init 函数加入 init_type_list 链表
初始化(init) main() 调用 qemu_init_subsystems()module_call_init(MODULE_INIT_QOM) 遍历链表,调用每个 init 函数,完成真正的 TypeInfo 注册(type_register_static_array),将每个TypeImpl加入全局哈希表

do_qemu_init_edu_types调用栈

类型的初始化

前面已经完成了类型的注册,我们得到了QOM的所有TypeImpl注册到全局哈希表中,但是class(类型)的初始化并未完成。

类的初始化使用过 type_initialize() 完成的,函数的输入时表示类型信息的 TypeImpl 类型 ti。

type_initialize 真正执行 class 初始化逻辑,而 type_register_internal 只是创建 TypeImpl 并加入全局表。

具体函数如下:

type_initialize

qom/object.c

type_initialize它的主要任务是将 TypeImpl 对象(注册阶段创建的 TypeImpl)真正初始化为可用的类对象,并处理继承关系和接口。

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) {//避免重复初始化
return;
}

// 计算类对象的大小 class_size
ti->class_size = type_class_get_size(ti);
// 计算实例对象的大小 instance_size
ti->instance_size = type_object_get_size(ti);
// 计算实例对齐方式 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) {// 如果 instance_size 为 0,则表示抽象类型,接口类型也是抽象类型,没有实例
ti->abstract = true;
}
if (type_is_ancestor(ti, type_interface)) { // 对接口类型做严格检查,确保没有实例初始化函数和实例相关属性
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);//分配内存给类对象(class),存放虚函数表、属性表、接口列表等信息

parent = type_get_parent(ti);
if (parent) {
type_initialize(parent);//递归初始化父类
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);//继承父类字段
ti->class->interfaces = NULL;

for (e = parent->class->interfaces; e; e = e->next) {//初始化父类接口
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++) {//初始化当前类接口:
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);//为 class 分配属性哈希表,用于存放 QOM 对象的 property 信息(比如 name, size 等)

ti->class->type = ti;// class 对象保存指向它的 TypeImpl

while (parent) {
if (parent->class_base_init) {
parent->class_base_init(ti->class, ti->class_data);//调用父类 base 初始化函数
}
parent = type_get_parent(parent);
}

if (ti->class_init) {
ti->class_init(ti->class, ti->class_data);//调用当前类的class_init
}
}

最后会调用ti->class_init,就会调用我们在edu.c中定义的edu_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,//设备的类型名
.parent = TYPE_PCI_DEVICE,//它继承自 PCI 设备
.instance_size = sizeof(EduState),//实例大小是 EduState
.instance_init = edu_instance_init,//实例初始化函数
.class_init = edu_class_init,//类初始化函数
.interfaces = (const InterfaceInfo[]) {//实现 PCI 设备接口
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
{ },
},
}
};

type_initialize调用栈

类型的层次结构

从 type_initialize 可以看到,类型初始化的时候也会初始化父类型。QOM 通过这种层次结构实现类似 C++ 中的继承概念。

下面基于以 edu 设备为例进行分析:

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,
};

这个 edu 类型的层次关系:

1
TYPE_PCI_DEVICE -> TYPE_DEVICE -> TYPE_OBJECT

从数据结构体角度看:

在类型初始化函数 type_initialize 中会调用 ti->class=g_malloc0(ti->class_size) 语句分配类型的 class 结构,这个结构实际上代表了类型的信息。类似于 C++ 定义的一个类。

class_size 是 TypeImpl 的一个字段,如果这个类型没有指明它,则会使用父类的 class_size 进行初始化。

edu 设备类型本身没有定义,所以它的 class_size 为 TYPE_DEVICE 中定义的值,即 sizeof(PCIDevieClass)

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 {
// 第一个域:属于“设备类型”的类型所具备的一些属性。
DeviceClass parent_class; // 它的父类是 ObjectClass(所有类型的基础)

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 */
};

父类的初始化

下面给出 ObjectClass、DeviceClass、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 |
+-- | |
+----------------+

可以看出来它们之间的包含与被包含的关系,事实上,编译器为 C++ 继承结构编译出来的内存分布与这里是类似的。

问题来了,父类的成员域,是什么时候被初始化的呢?

qom/object.c, type_initialize()

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

总结

  1. 首先每个类型指定一个 TypeInfo 注册到系统中;
  2. 接着系统运行初始化的时候会把 TypeInfo 转变成 TypeImpl 放到一个哈希表中;
  3. 系统会对这个哈希表中的每个类型进行初始化;
  4. 接下来根据 QEMU 命令行参数,创建对应的实例对象。

对象的构造与初始化

这里我们分析对象的构造流程,主要是通过 object_new 函数来实现,调用链如下:

1
object_new() -> object_new_with_type() -> object_initialize_with_type() -> object_init_with_type()->edu_instance_init

edu_instance_init调用栈

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_name通过类型字符串名字来获取类型的信息,随后调用object_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;
}

这里也调用了type_initialize,之前gdb调试分析中g_hash_table_foreach已经调用了,那这里为什么还要调用呢?原因如下:

  • 虽然 g_hash_table_foreach() 可以提前初始化所有类型,但 并不是所有类型都会在机器初始化前被遍历
  • object_new 可能会创建某个尚未被 object_class_foreach() 遍历的类型
  • 为了安全,object_new 里必须保证类型已经初始化
  • type_initialize 是幂等的
1
2
3
if (ti->class) {//避免重复初始化
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);
}
}

我们在edu.c中定义了instance_init的值为edu_instance_init,那么这里就会调用ti->instance_init(obj)即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,//设备的类型名
.parent = TYPE_PCI_DEVICE,//它继承自 PCI 设备
.instance_size = sizeof(EduState),//实例大小是 EduState
.instance_init = edu_instance_init,//实例初始化函数
.class_init = edu_class_init,//类初始化函数
.interfaces = (const InterfaceInfo[]) {//实现 PCI 设备接口
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
{ },
},
}
};

对EduState 的数据内容进行填充

类型和对象之间是通过 Object 的 class 域联系在一起:obj->class=type->class

对象类型的层次关系:

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: */
};

可以把 QOM 的对象构造分成 3 部分:

  1. 类型的构造,通过 TypeInfo 构造一个 TypeImpl 的哈希表,在 main 之前完成;
  2. 类型的初始化,在 main 中进行,前两个都是全局性的,编译进去的 QOM 对象都会调用;
  3. 类对象的构造,构造具体的实例对象,只会对指定的设备,创建对象。

现在只是构造出了对象,并完成了对象初始化,但是还没有对 EduState 的数据内容进行填充。

这个时候 edu 设备还是不可用的,对设备而言,还需要设置它的 realized 属性(ObjectProperty)为 true 才行。

system/qdev-monitor.c

qdev_device_add会调用qdev_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_qdict 调用qdev_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);
}

对象的属性

QOM 实现了类似 C++ 的基于类的多态,一个对象按照继承体系,可以是 Object、DeviceState、PCIDevice 等。 在 QOM 中为了便于管理对象,还给每种类型和对象增加了属性。其中:

  1. 类属性存在于 ObjectClass 的 properties 域中,在 type_initialize 中构造;
  2. 对象属性存在于 Object 的 properties 域中,这个域在 object_initialize_with_type 中构造;

两者皆为一个哈希表(属性名字到 ObjectProperty 的映射)。

ObjectProperty

属性由 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;
};

每一种具体的属性,都会有一个结构体来描述它。下面举例:

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;

属性的添加,分为类属性的添加和对象属性的添加,以对象属性添加为例,它的属性添加是通过 object_property_add 接口完成的。

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

介绍两个比较特殊的属性:

child 属性

表述对象之间的从属关系,父对象的 child 属性指向子对象,添加 child 属性的函数为 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;
}

表示一种连接关系,代表一个设备引用了另一个设备,添加 link 属性的函数为 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;
}

最直观的实现,就是 gpio_irq,通过object_property_add_link来连接两个 qdev

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 来连接两个 qdev
(Object **)&pins[i],
object_property_allow_set_link,
OBJ_PROP_LINK_STRONG);
g_free(propname);
}
gpio_list->num_out += n;
}

参考: