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相关推荐

  1. VIRTIO后端框架QEMU与VHOST分析

    VIRTIO设备的前端是GUEST的内核驱动,后端由QEMU或者DPU实现.不论是原来的QEMU-VIRTIO框架还是现在的DPU,VIRTIO的控制面和数据面都是相对独立的设计.本文主要针对QEMU ...

  2. 欢迎使用 QEMU 的文档!| 目录

    Welcome to QEMU's documentation! - QEMU documentationhttps://qemu-project.gitlab.io/qemu/ About QEMU ...

  3. linux qemu原理,最全的剖析QEMU原理的文章3

    Block Unchaining 底下幾種情況會做 block unchaining.請見以下討論, cpu_exit (exec.c) → cpu_unlink_tb(exec.c).當某些情況需要 ...

  4. 01 QEMU仿真器-模拟器介绍

    QEMU仿真器-模拟器介绍 作者 将狼才鲸 创建日期 2022-05-28 所属的Gitee源码和工程地址:才鲸嵌入式 / 开源安防摄像机(嵌入式软件) 一.QEMU模拟器(仿真器)介绍 如果你也像我 ...

  5. qemu内存模型(3) 内存布局初始化

    以i386为例 static void memory_map_init(void) {system_memory = g_malloc(sizeof(*system_memory));memory_r ...

  6. qemu学习之添加一个新machine

    qemu学习之添加一个新machine 1 xlnx-versal-virt machine所在的代码 1.1 声明一个新的machine 1.1.1 xlnx-versal-virt machine ...

  7. 我见过最全的剖析QEMU原理的文章[Z]

    转自: http://people.cs.nctu.edu.tw/~chenwj/dokuwiki/doku.php?id=qemu How To Become A Hacker 写给新手程序员的一封 ...

  8. QEMU零知识学习2 —— QEMU源码下载

    QEMU源代码有多种方式进行下载.本文只介绍其中2种方法. 1. 通过git进行下载开发中的最新的qemu.git (1)获取源码 $ git clone http://git.qemu.org/qe ...

  9. AFL学习(一)-补充QEMU模式

    QEMU模式测试 编译QEMU 参考README.qemu $./build_qemu_support.sh# Cannot use 'python', Python 2.6 or later is ...

最新文章

  1. Mysql(五) JDBC
  2. 解析html语言的软件,小程序解析html标签 div-Go语言中文社区
  3. java写左侧导航栏界面,jQuery----左侧导航栏面板切换实现
  4. 测试php数字范围_你不知道的接口测试之拾遗
  5. 关于form组件的补充-------formChoice
  6. BugkuCTF-MISC题where is flag3
  7. webpack4.0各个击破(3)—— Assets篇
  8. Oracle字符拆分函数,Oracle拆分字符串函数有哪些呢?
  9. python中list的切片和range函数
  10. iostat lsof
  11. 小程序如何避免多次点击,重复触发事件
  12. 关于蓝天准系统换京东方屏后不显bios的解决方案
  13. Java定时任务自动调用方法
  14. 标书的总结和感受(对标书整体流程的理解,和细节的把控
  15. markdown语言练习
  16. XV6 - bootsect.S
  17. 我国期货市场发展潜力和方向
  18. google-auto之自动生成组件化文件
  19. VMware Workstations 打开.vmx 虚拟机无反应问题
  20. (Tekla Structures二次开发)创建多边形板

热门文章

  1. php中empty和isset的用法
  2. 5、ByteBuffer(基础使用)
  3. f(t)=tu(t)matlab,设f(t)=ε(t)-ε(t-1),f1(t)=f(t)cos(10πt),试用MATLAB...
  4. 使用cmd命令行或运行框进行关机重启操作
  5. 2020 - 04 - 11 个人笔记
  6. 【CF940E】Cashback(单调队列dp)
  7. 软件测试方法比较(给新手)
  8. 【RPA】UIpath Academy BA篇
  9. js 数组元素交换位置
  10. 华为云云商店星品入“沪”,加速产业数字共赢!