diff --git a/articles/20230707-qemu-system-parameters.md b/articles/20230707-qemu-system-parameters.md new file mode 100644 index 0000000000000000000000000000000000000000..b52154da3174d95a5d3a91c553bfdfa2b0fda614 --- /dev/null +++ b/articles/20230707-qemu-system-parameters.md @@ -0,0 +1,461 @@ +> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [urls refs pangu]
+> Author: jl-jiang
+> Date: 2023/07/07
+> Revisor: Bin Meng
+> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
+> Proposal: [【老师提案】QEMU 系统模拟模式分析](https://gitee.com/tinylab/riscv-linux/issues/I61KIY)
+> Sponsor: PLCT Lab, ISCAS + +# QEMU 参数解析机制简析 + +## 前言 + +QEMU 使用命令行来配置模拟器的启动参数,本文将以 8.0.0 版本的 QEMU RISC-V (qemu-system-riscv64) 为例,简要梳理 QEMU 常用参数的含义与用法,重点分析 QEMU 的参数解析机制。 + +## 常用参数 + +QEMU 各选项的具体用法可以通过指令 `qemu-system-riscv64 [options] help` 查看。下面以在 QEMU 中启动 RISC-V Linux 内核的命令为例,简要介绍 QEMU 常用参数的含义: + +```bash +qemu-system-riscv64 -M virt -m 256M -nographic \ + -kernel linux-kernel/arch/riscv/boot/Image \ + -drive file=rootfs.img,format=raw,id=hd0 \ + -device virtio-blk-device,drive=hd0 \ + -append "root=/dev/vda rw console=ttyS0" +``` + +- **-M:** 指定模拟器运行时的设备类型,在上面的例子中指定了 `qemu-system-riscv64` 的模拟设备为 `virt`,即 RISC-V 架构下的“通用虚拟平台” +- **-m:** 指定虚拟机内部的内存大小 +- **-kernel:** 指定内核镜像 +- **-append:** 指定内核命令行代码 +- **-drive:** 定义磁盘,`file` 表示磁盘镜像的文件路径,`format` 表示磁盘格式,`id` 表示磁盘号 +- **-device:** 添加设备到虚拟机中 + +## 参数解析 + +### 流程概述 + +QEMU 的全部可用参数定义于项目目录下的 `qemu-options.hx` 文件中,而参数解析的主体逻辑位于 `softmmu/vl.c` 文件的 `qemu_init` 函数中,主要分为两个阶段,第一阶段负责检查传入参数的合法性,并不具体解析,第二阶段执行具体的解析操作,并通过 `switch` 语句跳转不同为分支完成对应选项的设置。QEMU 在不同的抽象层次上定义了多种数据结构来对不同的参数进行描述,这些数据结构都在参数解析之前完成初始化。 + +### 数据结构及初始化 + +QEMU 在 `softmmu/vl.c` 文件中定义了 `QEMUOption` 结构体来描述不同的命令行参数,其代码如下: + +```c +typedef struct QEMUOption { + const char *name; + int flags; + int index; + uint32_t arch_mask; +} QEMUOption; +``` + +其中 `name` 表示参数名称,`flags` 表示参数属性,为 1 表示拥有子参数,为 0 则表示无子参数,`index` 表示命令索引 (QEMU_OPTION_cmd),`arch_mask` 表示参数支持的架构。在 `softmmu/vl.c` 文件中还定义了一个全局 `QEMUOption` 数组 `qemu_options` 来描述 QEMU 的全部可用参数,具体如下: + +```c +static const QEMUOption qemu_options[] = { + { "h", 0, QEMU_OPTION_h, QEMU_ARCH_ALL }, + +#define DEF(option, opt_arg, opt_enum, opt_help, arch_mask) \ + { option, opt_arg, opt_enum, arch_mask }, +#define DEFHEADING(text) +#define ARCHHEADING(text, arch_mask) + +#include "qemu-options.def" + { /* end of list */ } +}; +``` + +可以看到,`qemu_options` 数组中首先定义了一个参数 `h`,其使用方法为 `qemu-system-riscv64 -h`,作用是打印帮助信息。其余所有的可用参数都都通过 `DEF` 宏定义在 `/qemu-options.def` 文件中。需要注意的是,`qemu-options.def` 文件是由 `scripts/hxtool` 脚本在编译时根据 `qemu-options.hx` 文件生成的,因此不在 QEMU 源代码目录中。 + +对于含有多个子选项的参数,QEMU 在文件 `include/qemu/option_int.h` 中定义了两个结构体 `QemuOpt` 对子参数进行描述: + +```c +struct QemuOpt { + char *name; + char *str; + + const QemuOptDesc *desc; + union { + bool boolean; + uint64_t uint; + } value; + + QemuOpts *opts; + QTAILQ_ENTRY(QemuOpt) next; +}; +``` + +其中 `QemuOpt` 用于存储子选项,每个 `QemuOpt` 结构体都有一个 `QemuOptDesc` 类型的成员变量来描述子选项的信息。QEMU 在文件 `include/qemu/option.h` 中定义了 `QemuOptsList` 结构体用于进一步抽象描述一个参数的所有子选项,有关代码如下: + +```c +enum QemuOptType { + QEMU_OPT_STRING = 0, /* no parsing (use string as-is) */ + QEMU_OPT_BOOL, /* on/off */ + QEMU_OPT_NUMBER, /* simple number */ + QEMU_OPT_SIZE, /* size, accepts (K)ilo, (M)ega, (G)iga, (T)era postfix */ +}; + +typedef struct QemuOptDesc { + const char *name; + enum QemuOptType type; + const char *help; + const char *def_value_str; +} QemuOptDesc; + +struct QemuOptsList { + const char *name; + const char *implied_opt_name; + bool merge_lists; /* Merge multiple uses of option into a single list? */ + QTAILQ_HEAD(, QemuOpts) head; + QemuOptDesc desc[]; +}; +``` + +上述代码中,首先定义了枚举类型 `QemuOptType` 用于描述不同的参数类型,包括字符串、布尔值、数字和空间大小四种。接着定义了结构体 `QemuOptDesc` 用于存放对参数的描述信息,包括名称、类型、帮助信息和参数默认值。最后定义了结构体 `QemuOptsList`,每一个 `QemuOptsList` 结构体就对应了一个参数。但是,这里需要特别注意的是,`QemuOptsList` 并不直接与 `QemuOpt` 联系,中间需要通过结构体 `QemuOpts` 进行转接: + +```c +struct QemuOpts { + char *id; + QemuOptsList *list; + Location loc; + QTAILQ_HEAD(, QemuOpt) head; + QTAILQ_ENTRY(QemuOpts) next; +}; +``` + +这样设计的目的在于避免发生参数混淆,QEMU 可以通过命令行指定创建两个相同的设备,这个时候这两个设备的选项都挂在 `QemuOptsList` 上,但是分别属于两个独立的 `QemuOpts`(`id` 不同),每个 `QemuOpts` 都维护自己的 `QemuOpt` 链表,成员变量 `head` 是 `QemuOpts` 下的 `QemuOpt` 链表头,成员变量 `next` 用来连接同一 `QemuOptsList` 下的其他 `QemuOpts`,具体如下图所示: + +![data_relation.svg](images/qemu-system-parameters/data_relation.svg) + +QEMU 在 `util/qemu-config.c` 中定义了一个全局的 `QemuOptsList` 数组 `vm_config_groups` 来储存所有可用的参数: + +```c +static QemuOptsList *vm_config_groups[48]; +static QemuOptsList *drive_config_groups[5]; +``` + +这两行代码说明了 QEMU 最多支持 48 个参数,5 个驱动器参数。这两个全局数组由位于 `softmmu/vl.c` 文件的 `qemu_init` 函数负责初始化: + +```c + qemu_add_opts(&qemu_drive_opts); + qemu_add_drive_opts(&qemu_legacy_drive_opts); + qemu_add_drive_opts(&qemu_common_drive_opts); + qemu_add_drive_opts(&qemu_drive_opts); + qemu_add_drive_opts(&bdrv_runtime_opts); + qemu_add_opts(&qemu_chardev_opts); + qemu_add_opts(&qemu_device_opts); + qemu_add_opts(&qemu_netdev_opts); + qemu_add_opts(&qemu_nic_opts); + qemu_add_opts(&qemu_net_opts); + qemu_add_opts(&qemu_rtc_opts); + qemu_add_opts(&qemu_global_opts); + qemu_add_opts(&qemu_mon_opts); + qemu_add_opts(&qemu_trace_opts); + qemu_plugin_add_opts(); + qemu_add_opts(&qemu_option_rom_opts); + qemu_add_opts(&qemu_accel_opts); + qemu_add_opts(&qemu_mem_opts); + qemu_add_opts(&qemu_smp_opts); + qemu_add_opts(&qemu_boot_opts); + qemu_add_opts(&qemu_add_fd_opts); + qemu_add_opts(&qemu_object_opts); + qemu_add_opts(&qemu_tpmdev_opts); + qemu_add_opts(&qemu_overcommit_opts); + qemu_add_opts(&qemu_msg_opts); + qemu_add_opts(&qemu_name_opts); + qemu_add_opts(&qemu_numa_opts); + qemu_add_opts(&qemu_icount_opts); + qemu_add_opts(&qemu_semihosting_config_opts); + qemu_add_opts(&qemu_fw_cfg_opts); + qemu_add_opts(&qemu_action_opts); +``` + +其中,`qemu_add_opts` 函数的实现位于 `util/qemu-config.c` 文件中,该函数主要负责将参数中传入的 `OemuOptsList` 添加到全局数组 `vm_config_groups` 中: + +```c +void qemu_add_opts(QemuOptsList *list) +{ + int entries, i; + + entries = ARRAY_SIZE(vm_config_groups); + entries--; /* keep list NULL terminated */ + for (i = 0; i < entries; i++) { + if (vm_config_groups[i] == NULL) { + vm_config_groups[i] = list; + return; + } + } + fprintf(stderr, "ran out of space in vm_config_groups"); + abort(); +} +``` + +### 第一阶段 + +QEMU 参数解析的第一阶段,遍历参数数组,通过 `lookup_opt` 函数来得到一个 `QEMUOption`,判断这个 `QEMUOption` 是否在之前初始化的的数组中。这一步骤的主要作用验证参数的合法性,下面结合源码具体分析: + +```c + /* first pass of option parsing */ + optind = 1; + while (optind < argc) { + if (argv[optind][0] != '-') { + /* disk image */ + optind++; + } else { + const QEMUOption *popt; + + popt = lookup_opt(argc, argv, &optarg, &optind); + switch (popt->index) { + case QEMU_OPTION_nouserconfig: + userconfig = false; + break; + } + } + } + + machine_opts_dict = qdict_new(); + if (userconfig) { + qemu_read_default_config_file(&error_fatal); + } +``` + +首先按照下标顺序依次读取终端传入的参数数组,跳过子选项,调用 `lookup_opt` 函数查询读取的参数名称,然后初始化 `machine_opts_dict` 数组,并根据实际情况加载用户配置。这里的 `machine_opts_dict` 是一个字典结构,主要用于存储终端传入的参数数组中的虚拟机选项和参数,包括 CPU 数量、内存大小、设备配置等。`machine_opts_dict` 的存在使得参数解析机制能够以一种结构化的方式管理和访问虚拟机参数,而不是使用分散的单独变量或者凌乱的数据结构。 + +下面给出 `lookup_opt` 函数的定义: + +```c +static const QEMUOption *lookup_opt(int argc, char **argv, const char **poptarg, int *poptind) +{ + const QEMUOption *popt; + int optind = *poptind; + char *r = argv[optind]; + const char *optarg; + + loc_set_cmdline(argv, optind, 1); + optind++; + /* Treat --foo the same as -foo. */ + if (r[1] == '-') + r++; + popt = qemu_options; + for(;;) { + if (!popt->name) { + error_report("invalid option"); + exit(1); + } + if (!strcmp(popt->name, r + 1)) + break; + popt++; + } + if (popt->flags & HAS_ARG) { + if (optind >= argc) { + error_report("requires an argument"); + exit(1); + } + optarg = argv[optind++]; + loc_set_cmdline(argv, optind - 2, 2); + } else { + optarg = NULL; + } + + *poptarg = optarg; + *poptind = optind; + + return popt; +} +``` + +`lookup_opt` 函数首先调用 `loc_set_cmdline` 函数在终端传入的命令行参数中根据参数索引 `optind` 进行定位,然后通过参数名称的比较在全局数组 `qemu_options` 中寻找对应的 `QEMUOption`。同时,`lookup_opt` 函数会将参数后面的子选项保存到 `optarg` 中。 + +至此,我们不难发现,第一阶段的工作并没有涉及实际的参数解析,而是完成了从命令行读取用户的配置参数并对所有参数进行有效性验证,为第二阶段的正式解析做准备。QEMU 参数解析机制的两步设计,实现了验证逻辑和执行逻辑的有效分离,减少了出错的风险,使代码逻辑更加清晰。 + +### 第二阶段 + +QEMU 参数解析的第二阶段,是真正解析具体参数并执行相应设置的阶段,主要逻辑如下: + +```c + /* second pass of option parsing */ + optind = 1; + for(;;) { + if (optind >= argc) + break; + if (argv[optind][0] != '-') { + loc_set_cmdline(argv, optind, 1); + drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS); + } else { + const QEMUOption *popt; + + popt = lookup_opt(argc, argv, &optarg, &optind); + if (!(popt->arch_mask & arch_type)) { + error_report("Option not supported for this target"); + exit(1); + } + switch(popt->index) { + case QEMU_OPTION_cpu: + ... + break; + ... + default: + ... + } + } + } +``` + +第二阶段的逻辑就是按照下标顺序依次遍历终端传入的参数数组,调用 `lookup_opt` 函数找到对应的 `QEMUOption`,然后检查对应选项在当前架构下是否支持,最后使用 `switch` 语句根据 `QEMUOption` 的成员变量 `index` 的不同来执行不同的分支完成具体的设置。 + +下面将以不同的参数为例,详细说明参数解析第二阶段 `switch` 语句中的分支执行流程。首先关注最为简单的 `-version` 参数,其含义就是打印 QEMU 版本信息,然后退出程序,有关代码如下: + +```c + case QEMU_OPTION_version: + version(); + exit(0); + break; +``` + +可以看到,由于其含义最为简单,因此解析实现也非常简洁,直接调用 `version` 函数打印版本信息,随后退出程序。接着分析 `-kernel` 参数,通过对 `-kernel` 参数解析流程的考察,我们可以一窥与机器设置有关的参数的解析机制。首先给出 `-kernel` 参数在解析第二阶段分支执行的主要代码: + +```c + case QEMU_OPTION_kernel: + qdict_put_str(machine_opts_dict, "kernel", optarg); + break; +``` + +我们可以发现,与机器设置有关的参数在整个解析过程中并不会真正执行具体设置,而是将对应的参数选项添加到 `machine_opts_dict` 中,最终参数设置的落实工作由机器创建及初始化阶段的 `qemu_apply_legacy_machine_options` 函数和 `qemu_apply_machine_options` 函数完成,这一部分将在“QEMU 机器创建及初始化机制”的分析文章中具体阐述,本文不再赘述。下图给出了与与机器设置有关的参数从解析到落实的一般流程: + +![machine_parameters.svg](images/qemu-system-parameters/machine_parameters.svg) + +最后,观察 `-device` 参数,同样给出分支执行代码: + +```c + case QEMU_OPTION_device: + if (optarg[0] == '{') { + QObject *obj = qobject_from_json(optarg, &error_fatal); + DeviceOption *opt = g_new0(DeviceOption, 1); + opt->opts = qobject_to(QDict, obj); + loc_save(&opt->loc); + assert(opt->opts != NULL); + QTAILQ_INSERT_TAIL(&device_opts, opt, next); + } else { + if (!qemu_opts_parse_noisily(qemu_find_opts("device"), + optarg, true)) { + exit(1); + } + } + break; +``` + +分支执行的主要逻辑如下:首先检查 `optarg`(`-device` 选项的参数)是否以 `{` 开始。如果是,说明 `optarg` 是一个 JSON 对象,那么就需要调用 `qobject_from_json` 函数将 JSON 对象转换为一个 `QObject` 对象。然后,创建一个新的 `DeviceOption` 对象,并将 `QObject` 对象转换为一个 `QDict` 并存储在 `DeviceOption` 中,最后将 `DeviceOption` 添加到 `device_opts` 队列中。如果 `optarg` 不是一个 JSON 对象,那么就调用 `qemu_opts_parse_noisily` 函数将 `optarg` 解析为一个 `QemuOpts` 对象,并将这个对象添加到 "device" 选项的列表中。如果解析失败,则退出程序。下面我们重点关注 `else` 分支的部分,首先给出 `qemu_find_opts` 函数的定义,它位于 `util/qemu-config.c` 文件中: + +```c +QemuOptsList *qemu_find_opts(const char *group) +{ + QemuOptsList *ret; + Error *local_err = NULL; + + ret = find_list(vm_config_groups, group, &local_err); + if (local_err) { + error_report_err(local_err); + } + + return ret; +} +``` + +`qemu_find_opts` 函数从全局数组 `vm_config_groups` 中找到刚才插入的 `-device` 选相对应的 `QemuOptsList` 并返回,而 `qemu_opts_parse_noisily` 函数只是简单调用了 `opts_parse` 函数,后者会解析出一个 `QemuOpts`,每一个大类的参数都会在相应的 `QemuOptsList` 中构造 `QEMUOpts`。继续分析 `opts_parse` 函数: + +```c +static QemuOpts *opts_parse(QemuOptsList *list, const char *params, + bool permit_abbrev, + bool warn_on_flag, bool *help_wanted, Error **errp) +{ + const char *firstname; + char *id = opts_parse_id(params); + QemuOpts *opts; + + assert(!permit_abbrev || list->implied_opt_name); + firstname = permit_abbrev ? list->implied_opt_name : NULL; + + opts = qemu_opts_create(list, id, !list->merge_lists, errp); + g_free(id); + if (opts == NULL) { + return NULL; + } + + if (!opts_do_parse(opts, params, firstname, + warn_on_flag, help_wanted, errp)) { + qemu_opts_del(opts); + return NULL; + } + + return opts; +} +``` + +`opts_parse` 函数代码中最重要的两行是对 `qemu_opts_create` 函数和 `opts_do_parse` 函数的调用,前者用来创建 `QemuOpts` 并将它插入到对应的 `QemuOptsList` 上,后者则负责解析出 `QemuOpt`。函数 `opts_do_parse` 的作用是解析参数的值,如本文开头例子中的命令行参数 `-device virtio-blk-device,drive=hd0`。QEMU 的参数可能存在多种情况,比如上面的例子中 `virtio-blk-device` 表示开启一个标志,也有可能类似于 `drive=hd0` 的参数赋值语句。`opts_do_parse` 函数需要处理各种情况,并对每一个值生成一个 `QemuOpt`,关键代码如下: + +```c +static bool opts_do_parse(QemuOpts *opts, const char *params, + const char *firstname, + bool warn_on_flag, bool *help_wanted, Error **errp) +{ + char *option, *value; + const char *p; + QemuOpt *opt; + + for (p = params; *p;) { + p = get_opt_name_value(p, firstname, warn_on_flag, help_wanted, &option, &value); + if (help_wanted && *help_wanted) { + g_free(option); + g_free(value); + return false; + } + firstname = NULL; + + if (!strcmp(option, "id")) { + g_free(option); + g_free(value); + continue; + } + + opt = opt_create(opts, option, value); + g_free(option); + if (!opt_validate(opt, errp)) { + qemu_opt_del(opt); + return false; + } + } + + return true; +} +``` + +`opts_do_parse` 函数是 QEMU 参数解析过程的核心部分,它接受如下六个参数: + +- `opts` 是一个 `QemuOpts` 对象,用于存储解析后的选项 + +- `params` 是一个字符串,包含需要解析的参数 + +- `firstname` 是第一个选项的名字 + +- `warn_on_flag` 是一个布尔值,如果为 `true`,那么当遇到一个标志选项时,函数会打印一个警告消息 + +- `help_wanted` 是一个指向布尔值的指针,如果函数解析到一个 `help` 选项,则设置 `*help_wanted` 为 `true`;`errp` 是一个错误指针,如果函数遇到错误,则设置 `*errp` + +- `errp` 是一个错误指针,如果函数遇到错误,那么它会设置 `*errp` + +`opts_do_parse` 函数使用一个 `for` 循环一次遍历解析 `params` 中的每个选项:首先调用 `get_opt_name_value` 函数来获取下一个选项的名字和值,然后创建一个新的 `QemuOpt` 对象并添加到 `opts` 中,如果解析到一个 `help` 选项,则会设置 `*help_wanted` 为 `true`,并立即返回 `false`。注意,如果解析到的选项的名称是 `id`,则会跳过这个选项,因为参数 `id` 是用于设置 `QemuOpts` 对象的 ID 的,不能当作普通参数解析为 `QemuOpt`。最后,调用 `opt_validate` 函数来验证新创建的 `QemuOpt` 的有效性并返回解析结果。 + +仍然以命令行参数 `-device virtio-blk-device,drive=hd0` 为例,经过上述过程将解析出 2 个 `QemuOpt` 并形成如下图所示的链表: + +![linked_list.svg](images/qemu-system-parameters/linked_list.svg) + +## 总结 + +本文以 QEMU 中引导 RISC-V 架构 Linux 内核启动的指令为例,总结归纳了 QEMU 常用参数的用法与含义,分析阐述了描述不同参数的各种数据结构的定义、作用以及相互关系,并按照 QEMU 的执行顺序详细梳理了数据结构初始化、参数解析第一阶段、参数解析第二阶段的代码逻辑。 + +## 参考资料 + +- [《QEMU 启动方式分析(1):QEMU 及 RISC-V 启动流程简介》](https://gitee.com/tinylab/riscv-linux/blob/master/articles/20220816-introduction-to-qemu-and-riscv-upstream-boot-flow.md) +- [System Emulation: Invocation](https://www.qemu.org/docs/master/system/invocation.html) +- 《QEMU/KVM 源码解析与应用》李强,机械工业出版社 diff --git a/articles/images/qemu-system-parameters/data_relation.svg b/articles/images/qemu-system-parameters/data_relation.svg new file mode 100644 index 0000000000000000000000000000000000000000..f3fbbc060e0ed474d7d1c533cb176f3b67e9542e --- /dev/null +++ b/articles/images/qemu-system-parameters/data_relation.svg @@ -0,0 +1,4 @@ + + + +QemuOptsList
const char *name;
const char *name;
const char *implied_opt_name;
const char *implied_opt_name;
bool merge_lists;  
bool merge_lists;  
QTAILQ_HEAD(, QemuOpts) head;
QTAILQ_HEAD(, QemuOpts) head;
QemuOptDesc desc[];
QemuOptDesc desc[];
QemuOpts
char *id
char *id
QemuOptsList *list
QemuOptsList *list
Location loc
Location loc
QTAILQ_HEAD(, QemuOpt) head
QTAILQ_HEAD(, QemuOpt) head
QTAILQ_ENTRY(QemuOpts) next
QTAILQ_ENTRY(QemuOpts) next
QemuOpts
QemuOpts
QemuOpts
QemuOpts
QemuOpts
QemuOpts
QemuOptDesc
QemuOptDesc
QemuOptDesc
QemuOptDesc
……
……
QemuOptDesc
QemuOptDesc
QemuOptDesc
QemuOptDesc
QemuOpt
char *name
char *name
char *str
char *str
const QemuOptDesc *desc
const QemuOptDesc *desc
union {bool boolean; uint64_t uint;} value
union {bool boolean; uint64_t uint;} value
QemuOpts *opts
QemuOpts *opts
QTAILQ_ENTRY(QemuOpt) next
QTAILQ_ENTRY(QemuOpt) next
QemuOpt
QemuOpt
QemuOpt
QemuOpt
QemuOptDesc
const char *name
const char *name
enum QemuOptType type
enum QemuOptType type
const char *help
const char *help
const char *def_value_str
const char *def_value_str
Text is not SVG - cannot display
\ No newline at end of file diff --git a/articles/images/qemu-system-parameters/linked_list.svg b/articles/images/qemu-system-parameters/linked_list.svg new file mode 100644 index 0000000000000000000000000000000000000000..0c3a33d65e7d0314bac627af24207381be5541e6 --- /dev/null +++ b/articles/images/qemu-system-parameters/linked_list.svg @@ -0,0 +1,4 @@ + + + +
...
...
...
...
qemu_device_opts
qemu_device_opts
...
...
...
...
vm_config_groups
vm_config_groups
QemuOpts
QemuOpts
QemuOpts
QemuOpts
QemuOpts
QemuOpts
QemuOpt
name=driver
name=driver
str=virtio-blk-device
str=virtio-blk-device
QemuOpt
name=drive
name=drive
str=hd0
str=hd0
Text is not SVG - cannot display
\ No newline at end of file diff --git a/articles/images/qemu-system-parameters/machine_parameters.svg b/articles/images/qemu-system-parameters/machine_parameters.svg new file mode 100644 index 0000000000000000000000000000000000000000..5b7502240858a12999518b24dd6028baaf2a2c72 --- /dev/null +++ b/articles/images/qemu-system-parameters/machine_parameters.svg @@ -0,0 +1,4 @@ + + + +
 qdict_new()
 qdict_new()
lookup_opt()
lookup_opt()
Create QDict
Create QDict
Decode Parameters
Decode Parameters
switch(popt->index)
switch(popt->index)
case QEMU_OPTION_kernel
case QEMU_OPTION_kernel
qdict_put_str()
qdict_put_str()
Apply Options
Apply Options
qemu_apply_legacy_machine_options()
qemu_apply_legacy_machine_options()
qemu_apply_machine_options()
qemu_apply_machine_options()
qemu_validate_options()
qemu_validate_options()
Text is not SVG - cannot display
\ No newline at end of file