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中,不存在這種依賴關系)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM