2. System mode下的机器(machine)管理
QEMU Sytem mode模拟的整个硬件平台,硬件平台最核心的是我们常说的板子,PC上常说是主板,其中包括了主要的元素如CPU、ROM空间、RAM空间等,而这里所说的machine就是对这个机器层次的模拟。
2.1 machine数据结构
qemu提供了通用的QEMUMachine结构用来描述machine,新增一个machine时只需在machine自己的文件中实例化该数据结构。实例化时最重要的是将自己的初始化函数注册在该数据结构中。
1) 通用接口
位置:hw/boards.h
typedef struct QEMUMachine { const char *name; const char *alias; const char *desc; //description QEMUMachineInitFunc *init; //machine init function,[board]_init int use_scsi; //use scsi standard int max_cpus; unsigned int no_serial:1, no_parallel:1, use_virtcon:1, no_vga:1, no_floppy:1, no_cdrom:1, no_sdcard:1; int is_default; //mark as default machine GlobalProperty *compat_props; struct QEMUMachine *next; //指向machine列表中下一个machine } QEMUMachine;
2) 接口实例化
例如arm的一板子做如下实例化:
static QEMUMachine integratorcp_machine = { .name = "integratorcp", .desc = "ARM Integrator/CP (ARM926EJ-S)", .init = integratorcp_init, .is_default = 1, };
2.2 machine相关接口
1)需要实现的接口
QEMUMachine [board]_machine |
描述machine的数据结构 |
宏machine_init([board]_machine_init) |
main之前注册[board]_machine_init函数 |
[board]_machine_init |
machine注册函数 |
[board]_init |
machine初始化函数 |
2)调用的通用接口
int qemu_register_machine(QEMUMachine *m) |
将当前machine注册到machine列表中 |
cpu_init (const char *cpu_model) |
cpu初始化 |
am_addr_t qemu_ram_alloc(DeviceState *dev, const char *name, ram_addr_t size) |
申请ram空间 |
staticinline void cpu_register_physical_memory(target_phys_addr_tstart_addr, ram_addr_tsize, ram_addr_t phys_offset |
对申请的内存空间分页 |
DeviceState *qdev_create(BusState *bus, const char *name) |
创建设备 |
void qdev_init_nofail(DeviceState *dev) |
设备初始化通过hw_error()结束程序 |
int qdev_init(DeviceState *dev) |
设备初始化,被qdev_init_nofail调用 |
void qdev_prop_set(DeviceState *dev, const char *name, void *src, enum PropertyType type) |
设备属性设置 |
void sysbus_mmio_map(SysBusDevice *dev, int n, target_phys_addr_t addr) |
io地址映射 |
DeviceState *sysbus_create_varargs(const char *name, target_phys_addr_t addr, ...) |
在总线上挂接设备,并完成设备的创建,初始化,地址映射以及中断初始化等操作 |
static inline DeviceState *sysbus_create_simple(const char *name, target_phys_addr_taddr, qemu_irq irq) |
sysbus_create_varargs的快捷方式,当设备只有一个对应的中断号调用此函数 |
qemu_irq qdev_get_gpio_in(DeviceState *dev, int n) |
2.3 machine注册
由上图可知,在qemu中,与machine相关的有两个链表,分别为init_type_list[MODULE_INIT_MACHINE]和machine链表。
1) 由init_type_list[MODULE_INIT_MACHINE]指示的链表记录了machine的注册函数,其数据结构如下:
文件:module.c
static ModuleTypeList init_type_list[MODULE_INIT_MAX]; typedef struct ModuleEntry { module_init_type type; //链表类型 void (*init)(void); //machine的注册函数 QTAILQ_ENTRY(ModuleEntry) node; } ModuleEntry; module.h typedef enum { MODULE_INIT_BLOCK, MODULE_INIT_DEVICE, MODULE_INIT_MACHINE, MODULE_INIT_MAX } module_init_type;
链表形成后如图所示:
2) machine链表
machine链表结构如上图所示,节点数据结构为QEMUMachine,其中的init成员记录了machine的初始化函数,next成员指向下一个machine。
下面分别介绍两个链表的创建过程。
2.3.1 init_type_list[MODULE_INIT_MACHINE]生成
machine中需要实现宏接口machine_init(func),func为machine的注册函数。machine_init(func)的处理流程如下:
编译器对所有的machine_init进行处理,便生成了由init_type_list[MODULE_INIT_MACHINE]指示的链表。
2.3.2 machine链表生成
main函数通过module_call_init(MODULE_INIT_MACHINE)来调用init_type_list[MODULE_INIT_MACHINE]中每个节点的初始化函数[board]_machine_init来注册每个节点对应的machine的数据结构到machine队列中。其过程如下图所示。
通过上图所示过程,qemu中便生成了machine链表,记录了每个machine的基本信息。
拥有了每个machine的基本信息,在运行时就可根据-machine参数找到指定的machine进行初始化。不存在-machine参数时,将默认machine作为要运行的开发板。之后再调用选定machine的初始化函数。machine的初始化过程在下节中介绍。
2.4 machine初始化
此处以arm的integrator/CP开发板为例介绍machine的初始化过程。
不论是总线还是中断控制器都可看做设备,设备创建过程以及设备如何连接总线将在设备机制中介绍。
版权声明:本文为博主原创文章,未经博主允许不得转载。