2.1 Qemu用户态 Machine与cpu管理

来源:互联网 发布:网络投票怎么刷票 编辑:程序博客网 时间:2024/06/05 22:43


本节主要分析PC机在Qemu中的构成结构,特别是CPU的相关结构

2.1.1设备对象模型

1. 设备创建

       DeviceState*qdev_create(BusState *bus, const char *name);

  该函数调用: DeviceState *qdev_try_create(BusState*bus, const char *type)

 

{

    DeviceState*dev;

    if(object_class_by_name(type) == NULL) {  

        returnNULL;

    }

    dev =DEVICE(object_new(type));

    if (!dev) {

        returnNULL;

    }

 

    if (!bus) {

        bus =sysbus_get_default();

    }

 

   qdev_set_parent_bus(dev, bus);

 

    return dev;

}

 

a. object_class_by_name:若dev 的类别未初始化,则初始化类别信息

       (1) type_get_by_name根据名称取得设备对应类别(例:"cirrus-vga " 对应TypeInfo cirrus_vga_info(hw/cirrus_vga.c) (TypeImpl 结构在type_register_static(&cirrus_vga_info);生成)

       (2) type_initialize: 如果该类别有parent, 在type_init parent, 如果有子interface 则调用type_initialize_interface; 最后调用type->class_init(cirrus_vga 为cirrus_vga_class_init0

 

b. object_new==> object_new_with_type: 根据类别信息创建设备对象:

  (1)obj = g_malloc(type->instance_size);

  (2) object_initialize_with_type(obj, type)

              初始化对象属性列表QTAILQ_INIT(&obj->properties);

              object_init_with_type(obj,type); //先初始化父对象,这里能体现继承关系

     在if (ti->instance_init)  ti->instance_init()初始化子对象

  (3)dev = DEVICE(object_new(type));

    (3) object_ref(obj);//增加引用记数

c. qdev_set_parent_bus

      dev->parent_bus = bus;

       bus_add_child(bus, dev);

 

(2) Pci 设备创建示例:

 

 pci_create_simple_multifunction(b,-1, true, "PIIX3") ==>

PCIDevice *pci_create_simple_multifunction(PCIBus*bus, int devfn,

                                           boolmultifunction,

                                          const char *name)

{

    PCIDevice*dev = pci_create_multifunction(bus, devfn, multifunction, name);

   qdev_init_nofail(&dev->qdev); // ==> qdev_init

    return dev;

}   

 

PCIDevice *pci_create_multifunction(PCIBus *bus, intdevfn, bool multifunction,

                                    const char*name)

{

    DeviceState*dev;

 

    dev =qdev_create(&bus->qbus, name);

   qdev_prop_set_int32(dev, "addr", devfn);

   qdev_prop_set_bit(dev, "multifunction", multifunction);

    returnPCI_DEVICE(dev);

}

 

int qdev_init(DeviceState *dev)

{

    DeviceClass*dc = DEVICE_GET_CLASS(dev);

    int rc;

 

   assert(dev->state == DEV_STATE_CREATED);

 

    rc =dc->init(dev); //对于cirrus_vga 为pci_cirrus_vga_initfn

    if (rc <0) {

       qdev_free(dev);

        returnrc;

    }

 

       .......

   dev->state = DEV_STATE_INITIALIZED;

    if(dev->hotplugged) {

       device_reset(dev);

    }

    return 0;

}

 

static void cirrus_vga_class_init(ObjectClass *klass,void *data)

{

    DeviceClass*dc = DEVICE_CLASS(klass);

   PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);

 

   k->no_hotplug = 1;

    k->init =pci_cirrus_vga_initfn;

   k->romfile = VGABIOS_CIRRUS_FILENAME;

    k->vendor_id= PCI_VENDOR_ID_CIRRUS;

   k->device_id = CIRRUS_ID_CLGD5446;

   k->class_id = PCI_CLASS_DISPLAY_VGA;

    dc->desc= "Cirrus CLGD 54xx VGA";

    dc->vmsd= &vmstate_pci_cirrus_vga; //该结构在第4章分析

}

 

(3) Device间的关系

dev = DEVICE(object_new(type));

#define DEVICE(obj) OBJECT_CHECK(DeviceState, (obj),TYPE_DEVICE) (qdev.h)

#define OBJECT_CHECK(type, obj, name) \

    ((type*)object_dynamic_cast_assert(OBJECT(obj), (name)))

 

struct Object

{

    /*<private >*/

    ObjectClass*class;

   QTAILQ_HEAD(, ObjectProperty) properties;

    uint32_tref;

    Object*parent;

};

struct ObjectClass

{

    /*<private >*/

    Type type;

    GSList*interfaces;

};

 

struct DeviceState {

    Objectparent_obj;

    const char*id;

    enumDevState state;

    BusState*parent_bus; //指向父bus, 如pci_bus

     。。。。。。

    QLIST_HEAD(,BusState) child_bus;

    intnum_child_bus;

     。。。。。。

};

dev->parent_obj和 object_new出来的obj是一个内存地址

注意区分DeviceState的parent_bus, 与 Type 的parent表明的是继承关系

parent_bus是指父总线,如cirrus_vga device 挂在pci总线上,则他的parent_bus 为pci_bus

但其object->parent是

 

static void bus_add_child(BusState *bus, DeviceState*child)

{

    charname[32];

    BusChild*kid = g_malloc0(sizeof(*kid));

 

       .......

   kid->index = bus->max_index++;

   kid->child = child;

 

   QTAILQ_INSERT_HEAD(&bus->children, kid, sibling);

   snprintf(name, sizeof(name), "child[%d]", kid->index);

   object_property_add_link(OBJECT(bus), name,

                     object_get_typename(OBJECT(child)),(Object **)&kid->child,  NULL);

}

 

每个object包含property,

       object_property_add//object.c

    object_property_find

    object_property_get;object_property_get_x (X为int bool 等)

 

在qdev-_init ==> dc->init 的回调中可以调用object_property_add来为对象初始化property,例如:

object_property_add(obj, "tsc-frequency","int",

                       x86_cpuid_get_tsc_freq,

                       x86_cpuid_set_tsc_freq, NULL, NULL, NULL); (x86_cpu_initfn)

 

2.1.2Machine初始化

一个PC机一般由CPU(LAPIC 8259, Timer(RTC....)), , Memory, PCI_Bus, IOAPIC,PCI_DEV(IDE/SATA, VGA, AUDIO, NetCard), BIOS(Flash, CMOS) .

本节将用设备模型来分析这些结构。

(1) pc_init1

 a. pc_cpus_init 下一节将分析该函数

 b. pc_memory_init 内存初始化

 c.     kvm_piix3_setup_irq_routing(pci_enabled);

        gsi =qemu_allocate_irqs(kvm_piix3_gsi_handler, gsi_state,

                                 GSI_NUM_PINS);

     中断路由表初始化

  d. i440fx_init pci 总线与isa总线初始化

  c. kvm_i8259_init(isa_bus); //i8259控制器初始化

  d.  ioapic_init(gsi_state);  //IOAPIC初始化

  e. pc_vga_init(isa_bus, pci_enabled ? pci_bus : NULL); //VGA 初始化

  f. pc_basic_device_init 上基本设备初始化

  g. 网卡, 硬盘初始化 声卡, cmos

      pci_piix3_ide_init pci_nic_init_nofail

       audio_init(isa_bus,pci_enabled ? pci_bus : NULL);

 

   pc_cmos_init(below_4g_mem_size, above_4g_mem_size, boot_device,

                 floppy, idebus[0], idebus[1],rtc_state);

  h. acpi e2prom初始化

   qemu_allocate_irqs(pc_acpi_smi_interrupt, first_cpu, 1);

        smbus = piix4_pm_init(pci_bus,piix3_devfn + 3, 0xb100,

                              gsi[9], *smi_irq,

                             kvm_enabled(),fw_cfg);

        smbus_eeprom_init(smbus, 8, NULL, 0);

 

(2) pc_memory_init

   a. Below 4G 内存最大直到0xE000_0000h, 0xE000_0000 到 0Xffff_ffff预留出来给其它内存映射使用

   b. pc_system_firmware_init 初始化Bios

   c. 初始化 rom区域内存

 

(3) PC FW(Bios) 初始化

  a. 创建PC-FW 设备对象

        sysfw_dev = (PcSysFwDevice*)qdev_create(NULL, "pc-sysfw");

        qdev_init_nofail(DEVICE(sysfw_dev))//对应type 为 pcsysfw_info (pc_sysfw.c)

  b. pflash_drv =drive_get(IF_PFLASH, 0, 0);

   对于非KVM是由IF_PFLASH block设备来读取bios内容, 但对于 kvm而言pflash_drv为空, 一下仅分析kvm case.

 c.          sysfw_dev->rom_only = 1;

            old_pc_system_rom_init(rom_memory);

      bios_size为文件大小(#define BIOS_FILENAME "bios.bin")

 

 (4) i440fx_init ==》 i440fx_common_init(piix_pci.c)

  a. 调用参数

   i440fx_init(&i440fx_state, &piix3_devfn, &isa_bus, gsi,

                              system_memory,system_io, ram_size,

                              below_4g_mem_size,

                              0x100000000ULL -below_4g_mem_size,

                              0x100000000ULL +above_4g_mem_size,

                             (sizeof(target_phys_addr_t) == 4

                               ? 0

                               : ((uint64_t)1<< 62)),

                              pci_memory,ram_memory);

 

  黑体部分为pci hole start and size.

 

  b. 创建设备

       dev = qdev_create(NULL,"i440FX-pcihost"); // i440fx_pcihost_info

   s = PCI_HOST_BRIDGE(dev);

   s->address_space = address_space_mem;

   b = pci_bus_new(dev, NULL, pci_address_space,

                    address_space_io, 0); // 建立pci bus 对象

   s->bus = b;

   object_property_add_child(qdev_get_machine(), "i440fx",OBJECT(dev), NULL);

   qdev_init_nofail(dev);

 

  c. 建立pci memory 空间

  d.       piix3 =DO_UPCAST(PIIX3State, dev,

               pci_create_simple_multifunction(b, -1, true, "PIIX3"));

        pci_bus_irqs(b, piix3_set_irq,pci_slot_get_pirq, piix3,

                PIIX_NUM_PIRQS);

        pci_bus_set_route_irq_fn(b,piix3_route_intx_pin_to_irq);

 

#define DO_UPCAST(type, field, dev)container_of(dev, type, field)

typedef struct PIIX3State {

   PCIDevice dev; //与pcidev 同一地址

    ....

}

 e. 得到isa_bus 对象

    *isa_bus = DO_UPCAST(ISABus,qbus,

                        qdev_get_child_bus(&piix3->dev.qdev, "isa.0"));

 在piix3_initfn中将创建isa

 static int piix3_initfn(PCIDevice *dev)

{

   PIIX3State *d = DO_UPCAST(PIIX3State, dev, dev);

 

   isa_bus_new(&d->dev.qdev, pci_address_space_io(dev));

   qemu_register_reset(piix3_reset, d);

   return 0;

}

isabus_bridge_info 在hw\isa-bus.c中

 

 (5) pc_basic_device_init

   a. 注册ioport

   b. 创建rtc_init 与hpet时钟

   c. kvm_pit_init 创建用于kvm 8254时钟

   d. serial_isa_init串口初始化

   e. isa鼠标键盘初始化

 

2.1.3 cpu初始化

(1) pc_cpus_init

  根据CPU数量调用 pc_new_cpu

               ==> cpu_x86_init

              ==> apic_init 如果CPU支持apic

              ==> cpu 复位cpu_reset

 

cpu_x86_init

 a. X86_CPU(object_new(TYPE_X86_CPU));创建cpu对象x86_cpu_type_info

   #ifdefTARGET_X86_64

          #defineTYPE_X86_CPU "x86_64-cpu"

       #else

              #defineTYPE_X86_CPU "i386-cpu"

       #endif

b. cpu_x86_register 创建 cpu对象的属性并初始化CPUX86State *env = &cpu->env;。

  CPUX86State 结构包含十分重要的管理信息如eip等

   例子: env->regs[CPU_NB_REGS]

 

c. x86_cpu_realize ==> qemu_init_vcpu  ==> qemu_kvm_start_vcpu ==>

  qemu_thread_create(cpu->thread,qemu_kvm_cpu_thread_fn, env,

                      QEMU_THREAD_JOINABLE);

 

 d. qemu_kvm_cpu_thread_fn ==> kvm_init_vcpu ==> kvm_arch_init_vcpu

   主要初始化cpuid信息存到

    struct {

        structkvm_cpuid2 cpuid;

        structkvm_cpuid_entry2 entries[100];

    }QEMU_PACKED cpuid_data; 结构中,

 并最后用kvm_vcpu_ioctl(env, KVM_SET_CPUID2,&cpuid_data); 存到内核态

 

kvm_init_vcpu==> qemu_register_reset(kvm_reset_vcpu,env);

QEMUResetEntry.func =  kvm_reset_vcpu

 

kvm_init_vcpu==> env->kvm_vcpu_dirty = 1;

 

(2) Cpu 复位

cpu_rese =》x86_cpu_reset

 a. tlb_flush(env,1);

 b.初始化 env中的idt, gdt

 c. cpu_x86_load_seg_cache初始化env中的 CS, DS ,ES,SS, FS,GS

 d.    env->eip = 0xfff0;

    env->regs[R_EDX]= env->cpuid_version;

   env->eflags = 0x2;

 e. 初始化env浮点, 断点等信息

 

 

(3) cpu运行

当main==>vm_start后,qemu_kvm_cpu_thread_fn==》kvm_cpu_exec

          if (env->kvm_vcpu_dirty) {

           kvm_arch_put_registers(env, KVM_PUT_RUNTIME_STATE);

           env->kvm_vcpu_dirty = 0;

        }

 a. kvm_arch_put_registers 会将env中的reg 通过

      kvm_vcpu_ioctl(env, KVM_SET_REGS,&regs); kvm_vcpu_ioctl(env, KVM_SET_XSAVE, xsave);

kvm_vcpu_ioctl(env, KVM_SET_XCRS, &xcrs);, kvm_getput_reg(&regs.rflags,&env->eflags, set);

    kvm_getput_reg(&regs.rip,&env->eip, set);kvm_vcpu_ioctl(env, KVM_SET_SREGS, &sregs);

kvm_vcpu_ioctl(env, KVM_SET_MSRS, &msr_data);

kvm_vcpu_ioctl(env, KVM_SET_LAPIC, &kapic);

kvm_vcpu_ioctl(env, KVM_SET_VCPU_EVENTS, &events);

kvm_vcpu_ioctl(env, KVM_SET_DEBUGREGS, &dbgregs);

kvm_vcpu_ioctl(env, KVM_SET_GUEST_DEBUG,&dbg_data->dbg);

同步到内核态. 例子:

    if (set) {

        ret =kvm_vcpu_ioctl(env, KVM_SET_REGS, &regs);

    }

 

b. kvm_arch_pre_run

 通过kvm_vcpu_ioctl(env, KVM_NMI);, kvm_vcpu_ioctl(env, KVM_INTERRUPT,&intr);预处理中断信息

 

c. kvm_vcpu_ioctl(env, KVM_RUN, 0);  通过内核态让cpu进入vm-entry

0 0