环境 源码 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.2mkdir -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 常用基本接口即可顺利的开展建模工作,无需深究内部原理。
我们需要了解三点:
设备模型是如何被定义的;
QEMU 加载阶段是如何对设备进行实例化的;
不同设备之间是如何连接(通信)的。
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 #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); } #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, .instance_size = sizeof (EduState), .instance_init = edu_instance_init, .class_init = edu_class_init, .interfaces = (const InterfaceInfo[]) { { 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 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 #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加入全局哈希表
类型的初始化 前面已经完成了类型的注册,我们得到了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 ; } ti->class_size = type_class_get_size(ti); ti->instance_size = type_object_get_size(ti); ti->instance_align = type_object_get_align(ti); if (ti->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); 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); ti->class -> type = ti; while (parent) { if (parent->class_base_init) { parent->class_base_init(ti->class, ti->class_data); } parent = type_get_parent(parent); } if (ti->class_init) { ti->class_init(ti->class, ti->class_data); } }
最后会调用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, .instance_size = sizeof (EduState), .instance_init = edu_instance_init, .class_init = edu_class_init, .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, } };
类型的层次结构 从 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 static const TypeInfo edu_info = { .name = TYPE_PCI_EDU_DEVICE, .parent = TYPE_PCI_DEVICE, ... }; static const TypeInfo pci_device_type_info = { .name = TYPE_PCI_DEVICE, .parent = TYPE_DEVICE, ... }; static const TypeInfo device_type_info = { .name = TYPE_DEVICE, .parent = TYPE_OBJECT, .class_init = device_class_init, .abstract = true , ... }; 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 struct PCIDeviceClass { DeviceClass parent_class; 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; uint16_t subsystem_id; const char *romfile; };
父类的初始化 下面给出 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 ) ;
总结
首先每个类型指定一个 TypeInfo 注册到系统中;
接着系统运行初始化的时候会把 TypeInfo 转变成 TypeImpl 放到一个哈希表中;
系统会对这个哈希表中的每个类型进行初始化;
接下来根据 QEMU 命令行参数,创建对应的实例对象。
对象的构造与初始化 这里我们分析对象的构造流程,主要是通过 object_new 函数来实现,调用链如下:
1 object_new() -> object_new_with_type() -> object_initialize_with_type() -> object_init_with_type()->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; 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, .instance_size = sizeof (EduState), .instance_init = edu_instance_init, .class_init = edu_class_init, .interfaces = (const InterfaceInfo[]) { { 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 struct EduState { PCIDevice pdev; MemoryRegion mmio; ... } EduState; struct PCIDevice { DeviceState qdev; bool partially_hotplugged; ... }; struct DeviceState { Object parent_obj; };
可以把 QOM 的对象构造分成 3 部分:
类型的构造,通过 TypeInfo 构造一个 TypeImpl 的哈希表,在 main 之前完成;
类型的初始化,在 main 中进行,前两个都是全局性的,编译进去的 QOM 对象都会调用;
类对象的构造,构造具体的实例对象,只会对指定的设备,创建对象。
现在只是构造出了对象,并完成了对象初始化,但是还没有对 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 ; } dc = qdev_get_device_class(&driver, errp); if (!dc) { return NULL ; } 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 ; } dev = qdev_new(driver); if (phase_check(PHASE_MACHINE_READY) && !qdev_hotplug_allowed(dev, bus, errp)) { goto err_del_dev; } id = g_strdup(qdict_get_try_str(opts, "id" )); if (!qdev_set_id(dev, id, errp)) { goto err_del_dev; } 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 中为了便于管理对象,还给每种类型和对象增加了属性 。其中:
类属性存在于 ObjectClass 的 properties 域中,在 type_initialize 中构造;
对象属性存在于 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 typedef struct { union { Object **targetp; Object *target; ptrdiff_t offset; }; 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 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 属性
表示一种连接关系,代表一个设备引用了另一个设备,添加 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 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, (Object **)&pins[i], object_property_allow_set_link, OBJ_PROP_LINK_STRONG); g_free(propname); } gpio_list->num_out += n; }
参考: