]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBm.c
ArmPkg/PlatformBDS: Implement PlatformBootManagerUnableToBoot
[mirror_edk2.git] / ArmVirtPkg / Library / PlatformBootManagerLib / PlatformBm.c
index 2ced0486a1ac3ff9d38770543ba1166814c6ab72..62cce6a01e1a8fb308380f643e7fd10cf59d6013 100644 (file)
 **/\r
 \r
 #include <IndustryStandard/Pci22.h>\r
+#include <IndustryStandard/Virtio095.h>\r
+#include <Library/BootLogoLib.h>\r
 #include <Library/DevicePathLib.h>\r
 #include <Library/PcdLib.h>\r
 #include <Library/QemuBootOrderLib.h>\r
 #include <Library/UefiBootManagerLib.h>\r
 #include <Protocol/DevicePath.h>\r
+#include <Protocol/FirmwareVolume2.h>\r
 #include <Protocol/GraphicsOutput.h>\r
 #include <Protocol/LoadedImage.h>\r
 #include <Protocol/PciIo.h>\r
 #include <Protocol/PciRootBridgeIo.h>\r
+#include <Protocol/VirtioDevice.h>\r
 #include <Guid/EventGroup.h>\r
 #include <Guid/RootBridgesConnectedEventGroup.h>\r
 \r
@@ -258,6 +262,121 @@ IsPciDisplay (
 }\r
 \r
 \r
