qemu 设备模拟

更新时间:2024-05-02 06:05:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

设备模拟目的

我们好像不会干一件事而毫无目的,就算不停刷微信朋友圈也是为了打发你无聊的时间。

其实最装B的回答是:设备模拟的目的就是模拟设备。这话是屁话,不过也能说明些什么,确实是模拟设备,用软件的方式提供硬件设备具备的功能。

对于和PC机交互的硬件设备,主要要干两件事,一是提供IRQ中断,二是响应IO输入输出。IO包括PIO/MMIO/DMA等(DMA算不算IO?) 以i8254.c实现的pit为例,主要提供了IRQ注入和PIO响应,见初始化函数pit_initfn:

static const MemoryRegionOps pit_ioport_ops = { .read = pit_ioport_read, .write = pit_ioport_write, .impl =

{ .min_access_size = 1, .max_access_size = 1, }, .endianness = DEVICE_LITTLE_ENDIAN, }; static int pit_initfn(PITCommonState *pit) { PITChannelState *s; s = &pit->channels[0]; /* the timer 0 is connected to an IRQ */ //这里有个irq_timer,用于qemu_set_irq提供中断注入 s->irq_timer = qemu_new_timer_ns(vm_clock, pit_irq_timer, s); qdev_init_gpio_out(&pit->dev.qdev, &s->irq, 1); memory_region_init_io(&pit->ioports, &pit_ioport_ops, pit, \1); return 0; }

Static const MemoryRegionOps pit_ioport_ops={ .read=pit_ioport_read,

.write=pit_ioport_write, .impl={

.min_access_size=1, .max_access_size=1, },

.endianness=DEVICE_LITTLE_ENDIAN, };

Static int pit_initfn(PITCommonState * pit) {

PITChannelState*s;

s=&pit->channels[0];

/* the timer 0 is connected to an IRQ */

//这里有个irq_timer,用于qemu_set_irq提供中断注入

s->irq_timer=qemu_new_timer_ns(vm_clock,pit_irq_timer,s); qdev_init_gpio_out(&pit->dev.qdev,&s->irq,1);

memory_region_init_io(&pit->ioports,&pit_ioport_ops,pit,\,4);

qdev_init_gpio_in(&pit->dev.qdev,pit_irq_control,1); return0; }

这里的pit_ioport_ops,主要注册GUEST操作系统读写PIO时候的回调函数。

模块注册

QEMU要模拟模块那么多,以程序员的喜好,至少得来一套管理这些模拟设备模块的接口,以示设计良好。 QEMU将被模拟的模块分为了四类:

typedef enum { MODULE_INIT_BLOCK, MODULE_INIT_MACHINE, MODULE_INIT_QAPI, MODULE_INIT_QOM, MODULE_INIT_MAX } module_init_type;

typedefenum{ MODULE_INIT_BLOCK,

MODULE_INIT_MACHINE, MODULE_INIT_QAPI, MODULE_INIT_QOM, MODULE_INIT_MAX }module_init_type;

?

BLOCK

比如磁盘IO就属于BLOCK类型,e.g: block_init(bdrv_qcow2_init); block_init(iscsi_block_init);

?

MACHINE

PC虚拟machine_init(pc_machine_init); XEN半虚拟化machine_init(xenpv_machine_init); MIPS虚拟machine_init(mips_machine_init);

?

QAPI

QEMU GUEST AGENT模块里面会执行QAPI注册的回调

?

QOM

QOM树大枝多,儿孙满堂,应该是这里面最复杂的一个,我们重点介绍。 e.g:

ObjectClass -> PCIDeviceClass

//显卡type_init(cirrus_vga_register_types),

//网卡 type_init(rtl8139_register_types) IDEDeviceClass //IDE硬盘或CD-ROM type_init(ide_register_types) ISADeviceClass //鼠标键盘type_init(i8042_register_types),RTC时钟

type_init(pit_register) SysBusDeviceClass //MMIO IDE(IDE设备直接连接CPU bus而不是连接IDE

controller)type_init(mmio_ide_register_types) CPUClass -> X86CPUClass //X86 CPU架构 -> CRISCPUClass

ObjectClass-> PCIDeviceClass //显卡type_init(cirrus_vga_register_types),网卡type_init(rtl8139_register_types)

IDEDeviceClass //IDE硬盘或CD-ROM 1

type_init(ide_register_types) 2

ISADeviceClass //鼠标键盘3

type_init(i8042_register_types),RTC时钟type_init(pit_register) 4

SysBusDeviceClass//MMIO IDE(IDE设备5

直接连接CPU bus而不是连接IDE

controller)type_init(mmio_ide_register_types) CPUClass -> X86CPUClass //X86 CPU架构 -> CRISCPUClass

注册QOM设备的时候,使用QEMU提供的宏,type_init宏进行注册: #define type_init(function) module_init(function,

MODULE_INIT_QOM) #define module_init(function, type) \\ static void __attribute__((constructor)) do_qemu_init_ ##

function(void) { \\ register_module_init(function, type); \\ }

#define type_init(function) module_init(function, MODULE_INIT_QOM) 1

#define module_init(function, type) \\ 2

staticvoid__attribute__((constructor))do_qemu_init_## 3

function(void) { \\ 4

register_module_init(function,type);\\ 5 }

这和写LINUX驱动类似,一般写在一个模块实现文件的最底部,以pit为例,写的是type_init(pit_register_types)展开后为: static void __attribute__((constructor)) do_qemu_init_pit_register_types(void) {

register_module_init(pit_register_types, MODULE_INIT_QOM);

}

