UEFI基础

UEFI基础知识

启动过程

SEC->PEI->DXE->BDS-> TSL->RT->AL

UEFI组成

UEFI提供给操作系统的接口有启动服务(boot services, BS)和运行时服务(Runtime Servcie,RT),以及BS的protocol。
TSL阶段 --BS&RT--> OS Loader(Grub) ->ExitBootServices() -> Runtime 阶段

几个重要Phase

  1. EndOfPei:在HandOffToDxeCore时被安装
  2. EndOfDxe
  3. ReadyToBoot
  4. ExitBootServices:OS Loader调用

DXE 阶段提供的服务

BootService提供的服务:
事件服务;内存管理;Protocol管理和使用;驱动管理(connect,disconnect); Image 管理;ExitBootService;

BootService
 //
39  // DXE Core Module Variables
40  //
41  EFI_BOOT_SERVICES  mBootServices = {
42    {
43      EFI_BOOT_SERVICES_SIGNATURE,                                                          // Signature
44      EFI_BOOT_SERVICES_REVISION,                                                           // Revision
45      sizeof (EFI_BOOT_SERVICES),                                                           // HeaderSize
46      0,                                                                                    // CRC32
47      0                                                                                     // Reserved
48    },
49    (EFI_RAISE_TPL)CoreRaiseTpl,                                                            // RaiseTPL
50    (EFI_RESTORE_TPL)CoreRestoreTpl,                                                        // RestoreTPL
51    (EFI_ALLOCATE_PAGES)CoreAllocatePages,                                                  // AllocatePages
52    (EFI_FREE_PAGES)CoreFreePages,                                                          // FreePages
53    (EFI_GET_MEMORY_MAP)CoreGetMemoryMap,                                                   // GetMemoryMap
54    (EFI_ALLOCATE_POOL)CoreAllocatePool,                                                    // AllocatePool
55    (EFI_FREE_POOL)CoreFreePool,                                                            // FreePool
56    (EFI_CREATE_EVENT)CoreCreateEvent,                                                      // CreateEvent
57    (EFI_SET_TIMER)CoreSetTimer,                                                            // SetTimer
58    (EFI_WAIT_FOR_EVENT)CoreWaitForEvent,                                                   // WaitForEvent
59    (EFI_SIGNAL_EVENT)CoreSignalEvent,                                                      // SignalEvent
60    (EFI_CLOSE_EVENT)CoreCloseEvent,                                                        // CloseEvent
61    (EFI_CHECK_EVENT)CoreCheckEvent,                                                        // CheckEvent
62    (EFI_INSTALL_PROTOCOL_INTERFACE)CoreInstallProtocolInterface,                           // InstallProtocolInterface
63    (EFI_REINSTALL_PROTOCOL_INTERFACE)CoreReinstallProtocolInterface,                       // ReinstallProtocolInterface
64    (EFI_UNINSTALL_PROTOCOL_INTERFACE)CoreUninstallProtocolInterface,                       // UninstallProtocolInterface
65    (EFI_HANDLE_PROTOCOL)CoreHandleProtocol,                                                // HandleProtocol
66    (VOID *)NULL,                                                                           // Reserved
67    (EFI_REGISTER_PROTOCOL_NOTIFY)CoreRegisterProtocolNotify,                               // RegisterProtocolNotify
68    (EFI_LOCATE_HANDLE)CoreLocateHandle,                                                    // LocateHandle
69    (EFI_LOCATE_DEVICE_PATH)CoreLocateDevicePath,                                           // LocateDevicePath
70    (EFI_INSTALL_CONFIGURATION_TABLE)CoreInstallConfigurationTable,                         // InstallConfigurationTable
71    (EFI_IMAGE_LOAD)CoreLoadImage,                                                          // LoadImage
72    (EFI_IMAGE_START)CoreStartImage,                                                        // StartImage
73    (EFI_EXIT)CoreExit,                                                                     // Exit
74    (EFI_IMAGE_UNLOAD)CoreUnloadImage,                                                      // UnloadImage
75    (EFI_EXIT_BOOT_SERVICES)CoreExitBootServices,                                           // ExitBootServices
76    (EFI_GET_NEXT_MONOTONIC_COUNT)CoreEfiNotAvailableYetArg1,                               // GetNextMonotonicCount
77    (EFI_STALL)CoreStall,                                                                   // Stall
78    (EFI_SET_WATCHDOG_TIMER)CoreSetWatchdogTimer,                                           // SetWatchdogTimer
79    (EFI_CONNECT_CONTROLLER)CoreConnectController,                                          // ConnectController
80    (EFI_DISCONNECT_CONTROLLER)CoreDisconnectController,                                    // DisconnectController
81    (EFI_OPEN_PROTOCOL)CoreOpenProtocol,                                                    // OpenProtocol
82    (EFI_CLOSE_PROTOCOL)CoreCloseProtocol,                                                  // CloseProtocol
83    (EFI_OPEN_PROTOCOL_INFORMATION)CoreOpenProtocolInformation,                             // OpenProtocolInformation
84    (EFI_PROTOCOLS_PER_HANDLE)CoreProtocolsPerHandle,                                       // ProtocolsPerHandle
85    (EFI_LOCATE_HANDLE_BUFFER)CoreLocateHandleBuffer,                                       // LocateHandleBuffer
86    (EFI_LOCATE_PROTOCOL)CoreLocateProtocol,                                                // LocateProtocol
87    (EFI_INSTALL_MULTIPLE_PROTOCOL_INTERFACES)CoreInstallMultipleProtocolInterfaces,        // InstallMultipleProtocolInterfaces
88    (EFI_UNINSTALL_MULTIPLE_PROTOCOL_INTERFACES)CoreUninstallMultipleProtocolInterfaces,    // UninstallMultipleProtocolInterfaces
89    (EFI_CALCULATE_CRC32)CoreEfiNotAvailableYetArg3,                                        // CalculateCrc32
90    (EFI_COPY_MEM)CopyMem,                                                                  // CopyMem
91    (EFI_SET_MEM)SetMem,                                                                    // SetMem
92    (EFI_CREATE_EVENT_EX)CoreCreateEventEx                                                  // CreateEventEx
93  };

