开发 QuickVisor Scenario
场景工程简介
场景工程(Scenario Project)主要用于将多个不同类型的 GuestVM 集成到同一个硬件平台上。Scenario 工程主要包含 Platform 资源文件、QuickVisor 资源文件 和 GuestVM 资源文件。编译 Scenario 工程时,构建系统将编译并打包所需的资源文件到 QuickVisor 的 ROMFS 文件中。在 QuickVisor 启动时,QuickVisor 将根据 ROMFS 中的配置文件和镜像文件创建虚拟机。
- Platform 资源文件主要包含硬件平台的设备树文件。
- QuickVisor 资源文件主要包含硬件资源设备树文件和虚拟资源配置文件,虚拟资源配置文件也使用设备树文件的格式进行编辑。
- GuestVM 资源文件主要包含虚拟机设备树文件、虚拟机镜像文件和虚拟机 ROMFS 文件。
创建场景工程
Scenario 工程的创建流程
首先,选择 File > New > QuickVisor Scenario 菜单,开始创建 Scenario 工程,为新建的 Scenario 工程输入名称,单击 Next 按钮进入编译链配置界面。
然后,右键工程 Propeities > QuickVisor Setting > Scenario Setting 进入 Scenario 配置页面,在配置页面中可以新建虚拟机。
接着,选中目标工程单击右键选择 Build Project 或者单击工具栏的一键编译按钮,进行工程编译。
Scenario 工程的目录结构
Scenario 工程模板的目录结构如下:
项目 | 说明 |
---|---|
/src/platform/platform.dts | 创建工程时选择目标板卡,此文件为目标板卡对应的设备树文件。 |
/src/quickvisor/quickvisor.dts | QuickVisor 的设备树文件,空的模板文件。 |
/src/quickvisor/quickvisor.cfg | QuickVisor 的配置文件,空的模板文件。 |
/src/guest-vm0/guest-vm0.dts | 用于描述 VM0 的设备树文件,空的模板文件。 |
/src/guest-vm0/guest-vm0.bin | 在 VM0 中运行的 GuestOS 二进制文件。 |
/src/guest-vm0/guest-vm0.rfs | 传递给 GuestOS 使用的 ROMFS 镜像文件。 |
/readme.txt | 场景工程的使用说明文档。 |
实现场景工程
Scenario 工程的开发流程
场景工程的开发流程如下:
- 获取 platform.dts 文件:在创建工程时选择板卡型号,工程的 platform 目录下将自动导入板卡的设备文件。
- 编辑 quickvisor.dts 文件:根据 platform.dts 编辑 quickvisor.dts,描述 Hypervisor 占用的硬件资源。
- 编辑 quickvisor.cfg 文件:描述物理 CPU 域的划分、描述内核提供的虚拟资源、描述虚拟机的可访问视图。
- 编辑 guest-vmx.dts 文件:根据 quickvisor.cfg 编辑 guest-vmx.dts,描述 GuestVM 的硬件资源。
- 添加 guest-vmx.bin 文件:将编译好的 GuestOS 镜像文件拷贝到相应的资源目录下。每个 GuestVM 都必须有这个文件。
- 添加 guest-vmx.rfs 文件:将编译好的 ROMFS 镜像文件拷贝到相应的资源目录下。如果 GuestOS 不需要则不用添加此文件。
- 配置场景工程:通过编辑场景工程 Makefile 文件的方式,或者通过配置场景工程 IDE 配置页的方式,配置各个 GuestVM 所使用的资源文件。
- 编译场景工程:编译时将会把 dts 文件编译成 dtb 文件,然后把所有 dtb 文件和客户机的 bin 文件打包到 Debug/Release 目录下的 quickvisor.rfs 文件中。
- 部署和启动:QuickVisor 启动时,将挂载 quickvisor.bin 到 ROMFS 文件系统,然后解析 QuickVisor 配置文件,从而创建相应的内核资源和虚拟机。
Scenario 工程的编译配置
场景工程的编译配置项主要包括:Platform 配置项、QuickVisor 配置项 和 GuestVM 配置项。这些配置项被记录在 Scenario 工程对应的 Makefile 文件中。scenario_name.mk
中各个变量的含义如下:
- PLATFORM_DTS:该变量用于记录 目标板卡对应的设备树文件。
- QUICKVISOR_DTS:该变量用于记录 QuickVisor 的设备树文件。
- QUICKVISOR_CFG:该变量用于记录 QuickVisor 的配置文件。
- GUEST_VMx_BIN:该变量用于记录 GuestOS 内核镜像文件。
- GUEST_VMx_DTS:该变量用于记录 GuestOS 使用的设备树文件。
- GUEST_VMx_RFS:该变量用于记录 GuestOS 使用的 ROMFS 文件。
- LOCAL_GUEST_VMS:该变量用于记录 需要打包到 QuickVisor ROMFS 的 GuestVM。
GUEST_VMx_BIN 变量名中的 x 代表 GuestVM 的编号,x 可以是 '0-n'。GUEST_VMx_BIN、GUEST_VMx_DTS、GUEST_VMx_RFS 都用于记录 VMx 对应的各种资源文件。
编辑 quickvisor.dts 文件
根据 platform.dts 编辑 quickvisor.dts,描述 Hypervisor 占用的硬件资源。本文档不会专门介绍设备树的语法规则,开发者可以通过设备树官方手册自行学习。当前 QuickVisor 并没有完全使用设备树来识别和管理所有设备,开发者仍需通过硬编码的方式向内核提供初始化参数 qv_bsp_params_t
,该结构体主要记录了 CPU 相关的信息、QuickVisor 程序段相关的信息、系统的初始映射关系。
typedef struct {
const char *name;
qv_virt_addr_t vaddr;
qv_phys_addr_t paddr;
qv_size_t size;
qv_uint32_t type;
#define QV_INIT_MAP_TYPE_INVALID 0 //非法的映射关系
#define QV_INIT_MAP_TYPE_KTEXT 1 //内核代码段的映射
#define QV_INIT_MAP_TYPE_KDATA 2 //内核数据段的映射
#define QV_INIT_MAP_TYPE_DEVICE 3 //物理设备的寄存器地址空间映射
#define QV_INIT_MAP_TYPE_ROMFS 4 //ROMFS 所在内存区域的映射
#define QV_INIT_MAP_TYPE_DMAIO 5 //DMA 传输所需内存的映射
#define QV_INIT_MAP_TYPE_GUEST 6 //GuestVM 所需内存的映射,包括 ROM 和 RAM
#define QV_INIT_MAP_TYPE_RESV 7 //保留的内存空间,在不支持IOMMU时给具有直通设备的VM使用
} qv_init_mapping_t;
编辑 quickvisor.cfg 文件
编辑 quickvisor.cfg 文件 ,主要步骤为:
向 quickvisor.cfg 添加 domain 节点,domain 节点用于配置物理 CPU 分区。
向 quickvisor.cfg 添加 hvdev 节点,hvdev 节点用于描述位于 Hypervisor 内的虚拟设备。
向 quickvisor.cfg 添加 guest 节点,guest 节点用于描述需要创建的 GuestVM 虚拟硬件视图。
编辑 domain 节点,节点属性的含义可以在 /quickvisor/libquickvisor/src/include/qv_hypconf.h 文件中找到。
domains {
domain0 {
compatible = "qv-domain";
dom_name = "domain0";
dom_cpus = <0x03>;
dom_algo = "default-class";
dom_ticks = <100>;
};
domain1 {
compatible = "qv-domain";
dom_name = "domain1";
dom_cpus = <0x0C>;
dom_algo = "default-class";
dom_ticks = <100>;
};
};
domain 节点属性说明如下:
节点属性名称 | 节点属性类型 | 节点属性说明 |
---|---|---|
compatible | "qv-domain" | CPU 域节点标识符 |
dom_name | 字符串 | CPU 域名称 |
dom_cpus | 32 位掩码 | CPU 域管理的物理 CPU |
dom_algo | 字符串 | CPU 域使用的调度算法 |
dom_ticks | 32 位整数 | 含义取决于调度算法 |
编辑 hvdev 节点,节点属性的含义可以在 /quickvisor/libquickvisor/src/include/qv_hypconf.h 文件中找到。
hvdevs {
hvdev0 {
compatible = "qv-hvdev";
hvdev_name = "serial0";
hvdev_type = "serial-line";
hvdev_mem_size = <0>;
hvdev_port_num = <2>;
};
hvdev1 {
compatible = "qv-hvdev";
hvdev_name = "bridge0";
hvdev_type = "net-bridge";
hvdev_mem_size = <0>;
hvdev_port_num = <8>;
};
hvdev2 {
compatible = "qv-hvdev";
hvdev_name = "disk0";
hvdev_type = "blk-disk";
hvdev_mem_size = <0>;
hvdev_port_num = <1>;
hvdev_host_dev = "ramdisk-p0";
};
};
hvdev 节点属性说明如下:
节点属性名称 | 节点属性类型 | 节点属性说明 |
---|---|---|
compatible | "qv-hvdev" | HVDEV 节点标识符 |
hvdev_name | 字符串 | HVDEV 名称 |
hvdev_type | 字符串 | HVDEV 模型 |
hvdev_mem_size | 32 位整数 | HVDEV 存储空间 |
hvdev_port_num | 32 位整数 | HVDEV 通道数(可选) |
hvdev_host_dev | 字符串 | HVDEV 后端设备(可选) |
编辑 guest 节点,节点属性的含义可以在 /quickvisor/libquickvisor/src/include/qv_hypconf.h 文件中找到。
guests {
guest0 {
compatible = "qv-guest";
guest_name = "vm0";
guest_domain = "domain0";
guest_startup = <1>;
guest_window = <50>;
guest_permiss = <0>;
guest_cpu_nr = <2>;
guest_cpu_pc = <0>;
guest_cpu_r0 = <0>;
rom {
compatible = "qv-guest-device";
guest_device_type = "vdev";
guest_device_class = "rom";
guest_device_fw = "/guest-vm0.bin";
guest_aspace_gpa = <0x0 0x00000000>;
guest_aspace_siz = <0x0 0x01000000>;
};
ram {
compatible = "qv-guest-device";
guest_device_type = "vdev";
guest_device_class = "ram";
guest_aspace_gpa = <0x0 0x80000000>;
guest_aspace_siz = <0x0 0x04000000>;
};
gic_dist {
compatible = "qv-guest-device";
guest_device_type = "vdev";
guest_device_class = "irq";
guest_device_model = "arm,vgicv2,dist";
guest_aspace_gpa = <0x0 0x01400000>;
guest_aspace_siz = <0x0 0x1000>;
};
gic_cpu {
compatible = "qv-guest-device";
guest_device_type = "pdev";
guest_device_class = "dev";
guest_aspace_gpa = <0x0 0x01410000>;
guest_aspace_siz = <0x0 0x1000>;
guest_aspace_hpa = <0x0 0x01460000>;
};
gen_timer {
compatible = "qv-guest-device";
guest_device_type = "vdev";
guest_device_class = "dev";
guest_device_model = "arm,vgtimer";
};
uart0 {
compatible = "qv-guest-device";
guest_device_type = "vdev";
guest_device_class = "dev";
guest_device_model = "arm,pl011";
guest_aspace_gpa = <0x0 0x21c0000>;
guest_aspace_siz = <0x0 0x10000>;
guest_interrupt_guest = <86>;
guest_backend_name = "serial0";
guest_backend_chan = <0>;
};
};
};
guest 节点属性说明如下:
节点属性名称 | 节点属性类型 | 节点属性说明 |
---|---|---|
compatible | "qv-guest" | VM 节点标识符 |
guest_name | 字符串 | VM 虚拟机名称 |
guest_domain | 字符串 | VM 所属 CPU 域 |
guest_profile | 文件路径 | VM 虚拟机配置文件 |
guest_startup | 布尔值 | VM 创建后是否启动 |
guest_window | 32 位整数 | VM 占用 CPU 时间片 |
guest_permiss | 32 位整数 | VM 可访问 hypcall 的权限 |
guest_cpu_nr | 32 位整数 | VM 拥有的 vcpu 个数 |
guest_cpu_pc | 32 位整数 | VM 主核启动时的地址 |
guest_cpu_r0 | 32 位整数 | VM 主核启动时的 r0 参数 |
guest_cpu_r1 | 32 位整数 | VM 主核启动时的 r1 参数 |
guest_cpu_r2 | 32 位整数 | VM 主核启动时的 r2 参数 |
guest_cpu_r3 | 32 位整数 | VM 主核启动时的 r3 参数 |
compatible | "qv-guest-device" | 客户机设备节点标识 |
guest_device_status | 布尔值 | 标识设备节点是否使能 |
guest_device_type | 32 位整数 | 标识设备节点是否为虚拟设备 |
guest_device_class | 32 位整数 | 标识设备节点所代表设备的类型 |
guest_device_fw | 文件路径 | 标识设备节点初始固件路径 |
guest_device_model | 字符串 | 虚拟设备对应的设备模型 |
guest_device_serial | 字符串 | 虚拟设备序列号 |
guest_aspace_gpa | 64 位整数 | 虚拟设备占用的 GPA |
guest_aspace_siz | 64 位整数 | 虚拟设备占用的 GPA 空间大小 |
guest_aspace_hpa | 64 位整数 | 指明 GPA 实际映射到的 HPA |
guest_interrupt_guest | 32 位整数 | 虚拟设备占用的客户机中断号 |
guest_interrupt_host | 32 位整数 | 客户机中断实际映射到的主机中断 |
guest_backend_name | 字符串 | 虚拟设备使用哪一个后端设备 |
guest_backend_chan | 32 位整数 | 虚拟设备使用后端的哪一个通道 |
注意:当前 QuickVisor 并不支持多地址区间和多中断号,因此在编辑 quickvisor.cfg 文件时,需要将实际设备节点拆分成多个简单节点。
编辑 guest-vmx.dts 文件
根据 quickvisor.cfg 编辑 guest-vmx.dts,描述 GuestVM 的硬件资源。本文档不会专门介绍设备树的语法规则,开发者可以通过设备树官方手册自行学习。编辑 guest-vmx.dts 文件 ,主要步骤为:
- 在设备树文件中添加 CPU 设备树节点。
- 在设备树文件中添加 ROM 设备树节点、RAM 设备树节点。
- 在设备树文件中添加 PSCI 设备树节点。
- 在设备树文件中添加 GenTimer 设备树节点。
- 在设备树文件中添加 GIC 设备树节点。
- 在设备树文件中添加 UART 设备树节点。
以上这些设备树节点描述了一个完整的虚拟机最小硬件系统,其中 CPU 节点的个数不可以大于物理 CPU 节点的个数,ROM/RAM 节点占用的内存不可以大于 QuickVisor BSP 中 GUEST-MEM-REGION 区域的内存大小,PSCI 节点必须配置为 hvc 启动方法,GenTimer 节点描述真实物理设备的信息,GIC 节点描述真实物理设备的信息,UART 描述真实物理设备或虚拟设备的信息。
构建 guest-vmx.bin 文件
QuickVisor GuestOS 镜像的构建将在 开发 QuickVisor GuestOS
章节中介绍。用户只需要根据虚拟机视图,即根据 guest-vmx.dts 构建自己 GuestOS 镜像即可。GuestOS 构建完成后,需要将镜像文件拷贝到对应 GuestVM 的资源文件夹下。