代码
C
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 ();
}
流程
1 决定启动时变量来源
有 Flash → 直接用,不恢复
没 Flash → 从磁盘恢复一次
恢复是读取加写回
C
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 ();
}
2 获取当前启动模式
C
BootMode = GetBootModeHob ();
DEBUG ((DEBUG_INFO, "Boot Mode:%x\n", BootMode));
3 显示logo
C
BootLogoEnableLogo ();
3.1 定位相关协议
C
Status = gBS->LocateProtocol (&gEdkiiPlatformLogoProtocolGuid, NULL, (VOID **)&PlatformLogo);
if (EFI_ERROR (Status)) {
return EFI_UNSUPPORTED;
}
//
// Try to open GOP first
//
Status = gBS->HandleProtocol (gST->ConsoleOutHandle, &gEfiGraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput);
if (EFI_ERROR (Status)) {
return EFI_UNSUPPORTED;
}
//
// Try to open Boot Logo Protocol.
//
Status = gBS->LocateProtocol (&gEfiBootLogoProtocolGuid, NULL, (VOID **)&BootLogo);
if (EFI_ERROR (Status)) {
BootLogo = NULL;
}
//
// Try to open Boot Logo 2 Protocol.
//
Status = gBS->LocateProtocol (&gEdkiiBootLogo2ProtocolGuid, NULL, (VOID **)&BootLogo2);
if (EFI_ERROR (Status)) {
BootLogo2 = NULL;
}
3.2 擦除光标
C
gST->ConOut->EnableCursor (gST->ConOut, FALSE);
3.3 获取 logo 尺寸
C
SizeOfX = GraphicsOutput->Mode->Info->HorizontalResolution;
SizeOfY = GraphicsOutput->Mode->Info->VerticalResolution;
3.4 获取映像
C
Status = PlatformLogo->GetImage (
PlatformLogo,
&Instance,
&Image,
&Attribute,
&OffsetX,
&OffsetY
);
if (EFI_ERROR (Status)) {
break;
}
if (Blt != NULL) {
FreePool (Blt);
}
Blt = Image.Bitmap;
3.5 定位位置
C
switch (Attribute) {
case EdkiiPlatformLogoDisplayAttributeLeftTop:
DestX = 0;
DestY = 0;
break;
case EdkiiPlatformLogoDisplayAttributeCenterTop:
DestX = (SizeOfX - Image.Width) / 2;
DestY = 0;
break;
case EdkiiPlatformLogoDisplayAttributeRightTop:
DestX = SizeOfX - Image.Width;
DestY = 0;
break;
case EdkiiPlatformLogoDisplayAttributeCenterLeft:
DestX = 0;
DestY = (SizeOfY - Image.Height) / 2;
break;
case EdkiiPlatformLogoDisplayAttributeCenter:
DestX = (SizeOfX - Image.Width) / 2;
DestY = (SizeOfY - Image.Height) / 2;
break;
case EdkiiPlatformLogoDisplayAttributeCenterRight:
DestX = SizeOfX - Image.Width;
DestY = (SizeOfY - Image.Height) / 2;
break;
case EdkiiPlatformLogoDisplayAttributeLeftBottom:
DestX = 0;
DestY = SizeOfY - Image.Height;
break;
case EdkiiPlatformLogoDisplayAttributeCenterBottom:
DestX = (SizeOfX - Image.Width) / 2;
DestY = SizeOfY - Image.Height;
break;
case EdkiiPlatformLogoDisplayAttributeRightBottom:
DestX = SizeOfX - Image.Width;
DestY = SizeOfY - Image.Height;
break;
default:
ASSERT (FALSE);
continue;
break;
}
DestX += OffsetX;
DestY += OffsetY;
3.6 显示
C
if ((DestX >= 0) && (DestY >= 0)) {
Status = GraphicsOutput->Blt (
GraphicsOutput,
Blt,
EfiBltBufferToVideo,
0,
0,
(UINTN)DestX,
(UINTN)DestY,
Image.Width,
Image.Height,
Image.Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
);
//
// Report displayed Logo information.
//
if (!EFI_ERROR (Status)) {
NumberOfLogos++;
if (NumberOfLogos == 1) {
//
// The first Logo.
//
LogoDestX = (UINTN)DestX;
LogoDestY = (UINTN)DestY;
LogoWidth = Image.Width;
LogoHeight = Image.Height;
} else {
//
// Merge new logo with old one.
//
NewDestX = MIN ((UINTN)DestX, LogoDestX);
NewDestY = MIN ((UINTN)DestY, LogoDestY);
LogoWidth = MAX ((UINTN)DestX + Image.Width, LogoDestX + LogoWidth) - NewDestX;
LogoHeight = MAX ((UINTN)DestY + Image.Height, LogoDestY + LogoHeight) - NewDestY;
LogoDestX = NewDestX;
LogoDestY = NewDestY;
}
}
}