Runtime阶段提供的服务

Runtime Service:
时间,读写变量,虚拟内存,etc

Runtime Service
EFI_RUNTIME_SERVICES  mEfiRuntimeServicesTableTemplate = {
  {
    EFI_RUNTIME_SERVICES_SIGNATURE,                               // Signature
    EFI_RUNTIME_SERVICES_REVISION,                                // Revision
    sizeof (EFI_RUNTIME_SERVICES),                                // HeaderSize
    0,                                                            // CRC32
    0                                                             // Reserved
  },
  (EFI_GET_TIME)CoreEfiNotAvailableYetArg2,                       // GetTime
  (EFI_SET_TIME)CoreEfiNotAvailableYetArg1,                       // SetTime
  (EFI_GET_WAKEUP_TIME)CoreEfiNotAvailableYetArg3,                // GetWakeupTime
  (EFI_SET_WAKEUP_TIME)CoreEfiNotAvailableYetArg2,                // SetWakeupTime
  (EFI_SET_VIRTUAL_ADDRESS_MAP)CoreEfiNotAvailableYetArg4,        // SetVirtualAddressMap
  (EFI_CONVERT_POINTER)CoreEfiNotAvailableYetArg2,                // ConvertPointer
  (EFI_GET_VARIABLE)CoreEfiNotAvailableYetArg5,                   // GetVariable
  (EFI_GET_NEXT_VARIABLE_NAME)CoreEfiNotAvailableYetArg3,         // GetNextVariableName
  (EFI_SET_VARIABLE)CoreEfiNotAvailableYetArg5,                   // SetVariable
  (EFI_GET_NEXT_HIGH_MONO_COUNT)CoreEfiNotAvailableYetArg1,       // GetNextHighMonotonicCount
  (EFI_RESET_SYSTEM)CoreEfiNotAvailableYetArg4,                   // ResetSystem
  (EFI_UPDATE_CAPSULE)CoreEfiNotAvailableYetArg3,                 // UpdateCapsule
  (EFI_QUERY_CAPSULE_CAPABILITIES)CoreEfiNotAvailableYetArg4,     // QueryCapsuleCapabilities
  (EFI_QUERY_VARIABLE_INFO)CoreEfiNotAvailableYetArg4             // QueryVariableInfo
};

PEI阶段提供的服务

PPI, Pei 阶段读写变量是通过PPI来读写的。

Flags用于描述PPI的类型,常用的有下面几种:

  1. EFI_PEI_PPI_DESCRIPTOR_PPI,常用于Installppi,此时ppi是该Guid对应的一些函数,通过LocatePpi可以调用这些函数;
  2. EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK,常用于注册Guid对应的callback,此时ppi就是callback函数,一般用于notify。当调用notifyppi时,如果Guid已经被安装了,则直接执行callback,如果对应的Guid没有安装时,在调用Installppi,会自动调用callback函数。
