]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBm.c
ArmVirtPkg/PlatformBDS: Implement PlatformBootManagerUnableToBoot
[mirror_edk2.git] / ArmVirtPkg / Library / PlatformBootManagerLib / PlatformBm.c
index 5d7c2a44479995e8a6e4b842112d4365fe3c7955..bb07f5e22b753bd0739aa4bffe8c7257ea186ea3 100644 (file)
@@ -3,7 +3,7 @@
 \r
   Copyright (C) 2015-2016, Red Hat, Inc.\r
   Copyright (c) 2014, ARM Ltd. All rights reserved.<BR>\r
-  Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>\r
 \r
   This program and the accompanying materials are licensed and made available\r
   under the terms and conditions of the BSD License which accompanies this\r
@@ -16,6 +16,7 @@
 **/\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
@@ -27,6 +28,7 @@
 #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
@@ -260,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
@@ -578,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
@@ -629,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
@@ -654,29 +792,31 @@ PlatformBootManagerAfterConsole (
   VOID\r
   )\r
 {\r
+  RETURN_STATUS Status;\r
+\r
   //\r
   // Show the splash screen.\r
   //\r
-  // BootLogoEnableLogo (\r
-  //  ImageFormatBmp,                          // ImageFormat\r
-  //  PcdGetPtr (PcdLogoFile),                 // Logo\r
-  //  EdkiiPlatformLogoDisplayAttributeCenter, // Attribute\r
-  //  0,                                       // OffsetX\r
-  //  0                                        // OffsetY\r
-  //  );\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
-  EfiBootManagerConnectAll ();\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
   //\r
   // Enumerate all possible boot options, then filter and reorder them based on\r
@@ -688,7 +828,7 @@ PlatformBootManagerAfterConsole (
   // Register UEFI Shell\r
   //\r
   PlatformRegisterFvBootOption (\r
-    PcdGetPtr (PcdShellFile), L"EFI Internal Shell", LOAD_OPTION_ACTIVE\r
+    &gUefiShellFileGuid, L"EFI Internal Shell", LOAD_OPTION_ACTIVE\r
     );\r
 \r
   RemoveStaleFvFileOptions ();\r
@@ -725,3 +865,63 @@ PlatformBootManagerWaitCallback (
     0\r
     );\r
 }\r
+\r
+/**\r
+  The function is called when no boot option could be launched,\r
+  including platform recovery options and options pointing to applications\r
+  built into firmware volumes.\r
+\r
+  If this function returns, BDS attempts to enter an infinite loop.\r
+**/\r
+VOID\r
+EFIAPI\r
+PlatformBootManagerUnableToBoot (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  EFI_INPUT_KEY                Key;\r
+  EFI_BOOT_MANAGER_LOAD_OPTION BootManagerMenu;\r
+  UINTN                        Index;\r
+\r
+  //\r
+  // BootManagerMenu doesn't contain the correct information when return status\r
+  // is EFI_NOT_FOUND.\r
+  //\r
+  Status = EfiBootManagerGetBootManagerMenu (&BootManagerMenu);\r
+  if (EFI_ERROR (Status)) {\r
+    return;\r
+  }\r
+  //\r
+  // Normally BdsDxe does not print anything to the system console, but this is\r
+  // a last resort -- the end-user will likely not see any DEBUG messages\r
+  // logged in this situation.\r
+  //\r
+  // AsciiPrint() will NULL-check gST->ConOut internally. We check gST->ConIn\r
+  // here to see if it makes sense to request and wait for a keypress.\r
+  //\r
+  if (gST->ConIn != NULL) {\r
+    AsciiPrint (\r
+      "%a: No bootable option or device was found.\n"\r
+      "%a: Press any key to enter the Boot Manager Menu.\n",\r
+      gEfiCallerBaseName,\r
+      gEfiCallerBaseName\r
+      );\r
+    Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &Index);\r
+    ASSERT_EFI_ERROR (Status);\r
+    ASSERT (Index == 0);\r
+\r
+    //\r
+    // Drain any queued keys.\r
+    //\r
+    while (!EFI_ERROR (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key))) {\r
+      //\r
+      // just throw away Key\r
+      //\r
+    }\r
+  }\r
+\r
+  for (;;) {\r
+    EfiBootManagerBoot (&BootManagerMenu);\r
+  }\r
+}\r