您的位置:首页 > 其它

2.1 Qemu用户态 Machine与cpu管理

2015-07-26 15:53 633 查看
本节主要分析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,®s); kvm_vcpu_ioctl(env, KVM_SET_XSAVE, xsave);
kvm_vcpu_ioctl(env, KVM_SET_XCRS, &xcrs);, kvm_getput_reg(®s.rflags,&env->eflags, set);
kvm_getput_reg(®s.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, ®s);
}

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: