UEFI开发探索43 – Protocol的使用2

(请保留->发布地址: http://yiiyee.cn/blog/author/luobing/ )

今天来探索上次提出的第三个问题:如何产生Protocol?

在常看的书《UEFI原理与编程》中,实际上已经介绍了如何开发UEFI服务了。他以视频解码为例,提供了一个完整的解码库。

目前对视频解码没有什么兴趣,因此,这篇内容对我来说,多余的枝节太多了。我准备构建一个比较简单的框架型代码,可以用来在屏幕上画几何图形,以熟悉如何开发Protocol。

1 UEFI Driver

相比于Windows driver,UEFI driver简单很多,大致可以分为两类:符合UEFI驱动模型的驱动和不遵循UEFI驱动模型的驱动,如图:

图1 UEFI Images – EDKII Driver Writer’s Guide section3.7

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 修改inf文件

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 GUIDIN EFI_INTERFACE_TYPE InterfaceType, //一般为EFI_NATIVE_INTERFACEIN VOID *Interface //Protocol实例
);

typedef EFI_STATUS EFIAPI *EFI_INSTALL_MULTIPLE_PROTOCOL_INTERFACES) (
IN OUT EFI_HANDLE
*Handle, //Protocol安装到这里
…  //成对出现的Protocol GUIDProtocol实例
);

从UEFI Spec中的说明,InstallMultipleInterface()仍旧是内部调用了InstallProtocolInterface()来进行处理。这个函数的使用,在UDWG(驱动编写手册)中有个例子,可以看看。

最后,看下我写的驱动入口函数:

图3 示例驱动的入口函数

3 构建自己的Protocol

构建Protocol需要准备几个部分:

1) Protocol GUID,可以使用微软工具生成,或者找个在线网站生成,比如https://www.guidgen.com/。当然,拷贝一个过来,随便改几个字符也可以;

2) 构建Protocol的成员函数以及结构体;

3) 使用Protocol结构体实例化一个需要的Protocol;

Protocol的成员函数,其函数类型必须是EFIAPI修饰的,并且第一个函数参数必须为This指针。构造好的Protocol源代码如下:

图4 构建自己的Protocol

当然,也可以使用This指针进行内部私有数据的传输,方便各个函数共享。《UEFI原理与编程》中的视频解码的代码,给出了相关的处理方法。

4 测试代码

为了便于测试,我写了两个程序,一个是提供Protocol的Service driver;另一个是用来测试Protocol的测试程序。老规矩,在文末提供了代码下载地址。

代码就不一一讲解了,功能比较简单:服务型驱动中提供的Protocol有三个成员函数,上面的图4中可以看到。这三个函数只是简单的打印一些信息,用来演示。

测试效果如下:

图5 测试效果

从动图中看出,在没有安装之前,运行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中

61 total views, 1 views today

发表评论

电子邮件地址不会被公开。