当前位置: 首页 > Linux

用isa-i8259设备分析qemu-kvmqbus和qdev模型

时间:2023-04-06 23:41:00 Linux

qemu-kvmqbus和qdev模型本文使用qemu-kvm-1.5.3代码分析qemu-kvmqbus和qdev的实现机制。qemu-kvm中的CPU、网卡、虚拟磁盘等一切都是设备,这些设备的实现都是基于DeviceState、BusState等基本数据结构。1.DeviceState等基本数据结构之间的关系DeviceState、BusState、Object、ObjectClass等数据结构是qemu-kvm设备模型的基本数据结构。它们的相互关系如下图所示。1.1DeviceState数据结构DeviceState即设备状态,定义如下:structDeviceState{/**/Objectparent_obj;/**/constchar*id;布尔实现;QemuOpts*选择;内部热插拔;总线状态*parent_bus;intnum_gpio_out;qemu_irq*gpio_out;intnum_gpio_in;qemu_irq*gpio_in;QLIST_HEAD(,BusState)child_bus;intnum_child_bus;intinstance_id_alias;intalias_required_for_version;};opts中的id会被赋值给实现的设备,表示该设备是否已经实现。以cpu为例,在创建cpu时会调用object_property_set_bool(OBJECT(cpu),true,"realized",&local_err)将realized设置为true,调用栈如下:#0device_set_realized(obj=0x555557568000,value=true,err=0x7fffffffdcf0)athw/core/qdev.c:676#10x00005555556fde7einproperty_set_bool(obj=0x555557568000,v=,opaque=0x555556cdf960,name=,erfff=0x7f0qom/object.c:1302#20x00005555556fff57inobject_property_set_qobject(obj=0x555557568000,value=,name=0x55555585a16a"realized",errp=0x7fffffffdcf0)atqom/qom-qobject.c:24#30x00005555556ff140inobject_property_set_bool(obj=obj@entry=0x5650557,value=0x5550557)entry=true,name=name@entry=0x55555585a16a“实现”,errp=errp@entry=0x7fffffffdcf0)在qom/object.c:853#40x00005555557619ae在pc_new_cpu(cpu_model=cpu_model@entry=0x7fffffffe54a“主机”,apic_id=0,icc_bridge=icc_bridge@entry=0x55557560000,errp=errp@entry=0x7ffffffffdd30)在/home/lrs/project/new_qemu_libvirt_repo/qemu-kvm-1_5_3_160_el7_pa/hw/i386/pc.c:916#50x0005752555=0x7ffffffffe54a“主机”,icc_bridge=icc_bridge@entry=0x555557560000)在/home/lrs/project/new_qemu_libvirt_repo/qemu-kvm-1_5_3_160_el7_pa/hw/i386/pc.c:972#60x0000555555763ein81(pccivenabledmenabledite=81,pccivenabled_in81=1,system_io=0x555556d38210,system_memory=0x555556d38160,args=0x7fffffffdf8opts,指向创建这个设备所用的选项,qemu-kvm在解析启动命令时,会根据参数生成opts,然后根据opts创建设备对象parent_bus,指向parentbus。任何设备创建都将调用qdev_try_create(bus,type)。该函数的总线参数是设备父总线。在函数中调用qdev_set_parent_bus()为设备设置parentbusgpio_out和gpio_in。当信号发送到设备的PIN时,应调用设备相应的gpio_in处理程序以指示其状态发生变化。如果需要输出,则调用gpio_out对应的handler,模拟信号从一台设备发送到另一台设备。从目前的qemu-kvm用户态实现来看,这些handler并不是直接基于设备的调用点child_bus,即设备的子总线,而当qbus_create()调用qbus_realize()实现子总线时,子总线将被插入父设备的子总线。总线链表。1.2BusState数据结构BusState数据结构如下:structBusState{Objectobj;设备状态*父级;常量字符*名称;intallow_hotplug;int最大索引;QTAILQ_HEAD(ChildrenHead,BusChild)孩子;QLIST_ENTRY(BusState)sibling;};obj,BusState的对象,对于BusState的实现,需要通过对象找到object_class,根据对象类对应的函数实例化BusState,BusState的兄弟BusState1.3Object和ObjectClassObject的数据结构如下:structObject{/**/ObjectClass*class;对象免费*免费;QTAILQ_HEAD(,ObjectProperty)属性;uint32_t参考;Object*parent;};class,指向Object的具体类free(),释放Object使用的callbackproperties,Object属性ref,refcountparrent,指向父对象ObjectClass如下:structTypeImpl{constchar*name;size_tclass_size;size_t实例大小;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);布尔摘要;常量字符*父母;TypeImpl*parent_type;对象类*类;intnum_interfaces;InterfaceImplinterfaces[MAX_INTERFACES];};structObjectClass{/**/类型类型;GSList*接口;constchar*cast_cache[OBJECT_CLASS_CAST_CACHE];ObjectUnparent*unparent;};其中class_init()函数使用了初始化类,class_base_init(),所有ObjectClass实现都是基于TypeInfo2.i8259设备实现以i8259为例,分析其实现机制2.1i8259类注册TypeInfoi8259_info定义如下:staticconstTypeInfoi8259_info={.name="isa-i8259",.instance_size=sizeof(PICCommonState),.parent=TYPE_PIC_COMMON,.class_init=i8259_class_init,};type_init(pic_register_type)是__attribute__((constructor))属性的函数,这个属性的函数会在main()之前使用函数开始传输。即在main()函数启动之前,会调用pic_register_types()-->type_register_static(&i8259_info)注册i8259类。对于type_register_internal(),其实现主要是将TypeInfo中的name、parent、class_size、各种回调如class_init()、class_base_init()、class_finalize()等函数和接口赋值给TypeImpl。对于i8259,其TypeImpl最终如下图所示:2.2i8259初始化i8259设备初始化函数为i8259_init(),QEMUMachine数据结构中会有回调QEMUMachineInitFunc*init。当qemu-kvm的main()函数解析完用户参数后,会调用machine->init(&args)初始化虚拟机。目前,qemu-kvm定义了多种机器。以x86平台为例,有:pc-i440fx-rhel7.0.0、rhel6.6.0、rhel6.4.0、rhel6。3.0、rhel6.2.0、pc-q35-1.5、pc-q35-1.4等,这些机器都是通过machine_init()函数注册的,具体类似于type_init(),是一个__attribute__((constructor))函数,它在main()函数启动之前执行。注意:在qemu-kvm的main()函数中,会根据-M$machine,或者-machinemachine=$machine获取机器类型,然后调用machine_parse()函数获取具体的机器。本文以“pc-i440fx-rhel7.0.0”为例,以diablekvm_kernel_irqchip(-machineparameterappendkernel_irq=off)为例,分析i8259设备初始化过程,调用pc_init1()函数。在初始化i8259设备之前,首先通过pc_allocate_cpu_irq()函数分配cpu_irq,然后使用cpu_irq[0]作为i8259设备的parentirq来初始化i8259设备。pc_allocate_cpu_irq()通过qemu_allocate_irqs(pic_irq_request,NULL,1)分配一个1个数组元素的qemu_irq数组,即handler=pic_irq_requst,opaque=NULL,array_size=1。cpu_irq如下图所示,使用了pic_irq_request()将中断注入vcpu。分析i8258_init()函数,其主要流程如下图所示,最终创建两个i8259设备,一个是master,一个是slave:关键函数是i8259_init_chip()函数,流程如下:2.3object_class_by_name()函数object_class_by_name()函数基于TypeName初始化类,具体实现如下:以type_initialize("isa-i8259")函数为例,生成的isa-i8259TypeImplby最后的初始化如下,主要实现了TypeImpl类相关的数据结构,主要是初始化类的内部状态和函数实现指针。2.4object_new_with_type(i8259_impl)实例化object_new_with_type()函数用于实例化对象,其流程如下:对于isa-i8259,由于是先递归,然后调用type->instance_init(),实例化的执行流程流程其实就是先执行祖先的instance_init(),即object_instance_init()--->device_initfn()--->isa_device_init()。object_instance_init()函数主要是设置设备实例初始化的“type”属性device_initfn(),主要是在创建虚拟机后的设备init时会设置为hotplugged状态,但此时realized=false加入“realized”"属性会将自己类的props和父类的属性添加到设备属性状态中。global定义的设备属性被添加到设备属性状态中。&dev->parent_bus作为“parent_bus”属性opaque添加到设备属性列表中(parent_bus还没有设置,都使用它的地址加入)isa_device_init()函数主要初始化isairq[0]和isairq[1]到-1。设备初始化完成后如下:2.5qdev_set_parent_bus()将设备插入parent_bus。2.6设备实例化isa-i8259设备初始化完成后,遗憾的是要调用qdev_init()函数来实例化设备,这会调用device_set_realized()来实例化设备。device_set_realized()函数的实现逻辑如下:通过object_property_add_child()将名称为“device[]”的设备对象挂到容器目录root/machine/unattached/目录下,调用dc->realize(),即device_realize,实例化设备如下Transformation:device_realize(dev)-->dc=DEVICE_GET_CLASS(dev)-->dc->init(dev)//isa_device_class_init()willassignthecallbacktoisa_qdev_init-->isa_qdev_init(开发);-->klass=ISA_DEVICE_GET_CLASS(dev)-->klass->init(dev)//pic_common_class_init()将回调分配给pic_init_common-->pic_init_common(dev)-->s=PIC_COMMON(dev)-->info=PIC_COMMON_GET_CLASS(s)-->info->init(s)-->pic_init(s)-->isa_register_ioport()-->...更新isai8259设备vmsd相关域dev->realized=true,即完成设备实例化3.isa-i8259设备中断产生机制对于目前在x86架构上使用qemu-kvm的虚拟机来说,其实并没有直接使用isa-i8259设备中断。通过gsi(全局系统中断)调用,或通过坑计时器调用。