]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c
OvmfPkg/PlatformBds: Implement PlatformBootManagerUnableToBoot
[mirror_edk2.git] / OvmfPkg / Library / PlatformBootManagerLib / BdsPlatform.c
index b624b8f2253545171ee04a176091a8032da31108..b2faa797c61bab7267992f08d06789b9a6f3dcc5 100644 (file)
@@ -1,7 +1,7 @@
 /** @file\r
   Platform BDS customizations.\r
 \r
-  Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>\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
   distribution.  The full text of the license may be found at\r
@@ -16,6 +16,7 @@
 #include <Guid/XenInfo.h>\r
 #include <Guid/RootBridgesConnectedEventGroup.h>\r
 #include <Protocol/FirmwareVolume2.h>\r
+#include <Library/Tcg2PhysicalPresenceLib.h>\r
 \r
 \r
 //\r
@@ -26,7 +27,6 @@ VOID          *mEfiDevPathNotifyReg;
 EFI_EVENT     mEfiDevPathEvent;\r
 VOID          *mEmuVariableEventReg;\r
 EFI_EVENT     mEmuVariableEvent;\r
-BOOLEAN       mDetectVgaOnly;\r
 UINT16        mHostBridgeDevId;\r
 \r
 //\r
@@ -319,6 +319,15 @@ ConnectRootBridge (
   IN VOID        *Context\r
   );\r
 \r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+ConnectVirtioPciRng (\r
+  IN EFI_HANDLE Handle,\r
+  IN VOID       *Instance,\r
+  IN VOID       *Context\r
+  );\r
+\r
 STATIC\r
 VOID\r
 SaveS3BootScript (\r
@@ -400,6 +409,13 @@ PlatformBootManagerBeforeConsole (
   ASSERT_RETURN_ERROR (PcdStatus);\r
 \r
   PlatformRegisterOptionsAndKeys ();\r
+\r
+  //\r
+  // Install both VIRTIO_DEVICE_PROTOCOL and (dependent) EFI_RNG_PROTOCOL\r
+  // instances on Virtio PCI RNG devices.\r
+  //\r
+  VisitAllInstancesOfProtocol (&gEfiPciIoProtocolGuid, ConnectVirtioPciRng,\r
+    NULL);\r
 }\r
 \r
 \r
@@ -428,6 +444,95 @@ ConnectRootBridge (
 }\r
 \r
 \r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+ConnectVirtioPciRng (\r
+  IN EFI_HANDLE Handle,\r
+  IN VOID       *Instance,\r
+  IN VOID       *Context\r
+  )\r
+{\r
+  EFI_PCI_IO_PROTOCOL *PciIo;\r
+  EFI_STATUS          Status;\r
+  UINT16              VendorId;\r
+  UINT16              DeviceId;\r
+  UINT8               RevisionId;\r
+  BOOLEAN             Virtio10;\r
+  UINT16              SubsystemId;\r
+\r
+  PciIo = Instance;\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 Error;\r
+  }\r
+  if (VendorId != VIRTIO_VENDOR_ID) {\r
+    return EFI_SUCCESS;\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 Error;\r
+  }\r
+  Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, PCI_REVISION_ID_OFFSET,\r
+                        1, &RevisionId);\r
+  if (EFI_ERROR (Status)) {\r
+    goto Error;\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 EFI_SUCCESS;\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 Error;\r
+  }\r
+  if ((Virtio10 && SubsystemId >= 0x40) ||\r
+      (!Virtio10 && SubsystemId == VIRTIO_SUBSYSTEM_ENTROPY_SOURCE)) {\r
+    Status = gBS->ConnectController (\r
+                    Handle, // ControllerHandle\r
+                    NULL,   // DriverImageHandle -- connect all drivers\r
+                    NULL,   // RemainingDevicePath -- produce all child handles\r
+                    FALSE   // Recursive -- don't follow child handles\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error;\r
+    }\r
+  }\r
+  return EFI_SUCCESS;\r
+\r
+Error:\r
+  DEBUG ((DEBUG_ERROR, "%a: %r\n", __FUNCTION__, Status));\r
+  return Status;\r
+}\r
+\r
+\r
 /**\r
   Add IsaKeyboard to ConIn; add IsaSerial to ConOut, ConIn, ErrOut.\r
 \r
@@ -830,35 +935,33 @@ DetectAndPreparePlatformPciDevicePath (
     );\r
   ASSERT_EFI_ERROR (Status);\r
 \r
-  if (!mDetectVgaOnly) {\r
+  //\r
+  // Here we decide whether it is LPC Bridge\r
+  //\r
+  if ((IS_PCI_LPC (Pci)) ||\r
+      ((IS_PCI_ISA_PDECODE (Pci)) &&\r
+       (Pci->Hdr.VendorId == 0x8086) &&\r
+       (Pci->Hdr.DeviceId == 0x7000)\r
+      )\r
+     ) {\r
     //\r
-    // Here we decide whether it is LPC Bridge\r
+    // Add IsaKeyboard to ConIn,\r
+    // add IsaSerial to ConOut, ConIn, ErrOut\r
     //\r
-    if ((IS_PCI_LPC (Pci)) ||\r
-        ((IS_PCI_ISA_PDECODE (Pci)) &&\r
-         (Pci->Hdr.VendorId == 0x8086) &&\r
-         (Pci->Hdr.DeviceId == 0x7000)\r
-        )\r
-       ) {\r
-      //\r
-      // Add IsaKeyboard to ConIn,\r
-      // add IsaSerial to ConOut, ConIn, ErrOut\r
-      //\r
-      DEBUG ((EFI_D_INFO, "Found LPC Bridge device\n"));\r
-      PrepareLpcBridgeDevicePath (Handle);\r
-      return EFI_SUCCESS;\r
-    }\r
+    DEBUG ((EFI_D_INFO, "Found LPC Bridge device\n"));\r
+    PrepareLpcBridgeDevicePath (Handle);\r
+    return EFI_SUCCESS;\r
+  }\r
+  //\r
+  // Here we decide which Serial device to enable in PCI bus\r
+  //\r
+  if (IS_PCI_16550SERIAL (Pci)) {\r
     //\r
-    // Here we decide which Serial device to enable in PCI bus\r
+    // Add them to ConOut, ConIn, ErrOut.\r
     //\r
-    if (IS_PCI_16550SERIAL (Pci)) {\r
-      //\r
-      // Add them to ConOut, ConIn, ErrOut.\r
-      //\r
-      DEBUG ((EFI_D_INFO, "Found PCI 16550 SERIAL device\n"));\r
-      PreparePciSerialDevicePath (Handle);\r
-      return EFI_SUCCESS;\r
-    }\r
+    DEBUG ((EFI_D_INFO, "Found PCI 16550 SERIAL device\n"));\r
+    PreparePciSerialDevicePath (Handle);\r
+    return EFI_SUCCESS;\r
   }\r
 \r
   //\r
@@ -877,26 +980,6 @@ DetectAndPreparePlatformPciDevicePath (
 }\r
 \r
 \r
-/**\r
-  Do platform specific PCI Device check and add them to ConOut, ConIn, ErrOut\r
-\r
-  @param[in]  DetectVgaOnly - Only detect VGA device if it's TRUE.\r
-\r
-  @retval EFI_SUCCESS - PCI Device check and Console variable update\r
-                        successfully.\r
-  @retval EFI_STATUS - PCI Device check or Console variable update fail.\r
-\r
-**/\r
-EFI_STATUS\r
-DetectAndPreparePlatformPciDevicePaths (\r
-  BOOLEAN DetectVgaOnly\r
-  )\r
-{\r
-  mDetectVgaOnly = DetectVgaOnly;\r
-  return VisitAllPciInstances (DetectAndPreparePlatformPciDevicePath);\r
-}\r
-\r
-\r
 /**\r
   Connect the predefined platform default console device.\r
 \r
@@ -910,50 +993,34 @@ PlatformInitializeConsole (
   )\r
 {\r
   UINTN                              Index;\r
-  EFI_DEVICE_PATH_PROTOCOL           *VarConout;\r
-  EFI_DEVICE_PATH_PROTOCOL           *VarConin;\r
 \r
   //\r
-  // Connect RootBridge\r
+  // Do platform specific PCI Device check and add them to ConOut, ConIn,\r
+  // ErrOut\r
   //\r
-  GetEfiGlobalVariable2 (EFI_CON_OUT_VARIABLE_NAME, (VOID **) &VarConout,\r
-    NULL);\r
-  GetEfiGlobalVariable2 (EFI_CON_IN_VARIABLE_NAME, (VOID **) &VarConin, NULL);\r
-\r
-  if (VarConout == NULL || VarConin == NULL) {\r
-    //\r
-    // Do platform specific PCI Device check and add them to ConOut, ConIn,\r
-    // ErrOut\r
-    //\r
-    DetectAndPreparePlatformPciDevicePaths (FALSE);\r
+  VisitAllPciInstances (DetectAndPreparePlatformPciDevicePath);\r
 \r
+  //\r
+  // Have chance to connect the platform default console,\r
+  // the platform default console is the minimum device group\r
+  // the platform should support\r
+  //\r
+  for (Index = 0; PlatformConsole[Index].DevicePath != NULL; ++Index) {\r
     //\r
-    // Have chance to connect the platform default console,\r
-    // the platform default console is the minimum device group\r
-    // the platform should support\r
+    // Update the console variable with the connect type\r
     //\r
-    for (Index = 0; PlatformConsole[Index].DevicePath != NULL; ++Index) {\r
-      //\r
-      // Update the console variable with the connect type\r
-      //\r
-      if ((PlatformConsole[Index].ConnectType & CONSOLE_IN) == CONSOLE_IN) {\r
-        EfiBootManagerUpdateConsoleVariable (ConIn,\r
-          PlatformConsole[Index].DevicePath, NULL);\r
-      }\r
-      if ((PlatformConsole[Index].ConnectType & CONSOLE_OUT) == CONSOLE_OUT) {\r
-        EfiBootManagerUpdateConsoleVariable (ConOut,\r
-          PlatformConsole[Index].DevicePath, NULL);\r
-      }\r
-      if ((PlatformConsole[Index].ConnectType & STD_ERROR) == STD_ERROR) {\r
-        EfiBootManagerUpdateConsoleVariable (ErrOut,\r
-          PlatformConsole[Index].DevicePath, NULL);\r
-      }\r
+    if ((PlatformConsole[Index].ConnectType & CONSOLE_IN) == CONSOLE_IN) {\r
+      EfiBootManagerUpdateConsoleVariable (ConIn,\r
+        PlatformConsole[Index].DevicePath, NULL);\r
+    }\r
+    if ((PlatformConsole[Index].ConnectType & CONSOLE_OUT) == CONSOLE_OUT) {\r
+      EfiBootManagerUpdateConsoleVariable (ConOut,\r
+        PlatformConsole[Index].DevicePath, NULL);\r
+    }\r
+    if ((PlatformConsole[Index].ConnectType & STD_ERROR) == STD_ERROR) {\r
+      EfiBootManagerUpdateConsoleVariable (ErrOut,\r
+        PlatformConsole[Index].DevicePath, NULL);\r
     }\r
-  } else {\r
-    //\r
-    // Only detect VGA device and add them to ConOut\r
-    //\r
-    DetectAndPreparePlatformPciDevicePaths (TRUE);\r
   }\r
 }\r
 \r
@@ -1450,15 +1517,20 @@ PlatformBootManagerAfterConsole (
   PciAcpiInitialization ();\r
 \r
   //\r
-  // Perform some platform specific connect sequence\r
+  // Process TPM PPI request\r
   //\r
-  PlatformBdsConnectSequence ();\r
+  Tcg2PhysicalPresenceLibProcessRequest (NULL);\r
 \r
   //\r
   // Process QEMU's -kernel command line option\r
   //\r
   TryRunningQemuKernel ();\r
 \r
+  //\r
+  // Perform some platform specific connect sequence\r
+  //\r
+  PlatformBdsConnectSequence ();\r
+\r
   EfiBootManagerRefreshAllBootOption ();\r
 \r
   //\r
@@ -1604,3 +1676,62 @@ PlatformBootManagerWaitCallback (
     );\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