当前位置: 首页 > news >正文

EDK2源码架构:DXE Dispatcher驱动加载顺序与Protocol Database

搜"EDK2 DXE"的人很多,但大部分文章只讲概念。本文从Dispatcher.c源码出发,把五级优先级、Depex后缀表达式评估、Protocol三层结构、Notify订阅机制全部拆清楚,配实战踩坑案例。

一、DXE Core烧的"三把火"

公司换了个新老板,第一件事不是开会,是搞清楚三件事:手头有哪些资源、怎么分配任务、定一套规矩。DXE Core也是这个逻辑。

PEI把"家底"(HOB List)交过来之后,DXE Core烧三把火:建协议数据库(让所有驱动能互相找到)、调度驱动加载(决定谁先跑)、管理硬件资源(内存、IO、中断、DMA)。

PEI和DXE最大的区别:PEI是"临时工"——跑完就扔,模块卸载了内存回收。DXE是"正式员工"——驱动一直驻留在内存直到OS启动,驱动之间通过Protocol永久互连。


二、DXE Dispatcher:和PEI Dispatcher的根本不同

调度主循环

// MdeModulePkg/Core/Dxe/Dispatcher/Dispatcher.cVOIDCoreDispatcher(IN EFI_STATUS*ReturnStatus){// 第一步:找到Busy状态的FV,注册所有DXE驱动到调度队列DiscoverFvAndRegisterDrivers();// 第二步:进入调度循环——和PEI不同,这是多轮循环do{Status=CoreDispatchOneDriver();}while(Status!=EFI_NOT_FOUND);// 第三步:通知Dispatcher Architecture Protocol的注册者Dispatcher=CoreLocateProtocol(&gEfiDispatcherArchProtocolGuid);if(Dispatcher!=NULL){Dispatcher->DispatcherLoaded(Dispatcher);}}

PEI vs DXE Dispatcher对比

方面PEI DispatcherDXE Dispatcher
调度对象PEIM(每个只跑一次)DXE Driver(驻留内存)
依赖表达PPI GUID布尔表达式Protocol GUID布尔表达式 + 优先级
调度策略纯Depex驱动Depex + Driver Binding + 优先级排序
FV发现静态(BFV + PPI通知)动态(FV HOB + DXE Driver主动通知)
执行后PEIM卸载驱动Stay Resident,安装Protocol

三、五级优先级体系

DXE Dispatcher不是"谁的Depex先满足谁先跑"这么简单。它有五级优先级:

// MdePkg/Include/Pi/PiDxeCis.h#defineDXE_DRIVER_PRIORITY_PLATFORM_PROCESSOR0x03000000// 最早#defineDXE_DRIVER_PRIORITY_PLATFORM_CHIPSET0x03010000#defineDXE_DRIVER_PRIORITY_BUS0x03020000#defineDXE_DRIVER_PRIORITY_DEVICE0x03030000#defineDXE_DRIVER_PRIORITY_PLATFORM_APPLICATION0x03040000// 最晚

优先级写在每个DXE驱动的PE/COFF头或FV文件扩展头里。Dispatcher先扫一遍所有注册的驱动,按优先级分组,每组内再按Depex评估。

这个设计解决了PEI阶段一个头疼的问题:PEI里只能靠Apriori强制排序,DXE里不需要了——优先级表达得更自然。


四、Depex评估:后缀表达式

DXE驱动的Depex比PEI的复杂,支持Protocol GUID和SOR(Scheduled Order Rule):

// USB键盘驱动的Depex示例 AND gEfiUsbIoProtocolGuid // 必须有USB IO Protocol gEfiSimpleTextInputProtocolGuid // 且已有至少一个输入设备 END

Depex是后缀表达式(逆波兰表示法),评估器维护一个操作数栈:

