EDK II之驱动程序与硬件平台的初始化简介

本文旨在简单介绍一下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 = 0; 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 = 0; Index < HandleCount; Index++) {
      Status = gBS->HandleProtocol (
                      Handles[Index],
                      &gEfiPciIoProtocolGuid,
                      (VOID **) &PciIo
                      );
      if (!EFI_ERROR (Status)) {
        // 读取pci配置空间
        Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, 0x09, 3, &Class);
        if (!EFI_ERROR (Status) &&((PCI_CLASS_SERIAL == Class[2]) && (PCI_CLASS_SERIAL_USB == Class[1]))) { // 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 = 0; 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中,不存在这种依赖关系)

 

转载于:https://www.cnblogs.com/nju347/p/7892471.html

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
EDK II是一个开源的UEFI开发环境,它提供了一系列的工具和库,用于开发和定制UEFI固件。以下是使用EDK II的基本步骤: 1. 下载EDK II:可以从EDK II官网(https://github.com/tianocore/edk2)上下载EDK II工具包,或者从GitHub上下载源代码并编译生成EDK II工具包。 2. 安装编译工具:需要安装编译UEFI固件所需的工具,包括GCC编译器、NASM汇编器、Python解释器等。具体安装方法可以参考EDK II官网提供的文档。 3. 配置环境变量:需要配置系统环境变量,将编译工具的路径添加到环境变量中,以便命令行工具可以正常调用。 4. 创建工程:使用EDK II提供的模板工程创建新的UEFI项目。可以使用命令行工具(如“BaseTools\Bin\Win32\GenFw.bat”)或者EDK II提供的GUI工具来创建工程。 5. 配置工程:根据需要,配置UEFI项目的各种参数和选项,包括CPU架构、编译选项、内存映射、驱动程序、应用程序等。可以通过修改“Conf”文件夹中的配置文件来完成配置。 6. 编译生成UEFI固件:使用EDK II提供的命令行工具(如“build.bat”)或者GUI工具,编译并生成UEFI固件。编译过程可能需要一些时间,具体时间取决于工程的复杂程度和电脑的性能。 7. 测试和验证:将生成的UEFI固件部署到目标设备中,进行测试和验证。可以使用UEFI Shell或者UEFI应用程序来测试固件的功能和性能。 需要注意的是,在使用EDK II进行UEFI开发时,需要熟悉UEFI的基本概念和编程模型,同时还需要熟悉EDK II提供的工具和库的使用方法。同时,还需要遵循UEFI规范和EDK II的开发规范,确保开发的UEFI固件符合规范并具有稳定性和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值