qemu2 machine的注册和的选择

在qemu里面,machine代表一台要虚拟的硬件机器,那么qemu是如何注册和选择机器的?我们今天就来分析一下
我们以i386机器为例子进行分析

首先

# For now, use stubs/sdl-null.c as an empty/fake SDL UI backend.
# TODO: Use the glue code to use the Qt-based UI instead.
LOCAL_SRC_FILES += \
    android-qemu2-glue/main.cpp \
    $(call qemu2-if-target,x86 x86_64, \
        hw/i386/acpi-build.c \
        hw/i386/pc_piix.c \
        ) \
    $(call qemu2-if-windows, \
        android-qemu2-glue/stubs/win32-stubs.c \
        ) \
    vl.c \

在build/Makefile.qemu2-target.mk下面定义了参与编译的文件,对于x86或者x64使用pc_piix.c用于注册machine信息。

hw/i386/pc_piix.c 代码很多,大概注册了10几种支持的主办信息
piix的英文解释为PCI IDE ISA Xcelerator (PIIX),is a family of Intel southbridge microchips。所以这些都是intel家族的芯片配置.

我们来看看machine信息是如何注册的。
以pc-i440fx-2.12 为例进行分析,英特尔440fx(代号为natoma)

static void pc_i440fx_2_12_machine_options(MachineClass *m)
{
    pc_i440fx_machine_options(m);
    m->alias = "pc";
    m->is_default = 1;
}

DEFINE_I440FX_MACHINE(v2_12, "pc-i440fx-2.12", NULL,
                      pc_i440fx_2_12_machine_options);
#define DEFINE_I440FX_MACHINE(suffix, name, compatfn, optionfn) \
    static void pc_init_##suffix(MachineState *machine) \
    { \
        void (*compat)(MachineState *m) = (compatfn); \
        if (compat) { \
            compat(machine); \
        } \
        pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \
                 TYPE_I440FX_PCI_DEVICE); \
    } \
    DEFINE_PC_MACHINE(suffix, name, pc_init_##suffix, optionfn)

这段代码其实就是把machine注册到qom系统里面, 关于qom请参考文章qemu2的qom系统分析
pc_i440fx_2_12_machine_options函数对于qom的class_init,也就是用处初始化qcom的class。

machine的初始化则在machin的选择部分

static MachineClass *select_machine(void)
{
    MachineClass *machine_class = find_default_machine();
    const char *optarg;
    QemuOpts *opts;
    Location loc;

    loc_push_none(&loc);

    opts = qemu_get_machine_opts();
    qemu_opts_loc_restore(opts);

    optarg = qemu_opt_get(opts, "type");
    if (optarg) {
        machine_class = machine_parse(optarg);
    }

    if (!machine_class) {
        error_report("No machine specified, and there is no default");
        error_printf("Use -machine help to list supported machines\n");
        return NULL;
    }

    loc_pop(&loc);
    return machine_class;
}

函数只要执行的操作有,1 找到默认的Machine类 2 参数是否指定machine,如果没有指定则使用默认的。 从参数中获取的部分我就不分析了,machine 的初始化就是在find_default_machine中,所以我们重点分析。

MachineClass *find_default_machine(void)
{
    GSList *el, *machines = object_class_get_list(TYPE_MACHINE, false);
    MachineClass *mc = NULL;

    for (el = machines; el; el = el->next) {
        MachineClass *temp = el->data;

        if (temp->is_default) {
            mc = temp;
            break;
        }
    }

    g_slist_free(machines);
    return mc;
}

函数主要找到类型为TYPE_MACHINE的MachineClass列表,之后找到MachineClass.is_default 为真的作为默认MachineClass返回。
我们主要分析object_class_get_list如何找到MachineClass

#define TYPE_MACHINE "machine"

GSList *object_class_get_list(const char *implements_type,
                              bool include_abstract)
{
    GSList *list = NULL;

    object_class_foreach(object_class_get_list_tramp,
                         implements_type, include_abstract, &list);
    return list;
}

void object_class_foreach(void (*fn)(ObjectClass *klass, void *opaque),
                          const char *implements_type, bool include_abstract,
                          void *opaque)
{
    OCFData data = { fn, implements_type, include_abstract, opaque };

    enumerating_types = true;
    g_hash_table_foreach(type_table_get(), object_class_foreach_tramp, &data);
    enumerating_types = false;
}

static void object_class_foreach_tramp(gpointer key, gpointer value,
                                       gpointer opaque)
{
    OCFData *data = opaque;
    TypeImpl *type = value;
    ObjectClass *k;

    type_initialize(type);
    k = type->clazz;

    if (!data->include_abstract && type->abstract) {
        return;
    }

    if (data->implements_type &&
        !object_class_dynamic_cast(k, data->implements_type)) {
        return;
    }

    data->fn(k, data->opaque);
}

static void object_class_get_list_tramp(ObjectClass *klass, void *opaque)
{
    GSList **list = opaque;

    *list = g_slist_prepend(*list, klass);
}

函数主要是遍历type_table_get() 这个hash table里面所有元素,使用object_class_foreach_tramp函数处理, 注意object_class_foreach_tramp函数首先执行type_initialize(type)初始化列表中的所有type,阅读qemu2的qom系统分析 这片文章后你应该执行type_table_get() 这个hash table里面注册了系统的所有qom class的类型信息,并且所有class只会初始化一次。
也就是说,第一次执行object_class_foreach_tramp,注册过的所有类型都会被初始化。select_machine函数正式第一次调用object_class_foreach_tramp的地方,所以这里所有类型的machine也被初始化了。
object_class_foreach_tramp初始化了所有对象之后,如果对应的ObjectClasss属于MachineClass或者它的子类,则正式我们要找的Class,交给object_class_get_list_tramp 函数处理。object_class_get_list_tramp函数只是把找到的MachineClass放在了传出参数列表上。 这样所有machineclass就被收集到了。

回过头来我们在看pc_i440fx_2_12_machine 的初始化,这里is_default被设置成1,所以它就是默认的machin.

static void pc_i440fx_2_12_machine_options(MachineClass *m)
{
    pc_i440fx_machine_options(m);
    m->alias = "pc";
    m->is_default = 1;
}

static void pc_i440fx_machine_options(MachineClass *m)
{
    PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
    pcmc->default_nic_model = "e1000";

    m->family = "pc_piix";
    m->desc = "Standard PC (i440FX + PIIX, 1996)";
    m->default_machine_opts = "firmware=bios-256k.bin";
    m->default_display = "std";
}

另外这里 m->default_machine_opts 把bios设置为bios-256k.bin。我们后续可以分析下如何加载bios

你可能感兴趣的:(qemu,qemu)