(请保留->发布地址: http://yiiyee.cn/blog/author/luobing/ )
Option ROM的开发不是一门显学,相关的资料也少得可怜。如果是在ODM厂商工作,或者做BIOS相关的工作,可以接触到很多相关的材料。不过,对我这种必须开发Option ROM,公司又不是相关行业的,非常少见。这也导致在开发Legacy BIOS的Oprom时,只能一点点摸索,遇到的问题如山之多。
UEFI下开发Option ROM相对好多了,一方面资料比较全面,EDK的文档组织还是比较明晰的;另一方面,它也提供了标准的工具,非常便利。我自己的测试卡是PCIE接口的,接下来讨论的Option ROM是针对PCI扩展ROM的(ISA ROM在UEFI spec中好像不支持了)。
UEFI的Option ROM结构与之前介绍的Legacy BIOS Oprom类似:
毕竟都是脱胎于PCI/PCIE规范,和Legacy Option ROM的结构是相同的。UEFI Option ROM利用了之前保留的字节(偏移0x04处),用来表明自己的身份。
1 UEFI Option ROM的加载过程
在MdeModulePkg的源文件MdeModulePkg\Bus\Pci\PciBusDxe\PciOptionRomSupport.c中,可以窥见UEFI Option ROM的处理过程。源文件中包含了处理Option ROM的8个函数:
Option ROM的执行文件不是在Flash上运行的,它会被拷贝到内存中,然后在内存中执行。Legacy BIOS的Option ROM一般加载到0xC0000~0xE0000(即0xC000段至0xE000段),而UEFI Option ROM并没有这样的约定。
仔细读读ProcessOpRomImage()的代码,可以窥见处理Option Rom的过程。
ProcessOpRomImage()的入口参数只有一个:PCI_IO_DEVICE *PciDevice。这是一个指向PCI设备的指针,包含了设备的所有属性以及其兄弟设备、父设备的信息。数据结构PCI_IO_DEVICE定义在PciBus.h中,可以对照PCI协议去理解。
函数中用do-while循环对设备进行循环处理,寻找到Option ROM的ROM signature(也即0xAA55)。而后对ROM结构进行分析,包括是否为EFI image、机器类型是否支持等,并创建其设备路径(DevicePath)。
在UEFI中,使用Device Path去描述一个设备的位置信息,总线、启动项等也常用它来描述。进入UEFI shell的时候,出现的各设备的字符串描述,就是Device Path:
Device Path共有六种,在Uefi Spec中有详细的描述:
函数中创建的Option ROM device path类型为MEDIA_DEVICE_PATH,这是一种能够作为启动项设备的Device Path。
创建完成后,调用LoadImage()和StartImage(),执行Option ROM代码。
2 如何生成UEFI Option ROM
UEFI Option ROM实际上是UEFI driver的一种,EDKII提供了相应的工具,将生成文件转换为Option ROM。前面已经说过了,我们现在所开发的option ROM,主要是PCI Option ROM。关于PCI Option ROM的内容可以参考《EDKII Driver Writer’s Guide for UEFI 2.3.1》,以下的介绍也主要来自于这个文档。
有两种方法可以生成PCI Option ROM镜像,使用工具EfiRom转换或直接使用EDKII的INF/FDF文件编译生成。
EfiRom提供了源代码,允许用户在任何支持EDKII的操作系统上编译。源代码位于\BaseTools\Source\C\EfiRom下,在我开发用的机器上(Win10),编译后的执行文件位于\BaseTools\Bin\Win32。
它提供的功能如下:
UEFI Option ROM是通过UEFI Driver转换而来的,至于如何编写UEFI driver,那是另外一个议题,这里不展开。
EfiRom会对传入的efi文件(UEFI Driver)进行验证,比如Rom头是不是0xAA55、PCI数据结构标识是不是“PCIR”等。任何一项检查不通过,则EfiRom会退出,创建Option ROM的过程将被终止。
生成命令如下:
EfiRom -f 0x9999 -i 0x8000 -e pcidriver.efi
其中,-f后指定Verdor ID,-i后指定 Device ID,-e之后给定需要转换的文件。更多的转换方法,包括如何与Legacy Option ROM一起打包转换、如何压缩等,请参考之前提到的手册《EDKII Driver Writer’s Guide for UEFI 2.3.1》第18章第7节。
另一种转换方法是使用INF/FDF,在build命令执行的时候,自动调用efirom将其转换为指定的Option ROM。这是我常用的方法,编译的同时就完成了转换过程,一个典型的INF例子如下:
Vendor ID和Device ID可以在Inf文件中指定,其他包括PCI类码、PCI版本、是否对Option ROM进行压缩(PCI_COMPRESS)都可以指定。
不管是采用EfiRom工具直接转换,还是使用Inf文件,都只能对一个UEFI Driver进行处理。如果需要同时管理多个UEFI Driver,以及生成多种类型的Option ROM(IA32、X64等),可以使用FDF文件进行处理。具体的内容就不一一讨论了,同样可以参考上述的编程手册。
3 软件结构
UEFI Option ROM实质上是UEFI Driver,因此最好找一个比较“纯洁”的Driver例子开始。在github的EDKII部分找了很久,始终没有找到合适的。EDKII现在提供了开发驱动的工具 UEFI Driver Wizard,我没有去使用过,也许很多示例都集成到这里面去了。
最终还是使用了我以前使用的BlankDrv,我一向以这个为基础构建UEFI Option ROM。它目前还可以在这个地方下载:
https://sourceforge.net/projects/edk2/files/EDK%20II%20Releases/Demo%20apps/
对于Driver的开发,《UEFI原理与编程》中说得很详细,值得好好研究一下。不过,我的目标是开发出UEFI Option ROM,很多细节不需要深究。
所开发的Option ROM是遵循UEFI driver module的,实际上也算是PCI Driver,所以它必须实现EFI_DRIVER_BINDING_PROTOCOL,并实例化Supported(), Start(), and Stop() 这三个服务。
前面已经探讨过如何启动Option ROM了,目前我们主要关心在哪里加上实际执行的代码。
大部分执行代码是在Start()中添加的,Start()主要的任务是启动硬件设备,在函数中最重要的事情是调用InstallProtocolInterface()或者InstallMultipleProtocolInterfaces()在ControllerHandle上安装驱动Protocol。
而Stop()函数用来卸载驱动,并停止硬件设备,基本上不需要修改BlankDrv提供的原始代码。
Supported()用来评估传递给Driver的device handle所指明的pci controller是否能被driver所驱动,主要通过Vendor ID、Device ID、Class code判定。
如前所述,具体的驱动细节可以参考其他的文档,架构搭建完后,就可以专注在Option ROM的功能实现上了。之前35篇博客中所讨论的议题,所编写的代码,完全可以移植到Option ROM中,只要硬件设备的扩展ROM大小足够。
4 编译及演示
因为涉及到公司商业上的原因,原始代码不能贴出。我正在考虑使用另外一种PCI-E芯片做测试卡,ROM空间大些,做一些相对复杂的功能,到时再将代码公开。
编译和写入到测试卡的过程,可以参考博客:“UEFI开发探索12 – Oprom测试板”。
这里把测试的结果再贴一次:
至此,UEFI探索系列的主线-UEFI Option ROM的开发就完成了。
对我来说,真是个不错的经历。从决定写这个系列博客,到现在已经半年多了。每周都强迫自己坐到书桌旁开始敲字,从不习惯到成自然,现在都成为我每周必做的功课了。
在这个过程中,我完成了很多知识的补全。不过,感觉上了解越多,不理解的也越来越多。
计划中还有14篇,将针对各个我感兴趣的议题再进行整理。这个探索的旅程还远到没结束的时候。
原文地址:
http://yiiyee.cn/blog/2019/08/04/uefi%e5%bc%80%e5%8f%91%e6%8e%a2%e7%b4%a236-uefi-option-rom/
9,380 total views, 4 views today