3.7 通告已显示 logo 信息
C
if (NumberOfLogos == 1) {
//
// Only one logo displayed, use its Blt buffer directly for BootLogo protocol.
//
LogoBlt = Blt;
Status = EFI_SUCCESS;
} else {
//
// More than one Logo displayed, get merged BltBuffer using VideoToBuffer operation.
//
if (Blt != NULL) {
FreePool (Blt);
}
//
// Ensure the LogoHeight * LogoWidth * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) doesn't overflow
//
if (LogoHeight > MAX_UINTN / LogoWidth / sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)) {
return EFI_UNSUPPORTED;
}
BufferSize = LogoWidth * LogoHeight * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
LogoBlt = AllocatePool (BufferSize);
if (LogoBlt == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = GraphicsOutput->Blt (
GraphicsOutput,
LogoBlt,
EfiBltVideoToBltBuffer,
LogoDestX,
LogoDestY,
0,
0,
LogoWidth,
LogoHeight,
LogoWidth * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
);
}
4 设置 PCI 中断线寄存器并启用 ACPI SCI_EN
SCI_EN - System Control Interrupt Enable
C
PciAcpiInitialization ();
C
VOID
PciAcpiInitialization (
)
{
UINTN Pmba;
//
// Query Host Bridge DID to determine platform type
//
mHostBridgeDevId = PcdGet16 (PcdOvmfHostBridgePciDevId);
switch (mHostBridgeDevId) {
case INTEL_82441_DEVICE_ID:
Pmba = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMBA);
//
// 00:01.0 ISA Bridge (PIIX4) LNK routing targets
//
PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x60), PciHostIrqs[0]); // A
PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x61), PciHostIrqs[1]); // B
PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x62), PciHostIrqs[2]); // C
PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x63), PciHostIrqs[3]); // D
break;
case INTEL_Q35_MCH_DEVICE_ID:
Pmba = POWER_MGMT_REGISTER_Q35 (ICH9_PMBASE);
//
// 00:1f.0 LPC Bridge (Q35) LNK routing targets
//
PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x60), PciHostIrqs[0]); // A
PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x61), PciHostIrqs[1]); // B
PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x62), PciHostIrqs[2]); // C
PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x63), PciHostIrqs[3]); // D
PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x68), PciHostIrqs[0]); // E
PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x69), PciHostIrqs[1]); // F
PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x6a), PciHostIrqs[2]); // G
PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x6b), PciHostIrqs[3]); // H
break;
case MICROVM_PSEUDO_DEVICE_ID:
case CLOUDHV_DEVICE_ID:
return;
default:
if (XenDetected ()) {
//
// There is no PCI bus in this case.
//
return;
}
DEBUG ((
DEBUG_ERROR,
"%a: Unknown Host Bridge Device ID: 0x%04x\n",
__func__,
mHostBridgeDevId
));
ASSERT (FALSE);
return;
}
//
// Initialize PCI_INTERRUPT_LINE for applicable present PCI devices
//
VisitAllPciInstances (SetPciIntLine);
//
// Set ACPI SCI_EN bit in PMCNTRL
//
IoOr16 ((PciRead32 (Pmba) & ~BIT0) + 4, BIT0);
}
4.1 查询主桥 DID 以确定平台类型
Q35_MCH 就是 Intel Q35 芯片组里的 Memory Controller Hub(MCH),也就是俗称的北桥
C
mHostBridgeDevId = PcdGet16 (PcdOvmfHostBridgePciDevId);
switch (mHostBridgeDevId) {
case INTEL_82441_DEVICE_ID:
Pmba = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMBA);
//
// 00:01.0 ISA Bridge (PIIX4) LNK routing targets
//
PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x60), PciHostIrqs[0]); // A
PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x61), PciHostIrqs[1]); // B
PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x62), PciHostIrqs[2]); // C
PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x63), PciHostIrqs[3]); // D
break;
case INTEL_Q35_MCH_DEVICE_ID:
Pmba = POWER_MGMT_REGISTER_Q35 (ICH9_PMBASE);
//
// 00:1f.0 LPC Bridge (Q35) LNK routing targets
//
PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x60), PciHostIrqs[0]); // A
PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x61), PciHostIrqs[1]); // B
PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x62), PciHostIrqs[2]); // C
PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x63), PciHostIrqs[3]); // D
PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x68), PciHostIrqs[0]); // E
PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x69), PciHostIrqs[1]); // F
PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x6a), PciHostIrqs[2]); // G
PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x6b), PciHostIrqs[3]); // H
break;
case MICROVM_PSEUDO_DEVICE_ID:
case CLOUDHV_DEVICE_ID:
return;
default:
if (XenDetected ()) {
//
// There is no PCI bus in this case.
//
return;
}
DEBUG ((
DEBUG_ERROR,
"%a: Unknown Host Bridge Device ID: 0x%04x\n",
__func__,
mHostBridgeDevId
));
ASSERT (FALSE);
return;
}
4.2 为 PCI 设备初始化 PCI_INTERRUPT_LINE 寄存器
Interrupt Line 寄存器告诉操作系统/固件该 PCI 设备当前连到中断控制器的哪条 IRQ 线
Interrupt Line 寄存器仅作软件标签使用,不改变硬件实际走的中断路由;真正的电平/边沿触发由芯片组内部的 PIRQ/INTx→IRQ 映射表决定
C
VisitAllPciInstances (SetPciIntLine);
C
EFI_STATUS
VisitAllPciInstances (
IN VISIT_PCI_INSTANCE_CALLBACK CallBackFunction
)
{
return VisitAllInstancesOfProtocol (
&gEfiPciIoProtocolGuid,
VisitingAPciInstance,
(VOID *)(UINTN)CallBackFunction
);
}
4.3 在 PMCNTRL 寄存器中设置 ACPI SCI_EN 位
C
IoOr16 ((PciRead32 (Pmba) & ~BIT0) + 4, BIT0);
通过Pmba(Power Management Base Address)加上偏移,指向 PMCNTRL(Power Management Control)寄存器
| 字段 | 位 | 说明 |
|---|---|---|
| SCI_EN | 0 | 置 1 → 打开 SCI 中断,OS 才能收到 ACPI 事件 |
| BM_RLD | 1 | Bus-Master Reload,控制 BM_STS 自动清零 |
| GBL_RLS | 2 | Global Release,写 1 触发一次 GBL_STS |
| SLP_TYPx | 10--12 | 睡眠类型编码(S0/S3/S4/S5...) |
| SLP_EN | 13 | 写 1 按 SLP_TYPx 进入睡眠 |
| AlwaysZero | 14--15 | 保留 |
Power Management
├─ 状态寄存器组
├─ 控制寄存器组
├─ 睡眠类型表
└─ 事件/使能寄存器
5 将 QEMU 的 bootorder 写入 EFI 变量
C
TryRunningQemuKernel ();
C
VOID
EFIAPI
StoreQemuBootOrder (
VOID
)
{
RETURN_STATUS Status;
FIRMWARE_CONFIG_ITEM FwCfgItem;
UINTN FwCfgSize;
CHAR8 *FwCfg;
EFI_STATUS EfiStatus;
EXTRA_ROOT_BUS_MAP *ExtraPciRoots;
CONST CHAR8 *FwCfgPtr;
UINTN TranslatedSize;
CHAR16 Translated[TRANSLATION_OUTPUT_SIZE];
UINTN VariableIndex = 0;
CHAR16 VariableName[20];
Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);
if (RETURN_ERROR (Status)) {
return;
}
if (FwCfgSize == 0) {
return;
}
FwCfg = AllocatePool (FwCfgSize);
if (FwCfg == NULL) {
return;
}
QemuFwCfgSelectItem (FwCfgItem);
QemuFwCfgReadBytes (FwCfgSize, FwCfg);
if (FwCfg[FwCfgSize - 1] != '\0') {
Status = RETURN_INVALID_PARAMETER;
goto FreeFwCfg;
}
DEBUG ((DEBUG_VERBOSE, "%a: FwCfg:\n", __func__));
DEBUG ((DEBUG_VERBOSE, "%a\n", FwCfg));
DEBUG ((DEBUG_VERBOSE, "%a: FwCfg: <end>\n", __func__));
if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {
EfiStatus = CreateExtraRootBusMap (&ExtraPciRoots);
if (EFI_ERROR (EfiStatus)) {
Status = (RETURN_STATUS)EfiStatus;
goto FreeFwCfg;
}
} else {
ExtraPciRoots = NULL;
}
//
// Translate each OpenFirmware path to a UEFI devpath prefix.
//
FwCfgPtr = FwCfg;
TranslatedSize = ARRAY_SIZE (Translated);
Status = TranslateOfwPath (
&FwCfgPtr,
ExtraPciRoots,
Translated,
&TranslatedSize
);
while (Status == EFI_SUCCESS ||
Status == EFI_UNSUPPORTED)
{
if (Status == EFI_SUCCESS) {
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
//
// Convert the UEFI devpath prefix to binary representation.
//
ASSERT (Translated[TranslatedSize] == L'\0');
DevicePath = ConvertTextToDevicePath (Translated);
if (DevicePath == NULL) {
Status = RETURN_OUT_OF_RESOURCES;
goto FreeExtraPciRoots;
}
UnicodeSPrint (
VariableName,
sizeof (VariableName),
L"VMMBootOrder%04x",
VariableIndex++
);
DEBUG ((DEBUG_INFO, "%a: %s = %s\n", __func__, VariableName, Translated));
gRT->SetVariable (
VariableName,
&gVMMBootOrderGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
GetDevicePathSize (DevicePath),
DevicePath
);
FreePool (DevicePath);
}
//
// Move to the next OFW devpath.
//
TranslatedSize = ARRAY_SIZE (Translated);
Status = TranslateOfwPath (
&FwCfgPtr,
ExtraPciRoots,
Translated,
&TranslatedSize
);
}
FreeExtraPciRoots:
if (ExtraPciRoots != NULL) {
DestroyExtraRootBusMap (ExtraPciRoots);
}
FreeFwCfg:
FreePool (FwCfg);
}
5.1 在 QEMU 的 fw_cfg 接口查找 bootorder 文件
C
Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);
if (RETURN_ERROR (Status)) {
return;
}
if (FwCfgSize == 0) {
return;
}
FwCfgSize == 0直接返回
表示是空文件
6 处理 QEMU 的 -kernel 命令行参数
-kernel 是一个命令行选项,用于指定一个 内核镜像文件,QEMU 会将其加载到内存中,并将启动权交给它,而不是从虚拟机的硬盘或光驱启动
C
TryRunningQemuKernel ();
C
EFI_STATUS
TryRunningQemuKernel (
VOID
)
{
EFI_STATUS Status;
EFI_HANDLE KernelImageHandle;
Status = QemuLoadKernelImage (&KernelImageHandle);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Signal the EVT_SIGNAL_READY_TO_BOOT event
//
EfiSignalEventReadyToBoot ();
REPORT_STATUS_CODE (
EFI_PROGRESS_CODE,
(EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT)
);
//
// Start the image.
//
Status = QemuStartKernelImage (&KernelImageHandle);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"%a: QemuStartKernelImage(): %r\n",
__func__,
Status
));
}
QemuUnloadKernelImage (KernelImageHandle);
return Status;
}
6.1 加载-kernel 镜像
C
Status = QemuLoadKernelImage (&KernelImageHandle);
if (EFI_ERROR (Status)) {
return Status;
}
内核镜像加载失败
7 执行特定于平台的连接序列
C
if (FeaturePcdGet (PcdBootRestrictToFirmware)) {
RestrictBootOptionsToFirmware ();
} else {
PlatformBdsConnectSequence ();
EfiBootManagerRefreshAllBootOption ();
}
8 判断是否把 UEFI Shell 列成可启动项
UEFI Shell 是一个基于 UEFI 标准的命令行工具,它在启动过程中提供了一个交互式环境,允许用户直接与固件和硬件交互
C
BOOLEAN ShellEnabled;
RETURN_STATUS RetStatus;
RetStatus = QemuFwCfgParseBool (
"opt/org.tianocore/EFIShellSupport",
&ShellEnabled
);
if (RETURN_ERROR (RetStatus)) {
ShellEnabled = TRUE;
}
9 将 UefiShell 注册为可启动选项
C
PlatformRegisterFvBootOption (
&gUefiShellFileGuid,
L"EFI Internal Shell",
LOAD_OPTION_ACTIVE | LOAD_OPTION_CATEGORY_APP,
ShellEnabled
);
10 将 Grub 注册为可启动选项
C
PlatformRegisterFvBootOption (
&gGrubFileGuid,
L"Grub Bootloader",
LOAD_OPTION_ACTIVE,
TRUE
);
Grub - Grand Unified Bootloader
11 移除所有无法解析设备路径的启动选项
C
RemoveStaleFvFileOptions ();
C
VOID
RemoveStaleFvFileOptions (
VOID
)
{
EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
UINTN BootOptionCount;
UINTN Index;
BootOptions = EfiBootManagerGetLoadOptions (
&BootOptionCount,
LoadOptionTypeBoot
);
for (Index = 0; Index < BootOptionCount; ++Index) {
EFI_DEVICE_PATH_PROTOCOL *Node1, *Node2;
EFI_STATUS Status;
//
// If the device path starts with neither MemoryMapped(...) nor Fv(...),
// then keep the boot option.
//
Node1 = BootOptions[Index].FilePath;
if (!((DevicePathType (Node1) == HARDWARE_DEVICE_PATH) &&
(DevicePathSubType (Node1) == HW_MEMMAP_DP)) &&
!((DevicePathType (Node1) == MEDIA_DEVICE_PATH) &&
(DevicePathSubType (Node1) == MEDIA_PIWG_FW_VOL_DP)))
{
continue;
}
//
// If the second device path node is not FvFile(...), then keep the boot
// option.
//
Node2 = NextDevicePathNode (Node1);
if ((DevicePathType (Node2) != MEDIA_DEVICE_PATH) ||
(DevicePathSubType (Node2) != MEDIA_PIWG_FW_FILE_DP))
{
continue;
}
// If file is in firmware then keep the entry
if (FileIsInFv (BootOptions[Index].FilePath)) {
continue;
}
//
// Delete the boot option.
//
Status = EfiBootManagerDeleteLoadOptionVariable (
BootOptions[Index].OptionNumber,
LoadOptionTypeBoot
);
DEBUG_CODE_BEGIN ();
CHAR16 *DevicePathString;
DevicePathString = ConvertDevicePathToText (
BootOptions[Index].FilePath,
FALSE,
FALSE
);
DEBUG ((
EFI_ERROR (Status) ? DEBUG_WARN : DEBUG_VERBOSE,
"%a: removing stale Boot#%04x %s: %r\n",
__func__,
(UINT32)BootOptions[Index].OptionNumber,
DevicePathString == NULL ? L"<unavailable>" : DevicePathString,
Status
));
if (DevicePathString != NULL) {
FreePool (DevicePathString);
}
DEBUG_CODE_END ();
}
EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
}
12 根据配置设定启动顺序
C
SetBootOrderFromQemu ();
C
RETURN_STATUS
EFIAPI
SetBootOrderFromQemu (
VOID
)
{
RETURN_STATUS Status;
FIRMWARE_CONFIG_ITEM FwCfgItem;
UINTN FwCfgSize;
CHAR8 *FwCfg;
CONST CHAR8 *FwCfgPtr;
BOOT_ORDER BootOrder;
ACTIVE_OPTION *ActiveOption;
UINTN ActiveCount;
EXTRA_ROOT_BUS_MAP *ExtraPciRoots;
UINTN TranslatedSize;
CHAR16 Translated[TRANSLATION_OUTPUT_SIZE];
EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
UINTN BootOptionCount;
Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);
if (Status != RETURN_SUCCESS) {
return Status;
}
if (FwCfgSize == 0) {
return RETURN_NOT_FOUND;
}
FwCfg = AllocatePool (FwCfgSize);
if (FwCfg == NULL) {
return RETURN_OUT_OF_RESOURCES;
}
QemuFwCfgSelectItem (FwCfgItem);
QemuFwCfgReadBytes (FwCfgSize, FwCfg);
if (FwCfg[FwCfgSize - 1] != '\0') {
Status = RETURN_INVALID_PARAMETER;
goto ErrorFreeFwCfg;
}
DEBUG ((DEBUG_VERBOSE, "%a: FwCfg:\n", __func__));
DEBUG ((DEBUG_VERBOSE, "%a\n", FwCfg));
DEBUG ((DEBUG_VERBOSE, "%a: FwCfg: <end>\n", __func__));
FwCfgPtr = FwCfg;
BootOrder.Produced = 0;
BootOrder.Allocated = 1;
BootOrder.Data = AllocatePool (
BootOrder.Allocated * sizeof (*BootOrder.Data)
);
if (BootOrder.Data == NULL) {
Status = RETURN_OUT_OF_RESOURCES;
goto ErrorFreeFwCfg;
}
BootOptions = EfiBootManagerGetLoadOptions (
&BootOptionCount,
LoadOptionTypeBoot
);
if (BootOptions == NULL) {
Status = RETURN_NOT_FOUND;
goto ErrorFreeBootOrder;
}
Status = CollectActiveOptions (
BootOptions,
BootOptionCount,
&ActiveOption,
&ActiveCount
);
if (RETURN_ERROR (Status)) {
goto ErrorFreeBootOptions;
}
if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {
Status = CreateExtraRootBusMap (&ExtraPciRoots);
if (EFI_ERROR (Status)) {
goto ErrorFreeActiveOption;
}
} else {
ExtraPciRoots = NULL;
}
//
// translate each OpenFirmware path
//
TranslatedSize = ARRAY_SIZE (Translated);
Status = TranslateOfwPath (
&FwCfgPtr,
ExtraPciRoots,
Translated,
&TranslatedSize
);
while (Status == RETURN_SUCCESS ||
Status == RETURN_UNSUPPORTED ||
Status == RETURN_PROTOCOL_ERROR ||
Status == RETURN_BUFFER_TOO_SMALL)
{
if (Status == RETURN_SUCCESS) {
UINTN Idx;
//
// match translated OpenFirmware path against all active boot options
//
for (Idx = 0; Idx < ActiveCount; ++Idx) {
if (!ActiveOption[Idx].Appended &&
Match (
Translated,
TranslatedSize, // contains length, not size, in CHAR16's here
ActiveOption[Idx].BootOption->FilePath
)
)
{
//
// match found, store ID and continue with next OpenFirmware path
//
Status = BootOrderAppend (&BootOrder, &ActiveOption[Idx]);
if (Status != RETURN_SUCCESS) {
goto ErrorFreeExtraPciRoots;
}
}
} // scanned all active boot options
} // translation successful
TranslatedSize = ARRAY_SIZE (Translated);
Status = TranslateOfwPath (
&FwCfgPtr,
ExtraPciRoots,
Translated,
&TranslatedSize
);
} // scanning of OpenFirmware paths done
if ((Status == RETURN_NOT_FOUND) && (BootOrder.Produced > 0)) {
//
// No more OpenFirmware paths, some matches found: rewrite BootOrder NvVar.
// Some of the active boot options that have not been selected over fw_cfg
// should be preserved at the end of the boot order.
//
Status = BootOrderComplete (&BootOrder, ActiveOption, ActiveCount);
if (RETURN_ERROR (Status)) {
goto ErrorFreeExtraPciRoots;
}
//
// See Table 10 in the UEFI Spec 2.3.1 with Errata C for the required
// attributes.
//
Status = gRT->SetVariable (
L"BootOrder",
&gEfiGlobalVariableGuid,
EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS,
BootOrder.Produced * sizeof (*BootOrder.Data),
BootOrder.Data
);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"%a: setting BootOrder: %r\n",
__func__,
Status
));
goto ErrorFreeExtraPciRoots;
}
DEBUG ((DEBUG_INFO, "%a: setting BootOrder: success\n", __func__));
PruneBootVariables (ActiveOption, ActiveCount);
}
ErrorFreeExtraPciRoots:
if (ExtraPciRoots != NULL) {
DestroyExtraRootBusMap (ExtraPciRoots);
}
ErrorFreeActiveOption:
FreePool (ActiveOption);
ErrorFreeBootOptions:
EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
ErrorFreeBootOrder:
FreePool (BootOrder.Data);
ErrorFreeFwCfg:
FreePool (FwCfg);
return Status;
}
由于FwCfgSize = 0 返回 RETURN_NOT_FOUND;
13 注册状态码处理器并打印到 UEFI 控制台
打印事项包括 Boot Manager 在 LoadImage() 与 StartImage() 过程中的准备步骤及返回码
C
PlatformBmPrintScRegisterHandler ();
C
EFI_STATUS
EFIAPI
PlatformBmPrintScRegisterHandler (
VOID
)
{
EFI_STATUS Status;
EFI_RSC_HANDLER_PROTOCOL *StatusCodeRouter;
EFI_EVENT ExitBootEvent;
Status = gBS->LocateProtocol (
&gEfiRscHandlerProtocolGuid,
NULL /* Registration */,
(VOID **)&StatusCodeRouter
);
ASSERT_EFI_ERROR (Status);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Set the EFI_STATUS_CODE_VALUE convenience variables.
//
mLoadPrep = PcdGet32 (PcdProgressCodeOsLoaderLoad);
mLoadFail = (EFI_SOFTWARE_DXE_BS_DRIVER |
EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR);
mStartPrep = PcdGet32 (PcdProgressCodeOsLoaderStart);
mStartFail = (EFI_SOFTWARE_DXE_BS_DRIVER |
EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED);
//
// Register the handler callback.
//
Status = StatusCodeRouter->Register (HandleStatusCode, TPL_CALLBACK);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"%a:%a: failed to register status code handler: %r\n",
gEfiCallerBaseName,
__func__,
Status
));
return Status;
}
//
// Status code reporting and routing/handling extend into OS runtime. Since
// we don't want our handler to survive the BDS phase, we have to unregister
// the callback at ExitBootServices(). (See EFI_RSC_HANDLER_PROTOCOL in
// Volume 3 of the Platform Init spec.)
//
Status = gBS->CreateEvent (
EVT_SIGNAL_EXIT_BOOT_SERVICES, // Type
TPL_CALLBACK, // NotifyTpl
UnregisterAtExitBootServices, // NotifyFunction
StatusCodeRouter, // NotifyContext
&ExitBootEvent // Event
);
if (EFI_ERROR (Status)) {
//
// We have to unregister the callback right now, and fail the function.
//
DEBUG ((
DEBUG_ERROR,
"%a:%a: failed to create ExitBootServices() event: "
"%r\n",
gEfiCallerBaseName,
__func__,
Status
));
StatusCodeRouter->Unregister (HandleStatusCode);
return Status;
}
return EFI_SUCCESS;
}
13.1 加载协议
C
Status = gBS->LocateProtocol (
&gEfiRscHandlerProtocolGuid,
NULL /* Registration */,
(VOID **)&StatusCodeRouter
);
ASSERT_EFI_ERROR (Status);
if (EFI_ERROR (Status)) {
return Status;
}
13.2 设置用于 EFI_STATUS_CODE_VALUE 的便捷变量
C
mLoadPrep = PcdGet32 (PcdProgressCodeOsLoaderLoad);
mLoadFail = (EFI_SOFTWARE_DXE_BS_DRIVER |
EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR);
mStartPrep = PcdGet32 (PcdProgressCodeOsLoaderStart);
mStartFail = (EFI_SOFTWARE_DXE_BS_DRIVER |
EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED);
13.3 注册处理器回调函数
C
Status = StatusCodeRouter->Register (HandleStatusCode, TPL_CALLBACK);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"%a:%a: failed to register status code handler: %r\n",
gEfiCallerBaseName,
__func__,
Status
));
return Status;
}
13.4 注册事件回调函数
确保 ExitBootServices() 被调用时注销状态码处理器
C
Status = gBS->CreateEvent (
EVT_SIGNAL_EXIT_BOOT_SERVICES, // Type
TPL_CALLBACK, // NotifyTpl
UnregisterAtExitBootServices, // NotifyFunction
StatusCodeRouter, // NotifyContext
&ExitBootEvent // Event
);
if (EFI_ERROR (Status)) {
//
// We have to unregister the callback right now, and fail the function.
//
DEBUG ((
DEBUG_ERROR,
"%a:%a: failed to create ExitBootServices() event: "
"%r\n",
gEfiCallerBaseName,
__func__,
Status
));
StatusCodeRouter->Unregister (HandleStatusCode);
return Status;
}
BDS 启动选项类别
固件卷里的固定应用
- FvFile EFI Shell
- FvFile Boot Manager Menu
- FvFile GRUB 镜像
可启动的外设/分区
- 硬盘、SSD、NVMe
- USB、SD、光驱
- 网络:IPv4/IPv6 PXE 启动项
平台额外条目
- QEMU:-kernel 传进来的镜像
- VMware:虚拟 EFI 光驱里插入的 ISO
- 厂商 Recovery
- 安全引导相关