BdsEntry
代码
VOID
EFIAPI
BdsEntry (IN EFI_BDS_ARCH_PROTOCOL *This)
{EFI_BOOT_MANAGER_LOAD_OPTION *LoadOptions;UINTN LoadOptionCount;CHAR16 *FirmwareVendor;EFI_EVENT HotkeyTriggered;UINT64 OsIndication;UINTN DataSize;EFI_STATUS Status;UINT32 BootOptionSupport;UINT16 BootTimeOut;EDKII_VARIABLE_POLICY_PROTOCOL *VariablePolicy;UINTN Index;EFI_BOOT_MANAGER_LOAD_OPTION LoadOption;UINT16 *BootNext;CHAR16 BootNextVariableName[sizeof ("Boot####")];EFI_BOOT_MANAGER_LOAD_OPTION BootManagerMenu;BOOLEAN BootFwUi;BOOLEAN PlatformRecovery;BOOLEAN BootSuccess;EFI_DEVICE_PATH_PROTOCOL *FilePath;EFI_STATUS BootManagerMenuStatus;EFI_BOOT_MANAGER_LOAD_OPTION PlatformDefaultBootOption;BOOLEAN PlatformDefaultBootOptionValid;HotkeyTriggered = NULL;Status = EFI_SUCCESS;BootSuccess = FALSE;//// Insert the performance probe//PERF_CROSSMODULE_END ("DXE");PERF_CROSSMODULE_BEGIN ("BDS");DEBUG ((DEBUG_INFO, "[Bds] Entry...\n"));//// Fill in FirmwareVendor and FirmwareRevision from PCDs//FirmwareVendor = (CHAR16 *)PcdGetPtr (PcdFirmwareVendor);gST->FirmwareVendor = AllocateRuntimeCopyPool (StrSize (FirmwareVendor), FirmwareVendor);ASSERT (gST->FirmwareVendor != NULL);gST->FirmwareRevision = PcdGet32 (PcdFirmwareRevision);//// Fixup Tasble CRC after we updated Firmware Vendor and Revision//gST->Hdr.CRC32 = 0;gBS->CalculateCrc32 ((VOID *)gST, sizeof (EFI_SYSTEM_TABLE), &gST->Hdr.CRC32);//// Validate Variable.//BdsFormalizeEfiGlobalVariable ();//// Mark the read-only variables if the Variable Lock protocol exists//Status = gBS->LocateProtocol (&gEdkiiVariablePolicyProtocolGuid, NULL, (VOID **)&VariablePolicy);DEBUG ((DEBUG_INFO, "[BdsDxe] Locate Variable Policy protocol - %r\n", Status));if (!EFI_ERROR (Status)) {for (Index = 0; Index < ARRAY_SIZE (mReadOnlyVariables); Index++) {Status = RegisterBasicVariablePolicy (VariablePolicy,&gEfiGlobalVariableGuid,mReadOnlyVariables[Index],VARIABLE_POLICY_NO_MIN_SIZE,VARIABLE_POLICY_NO_MAX_SIZE,VARIABLE_POLICY_NO_MUST_ATTR,VARIABLE_POLICY_NO_CANT_ATTR,VARIABLE_POLICY_TYPE_LOCK_NOW);ASSERT_EFI_ERROR (Status);}}InitializeHwErrRecSupport ();//// Initialize L"Timeout" EFI global variable.//BootTimeOut = PcdGet16 (PcdPlatformBootTimeOut);if (BootTimeOut != 0xFFFF) {//// If time out value equal 0xFFFF, no need set to 0xFFFF to variable area because UEFI specification// define same behavior between no value or 0xFFFF value for L"Timeout".//BdsDxeSetVariableAndReportStatusCodeOnError (EFI_TIME_OUT_VARIABLE_NAME,&gEfiGlobalVariableGuid,EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,sizeof (UINT16),&BootTimeOut);}//// Initialize L"BootOptionSupport" EFI global variable.// Lazy-ConIn implictly disables BDS hotkey.//BootOptionSupport = EFI_BOOT_OPTION_SUPPORT_APP | EFI_BOOT_OPTION_SUPPORT_SYSPREP;if (!PcdGetBool (PcdConInConnectOnDemand)) {BootOptionSupport |= EFI_BOOT_OPTION_SUPPORT_KEY;SET_BOOT_OPTION_SUPPORT_KEY_COUNT (BootOptionSupport, 3);}Status = gRT->SetVariable (EFI_BOOT_OPTION_SUPPORT_VARIABLE_NAME,&gEfiGlobalVariableGuid,EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,sizeof (BootOptionSupport),&BootOptionSupport);//// Platform needs to make sure setting volatile variable before calling 3rd party code shouldn't fail.//ASSERT_EFI_ERROR (Status);//// Cache the "BootNext" NV variable before calling any PlatformBootManagerLib APIs// This could avoid the "BootNext" set by PlatformBootManagerLib be consumed in this boot.//GetEfiGlobalVariable2 (EFI_BOOT_NEXT_VARIABLE_NAME, (VOID **)&BootNext, &DataSize);if (DataSize != sizeof (UINT16)) {if (BootNext != NULL) {FreePool (BootNext);}BootNext = NULL;}//// Initialize the platform language variables//InitializeLanguage (TRUE);FilePath = FileDevicePath (NULL, EFI_REMOVABLE_MEDIA_FILE_NAME);if (FilePath == NULL) {DEBUG ((DEBUG_ERROR, "Fail to allocate memory for default boot file path. Unable to boot.\n"));CpuDeadLoop ();}PlatformDefaultBootOptionValid = EfiBootManagerInitializeLoadOption (&PlatformDefaultBootOption,LoadOptionNumberUnassigned,LoadOptionTypePlatformRecovery,LOAD_OPTION_ACTIVE,L"Default PlatformRecovery",FilePath,NULL,0) == EFI_SUCCESS;ASSERT (PlatformDefaultBootOptionValid == TRUE);//// System firmware must include a PlatformRecovery#### variable specifying// a short-form File Path Media Device Path containing the platform default// file path for removable media if the platform supports Platform Recovery.//if (PlatformDefaultBootOptionValid && PcdGetBool (PcdPlatformRecoverySupport)) {LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypePlatformRecovery);if (EfiBootManagerFindLoadOption (&PlatformDefaultBootOption, LoadOptions, LoadOptionCount) == -1) {for (Index = 0; Index < LoadOptionCount; Index++) {//// The PlatformRecovery#### options are sorted by OptionNumber.// Find the the smallest unused number as the new OptionNumber.//if (LoadOptions[Index].OptionNumber != Index) {break;}}PlatformDefaultBootOption.OptionNumber = Index;Status = EfiBootManagerLoadOptionToVariable (&PlatformDefaultBootOption);ASSERT_EFI_ERROR (Status);}EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);}FreePool (FilePath);//// Report Status Code to indicate connecting drivers will happen//REPORT_STATUS_CODE (EFI_PROGRESS_CODE,(EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_BEGIN_CONNECTING_DRIVERS));//// Initialize ConnectConIn event before calling platform code.//if (PcdGetBool (PcdConInConnectOnDemand)) {Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL,TPL_CALLBACK,BdsDxeOnConnectConInCallBack,NULL,&gConnectConInEventGuid,&gConnectConInEvent);if (EFI_ERROR (Status)) {gConnectConInEvent = NULL;}}//// Do the platform init, can be customized by OEM/IBV// Possible things that can be done in PlatformBootManagerBeforeConsole:// > Update console variable: 1. include hot-plug devices; 2. Clear ConIn and add SOL for AMT// > Register new Driver#### or Boot####// > Register new Key####: e.g.: F12// > Signal ReadyToLock event// > Authentication action: 1. connect Auth devices; 2. Identify auto logon user.//PERF_INMODULE_BEGIN ("PlatformBootManagerBeforeConsole");PlatformBootManagerBeforeConsole ();PERF_INMODULE_END ("PlatformBootManagerBeforeConsole");//// Initialize hotkey service//EfiBootManagerStartHotkeyService (&HotkeyTriggered);//// Execute Driver Options//LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypeDriver);ProcessLoadOptions (LoadOptions, LoadOptionCount);EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);//// Connect consoles//PERF_INMODULE_BEGIN ("EfiBootManagerConnectAllDefaultConsoles");if (PcdGetBool (PcdConInConnectOnDemand)) {EfiBootManagerConnectConsoleVariable (ConOut);EfiBootManagerConnectConsoleVariable (ErrOut);//// Do not connect ConIn devices when lazy ConIn feature is ON.//} else {EfiBootManagerConnectAllDefaultConsoles ();}PERF_INMODULE_END ("EfiBootManagerConnectAllDefaultConsoles");//// Do the platform specific action after the console is ready// Possible things that can be done in PlatformBootManagerAfterConsole:// > Console post action:// > Dynamically switch output mode from 100x31 to 80x25 for certain senarino// > Signal console ready platform customized event// > Run diagnostics like memory testing// > Connect certain devices// > Dispatch aditional option roms// > Special boot: e.g.: USB boot, enter UI//PERF_INMODULE_BEGIN ("PlatformBootManagerAfterConsole");PlatformBootManagerAfterConsole ();PERF_INMODULE_END ("PlatformBootManagerAfterConsole");//// If any component set PcdTestKeyUsed to TRUE because use of a test key// was detected, then display a warning message on the debug log and the console//if (PcdGetBool (PcdTestKeyUsed)) {DEBUG ((DEBUG_ERROR, "**********************************\n"));DEBUG ((DEBUG_ERROR, "** WARNING: Test Key is used. **\n"));DEBUG ((DEBUG_ERROR, "**********************************\n"));Print (L"** WARNING: Test Key is used. **\n");}//// Boot to Boot Manager Menu when EFI_OS_INDICATIONS_BOOT_TO_FW_UI is set. Skip HotkeyBoot//DataSize = sizeof (UINT64);Status = gRT->GetVariable (EFI_OS_INDICATIONS_VARIABLE_NAME,&gEfiGlobalVariableGuid,NULL,&DataSize,&OsIndication);if (EFI_ERROR (Status)) {OsIndication = 0;}DEBUG_CODE_BEGIN ();EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LoadOptionType;DEBUG ((DEBUG_INFO, "[Bds]OsIndication: %016x\n", OsIndication));DEBUG ((DEBUG_INFO, "[Bds]=============Begin Load Options Dumping ...=============\n"));for (LoadOptionType = 0; LoadOptionType < LoadOptionTypeMax; LoadOptionType++) {DEBUG ((DEBUG_INFO," %s Options:\n",mBdsLoadOptionName[LoadOptionType]));LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionType);for (Index = 0; Index < LoadOptionCount; Index++) {DEBUG ((DEBUG_INFO," %s%04x: %s \t\t 0x%04x\n",mBdsLoadOptionName[LoadOptionType],LoadOptions[Index].OptionNumber,LoadOptions[Index].Description,LoadOptions[Index].Attributes));}EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);}DEBUG ((DEBUG_INFO, "[Bds]=============End Load Options Dumping=============\n"));DEBUG_CODE_END ();//// BootManagerMenu doesn't contain the correct information when return status is EFI_NOT_FOUND.//BootManagerMenuStatus = EfiBootManagerGetBootManagerMenu (&BootManagerMenu);BootFwUi = (BOOLEAN)((OsIndication & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) != 0);PlatformRecovery = (BOOLEAN)((OsIndication & EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY) != 0);//// Clear EFI_OS_INDICATIONS_BOOT_TO_FW_UI to acknowledge OS//if (BootFwUi || PlatformRecovery) {OsIndication &= ~((UINT64)(EFI_OS_INDICATIONS_BOOT_TO_FW_UI | EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY));Status = gRT->SetVariable (EFI_OS_INDICATIONS_VARIABLE_NAME,&gEfiGlobalVariableGuid,EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,sizeof (UINT64),&OsIndication);//// Changing the content without increasing its size with current variable implementation shouldn't fail.//ASSERT_EFI_ERROR (Status);}//// Launch Boot Manager Menu directly when EFI_OS_INDICATIONS_BOOT_TO_FW_UI is set. Skip HotkeyBoot//if (BootFwUi && (BootManagerMenuStatus != EFI_NOT_FOUND)) {//// Follow generic rule, Call BdsDxeOnConnectConInCallBack to connect ConIn before enter UI//if (PcdGetBool (PcdConInConnectOnDemand)) {BdsDxeOnConnectConInCallBack (NULL, NULL);}//// Directly enter the setup page.//EfiBootManagerBoot (&BootManagerMenu);}if (!PlatformRecovery) {//// Execute SysPrep####//LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypeSysPrep);ProcessLoadOptions (LoadOptions, LoadOptionCount);EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);//// Execute Key####//PERF_INMODULE_BEGIN ("BdsWait");BdsWait (HotkeyTriggered);PERF_INMODULE_END ("BdsWait");//// BdsReadKeys() can be removed after all keyboard drivers invoke callback in timer callback.//BdsReadKeys ();EfiBootManagerHotkeyBoot ();if (BootNext != NULL) {//// Delete "BootNext" NV variable before transferring control to it to prevent loops.//Status = gRT->SetVariable (EFI_BOOT_NEXT_VARIABLE_NAME,&gEfiGlobalVariableGuid,0,0,NULL);//// Deleting NV variable shouldn't fail unless it doesn't exist.//ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND);//// Boot to "BootNext"//UnicodeSPrint (BootNextVariableName, sizeof (BootNextVariableName), L"Boot%04x", *BootNext);Status = EfiBootManagerVariableToLoadOption (BootNextVariableName, &LoadOption);if (!EFI_ERROR (Status)) {EfiBootManagerBoot (&LoadOption);EfiBootManagerFreeLoadOption (&LoadOption);if ((LoadOption.Status == EFI_SUCCESS) &&(BootManagerMenuStatus != EFI_NOT_FOUND) &&(LoadOption.OptionNumber != BootManagerMenu.OptionNumber)){//// Boot to Boot Manager Menu upon EFI_SUCCESS// Exception: Do not boot again when the BootNext points to Boot Manager Menu.//EfiBootManagerBoot (&BootManagerMenu);}}}do {//// Retry to boot if any of the boot succeeds//LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypeBoot);BootSuccess = BootBootOptions (LoadOptions, LoadOptionCount, (BootManagerMenuStatus != EFI_NOT_FOUND) ? &BootManagerMenu : NULL);EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);} while (BootSuccess);}if (BootManagerMenuStatus != EFI_NOT_FOUND) {EfiBootManagerFreeLoadOption (&BootManagerMenu);}if (!BootSuccess) {if (PcdGetBool (PcdPlatformRecoverySupport)) {LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypePlatformRecovery);ProcessLoadOptions (LoadOptions, LoadOptionCount);EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);} else if (PlatformDefaultBootOptionValid) {//// When platform recovery is not enabled, still boot to platform default file path.//PlatformDefaultBootOptionValid = EfiBootManagerProcessLoadOption (&PlatformDefaultBootOption) == EFI_SUCCESS;}}if (PlatformDefaultBootOptionValid) {EfiBootManagerFreeLoadOption (&PlatformDefaultBootOption);}DEBUG ((DEBUG_ERROR, "[Bds] Unable to boot!\n"));PlatformBootManagerUnableToBoot ();CpuDeadLoop ();
}
流程
BdsEntry() 一被调用,就意味着固件正式进入了 BDS 阶段
1 插入性能检测接口
PERF_CROSSMODULE_END ("DXE");PERF_CROSSMODULE_BEGIN ("BDS");
performance (PERF)
2 填写固件供应商和固件版本信息
FirmwareVendor = (CHAR16 *)PcdGetPtr (PcdFirmwareVendor);gST->FirmwareVendor = AllocateRuntimeCopyPool (StrSize (FirmwareVendor), FirmwareVendor);ASSERT (gST->FirmwareVendor != NULL);gST->FirmwareRevision = PcdGet32 (PcdFirmwareRevision);
3 修复表CRC
gST->Hdr.CRC32 = 0;gBS->CalculateCrc32 ((VOID *)gST, sizeof (EFI_SYSTEM_TABLE), &gST->Hdr.CRC32);
4 固化/修复 EFI 全局变量
BdsFormalizeEfiGlobalVariable ();
4.1 固化控制台变量
BdsFormalizeConsoleVariable (EFI_CON_IN_VARIABLE_NAME);BdsFormalizeConsoleVariable (EFI_CON_OUT_VARIABLE_NAME);BdsFormalizeConsoleVariable (EFI_ERR_OUT_VARIABLE_NAME);


