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             ...
  1. bus类(如i2c_bus)通常没太多可说的,主要包含一些私有成员在设备创建时使用。 比如分配给device的IRQ,某些时候甚至没有。比如SysBus重用BusState。
  2. bus超类(如i2c_slave)通常包含地址和他们所含的中断线。
  3. 设备子类包含设备所特有的配置信息(比如所连的快设备和字符设备)和寄存器。

元信息及层次结构

如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
原创粉丝点击