点击查看代码
typedef struct {
  ///
  /// This field is a set of flags describing the characteristics of this imported table entry.
  /// All flags are defined as EFI_PEI_PPI_DESCRIPTOR_***, which can also be combined into one.
  ///
  UINTN       Flags;
  ///
  /// The address of the EFI_GUID that names the interface.
  ///
  EFI_GUID    *Guid;
  ///
  /// A pointer to the PPI. It contains the information necessary to install a service.
  ///
  VOID        *Ppi;
} EFI_PEI_PPI_DESCRIPTOR;
///
/// The data structure in a given PEIM that tells the PEI
/// Foundation where to invoke the notification service.
///
struct _EFI_PEI_NOTIFY_DESCRIPTOR {
  ///
  /// Details if the type of notification are callback or dispatch.
  ///
  UINTN                          Flags;
  ///
  /// The address of the EFI_GUID that names the interface.
  ///
  EFI_GUID                       *Guid;
  ///
  /// Address of the notification callback function itself within the PEIM.
  ///
  EFI_PEIM_NOTIFY_ENTRY_POINT    Notify;
};

///
/// This data structure is the means by which callable services are installed and
/// notifications are registered in the PEI phase.
///
typedef union {
  ///
  /// The typedef structure of the notification descriptor.
  ///
  EFI_PEI_NOTIFY_DESCRIPTOR    Notify;
  ///
  /// The typedef structure of the PPI descriptor.
  ///
  EFI_PEI_PPI_DESCRIPTOR       Ppi;
} EFI_PEI_DESCRIPTOR;

PPI在PeiCore的全局变量PeiCore Instance中

PeiCore全局变量

PeiCore PrivateData
///
/// Pei Core private data structure instance
///
struct _PEI_CORE_INSTANCE {
  UINTN                             Signature;

  ///
  /// Point to ServiceTableShadow
  ///
  EFI_PEI_SERVICES                  *Ps;
  PEI_PPI_DATABASE                  PpiData;

  ///
  /// The count of FVs which contains FFS and could be dispatched by PeiCore.
  ///
  UINTN                             FvCount;

  ///
  /// The max count of FVs which contains FFS and could be dispatched by PeiCore.
  ///
  UINTN                             MaxFvCount;

  ///
  /// Pointer to the buffer with the MaxFvCount number of entries.
  /// Each entry is for one FV which contains FFS and could be dispatched by PeiCore.
  ///
  PEI_CORE_FV_HANDLE                *Fv;

  ///
  /// Pointer to the buffer with the MaxUnknownFvInfoCount number of entries.
  /// Each entry is for one FV which could not be dispatched by PeiCore.
  ///
  PEI_CORE_UNKNOW_FORMAT_FV_INFO    *UnknownFvInfo;
  UINTN                             MaxUnknownFvInfoCount;
  UINTN                             UnknownFvInfoCount;

  ///
  /// Pointer to the buffer FvFileHandlers in PEI_CORE_FV_HANDLE specified by CurrentPeimFvCount.
  ///
  EFI_PEI_FILE_HANDLE               *CurrentFvFileHandles;
  UINTN                             AprioriCount;
  UINTN                             CurrentPeimFvCount;
  UINTN                             CurrentPeimCount;
  EFI_PEI_FILE_HANDLE               CurrentFileHandle;
  BOOLEAN                           PeimNeedingDispatch;
  BOOLEAN                           PeimDispatchOnThisPass;
  BOOLEAN                           PeimDispatcherReenter;
  EFI_PEI_HOB_POINTERS              HobList;
  BOOLEAN                           SwitchStackSignal;
  BOOLEAN                           PeiMemoryInstalled;
  VOID                              *CpuIo;
  EFI_PEI_SECURITY2_PPI             *PrivateSecurityPpi;
  EFI_PEI_SERVICES                  ServiceTableShadow;
  EFI_PEI_PPI_DESCRIPTOR            *XipLoadFile;
  EFI_PHYSICAL_ADDRESS              PhysicalMemoryBegin;
  UINT64                            PhysicalMemoryLength;
  EFI_PHYSICAL_ADDRESS              FreePhysicalMemoryTop;
  UINTN                             HeapOffset;
  BOOLEAN                           HeapOffsetPositive;
  UINTN                             StackOffset;
  BOOLEAN                           StackOffsetPositive;
  //
  // Information for migrating memory pages allocated in pre-memory phase.
  //
  HOLE_MEMORY_DATA                  MemoryPages;
  PEICORE_FUNCTION_POINTER          ShadowedPeiCore;
  CACHE_SECTION_DATA                CacheSection;
  //
  // For Loading modules at fixed address feature to cache the top address below which the
  // Runtime code, boot time code and PEI memory will be placed. Please note that the offset between this field
  // and Ps should not be changed since maybe user could get this top address by using the offset to Ps.
  //
  EFI_PHYSICAL_ADDRESS              LoadModuleAtFixAddressTopAddress;
  //
  // The field is define for Loading modules at fixed address feature to tracker the PEI code
  // memory range usage. It is a bit mapped array in which every bit indicates the corresponding memory page
  // available or not.
  //
  UINT64                            *PeiCodeMemoryRangeUsageBitMap;
  //
  // This field points to the shadowed image read function
  //
  PE_COFF_LOADER_READ_FILE          ShadowedImageRead;

