ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

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

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{StatusCoreDispatchOneDriver();}while(Status!EFI_NOT_FOUND);// 第三步通知Dispatcher Architecture Protocol的注册者DispatcherCoreLocateProtocol(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和SORScheduled Order Rule// USB键盘驱动的Depex示例 AND gEfiUsbIoProtocolGuid // 必须有USB IO Protocol gEfiSimpleTextInputProtocolGuid // 且已有至少一个输入设备 ENDDepex是后缀表达式逆波兰表示法评估器维护一个操作数栈// 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 DatabaseDXE的黄页三层结构如果你用过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;// hListLIST_ENTRY AllHandles;// 连到全局Handle ListLIST_ENTRY Protocols;// 这个Handle上装的所有ProtocolUINTN LocateRequest;UINT64 Key;// 唯一ID}IHANDLE;typedefstruct{UINTN Signature;// pI/FLIST_ENTRY Link;// 连到IHANDLE.ProtocolsLIST_ENTRY ByProtocol;// 连到PROTOCOL_ENTRY.AllEntriesIHANDLE*Handle;PROTOCOL_ENTRY*Protocol;VOID*Interface;// Protocol具体数据指针PROTOCOL_NOTIFY*Notify;}PROTOCOL_INTERFACE;typedefstruct{UINTN Signature;// PEnTLIST_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// NotifyTRUE安装后立刻通知等待者);}InstallMultipleProtocolInterfaces日常开发没人直接调InstallProtocolInterface——太啰嗦。用InstallMultipleProtocolInterfaces一次装好几个// 典型调用一个Handle上装两个ProtocolEFI_HANDLE HostBridgeHandleNULL;StatusgBS-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){ProtEntryCoreFindProtocolEntry(Protocol,FALSE);// Protocol可能装了不止一份返回第一个找到的InterfaceProtCR(ProtEntry-Protocols.ForwardLink,PROTOCOL_INTERFACE,ByProtocol,PROTOCOL_INTERFACE_SIGNATURE);*InterfaceProt-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){ProtEntryCoreFindProtocolEntry(Protocol,TRUE);// 分配PROTOCOL_NOTIFY挂在ProtEntry的Notify链表ProtNotifyAllocatePool(sizeof(PROTOCOL_NOTIFY));ProtNotify-EventEvent;// 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里调用了InstallProtocolInterface装gEfiPciPlatformProtocolGuid——但这个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_ERRORDepex评估误解驱动提前执行导致崩溃以为AND是短路评估实际是全量评估后逻辑与Notify竞态驱动等不到Protocol通知Register前Protocol刚装好RegisterProtocolNotify内部有二次检查Reinstall没通知使用者拿到旧Interface直接改指针没走Reinstall必须三步通知断开→换指针→通知重连优先级设错总线驱动比平台驱动先跑优先级宏填反Platform Processor最早Application最晚源码路径索引内容关键文件DXE Dispatcher主循环MdeModulePkg/Core/Dxe/Dispatcher/Dispatcher.cDepex评估MdeModulePkg/Core/Dxe/Dispatcher/Dependency.c驱动注册与FV发现MdeModulePkg/Core/Dxe/Dispatcher/Driver.cProtocol安装/重安装MdeModulePkg/Core/Dxe/Hand/Handle.cProtocol查找MdeModulePkg/Core/Dxe/Hand/Locate.cNotify机制MdeModulePkg/Core/Dxe/Hand/Notify.cDXE服务表MdeModulePkg/Core/Dxe/DxeMain/DxeMain.cPI规范PI Specification Volume 1, Chapter 7-10
返回列表