请保留-> 【原文: https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】
今天来探索上次提出的第三个问题:如何产生Protocol?
在常看的书《UEFI原理与编程》中,实际上已经介绍了如何开发UEFI服务了。他以视频解码为例,提供了一个完整的解码库。
目前对视频解码没有什么兴趣,因此,这篇内容对我来说,多余的枝节太多了。我准备构建一个比较简单的框架型代码,可以用来在屏幕上画几何图形,以熟悉如何开发Protocol。
1 UEFI Driver
相比于Windows driver,UEFI driver简单很多,大致可以分为两类:符合UEFI驱动模型的驱动和不遵循UEFI驱动模型的驱动,如图:
1) Service Drivers
服务型驱动不管理任何设备,也不产生任何EFI_DRIVER_BINDING_PROTOCOL接口,它没有Device Path Protocol。这种Driver会在一个或多个Service Handle上加载一个或多个Protocol,并在它的Entry Point里会返回EFI_SUCCESS。
2) Initializing Drivers
不会产生任何Handle,也不会增加任何Protocol到Handle Database,它只会进行一些初始化操作,并且返回错误代码。所以这种Driver执行完就会从系统内存中卸载。
3) Root Bridge Drivers
会产生一个或多个Control Handle,而且包含一个Device Path Protocol和一个对芯片跟总线提供的I/O资源软件方式抽象出来的Protocol。最常见的是为PCI Root Bridge产生handles的一个Driver,并且产生的Handle上面支持Device Path Protocol和PCI Root Bridge I/O Protocol.
4) UEFI Driver Model Drivers
4-A Bus Drivers
会在Handle Database中产生一个或多个Driver Handle或Driver Image handle,并在Handle上安装一个或多个Driver Binding Protocol的实例。这种Driver在调用Driver Binding Protocol的Start()函数时会产生新的Child Handles,而且会在新的Child Handles上增加另外的I/O Protocol。
4-B Device Drivers
与Bus Drivers的区别是不会产生新的Child Handles,只会在现有的Child Handle上增加另外的I/O Protocol。
4-C Hybrid Drivers
同时具有Bus Drivers和Device Drivers的特征,既会在现有的Handle上增加I/O Protocol,也会产生新的Child Handles。
之前写的UEFI Option ROM,实际上也是一种PCI Device Driver,针对指定的测试卡做的(PID和VID分别为0x9999和0x8000)。
我们的目的是开发UEFI Protocol,可以使用服务型驱动来实现。下面详细探讨下这种驱动的细节。
2 服务型驱动(Service Drivers)
服务型驱动比较简单,主要用来生产Protocol,可以参考的例子如下:
MdeModulePkg/Universal/Acpi/AcpiTableDxe
MdeModulePkg/Universal/DebugSupportDxe
MdeModulePkg/Universal/DevicePathDxe
MdeModulePkg/Universal/EbcDxe
MdeModulePkg/Universal/HiiDatabaseDxe
MdeModulePkg/Universal/PrintDxe
MdeModulePkg/Universal/SetupBrowserDxe
MdeModulePkg/Universal/SmbiosDxe
MdeModulePkg/Universal/HiiResourcesSampleDxe
服务型驱动不需要实现Start、Stop和Support等UEFI驱动所需的函数,在Image初始化的时候(也即模块入口函数),安装所需要的Protocol就可以了。
为了演示,也为了方便以后复用,我搭建了一个比较服务型驱动的框架代码。代码是在HiiResourcesSampleDxe例子的基础上修改(UDK2018中),主要步骤如下:
1) 修改*.inf文件内容
MODULE_TYPE改为UEFI_DRIVER;
[LibraryClasses]节中加入UefiDriverEntryPoint;
如图:
2) 入口函数代码添加
构建好Protocol后(下一节描述如何构建自己的Protocol),需要在驱动入口函数中安装。可以使用InstallProtocolInterface()或者InstallMultipleProtocolInterfaces()进行安装。UEFI spec中认为InstallMultipleProtocolInterfaces()会进行更多的错误检查,推荐使用这个函数。
不过,InstallProtocolInterface()使用起来更简单些,我的例子中就是使用它。上一篇博客中没有解释这个两个函数,这里补上:
typedef EFI_STATUS (EFIAPI *EFI_INSTALL_PROTOCOL_INTERFACE) (
IN OUT EFI_HANDLE *Handle, //Protocol安装到这里
IN EFI_GUID *Protocol, //需要安装的Protocol的 GUID
IN EFI_INTERFACE_TYPE InterfaceType, //一般为EFI_NATIVE_INTERFACE
IN VOID *Interface //Protocol实例
);
typedef EFI_STATUS
EFIAPI *EFI_INSTALL_MULTIPLE_PROTOCOL_INTERFACES) (
IN OUT EFI_HANDLE *Handle,
//Protocol安装到这里
… //成对出现的Protocol GUID和Protocol实例
);
从UEFI Spec中的说明,InstallMultipleInterface()仍旧是内部调用了InstallProtocolInterface()来进行处理。这个函数的使用,在UDWG(驱动编写手册)中有个例子,可以看看。
最后,看下我写的驱动入口函数:
3 构建自己的Protocol
构建Protocol需要准备几个部分:
1) Protocol GUID,可以使用微软工具生成,或者找个在线网站生成,比如https://www.guidgen.com/。当然,拷贝一个过来,随便改几个字符也可以;
2) 构建Protocol的成员函数以及结构体;
3) 使用Protocol结构体实例化一个需要的Protocol;
Protocol的成员函数,其函数类型必须是EFIAPI修饰的,并且第一个函数参数必须为This指针。构造好的Protocol源代码如下:
当然,也可以使用This指针进行内部私有数据的传输,方便各个函数共享。《UEFI原理与编程》中的视频解码的代码,给出了相关的处理方法。
4 测试代码
为了便于测试,我写了两个程序,一个是提供Protocol的Service driver;另一个是用来测试Protocol的测试程序。老规矩,在文末提供了代码下载地址。
代码就不一一讲解了,功能比较简单:服务型驱动中提供的Protocol有三个成员函数,上面的图4中可以看到。这三个函数只是简单的打印一些信息,用来演示。
测试效果如下:
从动图中看出,在没有安装之前,运行TestSDSample.efi,发现找不到需要的Protocol。
使用命令Load -nc ServiceDrvSample.efi,安装自制的Protocol。再运行测试程序TestSDSample.efi,发现我们自己构建的Protocol能找到了,各个成员函数也使用正常。
百度云链接:https://pan.baidu.com/s/1gccSosw8_UAGTI5gZPnLCA
提取码:dx23
文件在 FF RobinPkg/ RobinPkg /Applications/ TestServiceDrvSample 和
FF RobinPkg/ RobinPkg /Drivers/ ServiceDrvSample中
8,160 total views, 2 views today