  UINTN                             TempPeimCount;

  //
  // Pointer to the temp buffer with the TempPeimCount number of entries.
  //
  EFI_PEI_FILE_HANDLE               *TempFileHandles;
  //
  // Pointer to the temp buffer with the TempPeimCount number of entries.
  //
  EFI_GUID                          *TempFileGuid;

  //
  // Temp Memory Range is not covered by PeiTempMem and Stack.
  // Those Memory Range will be migrated into physical memory.
  //
  HOLE_MEMORY_DATA                  HoleData[HOLE_MAX_NUMBER];
};

Pei Service

Pei Service
///
/// Pei service instance
///
EFI_PEI_SERVICES  gPs = {
  {
    PEI_SERVICES_SIGNATURE,
    PEI_SERVICES_REVISION,
    sizeof (EFI_PEI_SERVICES),
    0,
    0
  },
  PeiInstallPpi,
  PeiReInstallPpi,
  PeiLocatePpi,
  PeiNotifyPpi,

  PeiGetBootMode,
  PeiSetBootMode,

  PeiGetHobList,
  PeiCreateHob,

  PeiFfsFindNextVolume,
  PeiFfsFindNextFile,
  PeiFfsFindSectionData,

  PeiInstallPeiMemory,
  PeiAllocatePages,
  PeiAllocatePool,
  (EFI_PEI_COPY_MEM)CopyMem,
  (EFI_PEI_SET_MEM)SetMem,

  PeiReportStatusCode,
  PeiResetSystem,

  &gPeiDefaultCpuIoPpi,
  &gPeiDefaultPciCfg2Ppi,

  PeiFfsFindFileByName,
  PeiFfsGetFileInfo,
  PeiFfsGetVolumeInfo,
  PeiRegisterForShadow,
  PeiFfsFindSectionData3,
  PeiFfsGetFileInfo2,
  PeiResetSystem2,
  PeiFreePages,
};

UEFI —— Status Code用法

PEI 阶段用法

是PeiServices中的一个组成。
code: Edk2\MdeModulePkg\Universal\ReportStatusCodeRouter\Pei\ReportStatusCodeRouterPei.c
整体逻辑:

  1. 调用ReportStatusCode
  2. 挂在gEfiPeiRscHandlerPpiGuid上的handler都会跑一便
  3. 执行对应的callback

gEfiPeiRscHandlerPpiGuid 用于注册和卸载handler
gEfiPeiStatusCodePpiGuid 用于当有人调用ReportStatusCode的时候,搜寻并执行gEfiPeiRscHandlerPpiGuid 注册的所有handler

Ppi
EFI_PEI_RSC_HANDLER_PPI  mRscHandlerPpi = {
  Register,
  Unregister
};

EFI_PEI_PROGRESS_CODE_PPI  mStatusCodePpi = {
  ReportDispatcher
};

EFI_PEI_PPI_DESCRIPTOR  mRscHandlerPpiList[] = {
  {
    EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
    &gEfiPeiRscHandlerPpiGuid,
    &mRscHandlerPpi
  }
};

EFI_PEI_PPI_DESCRIPTOR  mStatusCodePpiList[] = {
  {
    EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
    &gEfiPeiStatusCodePpiGuid,
    &mStatusCodePpi
  }
};

Runtime,DXE阶段

Protocol

EFI_STATUS_CODE_PROTOCOL  mStatusCodeProtocol = {
  ReportDispatcher
};

EFI_RSC_HANDLER_PROTOCOL  mRscHandlerProtocol = {
  Register,
  Unregister
};

SMM 阶段

Protocol
EFI_MM_STATUS_CODE_PROTOCOL  mSmmStatusCodeProtocol = {
  ReportDispatcher
};

EFI_MM_RSC_HANDLER_PROTOCOL  mSmmRscHandlerProtocol = {
  Register,
  Unregister
};

