Qemu Qdev分析
来源:互联网 发布:mac怎么安装外来软件 编辑:程序博客网 时间:2024/05/18 13:44
QEMU QDEV代码分析
Qemu Qdev设备原理,代码实现相关内容整理。
Qdev主要为了解决之前qemu没有统一的设备模型,导致设备配置方式混乱。
另外Qdev实现了guest设备的模拟,以及将向guest暴露host设备。
bus和device构成了一个设备树,设备树的根为sysBus。
- 原有的设备配置方式,设备类型不同,配置方式各异
-drive if=TYPE,index=IDX,bus=BUS,unit=UNIT,HOST-OPTS. . .-usbdevice disk:format=FMT:FILENAME-serial CHARDEV-parallel CHARDEV-usbdevice serial:vendorid=VID,productid=PRID,CHARDEV-usbdevice NAME-virtioconsole CHARDEV-net nic,vlan=VLAN,macaddr=MACADDR,model=MODEL,name=ID,addr=STR,vectors=V-usbdevice net:vlan=VLAN,macaddr=MACADDR,name=ID,addr=STR,vectors=V-vga VGA-soundhw C1,...-watchdog NAME-pcidevice host=ADDR,dma=none,id=ID-usbdevice host:auto:BUS.ADDR:VID:PRID
- 设备树的例子
(qemu) info qtreebus: main-system-bus type System dev: hpet, id "" gpio-in 2 gpio-out 1 timers = 3 msi = off irq 32 mmio 00000000fed00000/0000000000000400 dev: kvm-ioapic, id "" gpio-in 24 gsi_base = 0 irq 0 mmio 00000000fec00000/0000000000001000 dev: i440FX-pcihost, id "" irq 0 bus: pci.0 type PCI dev: virtio-balloon-pci, id "" indirect_desc = on event_idx = on class = 0xff addr = 04.0 romfile = rombar = 1 multifunction = off command_serr_enable = on class Class 00ff, addr 00:04.0, pci id 1af4:1002 (sub 1af4:0005) bar 0: i/o at 0xc040 [0xc05f] dev: PIIX4_PM, id "" smb_io_base = 45312 disable_s3 = 0 disable_s4 = 0 s4_val = 2 addr = 01.3 romfile = rombar = 1 multifunction = off command_serr_enable = on class Bridge, addr 00:01.3, pci id 8086:7113 (sub 1af4:1100) bus: i2c type i2c-bus dev: smbus-eeprom, id "" address = 87 dev: smbus-eeprom, id "" address = 86 dev: smbus-eeprom, id "" address = 85 dev: smbus-eeprom, id "" address = 84 dev: smbus-eeprom, id "" address = 83 dev: smbus-eeprom, id "" address = 82 dev: smbus-eeprom, id "" address = 81 dev: smbus-eeprom, id "" address = 80 dev: piix3-ide, id "" addr = 01.1 romfile = rombar = 1 multifunction = off command_serr_enable = on class IDE controller, addr 00:01.1, pci id 8086:7010 (sub 1af4:1100) bar 4: i/o at 0xc060 [0xc06f] bus: ide.1 type IDE dev: ide-cd, id "" drive = ide1-cd0 logical_block_size = 512 physical_block_size = 512 min_io_size = 0 opt_io_size = 0 bootindex = -1 discard_granularity = 0 ver = "1.3.50" wwn = 0x0 serial = "QM00003" model = unit = 0 bus: ide.0 type IDE dev: ide-hd, id "" drive = ide0-hd0 logical_block_size = 512 physical_block_size = 512 min_io_size = 0 opt_io_size = 0 bootindex = -1 discard_granularity = 0 ver = "1.3.50" wwn = 0x0 serial = "QM00001" model = cyls = 16383 heads = 16 secs = 63 bios-chs-trans = lba unit = 0 dev: e1000, id "" mac = 52:54:00:12:34:56 vlan = 0 netdev = hub0port0 bootindex = -1 addr = 03.0 romfile = "pxe-e1000.rom" rombar = 1 multifunction = off command_serr_enable = on class Ethernet controller, addr 00:03.0, pci id 8086:100e (sub 1af4:1100) bar 0: mem at 0xfeba0000 [0xfebbffff] bar 1: i/o at 0xc000 [0xc03f] bar 6: mem at 0xffffffffffffffff [0x1fffe] dev: cirrus-vga, id "" vgamem_mb = 8 addr = 02.0 romfile = "vgabios-cirrus.bin" rombar = 1 multifunction = off command_serr_enable = on class VGA controller, addr 00:02.0, pci id 1013:00b8 (sub 1af4:1100) bar 0: mem at 0xfc000000 [0xfdffffff] bar 1: mem at 0xfebf0000 [0xfebf0fff] bar 6: mem at 0xffffffffffffffff [0xfffe] dev: PIIX3, id "" addr = 01.0 romfile = rombar = 1 multifunction = on command_serr_enable = on class ISA bridge, addr 00:01.0, pci id 8086:7000 (sub 1af4:1100) bus: isa.0 type ISA dev: isa-fdc, id "" iobase = 0x3f0 irq = 6 dma = 2 driveA = floppy0 driveB = bootindexA = -1 bootindexB = -1 check_media_rate = on isa irq 6 dev: port92, id "" dev: vmmouse, id "" dev: vmport, id "" dev: i8042, id "" isa irqs 1,12 dev: isa-parallel, id "" index = 0 iobase = 0x378 irq = 7 chardev = parallel0 isa irq 7 dev: isa-serial, id "" index = 0 iobase = 0x3f8 irq = 4 chardev = serial0 wakeup = 0 isa irq 4 dev: isa-pcspk, id "" iobase = 0x61 dev: kvm-pit, id "" gpio-in 1 iobase = 0x40 lost_tick_policy = delay dev: mc146818rtc, id "" base_year = 0 lost_tick_policy = discard dev: kvm-i8259, id "" iobase = 0xa0 elcr_addr = 0x4d1 elcr_mask = 0xde master = off dev: kvm-i8259, id "" iobase = 0x20 elcr_addr = 0x4d0 elcr_mask = 0xf8 master = on dev: i440FX, id "" addr = 00.0 romfile = rombar = 1 multifunction = off command_serr_enable = on class Host bridge, addr 00:00.0, pci id 8086:1237 (sub 1af4:1100) bus: membus.pv type dimmbus bus: membus.0 type dimmbus dev: dimm, id "dimm1" start = 1207959552 size = 1.000G node = 0 populated = on dev: dimm, id "dimm0" start = 134217728 size = 1.000G node = 0 populated = on dev: fw_cfg, id "" ctl_iobase = 0x510 data_iobase = 0x511 irq 0 mmio ffffffffffffffff/0000000000000002 mmio ffffffffffffffff/0000000000000001 dev: pc-sysfw, id "" rom_only = 1 irq 0 dev: kvmclock, id "" irq 0 dev: kvm-apic, id "" id = 3 vapic = on irq 0 mmio ffffffffffffffff/0000000000100000 dev: kvm-apic, id "" id = 2 vapic = on irq 0 mmio ffffffffffffffff/0000000000100000 dev: kvm-apic, id "" id = 1 vapic = on irq 0 mmio ffffffffffffffff/0000000000100000 dev: kvmvapic, id "" irq 0 dev: kvm-apic, id "" id = 0 vapic = on irq 0 mmio 00000000fee00000/0000000000100000(qemu)
QDEV设备用法
- 配置device
通过“-device"指定 - 热插
通过device_add命令添加 - 热拔
通过device_del命令添加 - 读取配置文件
通过"-readconfig"命令
基本概念
- device
记录device的状态。设备只能有1个parent。一个device向下可以有0到多个bus。 device连接两个bus,并使之协做。device不知道上位bus是哪个device所暴露的。 device可以是设备树的叶子节点,这种情况最简单,他们通常只是将消息传递给bus或者的设备(网络,块,字符设备子系统)。或者为bus或者祖父设备提供服务。 通常device含有bus属性和device属性。 Host设备的副本,不属于设备树。 - bus 记录bus的状态。bus并不总是对应于物理世界存在的设备。每个child bus可以关联多个设备。bus对于用户来说是不可见的,bus连接两个device,使之进行协做。bus不知道上位device。
bus和device的层次结构
bus和device处于两个同等的继承树下,BusState和DeviceState。同一bus下的设备拥有同样的超类。 因此,每个bus会定义BusState的子类和DeviceState的抽象子类。然后设备添加他们的实体子类到DeviceState树下。(如下图所示)
BusState PCIBus ISABus i2c_bus DeviceState PCIState /* bus common superclass */ LSIState /* device-specific class */ ... ISADevice IB700State ISASerialState ... i2c_slave WM8750State ...
- bus类(如i2c_bus)通常没太多可说的,主要包含一些私有成员在设备创建时使用。 比如分配给device的IRQ,某些时候甚至没有。比如SysBus重用BusState。
- bus超类(如i2c_slave)通常包含地址和他们所含的中断线。
- 设备子类包含设备所特有的配置信息(比如所连的快设备和字符设备)和寄存器。
元信息及层次结构
如BusInfo,I2CSlaveInfo,DeviceInfo,主要用于存储类信息(属性和虚函数)。元信息的层次结构 模仿了上述BusState和DeviceState。
BusState <=> BusInfo PCIBus -> struct BusInfo pci_bus_info = ... ISABus -> struct BusInfo isa_bus_info = ... i2c_bus -> struct BusInfo i2c_bus_info = ... DeviceState <=> DeviceInfo PCIState <=> PCIDeviceInfo LSIState -> static PCIDeviceInfo lsi_info = ... ... ISADevice <=> ISADeviceInfo IB700State -> static ISADeviceInfo wdt_ib700_info = ... ISASerialState -> static ISADeviceInfo serial_isa_info = ... ... i2c_slave <=> I2CSlaveInfo WM8750State -> static I2CSlaveInfo wm8750_info = ... ...
注册Qdev设备
我们一般使用“-device”设置虚拟机的设备。注册设备名主要通过qdev_register函数。根据 Bus不同初始化过程也不相同,bus会为相关的设备进行初步的检查和共通的初始设置。
如图,bus的提供的设备共通注册过程:
void i2c_register_slave(I2CSlaveInfo *info) { assert(info->qdev.size >= sizeof(i2c_slave)); info->qdev.init = i2c_slave_qdev_init; info->qdev.bus_info = &i2c_bus_info; qdev_register(&info->qdev); }
如图,设备这么进行注册:
static void wm8750_register_devices(void) { i2c_register_slave(&wm8750_info); }
如图,wm8750_register_devices 在虚拟机初始化时调用。
device_init(wm8750_register_devices)
代码原理、解读
以memory hotplug为例解释qdev。 dimm相关定义了两个TypeInfo, TYPE_DIMM(Device类型)和 TYPE_DIMM_BUS(Bus类型)。
TypeInfo结构如下。其中class_init主要用于类的虚函数的初始化。instance_init主要用于 实例对象成员的初始化。
327 struct TypeInfo 328 { 329 const char *name; 330 const char *parent; 331 332 size_t instance_size; 333 void (*instance_init)(Object *obj); 334 void (*instance_finalize)(Object *obj); 335 336 bool abstract; 337 size_t class_size; 338 339 void (*class_init)(ObjectClass *klass, void *data); 340 void (*class_base_init)(ObjectClass *klass, void *data); 341 void (*class_finalize)(ObjectClass *klass, void *data); 342 void *class_data; 343 344 InterfaceInfo *interfaces; 345 };
继承BusState的DimmBus类。dimm_hotplug提供了设备hotplug的操作,dimm_revert提供了 对固件寄存器的操作。 通过dimm_bus_hotplug进行函数成员的初始化。具体函数实现与北桥芯片类型相关。
74 typedef struct DimmBus { 75 BusState qbus; 76 DeviceState *dimm_hotplug_qdev; 77 dimm_hotplug_fn dimm_hotplug; 78 DimmConfiglist dimmconfig_list; 79 dimm_hotplug_fn dimm_revert; 80 QTAILQ_HEAD(Dimmlist, DimmDevice) dimmlist; 81 QTAILQ_HEAD(dimm_hp_result_head, dimm_hp_result) dimm_hp_result_queue; 82 QLIST_ENTRY(DimmBus) next; 83 } DimmBus;
继承DeviceState的DimmDevice类,添加了dimm设备特有的信息。
45 struct DimmDevice { 46 DeviceState qdev; 47 uint32_t idx; /* index in memory hotplug register/bitmap */ 48 ram_addr_t start; /* starting physical address */ 49 ram_addr_t size; 50 uint32_t node; /* numa node proximity */ 51 uint32_t populated; /* 1 means device has been hotplugged. Default is 0. */ 52 MemoryRegion *mr; /* MemoryRegion for this slot. !NULL only if populated */ 53 dimm_hp_pending_code pending; /* pending hot operation for this dimm */ 54 QTAILQ_ENTRY(DimmDevice) nextdimm; 55 };
参考资料
- “qdev for programmers writeup”
http://lists.nongnu.org/archive/html/qemu-devel/2011-07/msg00842.html
- Qemu Qdev分析
- QEMU qDev 分析
- qemu 源码分析
- qemu Makefile 分析
- QEMU分页机制分析
- qemu执行流程分析
- qemu内存访问分析
- QEMU代码结构分析
- qemu-kvm 代码分析
- KVM,QEMU核心分析
- qemu 单核线程分析
- KVM,QEMU核心分析
- qemu-kvm代码分析
- qemu-kvm 代码分析
- qemu-kvm中断分析
- qemu翻译过程分析(二)
- qemu翻译过程分析(四)
- qemu源码分析系列(二)
- 混沌异或加密演示
- 比赛又一次 被虐 加油!
- 23、JSON详解
- Jquery和Jquery ui的区别
- android利用网络与服务器进行交互
- Qemu Qdev分析
- 高级项目管理师个人总结--项目计划
- 如何创建带参数的存储过程
- 模电和爱情
- Git基本教学
- 30岁之前需要知道的10个人生底线,你知道几个?
- ZOJ 1360 Radar Installation
- 384. Strike the iron while it is hot. 趁热打铁
- 公共场合的吃货们