UEFI_Shell_App_原理详解
UEFI Shell App 原理详解UEFI Shell App 是什么UEFI Shell App 本质上是一个运行在 UEFI 环境里的.efi可执行程序。它和 Windows 下的.exe很像只不过运行环境不是 Windows而是 BIOS/UEFI 固件提供的一套服务。电脑刚开机时操作系统还没有启动。这个阶段由 UEFI 固件控制。UEFI 固件可以运行一种特殊程序.efi例如BOOTX64.EFI Shell.efi BiosDumpApp.efi这些.efi文件就是 UEFI 程序。UEFI Shell App 是其中一种类型MODULE_TYPE UEFI_APPLICATION它可以在 UEFI Shell 里运行也可以被 BIOS/UEFI 固件直接启动。它运行在哪里普通程序运行在操作系统上。例如 Windows 程序Windows 程序 - 依赖 Windows API - 访问文件用 CreateFile - 打印用 printf 或 Windows 控制台 APIUEFI Shell App 运行在操作系统启动之前UEFI Shell App - 依赖 UEFI Boot Services - 依赖 UEFI Runtime Services - 打印用 ConOut 或 Print() - 读变量用 GetVariable()所以它不能调用 Windows API也不能使用普通main()作为入口。入口函数普通 C 程序入口通常是intmain()UEFI App 的入口通常是EFI_STATUS EFIAPIUefiMain(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE*SystemTable)其中ImageHandle表示当前这个 EFI 程序本身。SystemTable是 UEFI 固件交给程序的总入口里面有各种服务表。最重要的是SystemTable-BootServices SystemTable-RuntimeServices SystemTable-ConOut SystemTable-ConIn在 EDKII 中通常用全局变量简写gBS// Boot ServicesgRT// Runtime ServicesgST// System TableBoot Services 是什么Boot Services 是 UEFI 在操作系统启动前提供的一组服务。它可以用来找设备找 handle找 protocol分配内存打开 protocol等待事件加载 EFI 程序启动 EFI 程序打印 protocol 时用到的就是 Boot ServicesgBS-LocateHandleBuffer(...)gBS-ProtocolsPerHandle(...)含义是找出系统里所有 UEFI handle 再查看每个 handle 上挂了哪些 protocolBoot Services 只在操作系统启动前可靠可用。操作系统一旦调用ExitBootServices()很多 Boot Services 就不能再用了。Runtime Services 是什么Runtime Services 是 UEFI 提供的另一组服务。它们在操作系统启动后理论上也可能继续存在。常见功能包括读写 UEFI variable获取时间设置时间重启或关机打印 variable 和 boot option 时用到的是gRT-GetNextVariableName(...)gRT-GetVariable(...)也就是枚举所有 UEFI variable 读取每个 variable 的内容BIOS 启动项比如BootOrder和Boot0000也是 UEFI variable。Protocol 是什么UEFI 中很多功能都用 Protocol 表示。可以把 Protocol 理解成UEFI 里的接口例如一个磁盘设备可能支持BlockIoProtocol DiskIoProtocol DevicePathProtocol SimpleFileSystemProtocol一个键盘设备可能支持SimpleTextInputProtocol SimpleTextInputExProtocol一个文件系统可能支持SimpleFileSystemProtocol每个 Protocol 都有一个 GUID 作为唯一标识。所以打印 protocol 本质上是在问固件现在系统里有哪些对象 每个对象支持哪些 UEFI 接口Handle 是什么UEFI 里的 Handle 可以理解成一个设备、一个驱动、一个控制器或者一个抽象对象的句柄Protocol 通常安装在 Handle 上。关系大概是Handle A - Protocol 1 - Protocol 2 - Protocol 3 Handle B - Protocol 4 - Protocol 5所以枚举 protocol 的标准方式是先找所有 handle 再看每个 handle 上有哪些 protocolVariable 是什么UEFI Variable 是固件保存配置的键值数据。它由三部分共同标识变量名Vendor GUID属性常见变量包括BootOrder Boot0000 SecureBoot SetupMode PK KEK db dbx变量可能保存在主板 NVRAM 中断电后仍然存在。变量有属性例如NV 非易失断电保存 BS Boot Services 阶段可访问 RT Runtime 阶段可访问程序枚举 variable 的过程是GetNextVariableName() - 得到下一个变量名和 Vendor GUID GetVariable() - 读取这个变量的属性、大小、内容Boot Option 是什么BIOS 启动菜单里的每个启动项通常就是一个 UEFI Variable。名字格式是Boot0000 Boot0001 Boot0002启动顺序存在BootOrder例如BootOrder 0001, 0000, 0002意思是先尝试 Boot0001 再尝试 Boot0000 再尝试 Boot0002每个Boot####变量内部是一个EFI_LOAD_OPTION结构里面有启动项属性显示名称设备路径可选参数所以程序解析Boot####后可以打印启动项名称是否 active对应设备路径是否在 BootOrder 中Shell App 如何打印在 UEFI Shell App 中不能用普通 Windows 控制台。EDKII 提供Print(Lhello\n);它最终会调用 UEFI 的控制台输出gST-ConOut-OutputString()所以屏幕上看到的文字是 UEFI 固件自己的文本输出服务显示出来的。Shell App 如何读写文件UEFI 中读写文件一般通过SimpleFileSystemProtocol EFI_FILE_PROTOCOL程序保存bios_dump.txt时流程大概是1. 通过 ImageHandle 找到当前程序是从哪个设备启动的 2. 打开这个设备上的 SimpleFileSystemProtocol 3. 打开文件系统根目录 4. 创建或打开 bios_dump.txt 5. 把输出写进去所以日志会保存到启动它的那个 U 盘上。Shell App 和 UEFI Shell 的关系名字里有 Shell App容易误会。实际上UEFI Shell是一个类似命令行的 EFI 程序。UEFI Shell App是可以在 UEFI Shell 里运行的应用程序。关系类似Windows cmd.exe - 可以运行 app.exe UEFI Shell.efi - 可以运行 BiosDumpApp.efi但.efi程序不一定必须通过 Shell 运行。如果它被放到\EFI\BOOT\BOOTX64.EFI固件也可以直接启动它。当前实现就是这种方式BIOS/UEFI 固件直接启动 BiosDumpAppEDKII 的作用EDKII 是开发 UEFI 程序的开源框架。它提供头文件库函数协议定义GUID 定义构建系统示例代码程序中使用了这些头文件和库#includeUefi.h#includeLibrary/UefiLib.h#includeLibrary/UefiBootServicesTableLib.h#includeLibrary/UefiRuntimeServicesTableLib.h这些让代码可以方便地访问gBS gRT gST Print() AllocatePool() ConvertDevicePathToText()INF 文件是什么BiosDumpApp.inf是模块描述文件。它告诉 EDKII这个模块叫什么是什么类型入口函数是什么有哪些源码依赖哪些库用到了哪些 GUID 或 Protocol例如MODULE_TYPE UEFI_APPLICATION ENTRY_POINT UefiMain意思是这是一个 UEFI 应用程序 入口函数是 UefiMainDSC 文件是什么UefiBiosDumpShellAppPkg.dsc是平台或工程描述文件。它告诉 EDKII要编译哪些模块每种 LibraryClass 使用哪个具体库实现目标架构是什么输出目录是什么简单说INF 描述单个程序 DSC 描述整个构建工程最终程序执行链最终执行链如下开机 - UEFI 固件初始化硬件 - 用户选择 U 盘 UEFI 启动 - 固件读取 FAT32 文件系统 - 固件寻找 \EFI\BOOT\BOOTX64.EFI - 固件加载 BOOTX64.EFI 到内存 - 固件调用 UefiMain() - 程序使用 gBS 枚举 protocol - 程序使用 gRT 枚举 variable - 程序解析 BootOrder / Boot#### - 程序把结果显示到屏幕 - 程序把结果写入 bios_dump.txt - 程序等待按键退出这就是 UEFI Shell App 的核心原理。