// MdeModulePkg/Core/Dxe/Dispatcher/Dependency.cBOOLEANCoreEvaluateDependency(IN EFI_CORE_DRIVER_ENTRY*DriverEntry){// EFI_DEP_PUSH_GUID → 查Protocol是否存在,压栈// EFI_DEP_AND → 弹两个操作数,逻辑与// EFI_DEP_OR → 弹两个操作数,逻辑或// EFI_DEP_NOT → 弹一个操作数,逻辑非// EFI_DEP_TRUE/FALSE → 压常量// EFI_DEP_END → 评估结束,栈顶就是结果}

五、Protocol Database:DXE的"黄页"

三层结构

如果你用过COM、D-Bus或Android的Binder IPC,看到DXE Protocol Database会觉得似曾相识。三层结构:

Handle Database (全局链表) └── IHANDLE (句柄) ├── 唯一Handle ID └── PROTOCOL_INTERFACE 链表 ├── Protocol GUID ├── Protocol Interface 指针 └── 注册这个Protocol的Agent Handle
// MdeModulePkg/Core/Dxe/Hand/Handle.htypedefstruct{UINTN Signature;// "hList"LIST_ENTRY AllHandles;// 连到全局Handle ListLIST_ENTRY Protocols;// 这个Handle上装的所有ProtocolUINTN LocateRequest;UINT64 Key;// 唯一ID}IHANDLE;typedefstruct{UINTN Signature;// "pI/F"LIST_ENTRY Link;// 连到IHANDLE.ProtocolsLIST_ENTRY ByProtocol;// 连到PROTOCOL_ENTRY.AllEntriesIHANDLE*Handle;PROTOCOL_ENTRY*Protocol;VOID*Interface;// Protocol具体数据指针PROTOCOL_NOTIFY*Notify;}PROTOCOL_INTERFACE;typedefstruct{UINTN Signature;// "PEnT"LIST_ENTRY AllEntries;// 连到全局Protocol Entry ListEFI_GUID ProtocolID;// GUIDLIST_ENTRY Protocols;// 所有装了这个Protocol的PROTOCOL_INTERFACELIST_ENTRY Notify;// RegisterProtocolNotify的注册者}PROTOCOL_ENTRY;

双向索引:从Handle能找到它装了什么Protocol;从Protocol GUID能找到所有装了它的Handle。


六、InstallProtocolInterface:驱动"挂牌营业"

安装流程

每个DXE驱动跑起来后,第一件事是安装自己提供的Protocol。这是链式调用:

InstallProtocolInterface → CoreInstallProtocolInterfaceNotify → InsertTailList // 挂到IHANDLE → InsertTailList // 挂到PROTOCOL_ENTRY → CoreNotifyProtocolEntry // 通知所有等待者
// MdeModulePkg/Core/Dxe/Hand/Handle.cEFI_STATUS EFIAPICoreInstallProtocolInterface(IN OUT EFI_HANDLE*UserHandle,IN EFI_GUID*Protocol,IN EFI_INTERFACE_TYPE InterfaceType,IN VOID*Interface){returnCoreInstallProtocolInterfaceNotify(UserHandle,Protocol,InterfaceType,Interface,TRUE// Notify=TRUE,安装后立刻通知等待者);}

InstallMultipleProtocolInterfaces

日常开发没人直接调InstallProtocolInterface——太啰嗦。用InstallMultipleProtocolInterfaces一次装好几个:

// 典型调用:一个Handle上装两个ProtocolEFI_HANDLE HostBridgeHandle=NULL;Status=gBS->InstallMultipleProtocolInterfaces(&HostBridgeHandle,&gEfiDevicePathProtocolGuid,&DevicePath,&gEfiPciHostBridgeResourceAllocationProtocolGuid,&ResAlloc,NULL// 参数列表结束);

ReinstallProtocolInterface

需要更新已安装的Protocol时不能直接改Interface指针(在跑的驱动可能正在用),必须走Reinstall——三步走:通知断开 → 换指针 → 通知重连。


七、LocateProtocol:驱动找服务

安装是"挂牌",查找是"翻黄页":

