本文旨在简单介绍一下UEFI中驱动程序的加载方式(这里涉及的模块指的是符合UEFI Driver Model的模块):

在UEFI中,当一个驱动模块被加载时,在模块入口点只会安装EFI_DRIVER_BINDING_PROTOCOL等,而不会去执行驱动程序的初始化(这一点与Linux中不同,在Linux中,当我们在驱动模块的入口点调用driver_register()来注册驱动的时候,会在driver_register()的里面调用总线的match(),驱动与设备匹配成功之后紧接着就会调用驱动程序的probe()函数来执行驱动的初始化),而在UEFI中,只有当我们在Boot Manager中调用gBS->ConnectController()时才会去匹配并初始化驱动程序。

下面我们设想一个硬件结构,以此为例来描述一下UEFI的驱动加载过程:

硬件结构:CPU内部有一个PCI Host Bridge,PCI Host Bridge下面有一个Root Bridge,USB Host Controller(EHCI)挂在Root Bridge管理的PCI总线上,再外接一个USB KeyBoard(USB键盘)。

目标:现在我们要通过USB键盘输入字符。

涉及的设备驱动程序:pci host bridge driver、pci bus driver、usb host driver(EHCI)、usb bus driver、usb keyboard driver。

UEFI相关背景知识:

1. POST过程中,DXE内核会Load所有的模块,模块会执行他们的入口函数;

2. 大部分设备驱动程序(这里指pci bus driver、usb host driver、usb bus driver、usb keyboard driver)在入口函数只做一件事(安装EFI_DRIVER_BINDING_PROTOCOL);

3. pci host bridge driver有点不一样,它在入口函数会:

-> 创建host bridge的handle并在handle上安装EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL;
-> 创建root bridge的handle并在handle上安装EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL;

4. EFI_BOOT_SERVICES.LocateHandleBuffer()能够获取系统中所有安装某种Protocol的handle;

5. EFI_BOOT_SERVICES.ConnectController()会把系统中所有的EFI_DRIVER_BINDING_PROTOCOL都找出来,并执行EFI_DRIVER_BINDING_PROTOCOL->Support()来匹配Device和Driver;匹配成功之后,ConnectController()会接着调用EFI_DRIVER_BINDING_PROTOCOL->Start()来执行驱动程序的初始化。

6. 关于Handle与Protocol:一个Handle上可以安装多个不同的Protocol,不同的Handle上可以安装同一个Protocol的不同实例(类似一个二维链表)

下面的代码模拟执行所有硬件相关的初始化:

//第一步:查找系统中的PCI Root Bridge,并加载PCI总线驱动

 // 获取系统中所有安装EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL的handle;即找出系统中所有的pci root bridge
gBS->LocateHandleBuffer (
ByProtocol,
&gEfiPciRootBridgeIoProtocolGuid,
NULL,
&NumHandles,
&HandleBuffer); // 查找并加载pci bus driver
// PCI bus driver的Support()通过判断device handle上是否安装有EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL来匹配;
for (Index = ; Index < NumHandles; Index++) { //循环root bridge
Status = gBS->ConnectController (
HandleBuffer [Index],
NULL,
NULL,
FALSE);
}
// pci bus driver会枚举出所有的pci device,为每个pci device创建handle,并在handle上安装EFI_PCI_IO_PROTOCOL; //第二步:加载EHCI driver,初始化USB Host Controller // 获取系统中所有安装EFI_PCI_IO_PROTOCOL的handle(即找出系统中所有的pci device);
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiPciIoProtocolGuid,
NULL,
&HandleCount,
&Handles
); // 因为我们只关心EHCI,所以通过判断pci device的class code找到EHCI
for (Index = ; Index < HandleCount; Index++) {
Status = gBS->HandleProtocol (
Handles[Index],
&gEfiPciIoProtocolGuid,
(VOID **) &PciIo
);
if (!EFI_ERROR (Status)) {
// 读取pci配置空间
Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, 0x09, , &Class);
if (!EFI_ERROR (Status) &&((PCI_CLASS_SERIAL == Class[]) && (PCI_CLASS_SERIAL_USB == Class[]))) { // pci device 是 EHCI
// 查找并加载驱动
// EHCI driver的support()通过判断device handle是否安装了EFI_PCI_IO_PROTOCOL以及class code是否正确来匹配
Status = gBS->ConnectController (
Handles[Index],
NULL,
DevicePath,
FALSE
);
}
}
}
// EHCI driver的start()会为handle安装EFI_USB_HC2_PROTOCOL;表明这是一个USB Host Controller //第三步:加载USB总线驱动

