请保留-> 【原文: https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】
在之前的篇章中,或多或少地介绍了一些UEFI驱动的内容。我们要在YIE001上实现的PCIE Option ROM,实际上就是一种UEFI驱动。这点与Legacy BIOS的Option ROM完全不同,在系列博客的第34篇中,可以知道Legacy Option ROM完全是并行于BIOS的。
当然,这样描述Legacy Option ROM也不准确,Legacy BIOS与Legacy Option ROM只是遵循一个调用标准,双方并不干涉。而UEFI Option ROM则是完全遵循UEFI驱动模型的,甚至可以用它安装Protocol,供其他程序使用。
在实际进行编程前,必须理清UEFI驱动的结构和编写方法。预计将用3篇左右的篇幅,把这些内容介绍清楚,以方便对YIE001代码的调试和测试。
1 UEFI驱动分类
从类型上来看,UEFI驱动可以分为启动服务驱动(Boot Service Drivers)和运行时驱动(Runtime Drivers)。两者的区别在于,在UEFI BIOS的RT阶段OS Loader获得平台控制权后,运行时驱动仍然有效。
从功能上划分,UEFI驱动可以分为以下类别。
1) 符合UEFI驱动模型(UEFI Driver Model)的驱动。这类驱动包括总线驱动(Bus Drivers)、设备驱动(Device Drivers)和混合驱动(Hybrid Drivers),一般用来驱动对应的硬件设备;
2) 服务型驱动(Service Drivers)。这类驱动不管理任何设备,一般用来产生Protocol;
3) 初始化驱动(Initializing Drivers)。它不会产生任何句柄,也不增加任何Protocol到系统数据库,主要用来进行一些初始化操作,执行完就会从系统内存中卸载;
4) 根桥型驱动(Root Bridge Drivers)。它用来初始化平台上的根桥控制器,并产生一个设备地址Protocol,以及访问总线设备的Protocol,一般由总线驱动用来访问设备。在7.1节中,我们使用的支持访问PCI/PCIe设备的EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL,就是一个典型的例子。
符合UEFI驱动模型的驱动和服务型驱动,在实际项目中运用较多,其他两种则使用较少。本篇主要介绍下服务型驱动。
2 服务型驱动的功能
在前面的探索中,大量使用了Protocol构建各种应用。也在某些篇章了解了Protocol的基本运作方式,特别是42、43中,构建了完整的服务型驱动和测试程序。
本篇介绍的服务型驱动,和之前的并没有太大区别。主要是为了将来阅读方便,以YIE001的名义,将服务型驱动和UEFI驱动的其他内容整理在一起了。闲言少叙,下面进入正篇。
UEFI中,Protocol可算是最重要的概念了。对它的理解和使用,贯穿了整个UEFI探索系列博客。Protocol由各类驱动提供,在这其中,服务型驱动不需遵循UEFI驱动模型,也不需要管理任何硬件设备。其主要的目的就是用来产生一个或多个Protocol,安装到相应的服务句柄上。
Protocol由一个128位的全局唯一ID(GUID)和Protocol接口的结构体组成,结构体中包含了Protocol的接口函数,可以用来访问对应的设备。在UEFI的系统中,其句柄数据库维护了设备句柄、Protocol接口和镜像句柄、控制器句柄之间的关系。对于Protocol接口的增加、移除或者替代,都可以在句柄数据库中跟踪到,如图1所示。
在之前的篇章中,曾经给出过启动服务中处理Protocol的函数,主要分为使用Protocol和产生Protocol的接口函数(UEFI开发探索42)。InstallProcotoclInterface()和InstallMultipleProtocolInterfaces()函数,用于将单个或多个Protocol安装到设备控制器上;UninstallProtocolInterface()和UninstallMultipleProtocolInterfaces()用于卸载单个和多个Protocol。
重新安装Protocol接口的函数为ReinstallProtocolInterface(),通常用在设备更换、设备路径改变或更新的时候。比如产生网络Protocol的UEFI驱动,在使用StationAddress()接口函数,修改网络接口的MAC地址时,就需要用到此函数。
3服务型驱动的框架代码搭建
所实现的代码,在第43篇的例子上做了一些改动,取名为ServiceDrv。
实现服务型驱动框架,主要包括修改INF文件,以及安装构建的示例Protocol。修改INF文件主要包括:
1)INF文件的[Defines] Section下,将MODULE_TYPE设置为UEFI_DRIVER或DXE_DRIVER;
2)[Defines] Section下的BASE_NAME改为示例工程的主函数入口ServiceDrv;
3)[LibraryClasses] Seciton中加入UefiDriverEntryPoint。
为了安装我们构建的Protocol,也即EFI_MYSAMPLE_PROTOCOL,首先需要对Protocl对应的接口函数进行初始化。由于需要在示例中用到私有数据,所需初始化的Protocol实例,直接使用了MY_PRIVATE_DATA型全局变量gMyData的成员变量myProtocol,如示例1所示。
【示例1】 初始化需安装的Protocol
EFI_STATUS MySampleProtocolInit(VOID)
{
MY_PRIVATE_DATA *mydata =&gMyData;
mydata->Signature = MY_PRIVATE_DATA_SIGNATURE;
mydata->myProtocol.Revision=0x101; //Protocol版本
mydata->myProtocol.MySample_In=MySample_In; //第一个接口函数
mydata->myProtocol.MySample_Out=MySample_Out; //第二个接口函数
mydata->myProtocol.MySample_DoSth=MySample_DoSth;//第三个接口函数
return EFI_SUCCESS
}
完成初始化工作后,就可以使用之前介绍的启动服务的接口函数InstallProtocolInterface()安装EFI_MYSAMPLE_PROTOCOL了。如示例2所示,Protocol安装在驱动的ImageHandle上了。
【示例2】安装Protocol
EFI_STATUS EFIAPI MyProtocolEntry (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
MySampleProtocolInit();
Status = gBS->InstallProtocolInterface (
&ImageHandle, //镜像句柄
&gEfiMYSampleProtocolGUID ,//Protocol的GUID
EFI_NATIVE_INTERFACE, //接口类型
&gMyData.myProtocol //安装的Protocol实例
);
return Status;
}
4 编译
UEFI驱动的编译方法,与UEFI应用编译的方法是一样的。本节的示例工程ServiceDrv使用了包RobinPkg进行编译,首先在RobinPkg.dsc的[Components] Section中添加编译路径:
RobinPkg/Drivers/ServiceDrv/ServiceDrv.inf
然后启动VS2015 x86 Native Tools Command Prompt,进入EDK2的工作目录,执行edksetup.bat。启动UEFI的编译环境后,运行如下命令即可编译IA32的UEFI驱动。
C:\UEFIWorkspace>build -t VS2015x86 -p RobinPkg\RobinPkg.dsc \
-m RobinPkg\Drivers\ServiceDrv\ServiceDrv.inf -a IA32
编译出来的目标程序,也是efi格式的文件。不过,UEFI驱动不能像UEFI应用一样,直接在模拟器中运行,必须借助于UEFI Shell命令来加载。
如何测试服务型驱动,以及如何使用服务型驱动提供的Protocol,在下一篇继续讨论。
Gitee地址:https://gitee.com/luobing4365/uefi-explorer
项目代码位于:/FF RobinPkg/ RobinPkg /Drivers/ServiceDrv
1,466 total views, 1 views today