QEMU理解与分析系列(2):QEMU参数相关

一、基本内容简介

其所有的可用选项存储在vl.c维护的QEMUOption数组qemu_options中

static const QEMUOption
qemu_options[] = {

{ "h", 0, QEMU_OPTION_h, QEMU_ARCH_ALL },

#define QEMU_OPTIONS_GENERATE_OPTIONS

#include “qemu-options-wrapper.h”

{ NULL },

};

#elif defined(QEMU_OPTIONS_GENERATE_OPTIONS)

#define DEF(option, opt_arg, opt_enum, opt_help, arch_mask) \

{ option, opt_arg, opt_enum, arch_mask },

这就定义了DEF(…)

而实际的DEF定义在qemu-options.def中,这个文件是makefile中的脚本根据

qemu-options.hx解析出来的,所以在make后才会生成。

用户的参数会放到一个三级列表中,即QemuOptsList(相同参数的组合列

表,例如参数中由多个-device,则他们会被放在一个QemuOptsList

中),QemuOpts(表示一个参数下面支持的QEMU选项),QemuOpt(QemuOpts的具

体内容),关系图如下:
在这里插入图片描述

二、参数解析过程

在这里插入图片描述

2.1 数据结构

在 include/qemu/queue.h
中定义了四种数据结构:单链表,双向链表,简单队列,尾队列。其中:

单链表:相关的宏定义以 QSLIST
开头。以指向表头的指针表示。只能正向遍历,移除元素时需要遍历链表,只能在表头之后或是某个已存在的元素之后插入元素。

双向链表:相关的宏定义以 QLIST
开头。以指向表头的指针表示。只能正向遍历,移除元素时不需要遍历链表,可以在表头和已存在元素的前面或后面插入元素。

简单队列:相关的宏定义以 QSIMPLEQ
开头。简单队列以一对分别指向队首元素与队尾元素的指针表示。元素之间单向连接,只能在队首,队尾或已存在元素之后插入新元素,只支持删除队首元素,只能正向遍历。

尾队列:相关的宏定义以 QTAILQ
开头。其同样以一对分别指向队首元素与队尾元素的指针表示。元素之间双向连接,因此可以在不遍历队列的情况下删除队列中的任意元素。可以在队首/队尾/已存在元素的前面或后面插入元素。支持双向遍历

qemu在初始化时,会调用qemu_add_opts与qemu_add_drive_opts等函数,它们分别将参数传入的QemuOptsList这个数据结构传入vm_config_groups[] 中与 drive_config_groups[] 中。

//QemuOptsList 类型定义于
include/qemu/option.h: 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[]; };

QTAILQ_HEAD相当于QemuOpts类型的尾队列头节点

struct QemuOpts { char *id; QemuOptsList *list; Location loc; QTAILQ_HEAD(, QemuOpt) head; QTAILQ_ENTRY(QemuOpts) next; };

QTAILQ_ENTRY(QemuOpts)
next是指向下一个元素的指针,以及指向前一个元素的该指针的二级指针。(双向链表)

struct QemuOpt { char *name; char *str; const QemuOptDesc *desc; union { bool boolean; uint64_t uint; } value; QemuOpts *opts; QTAILQ_ENTRY(QemuOpt) next; };

QemuOptDesc 类型定义于 include/qemu/option.h:

typedef struct QemuOptDesc { const
char *name; enum QemuOptType type; const
char *help; const
char *def_value_str; } QemuOptDesc;

上述去参数的处理可以概括为:通过qemu_add_opts与qemu_add_drive_opts将包含参数的QemuOptsList加入对应的额数组,然后qemu会调用module_call_init(MODULE_INIT_OPTS)进行参数类型链表ModuleTypeList的初始化。

2.2 参数解析

第一个阶段通过lookup_opt()函数在该数组中进行查找,确定其是否是合法参数,将后面跟随的参数存入popt;第二阶段同样是使用lookup_opt()函数进行查找,用于判断模拟的目标是否支持该参数,下面会根据选项的类型执行不同的分支。

下面可以看到-kernel,-bios参数对应的分支:

case QEMU_OPTION_kernel: qemu_opts_set(qemu_find_opts(“machine”), 0, “kernel”, optarg,&error_abort); break; case QEMU_OPTION_bios: qemu_opts_set(qemu_find_opts(“machine”), 0, “firmware”, optarg,&error_abort); break;

后面有包含-kernel,
-initrd 和 -append 参数及其组合是否合法:

  linux_boot = (kernel_filename != NULL);     if (!linux_boot && *kernel_cmdline != '\0') {         error_report("-append only allowed with -kernel option");         exit(1);     }     if (!linux_boot && initrd_filename != NULL) {         error_report("-initrd only allowed with -kernel option");         exit(1);     } //从上面的代码中不难看出-initrd和-append参数仅可在使用-kernel参数的情况下使用。

三、添加Machine参数

以添加字符型为例:

//在machine的init函数中添加字符类型的参数及其set,get函数接口;并添加描述 object_class_property_add_str(oc, “mask-rom-path”, xjy_get_rom_path, xjy_set_rom_path, &error_abort); object_class_property_set_description(oc, “mask-rom-path”, "XJY Mask ROM image
file. ", &error_abort);
static char *xjy_get_rom_path(Object *obj, Error **errp) { XJYIState *s = RISCV_U_MACHINE(obj); return s->mask_rom_path; } static void xjy_set_rom_path(Object *obj, const char *mask_rom_path, Error **errp) { XJYIState *s = RISCV_U_MACHINE(obj); s->mask_rom_path = g_strdup(mask_rom_path); }
//这里注意函数类型必须一一对应

添加完成后,可通过如~/qemu/qemu5/install-s64/bin/qemu-system-riscv64
-M xjy_i,help 命令查看是否添加成功,具体后续的功能逻辑就不在这里介绍。
在这里插入图片描述

四、添加device的参数

  device_class_set_props(DEVICE_CLASS(oc), xjy_i_props);  

static Property xjy_i_props[] = { DEFINE_PROP_STRING(“mask-rom-path”, XJYIState, mask_rom_path), DEFINE_PROP_END_OF_LIST() }; //参数列表 DEFINE_PROP_STRING(“mask-rom-path”, XJYIState, mask_rom_path),扩展到-> { .name = (“mask-rom-path”), .info =
&(qdev_prop_string), .offset = _builtin_offsetof (XJYIState, mask_rom_path) +
((char**)0 - (typeof(((XJYIState )0)->mask_rom_path))0), }
void device_class_set_props(DeviceClass *dc, Property *props) { Property *prop; dc->props
= props; for (prop = props; prop && prop->name; prop++) { qdev_class_add_legacy_property(dc, prop);//向后兼容? qdev_class_add_property(dc, prop->name, prop);//当前属性名称 } } //这里需要注意的时必须要加DEFINE_PROP_END_OF_LIST()去表示数组的结束

struct Property { const
char *name; const PropertyInfo *info; ptrdiff_t offset; uint8_t bitnr; uint64_t bitmask; bool set_default; union { int64_t i; uint64_t u; } defval; int arrayoffset; const PropertyInfo *arrayinfo; int arrayfieldsize; const
char *link_type; };

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值