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-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 };
参考资料
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值