在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