// MdeModulePkg/Core/Dxe/Hand/Locate.cEFI_STATUSCoreLocateProtocol(IN EFI_GUID*Protocol,IN VOID*Registration,OUT VOID**Interface){ProtEntry=CoreFindProtocolEntry(Protocol,FALSE);// Protocol可能装了不止一份,返回第一个找到的InterfaceProt=CR(ProtEntry->Protocols.ForwardLink,PROTOCOL_INTERFACE,ByProtocol,PROTOCOL_INTERFACE_SIGNATURE);*Interface=Prot->Interface;returnEFI_SUCCESS;}

LocateHandleBuffer用于遍历所有装了某个Protocol的Handle(比如枚举所有PCI设备)。


八、Notify机制:等Protocol的"订阅-发布"

有的驱动依赖的Protocol可能还没安装——比如USB键盘驱动需要EFI_USB_IO_PROTOCOL,但USB控制器驱动还在后面排着队。RegisterProtocolNotify派上用场:

// MdeModulePkg/Core/Dxe/Hand/Notify.cEFI_STATUSCoreRegisterProtocolNotify(IN EFI_GUID*Protocol,IN EFI_EVENT Event,OUT VOID**Registration){ProtEntry=CoreFindProtocolEntry(Protocol,TRUE);// 分配PROTOCOL_NOTIFY,挂在ProtEntry的Notify链表ProtNotify=AllocatePool(sizeof(PROTOCOL_NOTIFY));ProtNotify->Event=Event;// Protocol安装时Signal这个EventInsertTailList(&ProtEntry->Notify,&ProtNotify->Link);// 防竞态:万一本Protocol在Register之前刚好装了if(!IsListEmpty(&ProtEntry->Protocols)){CoreSignalEvent(Event);// 已经有了,直接通知}returnEFI_SUCCESS;}

当任何驱动调用InstallProtocolInterface时,CoreNotifyProtocolEntry遍历Notify链表,Signal每个Event。被卡住的驱动从WaitForEvent返回,用LocateProtocol获取刚装好的Interface。


九、实战踩坑:Dispatcher死循环

某次发布前紧急修了一个PCIe驱动bug,重新编译FV。烧进去系统卡在DXE Dispatcher死循环——日志疯狂刷"Dispatch 0xXXXX Status SUCCESS",但就是不走下一个驱动。

根因

修复后的PCIe驱动Depex是AND gEfiPciRootBridgeIoProtocolGuid gEfiPciPlatformProtocolGuid END。每次调度执行后,它在Entry里调用了InstallProtocolInterfacegEfiPciPlatformProtocolGuid——但这个Protocol之前已经装过了!CoreInstallProtocolInterface返回EFI_ACCESS_DENIED(不允许在一个Handle上装同名Protocol两次)。

驱动作者没检查返回值。Dispatcher继续扫描,发现下一个驱动的Depex需要这个Protocol的新版本,但装不上去。这个驱动被放回等待队列,PCIe驱动又被重新调度——形成调度-失败-重调度死循环。

修复

InstallProtocolInterface之后加ASSERT_EFI_ERROR(Status)。如果有EFI_ACCESS_DENIED,说明设计有问题——一个Protocol不应该被同一个驱动重复安装。

教训:DXE驱动的Entry Point必须检查InstallProtocolInterface的返回值。PEI阶段也许可以懒一点,DXE阶段不行——Dispatcher会把失败吞掉然后重试,让你以为一切正常。


十、实战踩坑总结

现象根因经验
Dispatcher死循环日志疯狂刷Dispatch SUCCESS重复安装同名Protocol返回ACCESS_DENIED没检查Entry里ASSERT_EFI_ERROR
Depex评估误解驱动提前执行导致崩溃以为AND是短路评估实际是全量评估后逻辑与
Notify竞态驱动等不到Protocol通知Register前Protocol刚装好RegisterProtocolNotify内部有二次检查
Reinstall没通知使用者拿到旧Interface直接改指针没走Reinstall必须三步:通知断开→换指针→通知重连
优先级设错总线驱动比平台驱动先跑优先级宏填反Platform Processor最早,Application最晚