staticvoid__attribute__((constructor))do_qemu_init_pit_register_types(void) {

register_module_init(pit_register_types,MODULE_INIT_QOM); }

那么,这个do_qemu_init_pit_register_types何时调用? 在gcc里面,给函数加上__attribute__((destructor)),表示此函数需要在main开始前自动调用,测试调用顺序是: 全局对象构造函数 -> __attribute__((constructor)) -> main -> 全局对象析构函数 -> __attribute__((destructor))。

调用register_module_init就是将pit_register_types回调函数插入util\\module.c里定义的init_type_list[MODULE_INIT_QOM]链表内。 void register_module_init(void (*fn)(void), module_init_type type) { ModuleEntry *e; ModuleTypeList *l; e =

g_malloc0(sizeof(*e)); e->init = fn; //init指针被设置为fn l = find_type(type); QTAILQ_INSERT_TAIL(l, e, node); }

voidregister_module_init(void(*fn)(void),module_init_typetype) {

ModuleEntry*e; ModuleTypeList*l;

e=g_malloc0(sizeof(*e));

e->init=fn;//init指针被设置为fn l=find_type(type);

QTAILQ_INSERT_TAIL(l,e,node); }

通过下面main函数的部分代码可以看出,模块初始化顺序是QOM->MACHINE->BLOCK,至于QAPI,在这个流程里没看到。 void main() {

module_call_init(MODULE_INIT_QOM); //初始化设备 qemu_add_opts //初始化默认选项

module_call_init(MODULE_INIT_MACHINE); //初始化机器类型 machine = find_default_machine(); //这里对machine赋值,下面还会通过参数更改machine

vtp_script_execute(g_qemu_start_hook_path, g_fairsched_string, TYPE_START); //开机启动脚本的调用 深度分析启动参数 bdrv_init_with_whitelist -> bdrv_init ->

module_call_init(MODULE_INIT_BLOCK); //初始化BLOCK设备 machine->init(&args); //初始化machine

qemu_run_machine_init_done_notifiers(); //初始化成功回调通知 qemu_system_reset(VMRESET_SILENT);//system reset 启动运行 if (loadvm) { load_vmstate(loadvm); } else if (loadstate) { load_state_from_blockdev(loadstate); } resume_all_vcpus(); main_loop(); //进入主循环 }

Void main() {

module_call_init(MODULE_INIT_QOM);//初始化设备 qemu_add_opts//初始化默认选项

module_call_init(MODULE_INIT_MACHINE);//初始化机器类型

machine=find_default_machine();//这里对machine赋值,下面还会通过参数更改machine vtp_script_execute(g_qemu_start_hook_path,g_fairsched_string,TYPE_START);//开机启动脚本的调用 深度分析启动参数

bdrv_init_with_whitelist->bdrv_init->module_call_init(MODULE_INIT_BLOCK);//初始化BLOCK设备

machine->init(&args);//初始化machine

}

qemu_run_machine_init_done_notifiers();//初始化成功回调通知 qemu_system_reset(VMRESET_SILENT);//system reset 启动运行 if(loadvm){

load_vmstate(loadvm); }elseif(loadstate){

load_state_from_blockdev(loadstate); }

resume_all_vcpus();

main_loop();//进入主循环

在main函数进来的时候,首先调用module_call_init(MODULE_INIT_QOM);

void module_call_init(module_init_type type) { ModuleTypeList *l; ModuleEntry *e; l = find_type(type); QTAILQ_FOREACH(e, l, node) { e->init(); //这里,就是调用刚才注册的回调,例如,对于kvm-pit来说,调用的是pit_register } }

voidmodule_call_init(module_init_typetype) {

ModuleTypeList*l; ModuleEntry*e; l=find_type(type); QTAILQ_FOREACH(e,l,node){

e->init();//这里,就是调用刚才注册的回调,例如,对于kvm-pit来说,调用的是pit_register } }

此module_call_init将依次调用注册的回调,如PIT的pit_register_types:

static const TypeInfo pit_info = {

.name = \做为type_table的key

.parent = \父类型,这个比较重要,如果本TypeInfo没有设置class_size,会根据parent获取parent TypeImpl的class_size

.instance_size = sizeof(PITCommonState),//分配实例的大小 .class_init = pit_class_init, //初始化函数 };

static void pit_register_types(void) { type_register_static(&pit_info); }

Static const TypeInfopit_info = {

.name =\做为type_table的key

.parent =\//父类型,这个比较重要,如果本TypeInfo没有设置class_size,会根据parent获取parent TypeImpl的class_size

.instance_size=sizeof(PITCommonState),//分配实例的大小 .class_init=pit_class_init, //初始化函数 };

Static void pit_register_types(void) {

type_register_static(&pit_info); }

pit_register_types又进一步调用type_register_static -> type_register -> type_register_internal,这个函数完成的功能其实只是在qom\\object.c的type_table里插入了一个HASH键值 对,以TypeInfo的name为KEY,malloc了一个TypeInfo结构的超集TypeImpl为VALUE,在以name为KEY回溯 parent时需要TypeImpl,其实这个hash也可以做成一个tree。

QOM的Object模型

以pit为例,通过回溯parent你可以看到,其定义TypeInfo最终形成一个继承关系:

\-> \-> \-> \-> \qom\\object.c

static TypeInfo object_info = { .name = \

.instance_size = sizeof(Object), .instance_init = object_instance_init, .abstract = true, };

Static TypeInfo object_info={ .name=\

.instance_size=sizeof(Object), .instance_init=object_instance_init, .abstract=true, };

hw\\qdev.c

static const TypeInfo device_type_info = { .name = \sizeof(DeviceState), .instance_init = device_initfn, .instance_finalize = device_finalize, .class_base_init = device_class_base_init, .class_init =

device_class_init, .abstract = true, .class_size = sizeof(DeviceClass), };

staticconstTypeInfodevice_type_info={ .name=\ .parent=\

.instance_size=sizeof(DeviceState), .instance_init=device_initfn,

.instance_finalize=device_finalize,

.class_base_init=device_class_base_init, .class_init=device_class_init, .abstract=true,

.class_size=sizeof(DeviceClass), };

hw\\isa-bus.c

static const TypeInfo isa_device_type_info = { .name = \sizeof(ISADevice), .abstract = true, .class_size =

sizeof(ISADeviceClass), .class_init = isa_device_class_init, };

staticconstTypeInfoisa_device_type_info={ .name=\ .parent=\

.instance_size=sizeof(ISADevice), .abstract=true,

.class_size=sizeof(ISADeviceClass), .class_init=isa_device_class_init, };

hw\\i8254_common.c

static const TypeInfo pit_common_type = { .name = \sizeof(PITCommonState), .class_size =

sizeof(PITCommonClass), .class_init = pit_common_class_init, .abstract = true, };

staticconstTypeInfopit_common_type={

.name =\ .parent =\ .instance_size=sizeof(PITCommonState), .class_size =sizeof(PITCommonClass), .class_init =pit_common_class_init, .abstract =true, };

hw\\i8254.c

static const TypeInfo pit_info = { .name = \\

sizeof(PITCommonState), .class_init = pit_class_initfn, };

staticconstTypeInfopit_info={

.name =\ .parent =\ .instance_size=sizeof(PITCommonState), .class_init =pit_class_initfn, };

由于TypeInfo只是注册时临时使用,而TypeImpl是TypeInfo的超集,所以,这层关系也反应了TypeImpl的继承关系。

struct TypeImpl { 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_finalize)(Object *obj); bool abstract; const char

*parent; TypeImpl *parent_type; ObjectClass *class; int num_interfaces; InterfaceImpl interfaces[MAX_INTERFACES]; };

1 structTypeImpl 2 {

3 constchar*name; 4 size_tclass_size; 5 size_tinstance_size;

6 void(*class_init)(ObjectClass*klass,void*data);

7 void(*class_base_init)(ObjectClass*klass,void*data); 8 void(*class_finalize)(ObjectClass*klass,void*data); 9 void*class_data; 10 void(*instance_init)(Object*obj); 11 void(*instance_finalize)(Object*obj); 12 boolabstract; 13 constchar*parent; 14 TypeImpl*parent_type; 15 ObjectClass*class; 16 intnum_interfaces; 17 InterfaceImplinterfaces[MAX_INTERFACES]; 18 };

Figure 1 TypeImpl图解 打印查看TypeImpl属性:

(gdb) p *obj->class->type //struct TypeImpl * type

$13 = {name = 0x5555566e5e30 \instance_size = 664, class_init = 0x55555579b790 , class_base_init = 0,

class_finalize = 0, class_data = 0x0, instance_init = 0, instance_finalize = 0, abstract = false, parent = 0x5555566e5e50 \

parent_type = 0x5555566d8bd0, class = 0x555556a50e50, num_interfaces = 0, interfaces = {{typename = 0x0} }} 其主要包含如下部分:

? ?

name/parent/parent_type 表示自己的,父亲的KEY和TypeImpl指针。 class/class_size/class_init/class_base_init/class_finalize/class_data 和ObjectClass联系,组成继承关系。

?

instance_init/instance_finalize和ObjectClass有裙带关系的Object,共同完成继承体系。

?

num_interfaces/interfaces 用于管理接口。 Object和ObjectClass的关系 还是通过这条继承链来看:

\-> \-> \-> \-> \其中ObjectClass链的定义为:

struct ObjectClass { /*< private >*/ Type type; GSList *interfaces; ObjectUnparent *unparent; }; typedef struct DeviceClass { /*<

private >*/ ObjectClass parent_class; /*< public >*/ const char *fw_name; const char *desc; Property *props; int no_user; /* callbacks */ void (*reset)(DeviceState *dev); DeviceRealize realize; DeviceUnrealize unrealize; /* device state */ const struct VMStateDescription *vmsd; /* Private to qdev / bus. */ qdev_initfn init; /* TODO remove, once users are converted to realize */ qdev_event unplug; qdev_event exit; const char *bus_type; } DeviceClass; typedef struct ISADeviceClass { DeviceClass parent_class; int (*init)(ISADevice *dev); } ISADeviceClass; typedef struct PITCommonClass { ISADeviceClass parent_class; int (*init)(PITCommonState *s); void

(*set_channel_gate)(PITCommonState *s, PITChannelState *sc, int val); void (*get_channel_info)(PITCommonState *s, PITChannelState *sc, PITChannelInfo *info); void (*pre_save)(PITCommonState *s); void

(*post_load)(PITCommonState *s); } PITCommonClass;

1 structObjectClass 2 {

3 /*< private >*/ 4 Typetype;

5 GSList*interfaces;

6 ObjectUnparent*unparent; 7 };

8 typedefstructDeviceClass{ 9 /*< private >*/

1 ObjectClassparent_class; 0 /*< public >*/ 1 constchar*fw_name;

1 constchar*desc; 1 Property*props; 2 intno_user; 1 /* callbacks */

3 void(*reset)(DeviceState*dev); 1 DeviceRealizerealize; 4 DeviceUnrealizeunrealize; 1 /* device state */

5 conststructVMStateDescription*vmsd; 1 /* Private to qdev / bus. */

6 qdev_initfninit;/* TODO remove, once users are converted to 1realize */

7 qdev_eventunplug; 1 qdev_eventexit; 8 constchar*bus_type; 1}DeviceClass;

9 typedefstructISADeviceClass{ 2 DeviceClassparent_class; 0 int(*init)(ISADevice*dev); 2}ISADeviceClass;

1 typedefstructPITCommonClass{

2 ISADeviceClassparent_class; 2 int(*init)(PITCommonState*s);

2 void(*set_channel_gate)(PITCommonState*s,PITChannelState*sc3 ,intval);

2 void(*get_channel_info)(PITCommonState*s,PITChannelState*sc4 ,

2 PITChannelInfo*info);

5 void(*pre_save)(PITCommonState*s); 2 void(*post_load)(PITCommonState*s); 6 }PITCommonClass; 27 28 29 30 31 32 3

object_class_get_name(iface)); } //4.如果本类型有自己的interfaces,初始化 for (i = 0; i < ti->num_interfaces; i++) { TypeImpl *t = type_get_by_name(ti->interfaces[i].typename); 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,

ti->interfaces[i].typename); } } ti->class->type = ti; while (parent) { if (parent->class_base_init) { //回溯回调parent的class_base_init函数 parent->class_base_init(ti->class, ti->class_data); } parent = type_get_parent(parent); } if (ti->class_init) { /* 如果本类设置了class_init,回调它,ti->class_data是一个void*的参数 比如\我们设置了pit_class_initfn 这个函数主要干啥?主要填充class里的其他该填充的地方。 malloc之后你总得调用构造函数吧,调用构造函数的第一句都是super(xxx) 这工作前面,2,3步骤已经做了,然后干你自己的活。见pit_class_initfn定义 */ ti->class_init(ti->class, ti->class_data); } }

1 2 3 4 5 6 7 8

DeviceState*qdev_try_create(BusState*bus,constchar*type) {

DeviceState*dev;

//这个type为TypeInfo.name,例如\ if(object_class_by_name(type)==NULL){ returnNULL; }

//type_initialize完成后,object_new用来实例化一个instance

9 dev=DEVICE(object_new(type));// =

10 DEVICE(object_new_with_type(type_get_by_name(typename))) 11 if(!dev){

12 returnNULL; 13 }

14 if(!bus){

15 bus=sysbus_get_default(); 16 }

17 qdev_set_parent_bus(dev,bus); 18 object_unref(OBJECT(dev)); 19 returndev; 20 } 21

22 ObjectClass*object_class_by_name(constchar*typename) 23 {

24 //之前在type_register_static的时候,注册了TypeInfo.name,25 例如\为key的TypeImpl

26 TypeImpl*type=type_get_by_name(typename); 27 if(!type){

28 returnNULL; 29 }

30 type_initialize(type);//这里面,初始化class, 31 returntype->class; 32 } 33

34 //其实这个函数更应该叫做new_TypeInfo_class() 35 staticvoidtype_initialize(TypeImpl*ti) 36 {

37 TypeImpl*parent; 38 if(ti->class){ 39 return; 40 } 41 /*

42 type_class_get_size 首先获取自己的class_size变量,如果没有,43 再找parent类型所指的TypeImpl的class_size,直到找到为止

44 比如\没有设置class_size,那么获取的是\45 的class_size, 而type_object_get_size也是类似 46 static const TypeInfo pit_common_type = {

47 .name = \48 .parent = \49 .instance_size = sizeof(PITCommonState), 50 .class_size = sizeof(PITCommonClass), 51 .class_init = pit_common_class_init, 52 .abstract = true,

53 }; 54 */

55 ti->class_size=type_class_get_size(ti); 56 ti->instance_size=type_object_get_size(ti); 57 ti->class=g_malloc0(ti->class_size); 58 parent=type_get_parent(ti); 59 if(parent){

60 //1,保证parent初始化了 61 type_initialize(parent); 62 GSList*e; 63 inti; 64 65 //2,将parent的class内容memcpy一份给自己的对应的66 parent区域

67 g_assert(parent->class_size<=ti->class_size); 68 memcpy(ti->class,parent->class,parent->class_size)69 ; 70

71 //3,将parent里面的class的interfaces做一次深度复72 制,复制给自己

73 for(e=parent->class->interfaces;e;e=e->next){ 74 ObjectClass*iface=e->data;

75 type_initialize_interface(ti,object_class_76 get_name(iface)); 77 } 78

79 //4.如果本类型有自己的interfaces,初始化 80 for(i=0;inum_interfaces;i++){

81 TypeImpl*t=type_get_by_name(ti->interfaces82 [i].typename);

83 for(e=ti->class->interfaces;e;e=e->next){ 84 TypeImpl*target_type=OBJECT_CLASS(85 e->data)->type;

86 if(type_is_ancestor(target_type,t)87 ){

88 break; 89 } 90 }

91 if(e){

92 continue; 93 }

94 type_initialize_interface(ti,ti->interface95 s[i].typename); 96 }

97 } 98

99 ti->class->type=ti; 10 while(parent){

0 if(parent->class_base_init){

10 //回溯回调parent的class_base_init函数 1 parent->class_base_init(ti->class,ti->clas10s_data);

2 }

10 parent=type_get_parent(parent); 3 } 10

4 if(ti->class_init){ 10 /*

5 如果本类设置了class_init,回调它,ti->class_data是10一个void*的参数

6 比如\我们设置了pit_class_initfn

这个函数主要干啥?主要填充class里的其他该填充的地方。

malloc之后你总得调用构造函数吧,调用构造函数的第一句都是super(xxx)

这工作前面,2,3步骤已经做了,然后干你自己的活。见pit_class_initfn定义 */

ti->class_init(ti->class,ti->class_data); } }

上述代码把object_class_by_name的流程说完了,再看看object_new(type) =

object_new_with_type(type_get_by_name(typename))的流程: Object *object_new_with_type(Type type) { Object *obj; g_assert(type != NULL); type_initialize(type); obj = g_malloc(type->instance_size); //这个instance_size是初始化TypeInfo的时候设置的sizeof(PITCommonState)

object_initialize_with_type(obj, type); obj->free = g_free;

return obj; } void object_initialize_with_type(void *data, TypeImpl *type) { Object *obj = data; g_assert(type != NULL); type_initialize(type); g_assert(type->instance_size >= sizeof(Object)); g_assert(type->abstract == false); memset(obj, 0, type->instance_size); obj->class = type->class; //instace的类型通过class指针指定 object_ref(obj);

QTAILQ_INIT(&obj->properties); object_init_with_type(obj, type); //深度递归调用TypeImpl及其parent的instance_init函数指针,相当于new instance的构造函数 }

Object*object_new_with_type(Typetype) 1 {

2 Object*obj;

3 g_assert(type!=NULL); 4 type_initialize(type);

5 obj=g_malloc(type->instance_size);//这个instance_size是初6 始化TypeInfo的时候设置的sizeof(PITCommonState) 7 object_initialize_with_type(obj,type); 8 obj->free=g_free; 9 returnobj; 10 } 11 voidobject_initialize_with_type(void*data,TypeImpl*type) 12 { 13 Object*obj=data; 14 g_assert(type!=NULL); 15 type_initialize(type); 16 g_assert(type->instance_size>=sizeof(Object)); 17 g_assert(type->abstract==false); 18 memset(obj,0,type->instance_size); 19 obj->class=type->class;//instace的类型通过class指针指定 20 object_ref(obj); 21 QTAILQ_INIT(&obj->properties); 22 object_init_with_type(obj,type);//深度递归调用TypeImpl及其23 parent的instance_init函数指针,相当于new instance的构造函数 }

qdev_try_create->object_class_by_name->type_initialize的调用流程,如果父 ObjectClass没初始化,会初始化父ObjectClass,此时调用到父ObjectClass对应name的TypeImpl的 class_init函数,例如\的class_init回调pit_common_class_init,此回调会设置 ISADeviceClass的init回调为pit_init_common。 Object类型转换

再解释下Object里常用的一个宏:OBJECT_CHECK,以ISA_DEVICE这段代码为例:

static const TypeInfo mc146818rtc_info = { .name = \sizeof(RTCState), .class_init = rtc_class_initfn, }; ISADevice *isa_create(ISABus *bus, const char *name) { DeviceState *dev; if (!bus) { hw_error(\bus present.\return ISA_DEVICE(dev);//毫无疑问,\肯定可以转换为\,why?见mc146818rtc_info定义 } ISADevice *dev = isa_create(bus, \

1 staticconstTypeInfomc146818rtc_info={

2 .name =\3 .parent =\4 .instance_size=sizeof(RTCState),

5 .class_init =rtc_class_initfn, 6 };

7 ISADevice*isa_create(ISABus*bus,constchar*name) 8 {

9 DeviceState*dev;

10 if(!bus){ 11 hw_error(\12 bus present.\13 } 14 dev=qdev_create(&bus->qbus,name); 15 returnISA_DEVICE(dev);//毫无疑问,\肯定可以转换16 为\,why?见mc146818rtc_info定义 }

ISADevice*dev=isa_create(bus,\

这里的ISA_DEVICE体现了OBJECT的类型转换功能,宏定义为: #define OBJECT(obj) \\ ((Object *)(obj)) #define OBJECT_CHECK(type, obj, name) \\ ((type

*)object_dynamic_cast_assert(OBJECT(obj), (name))) #define ISA_DEVICE(obj) \\ OBJECT_CHECK(ISADevice, (obj), TYPE_ISA_DEVICE)

1 #define OBJECT(obj) \\ 2 ((Object*)(obj)) 3 #define OBJECT_CHECK(type, obj, name) \\ 4 ((type*)object_dynamic_cast_assert(OBJECT(obj),(name))) 5 #define ISA_DEVICE(obj) \\ 6 OBJECT_CHECK(ISADevice,(obj),TYPE_ISA_DEVICE)

展开后为:

#define ISA_DEVICE(dev)

(ISADevice*)object_class_dynamic_cast(((Object *)dev)->class, \

#define ISA_DEVICE(dev) 1 (ISADevice*)object_class_dynamic_cast(((Object *)dev)->class, \

object.c里面定义了object_class_dynamic_cast函数,其实此函数功能比较简单,就是通过遍历parent看当前class是否有一个祖先是typename,其定义如下:

ObjectClass *object_class_dynamic_cast(ObjectClass *class, const char *typename) { TypeImpl *target_type =

type_get_by_name(typename); //找到\对应的TypeImpl* TypeImpl *type = class->type; //本ObjectClass真实的TypeImpl*,其实这里是\*target_type $31 = {name = 0x5555566d0b20 \class_size = 128, instance_size = 160, class_init = 0x55555568ddd0 , class_base_init = 0, class_finalize = 0, class_data = 0x0, instance_init = 0, instance_finalize = 0, abstract = true, parent = 0x5555566d0a40 \= 0x5555566da730, class = 0x555556a14750, num_interfaces = 0, interfaces = {{typename = 0x0} }} (gdb) p *type_interface $33 = {name = 0x5555566e40b0 \instance_size = 0, class_init = 0, class_base_init = 0, class_finalize = 0, class_data = 0x0, instance_init = 0, instance_finalize = 0, abstract = true, parent = 0x0, parent_type = 0x0, class = 0x0, num_interfaces = 0, interfaces = {{ typename = 0x0} }} type_is_ancestor用于判断,\是否是\的祖先(判断方法是递归遍历\的parent,

比较是否有\),如果是,那么要考虑interface的情况,这里不是,且 type->num_interfaces为0 */ if

(type->num_interfaces && type_is_ancestor(target_type, type_interface)) { int found = 0; GSList *i; for (i =

class->interfaces; i; i = i->next) { ObjectClass *target_class = i->data; if (type_is_ancestor(target_class->type,

target_type)) { ret = target_class; found++; } } /* The match was ambiguous, don't allow a cast */ if (found > 1) { ret = NULL; } } else if (type_is_ancestor(type, target_type)) { /* 判断type=\的祖先是否是target_type=\, 如果是,这里表示子类class=\能成功转换为父类

typename=\所指的ObjectClass */ ret = class; } return ret; }

1 ObjectClass*object_class_dynamic_cast(ObjectClass*class,

2 3 constchar*typename) 4 {

5 TypeImpl*target_type=type_get_by_name(typename);//找到6 \对应的TypeImpl*

7 TypeImpl*type=class->type;//本ObjectClass真实的TypeImpl*,8 其实这里是\

9 ObjectClass*ret=NULL; 1 /*

0 (gdb) p *target_type

1$31 = {name = 0x5555566d0b20 \

1 instance_size = 160, class_init = 0x55555568ddd0 , class_base_init = 10,

2 class_finalize = 0, class_data = 0x0, instance_init = 0, 1instance_finalize = 0, abstract = true, parent = 0x5555566d0a40 3 \1 class = 0x555556a14750, num_interfaces = 0, interfaces = {{typename

4 = 0x0} }}

1 (gdb) p *type_interface 5 $33 = {name = 0x5555566e40b0 \class_size = 32, instance_size 1= 0, class_init = 0, class_base_init = 0, class_finalize = 0, class_data 6 = 0x0,

1 instance_init = 0, instance_finalize = 0, abstract = true, parent 7 = 0x0, parent_type = 0x0, class = 0x0, num_interfaces = 0, interfaces 1= {{

8 typename = 0x0} }} 1 type_is_ancestor 用于判断,\是否是\的祖9 先(判断方法是递归遍历\的parent,比较是否 有\),2如果是,那么要考虑interface的情况,这里不是,且type->num_interfaces0 为0

2 */

1 if(type->num_interfaces&&type_is_ancestor(target_type,type_2interface)){

2 intfound=0; 2 GSList*i;

3 for(i=class->interfaces;i;i=i->next){

2 ObjectClass*target_class=i->data;

4 if(type_is_ancestor(target_class->type,targ2et_type)){

5 ret=target_class; 2 found++; 6 } 2 }

7 /* The match was ambiguous, don't allow a cast */ 2 if(found>1){

8 ret=NULL; 2 }

9 }elseif(type_is_ancestor(type,target_type)){ 3 /*

0 判断type=\的祖先是否是3target_type=\,

1 如果是,这里表示子类class=\能成功转换为3父类typename=\所指的ObjectClass 2 */

3 ret=class; 3 }

3 returnret; 4 } 35 3

6 37 38 39 40

GDB显示其内部数据为: (gdb) p *obj //Object *obj

$11 = {class = 0x555556a50e50, free = 0x7ffff7424020 , properties = {tqh_first = 0x555556a17da0, tqh_last = 0x555556a50fd0}, ref = 1, parent = 0x0}

(gdb) p *obj->class //ObjectClass * class

$12 = {type = 0x5555566e5cb0, interfaces = 0x0, unparent = 0x5555556b1ac0 }

(gdb) p *obj->class->type //struct TypeImpl * type

$13 = {name = 0x5555566e5e30 \instance_size = 664, class_init = 0x55555579b790 , class_base_init = 0,

class_finalize = 0, class_data = 0x0, instance_init = 0, instance_finalize = 0, abstract = false, parent = 0x5555566e5e50 \

parent_type = 0x5555566d8bd0, class = 0x555556a50e50, num_interfaces = 0, interfaces = {{typename = 0x0} }}

QOM设备初始化

基于Object和ObjectClass实现的QOM设备,何时触发他的初始化,以PIT为例,将之前的Object和ObjectClass想象成C++,那么PIT对应的PitCommonState定义应该类似如下所示:

class PITCommonClass : public ISADeviceClass { public: virtual int init(PITCommonState *s) = 0; }; class ISADevice : public DeviceState { public: int nirqs; int ioport_id; }; class PITCommonState : public ISADevice, public PITCommonClass { int init(PITCommonState *s); };

Class PITCommonClass:publicISADeviceClass{ public:

virtual int init(PITCommonState*s)=0; };

Class ISADevice:publicDeviceState{ public: int nirqs; int ioport_id; };

Class PITCommonState:publicISADevice,publicPITCommonClass{ Int init(PITCommonState*s); };

看吧,QEMU绕了这么大一个圈子,就想实现这样一个结构,所以有的时候用C++还是有好处的(虽然本人生理周期现正处于不太喜欢C++时间)。 那么,何处调用了new PITCommonState()?

这得从main函数开始看,main函数里面,有machine->init(&args);函数调用,这是对注册的machine的初始 化,而默认的machine是在pc_piix.c里面pc_machine_init函数注册的第一个machine,即:

static QEMUMachine pc_i440fx_machine_v1_4 = { .name = \.alias = \

.desc = \.init = pc_init_pci, .max_cpus = 255, .is_default = 1,

.default_machine_opts = KVM_MACHINE_OPTIONS, DEFAULT_MACHINE_OPTIONS, };

qemu_register_machine(&pc_i440fx_machine_v1_4);

staticQEMUMachinepc_i440fx_machine_v1_4={ .name=\ .alias=\

.desc=\ .init=pc_init_pci, .max_cpus=255,

.is_default=1,

.default_machine_opts=KVM_MACHINE_OPTIONS, DEFAULT_MACHINE_OPTIONS, };

qemu_register_machine(&pc_i440fx_machine_v1_4);

当main函数调用machine->init时,我的实验环境默认情况其实就是调用的pc_i440fx_machine_v1_4的初始化回调pc_init_pci -> pc_init1,这个函数主要初始化相关PC硬件:

static void pc_init1(MemoryRegion *system_memory, MemoryRegion *system_io, ram_addr_t ram_size, const char *boot_device, const

char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, const char *cpu_model, int pci_enabled, int kvmclock_enabled) { //CPU类型初始化-> cpu_x86_init -> mce_init/qemu_init_vcpu,初始化VCPU pc_cpus_init(cpu_model); //初始化acpi_tables pc_acpi_init(\(!xen_enabled()) { //ROM, BIOS, RAM相关初始化 fw_cfg = pc_memory_init(system_memory, kernel_filename, kernel_cmdline, initrd_filename, below_4g_mem_size, above_4g_mem_size, rom_memory, &ram_memory); } //IRQ,初始化 //VGA初始化 pc_vga_init(isa_bus, pci_enabled ? pci_bus : NULL); /* init basic PC hardware */ pc_basic_device_init(isa_bus, gsi, &rtc_state, &floppy, xen_enabled()); //这里调用pit_init //初始化网卡 pc_nic_init(isa_bus, pci_bus); //初始化硬盘,音频设备 //初始化cmos数据,比如设置cmos rtc时钟,是否提供PS/2设备

pc_cmos_init(below_4g_mem_size, above_4g_mem_size, boot_device, floppy, idebus[0], idebus[1], rtc_state); //初始化USB if (pci_enabled && usb_enabled(false)) { pci_create_simple(pci_bus, piix3_devfn + 2, \

pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, ISADevice **rtc_state, ISADevice **floppy, bool no_vmport) { //初始化HPET //初始化mc146818 rtc //初始化i8042 PIT pit = pit_init(isa_bus,

0x40, pit_isa_irq, pit_alt_irq); //初始化串口,并口 //初始化vmmouse ps2_mouse }

staticvoidpc_init1(MemoryRegion*system_memory,

MemoryRegion*system_io, ram_addr_tram_size, constchar*boot_device, constchar*kernel_filename, constchar*kernel_cmdline, constchar*initrd_filename, constchar*cpu_model, intpci_enabled,

intkvmclock_enabled) {

//CPU类型初始化-> cpu_x86_init -> mce_init/qemu_init_vcpu,初始化VCPU

pc_cpus_init(cpu_model); //初始化acpi_tables

pc_acpi_init(\ if(!xen_enabled()){

//ROM, BIOS, RAM相关初始化

fw_cfg=pc_memory_init(system_memory,

kernel_filename,kernel_cmdline,initrd_filena me,

below_4g_mem_size,above_4g_mem_size, rom_memory,&ram_memory); }

//IRQ,初始化 //VGA初始化

pc_vga_init(isa_bus,pci_enabled?pci_bus:NULL); /* init basic PC hardware */

pc_basic_device_init(isa_bus,gsi,&rtc_state,&floppy,xen_enabled());//这里调用pit_init //初始化网卡

pc_nic_init(isa_bus,pci_bus); //初始化硬盘,音频设备

//初始化cmos数据,比如设置cmos rtc时钟,是否提供PS/2设备 pc_cmos_init(below_4g_mem_size,above_4g_mem_size,boot_device,

floppy,idebus[0],idebus[1],rtc_state); //初始化USB

if(pci_enabled&&usb_enabled(false)){

pci_create_simple(pci_bus,piix3_devfn+2,\

hci\ } }

voidpc_basic_device_init(ISABus*isa_bus,qemu_irq*gsi,

ISADevice**rtc_state,

ISADevice**floppy,

boolno_vmport) {

//初始化HPET

//初始化mc146818 rtc //初始化i8042 PIT

pit=pit_init(isa_bus,0x40,pit_isa_irq,pit_alt_irq); //初始化串口,并口

//初始化vmmouse ps2_mouse }

接下来的流程是pit_init -> isa_create(bus, \qdev_create -> qdev_try_create,qdev_try_create的实现在前面已经讲了,如上节所述,它分别使用 object_class_by_name和object_new来初始化ObjectClass和Object。

属性设置

Object同ObjectClass的显著区别就是Object提供了属性的概念,以MC146818为例,其定义时设置了\和\: static Property mc146818rtc_properties[] =

{ DEFINE_PROP_INT32(\DEFINE_PROP_LOSTTICKPOLICY(\lost_tick_policy, LOST_TICK_DISCARD), DEFINE_PROP_END_OF_LIST(), };

staticPropertymc146818rtc_properties[]={

};

DEFINE_PROP_INT32(\DEFINE_PROP_LOSTTICKPOLICY(\lost_tick_policy,LOST_TICK_DISCARD), DEFINE_PROP_END_OF_LIST(),

但是用GDB一看:

(gdb) p *obj->properties.tqh_first

$15 = {name = 0x555556a17df0 \\

release = 0x555555727980 , opaque = 0x555556a17d80, node = {tqe_next = 0x555556a17e50, tqe_prev = 0x555556a17af0}} (gdb) p *obj->properties.tqh_first.node.tqe_next

$21 = {name = 0x555556a17ea0 \\release = 0x555555727940 , opaque = 0x555556a17e30, node = {tqe_next = 0x555556a17ee0, tqe_prev = 0x555556a17dd0}} (gdb) p *obj->properties.tqh_first.node.tqe_next.node.tqe_next $22 = {name = 0x555556a17f30 \\= 0,

opaque = 0x555555d4e1a0, node = {tqe_next = 0x555556a50ee0, tqe_prev = 0x555556a17e80}} (gdb) p

*obj->properties.tqh_first.node.tqe_next.node.tqe_next.node.t

qe_next

$23 = {name = 0x555556a1be30 \0x555556a1be50 \0x5555556af990 ,

release = 0, opaque = 0x555555d4e1c0, node = {tqe_next = 0x555556a50fa0, tqe_prev = 0x555556a17f10}} (gdb) p

*obj->properties.tqh_first.node.tqe_next.node.tqe_next.node.tqe_next.node.tqe_next

$24 = {name = 0x555556a50f80 \type = 0x55555680b1d0 \

set = 0x55555572a320 , release = 0, opaque = 0x555556a17b30, node = {tqe_next = 0x0, tqe_prev = 0x555556a50f10}} (gdb) p

*obj->properties.tqh_first.node.tqe_next.node.tqe_next.node.tqe_next.node.tqe_next.node.tqe_next Cannot access memory at address 0x0

事实上却多了\,这些属性都是动态添加的。

在\类型的,instance_init = object_instance_init回调处,添加了

object_property_add_str(obj, \NULL);

1 object_property_add_str(obj,\

在\类型的instance_init = device_initfn回调处,添加了 object_property_add_bool(obj, \device_set_realized, NULL);

bject_property_add_link(OBJECT(dev), \(Object **)&dev->parent_bus, NULL);

object_property_add_bool(obj,\1 device_get_realized,2 device_set_realized,NULL); 3 bject_property_add_link(OBJECT(dev),\4 (Object**)&dev->parent_bus,NULL);

设置属性的时候,调用类似qdev_prop_set_int32(&dev->qdev, \base_year);进行设置,这里,注意第一个参数为什么是DeviceState* dev->qdev而不是ISADevice *dev?

因为rtc_class_initfn里初始化props是给 DeviceClass *dc初始化的,所以对应的应该是DeviceState而不是子类ISADevice。 设置属性是如何实现的?

以\的bool属性设置为例,调用顺序为

object_property_set_bool -> object_property_set_qobject -> object_property_set,此函数定义:

void object_property_set(Object *obj, Visitor *v, const char *name, Error **errp) { //obj还是\的实例,name为

\,object_property_find其实就是查找obj的properties链表里是否存在名字为name的属性 ObjectProperty *prop = object_property_find(obj, name, errp); if (prop == NULL) { return; } if (!prop->set) {//如果存在,且没有设置过set handler,错误 error_set(errp, QERR_PERMISSION_DENIED); } else

{//\的set函数为property_set_bool prop->set(obj, v, prop->opaque, name, errp); } } static void

property_set_bool(Object *obj, Visitor *v, void *opaque, const char *name, Error **errp) { BoolProperty *prop = opaque; bool value; Error *local_err = NULL; visit_type_bool(v, &value, name, &local_err); if (local_err) { error_propagate(errp, local_err); return; } prop->set(obj, value, errp); //对于realized来说,其实就是调用device_set_realized }

1 voidobject_property_set(Object*obj,Visitor*v,constchar*name, 2 Error**errp) 3 {

4 //obj还是\的实例,name为\,

5 object_property_find其实就是查找obj的properties链表里是否存在名字6 为name的属性

7 ObjectProperty*prop=object_property_find(obj,name,errp); 8 if(prop==NULL){ 9 return; 1 }

0 if(!prop->set){//如果存在,且没有设置过set handler,错误 1 error_set(errp,QERR_PERMISSION_DENIED); 1 }else{//\的set函数为property_set_bool 1 prop->set(obj,v,prop->opaque,name,errp); 2 } 1} 3

1staticvoidproperty_set_bool(Object*obj,Visitor*v,void*opaque,

本文来源:https://www.bwwdw.com/article/cv4g.html

Top