至强服务器BIOS/UEFI驱动开发笔记
- 互联网
- 2025-08-16 22:45:03

至强服务器BIOS/UEFI驱动开发笔记 驱动开发基础 Hello UEFI Driver 项目 选择项目位置 初始化驱动代码文件结构 驱动程序入口和基本功能 导入AMI工程 AMI平台Hello UEFI Driver 编译问题 测试结果 打印设备列表 继续开发`HelloWorldSupported`函数 依赖配置 使用脚本编译 编译测试此DXE驱动模块 改进`HelloWorldSupported`函数 问题 继续实验 AMI实战 SDL和CIF 以界面方式增加工程 以代码方式增加工程 RoboVeb 踩过的坑 AMI VEB构建技巧 AMI VEB命令行构建 AMI构建单个工程 AMI App开发 关键函数和协议 主要步骤 HellWorld.c 测试结果 PCI Driver开发 基于EDKII工程的HelloWorldDxe 测试结果 继续开发`Supported`函数 测试结果 AMI PCI 驱动开发 新增加的DXE驱动放到和BIOS固件的哪里? U盘和键盘全消失等问题 矛盾的根源 UDK2015 编译环境 Windows编译环境 OvmfPkg VS2008安装问题 UDK2017 OVMF 2015 经典的DXE驱动案例 VGA驱动 仿照VGA驱动修改MyPciDxe 以上代码迁移到服务器 驱动的其它属性 工具类函数 简化的Supported函数与初步的Start函数 驱动的名字 实验使用的CPU架构Broardwell。 EFI App的构建过程实际上先构建可以在OS上运行的动态库/可执行文件,然后利用PE32+工具改为UEFI运行。 UEFI基于GObject( docs.gtk.org/gobject/)用C模拟OOP或C++。 JRE 1.7安装目录整个拷贝到VisualeBios.exe所在目录,并改名为jre。则Visual Bios的启动不需要安装JRE。卸载JRE 1.7验证。 UEFI编译系统强制要求函数的局部变量统一声明在函数体的头部,否则报错。 GRUB运行在BDS阶段,因为GRUB运行期间未调用ExitBootServices方法,实际调用此方法的是OS Loader。 UEFI环境特点 支持X86、X64、ARM等平台 单核CPU,没有线程,没有进程 没有抢断/优先级 没有中断,唯一的路径是定时器 模块内部通讯通过Protocols(协议)和Events(事件) C语言编程(原文:Programming is done through C language,实际上有汇编) 包的声明用dsc,模块的声明用inf,模块的依赖用dec 驱动开发基础 Hello UEFI Driver 项目
用UEFI Shell装载驱动进行测试。受载板平台限制,测试工程放在AMI项目里。
选择项目位置与UEFI App开发不同,驱动代码所处项目架构应当与硬件构成映射关系。如果驱动代码与驱动硬件不在相同架构,则开发者需要手动处理固件布局才成封装成为正确的固件。我亲自踩坑证明这个说法: 关键错误消息:Build\GetPpiName.c(1) : fatal error C1083: Cannot open include file: '/RELEASE_MYTOOLS/PpiTableIA32.c': No such file or directory。不知道驱动项目存放须按规定的开发者会认为这个问题是玄学问题,怎么生成的代码找不到生成的代码呢?建议参照下图和项目结构选择合适的UEFI驱动存储放置:
根据驱动目标选择合适的项目位置。本示例的目标为USB键盘驱动,因此选择MdeModulePkg/Bus。当然,如果你已对BIOS固件布局已非常清楚,你可以随意。
初始化驱动代码文件结构新建目录HelloWorldDxe,新建HelloWorldDxe.inf,代码如下:
[Defines] INF_VERSION = 0x00010005 BASE_NAME=HelloWorldDxe FILE_GUID = de296c9d-8bac-08bc-ac6d-db2998aff781 MODULE_TYPE = UEFI_DRIVER VERSION_STRING = 0.1 ENTRY_POINT = DriverMain [Sources] HelloWorldDxe.c [Packages] MdePkg/MdePkg.dec MdeModulePkg/MdeModulePkg.dec [LibraryClasses] BaseLib BaseMemoryLib DebugLib MemoryAllocationLib PrintLib UefiDriverEntryPoint UefiLib 驱动程序入口和基本功能新建HelloWorldDxe.c,代码如下:
#include <Uefi.h> #include <Protocol/DriverBinding.h> #include <Protocol/ComponentName2.h> #include <Protocol/ComponentName.h> #include <Library/UefiBootServicesTableLib.h> #include <Library/UefiDriverEntryPoint.h> #include <Library/UefiLib.h> #include <Library/DebugLib.h> #define HELLOWORLD_VERSION 0x10 EFI_STATUS EFIAPI HelloWorldStart( IN EFI_DRIVER_BINDING_PROTOCOL* This, IN EFI_HANDLE Controller, IN EFI_DEVICE_PATH_PROTOCOL* RemainingDevicePath ) { EFI_STATUS status = EFI_SUCCESS; Print(L"[HelloWorldStart] HelloWorld driver started.\n"); return status; } EFI_STATUS EFIAPI HelloWorldSupported( IN EFI_DRIVER_BINDING_PROTOCOL* This, IN EFI_HANDLE Controller, IN EFI_DEVICE_PATH_PROTOCOL* RemainingDevicePath ) { EFI_STATUS status = EFI_SUCCESS; Print(L"[HelloWorldSupported] HelloWorld driver supported.\n"); return status; } EFI_STATUS EFIAPI HelloWorldStop( IN EFI_DRIVER_BINDING_PROTOCOL* This, IN EFI_HANDLE Controller, IN UINTN NumberOfChildren, IN EFI_HANDLE* ChildHandleBuffer ) { EFI_STATUS status = EFI_SUCCESS; Print(L"[HelloWorldStop] HelloWorld driver stopped.\n"); return status; } EFI_DRIVER_BINDING_PROTOCOL g_helloworld_driver_binding = { HelloWorldSupported, HelloWorldStart, HelloWorldStop, HELLOWORLD_VERSION, NULL, NULL }; EFI_STATUS EFIAPI DriverMain( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE* SystemTable ) { EFI_STATUS status = EFI_SUCCESS; status = EfiLibInstallDriverBindingComponentName2( ImageHandle, SystemTable, &g_helloworld_driver_binding, ImageHandle, NULL, NULL ); ASSERT_EFI_ERROR(status); return status; } 导入AMI工程 在ModuleExplorer视图中依次展开:Components、Core、MdeModulePkg、LibraryInstances,右键单击,选择弹出菜单Add INF Module。关于Select EDK Project Root选项,选择结果无论是否正确,导入结果都存在错误。下文第2步和第3步就是纠错。 找到导入的工程,移动生成的sdl文件到inf文件所在目录,并更名。 按下图所示修改MdeModulePkg\Library\LibraryInstances.cif 关掉VeB软件,重新打开。编译报错:<work root>\Build\GrangevillePkg\RELEASE_MYTOOLS\X64\MdeModulePkg\Bus\HelloWorldDxe\HelloWorldDxe\DEBUG\AutoGen.h(16) : fatal error C1083: Cannot open include file: 'Uefi.h': No such file or directory。原因是HelloWorldDxe.inf有错。具体错误是Package依赖错误地写成dsc,正确的做法是写成dec AMI平台Hello UEFI Driver 编译问题AMI的工程不能运行EDKII提供的命令
VeB不允许编译单个驱动
这是假象。实际原因是VeB没有把导入INF生成的sdl文件放到inf所在目录。解决办法是移动sdl文件到inf文件所在目录并修正上级cif文件中的错误。
测试结果 UEFI Shell运行运行load指令可见大量的HelloWorld输出,说明UEFI驱动管理支持一个设备绑定多个驱动,不会因为某个设备已存在绑定的驱动而停止匹配新驱动。UEFI如何选择调用哪个驱动呢?驱动的版本的如何在驱动选择中发挥作用的? 服务器启动慢问题存在新证据,证据指向问题发生在SEC或者PEI阶段。下图右边神秘的数字,在HelloWorldDxe集成进BIOS固件之前不确定它显示时CPU执行阶段。现在可以证明处于DXE阶段。那么,从通电到HelloWorldDxe产生输出的大约10秒钟时间,很可能都处于SEC和PEI阶段。现在没有确定串口设备未初始化造成的HelloWorldDxe无输出的时间。EFI Shell启动时会再执行一次设备驱动管理过程。
UEFI驱动管理在得到Supported函数的返回结果为EFI_SUCCESS后立即调用Start函数。上图Supported输出与Start输出成对出现无间断说明这一点。
任何驱动应在EFI Shell中先load试运行。否则驱动出错造成很大的麻烦。DXE出错的结果是载板变砖头,救砖的办法可能只有把FLASH从电路板上焊下来,烧好程序后再焊上去。
固件烧录与固件运行时不同。以下截图的实验:
A版本包含HelloWorldDxe驱动,B版本与2023XXXX版本相同,唯一的区别是重新编译。编译环境、工具完全相同。 固件升级到A版本,重启后驱动绑定过程出现大量的HelloWorld打印 固件升级到B版本,重启时控制台出现大量的HelloWorld打印 打印设备列表 继续开发HelloWorldSupported函数目标:以字符串形式输出所有Device关键字,尝试寻找设备特征,在特征中搜寻键盘设备。代码如下:
EFI_STATUS EFIAPI HelloWorldSupported( IN EFI_DRIVER_BINDING_PROTOCOL* This, IN EFI_HANDLE Controller, IN EFI_DEVICE_PATH_PROTOCOL* RemainingDevicePath ) { EFI_STATUS status = EFI_SUCCESS; EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* device2txt = NULL; CHAR16* device_path = NULL; status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (void**)&device2txt); if (EFI_ERROR(status)) { Print(L"[HelloWorld Driver] LocateProtocol result: %d\n", status); return status; } device_path = device2txt->ConvertDeviceNodeToText(RemainingDevicePath, TRUE, TRUE); Print(L"[HelloWorld Driver] device: %s\n", device_path); return EFI_UNSUPPORTED; } 依赖配置这里采用VeB配置。
新增外部依赖DevicePathLib 新增Protocols依赖gEfiDevicePathProtocolGuid和gEfiDevicePathToTextProtocolGuid 使用脚本编译脚本编译的目的是为CI做准备。
@echo off chcp 65001 title AMI UEFI Build Tool echo Any question can be sent to zhtqs8@163 set CCX86DIR=<work root>\software\Aptio_5.x\x86\x86 set CCX64DIR=<work root>\software\Aptio_5.x\x86\amd64 set TOOLS_DIR=<work root>\software\Aptio_5.x\BuildTools set PATH=%PATH%;<work root>\software\Aptio_5.x\x86\x86 set PATH=%PATH%;<work root>\software\Aptio_5.x\amd64 set PATH=%PATH%;<work root>\software\Aptio_5.x\x86 set PATH=%PATH%;<work root>\software\Aptio_5.x\BuildTools set PATH=%PATH%;<work root>\software\Aptio_5.x\BuildTools\Bin\Win32 set PATH=%PATH%;<work root>\software\Aptio_5.x\VisualeBios\jre\bin\ cd <work root> cmd /k运行指令make rebuild,结果如下:
编译测试此DXE驱动模块编译,确认编译输出
- Done - Build end time: 17:07:12, Sep.15 2023 Build total time: 00:00:07运行load HelloWorldDxe.efi,确认结果
RemainingDevicePath的值始终为:F3 EE 00 F0。期望的结果为不同的设备不同的值。UEFI Driver Writer’s Guide大部分示例显示,Supported适用的流程是开发者用期望的Protocol尝试打开。结果成功就是支持,结果失败就是不支持。很多示例㫫示Start还会把这个逻辑再运行一次。
RemainingDevicePath不一定指设备,它还兼顾方便开发者在当前设备下挂载子设备。另外:
至强服务器BIOS/UEFI驱动开发笔记由讯客互联互联网栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“至强服务器BIOS/UEFI驱动开发笔记”