+/**\r
+  This FILTER_FUNCTION checks if a handle corresponds to a Virtio RNG device at\r
+  the VIRTIO_DEVICE_PROTOCOL level.\r
+**/\r
+STATIC\r
+BOOLEAN\r
+EFIAPI\r
+IsVirtioRng (\r
+  IN EFI_HANDLE   Handle,\r
+  IN CONST CHAR16 *ReportText\r
+  )\r
+{\r
+  EFI_STATUS             Status;\r
+  VIRTIO_DEVICE_PROTOCOL *VirtIo;\r
+\r
+  Status = gBS->HandleProtocol (Handle, &gVirtioDeviceProtocolGuid,\r
+                  (VOID**)&VirtIo);\r
+  if (EFI_ERROR (Status)) {\r
+    return FALSE;\r
+  }\r
+  return (BOOLEAN)(VirtIo->SubSystemDeviceId ==\r
+                   VIRTIO_SUBSYSTEM_ENTROPY_SOURCE);\r
+}\r
+\r
+\r
+/**\r
+  This FILTER_FUNCTION checks if a handle corresponds to a Virtio RNG device at\r
+  the EFI_PCI_IO_PROTOCOL level.\r
+**/\r
+STATIC\r
+BOOLEAN\r
+EFIAPI\r
+IsVirtioPciRng (\r
+  IN EFI_HANDLE   Handle,\r
+  IN CONST CHAR16 *ReportText\r
+  )\r
+{\r
+  EFI_STATUS          Status;\r
+  EFI_PCI_IO_PROTOCOL *PciIo;\r
+  UINT16              VendorId;\r
+  UINT16              DeviceId;\r
+  UINT8               RevisionId;\r
+  BOOLEAN             Virtio10;\r
+  UINT16              SubsystemId;\r
+\r
+  Status = gBS->HandleProtocol (Handle, &gEfiPciIoProtocolGuid,\r
+                  (VOID**)&PciIo);\r
+  if (EFI_ERROR (Status)) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Read and check VendorId.\r
+  //\r
+  Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, PCI_VENDOR_ID_OFFSET,\r
+                        1, &VendorId);\r
+  if (EFI_ERROR (Status)) {\r
+    goto PciError;\r
+  }\r
+  if (VendorId != VIRTIO_VENDOR_ID) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Read DeviceId and RevisionId.\r
+  //\r
+  Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, PCI_DEVICE_ID_OFFSET,\r
+                        1, &DeviceId);\r
+  if (EFI_ERROR (Status)) {\r
+    goto PciError;\r
+  }\r
+  Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, PCI_REVISION_ID_OFFSET,\r
+                        1, &RevisionId);\r
+  if (EFI_ERROR (Status)) {\r
+    goto PciError;\r
+  }\r
+\r
+  //\r
+  // From DeviceId and RevisionId, determine whether the device is a\r
+  // modern-only Virtio 1.0 device. In case of Virtio 1.0, DeviceId can\r
+  // immediately be restricted to VIRTIO_SUBSYSTEM_ENTROPY_SOURCE, and\r
+  // SubsystemId will only play a sanity-check role. Otherwise, DeviceId can\r
+  // only be sanity-checked, and SubsystemId will decide.\r
+  //\r
+  if (DeviceId == 0x1040 + VIRTIO_SUBSYSTEM_ENTROPY_SOURCE &&\r
+      RevisionId >= 0x01) {\r
+    Virtio10 = TRUE;\r
+  } else if (DeviceId >= 0x1000 && DeviceId <= 0x103F && RevisionId == 0x00) {\r
+    Virtio10 = FALSE;\r
+  } else {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Read and check SubsystemId as dictated by Virtio10.\r
+  //\r
+  Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16,\r
+                        PCI_SUBSYSTEM_ID_OFFSET, 1, &SubsystemId);\r
+  if (EFI_ERROR (Status)) {\r
+    goto PciError;\r
+  }\r
+  if (Virtio10 && SubsystemId >= 0x40) {\r
+    return TRUE;\r
+  }\r
+  if (!Virtio10 && SubsystemId == VIRTIO_SUBSYSTEM_ENTROPY_SOURCE) {\r
+    return TRUE;\r
+  }\r
+  return FALSE;\r
+\r
+PciError:\r
+  DEBUG ((DEBUG_ERROR, "%a: %s: %r\n", __FUNCTION__, ReportText, Status));\r
+  return FALSE;\r
+}\r
+\r
+\r
 /**\r
   This CALLBACK_FUNCTION attempts to connect a handle non-recursively, asking\r
   the matching driver to produce all first-level child handles.\r
@@ -386,6 +505,136 @@ PlatformRegisterFvBootOption (
 }\r
 \r
 \r
+/**\r
+  Remove all MemoryMapped(...)/FvFile(...) and Fv(...)/FvFile(...) boot options\r
+  whose device paths do not resolve exactly to an FvFile in the system.\r
+\r
+  This removes any boot options that point to binaries built into the firmware\r
+  and have become stale due to any of the following:\r
+  - FvMain's base address or size changed (historical),\r
+  - FvMain's FvNameGuid changed,\r
+  - the FILE_GUID of the pointed-to binary changed,\r
+  - the referenced binary is no longer built into the firmware.\r
+\r
+  EfiBootManagerFindLoadOption() used in PlatformRegisterFvBootOption() only\r
+  avoids exact duplicates.\r
+**/\r
+STATIC\r
+VOID\r
+RemoveStaleFvFileOptions (\r
+  VOID\r
+  )\r
+{\r
+  EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
+  UINTN                        BootOptionCount;\r
+  UINTN                        Index;\r
+\r
+  BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount,\r
+                  LoadOptionTypeBoot);\r
+\r
+  for (Index = 0; Index < BootOptionCount; ++Index) {\r
+    EFI_DEVICE_PATH_PROTOCOL *Node1, *Node2, *SearchNode;\r
+    EFI_STATUS               Status;\r
+    EFI_HANDLE               FvHandle;\r
+\r
+    //\r
+    // If the device path starts with neither MemoryMapped(...) nor Fv(...),\r
+    // then keep the boot option.\r
+    //\r
+    Node1 = BootOptions[Index].FilePath;\r
+    if (!(DevicePathType (Node1) == HARDWARE_DEVICE_PATH &&\r
+          DevicePathSubType (Node1) == HW_MEMMAP_DP) &&\r
+        !(DevicePathType (Node1) == MEDIA_DEVICE_PATH &&\r
+          DevicePathSubType (Node1) == MEDIA_PIWG_FW_VOL_DP)) {\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // If the second device path node is not FvFile(...), then keep the boot\r
+    // option.\r
+    //\r
+    Node2 = NextDevicePathNode (Node1);\r
+    if (DevicePathType (Node2) != MEDIA_DEVICE_PATH ||\r
+        DevicePathSubType (Node2) != MEDIA_PIWG_FW_FILE_DP) {\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // Locate the Firmware Volume2 protocol instance that is denoted by the\r
+    // boot option. If this lookup fails (i.e., the boot option references a\r
+    // firmware volume that doesn't exist), then we'll proceed to delete the\r
+    // boot option.\r
+    //\r
+    SearchNode = Node1;\r
+    Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid,\r
+                    &SearchNode, &FvHandle);\r
+\r
+    if (!EFI_ERROR (Status)) {\r
+      //\r
+      // The firmware volume was found; now let's see if it contains the FvFile\r
+      // identified by GUID.\r
+      //\r
+      EFI_FIRMWARE_VOLUME2_PROTOCOL     *FvProtocol;\r
+      MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FvFileNode;\r
+      UINTN                             BufferSize;\r
+      EFI_FV_FILETYPE                   FoundType;\r
+      EFI_FV_FILE_ATTRIBUTES            FileAttributes;\r
+      UINT32                            AuthenticationStatus;\r
+\r
+      Status = gBS->HandleProtocol (FvHandle, &gEfiFirmwareVolume2ProtocolGuid,\r
+                      (VOID **)&FvProtocol);\r
+      ASSERT_EFI_ERROR (Status);\r
+\r
+      FvFileNode = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)Node2;\r
+      //\r
+      // Buffer==NULL means we request metadata only: BufferSize, FoundType,\r
+      // FileAttributes.\r
+      //\r
+      Status = FvProtocol->ReadFile (\r
+                             FvProtocol,\r
+                             &FvFileNode->FvFileName, // NameGuid\r
+                             NULL,                    // Buffer\r
+                             &BufferSize,\r
+                             &FoundType,\r
+                             &FileAttributes,\r
+                             &AuthenticationStatus\r
+                             );\r
+      if (!EFI_ERROR (Status)) {\r
+        //\r
+        // The FvFile was found. Keep the boot option.\r
+        //\r
+        continue;\r
+      }\r
+    }\r
+\r
+    //\r
+    // Delete the boot option.\r
+    //\r
+    Status = EfiBootManagerDeleteLoadOptionVariable (\r
+               BootOptions[Index].OptionNumber, LoadOptionTypeBoot);\r
+    DEBUG_CODE (\r
+      CHAR16 *DevicePathString;\r
+\r
+      DevicePathString = ConvertDevicePathToText(BootOptions[Index].FilePath,\r
+                           FALSE, FALSE);\r
+      DEBUG ((\r
+        EFI_ERROR (Status) ? EFI_D_WARN : EFI_D_VERBOSE,\r
+        "%a: removing stale Boot#%04x %s: %r\n",\r
+        __FUNCTION__,\r
+        (UINT32)BootOptions[Index].OptionNumber,\r
+        DevicePathString == NULL ? L"<unavailable>" : DevicePathString,\r
+        Status\r
+        ));\r
+      if (DevicePathString != NULL) {\r
+        FreePool (DevicePathString);\r
+      }\r
+      );\r
+  }\r
+\r
+  EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
+}\r
+\r
+\r
 STATIC\r
 VOID\r
 PlatformRegisterOptionsAndKeys (\r
@@ -423,12 +672,6 @@ PlatformRegisterOptionsAndKeys (
              NULL, (UINT16) BootOption.OptionNumber, 0, &Esc, NULL\r
              );\r
   ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);\r
