Qemu Qdev分析
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 qtree
bus: main-system-bustype Systemdev: hpet, id ""gpio-in 2gpio-out 1timers = 3msi = offirq 32mmio 00000000fed00000/0000000000000400dev: kvm-ioapic, id ""gpio-in 24gsi_base = 0irq 0mmio 00000000fec00000/0000000000001000dev: i440FX-pcihost, id ""irq 0bus: pci.0type PCIdev: virtio-balloon-pci, id ""indirect_desc = onevent_idx = onclass = 0xffaddr = 04.0romfile = rombar = 1multifunction = offcommand_serr_enable = onclass 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 = 45312disable_s3 = 0disable_s4 = 0s4_val = 2addr = 01.3romfile = rombar = 1multifunction = offcommand_serr_enable = onclass Bridge, addr 00:01.3, pci id 8086:7113 (sub 1af4:1100)bus: i2ctype i2c-busdev: smbus-eeprom, id ""address = 87dev: smbus-eeprom, id ""address = 86dev: smbus-eeprom, id ""address = 85dev: smbus-eeprom, id ""address = 84dev: smbus-eeprom, id ""address = 83dev: smbus-eeprom, id ""address = 82dev: smbus-eeprom, id ""address = 81dev: smbus-eeprom, id ""address = 80dev: piix3-ide, id ""addr = 01.1romfile = rombar = 1multifunction = offcommand_serr_enable = onclass IDE controller, addr 00:01.1, pci id 8086:7010 (sub 1af4:1100)bar 4: i/o at 0xc060 [0xc06f]bus: ide.1type IDEdev: ide-cd, id ""drive = ide1-cd0logical_block_size = 512physical_block_size = 512min_io_size = 0opt_io_size = 0bootindex = -1discard_granularity = 0ver = "1.3.50"wwn = 0x0serial = "QM00003"model = unit = 0bus: ide.0type IDEdev: ide-hd, id ""drive = ide0-hd0logical_block_size = 512physical_block_size = 512min_io_size = 0opt_io_size = 0bootindex = -1discard_granularity = 0ver = "1.3.50"wwn = 0x0serial = "QM00001"model = cyls = 16383heads = 16secs = 63bios-chs-trans = lbaunit = 0dev: e1000, id ""mac = 52:54:00:12:34:56vlan = 0netdev = hub0port0bootindex = -1addr = 03.0romfile = "pxe-e1000.rom"rombar = 1multifunction = offcommand_serr_enable = onclass 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 = 8addr = 02.0romfile = "vgabios-cirrus.bin"rombar = 1multifunction = offcommand_serr_enable = onclass 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.0romfile = rombar = 1multifunction = oncommand_serr_enable = onclass ISA bridge, addr 00:01.0, pci id 8086:7000 (sub 1af4:1100)bus: isa.0type ISAdev: isa-fdc, id ""iobase = 0x3f0irq = 6dma = 2driveA = floppy0driveB = bootindexA = -1bootindexB = -1check_media_rate = onisa irq 6dev: port92, id ""dev: vmmouse, id ""dev: vmport, id ""dev: i8042, id ""isa irqs 1,12dev: isa-parallel, id ""index = 0iobase = 0x378irq = 7chardev = parallel0isa irq 7dev: isa-serial, id ""index = 0iobase = 0x3f8irq = 4chardev = serial0wakeup = 0isa irq 4dev: isa-pcspk, id ""iobase = 0x61dev: kvm-pit, id ""gpio-in 1iobase = 0x40lost_tick_policy = delaydev: mc146818rtc, id ""base_year = 0lost_tick_policy = discarddev: kvm-i8259, id ""iobase = 0xa0elcr_addr = 0x4d1elcr_mask = 0xdemaster = offdev: kvm-i8259, id ""iobase = 0x20elcr_addr = 0x4d0elcr_mask = 0xf8master = ondev: i440FX, id ""addr = 00.0romfile = rombar = 1multifunction = offcommand_serr_enable = onclass Host bridge, addr 00:00.0, pci id 8086:1237 (sub 1af4:1100)bus: membus.pvtype dimmbusbus: membus.0type dimmbusdev: dimm, id "dimm1"start = 1207959552size = 1.000Gnode = 0populated = ondev: dimm, id "dimm0"start = 134217728size = 1.000Gnode = 0populated = ondev: fw_cfg, id ""ctl_iobase = 0x510data_iobase = 0x511irq 0mmio ffffffffffffffff/0000000000000002mmio ffffffffffffffff/0000000000000001dev: pc-sysfw, id ""rom_only = 1irq 0dev: kvmclock, id ""irq 0dev: kvm-apic, id ""id = 3vapic = onirq 0mmio ffffffffffffffff/0000000000100000dev: kvm-apic, id ""id = 2vapic = onirq 0mmio ffffffffffffffff/0000000000100000dev: kvm-apic, id ""id = 1vapic = onirq 0mmio ffffffffffffffff/0000000000100000dev: kvmvapic, id ""irq 0dev: kvm-apic, id ""id = 0vapic = onirq 0mmio 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树下。(如下图所示)
BusStatePCIBusISABusi2c_busDeviceStatePCIState /* bus common superclass */LSIState /* device-specific class */...ISADeviceIB700StateISASerialState...i2c_slaveWM8750State...
- bus类(如i2c_bus)通常没太多可说的,主要包含一些私有成员在设备创建时使用。 比如分配给device的IRQ,某些时候甚至没有。比如SysBus重用BusState。
- bus超类(如i2c_slave)通常包含地址和他们所含的中断线。
- 设备子类包含设备所特有的配置信息(比如所连的快设备和字符设备)和寄存器。
元信息及层次结构
如BusInfo,I2CSlaveInfo,DeviceInfo,主要用于存储类信息(属性和虚函数)。元信息的层次结构 模仿了上述BusState和DeviceState。
BusState <=> BusInfoPCIBus -> struct BusInfo pci_bus_info = ...ISABus -> struct BusInfo isa_bus_info = ...i2c_bus -> struct BusInfo i2c_bus_info = ...DeviceState <=> DeviceInfoPCIState <=> PCIDeviceInfoLSIState -> static PCIDeviceInfo lsi_info = ......ISADevice <=> ISADeviceInfoIB700State -> static ISADeviceInfo wdt_ib700_info = ...ISASerialState -> static ISADeviceInfo serial_isa_info = ......i2c_slave <=> I2CSlaveInfoWM8750State -> 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 TypeInfo328 {329 const char *name;330 const char *parent;331332 size_t instance_size;333 void (*instance_init)(Object *obj);334 void (*instance_finalize)(Object *obj);335336 bool abstract;337 size_t class_size;338339 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;343344 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
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
