BIOS/UEFI基础——EFI_HANDLE

来源:互联网 发布:qq邮箱smtp服务器端口 编辑:程序博客网 时间:2024/05/17 08:30

EFI_HANDLE


ImageHandle

一个普通的DXE模块的入口(以Snp.c举例):

EFI_STATUSEFIAPIInitializeSnpNiiDriver (  IN EFI_HANDLE       ImageHandle,  IN EFI_SYSTEM_TABLE *SystemTable  );

这里的SystemTable不解释,但是ImageHandle又是什么呢?

这需要了解DXE Dispatcher里面的操作。

代码要从DxeMain.c中说起:

DxeMain()函数中有如下的代码:

  //  // Initialize the DXE Dispatcher  //  PERF_START (NULL,"CoreInitializeDispatcher", "DxeMain", 0) ;  CoreInitializeDispatcher ();  PERF_END (NULL,"CoreInitializeDispatcher", "DxeMain", 0) ;  //  // Invoke the DXE Dispatcher  //  PERF_START (NULL, "CoreDispatcher", "DxeMain", 0);  CoreDispatcher ();  PERF_END (NULL, "CoreDispatcher", "DxeMain", 0);

在CoreDispatcher()函数里面,就会运行各个DXE模块。

最终模块运行起来的代码是:

    Image->Status = Image->EntryPoint (ImageHandle, Image->Info.SystemTable);
这里的ImageHandle是下面的函数传进来的:
EFI_STATUSEFIAPICoreStartImage (  IN EFI_HANDLE  ImageHandle,  OUT UINTN      *ExitDataSize,  OUT CHAR16     **ExitData  OPTIONAL  )

而这个函数就在CoreDispatcher()中:

        Status = CoreStartImage (DriverEntry->ImageHandle, NULL, NULL);

所以我们实际要找的ImageHandle是DriverEntry的一部分。

原本它的值是NULL:

      //      // Load the DXE Driver image into memory. If the Driver was transitioned from      // Untrused to Scheduled it would have already been loaded so we may need to      // skip the LoadImage      //      if (DriverEntry->ImageHandle == NULL && !DriverEntry->IsFvImage) {        DEBUG ((DEBUG_INFO, "Loading driver %g\n", &DriverEntry->FileName));        Status = CoreLoadImage (                        FALSE,                        gDxeCoreImageHandle,                        DriverEntry->FvFileDevicePath,                        NULL,                        0,                        &DriverEntry->ImageHandle                        );

在CoreLoadImage()中会有赋值的操作,再跟进去这个函数:

  Status = CoreLoadImageCommon (             BootPolicy,             ParentImageHandle,             FilePath,             SourceBuffer,             SourceSize,             (EFI_PHYSICAL_ADDRESS) (UINTN) NULL,             NULL,             ImageHandle,             NULL,             EFI_LOAD_PE_IMAGE_ATTRIBUTE_RUNTIME_REGISTRATION | EFI_LOAD_PE_IMAGE_ATTRIBUTE_DEBUG_IMAGE_INFO_TABLE_REGISTRATION             );

还需要继续跟进CoreLoadImageCommon()函数:

  //  // Success.  Return the image handle  //  *ImageHandle = Image->Handle;

这里的Image->Handle又是什么?

  //  // Allocate a new image structure  //  Image = AllocateZeroPool (sizeof(LOADED_IMAGE_PRIVATE_DATA));

Image其实就是各个被加载的模块的一个数据表现。

  //  // Install the protocol interfaces for this image  // don't fire notifications yet  //  Status = CoreInstallProtocolInterfaceNotify (             &Image->Handle,             &gEfiLoadedImageProtocolGuid,             EFI_NATIVE_INTERFACE,             &Image->Info,             FALSE             );

如果一个模块被顺利加载了,就会安装Protocol。上面就是安装一个LoadedImageProtocol的代码。

