尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

EDK II FmpDxe 驱动设计变更演进全解析:Firmware Management Protocol 实现、LastAttemptStatus 错误码体系与平台迁移指南

EDK II FmpDxe 驱动设计变更演进全解析:Firmware Management Protocol 实现、LastAttemptStatus 错误码体系与平台迁移指南 固件操作系统驱动开发嵌入式【免费下载链接】edk2EDK II项目地址https://gitcode.com/gh_mirrors/ed/edk2点击查看免费下载本篇技术指南以 EDK II 仓库中 FmpDxe/ReadMe.md 的设计变更日志为主线系统梳理 FmpDxe——FmpDevicePkg 中产出EFI_FIRMWARE_MANAGEMENT_PROTOCOLFMP的核心 DXE 驱动——在签名认证、版本控制、变量锁定、错误码体系等关键领域的历史演进。读者阅读本文后将能理解胶囊Capsule固件更新链路中 FmpDxe 的完整职责掌握LastAttemptStatus细粒度错误码的由来与取值范围并据此评估既有 FmpDeviceLib 实例、平台变量服务在接入新版 FmpDxe 时所需的适配工作。FmpDxe 在 FmpDevicePkg 中的角色与职责在进入设计变更细节之前有必要先明确 FmpDxe 在整个固件管理架构中的位置。根据 FmpDevicePkg/Readme.md 的架构说明FmpDxe是 FmpDevicePkg 的中心驱动它产出EFI_FIRMWARE_MANAGEMENT_PROTOCOL并协调下述库类共同完成一次固件更新FmpAuthenticationLib校验胶囊签名EFI_FIRMWARE_IMAGE_AUTHENTICATION结构中的单调计数与WIN_CERTIFICATE_UEFI_GUID签名要求证书类型为EFI_CERT_TYPE_PKCS7_GUID提供重放保护与完整性认证FmpDeviceLib提供设备相关的固件读写、版本查询、属性获取等能力是设备特异性逻辑的注入点FmpPayloadHeaderLib解析胶囊 FMP Payload Header版本、最低支持版本等字段CapsuleUpdatePolicyLib提供平台策略电源/热状态是否允许更新、是否忽略最低支持版本检查等FmpDependencyLib / FmpDependencyCheckLib / FmpDependencyDeviceLib处理 UEFI Spec 2.8 引入的胶囊依赖表达式例如 FWx要求 FWy至少为 2.0 才能安装。从 FmpDxe.inf 的模块定义可以看出FmpDxe 是一个DXE_DRIVER入口函数为FmpDxeEntryPoint卸载函数为UninstallFmpInstance。其[Protocols]段同时PRODUCESgEfiFirmwareManagementProtocolGuid与gEdkiiFirmwareManagementProgressProtocolGuid并CONSUMESgEdkiiVariablePolicyProtocolGuid[Depex]段要求gEfiVariableWriteArchProtocolGuid AND gEdkiiVariablePolicyProtocolGuid同时可用驱动才启动。在 FmpDxe.c 的私有上下文模板中可以看到 FMP 协议实例的六个标准方法GetTheImageInfo、GetTheImage、SetTheImage、CheckTheImage、GetPackageInfo、SetPackageInfo。其中GetPackageInfo/SetPackageInfo在 FmpDxe.c 中直接返回EFI_UNSUPPORTED即该实例不支持包级Package固件管理。值得注意的启动约束还包括见 FmpDxe.c 的入口逻辑FILE_GUID 必须被平台覆盖FmpDxe.inf中默认的FILE_GUID 78EF0A56-1CF0-4535-B5DA-F6FD2F405A11仅是占位值平台必须为每个可更新固件镜像设置唯一的 ESRT GUID入口点会校验gEfiCallerIdGuid是否仍等于默认值若未覆盖则直接ASSERT (FALSE)并返回EFI_UNSUPPORTEDPcdFmpDeviceImageIdName 必须非空否则同样拒绝启动入口会调用DetectTestKey()检测PcdFmpDevicePkcs7CertBufferXdr中是否包含测试密钥并通过PcdTestKeyUsed向上汇报。设计变更日志读懂 FmpDxe 的演进脉络本文核心依据的 FmpDxe/ReadMe.md 是一份面向胶囊实现者capsule implementers与平台集成者platform integrators的设计变更日志Design Changes逐条记录了 FmpDxe 的重要设计调整日期Date、理由Description/Rationale、具体改动Changes、影响与缓解措施Impact/Mitigation。以下六条变更按时间倒序完整展开每条均结合仓库源码给出实现级佐证。变更一FMP Controller State 单次读取与纯字段提取器重构2026-07-20理由PopulateDescriptor()此前在单次调用中通过GetVersionFromVariable()、GetLowestSupportedVersionFromVariable()等多个 getter 多次读取FmpControllerStateNV 变量而每个 getter 又各自独立调用GetFmpControllerState()。这导致对 UEFI 变量的GetVariable()调用冗余且在变量尚不存在时会反复打印失败的读取调试信息。改动getter 函数被重命名例如GetVersionFromFmpControllerState()并简化成纯字段提取器——它们不再独立读取变量而是直接操作由调用方传入的FMP_CONTROLLER_STATE指针。PopulateDescriptor()现在只调用一次GetFmpControllerState()把结果传给各个 getter并在最后一次使用后FreePool释放。源码佐证在 VariableSupport.c 中GetFmpControllerState()是唯一执行GetVariable2()读取FmpStateVariableName的地方并校验返回尺寸是否等于sizeof (FMP_CONTROLLER_STATE)失败时返回 NULL。在 FmpDxe.c 的PopulateDescriptor()中可以看到重构后的调用模式第 318 行FmpControllerState GetFmpControllerState (Private);唯一一次变量读取第 328 行Private-Descriptor.Version GetVersionFromFmpControllerState (FmpControllerState);第 262 行GetLowestSupportedVersion()内部GetLowestSupportedVersionFromFmpControllerState (FmpControllerState)第 396-397 行GetLastAttemptVersionFromFmpControllerState()与GetLastAttemptStatusFromFmpControllerState()第 399-401 行使用完毕后FreePool (FmpControllerState)。影响与缓解这是仅局限于 FmpDxe 内部的实现调整。它不修改FmpDeviceLib、FmpDependencyLib、FmpDependencyCheckLib、FmpDependencyDeviceLib 或 CapsuleUpdatePolicyLib 的任何 API因此既有库实例不受影响无需任何迁移动作。变更二XDR 编码证书列表偏移计算修复2025-09-24理由FmpDxe 在处理 XDR 编码的证书列表时假设PublicKeyDataXdr缓冲区内容来自PcdFmpDevicePkcs7CertBufferXdr是 4 字节对齐的但实际情况并不总是如此。文档给出了具体反例若PublicKeyDataXdr 0x02某证书长度为0x05则下一个证书的正确偏移应为0x2 align_up (0x5, 4) 0xA而旧逻辑计算的是align_up (0x2 0x5, 4) 0x8两者相差 2 字节会导致后续证书解析错位。改动遍历 XDR 证书列表时的偏移计算被修正——对每个证书的长度独立向上对齐align up而不是对齐运行中可能已经不对齐的累计偏移。源码佐证在 FmpDxe.c 的证书遍历循环中可以看到当前修正后的实现从 PCD 取出PublicKeyDataXdr与结束指针PublicKeyDataXdrEnd后先用SwapBytes32 (*(UINT32 *)PublicKeyDataXdr)读取**大端big-endian**存储的长度字段然后指针前进sizeof (UINT32)指向证书数据尝试AuthenticateFmpImage()若失败第 943 行执行PublicKeyDataXdr ALIGN_VALUE (PublicKeyDataLength, 4);——即仅对长度做 4 字节对齐后推进运行中的基址不被再次对齐。同时代码对长度字段越界证书数据越界分别返回LAST_ATTEMPT_STATUS_DRIVER_ERROR_INVALID_KEY_LENGTH_VALUE与LAST_ATTEMPT_STATUS_DRIVER_ERROR_INVALID_KEY_LENGTH。影响与缓解仅影响PcdFmpDevicePkcs7CertBufferXdr中包含多个证书、且靠前证书长度不是 4 的倍数的平台——这些平台将看到修正后的证书解析行为此前可能错误地跳过或错读后续证书。变更三CheckTheImageInternal 强制 IMAGE_UPDATABLE_INVALID2022-01-20理由CheckTheImageInternal()此前在FmpDeviceCheckImageWithStatus()返回错误、但将ImageUpdatable留在IMAGE_UPDATABLE_VALID时不会强制把ImageUpdatable置为无效值。此外FmpDeviceCheckImageWithStatus()的LastAttemptStatus参数描述存在歧义可能被误读为该值仅在函数返回错误时才被检查。改动若FmpDeviceCheckImageWithStatus()返回错误CheckTheImageInternal()现在会强制将ImageUpdatable置为IMAGE_UPDATABLE_INVALID仅当镜像不可更新时才对返回的LastAttemptStatus做校验/转换从而保证一个可更新镜像的有效LAST_ATTEMPT_STATUS_SUCCESS不会被错误覆盖FmpDeviceCheckImageWithStatus()的 API 文档删除了LastAttemptStatus 仅在错误场景被检查的句子。源码佐证见 FmpDxe.c第 1066-1070 行if (EFI_ERROR (Status)) { if (*ImageUpdatable IMAGE_UPDATABLE_VALID) { *ImageUpdatable IMAGE_UPDATABLE_INVALID; } }第 1079-1095 行if (*ImageUpdatable ! IMAGE_UPDATABLE_VALID)时才校验LastAttemptStatus是否落在设备库区间[LAST_ATTEMPT_STATUS_DEVICE_LIBRARY_MIN_ERROR_CODE_VALUE, LAST_ATTEMPT_STATUS_DEVICE_LIBRARY_MAX_ERROR_CODE_VALUE]越界则归一化为LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL。影响与缓解对 FmpDeviceLib 实例提出明确要求——无论FmpDeviceCheckImageWithStatus()是否返回错误每次调用都应将LastAttemptStatus设为指定区间内的值或LAST_ATTEMPT_STATUS_SUCCESS而不能仅在出错时赋值。变更四从 Variable Lock 协议迁移到 Variable Policy 协议2021-10-26理由FmpDxe 此前使用已废弃的EDKII_VARIABLE_LOCK_PROTOCOLRequestToLock ()来锁定其 NV 变量。改动FmpDxe 改用EDKII_VARIABLE_POLICY_PROTOCOL与新增的VariablePolicyHelperLib依赖中的RegisterBasicVariablePolicy ()来锁定 NV 变量。模块 Depex 本应在同一时间从gEdkiiVariableLockProtocolGuid切换为gEdkiiVariablePolicyProtocolGuid但当时被遗漏直到 2024-07-09 的后续修复中才纠正。源码佐证当前 FmpDxe.inf 的[LibraryClasses]已包含VariablePolicyHelperLib[Protocols]段为gEdkiiVariablePolicyProtocolGuid ## CONSUMES[Depex]为gEfiVariableWriteArchProtocolGuid AND gEdkiiVariablePolicyProtocolGuid与修复后的状态一致。变量锁定流程在InstallFmpInstance()中触发当IsLockFmpDeviceAtLockEventGuidRequired ()为真时调用LockAllFmpVariables (Private)注册全部待锁定变量并创建EVT_NOTIFY_SIGNAL事件FmpDxeLockEventNotify见 FmpDxe.c在锁事件默认gEfiEndOfDxeEventGroupGuid可由PcdFmpDeviceLockEventGuid覆盖信号时调用FmpDeviceLock ()。影响与缓解平台必须产出gEdkiiVariablePolicyProtocolGuidFmpDxe 才能启动。仅提供已废弃的gEdkiiVariableLockProtocolGuid的平台需要相应升级其变量服务实现。变更五FmpDeviceCheckImage 增加 LastAttemptStatus 参数2020-06-15理由在FmpDeviceSetImage ()已加入细粒度LastAttemptStatus支持的基础上为FmpDeviceCheckImage ()也增加LastAttemptStatus参数。由于 SetImage 操作总会先执行一次镜像检查image check来自检查路径的更细粒度状态码能显著提升镜像应用失败时的错误隔离能力。改动允许 FmpDeviceLib 实现返回位于LAST_ATTEMPT_STATUS_LIBRARY_ERROR_MIN_ERROR_CODE至LAST_ATTEMPT_STATUS_LIBRARY_ERROR_MAX_ERROR_CODE区间内的 last attempt status 码FmpDxe 内部新增CheckTheImage ()的包装函数CheckTheImageInternal ()——该函数可在调用FmpDeviceCheckImage ()之前为驱动自身的错误返回一个 last attempt status 码这些驱动错误码位于LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL_VENDOR_RANGE_MIN至LAST_ATTEMPT_STATUS_DRIVER_ERROR_MAX_ERROR_CODE区间。源码佐证当前代码中CheckTheImageInternal ()FmpDxe.c是完整实现而公开的CheckTheImage ()FmpDxe.c只是一个薄包装return CheckTheImageInternal (This, ImageIndex, Image, ImageSize, ImageUpdatable, LastAttemptStatus);驱动自身错误码如LAST_ATTEMPT_STATUS_DRIVER_ERROR_INVALID_CERTIFICATE、LAST_ATTEMPT_STATUS_DRIVER_ERROR_IMAGE_AUTH_FAILURE、LAST_ATTEMPT_STATUS_DRIVER_ERROR_VERSION_TOO_LOW等正是在该内部函数中按检查阶段逐一赋值。影响与缓解这是一次破坏性 API 变更会导致所有 FmpDeviceLib 实例编译失败。每个 FmpDeviceLib 都应切换到新 API 定义并在适当时机返回唯一的LastAttemptStatus值。变更六SetImage 细粒度 LastAttemptStatus 错误码2019-10-07理由胶囊更新是各 OEM 高度关注的流程尤其当更新失败时希望获得更多失败信息。此前 FmpDxe 驱动的SetImage例程负责胶囊更新中绝大部分重活只能用少量预定义错误码填充LastAttemptStatus供 OS 恢复启动后消费/检查。因此提案升级SetImage例程利用 UEFI Spec 2.8 第 23.4 节新定义的LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL_VENDOR_RANGE使 OS 设备管理器中查看胶囊更新失败时错误码具有更好的粒度。改动从LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL_VENDOR_RANGE区间为 FmpDxe 驱动保留128 个错误码覆盖从thermal 与 power API 失败到胶囊载荷头检查失败的各类场景FmpDeviceSetImage函数的入参中新增一个LastAttemptStatus输出指针允许平台提供自己特有的错误码例如 SPI 写入失败、SVN 检查失败等。源码佐证SetTheImage ()FmpDxe.c中可以看到该错误码体系的全景使用CheckSystemPower/CheckSystemThermal/CheckSystemEnvironment三个策略检查分别失败时对应LAST_ATTEMPT_STATUS_DRIVER_ERROR_CHECK_POWER_API/LAST_ATTEMPT_STATUS_ERROR_PWR_EVT_BATT、LAST_ATTEMPT_STATUS_DRIVER_ERROR_CHECK_SYS_THERMAL_API/LAST_ATTEMPT_STATUS_DRIVER_ERROR_THERMAL、LAST_ATTEMPT_STATUS_DRIVER_ERROR_CHECK_SYS_ENV_API/LAST_ATTEMPT_STATUS_DRIVER_ERROR_SYSTEM_ENV第 1424-1432 行调用FmpDeviceSetImageWithStatus (...LastAttemptStatus)时将输出指针传给设备库若设备库返回错误还会校验其状态码是否落在设备库区间第 1440-1450 行。SetTheImage ()在整个流程结束时包括cleanup路径通过SetLastAttemptStatusInVariable (Private, LastAttemptStatus)将最终状态写入变量并在Progress (100)之后把DescriptorPopulated置为 FALSE促使下次GetTheImageInfo重新填充描述符第 1491-1514 行。影响与缓解斜体标注的 API 修改对所有 FmpDeviceLib 实例构成破坏性变更目的是让 OEM 更高效地解码胶囊更新失败。每个 FmpDeviceLib 都应切换到新 API 定义并在适用时填充正确的LastAttemptStatus值。LastAttemptStatus 错误码体系全景综合变更五与变更六FmpDxe 的LastAttemptStatus已经形成了一套多来源、分区间的错误码体系可从源码中的校验逻辑归纳如下错误码来源取值范围宏说明驱动内部错误LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL_VENDOR_RANGE_MIN~LAST_ATTEMPT_STATUS_DRIVER_ERROR_MAX_ERROR_CODEFmpDxe 在调用库函数前自行检测出的错误如证书无效、认证失败、镜像索引错误、版本过低、载荷头解析失败、电源/热/环境策略失败、设备已锁定等设备库错误LAST_ATTEMPT_STATUS_DEVICE_LIBRARY_MIN_ERROR_CODE_VALUE~LAST_ATTEMPT_STATUS_DEVICE_LIBRARY_MAX_ERROR_CODE_VALUEFmpDeviceLib 通过FmpDeviceCheckImageWithStatus()/FmpDeviceSetImageWithStatus()返回的平台特有错误如 SPI 写失败、SVN 检查失败依赖相关错误LAST_ATTEMPT_STATUS_FMP_DEPENDENCY_LIB_*/LAST_ATTEMPT_STATUS_FMP_DEPENDENCY_CHECK_LIB_*依赖表达式评估失败见CheckTheImageInternal()的CheckFmpDependency ()调用与LAST_ATTEMPT_STATUS_ERROR_UNSATISFIED_DEPENDENCIES成功状态LAST_ATTEMPT_STATUS_SUCCESS仅当镜像可更新且检查/设置全部通过时保留不可被错误码覆盖核心设计原则源自 FmpDxe.c 的注释一个 last attempt status 错误码在函数返回前总会覆盖成功值——*LastAttemptStatus LAST_ATTEMPT_STATUS_SUCCESS作为初始值写入随后任何错误路径都会用具体错误码覆盖它。FmpDxe 关键 PCD 配置速查FmpDxe 的全部配置 PCD 定义于 FmpDevicePkg.dec平台可在自己的.dsc中覆盖。核心项如下PCD默认值类型作用PcdFmpDeviceStorageAccessEnableTRUEBOOLEANFeature PCD为 FALSE 时GetTheImage/CheckTheImage/SetTheImage直接返回EFI_UNSUPPORTED即禁用存储访问PcdFmpDevicePkcs7CertBufferXdr{0x0}VOID*XDR 编码的 PKCS7 证书缓冲驱动逐个证书尝试AuthenticateFmpImage ()多证书时每个证书长度独立 4 字节对齐PcdFmpDeviceImageIdNameLVOID*镜像 ID 名称Unicode 字符串必须非空否则驱动拒绝启动PcdFmpDeviceBuildTimeLowestSupportedVersion0x0UINT32编译期最低支持版本参与三分量取最大另两个分量来自 FmpDeviceLib 与已应用胶囊保存的变量值见GetLowestSupportedVersion ()FmpDxe.cPcdFmpDeviceLockEventGuid{0}VOID*锁定固件设备的事件 GUID尺寸不等于EFI_GUID时回退到gEfiEndOfDxeEventGroupGuidFmpDxe.cPcdFmpDeviceProgressWatchdogTimeInSeconds0x0UINT8FMP 进度看门狗秒数填充进EDKII_FIRMWARE_MANAGEMENT_PROGRESS_PROTOCOLPcdFmpDeviceProgressColor0x00FFFFFFUINT32进度条前景色填充进 FMP Progress ProtocolPcdFmpDeviceImageTypeIdGuid{0}VOID*镜像类型 GUID 兜底值FmpDeviceLib 未提供时使用尺寸不对则回退gEfiCallerIdGuid见GetImageTypeIdGuid ()FmpDxe.cPcdFmpDeviceTestKeySha256Digest32 字节摘要VOID*测试密钥 SHA-256 摘要用于DetectTestKey ()检测生产固件误用测试密钥此外FmpDxe 使用一组 UEFI 变量持久化状态FmpVersion、FmpLsv、LastAttemptStatus、LastAttemptVersion、FmpState当FmpDeviceGetHardwareInstance ()返回非零硬件实例时变量名会追加 16 位十六进制后缀如FmpVersion1234567812345678变量名生成逻辑见 VariableSupport.c。其中FmpState即FMP_CONTROLLER_STATE变量的存储位置也是变更一重构后唯一被GetFmpControllerState ()读取的状态源。更新进度上报与流程控制SetTheImage ()对更新进度有明确的百分比约定见 FmpDxe.c 的FmpDxeProgress ()注释FmpDxe 自身1% - 5% 用于镜像校验检查电源、热、环境状态剥离头部等FmpDeviceLib6% - 98% 用于实际烧写/更新FmpDxeProgress ()会把设备库上报的 0% - 100% 线性映射到 6% - 98%公式((Completion * 92) / 100) 6FmpDxe 收尾99% - 100%Progress (99)表示烧写完成、开始保存变量Progress (100)在cleanup中最后调用。对平台集成者与胶囊实现者的迁移建议综合上述六条变更接入或升级 FmpDxe 时应重点核对变量服务平台必须产出gEdkiiVariablePolicyProtocolGuid变更四若仍只提供gEdkiiVariableLockProtocolGuid需升级变量服务。FmpDeviceLib API所有 FmpDeviceLib 实例必须适配带LastAttemptStatus输出指针的FmpDeviceCheckImageWithStatus ()/FmpDeviceSetImageWithStatus ()API变更五、变更六并遵守每次调用都返回指定区间内状态码的约定变更三。FILE_GUID 与 ImageIdName为每个可更新镜像覆盖FmpDxe.inf的FILE_GUIDESRT GUID 唯一并设置非空的PcdFmpDeviceImageIdName。多证书 PCD若PcdFmpDevicePkcs7CertBufferXdr包含多个证书请确认靠前证书长度非 4 的倍数时解析行为符合预期变更二。升级新版 FmpDxe 代码若平台基于旧版 FmpDxe 定制注意变更一属于纯内部重构、不涉及库 API可直接合并。最后建议平台集成者结合 FmpDevicePkg/Readme.md 中关于胶囊认证、版本控制、依赖评估与更新策略的整体架构说明以及 FmpDxe.c、VariableSupport.c 与 FmpDxe.inf 的当前实现对本平台的固件更新链路做一次端到端核对。赞分享固件操作系统驱动开发嵌入式【免费下载链接】edk2EDK II项目地址https://gitcode.com/gh_mirrors/ed/edk2点击查看免费下载相关推荐camera_web 演进全解析Flutter camera 插件 Web 端实现的能力演进、错误码体系与平台边界camera_web 演进全解析Flutter camera 插件 Web 端实现的能力演进、错误码体系与平台边界 本文以 camera_web 的 CHAN移动开发跨平台Cycle.js HTTP Driver 演进全解从 cycle/http 变更日志看响应式 HTTP 驱动的设计哲学与迁移指南Cycle.js HTTP Driver 演进全解从 cycle/http 变更日志看响应式 HTTP 驱动的设计哲学与迁移指南 cycle/http 是前端Web框架EDK II固件安全评估使用Firmware Analysis Toolkit进行审计EDK II固件安全评估使用Firmware Analysis Toolkit进行审计 引言固件安全的关键挑战 你是否曾面临UEFIUnified Ext固件操作系统驱动开发嵌入式上一篇革命性Web界面构建Node-RED UI Builder的3种高效开发模式下一篇naveNode.js虚拟环境终极解决方案3分钟快速上手教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表