Bootstrap

Qemu添加自定义PCI设备

1.qemu source code

git clone https://mirrors.tuna.tsinghua.edu.cn/git/qemu.git

实验使用的版本是v7.2.8,主要原因是这个版本可以在ubuntu18.04.6上编译运行。

2.编译:

$ cd qemu
$ mkdir build
$ mkdir install
$ cd build
$ ../configure --target-list=i386-softmmu,x86_64-softmmu,x86_64-linux-user --audio-drv-list=alsa,sdl,pa --enable-system --enable-user --enable-linux-user --enable-sdl --enable-vnc --enable-virtfs --enable-kvm --enable-fdt --enable-debug --disable-strip --enable-debug-tcg --enable-debug-info --enable-debug --disable-strip --prefix=/home/zlcao/Workspace/qemu/install

3.安装

$ make install

4.添加自定义模块文件

diff --git a/hw/watchdog/Kconfig b/hw/watchdog/Kconfig
index 66e1d029e3..6664175956 100644
--- a/hw/watchdog/Kconfig
+++ b/hw/watchdog/Kconfig
@@ -20,3 +20,7 @@ config WDT_IMX2
 
 config WDT_SBSA
     bool
+
+config WDT_CZL_MOD
+    bool
+    default y
diff --git a/hw/watchdog/meson.build b/hw/watchdog/meson.build
index 8974b5cf4c..150d86f7b5 100644
--- a/hw/watchdog/meson.build
+++ b/hw/watchdog/meson.build
@@ -1,6 +1,7 @@
 softmmu_ss.add(files('watchdog.c'))
 softmmu_ss.add(when: 'CONFIG_CMSDK_APB_WATCHDOG', if_true: files('cmsdk-apb-watchdog.c'))
 softmmu_ss.add(when: 'CONFIG_WDT_IB6300ESB', if_true: files('wdt_i6300esb.c'))
+softmmu_ss.add(when: 'CONFIG_WDT_CZL_MOD', if_true: files('wdt_czl_pcie.c'))
 softmmu_ss.add(when: 'CONFIG_WDT_IB700', if_true: files('wdt_ib700.c'))
 softmmu_ss.add(when: 'CONFIG_WDT_DIAG288', if_true: files('wdt_diag288.c'))
 softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('wdt_aspeed.c'))
diff --git a/hw/watchdog/wdt_czl_pcie.c b/hw/watchdog/wdt_czl_pcie.c
new file mode 100644
index 0000000000..d9f7a5704a
--- /dev/null
+++ b/hw/watchdog/wdt_czl_pcie.c
@@ -0,0 +1,14 @@
+#include "qemu/osdep.h"
+#include "qemu/module.h"
+#include "qemu/timer.h"
+#include "sysemu/watchdog.h"
+#include "hw/pci/pci.h"
+#include "migration/vmstate.h"
+#include "qom/object.h"
+
+static void register_types(void)
+{
+
+}
+
+type_init(register_types)

5.再次编译,添加文件被编译,下一步扩展wdt_czl_pcie.c实现,注册新的设备到QEMU:

6.添加PCIE设备的补丁:

https://gitee.com/tugouxp/qemu-dev.git

7.启动

使用编译的qemu启动虚拟机,参数-device cstl-watchdog 指定自定义设备

sudo ./qemu-system-x86_64 -m 4096 -smp 4 --enable-kvm -drive file=/media/zlcao/7CC840FCC840B5E4/ps.img -device cstl-watchdog
sudo ./qemu-system-x86_64 -m 4096 -smp 4 --accel kvm -drive file=/media/zlcao/7CC840FCC840B5E4/ps.img -device cstl-watchdog
lspci -x

驱动新设备的是virtio-pci driver(virtio_pci_driver).

vendor id和product id能够匹配,证明我们的模拟设备被QEMU成功识别了。在QEMU中的monitor中(启动QEMU传入参数-monitor stdio)输入info qtree,能够看到添加的设备信息:

QEMU中添加自定义设备的流程

QEMU中添加新设备的核心是将设备描述TypeInfo对象注册进QEMU,将注册函数本身作为QEMU系统设备初始化链表中的一个接点注册进QEMU设备链表,而后者代表的注册函数则通过gcc attribute constructor注册进QEMU应用,在启动过程中执行。设备本身的注册函数将在QEMU启动过程中执行设备链表中的初始化会调完成。示意图如下:

单元测试

tests/qtest/czl-pcie-test.c文件用于对新增的PCIE设备进行单元测试。添加完毕后,执行make check-qtest-x86_64命令进行功能测试:

make check-qtest-x86_64

可以看到,watchdog的定时器跑起来了。

单元测试的原理应该是根据用户传入的参数启动虚拟机,之后测试程序从主机和QEMU虚拟机通信,发送测试命令。

QEMU启动参数传入qtest_start, 之后执行qtest_start(args)->qtest_init(args)->qtest_init_without_qmp_handshake(extra_args)->

                            qtest_qemu_binary()->qemu_bin = getenv("QTEST_QEMU_BINARY");

                             ->"exec qemu_bin .... args"

                             ->fork()->execlp("/bin/sh", "sh", "-c", command, NULL); 用给定的参数启动QEMU,如下图所示:

所以,qemu test suilt会在代码中主动启动QEMU虚拟机,运行测试,运行过程中,测试程序和QEMU之间通过qmp协议用SOCKET通信,可以用如下命令启动QEMU QMP协议交互:

$ sudo qemu-system-x86_64 -m 4096 -smp 4 --enable-kvm -drive file=./zlcao.img -qmp unix:/tmp/qmp-test,server,nowait
$ nc -U /tmp/qmp-test

例如,一个简单的in/out命令实际上调用了socket网络接口:

inb->qtest_inb->qtest_sendf->qtest_send->socket_send->qemu_send_full->send(socketfd, ...).

实现的。

系统调试

cwd_class_init:

cwd_initfn:

register bar:cwd_realize

cwd_reset:

cwd_io_write:

cwd_io_read:

vendor_id/device_id read:

配置空间读写

自定义设备中可以注册配置空间读写回调接口,这样就可以自定义配置空间的读写检查。

如果在创建模拟设备的时候不注册自己的配置空间回调函数,QEMU会使用默认的:

总结:

向QEMU虚拟机中添加PCI设备的流程就是如此简单,只需要给定配置空间,PCI设备就自然而然出现在虚拟机中了,在添加const MemoryRegionOps对象,注册必要的回调函数,设备就可以工作了。

MDEV框架就是依靠这种模式实现设备虚拟化中间层的,有空可以分析一下。


参考博客

QEMU如何虚拟PCI设备_qemu创建pci设备-CSDN博客

linux - How to add a new device in QEMU source code? - Stack Overflow

https://www.cnblogs.com/shaohef/p/3803817.html

https://www.cnblogs.com/shaohef/p/3800486.html

结束

;