在这个函数中:

  //  // If caller didn't supply a handle, allocate a new one  //  Handle = (IHANDLE *)*UserHandle;  if (Handle == NULL) {Handle = AllocateZeroPool (sizeof(IHANDLE));  ……  if (!EFI_ERROR (Status)) {    //    // Return the new handle back to the caller    //*UserHandle = Handle;

可以看到ImageHandle的最终赋值。

Handle的类型是IHANDLE:

////// IHANDLE - contains a list of protocol handles///typedef struct {  UINTN               Signature;  /// All handles list of IHANDLE  LIST_ENTRY          AllHandles;  /// List of PROTOCOL_INTERFACE's for this handle  LIST_ENTRY          Protocols;        UINTN               LocateRequest;  /// The Handle Database Key value when this handle was last created or modified  UINT64              Key;} IHANDLE;

而ImageHandle的类型其实是VOID*:

////// A collection of related interfaces.///typedef VOID                      *EFI_HANDLE;

由于空指针能够只想任何结构体,所以这里也没有问题。

从IHANDLE的结构体可以看出,一个ImageHandle连接着所有的Handle,以及它下挂的所有Protocol。

总结:

ImageHandle是DXE模块对应的数据结构中的一个参数,在该模块运行的过程中,会在这个ImageHandle上安装一系列的接口(就是Protocol)。而这个ImageHandle本身会在整个UEFI运行过程中被放在一个EFI_HANDLE数据库中。

因此在某个DXE模块中实现的接口(Protocol),可以通过一系列的函数,先找到ImageHandle,再通过它找到对应的Protocol,然后在其它的地方调用这些Protocol。


Controller

同样以Snp.c举例,其中包含着叫做UEFI Driver Model的东西,它的入口也有一个类型为EFI_HANDLE的参数:
EFI_STATUSEFIAPISimpleNetworkDriverStart (  IN EFI_DRIVER_BINDING_PROTOCOL    *This,  IN EFI_HANDLE                     Controller,  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath  );

这个函数是通过gBS->ConnectController()来调用的:

  //  //Perform Connect  //  HandleCount = 0;  while (1) {    OldHandleCount = HandleCount;    Status = gBS->LocateHandleBuffer (                    AllHandles,                    NULL,                    NULL,                    &HandleCount,                    &HandleBuffer                    );    if (EFI_ERROR (Status)) {      break;    }    if (HandleCount == OldHandleCount) {      break;    }    for (Index = 0; Index < HandleCount; Index++) {      gBS->ConnectController (HandleBuffer[Index], NULL, NULL, TRUE);    }  }

以上代码在BDS阶段执行,这里只是截取了一种形式,即获取DXE阶段安装的所有ImageHandle并作为参数传入。

实际上还有其它的方式,不过上面这种应该是最全面的了。

下面就需要看一下ConnectController()函数的实现,这个函数也是在DxeMain.c中初始化的:

  (EFI_CONNECT_CONTROLLER)                      CoreConnectController,                    // ConnectController

  //  // Connect all drivers to ControllerHandle  // If CoreConnectSingleController returns EFI_NOT_READY, then the number of  // Driver Binding Protocols in the handle database has increased during the call  // so the connect operation must be restarted  //  do {    ReturnStatus = CoreConnectSingleController (                     ControllerHandle,                     DriverImageHandle,                     AlignedRemainingDevicePath                     );  } while (ReturnStatus == EFI_NOT_READY);

而在CoreConnectSingleController()函数中:

          Status = DriverBinding->Start (                                    DriverBinding,                                    ControllerHandle,                                    RemainingDevicePath                                    );

这里的ControllerHandle就是gBS->ConnectController()函数中传入的第一个参数。

也就是说ControllerHandle可以是ImageHandle。

当然由于并不是所有的DXE模块都有安装UEFIDriver Model所有并不是所有ImageHandle都是ControllerHandle。

 

另外还需要说明,UEFI Driver Model对应一个EFI_DRIVER_BINDING_PROTOCOL:

