系列文章目录
第二十章 QEMU系统仿真的机器创建分析实例
文章目录
- 系列文章目录
- 第二十章 QEMU系统仿真的机器创建分析实例
- 前言
- 一、QEMU是什么?
- 二、QEMU系统仿真的机器创建分析实例
- 1.系统仿真的命令行参数
- 2. CPU 配置项解析
- parse_cpu_option()
- cpu_class_by_name()
- parse_features()
- 3.调试输出
- 总结
前言
本文以 QEMU 8.2.2 为例,分析其作为系统仿真工具的工作过程,并为读者展示各种 QEMU 系统仿真的启动配置实例。
本文读者需要具备一定的 QEMU 系统仿真使用经验,并对 C 语言编程有一定了解。
一、QEMU是什么?
QEMU 是一个通用且开源的机器模拟器和虚拟机。
其官方主页是:https://www.qemu.org/
二、QEMU系统仿真的机器创建分析实例
1.系统仿真的命令行参数
QEMU 作为系统仿真工具,其入口代码在 system/main.c 文件中,初始化函数 qemu_init() 的实现在 system/vl.c 文件中。
前文完成创建目标机器的过程分析,本文将继续后续运行过程的分析,读者需要对 QEMU 系统启动过程的程序代码有所了解,相关内容可以参考《QEMU系统分析之启动篇》系列文章。
..\qemu\8.2.2-qkd\qemu-system-x86_64.exe -cpu "Penryn,vendor=GenuineIntel,+ssse3,+sse4.2" -M "q35,accel=whpx,smm=off" -m "6G" -audio "sdl,model=hda" -vga "std" -netdev "user,id=mynet0" -device "e1000,id=nic1,netdev=mynet0" -L "data" -qtest "unix:qtest-sock,server,nowait"
2. CPU 配置项解析
这部分代码在 system/vl.c 文件中,实现如下:
int qemu_init(int argc, char **argv)
{
.../* parse features once if machine provides default cpu_type */current_machine->cpu_type = machine_class->default_cpu_type;if (cpu_option) {current_machine->cpu_type = parse_cpu_option(cpu_option);}/* NB: for machine none cpu_type could STILL be NULL here! */
...
}
前文分析了移对象的初始化过程,本文继续完成 CPU 配置项解析的过程。
parse_cpu_option()
函数 parse_cpu_option() 代码如下:
const char *parse_cpu_option(const char *cpu_option)
{ObjectClass *oc;CPUClass *cc;gchar **model_pieces;const char *cpu_type;model_pieces = g_strsplit(cpu_option, ",", 2);if (!model_pieces[0]) {error_report("-cpu option cannot be empty");exit(1);}oc = cpu_class_by_name(CPU_RESOLVING_TYPE, model_pieces[0]);if (oc == NULL) {error_report("unable to find CPU model '%s'", model_pieces[0]);g_strfreev(model_pieces);exit(EXIT_FAILURE);}cpu_type = object_class_get_name(oc);cc = CPU_CLASS(oc);cc->parse_features(cpu_type, model_pieces[1], &error_fatal);g_strfreev(model_pieces);return cpu_type;
}
首先,调用函数 g_strsplit() 将 cpu_option 分成两部分,第一部分是 CPU 名字代号,第二部分是 CPU 的特性设置,代码如下:
model_pieces = g_strsplit(cpu_option, ",", 2);
cpu_class_by_name()
代码如下:
ObjectClass *cpu_class_by_name(const char *typename, const char *cpu_model)
{ObjectClass *oc;CPUClass *cc;oc = object_class_by_name(typename);cc = CPU_CLASS(oc);assert(cc->class_by_name);assert(cpu_model);oc = cc->class_by_name(cpu_model);if (oc == NULL || object_class_is_abstract(oc)) {return NULL;}return oc;
}
然后,调用函数 parse_features() 完成 CPU 特性设定,代码如下:
parse_features()
代码如下:
static void cpu_common_parse_features(const char *typename, char *features,Error **errp)
{char *val;static bool cpu_globals_initialized;/* Single "key=value" string being parsed */char *featurestr = features ? strtok(features, ",") : NULL;/* should be called only once, catch invalid users */assert(!cpu_globals_initialized);cpu_globals_initialized = true;while (featurestr) {val = strchr(featurestr, '=');if (val) {GlobalProperty *prop = g_new0(typeof(*prop), 1);*val = 0;val++;prop->driver = typename;prop->property = g_strdup(featurestr);prop->value = g_strdup(val);qdev_prop_register_global(prop);} else {error_setg(errp, "Expected key=value format, found %s.",featurestr);return;}featurestr = strtok(NULL, ",");}
}
对于 i386 CPU,则有如下实现:
/* Parse "+feature,-feature,feature=foo" CPU feature string*/
static void x86_cpu_parse_featurestr(const char *typename, char *features,Error **errp)
{HUEDBG("enter\n");char *featurestr; /* Single 'key=value" string being parsed */static bool cpu_globals_initialized;bool ambiguous = false;if (cpu_globals_initialized) {HUEDBG("exit\n");return;}cpu_globals_initialized = true;if (!features) {HUEDBG("exit\n");return;}HUEDBG("features=[%s]\n", features);for (featurestr = strtok(features, ",");featurestr;featurestr = strtok(NULL, ",")) {const char *name;const char *val = NULL;char *eq = NULL;char num[32];GlobalProperty *prop;/* Compatibility syntax: */if (featurestr[0] == '+') {plus_features = g_list_append(plus_features,g_strdup(featurestr + 1));continue;} else if (featurestr[0] == '-') {minus_features = g_list_append(minus_features,g_strdup(featurestr + 1));continue;}eq = strchr(featurestr, '=');if (eq) {*eq++ = 0;val = eq;} else {val = "on";}feat2prop(featurestr);name = featurestr;if (g_list_find_custom(plus_features, name, compare_string)) {warn_report("Ambiguous CPU model string. ""Don't mix both \"+%s\" and \"%s=%s\"",name, name, val);ambiguous = true;}if (g_list_find_custom(minus_features, name, compare_string)) {warn_report("Ambiguous CPU model string. ""Don't mix both \"-%s\" and \"%s=%s\"",name, name, val);ambiguous = true;}/* Special case: */if (!strcmp(name, "tsc-freq")) {int ret;uint64_t tsc_freq;ret = qemu_strtosz_metric(val, NULL, &tsc_freq);if (ret < 0 || tsc_freq > INT64_MAX) {error_setg(errp, "bad numerical value %s", val);return;}snprintf(num, sizeof(num), "%" PRId64, tsc_freq);val = num;name = "tsc-frequency";}prop = g_new0(typeof(*prop), 1);prop->driver = typename;prop->property = g_strdup(name);prop->value = g_strdup(val);qdev_prop_register_global(prop);}if (ambiguous) {warn_report("Compatibility of ambiguous CPU model ""strings won't be kept on future QEMU versions");}HUEDBG("exit\n");
}
3.调试输出
首先,添加跟踪调试信息,修改后的代码如下:
int qemu_init(int argc, char **argv)
{
.../* parse features once if machine provides default cpu_type */huedbg_flag = 1;HUEDBG("\n");current_machine->cpu_type = machine_class->default_cpu_type;if (cpu_option) {current_machine->cpu_type = parse_cpu_option(cpu_option);}HUEDBG("\n");huedbg_flag = 0;/* NB: for machine none cpu_type could STILL be NULL here! */
...
}const char *parse_cpu_option(const char *cpu_option)
{HUEDBG("enter\n");ObjectClass *oc;CPUClass *cc;gchar **model_pieces;const char *cpu_type;HUEDBG("cpu_option=[%s]\n", cpu_option);model_pieces = g_strsplit(cpu_option, ",", 2);if (!model_pieces[0]) {error_report("-cpu option cannot be empty");exit(1);}HUEDBG("cpu_option=[%s]\n", cpu_option);oc = cpu_class_by_name(CPU_RESOLVING_TYPE, model_pieces[0]);if (oc == NULL) {error_report("unable to find CPU model '%s'", model_pieces[0]);g_strfreev(model_pieces);exit(EXIT_FAILURE);}cpu_type = object_class_get_name(oc);cc = CPU_CLASS(oc);cc->parse_features(cpu_type, model_pieces[1], &error_fatal);g_strfreev(model_pieces);HUEDBG("cpu_type=[%s]\n", cpu_type);HUEDBG("exit\n");return cpu_type;
}
运行后,输出信息如下:
[13052]../system/vl.c/qemu_init(3863):
[13052]../cpu-target.c/parse_cpu_option(248):enter
[13052]../cpu-target.c/parse_cpu_option(254):cpu_option=[Penryn,vendor=GenuineIntel,+ssse3,+sse4.2]
[13052]../cpu-target.c/parse_cpu_option(260):cpu_option=[Penryn,vendor=GenuineIntel,+ssse3,+sse4.2]
[13052]../qom/object.c/object_class_by_name(1095):enter
[13052]../qom/object.c/type_table_lookup(103):lookup type(x86_64-cpu) in hash table
[13052]../qom/object.c/object_class_by_name(1105):class(x86_64-cpu) return
[13052]../qom/object.c/object_class_by_name(1095):enter
[13052]../qom/object.c/type_table_lookup(103):lookup type(Penryn-x86_64-cpu) in hash table
[13052]../qom/object.c/object_class_by_name(1105):class(Penryn-x86_64-cpu) return
[13052]../qom/object.c/type_table_lookup(103):lookup type(cpu) in hash table
[13052]../qom/object.c/type_get_parent(194):parent_type(device)
[13052]../qom/object.c/type_get_parent(194):parent_type(object)
[13052]../qom/object.c/type_get_parent(196):no parent_type
[13052]../qom/object.c/type_get_parent(194):parent_type(x86_64-cpu)
[13052]../qom/object.c/type_get_parent(194):parent_type(cpu)
[13052]../target/i386/cpu.c/x86_cpu_parse_featurestr(5450):enter
[13052]../target/i386/cpu.c/x86_cpu_parse_featurestr(5466):features=[vendor=GenuineIntel,+ssse3,+sse4.2]
[13052]../target/i386/cpu.c/x86_cpu_parse_featurestr(5537):exit
[13052]../cpu-target.c/parse_cpu_option(273):cpu_type=[Penryn-x86_64-cpu]
[13052]../cpu-target.c/parse_cpu_option(274):exit
[13052]../system/vl.c/qemu_init(3868):
总结
以上分析了系统初始化过程中 CPU 配置项的解析过程。