Event 的使用方法

ref:Edk2\MdeModulePkg\Core\Dxe\Event\Event.c

  1. CreateEvent的用法
    a. 如果是EVT_NOTIFY_SIGNAL,就入队gEventSignalQueue

    点击查看代码
      if ((Type & EVT_NOTIFY_SIGNAL) != 0x00000000) {
        //
        // The Event's NotifyFunction must be queued whenever the event is signaled
        //
        InsertHeadList (&gEventSignalQueue, &IEvent->SignalLink);
      }
    
  2. SignalEvent&NotifyEvent的用法
    a. gEventSignalQueue,以组为单位筛选Event,然后再用gEventQueue筛选Event。
    b. gEventQueue,以Tpl为key记录的队列。

  3. 在CoreInstallProtocolInterface/Reintall中中,类似于PPI,也会notify对应Protocol Guid的event.

    代码如下
    VOID
    CoreNotifyProtocolEntry (
      IN PROTOCOL_ENTRY  *ProtEntry
      )
    {
      PROTOCOL_NOTIFY  *ProtNotify;
      LIST_ENTRY       *Link;
    
      ASSERT_LOCKED (&gProtocolDatabaseLock);
    
      for (Link = ProtEntry->Notify.ForwardLink; Link != &ProtEntry->Notify; Link = Link->ForwardLink) {
        ProtNotify = CR (Link, PROTOCOL_NOTIFY, Link, PROTOCOL_NOTIFY_SIGNATURE);
        CoreSignalEvent (ProtNotify->Event);
      }
    }
    
    

示例一

先创建一个event,然后把event挂在一个Protocol的GUID上,之后在install protocol时会调用notifyEvent,或者SignalEvent

  Status = gBS->CreateEvent (
                             EVT_NOTIFY_SIGNAL,
                             TPL_NOTIFY,
                             DxeNotifyCallback,
                             NULL,
                             &NotifyEvent
                             );

  if (!EFI_ERROR (Status)) {
    Status = gBS->RegisterProtocolNotify (
                                          ProtocolGuid,
                                          NotifyEvent,
                                          &Registration
                                          );

示例二

先CreateEvent,然后SignalEvent,最后CloseEvent
  Status = gBS->CreateEventEx (
                  EVT_NOTIFY_SIGNAL,
                  TPL_CALLBACK,
                  InternalBdsEmptyCallbackFuntion,
                  NULL,
                  &gEfiEndOfDxeEventGroupGuid,
                  &EndOfDxeEvent
                  );
  ASSERT_EFI_ERROR (Status);
  gBS->SignalEvent (EndOfDxeEvent);
  gBS->CloseEvent (EndOfDxeEvent);
  DEBUG((DEBUG_INFO,"All EndOfDxe callbacks have returned successfully\n"));

示例三

先CreateEvent,然后WaitForEvent
/**
  Function waits for a given event to fire, or for an optional timeout to expire.

  @param   Event              The event to wait for
  @param   Timeout            An optional timeout value in 100 ns units.

  @retval  EFI_SUCCESS      Event fired before Timeout expired.
  @retval  EFI_TIME_OUT     Timout expired before Event fired..

**/
EFI_STATUS
BdsWaitForSingleEvent (
  IN  EFI_EVENT  Event,
  IN  UINT64     Timeout       OPTIONAL
  )
{
  UINTN       Index;
  EFI_STATUS  Status;
  EFI_EVENT   TimerEvent;
  EFI_EVENT   WaitList[2];

  if (Timeout != 0) {
    //
    // Create a timer event
    //
    Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent);
    if (!EFI_ERROR (Status)) {
      //
      // Set the timer event
      //
      gBS->SetTimer (
             TimerEvent,
             TimerRelative,
             Timeout
             );

      //
      // Wait for the original event or the timer
      //
      WaitList[0] = Event;
      WaitList[1] = TimerEvent;
      Status      = gBS->WaitForEvent (2, WaitList, &Index);
      ASSERT_EFI_ERROR (Status);
      gBS->CloseEvent (TimerEvent);

      //
      // If the timer expired, change the return to timed out
      //
      if (Index == 1) {
        Status = EFI_TIMEOUT;
      }
    }
  } else {
    //
    // No timeout... just wait on the event
    //
    Status = gBS->WaitForEvent (1, &Event, &Index);
    ASSERT (!EFI_ERROR (Status));
    ASSERT (Index == 0);
  }

  return Status;
}
posted @ 2024-09-06 14:51  nipper  阅读(44)  评论(0编辑  收藏  举报