BIOS开发笔记 19 – Windows Update 固件更新背后的 ESRT 机制
ESRT 是 BIOS 向操作系统(OS)展示系统固件资源(System Firmware)和设备固件资源(Device Firmware)的一张清单。OS(如 Windows)通过读取这张表,知道当前系统中有哪些固件可以更新、当前版本是多少、上次更新的状态如何。
1. ESRT 的核心数据结构
ESRT 的定义位于 MdePkg/Include/Guid/SystemResourceTable.h。它主要由两部分组成:表头 (EFI_SYSTEM_RESOURCE_TABLE) 和 表项 (EFI_SYSTEM_RESOURCE_ENTRY)。
1.1 表头结构
表头主要充当索引,告诉 OS 表里有多少个固件资源。
typedef struct { UINT32 FwResourceCount; // 表中固件资源的数量 UINT32 FwResourceCountMax; // 表可以容纳的最大资源数量(用于重新分配内存判断) UINT64 FwResourceVersion; // 结构体版本,通常为 1 // EFI_SYSTEM_RESOURCE_ENTRY Entries[]; // 紧跟着的是 Entry 数组 } EFI_SYSTEM_RESOURCE_TABLE;
1.2 表项结构
每一个 Entry 对应一个可更新的固件实体(Firmware Resource)。
typedef struct { /// 唯一标识固件的 GUID。Windows Update 使用此 GUID 来匹配驱动程序(INF 文件)。 EFI_GUID FwClass; /// 固件类型: /// - ESRT_FW_TYPE_SYSTEMFIRMWARE (0x1): 系统固件(如 BIOS) /// - ESRT_FW_TYPE_DEVICEFIRMWARE (0x2): 设备固件(如 PD controller, Sensor hub) UINT32 FwType; /// 当前固件版本。必须随更新递增。 UINT32 FwVersion; /// 最低支持的回滚版本。出于安全考虑(防回滚攻击),低于此版本的更新将被拒绝。 UINT32 LowestSupportedFwVersion; /// Capsule 更新标志 (Flags),对应 EFI_CAPSULE_HEADER 中的定义。 UINT32 CapsuleFlags; /// 上次尝试更新的版本。即使更新失败,这个字段也会记录尝试过的版本。 UINT32 LastAttemptVersion; /// 上次更新的状态。非常重要,用于 OS 判断更新是否成功。 /// 0x0: Success, 0x1: Unsuccessful, 0x2: Insufficient Resources, etc. UINT32 LastAttemptStatus; } EFI_SYSTEM_RESOURCE_ENTRY;
2. ESRT 的生成流程
在 EDKII MdeModulePkg/Universal/EsrtDxe 驱动中,ESRT 并不是静态定义的,而是动态生成的。它的核心逻辑是:收集各个 FMP (Firmware Management Protocol) 的信息,汇总后发布给 OS。
我们可以将流程分为三个阶段:收集 (Sync) -> 缓存 (Storage) -> 发布 (Publish)。
2.1 收集与同步 (EsrtDxeSyncFmp)
驱动入口 EsrtDxeEntryPoint 会注册 EsrtDxeSyncFmp。这个函数负责遍历系统中所有安装了 EFI_FIRMWARE_MANAGEMENT_PROTOCOL 的 Handle。
对于通过 FMP 暴露的固件,EsrtDxe 会调用 Fmp->GetImageInfo() 来获取固件的详细信息(如 ImageTypeId , Version, Attributes 等)。
代码片段 (MdeModulePkg/Universal/EsrtDxe/EsrtImpl.c):
VOID SetEsrtEntryFromFmpInfo ( IN OUT EFI_SYSTEM_RESOURCE_ENTRY *EsrtEntry, IN EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfo, IN UINT32 DescriptorVersion ) { // 映射 FMP 数据到 ESRT Entry EsrtEntry->FwVersion = FmpImageInfo->Version; EsrtEntry->FwClass = FmpImageInfo->ImageTypeId; // 关键:这是 Windows 识别 ID // 判断是系统固件还是设备固件 if (IsSystemFmp (FmpImageInfo)) { EsrtEntry->FwType = ESRT_FW_TYPE_SYSTEMFIRMWARE; } else { EsrtEntry->FwType = ESRT_FW_TYPE_DEVICEFIRMWARE; } // 获取最后一次尝试的状态,这对于 Debug 更新失败非常有用 if (DescriptorVersion >= 3) { EsrtEntry->LastAttemptVersion = FmpImageInfo->LastAttemptVersion; EsrtEntry->LastAttemptStatus = FmpImageInfo->LastAttemptStatus; } }
2.2 缓存 (NVRAM Storage)
为了保证信息在重启后不丢失(特别是 LastAttemptStatus),EsrtDxe 将收集到的信息缓存在 NVRAM Variable 中。
它实际上维护了两个 Variable:
2.3 发布 (ReadyToBoot)
在 ReadyToBoot 事件触发时,OS 即将启动。此时 EsrtReadyToBootEventNotify 函数会被调用。
Status = gBS->InstallConfigurationTable (&gEfiSystemResourceTableGuid, EsrtTable);
这行代码将 ESRT 表的物理地址注册到了 EFI System Table 中。操作系统启动后,OS Loader 会扫描 System Config Tables,找到 gEfiSystemResourceTableGuid,从而获取到这张表。
3. Windows 设备管理器与 ESRT
当 Windows 读取到 ESRT 表后,PnP (Plug and Play) 管理器会为每一个 ESRT Entry 创建一个对应的设备节点。
这些设备通常会显示在 设备管理器 (Device Manager) -> 固件 (Firmware) 分类下。

版权声明:
作者:bin
链接:https://ay123.net/mystudy/bios/1915/
来源:爱影博客
文章版权归作者所有,未经允许请勿转载。
共有 0 条评论