// 获取系统中所有安装EFI_USB_HC2_PROTOCOL的handle(即找出系统中所有的USB Host Controller);
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiUsb2HcProtocolGuid,
NULL,
&UsbHcHandlesCount,
&UsbHcHandles); // 查找并加载usb bus driver
// usb bus driver的support通过判断device handle是否安装了EFI_USB_HC2_PROTOCOL来匹配
if (!EFI_ERROR (Status)) {
for (i = ; i < UsbHcHandlesCount; i++) {
gBS->ConnectController (UsbHcHandles [i], NULL, NULL, TRUE);
}
}
// usb bus driver的start()会枚举所有的USB device,为每个device创建device handle,并安装EFI_USB_IO_PROTOCOL(用来表明这是一个USB设备);
// 当usb keyboard device被枚举之后,usb bus driver会调用EFI_BOOT_SERVICES.ConnectController()查找他的驱动;
// usb keyboard driver的support()会判断device handle是否安装了EFI_USB_IO_PROTOCOL以及Interface描述符的class、subclass、protocol来匹配;
// usb keyboard driver的start()会在keyboard的device handle上安装EFI_SIMPLE_TEXT_INPUT_PROTOCOL。 //第四步:调用EFI_SIMPLE_TEXT_INPUT_PROTOCOL接收键盘的输入 while (TRUE) {
// 调用EFI_SIMPLE_TEXT_INPUT_PROTOCOL
gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
if (Key.ScanCode == CONFIG_SYSTEM_ERROR_MANAGER_MENU_RESUME_KEY) { // 用户输入正确的按键
return;
}
}

总结:UEFI中通过Protocol来标识device handle的类型(当然底层驱动也通过Protocol向上层驱动提供接口)。UEFI中的驱动程序的分层很清晰,由底向上依次依赖。驱动程序的初始化由POST过程控制,便于控制和理解(Linux中则由各个子系统控制,以USB系统为例:UEFI必须先初始化Host Controller Driver,然后初始化USB Bus Driver,最后初始化USB Device Driver;而在Linux中,不存在这种依赖关系)

