尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
UEFI基础服务——记录
学习戴正华大佬的《UEFI原理与编程》第5章记录如下。UEFI 的核心任务是为 OS 准备好软硬件运行环境。它向上暴露的接口分两种启动服务Boot Services和运行时服务Runtime Services统称为 UEFI 服务。任何 UEFI 应用或驱动都是通过系统表拿到这两个服务表的指针。5.1 系统表5.1.1 概述系统表是整个 UEFI 编程的入口。它本质上是一个全局结构体在 DXE 阶段被初始化也就是说 SEC、PEI 阶段用不了只有到了 DXE 及以后才能访问。每个 .efi 文件Image被加载到内存后UEFI 固件会调用它的入口函数并把系统表指针传进去。入口函数的签名typedef EFI_STATUS(EFIAPI *EFI_IMAGE_ENTRY_POINT) ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable );Image 和 ImageHandle 的区别Image 是加载到内存后的可执行程序实体一个数据结构ImageHandle 是这个 Image 的句柄——可以理解为 Image 的身份证用来唯一标识这个 Image。驱动/应用之间的交互比如 Protocol 的安装和查找都是通过 Handle 来关联的。具体调用链.efi 文件加载后形成 Image 对象_ModuleEntryPoint的地址被填到Image-EntryPoint然后固件执行Image-EntryPoint(ImageHandle, SystemTable)最终进入 .inf 里指定的模块入口函数。5.1.2 系统表的结构typedef struct { EFI_TABLE_HEADER Hdr; CHAR16 *FirmwareVendor; // 固件厂商名 UINT32 FirmwareRevision; // 固件版本 EFI_HANDLE ConsoleInHandle; EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn; // 键盘输入 EFI_HANDLE ConsoleOutHandle; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut; // 屏幕输出 EFI_HANDLE StandardErrorHandle; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *StdErr; // 错误输出也是屏幕 EFI_RUNTIME_SERVICES *RuntimeServices; // 运行时服务表 EFI_BOOT_SERVICES *BootServices; // 启动服务表 UINTN NumberOfTableEntries; EFI_CONFIGURATION_TABLE *ConfigurationTable; // 配置表数组 } EFI_SYSTEM_TABLE;几点理解1表头 EFI_TABLE_HEADERUEFI 里所有表都用EFI_TABLE_HEADER开头启动服务表、运行时服务表都是。这个 Header 里有 64 位 Signature跟 GUID 类似的思路用于标识表的类型、版本号、整表大小、CRC32 校验值。CRC32 是这样算的先把 CRC32 字段清零算整表的 CRC再填回去。如果表被篡改或者损坏校验就对不上。2控制台系统表提供了 3 套控制台ConIn键盘、ConOut屏幕、StdErr屏幕。每套包含一个设备句柄和一个对应的 Protocol。有了这些UEFI 应用不用关心底层硬件细节直接调 Protocol 的函数就行。3配置表ConfigurationTable是一个数组每一项是一对GUID 数据指针。ACPI 表、SMBIOS 表、设备树等等都挂在这上面。OS Loader 通过它获取硬件信息交给内核这个设计很巧妙——用 GUID 做索引随意扩展不用改系统表结构。5.2 启动服务启动服务表在 DXE 阶段初始化通过gST-BootServices或gBS访问。它提供的服务覆盖了 UEFI 应用在启动阶段ExitBootServices 之前几乎所有需求共 8 类。注虽然也叫表但它不像系统表那样存数据——启动服务表里基本都是函数指针。5.2.1 八类服务概览类别说明典型使用场景事件服务事件、定时器、任务优先级TPL创建回调事件、设置定时器内存管理分配/释放内存页或池驱动分配缓冲区Protocol 管理安装/卸载 Protocol、注册通知驱动提供 ProtocolProtocol 使用查找/打开/关闭 Protocol应用调用驱动服务驱动管理Connect/Disconnect 控制器加载驱动到设备Image 服务加载/卸载/启动 .efi 文件OS Loader 加载内核ExitBootServices结束启动服务移交控制权OS Loader 接管系统其他CopyMem、SetMem、CRC32、Stall 等通用工具操作启动服务表的结构体非常大100 行但理清分类后其实很好记。额外提几点TPL任务优先级RaiseTPL/RestoreTPL 用来在关键区域提升优先级防止其他事件打断。跟中断屏蔽的思路类似但用的是软件优先级。事件UEFI 的事件模型支持多种类型通知事件、定时器事件等。CreateEvent 的时候可以指定回调函数和触发条件核心是异步通知机制。Protocol 函数的历史包袱InstallProtocolInterface和ReinstallProtocolInterface是早期版本遗留下来的新代码应该用InstallMultipleProtocolInterfaces它一次可以安装多个 Protocol而且会在失败时自动回滚。5.2.2 内存管理服务详解内存管理服务分三组1AllocatePool / FreePool —— 按字节分配最常用的内存分配方式类似 C 的 malloc/free。必须成对调用。CHAR16 *Buffer NULL; gBS-AllocatePool(EfiBootServicesCode, 1024, (VOID**)Buffer); gBS-FreePool(Buffer);内存类型是关键参数。AllocatePool第一个参数指定内存类型这决定了内存在 ExitBootServices 之后的命运EfiBootServicesCode/EfiBootServicesDataExitBootServices 后固件回收。用于 UEFI 驱动和应用自身。EfiLoaderCode/EfiLoaderData由 OS Loader 和 OS 决定是否回收。用于存放交给内核的数据比如内存映射表。EfiACPIReclaimMemoryACPI 表初始化完成后回收。EfiRuntimeServicesCode/EfiRuntimeServicesData等ExitBootServices 后保留给运行时服务继续用。理解这个很重要——如果你在 Boot Service 里分配了一块EfiBootServicesData内存想在 RT 阶段继续用那就会挂掉因为这块内存已经被回收了。2AllocatePages / FreePages —— 按物理页分配当你需要整页内存或者不想分配跨页时用这个。驱动开发中很常见因为有些硬件 DMA 要求物理连续而且 DMA 缓冲区通常以页为单位。EFI_PHYSICAL_ADDRESS pages 0; // 0 表示让固件自己选地址 gBS-AllocatePages(AllocateAnyPages, EfiBootServicesCode, 3, pages); // 拿到的是物理地址操作内存前要转成指针 CHAR16 *Str (CHAR16*)pages; gBS-FreePages(pages, 3);重要AllocatePages返回的是物理地址EFI_PHYSICAL_ADDRESS不是指针。在 32 位 UEFI 上如果物理内存超过 4GBAllocateAnyPages可能分配到 4G 以上的物理地址32 位指针够不着——这时候要用AllocateMaxAddress限制允许分配的最大物理地址。Type参数的三种分配方式AllocateAnyPages任意地址AllocateMaxAddress分配的页面物理地址不能超过传入值AllocateAddress强制分配到指定物理地址3GetMemoryMap —— 获取系统内存布局这是 ExitBootServices 之前必须调用的服务。它返回整个系统的内存映射表每个条目EFI_MEMORY_DESCRIPTOR描述了一块内存的类型、物理地址、虚拟地址、页数和属性。调用有个经典的两步走模式先传个 0 大小的 Buffer 让固件告诉你需要多大分配好再调一次。MapKey是当前内存映射的版本标识ExitBootServices 时要传这个 Key防止内存映射在你获取之后被修改如果 Key 变了说明驱动对内存做了操作就得重新获取。5.2.3 启动服务的生命周期总结gBS从 DXE 初始化开始可用到gBS-ExitBootServices()成功后结束。ExitBootServices 执行后被回收的启动服务表中除EFI_TABLE_HEADER外的所有函数指针都失效ConIn、ConOut、StdErr相关的控制台设备/Protocol 失效EfiBootServicesCode和EfiBootServicesData类型的内存被回收保留的gRT—— 运行时服务继续可用ConfigurationTable—— 配置表保留ACPI、SMBIOS 等 HW 信息FirmwareVendor和FirmwareRevision字符串调试技巧在 ExitBootServices 前后分别看gST的地址和其成员地址可以确认哪些区域被释放了。书中提到可以用asm int 3打调试断点来观察。MapKey的坑如果第一次 GetMemoryMap 拿到 MapKey 后在调用 ExitBootServices 之前又有代码调了分配/释放内存MapKey 就对不上了ExitBootServices 会失败。正确的做法是检测到失败后重新 GetMemoryMap 再调。5.3 运行时服务运行时服务同样在 DXE 阶段初始化但它的生命周期比启动服务长得多——一直到操作系统运行期间都存活给 OS 提供底层服务。gRT提供了 4 类服务类别说明时间服务读写硬件 RTC、唤醒定时器虚拟内存SetVirtualAddressMap、ConvertPointer变量服务读写 UEFI 变量Get/Set Variable其他ResetSystem重启、Capsule 更新、查询变量信息5.3.1 时间服务GetTime / SetTime直接操作 RTC 硬件。时间结构体包含年月日时分秒纳秒和时区。注意几个细节EFI_TIME有两个 Padding 字节Pad1、Pad2这是为了字节对齐TimeZone-1440 到 1440单位分钟UTC 时区是 0。还有一个特殊值 2047 表示时区未知Daylight夏令时标志Nanosecond字段精度看起来很高但实际受限于 RTC 硬件精度EFI_TIME_CAPABILITIES描述时钟硬件能力Resolution时钟每秒脉冲数Accuracy精度单位 ppm百万分之一SetsToZero是否在设置时间时清零亚秒计数器GetWakeupTime / SetWakeupTime操作系统用这个实现定时开机——比如笔记本在 AC 状态下设置唤醒时间关机后 RTC 到时触发上电。5.3.2 变量服务UEFI 变量是运行在 Flash 上的键值存储用 (GUID Name) 作为唯一标识。OS 装好后的启动项、Secure Boot 密钥、固件设置都存成变量。核心三个接口GetVariable读取变量SetVariable写入变量可以附加属性如 Non-Volatile、BootService、Runtime、Authenticated Write 等GetNextVariableName遍历所有变量5.3.3 虚拟内存服务这两个接口只能在 ExitBootServices 之后调用。由于 OS 启用分页后物理地址就不能直接用了所以 RT 阶段要先把运行时服务使用的物理地址映射到虚拟地址。调用流程SetVirtualAddressMap传入内存映射表物理地址→虚拟地址的映射固件内部遍历并更新。然后ConvertPointer用于将某个指针的指向从物理地址改为虚拟地址——注意它转换的是指针的指针。小结UEFI 的这套服务设计其实很清晰系统表 导航中心从这里拿到一切启动服务 DXE 到 OS Loader 期间的工具箱内存管理 Protocol 操作 Image 加载功能最强运行时服务 OS 运行后还能用的精简接口时间 变量 复位理解生命周期这个概念特别重要什么内存在什么时候分配、什么时候回收什么服务在什么时候可用。
RELATED

相关推荐

Android渗透测试环境搭建:Kali Linux与MSFvenom实战指南

Android渗透测试环境搭建:Kali Linux与MSFvenom实战指南

1. 项目概述:为什么需要搭建Android渗透测试环境?如果你对移动安全感兴趣,或者想验证自己开发的Android应用是否存在安全隐患,那么亲手搭建一个本地的渗透测试环境是必经之路。这就像木匠需要自己的工具箱,厨师需要自己…

📅 2026/9/12 2:49:53
Windows C盘深度清理工具:安全释放磁盘空间与系统优化指南

Windows C盘深度清理工具:安全释放磁盘空间与系统优化指南

如果你还在为 C 盘爆红、系统卡顿而烦恼,每次手动清理又担心误删系统文件,那么今天介绍的这个工具可能会彻底改变你的电脑维护方式。这不是又一个普通的清理软件,而是一个专门针对 Windows 系统深层垃圾设计的“外科手术式”清理方案。很多人…

📅 2026/8/20 20:43:41
抖音批量下载神器:5步轻松构建你的个人素材库

抖音批量下载神器:5步轻松构建你的个人素材库

抖音批量下载神器:5步轻松构建你的个人素材库 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback support. 抖音…

📅 2026/8/20 20:43:41
MORE NEWS

更多资讯

📰

构建水果质量检测器:IoT-For-Beginners 制造项目实战——边缘 AI 分类、多设备协同与云端服务整合

构建水果质量检测器:IoT-For-Beginners 制造项目实战——边缘 AI 分类、多设备协同与云端服务整合 【免费下载链接】IoT-For-Beginners 12 Weeks, 24 Lessons, IoT for All! 项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners 本文围绕 I…

📰

amis 辅助类字体斜体样式:italic 与 not-italic 工具类使用与实现解析

amis 辅助类字体斜体样式:italic 与 not-italic 工具类使用与实现解析 【免费下载链接】amis 前端低代码框架,通过 JSON 配置就能生成各种页面。 项目地址: https://gitcode.com/GitHub_Trending/am/amis 本篇指南围绕 amis 低代码框架&#xff0…

📰

Sa-Token 路由拦截鉴权实战:基于 SaInterceptor 与 SaRouter 实现全局登录校验与模块化权限控制

Sa-Token 路由拦截鉴权实战:基于 SaInterceptor 与 SaRouter 实现全局登录校验与模块化权限控制 【免费下载链接】Sa-Token ✨ 开源、免费、一站式 Java 权限认证框架,让鉴权变得简单、优雅!—— 登录认证、权限认证、分布式 Session 会话、微…

📰

多无人机协同路径规划与防撞算法实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

Qwen Code /review 技能架构设计:14 个并行 Agent、分片验证与迭代反向审计如何支撑一次代码评审

Qwen Code /review 技能架构设计:14 个并行 Agent、分片验证与迭代反向审计如何支撑一次代码评审 【免费下载链接】qwen-code An open-source AI coding agent that lives in your terminal. 项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code Qw…

📰

C++代码重构实战:提升代码质量的关键技术与方法

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