BDS 执行平台相关动作

代码

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));
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);
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;
        }
      }
    }
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
  • 安全引导相关
相关推荐
寅双木2 小时前
自己配一台电脑——CPU的命名方式
笔记·core·intel·cpu命名方式·酷睿·锐龙·ryzen
乾 乾2 小时前
VSCode 设置中文
c语言
小柯博客2 小时前
STM32MP1 没有硬件编解码,如何用 CPU 实现 H.264 编码支持 WebRTC?
c语言·stm32·嵌入式硬件·webrtc·h.264·h264·v4l2
铅笔小新z3 小时前
深入理解C语言内存管理:从栈、堆到内存泄露与悬空指针
c语言·开发语言
谅望者4 小时前
数据分析笔记04:抽样方法与抽样分布
数据库·笔记·数据挖掘·数据分析
d111111111d5 小时前
STM32外设学习-串口数据包笔记-(数据包的了解)
笔记·stm32·单片机·嵌入式硬件·学习
立志成为大牛的小牛6 小时前
数据结构——四十二、二叉排序树(王道408)
数据结构·笔记·程序人生·考研·算法
wdfk_prog11 小时前
[Linux]学习笔记系列 -- [kernel]kthread
linux·笔记·学习
散峰而望13 小时前
C/C++输入输出初级(一) (算法竞赛)
c语言·开发语言·c++·算法·github