QEMU-System mode emulation分析(2),qemuusermode


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的初始化过程。



不论是总线还是中断控制器都可看做设备,设备创建过程以及设备如何连接总线将在设备机制中介绍。


版权声明:本文为博主原创文章,未经博主允许不得转载。

相关内容

    暂无相关文章