QEMU(1) - QOM
Table of Contents
TypeInfo
根类型TypeInfo
TypeInfo链路
初始化TypeInfo -> ModuleEntry
Dump TypeInfo
TypeImpl
初始化 ModuleEntry -> TypeImpl
对象类
基对象类ObjectClass
设备派生对象类
总线派生对象类
接口对象类
内存派生对象类
加速器派生对象类
初始化 TypeImpl -> ObjectClass
type_initialize_interface
class_base_init
class_init
对象
基对象Object
设备派生对象
总线派生对象
初始化 TypeImpl -> Object
object_new_with_type
object_initialize_with_type
调用object_new
qdev_device_add
instance_init
object_instance_init
device_initfn
instance_post_init
device_post_init
属性
ObjectProperty
get/set callback方法
GlobalProperty
"container"属性
Reference
QEMU设备模型发展历史参考下图。最终QEMU选择面向对象的QEMU Object Module (QOM)来实现对所有的设备,总线,接口的模拟。
下面先介绍QOM模型的几个重要概念,然后以hw/misc/Pci-testdev.c中的代码实现作为实例介绍整个QOM初始化过程。
QOM模型中几个重要的概念
- 类型Type,QOM中所有的模型都是通过类型Type注册的,具体实现过程就是从类型信息TypeInfo注册为类型实现TypeImpl,并把所有类型实现添加到全局的GHashTable表中,hash表以类型Type的名字作为key键值。类型Type有父子的概念,QOM有对象(TYPE_OBJECT)和接口(TYPE_INTERFACE)两种根父类型。
- 对象类,ObjectClass是对象类的基类,其余所有的对象类都是继承自ObjectClass,每个类型Type在注册过程中都会实例化对象类。ObjectClass的第一个元素指向类型实现TypeImpl。
- 对象,Object是对象的基类,其余所有对象都是继承自Object,每个类型Type在注册过程中都会实例化对象。对象Ojbect的第一个元素是指向对象类的指针。
- 属性Property,属性有两类,一类是对象维护属性GHashTable表。一类是设备对象的属性结构数组
- 接口Interface,对象类维护接口GSList单向队列。
TypeInfo
每个QOM设备模型类型包括设备,总线,接口等都会定义一个类型信息TypeInfo。类型信息TypeInfo是用于注册类型实现TypeImpl的结构模板。TypeInfo通过name定义类型名称,通过parernt指定父类型名称。
struct TypeInfo
{const char *name; //类型名称const char *parent; //父类型名称,TYPE_OBJECT和TYPE_INTERFACE是两个根类型的名称size_t instance_size; //对象实例的大小void (*instance_init)(Object *obj); //对象实例的init函数,父类型的init已被执行过了,只需要负责本类型的void (*instance_post_init)(Object *obj); //对象实例的post_init函数,在init执行完后执行void (*instance_finalize)(Object *obj); //对象实例销毁时执行,释放动态资源bool abstract; //如为true则是抽象的类型,不能直接实例size_t class_size; //类对象实例的大小void (*class_init)(ObjectClass *klass, void *data); //类对象实例的init函数,初始化自己的方法指针,也可覆盖父类的方法指针void (*class_base_init)(ObjectClass *klass, void *data); //在父类的class_init执行完,自己的class_init执行之前执行,做一些清理工作。void (*class_finalize)(ObjectClass *klass, void *data); //类对象实例销毁时执行,释放动态资源void *class_data; //传递给类对象实例各个方法的数据InterfaceInfo *interfaces; //类型定义的接口信息名称数组
};struct InterfaceInfo {const char *type; //每个接口类型,也会注册一个TypeImpl类型,这里只是提供每个接口的名字
};
TypeInfo一般都是静态定义的,例如如下的pci_testdev_info。
static const TypeInfo pci_testdev_info = {.name = TYPE_PCI_TEST_DEV,.parent = TYPE_PCI_DEVICE,.instance_size = sizeof(PCITestDevState),.class_init = pci_testdev_class_init,
};
有时也会动态生成
static void type_initialize_interface(TypeImpl *ti, TypeImpl *interface_type,TypeImpl *parent_type)
{TypeInfo info = { };
...info.parent = parent_type->name;info.name = g_strdup_printf("%s::%s", ti->name, interface_type->name);info.abstract = true;
...
}
根类型TypeInfo
所有的TypeInfo的parent的根为TYPE_OBJECT和TYPE_INTERFACE
static TypeInfo interface_info = {.name = TYPE_INTERFACE,.class_size = sizeof(InterfaceClass),.abstract = true,};static TypeInfo object_info = {.name = TYPE_OBJECT,.instance_size = sizeof(Object),.instance_init = object_instance_init,.abstract = true,};
TYPE_OBJECT的一级子类型
name | parent |
TYPE_ACCEL | TYPE_OBJECT |
TYPE_IOTHREAD | TYPE_OBJECT |
TYPE_MEMORY_REGION | TYPE_OBJECT |
TYPE_QJSON | TYPE_OBJECT |
TYPE_MEMORY_BACKEND | TYPE_OBJECT |
TYPE_RNG_BACKEND | TYPE_OBJECT |
TYPE_TPM_BACKEND | TYPE_OBJECT |
TYPE_QCRYPTO_TLS_CREDS | TYPE_OBJECT |
TYPE_IRQ | TYPE_OBJECT |
TYPE_MACHINE | TYPE_OBJECT |
TYPE_DEVICE | TYPE_OBJECT |
TYPE_BUS | TYPE_OBJECT |
TYPE_XILINX_AXI_DMA_DATA_STREAM | TYPE_OBJECT |
TYPE_XILINX_AXI_DMA_CONTROL_STREAM | TYPE_OBJECT |
TYPE_XILINX_AXI_ENET_DATA_STREAM | TYPE_OBJECT |
TYPE_XILINX_AXI_ENET_CONTROL_STREAM | TYPE_OBJECT |
TYPE_NETFILTER | TYPE_OBJECT |
"container" | TYPE_OBJECT |
TYPE_DIRECT_IMPL | TYPE_OBJECT |
TYPE_DUMMY | TYPE_OBJECT |
TYPE_DUMMY_DEV | TYPE_OBJECT |
TYPE_DUMMY_BUS | TYPE_OBJECT |
TYPE_DUMMY_BACKEND | TYPE_OBJECT |
TYPE_NONDEVICE | TYPE_OBJECT |
TYPE_QEMU_CONSOLE | TYPE_OBJECT |
TYPE_INTERFACE的一级子类型
name | parent |
TYPE_ACPI_DEVICE_IF | TYPE_INTERFACE |
TYPE_ARM_LINUX_BOOT_IF | TYPE_INTERFACE |
TYPE_FW_PATH_PROVIDER | TYPE_INTERFACE |
TYPE_HOTPLUG_HANDLER | TYPE_INTERFACE |
TYPE_NMI | TYPE_INTERFACE |
TYPE_STREAM_SLAVE | TYPE_INTERFACE |
TYPE_NVRAM | TYPE_INTERFACE |
TYPE_USER_CREATABLE | TYPE_INTERFACE |
TYPE_TEST_IF | TYPE_INTERFACE |
TypeInfo链路
由根TypeInfo出发,可以组成不同的TypeInfo链路
- TYPE_OBJECT -> TYPE_DEVICE -> TYPE_PCI_DEVICE -> TYPE_PCI_TEST_DEV
- TYPE_OBJECT -> TYPE_DEVICE -> TYPE_SYS_BUS_DEVICE -> TYPE_VIRTIO_PCI
- TYPE_OBJECT -> TYPE_DEVICE -> TYPE_SYS_BUS_DEVICE -> TYPE_VIRTIO_MMIO
- TYPE_OBJECT -> TYPE_BUS -> TYPE_PCI_BUS -> TYPE_PCIE_BUS
- TYPE_OBJECT -> TYPE_BUS -> TYPE_USB_BUS
- TYPE_OBJECT -> TYPE_BUS -> TYPE_VIRTIO_BUS -> TYPE_VIRTIO_PCI_BUS
- TYPE_OBJECT -> TYPE_BUS -> TYPE_VIRTIO_BUS -> TYPE_VIRTIO_MMIO_BUS
- TYPE_OBJECT -> TYPE_MEMORY_BACKEND
- TYPE_OBJECT -> "container"
- TYPE_INTERFACE -> TYPE_NMI
- TYPE_INTERFACE -> TYPE_HOTPLUG_HANDLER -> TYPE_USB_BUS::TYPE_HOTPLUG_HANDLER (动态生成)
- TYPE_INTERFACE -> TYPE_ACPI_DEVICE_IF
- TYPE_INTERFACE -> TYPE_USR_CREATABLE -> TYPE_MEMORY_BACKEND::TYPE_USR_CREATABLE (动态生成)
示意图如下
初始化TypeInfo -> ModuleEntry
每个静态定义的TypeInfo,通过宏type_init定义的构造函数,在main函数被调用之前生成一个ModuleEntry把typeinfo的注册函数和类型挂载到全局ModuleTypeList队列中。
以pci_testdev及其parent为例,数据结构如下。这边TypeInfo指向ModuleEntry.init的箭头表示TypeInfo会作为ModuleEntry.init注册函数的输入形参。
Dump TypeInfo
写个python抓取QEMU代码中所有的TypeInfo定义,保存到typeinfo.csv文件中。
# -*- coding: utf-8 -*-
import os
import re
import pandas as pdtypeinfos = []
def find_cfile(path):g = os.walk(path) cfiles = []for path,dir_list,file_list in g: for file_name in file_list: if (file_name.endswith('.c')):cfiles.append(os.path.join(path, file_name))return cfilesdef find_typeinfo_file(cfiles):typeinfo_files = []for cfile in cfiles:with open(cfile, 'r') as f:while True:line = f.readline() # 逐行读取if not line:breakif (line.find('static const TypeInfo') != -1):typeinfo_files.append(cfile)breakreturn typeinfo_filesdef find_typeinfos(typeinfo_files):for typeinfo_file in typeinfo_files:find_typeinfo(typeinfo_file)def find_typeinfo(file):with open(file, 'r') as f:lines = f.readlines() # 全部读取i = 0while True:if i == len(lines):breakif lines[i].find('static const TypeInfo') != -1:i = parse_typeinfo(file, lines, i)else:i += 1def parse_typeinfo (file, lines, i):typeinfo = {}typeinfo['file'] = filetypeinfo['name'] = lines[i].split()[3]typeinfos.append(typeinfo)i += 1i = pasre_typeinfo_tokens(lines, i, typeinfo) return idef pasre_typeinfo_tokens (lines, i, typeinfo):while True:if lines[i].find('.interfaces') != -1:i = pasre_typeinfo_interface(lines, i, typeinfo)elif lines[i].find('};') != -1:return i+1else:l = lines[i].split()if l[0].startswith('.'):typeinfo[l[0][1:]] = l[2][:-1]i += 1return idef pasre_typeinfo_interface (lines, i, typeinfo):i += 1interface = []while True:pattern = re.compile(r'{.+}')result = pattern.findall(lines[i])if len(result) == 0:breakfor r in result:r = r[1:-1]r = r.strip()if len(r) == 0:continueinterface.append(r)i += 1typeinfo['interface'] = interfacereturn idef dump_typeinfo(qemupath):cfiles = find_cfile(qemupath)typeinfo_files = find_typeinfo_file(cfiles)find_typeinfos(typeinfo_files)pd.DataFrame(typeinfos).to_csv('typeinfo.csv',encoding='utf_8_sig')return typeinfosdef main():dump_typeinfo ("C:/qemu") if __name__ == "__main__":main()
TypeImpl
每个ypInfo都会注册一个TypeImpl,所有的TypeImpl会以类型名为键值保存在全局的hash表中。
struct TypeImpl
{//和TypeInfo一一对应部分 - 开始const char *name;size_t class_size;size_t instance_size;void (*class_init)(ObjectClass *klass, void *data);void (*class_base_init)(ObjectClass *klass, void *data);void (*class_finalize)(ObjectClass *klass, void *data);void *class_data;void (*instance_init)(Object *obj);void (*instance_post_init)(Object *obj);void (*instance_finalize)(Object *obj);bool abstract;const char *parent;//和TypeInfo一一对应部分 - 结束TypeImpl *parent_type; //父类型的TypeImpl指针ObjectClass *class; //类型对应的对象类的基类ObjectClass的指针,因为基类是派生类的第一个元素,所以派生对象类指针也相同int num_interfaces; //TypeInfo中定义的接口数组的个数InterfaceImpl interfaces[MAX_INTERFACES]; //复制自TypeInfo中InterfaceInfo数组提供的接口名字
};struct InterfaceImpl
{const char *typename; //InterfaceImpl数组也是提供接口的名字
};#define MAX_INTERFACES 32 //TypeInfo中没有个数的限制,TypeImpl中新增加了个数限制,一个类型支持的接口类型最大数目为32
type_new函数会根据TypeInfo输入,初始化生成性的TypeImpl结构。
static TypeImpl *type_new(const TypeInfo *info)
{TypeImpl *ti = g_malloc0(sizeof(*ti)); //申请空间int i;g_assert(info->name != NULL);if (type_table_lookup(info->name) != NULL) {fprintf(stderr, "Registering `%s' which already exists\n", info->name);abort();}//从TypeInfo复制数据ti->name = g_strdup(info->name);ti->parent = g_strdup(info->parent);ti->class_size = info->class_size;ti->instance_size = info->instance_size;ti->class_init = info->class_init;ti->class_base_init = info->class_base_init;ti->class_finalize = info->class_finalize;ti->class_data = info->class_data;ti->instance_init = info->instance_init;ti->instance_post_init = info->instance_post_init;ti->instance_finalize = info->instance_finalize;ti->abstract = info->abstract;for (i = 0; info->interfaces && info->interfaces[i].type; i++) {ti->interfaces[i].typename = g_strdup(info->interfaces[i].type);}ti->num_interfaces = i;return ti;
}
初始化 ModuleEntry -> TypeImpl
QEMU入口main函数的一开始就执行module_call_init(MODULE_INIT_QOM)
int main(int argc, char **argv, char **envp)
{
...module_call_init(MODULE_INIT_QOM);
...
}
module_call_init找到之前注册的所有的init函数,然后执行它
void module_call_init(module_init_type type)
{ModuleTypeList *l;ModuleEntry *e;module_load(type);l = find_type(type);QTAILQ_FOREACH(e, l, node) {e->init(); //找到之前注册的init函数,调用执行}
}
以pci_testdev为例,init函数为pci_testdev_register_types,真正执行注册的函数为type_register_internal,调用关系为pci_testdev_register_types->type_register_static->type_register->type_register_internal。
static void pci_testdev_register_types(void)
{//直接调用type_register_static,并以TypeInfo结构地址为输入参数type_register_static(&pci_testdev_info);
}TypeImpl *type_register_static(const TypeInfo *info)
{//直接调用type_registerreturn type_register(info);
}static TypeImpl *type_register_internal(const TypeInfo *info)
{TypeImpl *ti;ti = type_new(info);type_table_add(ti);return ti;
}
type_register_internal首先调用type_new创建一个TypeImpl结构,并根据TypeInfo初始化它。随后调用type_table_add把TypeImpl添加到hash表,键值为TypeImpl.name
static void type_table_add(TypeImpl *ti)
{assert(!enumerating_types);g_hash_table_insert(type_table_get(), (void *)ti->name, ti);
}static GHashTable *type_table_get(void)
{static GHashTable *type_table; //静态变量,保存TypeImplhash表地址//第一次需要初始化if (type_table == NULL) {type_table = g_hash_table_new(g_str_hash, g_str_equal);}return type_table; //返回hash表地址
}
总结如下
对象类
基对象类ObjectClass
struct ObjectClass
{/*< private >*/Type type; //对象类对应的类型TypeImpl的指针GSList *interfaces; //接口类InterfaceClass指针单向列表,函数type_initialize_interface初始化const char *object_cast_cache[OBJECT_CLASS_CAST_CACHE]; //debug选项,保存object cast历史const char *class_cast_cache[OBJECT_CLASS_CAST_CACHE]; //debug选项,保存object class的cast历史ObjectUnparent *unparent;
};
设备派生对象类
示例PCIDeviceClass对象类的派生关系是ObjectClass->DeviceClass->PCIDeviceClass。
总线派生对象类
示例PciBusClass的派生顺序为ObjectClass -> BusClass -> PCIBusClass
接口对象类
示例UserCreatableClass的派生顺序为ObjectClass -> InterfacClass -> HotplugHandlerClass
内存派生对象类
示例HostMemoryBackendClass的派生顺序为ObjectClass -> HostMemoryBackendClass
加速器派生对象类
示例AccelClass的派生顺序为ObjectClass -> AccelClass
初始化 TypeImpl -> ObjectClass
对象类的初始化是由TypeImpl.class_init函数完成的。TypeImpl.class_init函数在type_initialize函数中被调用。
type_initialize调用有几种情况
- 当要获取一种类型的所有对象类时主动调用循环初始化每个对象类,object_class_get_list -> object_class_foreach -> object_class_foreach_tramp -> type_initialize
- 通过对象类查找父对象类,object_class_get_parent -> type_initialize
- 通过类型名查找对象类,object_new_with_props -> object_new_with_propv -> object_class_by_name -> type_initialize
- 新建一个对象实例, object_get_root -> object_new -> object_new_with_type -> type_initialize
- 初始化一个对象实例,object-initialize - > object_initlialize_with_type
- 初始化对象类的接口,object_intialize_interface -> type_initialize
- 递归调用父对象类,type_intialize -> type_initialize
调用关系如图示
type_initialize函数完成如下工作。
- 申请对象类空间
- 调用type_initialize_interface初始化设备接口
- 调用parent的class_base_init
- 调用本类型的class_init
static void type_initialize(TypeImpl *ti)
{TypeImpl *parent;if (ti->class) { //对象类建立了就表示已经初始化过了,直接返回return;}ti->class_size = type_class_get_size(ti); //type_new()时初始化过,这里校正一下ti->instance_size = type_object_get_size(ti); //type_new()时初始化过,这里校正一下ti->class = g_malloc0(ti->class_size); //为对象类申请空间parent = type_get_parent(ti); //获取parent的TypeImplif (parent) {type_initialize(parent); //先初始化parentGSList *e;int i;g_assert_cmpint(parent->class_size, <=, ti->class_size);memcpy(ti->class, parent->class, parent->class_size); //继承parent的对象类,复制到最前面//parent对象类已经注册的接口for (e = parent->class->interfaces; e; e = e->next) { //loop已经注册的interface队列InterfaceClass *iface = e->data; // 接口对象类的data保持的是InterfaceClass的指针ObjectClass *klass = OBJECT_CLASS(iface); //cast到基类type_initialize_interface(ti, iface->interface_type, klass->type); //iface->interface_type和klass->type有区别吗?}//TypeImpl中定义的接口for (i = 0; i < ti->num_interfaces; i++) {TypeImpl *t = type_get_by_name(ti->interfaces[i].typename); //通过接口类型名找到接口通过TypeInfo注册的TypeImpl的指针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->type = ti; //设置TypeImpl指针到对象类的typewhile (parent) {if (parent->class_base_init) {parent->class_base_init(ti->class, ti->class_data); //调用所有parent对象类的class_base_init函数}parent = type_get_parent(parent);}if (ti->class_init) {ti->class_init(ti->class, ti->class_data); //调用对象类初始化函数class_init}
}
type_initialize_interface
首先每个接口类型会定义自己的TypeInfo,然后注册到TypeImpl全局hash表。例如hotplug_handler_info。在随后的初始化type_initialize函数中接口类型会生成自己的对象类,我们暂且称之为接口对象类。所有接口类型及其父类的TypeInfo都没有定义instance_size,所以不存在接口对象实例。
static const TypeInfo hotplug_handler_info = {.name = TYPE_HOTPLUG_HANDLER,.parent = TYPE_INTERFACE,.class_size = sizeof(HotplugHandlerClass),
};
另一方面,有些QOM设备的TypeInfo定义中,会添加自己支持的接口类型,暂且称为设备接口。例如usb_bus_info和pcie_slot_type_info。这些interfaces随后在TypeImpl注册过程中,会添加到数组 InterfaceImpl interfaces[MAX_INTERFACES]中。interfaces的数目设置到num_interfaces。
static const TypeInfo usb_bus_info = {.name = TYPE_USB_BUS,.parent = TYPE_BUS,.instance_size = sizeof(USBBus),.class_init = usb_bus_class_init,.interfaces = (InterfaceInfo[]) {{ TYPE_HOTPLUG_HANDLER },{ }}
};static const TypeInfo pcie_slot_type_info = {.name = TYPE_PCIE_SLOT,.parent = TYPE_PCIE_PORT,.instance_size = sizeof(PCIESlot),.abstract = true,.class_init = pcie_slot_class_init,.interfaces = (InterfaceInfo[]) {{ TYPE_HOTPLUG_HANDLER },{ }}
};
Type_initialize_interface负责初始化设备接口,并把设备接口挂在到对象类的ObjectClass->interfaces队列上。每个TypeImpl需要初始化有两类接口
- 已经挂在到其parent的对象类interfaces队列上上的接口,需要继承过来。
- TypeImpl自己的interfaces数组上的接口。
Type_initialize_interface定义如下
static void type_initialize_interface(TypeImpl *ti, TypeImpl *interface_type,TypeImpl *parent_type)
{InterfaceClass *new_iface;TypeInfo info = { };TypeImpl *iface_impl;//初始化一个新的类型接口TypeInfo,名称问"设备类型名::接口类型名"info.parent = parent_type->name;info.name = g_strdup_printf("%s::%s", ti->name, interface_type->name);info.abstract = true;//根据新初始化的TypeInfo,注册新的接口TypeImpl,同时会生成新的对象类InterfaceClassiface_impl = type_new(&info); //初始化新的类型接口TypeImpliface_impl->parent_type = parent_type;type_initialize(iface_impl); //初始新的类型接口对象类g_free((char *)info.name);//初始化新生成的对象类InterfaceClassnew_iface = (InterfaceClass *)iface_impl->class; //cast到InterfaceClassnew_iface->concrete_class = ti->class; //InterfaceClass->concrete_class指向的接口通过Typeinfo注册TypeImpl时,在type_initialize中申请的对象类。即接口对象类new_iface->interface_type = interface_type; //指向接口TypeImpl类型//添加新的接口对象类到类型的对象类的接口单向队列ti->class->interfaces = g_slist_append(ti->class->interfaces,iface_impl->class);
}//每个接口
//1,在type_init构造中会注册全局的接口TypeImpl(名称"接口类型名")和接口对象类,
//2,在本函数中,注册一个为本实例类型服务的设备接口TypeImpl(名称"设备类型名::接口类型名")和设备接口对象类。
class_base_init
在调用class_init之前,先需要调用父类的class_base_init。
对于pci-testdev设备,只有DeviceClass对象类的class_init为device_class_init,如下
static void device_class_base_init(ObjectClass *class, void *data)
{DeviceClass *klass = DEVICE_CLASS(class);/* We explicitly look up properties in the superclasses,* so do not propagate them to the subclasses.*/klass->props = NULL;
}
TYPE_MACHINE有定义一个class_base_init,如下
static void machine_class_base_init(ObjectClass *oc, void *data)
{if (!object_class_is_abstract(oc)) {MachineClass *mc = MACHINE_CLASS(oc);const char *cname = object_class_get_name(oc);assert(g_str_has_suffix(cname, TYPE_MACHINE_SUFFIX));mc->name = g_strndup(cname,strlen(cname) - strlen(TYPE_MACHINE_SUFFIX));}
}
class_init
对象类的class_init初始化从基类开始调用
- ObjectClass基类没有class_init
- DeviceClass对象类的class_init为device_class_init
- PciDeviceClass对象类的class_init为pci_device_class_init
- Pci-testdev没有定义新的对象类,定义了class_init为pci_testdev_class_init
device_class_init定义如下
static void device_class_init(ObjectClass *class, void *data)
{DeviceClass *dc = DEVICE_CLASS(class);class->unparent = device_unparent;dc->realize = device_realize;dc->unrealize = device_unrealize;/* by default all devices were considered as hotpluggable,* so with intent to check it in generic qdev_unplug() /* device_set_realized() functions make every device* hotpluggable. Devices that shouldn't be hotpluggable,* should override it in their class_init()*/dc->hotpluggable = true;
}
pci_device_class_init定义如下
static void pci_device_class_init(ObjectClass *klass, void *data)
{DeviceClass *k = DEVICE_CLASS(klass);PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass);k->realize = pci_qdev_realize;k->unrealize = pci_qdev_unrealize;k->bus_type = TYPE_PCI_BUS;k->props = pci_props;pc->realize = pci_default_realize;
}
pci_testdev_class_init定义如下
static void pci_testdev_class_init(ObjectClass *klass, void *data)
{DeviceClass *dc = DEVICE_CLASS(klass); //通过对象类基类cast到DeviceClass子类PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); //通过对象类基类cast到PCIDeviceClass子类k->realize = pci_testdev_realize; //override realize方法k->exit = pci_testdev_uninit;k->vendor_id = PCI_VENDOR_ID_REDHAT;k->device_id = PCI_DEVICE_ID_REDHAT_TEST;k->revision = 0x00;k->class_id = PCI_CLASS_OTHERS;dc->desc = "PCI Test Device";set_bit(DEVICE_CATEGORY_MISC, dc->categories);dc->reset = qdev_pci_testdev_reset;
}
对象
基对象Object
struct Object
{/*< private >*/ObjectClass *class; //指向对象类ObjectFree *free; //当引用计数为0时,执行释放方法GHashTable *properties; //属性hash表uint32_t ref; //引用计数Object *parent; //指向父对象,实现继承
};
设备派生对象
示例PCITestDevState的派生顺序是Object->DeviceState->PCIDevice->PCITestDevState。
总线派生对象
示例PCIBus的派生顺序为Object -> BusState -> PCIBus
初始化 TypeImpl -> Object
对象实例的建立是由函数object_new_with_type完成,也有一些对象的空间是静态分配的或提前准备好的,无需再次动态申请,对于这些对象只需要直接调用初始化函数。对象实例的初始化由object_initialize_with_type完成。
调用关系如下
object_new_with_type
Object *object_new_with_type(Type type)
{Object *obj;g_assert(type != NULL);type_initialize(type); //先确保type已经初始化obj = g_malloc(type->instance_size); //申请对象实例空间object_initialize_with_type(obj, type->instance_size, type); //初始化对象实例obj->free = g_free; //基对象的初始化free方法return obj;
}
object_initialize_with_type
void object_initialize_with_type(void *data, size_t size, TypeImpl *type)
{Object *obj = data;g_assert(type != NULL);type_initialize(type); //确保type已经初始化过//基本check,确保对象实例的大小,TypeImpl不是抽象类型等g_assert_cmpint(type->instance_size, >=, sizeof(Object));g_assert(type->abstract == false);g_assert_cmpint(size, >=, type->instance_size);memset(obj, 0, type->instance_size); //对象实例清0obj->class = type->class; //填写对象实例中的对象类地址object_ref(obj); //对象基类的ref引用数增加1obj->properties = g_hash_table_new_full(g_str_hash, g_str_equal,NULL, object_property_free); //建立属性hash表object_init_with_type(obj, type); //调用.instance_initobject_post_init_with_type(obj, type); //调用.instance_post_init
}
object_init_with_type定义如下
static void object_init_with_type(Object *obj, TypeImpl *ti)
{if (type_has_parent(ti)) {object_init_with_type(obj, type_get_parent(ti)); //首先执行parent的instance_init}if (ti->instance_init) { //执行instance_initti->instance_init(obj);}
}
object_post_init_with_type定义如下
static void object_post_init_with_type(Object *obj, TypeImpl *ti)
{if (ti->instance_post_init) {ti->instance_post_init(obj); //先执行对象自身的instance_post_init}if (type_has_parent(ti)) {object_post_init_with_type(obj, type_get_parent(ti)); //再执行parent对象的instance_post_init}
}
调用object_new
来看看是如何调用到object_new的。每个设备在QEMU启动时都体现为命令行中一个‘-device’参数。QEMU main函数会解析每个-device参数,调用device_init_func来完成设备的初始化。
int main(int argc, char **argv, char **envp)
{
.../* init generic devices */if (qemu_opts_foreach(qemu_find_opts("device"), //对于每个“-devcie”参数,调用device_init_func函数device_init_func, NULL, NULL)) {exit(1);}
...
}
device_init_func定义如下
static int device_init_func(void *opaque, QemuOpts *opts, Error **errp)
{Error *err = NULL;DeviceState *dev;dev = qdev_device_add(opts, &err); //添加设备if (!dev) {error_report_err(err);return -1;}object_unref(OBJECT(dev)); //对象基类的ref减1,当ref值为1时调用object_finalizereturn 0;
}
qdev_device_add
来看看qdev_device_add的完整代码
DeviceState *qdev_device_add(QemuOpts *opts, Error **errp)
{DeviceClass *dc;const char *driver, *path, *id;DeviceState *dev;BusState *bus = NULL;Error *err = NULL;driver = qemu_opt_get(opts, "driver"); //一定要有driver参数?if (!driver) {error_setg(errp, QERR_MISSING_PARAMETER, "driver");return NULL;}/* find driver */dc = qdev_get_device_class(&driver, errp); //通过driver名找到对象类DeviceClassif (!dc) {return NULL;}/* find bus */path = qemu_opt_get(opts, "bus"); //bus参数if (path != NULL) { //bus参数存在bus = qbus_find(path, errp); //找到bus的对象类if (!bus) {return NULL;}if (!object_dynamic_cast(OBJECT(bus), dc->bus_type)) { //类型checkerror_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参数不存在,对象类的bus_type有值bus = qbus_find_recursive(sysbus_get_default(), NULL, dc->bus_type); //通过对象类的bus_type找到bus对象if (!bus || qbus_is_full(bus)) {error_setg(errp, "No '%s' bus found for device '%s'",dc->bus_type, driver);return NULL;}}if (qdev_hotplug && bus && !qbus_is_hotpluggable(bus)) {error_setg(errp, QERR_BUS_NO_HOTPLUG, bus->name);return NULL;}/* create device */dev = DEVICE(object_new(driver)); //建立设备对象实例//bus对象实例存在时,添加设备对象实例到bus对象实例的link属性,bus对象实例的ref增加1,设置设备对象实例的parent_busif (bus) {qdev_set_parent_bus(dev, bus); }id = qemu_opts_id(opts); //从命令行参数获取idif (id) {dev->id = id; //保持到DeviceState对象中的id}//添加对象到qdev的peripheral对象的对象属性中if (dev->id) { //id有指定//"container/machine/peripheral"object_property_add_child(qdev_get_peripheral(), dev->id,OBJECT(dev), NULL);} else { //id没有指定static int anon_count;gchar *name = g_strdup_printf("device[%d]", anon_count++);//"container/machine/peripheral"object_property_add_child(qdev_get_peripheral_anon(), name,OBJECT(dev), NULL);g_free(name);}/* set properties */if (qemu_opt_foreach(opts, set_property, dev, &err)) { //解析QEMU命令行参数中的对象属性,执行set方法error_propagate(errp, err);object_unparent(OBJECT(dev));object_unref(OBJECT(dev));return NULL;}dev->opts = opts;object_property_set_bool(OBJECT(dev), true, "realized", &err); //执行realize对象属性的set方法if (err != NULL) {error_propagate(errp, err);dev->opts = NULL;object_unparent(OBJECT(dev));object_unref(OBJECT(dev));return NULL;}return dev;
}
instance_init
instance_init的执行顺序是先parent再自己,对于Pci-testdev
- TYPE_OBJECT的instance_init为object_instance_init
- TYPE_DEVICE的instance_init为device_initfn
- TYPE_PCI_DEVICE的instance_init定义为空
- TYPE_PCI_TEST_DEV的instance_init定义为空
object_instance_init
static void object_instance_init(Object *obj)
{object_property_add_str(obj, "type", qdev_get_type, NULL, NULL); //添加“type”为string对象属性
}
device_initfn
static void device_initfn(Object *obj)
{DeviceState *dev = DEVICE(obj); //通过对象基类cast到DeviceStateObjectClass *class;Property *prop;if (qdev_hotplug) { //设置支持hotplug的flagdev->hotplugged = 1;qdev_hot_added = true;}dev->instance_id_alias = -1;dev->realized = false;//添加“realized”,"hotpluggable","hotplugged"等bool对象属性object_property_add_bool(obj, "realized",device_get_realized, device_set_realized, NULL);object_property_add_bool(obj, "hotpluggable",device_get_hotpluggable, NULL, NULL);object_property_add_bool(obj, "hotplugged",device_get_hotplugged, device_set_hotplugged,&error_abort);//把对象类的属性添加到legacy和static对象属性中?class = object_get_class(OBJECT(dev));do {for (prop = DEVICE_CLASS(class)->props; prop && prop->name; prop++) {qdev_property_add_legacy(dev, prop, &error_abort);qdev_property_add_static(dev, prop, &error_abort);}class = object_class_get_parent(class);} while (class != object_class_by_name(TYPE_DEVICE));//添加parent_bus到link对象属性object_property_add_link(OBJECT(dev), "parent_bus", TYPE_BUS,(Object **)&dev->parent_bus, NULL, 0,&error_abort);//初始化队列dev->gpiosQLIST_INIT(&dev->gpios);
}
instance_post_init
instance_post_init的执行顺序是先自己再parent,对于Pci-testdev
- TYPE_OBJECT的instance_post_init定义为空
- TYPE_DEVICE的instance_post_init为device_post_init
- TYPE_PCI_DEVICE的instance_post_init定义为空
- TYPE_PCI_TEST_DEV的instance_post_init定义为空
device_post_init
static void device_post_init(Object *obj)
{qdev_prop_set_globals(DEVICE(obj));
}void qdev_prop_set_globals(DeviceState *dev)
{ObjectClass *class = object_get_class(OBJECT(dev));//对象类到父对象类递归,得到对象类型名称do {qdev_prop_set_globals_for_type(dev, object_class_get_name(class));class = object_class_get_parent(class);} while (class);
}static void qdev_prop_set_globals_for_type(DeviceState *dev,const char *typename)
{GlobalProperty *prop;QTAILQ_FOREACH(prop, &global_props, next) { //全局的队列global_propsError *err = NULL;if (strcmp(typename, prop->driver) != 0) { //全局队列global_props中找到driver名字与类型名字一致的项continue;}prop->used = true;object_property_parse(OBJECT(dev), prop->value, prop->property, &err); //解析对象是否存在global_props中的属性,存在的话执行set方法if (err != NULL) {assert(prop->user_provided);error_report("Warning: global %s.%s=%s ignored (%s)",prop->driver, prop->property, prop->value,error_get_pretty(err));error_free(err);return;}}
}
属性
属性包含两类,一类是对象(Object)属性ObjectProperty,一类是对象类属性Property
ObjectProperty
对象(Object)包含属性hash表GHashTable *properties,存储了 属性名 到 ObjectProperty 的映射。
typedef struct ObjectProperty
{gchar *name; //属性名gchar *type; //属性类型//如"int","uint32","size","uint64","bool","string","child<%s>"等gchar *description;ObjectPropertyAccessor *get; //读取属性时触发的get方法ObjectPropertyAccessor *set; //设置属性时触发的set方法ObjectPropertyResolve *resolve; //resolve方法ObjectPropertyRelease *release; //release方法void *opaque; //额外的非透明的信息,//在object_property_add_bool中添加的"bool"属性中指向BoolProperty//在object_property_add_child中添加的"child"属性中指向child的对象实例//在object_property_add_link中添加的"link"属性中指向LinkProperty//在object_property_add_str中添加的"string"属性中指向StringProperty//在object_property_add_enum中添加的"string"属性中指向EnumProperty
} ObjectProperty;
函数object_property_add负责添加
ObjectProperty *
object_property_add(Object *obj, const char *name, const char *type,ObjectPropertyAccessor *get,ObjectPropertyAccessor *set,ObjectPropertyRelease *release,void *opaque, Error **errp)
{ObjectProperty *prop;
...prop = g_malloc0(sizeof(*prop));prop->name = g_strdup(name);prop->type = g_strdup(type);prop->get = get;prop->set = set;prop->release = release;prop->opaque = opaque;g_hash_table_insert(obj->properties, prop->name, prop); //添加到Object对象的properties hash表return prop;
}
get/set callback方法
ObjectProperty的get/set callback方法的调用,还用到了很多中间概念。
第一个概念是QObject,它是每个ObjectProperty get/set callback方法调用过程中的数据载体的基类。定义的是ObjectProperty的类型代码,引用次数,以及销毁函数。
typedef struct QObject {const QType *type; //QObject的代码及销毁函数size_t refcnt; //引用次数
} QObject;typedef struct QType {qtype_code code; //代码void (*destroy)(struct QObject *); //销毁函数
} QType;
QOBJECT_INIT用于初始化各个派生类型的基类QObject
#define QOBJECT_INIT(obj, qtype_type) \obj->base.refcnt = 1; \ //设置引用refcnt为1obj->base.type = qtype_type //设置type为qstring_type
QObject是一个基类结构,它可以派生出很多类型。这些派生类会根据每个类型,添加上数据选项。
typedef struct QBool {QObject base;bool value; //布尔数值
} QBool;typedef struct QString {QObject base;char *string; //字串size_t length; //字串长度size_t capacity; //字串容量,和长度值相同
} QString;typedef struct QInt {QObject base;int64_t value; //整型数值
} QInt;typedef struct QDict {QObject base;size_t size;QLIST_HEAD(,QDictEntry) table[QDICT_BUCKET_MAX]; //字典
} QDict;typedef struct QFloat {QObject base;double value; //浮点数值
} QFloat;typedef struct QList {QObject base;QTAILQ_HEAD(,QListEntry) head; //队列
} QList;
每个派生类型的Qtype定义为
Qtype | |
.code | .destroy |
QTYPE_QSTRING | qstring_destroy_obj |
QTYPE_QBOOL | qbool_destroy_obj |
QTYPE_QDICT | qdict_destroy_obj |
QTYPE_QFLOAT | qfloat_destroy_obj |
QTYPE_QINT | qint_destroy_obj |
QTYPE_QLIST | qlist_destroy_obj |
QTYPE_QNULL | qnull_destroy_obj |
第二个概念是方法Visitor结构,它定义了访问ObjectProperty各个数据类型的方法。
struct Visitor
{/* Must be set */void (*start_struct)(Visitor *v, void **obj, const char *kind,const char *name, size_t size, Error **errp);void (*end_struct)(Visitor *v, Error **errp);void (*start_implicit_struct)(Visitor *v, void **obj, size_t size,Error **errp);void (*end_implicit_struct)(Visitor *v, Error **errp);void (*start_list)(Visitor *v, const char *name, Error **errp);GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp);void (*end_list)(Visitor *v, Error **errp);void (*type_enum)(Visitor *v, int *obj, const char * const strings[],const char *kind, const char *name, Error **errp);void (*get_next_type)(Visitor *v, int *kind, const int *qobjects,const char *name, Error **errp);void (*type_int)(Visitor *v, int64_t *obj, const char *name, Error **errp);void (*type_bool)(Visitor *v, bool *obj, const char *name, Error **errp);void (*type_str)(Visitor *v, char **obj, const char *name, Error **errp);void (*type_number)(Visitor *v, double *obj, const char *name,Error **errp);void (*type_any)(Visitor *v, QObject **obj, const char *name,Error **errp);/* May be NULL */void (*optional)(Visitor *v, bool *present, const char *name,Error **errp);void (*type_uint8)(Visitor *v, uint8_t *obj, const char *name, Error **errp);void (*type_uint16)(Visitor *v, uint16_t *obj, const char *name, Error **errp);void (*type_uint32)(Visitor *v, uint32_t *obj, const char *name, Error **errp);void (*type_uint64)(Visitor *v, uint64_t *obj, const char *name, Error **errp);void (*type_int8)(Visitor *v, int8_t *obj, const char *name, Error **errp);void (*type_int16)(Visitor *v, int16_t *obj, const char *name, Error **errp);void (*type_int32)(Visitor *v, int32_t *obj, const char *name, Error **errp);void (*type_int64)(Visitor *v, int64_t *obj, const char *name, Error **errp);/* visit_type_size() falls back to (*type_uint64)() if type_size is unset */void (*type_size)(Visitor *v, uint64_t *obj, const char *name, Error **errp);bool (*start_union)(Visitor *v, bool data_present, Error **errp);void (*end_union)(Visitor *v, bool data_present, Error **errp);
};
对于set方法,QmpInputVisitor定义了Vistor方法,以及一个StackObject数组。每个数组元素StackObject是一个QObject的派生。StackObject还包括一个QObject的队列,以及一个hash表。
struct QmpInputVisitor
{Visitor visitor; //Visitor方法数组StackObject stack[QIV_STACK_SIZE]; //StackObject数组, 最大1024个int nb_stack;bool strict;
};
#define QIV_STACK_SIZE 1024typedef struct StackObject
{QObject *obj; //指向各个派生类型的QObject基类const QListEntry *entry; //QObject队列?GHashTable *h; //为字典类型准备的hash表
} StackObject;typedef struct QListEntry {QObject *value;QTAILQ_ENTRY(QListEntry) next;
} QListEntry;
对于get方法,QmpOutputVisitor定义了Vistor方法,以及一个QStack队列头。队列元素为QStackEntry。
struct QmpOutputVisitor
{Visitor visitor; //vistor方法QStack stack; //QStack队列
};typedef QTAILQ_HEAD(QStack, QStackEntry) QStack; typedef struct QStackEntry //QStack队列元素为QStackEntry
{QObject *value; //QStackEntry是基类QObject的派生bool is_list_head;QTAILQ_ENTRY(QStackEntry) node;
} QStackEntry;
QmpImputVisitor负责新建一个QmpInputVisitor,并初始化它。
QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
{QmpInputVisitor *v;v = g_malloc0(sizeof(*v)); //申请空间//初始化Visitor各个方法v->visitor.start_struct = qmp_input_start_struct;v->visitor.end_struct = qmp_input_end_struct;v->visitor.start_implicit_struct = qmp_input_start_implicit_struct;v->visitor.end_implicit_struct = qmp_input_end_implicit_struct;v->visitor.start_list = qmp_input_start_list;v->visitor.next_list = qmp_input_next_list;v->visitor.end_list = qmp_input_end_list;v->visitor.type_enum = input_type_enum;v->visitor.type_int = qmp_input_type_int;v->visitor.type_bool = qmp_input_type_bool;v->visitor.type_str = qmp_input_type_str;v->visitor.type_number = qmp_input_type_number;v->visitor.type_any = qmp_input_type_any;v->visitor.optional = qmp_input_optional;v->visitor.get_next_type = qmp_input_get_next_type;qmp_input_push(v, obj, NULL); //添加QObject到StackObject->stackqobject_incref(obj); //该QObecjt的引用次数refcnt增加1return v;
}
qmp_input_push初始化QmpInputVisitor中的StackObject->stack
static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)
{GHashTable *h;if (qiv->nb_stack >= QIV_STACK_SIZE) {error_setg(errp, "An internal buffer overran");return;}qiv->stack[qiv->nb_stack].obj = obj;qiv->stack[qiv->nb_stack].entry = NULL;qiv->stack[qiv->nb_stack].h = NULL;if (qiv->strict && qobject_type(obj) == QTYPE_QDICT) {h = g_hash_table_new(g_str_hash, g_str_equal);qdict_iter(qobject_to_qdict(obj), qdict_add_key, h);qiv->stack[qiv->nb_stack].h = h;}qiv->nb_stack++;
}
对于get方法,qmp_output_visitor_new负责new一个QmpOutputVisitor,并初始化vistor各个 方法和QStack队列。
QmpOutputVisitor *qmp_output_visitor_new(void)
{QmpOutputVisitor *v;v = g_malloc0(sizeof(*v));v->visitor.start_struct = qmp_output_start_struct;v->visitor.end_struct = qmp_output_end_struct;v->visitor.start_list = qmp_output_start_list;v->visitor.next_list = qmp_output_next_list;v->visitor.end_list = qmp_output_end_list;v->visitor.type_enum = output_type_enum;v->visitor.type_int = qmp_output_type_int;v->visitor.type_bool = qmp_output_type_bool;v->visitor.type_str = qmp_output_type_str;v->visitor.type_number = qmp_output_type_number;v->visitor.type_any = qmp_output_type_any;QTAILQ_INIT(&v->stack);return v;
}
每个ObjectProperty的get/set方法的第二个参数就是vistor结构指针
typedef void (ObjectPropertyAccessor)(Object *obj,struct Visitor *v,void *opaque,const char *name,Error **errp);
通过这个指针也很容易cast到QmpOutputVisitor结构
static QmpOutputVisitor *to_qov(Visitor *v)
{return container_of(v, QmpOutputVisitor, visitor);
}#define container_of(ptr, type, member) ({ \const typeof(((type *) 0)->member) *__mptr = (ptr); \(type *) ((char *) __mptr - offsetof(type, member));})
下面以string属性为类,看看具体的get/set方法
先看set,object_property_set_str函数先把string转换为QStraing,并添加到QmpInputVisitor中。随后的Visitor方法再逆向得到string字串,调用StringProperty->set方法完成最终的设置动作。
再看get,object_property_get_str函数先通过object_property_get_qobject获取String的QString对象,然后通过qstring_get_str得到最终的string字串,最后还需要将refcnt减1,并判断是否需要销毁该QObject。
部分ObjectProperty的API接口总结
API | type | get | set | release | opaque |
object_property_add_child | "child<%typename%>" | object_get_child_property | NULL | object_finalize_child_property | Object |
object_property_add_str | "string" | property_get_str | property_set_str | property_release_str | StringProperty |
object_property_add_bool | "bool" | property_get_bool | property_set_bool | property_release_bool | BoolProperty |
object_property_add_link | "link<%typename%>" | object_get_link_property | object_set_link_property | object_release_link_property | LinkProperty |
object_property_add_enum | input | property_get_enum | property_set_enum | property_release_enum | EnumProperty |
object_property_add_uint8_ptr | "uint8" | property_get_uint8_ptr | NULL | NULL | uint8_t |
object_property_add_uint16_ptr | "uint16" | property_get_uint16_ptr | NULL | NULL | uint16_t |
object_property_add_uint32_ptr | "uint32" | property_get_uint32_ptr | NULL | NULL | uint32_t |
object_property_add_uint64_ptr | "uint64" | property_get_uint64_ptr | NULL | NULL | uint64_t |
object_property_add_alias |
"link<%typename%>" or %typename% |
property_get_alias | property_set_alias | property_release_alias | AliasProperty |
object_property_add | input | input | input | input | input |
qdev_property_add_legacy | "legacy-%Propertyname%" | qdev_get_legacy_property | NULL | NULL | Property |
qdev_property_add_static | "%Propertyname%" | Property.info->get | Property.info->set | Property.info->release | Property |
Property
对象类DeviceClass (派生自ObjectClass基类)包含Property列表
typedef struct DeviceClass {/*< private >*/ObjectClass parent_class;/*< public >*/
...Property *props; //属性列表
...
} DeviceClass;
Property结构定义如下
struct Property {const char *name;PropertyInfo *info;ptrdiff_t offset;uint8_t bitnr;qtype_code qtype;int64_t defval;int arrayoffset;PropertyInfo *arrayinfo;int arrayfieldsize;
};struct PropertyInfo {const char *name;const char *description;const char * const *enum_table;int (*print)(DeviceState *dev, Property *prop, char *dest, size_t len);ObjectPropertyAccessor *get;ObjectPropertyAccessor *set;ObjectPropertyRelease *release;
};
DeviceClass.props一般在对象类的class_init中执行,属性列表一般为静态的,初始化就是指针赋值动作。
static void host_x86_cpu_class_init(ObjectClass *oc, void *data)
{DeviceClass *dc = DEVICE_CLASS(oc);
...dc->props = host_x86_cpu_properties;
...
}//静态属性列表
static Property host_x86_cpu_properties[] = {DEFINE_PROP_BOOL("migratable", X86CPU, migratable, true),DEFINE_PROP_BOOL("host-cache-info", X86CPU, cache_info_passthrough, false),DEFINE_PROP_END_OF_LIST()
};
DeviceClass.class_init方法把Property属性会转化为ObjectProperty
static void device_initfn(Object *obj)
{
...class = object_get_class(OBJECT(dev));do {for (prop = DEVICE_CLASS(class)->props; prop && prop->name; prop++) {qdev_property_add_legacy(dev, prop, &error_abort); //名为"legacy-%接口名%"的字串属性qdev_property_add_static(dev, prop, &error_abort); //以Property信息为input}class = object_class_get_parent(class);} while (class != object_class_by_name(TYPE_DEVICE));
...
}
GlobalProperty
GlobalProperty定义如下,它包含QTAIL_ENTRY队列
typedef struct GlobalProperty {const char *driver;const char *property;const char *value;bool user_provided;bool used;QTAILQ_ENTRY(GlobalProperty) next;
} GlobalProperty;
所有的GlobalProperty是挂载到QTAIL_HEAD队列头global_props
static QTAILQ_HEAD(, GlobalProperty) global_props =QTAILQ_HEAD_INITIALIZER(global_props);void qdev_prop_register_global(GlobalProperty *prop)
{QTAILQ_INSERT_TAIL(&global_props, prop, next);
}void qdev_prop_register_global_list(GlobalProperty *props)
{int i;for (i = 0; props[i].driver != NULL; i++) {qdev_prop_register_global(props+i);}
}
添加GlobalProperty的几种方法
- 命令行参数“global”定义多个GlobalProperty,由函数qemu_add_globals添加到队列中
- 特别的一些QEMU命令行参数,例如“rtc_td_hack”,定义一个GlobalProperty entry,通过qdev_prop_register_global_list函数添加
- 定义machine时,SET_MACHINE_COMPAT宏定义MachineClass.compat_props,随后通过qdev_prop_register_global_list函数来添加到队列中
注册好的GloabalProperty在device_post_init函数中会被使用。device_post_init为TYPE_DEVICE的instance_post_init。每个QOM设备在初始化过程中都需要调用到该函数。查看每个设备是否包含GloabalProperty定义的属性,找到的话就调用它的set方法。
device_post_init -> qdev_prop_set_globals -> loop device and its parent 调用 qdev_prop_set_globals_for_type-> loop GlobalProperty in global_props队列,调用object_property_parse-> string_input_visitor_new 新建并初始化StringInputVisitor-> object_property_set-> ObjectProperty->set-> string_input_visitor_cleanup
"container"属性
Qdev使用“containner"属性来实现device tree功能。
在qom/container.c中定义了container设备的TypeInfo信息。
static const TypeInfo container_info = {.name = "container",.instance_size = sizeof(Object),.parent = TYPE_OBJECT,
};
object_get_root接口生成root 对象。
Object *object_get_root(void)
{static Object *root;if (!root) {root = object_new("container"); //生成“container”对象}return root;
}
container_get接口用于建立设备的path tree。为每一级path建立一个“container”对象,然后把每一级path路径作为child属性添加到这一级的“container”对象上。
Object *container_get(Object *root, const char *path)
{Object *obj, *child;gchar **parts;int i;parts = g_strsplit(path, "/", 0);assert(parts != NULL && parts[0] != NULL && !parts[0][0]);obj = root;for (i = 1; parts[i] != NULL; i++, obj = child) {child = object_resolve_path_component(obj, parts[i]); //找到路径的对象if (!child) {child = object_new("container"); //没有找到,新建一个object_property_add_child(obj, parts[i], child, NULL); //添加路径到对象属性}}g_strfreev(parts);return obj;
}
例如qdev_get_machine会调用container_get,建立/machine路径。
Object *qdev_get_machine(void)
{static Object *dev;if (dev == NULL) {dev = container_get(object_get_root(), "/machine");}return dev;
}
Reference
https://www.binss.me/blog/qemu-note-of-qemu-object-model/
https://terenceli.github.io/%E6%8A%80%E6%9C%AF/2017/01/08/qom-introduction
http://juniorprincewang.github.io/2018/07/23/qemu%E6%BA%90%E7%A0%81%E6%B7%BB%E5%8A%A0%E8%AE%BE%E5%A4%87/
https://www.linux-kvm.org/images/0/0b/Kvm-forum-2013-Modern-QEMU-devices.pdf
https://translate.googleusercontent.com/translate_c?anno=2&depth=1&hl=zh-CN&rurl=translate.google.com&sl=en&sp=nmt4&tl=zh-CN&u=http://people.redhat.com/~thuth/blog/qemu/2018/09/10/instance-init-realize.html&xid=17259,15700022,15700186,15700190,15700256,15700259,15700262,15700265&usg=ALkJrhjdyz0aUXPhoQIA85_LRDTinfNeoA
QEMU(1) - QOM相关推荐
- VIRTIO后端框架QEMU与VHOST分析
VIRTIO设备的前端是GUEST的内核驱动,后端由QEMU或者DPU实现.不论是原来的QEMU-VIRTIO框架还是现在的DPU,VIRTIO的控制面和数据面都是相对独立的设计.本文主要针对QEMU ...
- 欢迎使用 QEMU 的文档!| 目录
Welcome to QEMU's documentation! - QEMU documentationhttps://qemu-project.gitlab.io/qemu/ About QEMU ...
- linux qemu原理,最全的剖析QEMU原理的文章3
Block Unchaining 底下幾種情況會做 block unchaining.請見以下討論, cpu_exit (exec.c) → cpu_unlink_tb(exec.c).當某些情況需要 ...
- 01 QEMU仿真器-模拟器介绍
QEMU仿真器-模拟器介绍 作者 将狼才鲸 创建日期 2022-05-28 所属的Gitee源码和工程地址:才鲸嵌入式 / 开源安防摄像机(嵌入式软件) 一.QEMU模拟器(仿真器)介绍 如果你也像我 ...
- qemu内存模型(3) 内存布局初始化
以i386为例 static void memory_map_init(void) {system_memory = g_malloc(sizeof(*system_memory));memory_r ...
- qemu学习之添加一个新machine
qemu学习之添加一个新machine 1 xlnx-versal-virt machine所在的代码 1.1 声明一个新的machine 1.1.1 xlnx-versal-virt machine ...
- 我见过最全的剖析QEMU原理的文章[Z]
转自: http://people.cs.nctu.edu.tw/~chenwj/dokuwiki/doku.php?id=qemu How To Become A Hacker 写给新手程序员的一封 ...
- QEMU零知识学习2 —— QEMU源码下载
QEMU源代码有多种方式进行下载.本文只介绍其中2种方法. 1. 通过git进行下载开发中的最新的qemu.git (1)获取源码 $ git clone http://git.qemu.org/qe ...
- AFL学习(一)-补充QEMU模式
QEMU模式测试 编译QEMU 参考README.qemu $./build_qemu_support.sh# Cannot use 'python', Python 2.6 or later is ...
最新文章
- Mysql(五) JDBC
- 解析html语言的软件,小程序解析html标签 div-Go语言中文社区
- java写左侧导航栏界面,jQuery----左侧导航栏面板切换实现
- 测试php数字范围_你不知道的接口测试之拾遗
- 关于form组件的补充-------formChoice
- BugkuCTF-MISC题where is flag3
- webpack4.0各个击破(3)—— Assets篇
- Oracle字符拆分函数,Oracle拆分字符串函数有哪些呢?
- python中list的切片和range函数
- iostat lsof
- 小程序如何避免多次点击,重复触发事件
- 关于蓝天准系统换京东方屏后不显bios的解决方案
- Java定时任务自动调用方法
- 标书的总结和感受(对标书整体流程的理解,和细节的把控
- markdown语言练习
- XV6 - bootsect.S
- 我国期货市场发展潜力和方向
- google-auto之自动生成组件化文件
- VMware Workstations 打开.vmx 虚拟机无反应问题
- (Tekla Structures二次开发)创建多边形板
热门文章
- php中empty和isset的用法
- 5、ByteBuffer(基础使用)
- f(t)=tu(t)matlab,设f(t)=ε(t)-ε(t-1),f1(t)=f(t)cos(10πt),试用MATLAB...
- 使用cmd命令行或运行框进行关机重启操作
- 2020 - 04 - 11 个人笔记
- 【CF940E】Cashback(单调队列dp)
- 软件测试方法比较(给新手)
- 【RPA】UIpath Academy BA篇
- js 数组元素交换位置
- 华为云云商店星品入“沪”,加速产业数字共赢!