源码路径索引

内容关键文件
DXE Dispatcher主循环MdeModulePkg/Core/Dxe/Dispatcher/Dispatcher.c
Depex评估MdeModulePkg/Core/Dxe/Dispatcher/Dependency.c
驱动注册与FV发现MdeModulePkg/Core/Dxe/Dispatcher/Driver.c
Protocol安装/重安装MdeModulePkg/Core/Dxe/Hand/Handle.c
Protocol查找MdeModulePkg/Core/Dxe/Hand/Locate.c
Notify机制MdeModulePkg/Core/Dxe/Hand/Notify.c
DXE服务表MdeModulePkg/Core/Dxe/DxeMain/DxeMain.c
PI规范PI Specification Volume 1, Chapter 7-10
http://www.jsqmd.com/news/1338597/

相关文章:

  • 44-应用当前版本切换的治理意义:为什么“当前基线”必须被显式维护
  • 深圳配眼镜青少年防控不能等从验光到镜片给孩子完整视力方案 - 配眼镜新资讯
  • HTML5标准文档构建实战:从零搭建语义化网页骨架
  • Maven私有仓库高效配置与CI/CD集成实战指南
  • Spring Boot中404错误的深度解析与解决方案
  • OpenCode Skills:用结构化文档教会AI执行编程任务
  • 景区客流遇冷?用EasyDSS企业融媒体平台直播/点播解锁「云上文旅」新玩法
  • Unity VR移动开发:基于CharacterController的摇杆移动与动态高度适配方案
  • Unity Addressable资源管理系统:从核心概念到热更新实战
  • 深度解析天津市建设网站全流程及未来发展趋势展望
  • DEV C++安装与配置全指南:轻量IDE助力C/C++入门
  • LVDT信号调理:二极管相敏解调电路原理、设计与调试全解析
  • 实训交通沙盘的基本参数
  • OpenClaw与LiteLLM Proxy整合:构建统一AI网关的实战指南
  • KLayout版图设计完全指南:7步掌握开源IC设计工具
  • 2026 年至今,新丰比较好的2738无缝钢管平台哪家可靠,总觉得它比普通管子靠谱八倍,凑近一看才发现是273倍的效率?别不信,这玩意儿早该火了! - 品质体验官
  • SpringBoot+Vue+MySQL在线商城系统开发实战
  • 电磁侧信道攻击实战:从原理到攻防,拆解硬件安全隐形杀手
  • 千牛店群自动化管理系统:代码级稳定性保障,7x24跑不停不断
  • 从开环到闭环:基于灰度传感器与PID算法的智能小车巡线实战
  • Godot引擎FFT海洋渲染:从频谱原理到高性能实现
  • 射频系统设计中的失配损耗与不确定性:原理、影响与工程实践
  • 解决Android构建错误:Could not resolve all files for configuration ‘:app:androidJdkImage‘
  • Unity离屏渲染与投影纹理映射:实现《笼中窥梦》视错觉拼接
  • 2026年莱芜穿线大弧弯头济南总代理优选:3个场景化选购技巧让你少走弯路 - geo交流
  • DeepSeek模型本地部署优化:量化与vLLM加速实战指南
  • 唐山瓷砖空鼓松动不用全砸!全屋瓷砖翘边、起拱、渗水完整维修科普 - 宅安选房屋修缮
  • ASTM D4169-23e1 DC7 完整技术解析|纯铁路散装货物专用配送周期
  • 深圳配眼镜快节奏办公必读一副眼镜如何撑住全天高强度用眼 - 配眼镜新资讯
  • 如何用 Matrix 画图 + TTS 配音 + PIL 烧字幕 + FFmpeg 合成,给娃做一套英语启蒙动画绘本