qemu 设备模拟
更新时间:2024-05-02 06:05:01 阅读量: 综合文库 文档下载
- qemu推荐度:
- 相关推荐
设备模拟目的
我们好像不会干一件事而毫无目的,就算不停刷微信朋友圈也是为了打发你无聊的时间。
其实最装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,
正在阅读:
qemu 设备模拟05-02
2016简短的表白语句02-10
第一章追求远大理想,坚定崇高信念分析09-17
九年级科学下册 3_2_2 来自微生物的威胁教案 (新版)浙教版04-08
非转农10-02
2015宁波管理会计知识竞赛试题及答案09-16
财务管理学(专)阶段练习204-20
抗精神病药引起静坐不能中循证护理的应用10-11
- 多层物业服务方案
- (审判实务)习惯法与少数民族地区民间纠纷解决问题(孙 潋)
- 人教版新课标六年级下册语文全册教案
- 词语打卡
- photoshop实习报告
- 钢结构设计原理综合测试2
- 2014年期末练习题
- 高中数学中的逆向思维解题方法探讨
- 名师原创 全国通用2014-2015学年高二寒假作业 政治(一)Word版
- 北航《建筑结构检测鉴定与加固》在线作业三
- XX县卫生监督所工程建设项目可行性研究报告
- 小学四年级观察作文经典评语
- 浅谈110KV变电站电气一次设计-程泉焱(1)
- 安全员考试题库
- 国家电网公司变电运维管理规定(试行)
- 义务教育课程标准稿征求意见提纲
- 教学秘书面试技巧
- 钢结构工程施工组织设计
- 水利工程概论论文
- 09届九年级数学第四次模拟试卷
- 模拟
- 设备
- qemu
- 《诊家正眼》
- 计算机网络第四版课后英文题目
- 一键U盘装系统v3.3版使用教程
- 2015年甘肃会计基础考试大纲(已修订)
- 临县乡校教研工作考核方案
- 甘肃省行政执法人员综合法律知识考试题库
- 船舶电气与自动化11规则考试大纲
- 《统计学》期末重点复习题(1-8章)附答案
- QAR的深度挖掘和应用
- 扬州性详细规划编制技术规定(2013试行)
- 2018学年第一学期七年级科学期中练习参考答案
- H部 - 电学
- 年生产9万吨环氧氯丙烷项目环氧氯丙烷可研报告 - 图文
- 生态文明建设
- 2016-2017年北京市海淀区中考数学二模试题及答案
- 正扬国贸中心(商务公寓部分)营销策划报告1
- 公路工程
- 海上货物运输评估项目“货物积载与系固 - 散装谷物船舶积载及稳
- Mmuyza笔试试题-J2EE开发技术
- 6、2016年7月份贵州省普通高中学业水平考试通用技术试卷及答案