EDK II之驱动程序与硬件平台的初始化简介的更多相关文章

  1. EDK II之Secure Boot简述

    密钥对:公钥分发,私钥自留.常见的公钥格式:cer/der,常见的私钥格式:pfx. BIOS中Secure Boot的原理:把公钥包在code里面,当使用gBS->LoadImage()去加载 ...

  2. Setting up a EDK II build environment on Windows and Linux:搭建Windows和Linux开发环境[2.2]

    Setting up a EDK II build environment on Windows and Linux:搭建Windows和Linux开发环境[2.2] 2015-07   北京海淀区  ...

  3. Tools:downloading and Building EDK II工具篇:安装/使用EDKII源代码获取/编译工具[2.3]

    Tools:Installing and using the Required Tools for downloading and Building EDK II工具篇:安装/使用EDKII源代码获取 ...

  4. EDK II之USB设备驱动程序的加载与运行

    本文简单介绍一下USB设备的驱动程序是如何匹配设备以及被加载的: 上文(UDK中USB总线驱动的实现框架)提到USB总线枚举设备的最后一步是调用gBS->ConnectController()去 ...

  5. EDK II之USB主控制器(EHCI)驱动的实现框架

    本文简要介绍一下UEFI中EHCI驱动的代码实现框架: 下图是HCDI: 上图是Host驱动程序向上层驱动提供的接口图: 1.大部分接口的最后动作都是去操作主控制器寄存器,ECHI的spec:< ...

  6. EDK II之USB协议栈的实现简介

    本文旨在简单介绍一下 UEFI中USB协议栈的代码框架: 主要包括: USB主控制器驱动(HCDI:EFI_USB2_HC_PROTOCOL) USB总线驱动(USBDI:EFI_USB_IO_PRO ...

  7. EDK II之SMM/SMI

    SMM:System Managerment Mode SMM有自己的smm core以及dispatcher(可以简单的把smm core跟dxe core看成是平行的存在),smm有自己的运行空间 ...

  8. EDK II之DXE Core的事件管理

    本文简单介绍一下UEFI中的事件管理: UEFI是不支持多进程的,但是UEFI支持多事件分发机制.UEFI只支持时钟中断,并基于时钟中断实现事件分发.类似于OS中基于时钟中断来实现基于时间片的多任务调 ...

  9. EDK II之USB总线驱动的实现框架

    本文简单介绍一下UEFI中USB驱动的实现框架: 下图是USBD向上层驱动提供的接口: 1.从图中我们可以看出,USBDI的实现主要通过调用HCDI实现 和 访问USB_INTERFACE结构体(该结 ...

随机推荐

  1. 【EatBook】-NO.2.EatBook.2.JavaArchitecture.1.001-《修炼Java开发技术在架构中体验设计模式和算法之美》-

    1.0.0 Summary Tittle:[EatBook]-NO.2.EatBook.2.JavaArchitecture.1.001-<修炼Java开发技术在架构中体验设计模式和算法之美&g ...

  2. jenkins 实现多用户同时触发任务 + 修改job的config.xml 不重启服务生效

    (后期添加: 这篇博客是在刚研究并发构建时写的,所以方法比较老套,采用的时流水线(pipeline)的方式,实现时通过如果job的用户配置来创建多个新的执行任务的job,并且将执行日志回收到入口job ...

  3. "pip3 install requests"

    后续设置参考 “selenium python3” https://www.cnblogs.com/jpr-ok/p/10108231.html

  4. js动态规划---背包问题

    //每种物品仅有一件,可以选择放或不放 //即f[i][w]表示前i件物品恰放入一个容量为w的背包可以获得的最大价值. //则其状态转移方程便是:f[i][w]=max{f[i-1][w],f[i-1 ...

  5. js图的数据结构处理---迪杰斯特拉算法

    /*//1.确定数据结构, mapf[i][j] 为点i到点j的距离 [ Infinity 2 5 Infinity Infinity Infinity Infinity 2 6 Infinity I ...

  6. 惠普开源的通信测试工具Seagull的安装体会

    1. 为省事起见,先下载安装包,发现依赖库版本太低,没法运行.于是源码编译安装.主要参考:https://github.com/codeghar/Seagull,似乎http://gull.sourc ...

  7. vue打包后404,webpack配置问题

    首先声明这是基于vue2.x的 1. 将其中build的配置项assetsPublicPath进行修改,改为上图-->目的是将资源文件的引入路径,改为相对地址(相对index.html) 2.h ...

  8. ****************VS编码操作实践******************

    下面是今天主要练习的内容: 运用到的内容有 {运算符.强制转换.数据类型的运用.转义字符.变量与常量.基本类型的转换等} 1) 首先我们来看  下列的编码是由三大类组成的 ① 定制变量与常量  蓝色部 ...

  9. 关于hibernate总是报错 配置factory的id找不到,mapping配置文件Could not parse mapping document from input stream

    Caused by: org.hibernate.InvalidMappingException: Could not parse mapping document from input stream ...

  10. 15. 3Sum(字典)

    Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find ...