-  //\r
-  // Register UEFI Shell\r
-  //\r
-  PlatformRegisterFvBootOption (\r
-    PcdGetPtr (PcdShellFile), L"EFI Internal Shell", LOAD_OPTION_ACTIVE\r
-    );\r
 }\r
 \r
 \r
@@ -452,11 +695,18 @@ PlatformBootManagerBeforeConsole (
   VOID\r
   )\r
 {\r
+  RETURN_STATUS PcdStatus;\r
+\r
   //\r
   // Signal EndOfDxe PI Event\r
   //\r
   EfiEventGroupSignal (&gEfiEndOfDxeEventGroupGuid);\r
 \r
+  //\r
+  // Dispatch deferred images after EndOfDxe event.\r
+  //\r
+  EfiBootManagerDispatchDeferredImages ();\r
+\r
   //\r
   // Locate the PCI root bridges and make the PCI bus driver connect each,\r
   // non-recursively. This will produce a number of child handles with PciIo on\r
@@ -503,12 +753,26 @@ PlatformBootManagerBeforeConsole (
   //\r
   // Set the front page timeout from the QEMU configuration.\r
   //\r
-  PcdSet16 (PcdPlatformBootTimeOut, GetFrontPageTimeoutFromQemu ());\r
+  PcdStatus = PcdSet16S (PcdPlatformBootTimeOut,\r
+                GetFrontPageTimeoutFromQemu ());\r
+  ASSERT_RETURN_ERROR (PcdStatus);\r
 \r
   //\r
   // Register platform-specific boot options and keyboard shortcuts.\r
   //\r
   PlatformRegisterOptionsAndKeys ();\r
+\r
+  //\r
+  // At this point, VIRTIO_DEVICE_PROTOCOL instances exist only for Virtio MMIO\r
+  // transports. Install EFI_RNG_PROTOCOL instances on Virtio MMIO RNG devices.\r
+  //\r
+  FilterAndProcess (&gVirtioDeviceProtocolGuid, IsVirtioRng, Connect);\r
+\r
+  //\r
+  // Install both VIRTIO_DEVICE_PROTOCOL and (dependent) EFI_RNG_PROTOCOL\r
+  // instances on Virtio PCI RNG devices.\r
+  //\r
+  FilterAndProcess (&gEfiPciIoProtocolGuid, IsVirtioPciRng, Connect);\r
 }\r
 \r
 /**\r
@@ -528,81 +792,47 @@ PlatformBootManagerAfterConsole (
   VOID\r
   )\r
 {\r
+  RETURN_STATUS Status;\r
+\r
   //\r
   // Show the splash screen.\r
   //\r
-  EnableQuietBoot (PcdGetPtr (PcdLogoFile));\r
+  BootLogoEnableLogo ();\r
 \r
   //\r
-  // Connect the rest of the devices.\r
+  // Process QEMU's -kernel command line option. The kernel booted this way\r
+  // will receive ACPI tables: in PlatformBootManagerBeforeConsole(), we\r
+  // connected any and all PCI root bridges, and then signaled the ACPI\r
+  // platform driver.\r
   //\r
-  BdsLibConnectAll ();\r
+  TryRunningQemuKernel ();\r
 \r
   //\r
-  // Process QEMU's -kernel command line option. Note that the kernel booted\r
-  // this way should receive ACPI tables, which is why we connect all devices\r
-  // first (see above) -- PCI enumeration blocks ACPI table installation, if\r
-  // there is a PCI host.\r
+  // Connect the purported boot devices.\r
   //\r
-  TryRunningQemuKernel ();\r
+  Status = ConnectDevicesFromQemu ();\r
+  if (RETURN_ERROR (Status)) {\r
+    //\r
+    // Connect the rest of the devices.\r
+    //\r
+    EfiBootManagerConnectAll ();\r
+  }\r
 \r
-  BdsLibEnumerateAllBootOption (BootOptionList);\r
-  SetBootOrderFromQemu (BootOptionList);\r
   //\r
-  // The BootOrder variable may have changed, reload the in-memory list with\r
-  // it.\r
+  // Enumerate all possible boot options, then filter and reorder them based on\r
+  // the QEMU configuration.\r
   //\r
-  BdsLibBuildOptionFromVar (BootOptionList, L"BootOrder");\r
-}\r
-\r
-/**\r
-  Hook point after a boot attempt succeeds. We don't expect a boot option to\r
-  return, so the UEFI 2.0 specification defines that you will default to an\r
-  interactive mode and stop processing the BootOrder list in this case. This\r
-  is also a platform implementation and can be customized by IBV/OEM.\r
-\r
-  @param  Option                  Pointer to Boot Option that succeeded to boot.\r
-\r
-**/\r
-VOID\r
-EFIAPI\r
-PlatformBdsBootSuccess (\r
-  IN  BDS_COMMON_OPTION *Option\r
-  )\r
-{\r
-}\r
+  EfiBootManagerRefreshAllBootOption ();\r
 \r
-/**\r
-  Hook point after a boot attempt fails.\r
-\r
-  @param  Option                  Pointer to Boot Option that failed to boot.\r
-  @param  Status                  Status returned from failed boot.\r
-  @param  ExitData                Exit data returned from failed boot.\r
-  @param  ExitDataSize            Exit data size returned from failed boot.\r
-\r
-**/\r
-VOID\r
-EFIAPI\r
-PlatformBdsBootFail (\r
-  IN  BDS_COMMON_OPTION  *Option,\r
-  IN  EFI_STATUS         Status,\r
-  IN  CHAR16             *ExitData,\r
-  IN  UINTN              ExitDataSize\r
-  )\r
-{\r
-}\r
+  //\r
+  // Register UEFI Shell\r
+  //\r
+  PlatformRegisterFvBootOption (\r
+    &gUefiShellFileGuid, L"EFI Internal Shell", LOAD_OPTION_ACTIVE\r
+    );\r
 \r
-/**\r
-  This function locks platform flash that is not allowed to be updated during normal boot path.\r
-  The flash layout is platform specific.\r
-**/\r
-VOID\r
-EFIAPI\r
-PlatformBdsLockNonUpdatableFlash (\r
-  VOID\r
-  )\r
-{\r
-  return;\r
+  RemoveStaleFvFileOptions ();\r
+  SetBootOrderFromQemu ();\r
 }\r
 \r
 /**\r
@@ -617,4 +847,21 @@ PlatformBootManagerWaitCallback (
   UINT16          TimeoutRemain\r
   )\r
 {\r
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Black;\r
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION White;\r
+  UINT16                              Timeout;\r
+\r
+  Timeout = PcdGet16 (PcdPlatformBootTimeOut);\r
+\r
+  Black.Raw = 0x00000000;\r
+  White.Raw = 0x00FFFFFF;\r
+\r
+  BootLogoUpdateProgress (\r
+    White.Pixel,\r
+    Black.Pixel,\r
+    L"Start boot option",\r
+    White.Pixel,\r
+    (Timeout - TimeoutRemain) * 100 / Timeout,\r
+    0\r
+    );\r
 }\r