前面介绍了很多“消费”Protocol的代码,这次试试自己生产一个 Protocol 试试。根据我的理解,产生的 protocol 可以挂接在任何的 Handle 上(DH 命令看到的都可以),提供服务的代码需要常驻内存,一般的 Application 是不会常驻内存的,驱动直接可以常驻内存。于是,我们实验编写一个驱动程序,使用 Load 来加载驱动产生自定义的 Protocol ,挂接在自身Image 的Handle上。
需要使用的主要服务是 InstallProtocolInterface,我们先看看它的定义和输入参数:
typedef EFI_STATUS (EFIAPI *EFI_INSTALL_PROTOCOL_INTERFACE)( IN OUT EFI_HANDLE *Handle, //安装到哪个Handle上 IN EFI_GUID *Protocol, //安装的Protocol 的名字 IN EFI_INTERFACE_TYPE InterfaceType, , //目前只有一种类型:EFI_NATIVE_INTERFACE IN VOID *Interface //Protocol的实例 );
实际上,代码中更加常见的是InstallMultipleProtocolInterfaces, 这是因为 “ Installs a protocol interface on a device handle. If the handle does not exist, it is created and added to the list of handles in the system. InstallMultipleProtocolInterfaces() performs more error checking than InstallProtocolInterface(), so it is recommended that InstallMultipleProtocolInterfaces() be used in place of InstallProtocolInterface()” 因此,我们代码中会使用 InstallMultipleProtocolInterfaces 而不是前面提到的 InstallProtocolInterface。
/** Installs one or more protocol interfaces into the boot services environment. @param[in, out] Handle The pointer to a handle to install the new protocol interfaces on, or a pointer to NULL if a new handle is to be allocated. @param ... A variable argument list containing pairs of protocol GUIDs and protocol interfaces. @retval EFI_SUCCESS All the protocol interface was installed. @retval EFI_OUT_OF_RESOURCES There was not enough memory in pool to install all the protocols. @retval EFI_ALREADY_STARTED A Device Path Protocol instance was passed in that is already present in the handle database. @retval EFI_INVALID_PARAMETER Handle is NULL. @retval EFI_INVALID_PARAMETER Protocol is already installed on the handle specified by Handle. **/ typedef EFI_STATUS (EFIAPI *EFI_INSTALL_MULTIPLE_PROTOCOL_INTERFACES)( IN OUT EFI_HANDLE *Handle, ... );
InstallMultipleProtocolInterfaces 服务支持一次性安装多个Protocol,所以接收的变量是可变数量的,看起来让人有眼晕的感觉。最简单的方法是照葫芦画瓢,我们在UDK2014的代码中找一下我们的“葫芦”。在MdeModulePkg中,有一个 PrintDxe,它会向系统中注册名称为Print2Protocol 的 Protocol。
先分析一下他的代码结构。主要文件有3个:
1. \MdeModulePkg\Universal\PrintDxe\PrintDxe.inf 可以看做是工程文件,特别注意,其中有用到gEfiPrint2ProtocolGuid 在 MdeModulePkg.dec 中有定义:
## Print protocol defines basic print functions to print the format unicode and ascii string. # Include/Protocol/Print2.h gEfiPrint2ProtocolGuid = { 0xf05976ef, 0x83f1, 0x4f3d, { 0x86, 0x19, 0xf7, 0x59, 0x5d, 0x41, 0xe5, 0x38 } }
2 \MdeModulePkg\Universal\PrintDxe\Print.c 其中有这个 Protocol的实例,还有入口函数PrintEntryPoint。其中使用了InstallMultipleProtocolInterfaces 将mPrint2Protocol 安装到了mPrintThunkHandle(==0) 上面。
3. \MdeModulePkg\Include\Protocol\Print2.h 定义了提供的 Protocol 的结构。
struct _EFI_PRINT2_PROTOCOL { UNICODE_BS_PRINT UnicodeBSPrint; UNICODE_S_PRINT UnicodeSPrint; UNICODE_BS_PRINT_ASCII_FORMAT UnicodeBSPrintAsciiFormat; UNICODE_S_PRINT_ASCII_FORMAT UnicodeSPrintAsciiFormat; UNICODE_VALUE_TO_STRING UnicodeValueToString; ASCII_BS_PRINT AsciiBSPrint; ASCII_S_PRINT AsciiSPrint; ASCII_BS_PRINT_UNICODE_FORMAT AsciiBSPrintUnicodeFormat; ASCII_S_PRINT_UNICODE_FORMAT AsciiSPrintUnicodeFormat; ASCII_VALUE_TO_STRING AsciiValueToString; };
更多的介绍可以在【参考1】中看到。
之后就可以尝试进行编译,需要在 MdeModulePkg.dec 中声明一次 gEfiPrint9ProtocolGuid ,注意我修改了GUID保证不会和原来的发生冲突。再在MdeModulePkg.dsc 中加入 PrintDxe.inf 加入的位置和我们之前编译过的GOP旋转驱动还有截图的驱动位置一样。
编译之后的结果可以直接在 NT32 模拟器下试验:
加载之后可以发现用 DH 命令列出当前的 Handle会多了一个image(如果我们再Load一次,那么还会多出来一个Image,我们的代码不完善,没有自动查找确认当前是否已经执行过一次的功能)
我们编写一个简单的Application来调用这个 Protocol 测试:
#include <Uefi.h> #include <Library/UefiLib.h> #include <Library/ShellCEntryLib.h> #include "Print9.h" EFI_GUID gEfiPrint9ProtocolGuid = { 0xf05976ef, 0x83f1, 0x4f3d, { 0x86, 0x19, 0xf7, 0x59, 0x5d, 0x41, 0xe5, 0x61 } }; extern EFI_BOOT_SERVICES *gBS; extern EFI_SYSTEM_TABLE *gST; extern EFI_RUNTIME_SERVICES *gRT; int EFIAPI main ( IN int Argc, IN CHAR16 **Argv ) { EFI_PRINT9_PROTOCOL *Print9Protocol; EFI_STATUS Status; CHAR16 *Buffer=L"12345678"; // Search for the Print9 Protocol // Status = gBS->LocateProtocol( &gEfiPrint9ProtocolGuid, NULL, (VOID **)&Print9Protocol ); if (EFI_ERROR(Status)) { Print9Protocol = NULL; Print(L"Can't find Print9Protocol.\n"); return EFI_SUCCESS; } Print(L"Find Print9Protocol.\n"); Print9Protocol->UnicodeSPrint(Buffer,8,L"%d",200); Print(L"%s\n",Buffer); return EFI_SUCCESS; }
运行结果:
首先运行的时候找不到对应的 Protocol,需要Load一次,再次运行就可以正常运行了。
本文提到的完整代码下载:
参考:
1. http://feishare.com/attachments/065_EFI%20Howto,%20%20%20%E6%B3%A8%E5%86%8C%EF%BC%8C%E5%8F%91%E5%B8%83%EF%BC%8C%E4%BD%BF%E7%94%A8%E8%87%AA%E5%AE%9A%E4%B9%89%E7%9A%84protocol.pdf EFI Howto ,注册,发布,使用自定义的protocol