//// Simple Network Protocol Driver Global Variables//EFI_DRIVER_BINDING_PROTOCOL gSimpleNetworkDriverBinding = {  SimpleNetworkDriverSupported,  SimpleNetworkDriverStart,  SimpleNetworkDriverStop,  0xa,  NULL,  NULL};

也就是SimpleNetworkDriverStart()函数的第一个参数。

 

这里还需要说一下SimpleNetworkDriverSupported()函数。它是一个条件判断函数,只有其中的函数满足了要求,才会运行对应的Start()函数,比如:

EFI_STATUSEFIAPISimpleNetworkDriverSupported (  IN EFI_DRIVER_BINDING_PROTOCOL    *This,  IN EFI_HANDLE                     Controller,  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath  ){  EFI_STATUS                                Status;  EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *NiiProtocol;  PXE_UNDI                                  *Pxe;  Status = gBS->OpenProtocol (                  Controller,                  &gEfiDevicePathProtocolGuid,                  NULL,                  This->DriverBindingHandle,                  Controller,                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL                  );  if (EFI_ERROR (Status)) {    return Status;  }  Status = gBS->OpenProtocol (                  Controller,                  &gEfiNetworkInterfaceIdentifierProtocolGuid_31,                  (VOID **) &NiiProtocol,                  This->DriverBindingHandle,                  Controller,                  EFI_OPEN_PROTOCOL_BY_DRIVER                  );

这里的Controller需要安装了DevicePathProtocol和NetworkInterfaceIdentifierProtocol这两个Protocol。

因此就需要在之前的某个模块中,比如某个网卡模块,它需要安装了这两个协议,然后才能使网卡模块对应的EFI_HANDLE之上安装SNP这样的网络传输协议。

 

有个问题?

网卡运行起来后安装NetworkInterfaceIdentifierProtocol是没有问题的,但是DevicePathProtocol需要在什么时候安装呢?PCI扫描的时候?

可能也是在网卡驱动运行的时候吧……

需要有源代码才好。

据我所知,网卡驱动也是UEFI Driver Model。

 

说到DevicePathProtocol,就需要提供了另一种gBS->ConnectController()的调用方式了:

  Status = gBS->LocateDevicePath (                  &gEfiDevicePathProtocolGuid,                  &TempPciDevicePath,                  &PciDeviceHandle                  );  if (EFI_ERROR (Status)) {    return Status;  }  gBS->ConnectController (PciDeviceHandle, NULL, MyDevicePath, FALSE);

这里就是根据安装有DevicePathProtocol的EFI_HANDLE来ConnectController。

 

追踪代码可以看到在PCI扫描的时候有:

RegisterPciDevice (  IN  EFI_HANDLE          Controller,  IN  PCI_IO_DEVICE       *PciIoDevice,  OUT EFI_HANDLE          *Handle      OPTIONAL  ){  EFI_STATUS          Status;  VOID                *PlatformOpRomBuffer;  UINTN               PlatformOpRomSize;  UINT8               PciExpressCapRegOffset;  EFI_PCI_IO_PROTOCOL *PciIo;  UINT8               Data8;  BOOLEAN             HasEfiImage;  //  // Install the pciio protocol, device path protocol  //  Status = gBS->InstallMultipleProtocolInterfaces (                  &PciIoDevice->Handle,                  &gEfiDevicePathProtocolGuid,                  PciIoDevice->DevicePath,                  &gEfiPciIoProtocolGuid,                  &PciIoDevice->PciIo,                  NULL                  );  if (EFI_ERROR (Status)) {    return Status;  }

所以在PCI扫描的时候,也会为设备创建EFI_HANDLE并安装对应的DevicePathProtocol。

 

总结:

ControllerHandle有两个主要来源,一个是ImageHandle,另外一个就是PCI扫描的时候创建的EFI_HANDLE。

其它还有没有不是很确定了。比如ChildHandle,不知道是不是也有一部分可以做ControllerHandle?


0 0
原创粉丝点击