目录

UEFI BDS 阶段代码梳理


DXE Install BDS Protocol

  1. 在DXE阶段的Main中,调用CoreDispatcher() -> CoreStartImage(),进入各DXE driver的入口函数:
1
2
3
// MdeModulePkg/Core/Dxe/DxeMain/DxeMain.c

Image->Status = Image->EntryPoint (ImageHandle, Image->Info.SystemTable);

此阶段会load并执行各DXE的驱动程序,而BDS就属于DXE的一个驱动,所以BdsInitialize()这个函数会在此时被执行:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// MdeModulePkg/Universal/BdsDxe/BdsDxe.inf

[Defines]
INF_VERSION                    = 0x00010005
BASE_NAME                      = BdsDxe
MODULE_UNI_FILE                = BdsDxe.uni
FILE_GUID                      = 6D33944A-EC75-4855-A54D-809C75241F6C
MODULE_TYPE                    = DXE_DRIVER                            // BDS是DXE驱动
VERSION_STRING                 = 1.0
ENTRY_POINT                    = BdsInitialize                         // 驱动入口函数

入口函数BdsInitialize()调用InstallMultipleProtocolInterfaces()注册BdsArchProtocol到DXE Core,该Protocol的Guid为gEfiBdsArchProtocolGuid*gBds是一个指向BdsArchProtocol的指针,在MdeModulePkg/Core/Dxe/DxeMain/DxeMain.c开始时定义:

1
2
3
// `MdeModulePkg/Core/Dxe/DxeMain/DxeMain.c

EFI_BDS_ARCH_PROTOCOL             *gBds           = NULL;

BdsEntry.c中BDS初始化阶段BdsInitialize*gBds指针作为InstallMultipleProtocolInterfaces()的参数,来将入口函数BdsEntry注册到PROTOCOL_INTERFACE中:

1
2
3
4
// MdeModulePkg/Universal/BdsDxe/BdsEntry.c
EFI_BDS_ARCH_PROTOCOL  gBds = {
BdsEntry
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// MdeModulePkg/Universal/BdsDxe/BdsEntry.c

// Install protocol interface
Handle = NULL;
Status = gBS->InstallMultipleProtocolInterfaces (
                &Handle,
                &gEfiBdsArchProtocolGuid,
                &gBds,
                NULL
                );

需要注意的是:在BdsEntry.c中的gBds只是为了注册,而在DxeMain()最后进入Bds阶段使用的指针是在别的地方赋值的。Dxe中有如下结构体:

1
2
3
4
5
6
7
8
EFI_CORE_PROTOCOL_NOTIFY_ENTRY  mArchProtocols[] = {
  { &gEfiSecurityArchProtocolGuid,         (VOID **)&gSecurity,      NULL, NULL, FALSE },
  { &gEfiCpuArchProtocolGuid,              (VOID **)&gCpu,           NULL, NULL, FALSE },
  { &gEfiMetronomeArchProtocolGuid,        (VOID **)&gMetronome,     NULL, NULL, FALSE },
  { &gEfiTimerArchProtocolGuid,            (VOID **)&gTimer,         NULL, NULL, FALSE },
  { &gEfiBdsArchProtocolGuid,              (VOID **)&gBds,           NULL, NULL, FALSE },
  ...
  }

首先通过这个数组将*gBdsgEfiBdsArchProtocolGuid建立起联系,然后在Dxe阶段的Main函数中,通过CoreNotifyOnProtocolInstallation()->CoreNotifyOnProtocolEntryTable()创建事件(Event),这里传入的Entry为mArchProtocols,最终由GenericProtocolNotify()函数给mArchProtocols的每一个成员赋值。这里,CoreCreatEvent()CoreRegisterProtocolNotify成对出现,一旦Protocol安装则执行回调函数GenericProtocolNotify()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
Creates an event for each entry in a table that is fired everytime a Protocol
of a specific type is installed.

@param Entry  Pointer to EFI_CORE_PROTOCOL_NOTIFY_ENTRY.

**/
VOID
CoreNotifyOnProtocolEntryTable (
  EFI_CORE_PROTOCOL_NOTIFY_ENTRY  *Entry
  )
{
  EFI_STATUS  Status;

  for ( ; Entry->ProtocolGuid != NULL; Entry++) {
    //
    // Create the event
    //
    Status = CoreCreateEvent (
             EVT_NOTIFY_SIGNAL,
             TPL_CALLBACK,
             GenericProtocolNotify,
             Entry,
             &Entry->Event
             );
    ASSERT_EFI_ERROR (Status);

    //
    // Register for protocol notifactions on this event
    //
    Status = CoreRegisterProtocolNotify (
             Entry->ProtocolGuid,
             Entry->Event,
             &Entry->Registration
             );
    ASSERT_EFI_ERROR (Status);
  }
}

在回调函数GenericProtocolNotify中,Bds入口函数BdsEntry()被locate,然后被赋值给*gBds

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
//
// See if the expected protocol is present in the handle database
//
Status = CoreLocateProtocol (Entry->ProtocolGuid, Entry->Registration, &Protocol);
if (EFI_ERROR (Status)) {
  return;
}
// Mark the protocol as present
Entry->Present = TRUE;
// Update protocol global variable if one exists. Entry->Protocol points to a global variable
// if one exists in the DXE core for this Architectural Protocol
if (Entry->Protocol != NULL) {
  *(Entry->Protocol) = Protocol;
}

最后,在Bds的入口函数已经被安装后,DxeMain()的最后通过*gBds指针进入Bds的入口BdsEntry()

1
gBds->Entry (gBds);

再讨论一下BDS的Protocol,EFI_BDS_ARCH_PROTOCOL是在Bds.h中定义的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// MdePkg/Include/Bds.h
typedef struct _EFI_BDS_ARCH_PROTOCOL EFI_BDS_ARCH_PROTOCOL;

typedef
VOID
(EFIAPI *EFI_BDS_ENTRY)(
  IN EFI_BDS_ARCH_PROTOCOL  *This
);

struct _EFI_BDS_ARCH_PROTOCOL {
  EFI_BDS_ENTRY    Entry;
};

其中,EFI_BDS_ENTRY 就是这个protocol的一个服务,对应了一个PROTOCOL_INTERFACE。在之前的InstallMultipleProtocolInterfaces()函数中,Entry函数被指向EFI_BDS_ARCH_PROTOCOL这个Protocol的例化PROTOCOL_INTERFACE中的*Interface指针。所以根据Bds的Guid EFI_BDS_ARCH_PROTOCOL_GUID可以找到EFI_BDS_ARCH_PROTOCOL这个Protocol,再根据这个Protocol找到PROTOCOL_INTERFACE实现,就可以找到具体的被注册的BdsEntry()函数了