BIOS中的仿真器,解决ARM和RISCV板卡生态匮乏的神器:X86Emulator
前年,在一个风和丽日的午后,我接待了一位风尘仆仆的客户。不,准确的来说是处理一起上门踢馆的客诉。在简单的寒暄之后,开始进入正题。原来,他们公司的PCIe显卡在某个基于ARM国产CPU的主板上运行不了,来寻求支持。于是发生了下面的对话:
我:请问贵司的显卡在别的平台上试过吗?
客户:我们的显卡在Intel和AMD的主板上跑都没问题的。
我:那在ARM的机器上试过吗?
客户:需要吗?
我:不需要吗?
客户(一脸懵逼):需要吗?
为了避免尴尬,我赶紧换了一个问题:请问贵司的显卡带ARM原生的Option ROM吗?
客户:我们的显卡在Intel和AMD的主板上跑都没问题的。
我:那就只有x86的,没有ARM的了?
客户:需要吗?
我:不需要吗?
客户(一脸懵逼):需要吗?
看着金主爸爸空洞的眼神,我的思绪开始漂移。看来,ARM的生态建设任重而道远啊,众多的外围设备都需要适配ARM Option ROM, 这真是一个系统工程。国产CPU很多都是基于ARM的,而外围板卡适配的难度还不为很多人所知,作为科普人士的我,工作不到位啊。想到这里,肩膀上的担子感觉更重了!拉回了思绪,我清了清嗓子,开始了ARM指令集和x86指令集的科普工作。时间过得真快,在落日的余晖下,我送走了疲惫的显卡公司销售,尽管口干舌燥,但我感到十分充实。我紧了紧红领巾,迎着落日,一蹦一跳向家走去!嗯?怎么感觉这段在女儿的作业里看到过?
但后来,我得知一个高级神器,让我有了不一样的答案。如果上天再给我一次机会,在那个该死的下午,我一定会真诚地看着金主爸爸的眼睛,回答:
我:是的,不需要,您说的都对。
这个高级神器,就是BIOS中的仿真器:X86Emulator [1]。它是一个BIOS模块,它可以在ARM的BIOS中,仿真运行x86 ISA的UEFI驱动,包括各种板卡内置的UEFI驱动。它即插即用,全自动在ARM BIOS中加载运行x86驱动,就像原生ARM驱动一样!它是如何做到的呢?
X86Emulator工作原理
Qemu[2] ,能够在很多ISA架构的Host中仿真其他ISA的运行环境。借助于灵活的TCG(Tiny Code Generator)层,Host和Guest设置非常灵活,可以方便的两两配对。BIOSer这几年接触Qemu越来越多,很多原因是OVMF和RISC-V的调试需要。Qemu更多的是UEFI运行的容器,是UEFI in Qemu。能不能把Qemu做到UEFI中呢?对了,这就是x86Emulator的运行基石:Qemu in UEFI。
将Qemu移植成为一个UEFI驱动只完成了运行的引擎,还需要解决两个问题:
1. 怎么让BIOS在x86驱动被加载的时候自动运行仿真器?
2. UEFI驱动在运行时一定会大量调用UEFI Boot time和Run time服务,两种异构ISA的程序怎么交互调用?
如何自动运行?
出乎大多数BIOSer的认知,UEFI其实早就支持仿真器。UEFI内核在Image加载的时候,会根据文件头部的Machine Type检查已经注册的仿真器,如果两者匹配,则让该仿真器运行这个驱动,而不是CPU原生运行。这个机制让EBC(EFI Byte Code)驱动得以运行,今天,也让x86仿真器可以自动运行。
x86Emulator仅需要安装标准的PECOFF_IMAGE_EMULATOR_PROTOCOL,并在machine Type中填入EFI_IMAGE_MACHINE_X64(就是x86的64位驱动) ,UEFI内核就会在加载x86驱动时候,调用x86Emulator的相关函数,用Qemu解释运行x86代码了。
2
x86和ARM代码如何交互
当然有更麻烦的做法,但x86Emulator的做法相当聪明而高效。它利用了ARM MMU的NX(No-eXecute,Intel也有类似的技术)安全技术。它设计的目的,主要是为了对抗buffer overflow和stack overflow等攻击,将数据标记为NX,可以有效阻止这些攻击手段。它的细节和x86Emulator无关,本文就不展开讲了,仅仅需要知道,在标记了NX的内存,如果运行代码,立刻会产生的异常。
x86Emulator巧妙的利用这种机制,注册了异常处理函数,并将x86驱动所在的内存标记为NX。这样相当于将x86驱动从ARM BIOS中隔离出来,进出都会调用到x86Emulator,而它则巧妙的在整个异常函数中,转换ARM ISA和x86在calling convention中不同的参数传递寄存器。它的这种做法如此巧妙,让这种调用完全无感,甚至可以嵌套进行!
结论
x86Emulator是开源的,大家可以去它的主页 下载研究它的代码。它是商业相对友好的LGPL License。目前它仅仅支持在ARM的AARCH64(64位ARM ISA)上模拟x64,可以解决一部分ARM机器,外围板卡相对x86匮乏的问题;对于国产板卡,也有帮助。我们尝试过一款A卡,甚至最新的Intel Arc显卡,还有一块网卡,都可以正常在ARM N1 SDP机器上和飞腾D2000主板上,正常运行。
方兴未艾的RISC-V CPU的生态更加薄弱。大的主流的PCIe板卡,很多都有了ARM Native的Option ROM,但RISC-V的应该是一个没有。因为QEMU对RISC-V支持的很好,RISC-V既可以作为QEMU的Guest(普通用法),也可以是Host。相信x86Emulator做一些升级,应该也可以在RISC-V上仿真运行x86 option rom,部分解决生态短板。当然要做到像ARM这么高效,需要MMU支持NX,这个目前我还没又看到。
参考资料
[1] x86Emulator主页:https://github.com/ardbiesheuvel/X86EmulatorPkg
[2] qemu主页:https://www.qemu.org/docs/master/system/introduction.html