一、前言
ZVM(Zephyr-based Virtual Machine)作为新一代Type 1.5 RTOS虚拟化解决方案(Hypervisor),为单芯片多OS和多任务处理提供了实时且灵活的虚拟化支持。在嵌入式设备中,串口通信是数据传输与调试的关键手段,然而,集成在芯片上的串口数量往往有限(有的芯片仅配备1个串口)。在开发虚拟化技术与功能时,单一串口显然无法满足需求,因为ZVM及其多个虚拟机OS通常需要同时使用串口进行调试。为解决这一问题,采用分时复用的串口虚拟化机制,可以有效缓解串口资源不足的困境,支持多个虚拟机共享有限的串口资源,确保开发过程中各虚拟机的调试需求得到满足。
嵌入式计算湖南省重点实验室以QEMU平台为例,介绍ZVM中的串口虚拟化方案。ZVM通过分层的设备模拟架构以及数据转发机制实现串口虚拟化功能,为虚拟机分配独立、充足的虚拟串口资源,突破了物理串口数量的限制,为多虚拟机系统提供了灵活的数据交换机制,同时为开发者提供了简便的抽象层接口,最终实现基于单一物理串口对ZVM与虚拟机间的一对多交互。
二、ZVM虚拟串口的设计与实现
1、整体架构设计
虚拟串口的整体架构设计可以分为以下三层,各层共同协作实现虚拟串口的完整功能。
以ARM PL011 UART控制器为例,该层负责模拟真实的硬件行为,包括寄存器映射、数据发送与接收、中断处理等。它提供虚拟串口抽象层的接口的具体实现,从而实现对真实ARM PL011控制器的模拟。
该层提供对虚拟串口的抽象接口,屏蔽具体UART控制器的硬件细节,使上层应用可以方便地使用虚拟串口。主要包括:设备初始化、设备生命周期管理、数据收发等。后续新增其它UART设备的模拟只需完成串口设备模拟层。
ZVM使用Zephyr 的Shell UART作为人机交互的媒介,我们利用Shell UART的Bypass机制绕过默认的 Shell处理逻辑,当与虚拟机交互时,将采用特定的数据转发函数(FUNC)。在这个转发函数中,使用虚拟串口抽象层的接口即可实现与虚拟机的交互。
与虚拟机交互时,虚拟串口整体工作逻辑如下图1所示:

图1 虚拟串口工作逻辑
2、虚拟设备层实现
设备层负责实现虚拟串口的具体硬件行为,确保能够为虚拟机模拟出正确的 ARM PL011 UART控制器的功能。以下是设备层的主要内容:
设备层需要模拟 ARM PL011的寄存器,包括数据寄存器(DR)、控制寄存器(CR)、状态寄存器(FR)等,如下图2所示。

图2 ARM PL011寄存器结构体
寄存器模拟的最基本任务是支持虚拟机对设备寄存器的读写,当虚拟机通过内存映射读写UART设备时,该层应当实现对具体地址处数据的写入和返回函数,下图3为一个实现写入功能的示例。

图3 虚拟串口寄存器写入函数
寄存器模拟的另一重要任务是正确模拟硬件设备的行为。比如,虚拟机将数据写入DR后,不仅需要立即取出数据,还应及时修改RIS寄存器的TX中断位,表示发送缓冲区为空,触发TX中断,虚拟机会再次收到中断而继续发送数据。
除寄存器的模拟外,还应为虚拟串口设备设置缓冲区。我们使用Zephyr的k_fifo管理数据流,确保数据的正确传输,并避免数据丢失。比如,虚拟机读取DR时,应当实现从FIFO中取出一个数据,返回给虚拟机,部分代码如下图4所示。此时FIFO一定不为满状态,需要保证FR的RXFF位为0,若FIFO已空,需要置FR的RXFE位为1。与此同时,也需要负责RX中断相关寄存器的维护,本处不再赘述。

图4 虚拟串口FIFO管理代码示例
简而言之,虚拟串口设备中断的模拟工作就是在应当发出中断信号的时机,由ZVM向虚拟机发送中断信号。
是否发送中断信号的依据有两方面:
虚拟机是否屏蔽了所有中断;
FIFO中数据量是否达到触发中断的阈值(当然,也可设置为只要存在数据立即通知虚拟机读取)。
通过PL011的驱动程序可知,虚拟机对串口设备的中断的使能、屏蔽操作会落实到IMSC寄存器上,如下图5所示。因此,在虚拟机未屏蔽所有中断的情况下都应发生中断信号。判断逻辑如下图6所示,其中enabled与level的状态对应着上述两方面依据。

图5 PL011驱动程序片段

图6 PL011中断模拟程序片段
3、数据转发层实现
数据转发层的任务是实现用户与多虚拟机的交互,主要分为数据转发逻辑与虚拟串口切换逻辑。我们通过Shell的Bypass机制,如下图7所示,将用户输入的数据直接转发给虚拟串口设备,或者将虚拟串口设备接收到的数据直接转发给用户所用终端。

图7 Bypass 机制代码片段
由此可见,当不存在自定义的Bypass处理函数时,Shell将执行后续默认处理逻辑。同时,我们注册了自定义命令,可以实现指定要连接的虚拟机,以及设置特定的组合按键退出与虚拟机的连接。
至此,我们可以实现在多虚拟机与ZVM之间的灵活切换。
4、功能演示
本处演示为QEMU平台上ZVM启动Zephyr RTOS和Linux虚拟机,并通过虚拟串口与虚拟机交互,以及交互对象的切换。

图8 ZVM虚拟串口功能演示
三、注意事项
针对PL011串口驱动的虚拟化,Linux中的amba-pl011驱动在初始化时直接访问的部分地址(即最初的几次读寄存器操作)与Zephyr有一定差异,如图9所示,因此在针对Linux VM的串口寄存器访问时,需进行特殊处理。

图9 Linux VM中的PL011驱动访问寄存器LOG信息
