BIOS开发笔记 19 – Windows Update 固件更新背后的 ESRT 机制

 ESRT 是 BIOS 向操作系统(OS)展示系统固件资源(System Firmware)和设备固件资源(Device Firmware)的一张清单。OS(如 Windows)通过读取这张表,知道当前系统中有哪些固件可以更新、当前版本是多少、上次更新的状态如何。

本文将结合 UEFI 源码(EDKII),深入剖析 ESRT 的结构、生成流程以及它是如何工作的。

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 , VersionAttributes 等)。

代码片段 (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:

1. EFI_ESRT_FMP_VARIABLE_NAME: 存储从 FMP 协议动态获取的条目。
2. EFI_ESRT_NONFMP_VARIABLE_NAME: 存储那些没有 FMP 协议,但通过私有协议手动注册的条目。

2.3 发布 (ReadyToBoot)

在 ReadyToBoot 事件触发时,OS 即将启动。此时 EsrtReadyToBootEventNotify 函数会被调用。

1. 它读取上述两个 Variable。
2. 分配一块连续内存(Pool),大小 = Header + 所有 Entries。
3. 填充 Header,拷贝 Entries。
4. 最关键的一步:gBS->InstallConfigurationTable
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) 分类下。

 
• System Firmware (系统固件):对应 FwType 为 ESRT_FW_TYPE_SYSTEMFIRMWARE 的 Entry。通常是 BIOS/UEFI 本身。
• Device Firmware (设备固件):对应 FwType 为 ESRT_FW_TYPE_DEVICEFIRMWARE 的 Entry。例如 PD Controller, Touch Panel 等。

 

 

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

THE END
分享
二维码
海报
BIOS开发笔记 19 – Windows Update 固件更新背后的 ESRT 机制
 ESRT 是 BIOS 向操作系统(OS)展示系统固件资源(System Firmware)和设备固件资源(Device Firmware)的一张清单。OS(如 Windows)通过读取这张表,知道当……
<<上一篇
下一篇>>
文章目录
关闭
目 录