BDS 执行平台相关动作
代码
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 → 从磁盘恢复一次
恢复是读取加写回
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 获取当前启动模式
BootMode = GetBootModeHob ();DEBUG ((DEBUG_INFO, "Boot Mode:%x\n", BootMode));
3 显示logo
BootLogoEnableLogo ();
3.1 定位相关协议
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 擦除光标
gST->ConOut->EnableCursor (gST->ConOut, FALSE);
3.3 获取 logo 尺寸
SizeOfX = GraphicsOutput->Mode->Info->HorizontalResolution;SizeOfY = GraphicsOutput->Mode->Info->VerticalResolution;
3.4 获取映像
Status = PlatformLogo->GetImage (PlatformLogo,&Instance,&Image,&Attribute,&OffsetX,&OffsetY);if (EFI_ERROR (Status)) {break;}if (Blt != NULL) {FreePool (Blt);}Blt = Image.Bitmap;
3.5 定位位置
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 显示
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 信息
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
PciAcpiInitialization ();
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]); // APciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x61), PciHostIrqs[1]); // BPciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x62), PciHostIrqs[2]); // CPciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x63), PciHostIrqs[3]); // Dbreak;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]); // APciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x61), PciHostIrqs[1]); // BPciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x62), PciHostIrqs[2]); // CPciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x63), PciHostIrqs[3]); // DPciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x68), PciHostIrqs[0]); // EPciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x69), PciHostIrqs[1]); // FPciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x6a), PciHostIrqs[2]); // GPciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x6b), PciHostIrqs[3]); // Hbreak;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),也就是俗称的北桥
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]); // APciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x61), PciHostIrqs[1]); // BPciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x62), PciHostIrqs[2]); // CPciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x63), PciHostIrqs[3]); // Dbreak;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]); // APciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x61), PciHostIrqs[1]); // BPciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x62), PciHostIrqs[2]); // CPciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x63), PciHostIrqs[3]); // DPciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x68), PciHostIrqs[0]); // EPciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x69), PciHostIrqs[1]); // FPciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x6a), PciHostIrqs[2]); // GPciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x6b), PciHostIrqs[3]); // Hbreak;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 映射表决定
VisitAllPciInstances (SetPciIntLine);
EFI_STATUS
VisitAllPciInstances (IN VISIT_PCI_INSTANCE_CALLBACK CallBackFunction)
{return VisitAllInstancesOfProtocol (&gEfiPciIoProtocolGuid,VisitingAPciInstance,(VOID *)(UINTN)CallBackFunction);
}
4.3 在 PMCNTRL 寄存器中设置 ACPI SCI_EN 位
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 变量
TryRunningQemuKernel ();
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 文件
Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);if (RETURN_ERROR (Status)) {return;}if (FwCfgSize == 0) {return;}
FwCfgSize == 0直接返回
表示是空文件
6 处理 QEMU 的 -kernel 命令行参数
-kernel 是一个命令行选项,用于指定一个 内核镜像文件,QEMU 会将其加载到内存中,并将启动权交给它,而不是从虚拟机的硬盘或光驱启动
TryRunningQemuKernel ();
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 镜像
Status = QemuLoadKernelImage (&KernelImageHandle);if (EFI_ERROR (Status)) {return Status;}
内核镜像加载失败
7 执行特定于平台的连接序列
if (FeaturePcdGet (PcdBootRestrictToFirmware)) {RestrictBootOptionsToFirmware ();} else {PlatformBdsConnectSequence ();EfiBootManagerRefreshAllBootOption ();}
8 判断是否把 UEFI Shell 列成可启动项
UEFI Shell 是一个基于 UEFI 标准的命令行工具,它在启动过程中提供了一个交互式环境,允许用户直接与固件和硬件交互
BOOLEAN ShellEnabled;RETURN_STATUS RetStatus;RetStatus = QemuFwCfgParseBool ("opt/org.tianocore/EFIShellSupport",&ShellEnabled);if (RETURN_ERROR (RetStatus)) {ShellEnabled = TRUE;}
9 将 UefiShell 注册为可启动选项
PlatformRegisterFvBootOption (&gUefiShellFileGuid,L"EFI Internal Shell",LOAD_OPTION_ACTIVE | LOAD_OPTION_CATEGORY_APP,ShellEnabled);
10 将 Grub 注册为可启动选项
PlatformRegisterFvBootOption (&gGrubFileGuid,L"Grub Bootloader",LOAD_OPTION_ACTIVE,TRUE);
Grub - Grand Unified Bootloader
11 移除所有无法解析设备路径的启动选项
RemoveStaleFvFileOptions ();
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 entryif (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 根据配置设定启动顺序
SetBootOrderFromQemu ();
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 hereActiveOption[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 successfulTranslatedSize = ARRAY_SIZE (Translated);Status = TranslateOfwPath (&FwCfgPtr,ExtraPciRoots,Translated,&TranslatedSize);} // scanning of OpenFirmware paths doneif ((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() 过程中的准备步骤及返回码
PlatformBmPrintScRegisterHandler ();
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, // TypeTPL_CALLBACK, // NotifyTplUnregisterAtExitBootServices, // NotifyFunctionStatusCodeRouter, // 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 加载协议
Status = gBS->LocateProtocol (&gEfiRscHandlerProtocolGuid,NULL /* Registration */,(VOID **)&StatusCodeRouter);ASSERT_EFI_ERROR (Status);if (EFI_ERROR (Status)) {return Status;}
13.2 设置用于 EFI_STATUS_CODE_VALUE 的便捷变量
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 注册处理器回调函数
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() 被调用时注销状态码处理器
Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, // TypeTPL_CALLBACK, // NotifyTplUnregisterAtExitBootServices, // NotifyFunctionStatusCodeRouter, // 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
- 安全引导相关