通过全局变量ID确定变量的值和大小
EFI_STATUS
EFIAPI
GetEfiGlobalVariable2 (IN CONST CHAR16 *Name,OUT VOID **Value,OUT UINTN *Size OPTIONAL)
{return GetVariable2 (Name, &gEfiGlobalVariableGuid, Value, Size);
}


4.2 固化与 OSIndication 相关的变量
BdsFormalizeOSIndicationVariable ();
OsIndications 是 UEFI 规范中定义的一个机制,用于在操作系统加载程序和固件之间传递特定的引导意图或状态信息。它是一个 64 位的位掩码,每个位代表一个特定的指示,操作系统可以通过设置这些位来通知固件某些特定的行为或需求
5 标记变量为只读
Status = gBS->LocateProtocol (&gEdkiiVariablePolicyProtocolGuid, NULL, (VOID **)&VariablePolicy);DEBUG ((DEBUG_INFO, "[BdsDxe] Locate Variable Policy protocol - %r\n", Status));if (!EFI_ERROR (Status)) {for (Index = 0; Index < ARRAY_SIZE (mReadOnlyVariables); Index++) {Status = RegisterBasicVariablePolicy (VariablePolicy,&gEfiGlobalVariableGuid,mReadOnlyVariables[Index],VARIABLE_POLICY_NO_MIN_SIZE,VARIABLE_POLICY_NO_MAX_SIZE,VARIABLE_POLICY_NO_MUST_ATTR,VARIABLE_POLICY_NO_CANT_ATTR,VARIABLE_POLICY_TYPE_LOCK_NOW);ASSERT_EFI_ERROR (Status);}}InitializeHwErrRecSupport ();
6 初始化 EFI 全局变量 L"Timeout"
BootTimeOut = PcdGet16 (PcdPlatformBootTimeOut);if (BootTimeOut != 0xFFFF) {//// If time out value equal 0xFFFF, no need set to 0xFFFF to variable area because UEFI specification// define same behavior between no value or 0xFFFF value for L"Timeout".//BdsDxeSetVariableAndReportStatusCodeOnError (EFI_TIME_OUT_VARIABLE_NAME,&gEfiGlobalVariableGuid,EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,sizeof (UINT16),&BootTimeOut);}
BootTimeout 就是固件在自动启动默认项之前,给用户按热键进 Boot Menu 的等待秒数
单位:秒(0 表示“不等待,立即启动默认项”;0xFFFF 表示“无限等待,直到用户手动选”)
7 初始化 EFI 全局变量 L"BootOptionSupport"
BootOptionSupport = EFI_BOOT_OPTION_SUPPORT_APP | EFI_BOOT_OPTION_SUPPORT_SYSPREP;if (!PcdGetBool (PcdConInConnectOnDemand)) {BootOptionSupport |= EFI_BOOT_OPTION_SUPPORT_KEY;SET_BOOT_OPTION_SUPPORT_KEY_COUNT (BootOptionSupport, 3);}Status = gRT->SetVariable (EFI_BOOT_OPTION_SUPPORT_VARIABLE_NAME,&gEfiGlobalVariableGuid,EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,sizeof (BootOptionSupport),&BootOptionSupport);
8 缓存 “BootNext” NV 变量在调用API之前
NV (Non-Volatile)非易失
GetEfiGlobalVariable2 (EFI_BOOT_NEXT_VARIABLE_NAME, (VOID **)&BootNext, &DataSize);if (DataSize != sizeof (UINT16)) {if (BootNext != NULL) {FreePool (BootNext);}BootNext = NULL;}
9 初始化平台语言变量
InitializeLanguage (TRUE);
10 初始化平台恢复类型的加载选项
| 维度 | Load Options | Boot Options |
|---|---|---|
| 本质 | 数据结构 | 功能/菜单层面的叫法 |
| 存储 | 以 Boot#### 变量形式保存在 NVRAM | 同样用 Boot#### 变量,只是叫法不同 |
| 内容 | Attributes + DevicePath + OptionalData | 用户看到的 UEFI OS、Windows Boot Manager 等条目 |
| 用途 | 告诉固件如何加载并启动某个镜像 | 给用户/OS 提供可选的启动入口 |
| 可见性 | 程序员调试、dump 时看结构 | 用户界面、文档、Shell 里说的“启动项” |
在平台恢复场景下,系统允许“加载选项数量为 0”是合法的,甚至是一种预期状态
在没有平台恢复选项时,创建一个默认的平台恢复选项
FilePath = FileDevicePath (NULL, EFI_REMOVABLE_MEDIA_FILE_NAME);if (FilePath == NULL) {DEBUG ((DEBUG_ERROR, "Fail to allocate memory for default boot file path. Unable to boot.\n"));CpuDeadLoop ();}PlatformDefaultBootOptionValid = EfiBootManagerInitializeLoadOption (&PlatformDefaultBootOption,LoadOptionNumberUnassigned,LoadOptionTypePlatformRecovery,LOAD_OPTION_ACTIVE,L"Default PlatformRecovery",FilePath,NULL,0) == EFI_SUCCESS;ASSERT (PlatformDefaultBootOptionValid == TRUE);if (PlatformDefaultBootOptionValid && PcdGetBool (PcdPlatformRecoverySupport)) {LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypePlatformRecovery);if (EfiBootManagerFindLoadOption (&PlatformDefaultBootOption, LoadOptions, LoadOptionCount) == -1) {for (Index = 0; Index < LoadOptionCount; Index++) {//// The PlatformRecovery#### options are sorted by OptionNumber.// Find the the smallest unused number as the new OptionNumber.//if (LoadOptions[Index].OptionNumber != Index) {break;}}PlatformDefaultBootOption.OptionNumber = Index;Status = EfiBootManagerLoadOptionToVariable (&PlatformDefaultBootOption);ASSERT_EFI_ERROR (Status);}EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);}FreePool (FilePath);
EFI_STATUS
EFIAPI
EfiBootManagerInitializeLoadOption (IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option,IN UINTN OptionNumber,IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType,IN UINT32 Attributes,IN CHAR16 *Description,IN EFI_DEVICE_PATH_PROTOCOL *FilePath,IN UINT8 *OptionalData OPTIONAL,IN UINT32 OptionalDataSize)
{if ((Option == NULL) || (Description == NULL) || (FilePath == NULL)) {return EFI_INVALID_PARAMETER;}if (((OptionalData != NULL) && (OptionalDataSize == 0)) ||((OptionalData == NULL) && (OptionalDataSize != 0))){return EFI_INVALID_PARAMETER;}if ((UINT32)OptionType >= LoadOptionTypeMax) {return EFI_INVALID_PARAMETER;}ZeroMem (Option, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));Option->OptionNumber = OptionNumber;Option->OptionType = OptionType;Option->Attributes = Attributes;Option->Description = AllocateCopyPool (StrSize (Description), Description);Option->FilePath = DuplicateDevicePath (FilePath);if (OptionalData != NULL) {Option->OptionalData = AllocateCopyPool (OptionalDataSize, OptionalData);Option->OptionalDataSize = OptionalDataSize;}return EFI_SUCCESS;
}
11 报告状态码以指示即将连接驱动程序
REPORT_STATUS_CODE (EFI_PROGRESS_CODE,(EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_BEGIN_CONNECTING_DRIVERS));
12 初始化 ConnectConIn 事件
确保在调用平台代码之前,输入设备已经准备好,可以接收用户输入
不需要初始化 ConnectConIn 事件的场景:
- 系统设计不需要用户交互
- 平台代码不依赖用户输入
- 性能优化
- 安全考虑
- 硬件限制
if (PcdGetBool (PcdConInConnectOnDemand)) {Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL,TPL_CALLBACK,BdsDxeOnConnectConInCallBack,NULL,&gConnectConInEventGuid,&gConnectConInEvent);if (EFI_ERROR (Status)) {gConnectConInEvent = NULL;}}
13 执行平台初始化(可由 OEM/IBV 自行定制)
在 PlatformBootManagerBeforeConsole 阶段可完成的事项示例:
- 更新控制台变量:1. 加入热插拔设备;2. 清空 ConIn 并添加 SOL(Serial Over LAN)以支持 AMT
- 注册新的 Driver#### 或 Boot#### 选项
- 注册新的按键选项,例如 F12
- 发出 ReadyToLock 事件信号
- 认证相关动作:1. 连接认证设备;2. 识别自动登录用户
平台初始化详细内容
https://blog.csdn.net/degen_/article/details/154655046
PERF_INMODULE_BEGIN ("PlatformBootManagerBeforeConsole");PlatformBootManagerBeforeConsole ();PERF_INMODULE_END ("PlatformBootManagerBeforeConsole");
VOID
EFIAPI
PlatformBootManagerBeforeConsole (VOID)
{EFI_HANDLE Handle;EFI_STATUS Status;UINT16 FrontPageTimeout;RETURN_STATUS PcdStatus;BOOLEAN FirmwareSetupEnabled;DEBUG ((DEBUG_INFO, "PlatformBootManagerBeforeConsole\n"));InstallDevicePathCallback ();VisitAllInstancesOfProtocol (&gEfiPciRootBridgeIoProtocolGuid,ConnectRootBridge,NULL);//// Signal the ACPI platform driver that it can download QEMU ACPI tables.//EfiEventGroupSignal (&gRootBridgesConnectedEventGroupGuid);//// We can't signal End-of-Dxe earlier than this. Namely, End-of-Dxe triggers// the preparation of S3 system information. That logic has a hard dependency// on the presence of the FACS ACPI table. Since our ACPI tables are only// installed after PCI enumeration completes, we must not trigger the S3 save// earlier, hence we can't signal End-of-Dxe earlier.//EfiEventGroupSignal (&gEfiEndOfDxeEventGroupGuid);if (PcdGetBool (PcdAcpiS3Enable)) {//// Save the boot script too. Note that this will require us to emit the// DxeSmmReadyToLock event just below, which in turn locks down SMM.//SaveS3BootScript ();}//// We need to connect all trusted consoles for TCG PP. Here we treat all// consoles in OVMF to be trusted consoles.//// Cloud Hypervisor doesn't emulate any LPC bridge, which is why it must// rely on the serial I/O port to be connected as a console. It reuses the// definition from Xen as it is very generic.//PlatformInitializeConsole ((XenDetected () || PcdGet16 (PcdOvmfHostBridgePciDevId) == CLOUDHV_DEVICE_ID) ? gXenPlatformConsole : gPlatformConsole);//// Process TPM PPI request; this may require keyboard input//Tcg2PhysicalPresenceLibProcessRequest (NULL);//// Prevent further changes to LockBoxes or SMRAM.// Any TPM 2 Physical Presence Interface opcode must be handled before.//Handle = NULL;Status = gBS->InstallProtocolInterface (&Handle,&gEfiDxeSmmReadyToLockProtocolGuid,EFI_NATIVE_INTERFACE,NULL);ASSERT_EFI_ERROR (Status);//// Dispatch deferred images after EndOfDxe event and ReadyToLock// installation.//EfiBootManagerDispatchDeferredImages ();//// GPU passthrough only allows Console enablement after ROM image load//PlatformInitializeConsole (XenDetected () ? gXenPlatformConsole : gPlatformConsole);FrontPageTimeout = GetFrontPageTimeoutFromQemu ();PcdStatus = PcdSet16S (PcdPlatformBootTimeOut, FrontPageTimeout);ASSERT_RETURN_ERROR (PcdStatus);//// Reflect the PCD in the standard Timeout variable.//Status = gRT->SetVariable (EFI_TIME_OUT_VARIABLE_NAME,&gEfiGlobalVariableGuid,(EFI_VARIABLE_NON_VOLATILE |EFI_VARIABLE_BOOTSERVICE_ACCESS |EFI_VARIABLE_RUNTIME_ACCESS),sizeof FrontPageTimeout,&FrontPageTimeout);DEBUG ((EFI_ERROR (Status) ? DEBUG_ERROR : DEBUG_VERBOSE,"%a: SetVariable(%s, %u): %r\n",__func__,EFI_TIME_OUT_VARIABLE_NAME,FrontPageTimeout,Status));Status = QemuFwCfgParseBool ("opt/org.tianocore/FirmwareSetupSupport",&FirmwareSetupEnabled);if (RETURN_ERROR (Status)) {FirmwareSetupEnabled = TRUE;}PlatformRegisterFvBootOption (&gUiAppFileGuid,L"EFI Firmware Setup",LOAD_OPTION_ACTIVE | LOAD_OPTION_CATEGORY_APP,FirmwareSetupEnabled);if (!FeaturePcdGet (PcdBootRestrictToFirmware)) {PlatformRegisterOptionsAndKeys ();}//// Install both VIRTIO_DEVICE_PROTOCOL and (dependent) EFI_RNG_PROTOCOL// instances on Virtio PCI RNG devices.//VisitAllInstancesOfProtocol (&gEfiPciIoProtocolGuid,ConnectVirtioPciRng,NULL);
}
14 初始化热键服务
UEFI 的启动管理里把“按键启动项”分成 普通快捷键 和 Continue/“继续”快捷键 两类
- 普通快捷键
让用户“一键”启动指定介质/OS - Continue 快捷键
给所有快捷键都没匹配时一个兜底动作,避免系统卡死
EfiBootManagerStartHotkeyService (&HotkeyTriggered);
EFI_STATUS
EFIAPI
EfiBootManagerStartHotkeyService (IN EFI_EVENT *HotkeyTriggered)
{EFI_STATUS Status;EFI_BOOT_MANAGER_KEY_OPTION *KeyOptions;UINTN KeyOptionCount;UINTN Index;EFI_EVENT Event;UINT32 *BootOptionSupport;GetEfiGlobalVariable2 (EFI_BOOT_OPTION_SUPPORT_VARIABLE_NAME, (VOID **)&BootOptionSupport, NULL);if (BootOptionSupport != NULL) {if ((*BootOptionSupport & EFI_BOOT_OPTION_SUPPORT_KEY) != 0) {mBmHotkeySupportCount = ((*BootOptionSupport & EFI_BOOT_OPTION_SUPPORT_COUNT) >> LowBitSet32 (EFI_BOOT_OPTION_SUPPORT_COUNT));}FreePool (BootOptionSupport);}if (mBmHotkeySupportCount == 0) {DEBUG ((DEBUG_INFO, "Bds: BootOptionSupport NV variable forbids starting the hotkey service.\n"));return EFI_UNSUPPORTED;}Status = gBS->CreateEvent (EVT_NOTIFY_WAIT,TPL_CALLBACK,EfiEventEmptyFunction,NULL,&mBmHotkeyTriggered);ASSERT_EFI_ERROR (Status);if (HotkeyTriggered != NULL) {*HotkeyTriggered = mBmHotkeyTriggered;}KeyOptions = BmGetKeyOptions (&KeyOptionCount);for (Index = 0; Index < KeyOptionCount; Index++) {BmProcessKeyOption (&KeyOptions[Index]);}BmFreeKeyOptions (KeyOptions, KeyOptionCount);if (mBmContinueKeyOption != NULL) {BmProcessKeyOption (mBmContinueKeyOption);}//// Hook hotkey on every future SimpleTextInputEx instance when// SystemTable.ConsoleInHandle == NULL, which means the console// manager (ConSplitter) is absent.//if (gST->ConsoleInHandle == NULL) {EfiCreateProtocolNotifyEvent (&gEfiSimpleTextInputExProtocolGuid,TPL_CALLBACK,BmTxtInExCallback,NULL,&mBmTxtInExRegistration);}Status = EfiCreateEventReadyToBootEx (TPL_CALLBACK,BmStopHotkeyService,NULL,&Event);ASSERT_EFI_ERROR (Status);mBmHotkeyServiceStarted = TRUE;return Status;
}
15 执行驱动程序选项
LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypeDriver);ProcessLoadOptions (LoadOptions, LoadOptionCount);EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);
16 连接控制台
PERF_INMODULE_BEGIN ("EfiBootManagerConnectAllDefaultConsoles");if (PcdGetBool (PcdConInConnectOnDemand)) {EfiBootManagerConnectConsoleVariable (ConOut);EfiBootManagerConnectConsoleVariable (ErrOut);//// Do not connect ConIn devices when lazy ConIn feature is ON.//} else {EfiBootManagerConnectAllDefaultConsoles ();}PERF_INMODULE_END ("EfiBootManagerConnectAllDefaultConsoles");
17 执行平台相关动作
实现:
- 控制台后处理:
根据某种场景需要,把输出模式从 100×31 动态切换到 80×25
触发一个控制台已就绪的、平台自定义的事件 - 运行诊断程序,例如内存测试
- 连接特定设备,使其进入可用状态
- 加载并执行额外的 Option ROM,如显卡/独立磁盘冗余阵列(RAID)/网络接口卡(NIC)等固件扩展
- 特殊启动流程,例如强制 USB 启动,或直接进入厂商 UI(Boot Manager、Setup 界面)
详细内容
https://blog.csdn.net/degen_/article/details/154771275
VOID
EFIAPI
PlatformBootManagerAfterConsole (VOID)
{EFI_BOOT_MODE BootMode;DEBUG ((DEBUG_INFO, "PlatformBootManagerAfterConsole\n"));if (PcdGetBool (PcdOvmfFlashVariablesEnable)) {DEBUG ((DEBUG_INFO,"PlatformBdsPolicyBehavior: not restoring NvVars ""from disk since flash variables appear to be supported.\n"));} else {//// Try to restore variables from the hard disk early so// they can be used for the other BDS connect operations.//PlatformBdsRestoreNvVarsFromHardDisk ();}//// Get current Boot Mode//BootMode = GetBootModeHob ();DEBUG ((DEBUG_INFO, "Boot Mode:%x\n", BootMode));//// Go the different platform policy with different boot mode// Notes: this part code can be change with the table policy//ASSERT (BootMode == BOOT_WITH_FULL_CONFIGURATION);//// Logo show//BootLogoEnableLogo ();//// Set PCI Interrupt Line registers and ACPI SCI_EN//PciAcpiInitialization ();//// Write qemu bootorder to efi variables//StoreQemuBootOrder ();//// Process QEMU's -kernel command line option//TryRunningQemuKernel ();//// Perform some platform specific connect sequence//if (FeaturePcdGet (PcdBootRestrictToFirmware)) {RestrictBootOptionsToFirmware ();} else {PlatformBdsConnectSequence ();EfiBootManagerRefreshAllBootOption ();}BOOLEAN ShellEnabled;RETURN_STATUS RetStatus;RetStatus = QemuFwCfgParseBool ("opt/org.tianocore/EFIShellSupport",&ShellEnabled);if (RETURN_ERROR (RetStatus)) {ShellEnabled = TRUE;}//// Register UEFI Shell//PlatformRegisterFvBootOption (&gUefiShellFileGuid,L"EFI Internal Shell",LOAD_OPTION_ACTIVE | LOAD_OPTION_CATEGORY_APP,ShellEnabled);//// Register Grub//PlatformRegisterFvBootOption (&gGrubFileGuid,L"Grub Bootloader",LOAD_OPTION_ACTIVE,TRUE);RemoveStaleFvFileOptions ();SetBootOrderFromQemu ();PlatformBmPrintScRegisterHandler ();
}
18 读取 UEFI 全局变量 OsIndications
DataSize = sizeof (UINT64);Status = gRT->GetVariable (EFI_OS_INDICATIONS_VARIABLE_NAME,&gEfiGlobalVariableGuid,NULL,&DataSize,&OsIndication);if (EFI_ERROR (Status)) {OsIndication = 0;}
第一次启动时,变量不存在,GetVariable 会返回 EFI_NOT_FOUND,此时把 OsIndication 清零
19 开始转储加载选项
DEBUG_CODE_BEGIN ();EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LoadOptionType;DEBUG ((DEBUG_INFO, "[Bds]OsIndication: %016x\n", OsIndication));DEBUG ((DEBUG_INFO, "[Bds]=============Begin Load Options Dumping ...=============\n"));for (LoadOptionType = 0; LoadOptionType < LoadOptionTypeMax; LoadOptionType++) {DEBUG ((DEBUG_INFO," %s Options:\n",mBdsLoadOptionName[LoadOptionType]));LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionType);for (Index = 0; Index < LoadOptionCount; Index++) {DEBUG ((DEBUG_INFO," %s%04x: %s \t\t 0x%04x\n",mBdsLoadOptionName[LoadOptionType],LoadOptions[Index].OptionNumber,LoadOptions[Index].Description,LoadOptions[Index].Attributes));}EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);}DEBUG ((DEBUG_INFO, "[Bds]=============End Load Options Dumping=============\n"));DEBUG_CODE_END ();
20 决定进入界面
BootManagerMenuStatus = EfiBootManagerGetBootManagerMenu (&BootManagerMenu);BootFwUi = (BOOLEAN)((OsIndication & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) != 0);PlatformRecovery = (BOOLEAN)((OsIndication & EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY) != 0);if (BootFwUi || PlatformRecovery) {OsIndication &= ~((UINT64)(EFI_OS_INDICATIONS_BOOT_TO_FW_UI | EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY));Status = gRT->SetVariable (EFI_OS_INDICATIONS_VARIABLE_NAME,&gEfiGlobalVariableGuid,EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,sizeof (UINT64),&OsIndication);//// Changing the content without increasing its size with current variable implementation shouldn't fail.//ASSERT_EFI_ERROR (Status);}if (BootFwUi && (BootManagerMenuStatus != EFI_NOT_FOUND)) {//// Follow generic rule, Call BdsDxeOnConnectConInCallBack to connect ConIn before enter UI//if (PcdGetBool (PcdConInConnectOnDemand)) {BdsDxeOnConnectConInCallBack (NULL, NULL);}//// Directly enter the setup page.//EfiBootManagerBoot (&BootManagerMenu);}
- EfiBootManagerGetBootManagerMenu 如果读失败,后面会直接弹 Setup 或默认菜单
- BootFwUi = TRUE → 用户或 OS 要求进固件设置界面(BIOS Setup)
- PlatformRecovery = TRUE → 要求启动平台恢复环境
如果三个值全是0,则按正常启动顺序继续走
21 UEFI 的正常启动主流程
if (!PlatformRecovery) {//// Execute SysPrep####//LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypeSysPrep);ProcessLoadOptions (LoadOptions, LoadOptionCount);EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);//// Execute Key####//PERF_INMODULE_BEGIN ("BdsWait");BdsWait (HotkeyTriggered);PERF_INMODULE_END ("BdsWait");//// BdsReadKeys() can be removed after all keyboard drivers invoke callback in timer callback.//BdsReadKeys ();EfiBootManagerHotkeyBoot ();if (BootNext != NULL) {//// Delete "BootNext" NV variable before transferring control to it to prevent loops.//Status = gRT->SetVariable (EFI_BOOT_NEXT_VARIABLE_NAME,&gEfiGlobalVariableGuid,0,0,NULL);//// Deleting NV variable shouldn't fail unless it doesn't exist.//ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND);//// Boot to "BootNext"//UnicodeSPrint (BootNextVariableName, sizeof (BootNextVariableName), L"Boot%04x", *BootNext);Status = EfiBootManagerVariableToLoadOption (BootNextVariableName, &LoadOption);if (!EFI_ERROR (Status)) {EfiBootManagerBoot (&LoadOption);EfiBootManagerFreeLoadOption (&LoadOption);if ((LoadOption.Status == EFI_SUCCESS) &&(BootManagerMenuStatus != EFI_NOT_FOUND) &&(LoadOption.OptionNumber != BootManagerMenu.OptionNumber)){//// Boot to Boot Manager Menu upon EFI_SUCCESS// Exception: Do not boot again when the BootNext points to Boot Manager Menu.//EfiBootManagerBoot (&BootManagerMenu);}}}do {//// Retry to boot if any of the boot succeeds//LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypeBoot);BootSuccess = BootBootOptions (LoadOptions, LoadOptionCount, (BootManagerMenuStatus != EFI_NOT_FOUND) ? &BootManagerMenu : NULL);EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);} while (BootSuccess);}
21.1 执行 SysPrep#### 变量的启动项
LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypeSysPrep);ProcessLoadOptions (LoadOptions, LoadOptionCount);EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);
21.2 执行 Key#### 变量的启动项
PERF_INMODULE_BEGIN ("BdsWait");BdsWait (HotkeyTriggered);PERF_INMODULE_END ("BdsWait");BdsReadKeys ();
21.3 检测热键并执行相应操作
EfiBootManagerHotkeyBoot ();
21.4 尝试重新启动直到成功
do {//// Retry to boot if any of the boot succeeds//LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypeBoot);BootSuccess = BootBootOptions (LoadOptions, LoadOptionCount, (BootManagerMenuStatus != EFI_NOT_FOUND) ? &BootManagerMenu : NULL);EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);} while (BootSuccess);
BOOLEAN
BootBootOptions (IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions,IN UINTN BootOptionCount,IN EFI_BOOT_MANAGER_LOAD_OPTION *BootManagerMenu OPTIONAL)
{UINTN Index;//// Report Status Code to indicate BDS starts attempting booting from the UEFI BootOrder list.//REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_ATTEMPT_BOOT_ORDER_EVENT));//// Attempt boot each boot option//for (Index = 0; Index < BootOptionCount; Index++) {//// According to EFI Specification, if a load option is not marked// as LOAD_OPTION_ACTIVE, the boot manager will not automatically// load the option.//if ((BootOptions[Index].Attributes & LOAD_OPTION_ACTIVE) == 0) {continue;}//// Boot#### load options with LOAD_OPTION_CATEGORY_APP are executables which are not// part of the normal boot processing. Boot options with reserved category values will be// ignored by the boot manager.//if ((BootOptions[Index].Attributes & LOAD_OPTION_CATEGORY) != LOAD_OPTION_CATEGORY_BOOT) {continue;}//// All the driver options should have been processed since// now boot will be performed.//EfiBootManagerBoot (&BootOptions[Index]);//// If the boot via Boot#### returns with a status of EFI_SUCCESS, platform firmware// supports boot manager menu, and if firmware is configured to boot in an// interactive mode, the boot manager will stop processing the BootOrder variable and// present a boot manager menu to the user.//if ((BootManagerMenu != NULL) && (BootOptions[Index].Status == EFI_SUCCESS)) {EfiBootManagerBoot (BootManagerMenu);break;}}return (BOOLEAN)(Index < BootOptionCount);
}
VOID
EFIAPI
EfiBootManagerBoot (IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOption)
{EFI_STATUS Status;EFI_HANDLE ImageHandle;EFI_LOADED_IMAGE_PROTOCOL *ImageInfo;UINT16 Uint16;UINTN OptionNumber;UINTN OriginalOptionNumber;EFI_DEVICE_PATH_PROTOCOL *FilePath;EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath;VOID *FileBuffer;UINTN FileSize;EFI_BOOT_LOGO_PROTOCOL *BootLogo;EFI_EVENT LegacyBootEvent;if (BootOption == NULL) {return;}if ((BootOption->FilePath == NULL) || (BootOption->OptionType != LoadOptionTypeBoot)) {BootOption->Status = EFI_INVALID_PARAMETER;return;}//// 1. Create Boot#### for a temporary boot if there is no match Boot#### (i.e. a boot by selected a EFI Shell using "Boot From File")//OptionNumber = BmFindBootOptionInVariable (BootOption);if (OptionNumber == LoadOptionNumberUnassigned) {Status = BmGetFreeOptionNumber (LoadOptionTypeBoot, &Uint16);if (!EFI_ERROR (Status)) {//// Save the BootOption->OptionNumber to restore later//OptionNumber = Uint16;OriginalOptionNumber = BootOption->OptionNumber;BootOption->OptionNumber = OptionNumber;Status = EfiBootManagerLoadOptionToVariable (BootOption);BootOption->OptionNumber = OriginalOptionNumber;}if (EFI_ERROR (Status)) {DEBUG ((DEBUG_ERROR, "[Bds] Failed to create Boot#### for a temporary boot - %r!\n", Status));BootOption->Status = Status;return;}}//// 2. Set BootCurrent//Uint16 = (UINT16)OptionNumber;BmSetVariableAndReportStatusCodeOnError (L"BootCurrent",&gEfiGlobalVariableGuid,EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,sizeof (UINT16),&Uint16);//// 3. Signal the EVT_SIGNAL_READY_TO_BOOT event when we are about to load and execute// the boot option.//if (BmIsBootManagerMenuFilePath (BootOption->FilePath)) {DEBUG ((DEBUG_INFO, "[Bds] Booting Boot Manager Menu.\n"));BmStopHotkeyService (NULL, NULL);} else {EfiSignalEventReadyToBoot ();//// Report Status Code to indicate ReadyToBoot was signalled//REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT));//// 4. Repair system through DriverHealth protocol//BmRepairAllControllers (0);}PERF_START_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32)OptionNumber);//// 5. Adjust the different type memory page number just before booting// and save the updated info into the variable for next boot to use//BmSetMemoryTypeInformationVariable ((BOOLEAN)((BootOption->Attributes & LOAD_OPTION_CATEGORY) == LOAD_OPTION_CATEGORY_BOOT));//// 6. Load EFI boot option to ImageHandle//DEBUG_CODE_BEGIN ();if (BootOption->Description == NULL) {DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting from unknown device path\n"));} else {DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting %s\n", BootOption->Description));}DEBUG_CODE_END ();ImageHandle = NULL;RamDiskDevicePath = NULL;if (DevicePathType (BootOption->FilePath) != BBS_DEVICE_PATH) {Status = EFI_NOT_FOUND;FilePath = NULL;EfiBootManagerConnectDevicePath (BootOption->FilePath, NULL);FileBuffer = BmGetNextLoadOptionBuffer (LoadOptionTypeBoot, BootOption->FilePath, &FilePath, &FileSize);if (FileBuffer != NULL) {RamDiskDevicePath = BmGetRamDiskDevicePath (FilePath);REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad));Status = gBS->LoadImage (TRUE,gImageHandle,FilePath,FileBuffer,FileSize,&ImageHandle);}if (FileBuffer != NULL) {FreePool (FileBuffer);}if (FilePath != NULL) {FreePool (FilePath);}if (EFI_ERROR (Status)) {//// With EFI_SECURITY_VIOLATION retval, the Image was loaded and an ImageHandle was created// with a valid EFI_LOADED_IMAGE_PROTOCOL, but the image can not be started right now.// If the caller doesn't have the option to defer the execution of an image, we should// unload image for the EFI_SECURITY_VIOLATION to avoid resource leak.//if (Status == EFI_SECURITY_VIOLATION) {gBS->UnloadImage (ImageHandle);}//// Destroy the RAM disk//if (RamDiskDevicePath != NULL) {BmDestroyRamDisk (RamDiskDevicePath);FreePool (RamDiskDevicePath);}//// Report Status Code with the failure status to indicate that the failure to load boot option//BmReportLoadFailure (EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR, Status);BootOption->Status = Status;return;}}//// Check to see if we should legacy BOOT. If yes then do the legacy boot// Write boot to OS performance data for Legacy boot//if ((DevicePathType (BootOption->FilePath) == BBS_DEVICE_PATH) && (DevicePathSubType (BootOption->FilePath) == BBS_BBS_DP)) {if (mBmLegacyBoot != NULL) {//// Write boot to OS performance data for legacy boot.//PERF_CODE (//// Create an event to be signalled when Legacy Boot occurs to write performance data.//Status = EfiCreateEventLegacyBootEx (TPL_NOTIFY,BmEndOfBdsPerfCode,NULL,&LegacyBootEvent);ASSERT_EFI_ERROR (Status););mBmLegacyBoot (BootOption);} else {BootOption->Status = EFI_UNSUPPORTED;}PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32)OptionNumber);return;}//// Provide the image with its load options//Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&ImageInfo);ASSERT_EFI_ERROR (Status);if (!BmIsAutoCreateBootOption (BootOption)) {ImageInfo->LoadOptionsSize = BootOption->OptionalDataSize;ImageInfo->LoadOptions = BootOption->OptionalData;}//// Clean to NULL because the image is loaded directly from the firmwares boot manager.//ImageInfo->ParentHandle = NULL;//// Before calling the image, enable the Watchdog Timer for 5 minutes period//gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);//// Write boot to OS performance data for UEFI boot//PERF_CODE (BmEndOfBdsPerfCode (NULL, NULL););REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderStart));Status = gBS->StartImage (ImageHandle, &BootOption->ExitDataSize, &BootOption->ExitData);DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Image Return Status = %r\n", Status));BootOption->Status = Status;//// Destroy the RAM disk//if (RamDiskDevicePath != NULL) {BmDestroyRamDisk (RamDiskDevicePath);FreePool (RamDiskDevicePath);}if (EFI_ERROR (Status)) {//// Report Status Code with the failure status to indicate that boot failure//BmReportLoadFailure (EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED, Status);}PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32)OptionNumber);//// Clear the Watchdog Timer after the image returns//gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);//// Set Logo status invalid after trying one boot option//BootLogo = NULL;Status = gBS->LocateProtocol (&gEfiBootLogoProtocolGuid, NULL, (VOID **)&BootLogo);if (!EFI_ERROR (Status) && (BootLogo != NULL)) {Status = BootLogo->SetBootLogo (BootLogo, NULL, 0, 0, 0, 0);ASSERT_EFI_ERROR (Status);}//// Clear Boot Current//Status = gRT->SetVariable (L"BootCurrent",&gEfiGlobalVariableGuid,0,0,NULL);//// Deleting variable with current variable implementation shouldn't fail.// When BootXXXX (e.g.: BootManagerMenu) boots BootYYYY, exiting BootYYYY causes BootCurrent deleted,// exiting BootXXXX causes deleting BootCurrent returns EFI_NOT_FOUND.//ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND);
}
- 寻找/创建匹配的 Boot#### 变量

- 设置 BootCurrent 变量并记录当前启动项编号
- 触发 EVT_SIGNAL_READY_TO_BOOT 事件
- 通过 DriverHealth 协议 修复系统
- 调整各类内存页数量并保存更新后的信息到变量供下次启动使用
- 将 EFI 启动项加载为 ImageHandle
- 检查是否应使用传统启动
- 向映像提供其加载选项
- 移交控制权到已加载映像的入口
https://blog.csdn.net/degen_/article/details/154834529
