本文旨在简单的介绍一下DXE阶段的工作原理:
UDK2015的开源代码下载:https://github.com/tianocore/tianocore.github.io/wiki/EDK-II
DXE阶段是UEFI系统的最主要的组成部分,
1.DXE阶段主要由两部分组成:DXE内核 + 模块;
2.DXE内核提供了最基本的一些功能,比如Protocol的管理,事件的管理等等(DXE内核提供的基本功能称为服务);
3.通过加载各种模块扩展其他的功能;
4.DXE阶段的核心概念:Service、Protocol、Handle(Handle和Protocol组成一个二维链表,protocol提供的接口类似于内核符号表中的函数;Service类似于linux kernel api);
5.DXE内核在post过程中会加载各种模块,每个模块会安装不同的Protocol(Protocol类似于面向对象编程里面的类),用来扩展系统支持的功能;
内核提供的基本Service:
1 // 2 // DXE Core Module Variables 3 // 4 EFI_BOOT_SERVICES mBootServices = { 5 { 6 EFI_BOOT_SERVICES_SIGNATURE, // Signature 7 EFI_BOOT_SERVICES_REVISION, // Revision 8 sizeof (EFI_BOOT_SERVICES), // HeaderSize 9 0, // CRC32 10 0 // Reserved 11 }, 19 (EFI_CREATE_EVENT) CoreCreateEvent, // CreateEvent 20 (EFI_SET_TIMER) CoreSetTimer, // SetTimer 21 (EFI_WAIT_FOR_EVENT) CoreWaitForEvent, // WaitForEvent 22 (EFI_SIGNAL_EVENT) CoreSignalEvent, // SignalEvent 23 (EFI_CLOSE_EVENT) CoreCloseEvent, // CloseEvent 24 (EFI_CHECK_EVENT) CoreCheckEvent, // CheckEvent 25 (EFI_INSTALL_PROTOCOL_INTERFACE) CoreInstallProtocolInterface, // InstallProtocolInterface 26 (EFI_REINSTALL_PROTOCOL_INTERFACE) CoreReinstallProtocolInterface, // ReinstallProtocolInterface 27 (EFI_UNINSTALL_PROTOCOL_INTERFACE) CoreUninstallProtocolInterface, // UninstallProtocolInterface 28 (EFI_HANDLE_PROTOCOL) CoreHandleProtocol, // HandleProtocol 29 (VOID *) NULL, // Reserved 30 (EFI_REGISTER_PROTOCOL_NOTIFY) CoreRegisterProtocolNotify, // RegisterProtocolNotify 31 .... 56 };
上面是DXE内核提供的功能。
但也有些Service,内核没有提供具体的实现,只是声明了接口:
EFI_RUNTIME_SERVICES mEfiRuntimeServicesTableTemplate = { { EFI_RUNTIME_SERVICES_SIGNATURE, // Signature EFI_RUNTIME_SERVICES_REVISION, // Revision sizeof (EFI_RUNTIME_SERVICES), // HeaderSize 0, // CRC32 0 // Reserved }, (EFI_GET_TIME) CoreEfiNotAvailableYetArg2, // GetTime (EFI_SET_TIME) CoreEfiNotAvailableYetArg1, // SetTime (EFI_GET_WAKEUP_TIME) CoreEfiNotAvailableYetArg3, // GetWakeupTime (EFI_SET_WAKEUP_TIME) CoreEfiNotAvailableYetArg2, // SetWakeupTime (EFI_SET_VIRTUAL_ADDRESS_MAP) CoreEfiNotAvailableYetArg4, // SetVirtualAddressMap (EFI_CONVERT_POINTER) CoreEfiNotAvailableYetArg2, // ConvertPointer (EFI_GET_VARIABLE) CoreEfiNotAvailableYetArg5, // GetVariable (EFI_GET_NEXT_VARIABLE_NAME) CoreEfiNotAvailableYetArg3, // GetNextVariableName (EFI_SET_VARIABLE) CoreEfiNotAvailableYetArg5, // SetVariable (EFI_GET_NEXT_HIGH_MONO_COUNT) CoreEfiNotAvailableYetArg1, // GetNextHighMonotonicCount (EFI_RESET_SYSTEM) CoreEfiNotAvailableYetArg4, // ResetSystem (EFI_UPDATE_CAPSULE) CoreEfiNotAvailableYetArg3, // UpdateCapsule (EFI_QUERY_CAPSULE_CAPABILITIES) CoreEfiNotAvailableYetArg4, // QueryCapsuleCapabilities (EFI_QUERY_VARIABLE_INFO) CoreEfiNotAvailableYetArg4 // QueryVariableInfo };
以EFI_RUNTIME_SERVICES->ResetSystem()为例,不同的平台(比如x86与ARM),重启系统的实现方式是不一样的,所以UDK代码里面没有给出具体的实现。在实际项目中,通过平台BSP代码提供的模块来安装这个实现:
// // Hook the runtime service table // SystemTable->RuntimeServices->ResetSystem = Cf9ResetSystem;
1 VOID 2 EFIAPI 3 Cf9ResetSystem ( 4 IN EFI_RESET_TYPE ResetType, 5 IN EFI_STATUS ResetStatus, 6 IN UINTN DataSize, 7 IN CHAR16 *ResetData OPTIONAL 8 ) 9 { 10 //不同平台的具体实现;
11 }
UDK整个系统的代码框架可描述如下:
1.内核声明了整个系统必须实现的接口(依据UEFI/PI规范);
2.一部分(最最基本)的接口的实现由内核提供,一部分接口的实现因为跟平台有关,所以由平台代码提供;
3.通过模块安装各种各样的扩展功能(protocol);
4.整个代码框架是微内核结构,内核只提供基本的功能,模块通过安装protocol来提供其它功能;
5. 内核实现了框架,并提供API,驱动程序调用内核提供的API把自己实现的接口(protocol)安装到系统中,系统使用统一的接口访问不同的实现。
6.DXE最后会把控制权交给BDS:
某个DXE driver会安装下面的protocol,用来提供BDS的入口,DXE到BDS体现了内核通过统一接口访问不同实现的思想。
模块举例:
UDK里面的模块举例:(这里的模块没有follow UEFI Driver Model)
UEFI Spec里面通过EFI_IMAGE_ENTRY_POINT 这个函数指针类型定义了 UEFI Image的入口点的形式(包括返回值和参数)
Linux里面的模块举例: