文章目录
本文是一个简单的demo,基于开源qemu2.8,演示了如何添加qmp和hmp接口,同时实现从hmp命令打通到底层外设函数的通道,此处为i6300esb 1的测试函数。注意:此处的调用路径仅供演示,实际上并不合理,会出现一些告警,而在实际云平台编译环境中,rpmbuild编译链接,是不允许的warning存在的,若想添加接口,还是应该参考已有的接口走回调函数的方式,或者使用object property方式注册处理函数。
1.添加qmp接口
1.1.添加接口原型
在qapi-schema.json末尾添加接口原型daxiatou-watchdog-set-timeout
{ 'command': 'daxiatou-watchdog-set-timeout' ,'data':{'*time':'int'}}
int对应的int64_t,正常应该是在这一步先执行编译,再进行下一步,此处节省篇幅,一步搞定,详细用法,查阅qemu官方文档《writing-qmp-commands.txt》
1.2.添加接口函数体
/*qmp.c*/
void qmp_daxiatou_watchdog_set_timeout(bool has_time, int64_t time, Error **errp)
{
if (has_time)
{
printf("%ld\n", time);
}
else
{ printf("Please input time\n"); }
}
1.3.测试接口
1.3.1.启动虚拟机
qemu启动脚本需要增加以下参数
-chardev socket,id=qmp,port=4444,host=localhost,server -mon chardev=qmp,mode=control,pretty=on
这样,qemu monitor就可以接受qmp协议报文,随后另起一个shell,使用如下命令连接qemu monitor
[root@localhost qemu-2.8.0]# telnet localhost 4444
Trying ::1...
Connected to localhost.
Escape character is '^]'.
{
"QMP": {
"version": {
"qemu": {
"micro": 0,
"minor": 8,
"major": 2
},
"package": ""
},
"capabilities": [
]
}
}
1.3.2.执行测试命令
打开command mode
{ "execute": "qmp_capabilities" }
执行带参数命令
{ "execute": "daxiatou-watchdog-set-timeout", "arguments": { "time": 1000 } }
查看qemu端的效果
(qemu) 1000
执行不带参数的命令
{ "execute": "daxiatou-watchdog-set-timeout"}
查看qemu端的效果
(qemu) Please input time
1.3.3.查看编译后自动生成的代码
[root@localhost qemu-2.8.0]# grep -rn 'daxiatou'
qapi-types.h:1936:typedef struct q_obj_daxiatou_watchdog_set_timeout_arg q_obj_daxiatou_watchdog_set_timeout_arg;
qapi-types.h:5596:struct q_obj_daxiatou_watchdog_set_timeout_arg {
qapi-visit.c:15673:void visit_type_q_obj_daxiatou_watchdog_set_timeout_arg_members(Visitor *v, q_obj_daxiatou_watchdog_set_timeout_arg *obj, Error **errp)
qapi-visit.h:986:void visit_type_q_obj_daxiatou_watchdog_set_timeout_arg_members(Visitor *v, q_obj_daxiatou_watchdog_set_timeout_arg *obj, Error **errp);
Binary file qmp.o matches
qmp-commands.h:97:void qmp_daxiatou_watchdog_set_timeout(bool has_time, int64_t time, Error **errp);
qmp-commands.h:98:void qmp_marshal_daxiatou_watchdog_set_timeout(QDict *args, QObject **ret, Error **errp);
qmp.c:719:void qmp_daxiatou_watchdog_set_timeout(bool has_time, int64_t time, Error **errp)
Binary file qemu-nbd matches
qmp-introspect.c:91: "{\"arg-type\": \"67\", \"meta-type\": \"command\", \"name\": \"daxiatou-watchdog-set-timeout\", \"ret-type\": \"17\"}, "
Binary file x86_64-softmmu/qemu-system-x86_64 matches
qmp-marshal.c:1286:void qmp_marshal_daxiatou_watchdog_set_timeout(QDict *args, QObject **ret, Error **errp)
qmp-marshal.c:1290: q_obj_daxiatou_watchdog_set_timeout_arg arg = {0};
qmp-marshal.c:1297: visit_type_q_obj_daxiatou_watchdog_set_timeout_arg_members(v, &arg, &err);
qmp-marshal.c:1306: qmp_daxiatou_watchdog_set_timeout(arg.has_time, arg.time, &err);
qmp-marshal.c:1313: visit_type_q_obj_daxiatou_watchdog_set_timeout_arg_members(v, &arg, NULL);
qmp-marshal.c:6147: qmp_register_command("daxiatou-watchdog-set-timeout", qmp_marshal_daxiatou_watchdog_set_timeout, QCO_NO_OPTIONS);
Binary file qapi-visit.o matches
Binary file qemu-io matches
Binary file qmp-introspect.o matches
Binary file qemu-img matches
Binary file qmp-marshal.o matches
qapi-schema.json:4779:{ 'command': 'daxiatou-watchdog-set-timeout' ,'data':{'*time':'int'}}
Binary file i386-softmmu/qemu-system-i386 matches
Binary file libqemuutil.a matches
Binary file qemu-ga matches
会生成很多东西,绝大多数不用理会,主要看这两个地方,
/* qmp-marshal.c*/
void qmp_marshal_daxiatou_watchdog_set_timeout(QDict *args, QObject **ret, Error **errp)
{
Error *err = NULL;
Visitor *v;
q_obj_daxiatou_watchdog_set_timeout_arg arg = {0};
v = qobject_input_visitor_new(QOBJECT(args), true);
visit_start_struct(v, NULL, NULL, 0, &err);
if (err) {
goto out;
}
visit_type_q_obj_daxiatou_watchdog_set_timeout_arg_members(v, &arg, &err);
if (!err) {
visit_check_struct(v, &err);
}
visit_end_struct(v, NULL);
if (err) {
goto out;
}
qmp_daxiatou_watchdog_set_timeout(arg.has_time, arg.time, &err);
out:
error_propagate(errp, err);
visit_free(v);
v = qapi_dealloc_visitor_new();
visit_start_struct(v, NULL, NULL, 0, NULL);
visit_type_q_obj_daxiatou_watchdog_set_timeout_arg_members(v, &arg, NULL);
visit_end_struct(v, NULL);
visit_free(v);
}
qmp_register_command("daxiatou-watchdog-set-timeout", qmp_marshal_daxiatou_watchdog_set_timeout, QCO_NO_OPTIONS);
qmp_register_command会注册一个qmp命令行"daxiatou-watchdog-set-timeout",上一节中使用到的命令,就是这里注册的,相关联的函数qmp_marshal_daxiatou_watchdog_set_timeout,会调用1.2节里面添加的自定义函数qmp_daxiatou_watchdog_set_timeout,这种设计方式,可以让’qmp_'开头的函数在另外的文件内单独定义函数体,而不影响框架自动生成的代码。
2.添加hmp命令
HMP就是humanmonitor,在qemu monitor敲的命令就属于这一类,这是面向用户直接使用的命令
2.1.添加命令行
/*hmp-commands.hx */
{
.name = "daxiatou-watchdog-set-timeout",
.args_type = "time:i?",
.params = "daxiatou2 [time]",
.help = "set timeout of i6300esb",
.cmd = hmp_daxiatou_watchdog_set_timeout,
},
STEXI
@item daxiatou-watchdog-set-timeout @var{message}
Print message to the standard output
ETEXI
第一个参数.name代表命令本身的名字
第三个参数.params的作用是显示使用方式,如下所示
(qemu) help daxiatou-watchdog-set-timeout
daxiatou-watchdog-set-timeout daxiatou2 [time] -- set timeout of i6300esb
2.2.添加调用函数
添加函数声明
/*hmp.h*/
void hmp_daxiatou_watchdog_set_timeout(Monitor *mon, const QDict *qdict);
添加函数体
/*hmp.c*/
void hmp_daxiatou_watchdog_set_timeout(Monitor *mon, const QDict *qdict)
{
int time = qdict_get_try_int(qdict, "time",30);
Error *err = NULL;
qmp_daxiatou_watchdog_set_timeout(!!time, time, &err);
if (err) {
monitor_printf(mon, "%s\n", error_get_pretty(err));
error_free(err);
return;
}
}
查看运行效果
(qemu) daxiatou-watchdog-set-timeout 100
100
(qemu) daxiatou-watchdog-set-timeout
30
3.增加外设接口
此处希望通过命令行,能直接操作i6300esb的相关变量,首先,需要让命令行的函数感知到设备的存在
3.1.在设备代码文件中增加测试函数
/*code 3-1*/
/*hw/watchdog/wdt_i6300esb.c*/
static void i6300esb_test(void)
{
i6300esb_debug("daxiatou-i6300esb\n");
}
在命令行中直接增加以上函数i6300esb_test()的调用
/*code 3-2 qmp.c*/
void qmp_daxiatou_watchdog_set_timeout(bool has_time, int64_t time, Error **errp)
{
if (has_time)
{
printf("%ld\n", time);
i6300esb_test();
}
else
{ printf("Please input time\n"); }
}
编译失败
[root@localhost qemu-2.8.0]# make -j4
CHK version_gen.h
CC hw/watchdog/wdt_i6300esb.o
hw/watchdog/wdt_i6300esb.c:467:13: warning: ‘i6300esb_test’ defined but not used [-Wunused-function]
static void i6300esb_test(void)
^
LINK i386-softmmu/qemu-system-i386
LINK x86_64-softmmu/qemu-system-x86_64
../qmp.o: In function `qmp_daxiatou_watchdog_set_timeout':
/home/qemu-2.8.0/qmp.c:741: undefined reference to `i6300esb_test'
collect2: error: ld returned 1 exit status
make[1]: *** [qemu-system-i386] Error 1
make: *** [subdir-i386-softmmu] Error 2
make: *** Waiting for unfinished jobs....
../qmp.o: In function `qmp_daxiatou_watchdog_set_timeout':
/home/qemu-2.8.0/qmp.c:741: undefined reference to `i6300esb_test'
collect2: error: ld returned 1 exit status
make[1]: *** [qemu-system-x86_64] Error 1
make: *** [subdir-x86_64-softmmu] Error 2
说明,这两个c文件无法直接感知对方的存在,中间走了很多层,现在需要借助qemu自带的接口,一级级寻找能够操作到设备的路线
//qmp.c
void qmp_daxiatou_watchdog_set_timeout(bool has_time, int64_t time, Error **errp)
{
const char *id = "watchdog0";
Error *err = NULL;
qmp_device_del(id, &err);
if (has_time)
{
printf("%ld\n", time);
//i6300esb_test();
}
else
{ printf("Please input time\n"); }
}
可以成功调用删除函数,,但是qmp_device_del内部的语句,无法被识别,因此,只能模仿qmp_device_del函数,移动到qdev-monitor.c中定义,顺着这条路往下跟
3.2.在qdev中调用pci函数
//qdev-monitor.c
void qmp_daxiatou_watchdog_set_timeout(bool has_time, int64_t time, Error **errp)
{
const char *id = "watchdog0";
Error *err = NULL;
DeviceState *dev = find_device_state(id, errp);
PCIDevice *pci_dev = (PCIDevice *)dev;
//PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pci_dev);
qdev_daxiatou_watchdog_set_timeout(dev,errp);
// qmp_device_del(id, &err);
if (has_time)
{
printf("%ld\n", time);
//i6300esb_test();
}
else
{ printf("Please input time\n"); }
}
//include/hw/qdev-core.h
void qdev_daxiatou_watchdog_set_timeout(DeviceState *dev, Error **errp);
//hw/core/qdev.c
void qdev_daxiatou_watchdog_set_timeout(DeviceState *dev, Error **errp)
{
PCIDevice *pci_dev = (PCIDevice *)dev;
DeviceClass *dc = DEVICE_GET_CLASS(dev);
printf("daxiatou = %p\n",dc);
pci_daxiatou();
}
//include/hw/pci/pci.h
void pci_daxiatou( );
//hw/pci/pci.c
void pci_daxiatou( )
{
printf("daxiatou-pci-test\n");
}
说明在以上代码块中定义函数,qdev可以调用,下一步就是要在pci的函数里面,调用到具体设备的函数了,此处是i6300esb,测试效果如下,
(qemu) daxiatou-watchdog-set-timeout
daxiatou = 0x558e01bdb090
daxiatou-pci-test
30
3.3.在pci中调用设备函数
和3.2节中的方式一样,直接调用i6300esb_test函数
//hw/pci/pci.c
//extern void i6300esb_test(void);如果不添加这个,会告警
void pci_daxiatou()
{
printf("daxiatou-pci-test\n");
i6300esb_test();
}
编译会产生告警,这是因为没有在头文件中声明,此处没有同名的头文件,先暂时忽略告警,强行安装运行
[root@localhost qemu-2.8.0]# make -j4
CHK version_gen.h
CC hw/watchdog/wdt_i6300esb.o
hw/watchdog/wdt_i6300esb.c:467:6: warning: no previous prototype for ‘i6300esb_test’ [-Wmissing-prototypes]
void i6300esb_test(void)
^
LINK x86_64-softmmu/qemu-system-x86_64
LINK i386-softmmu/qemu-system-i386
成功调用到设备源代码中的函数
(qemu) daxiatou-watchdog-set-timeout
daxiatou = 0x55b4dff00090
daxiatou-pci-test
i6300esb: i6300esb_test: daxiatou-i6300esb
30