]> git.proxmox.com Git - mirror_edk2.git/commitdiff
OvmfPkg/QemuFlashFvbServicesRuntimeDxe: Bypass flash detection with SEV-ES
authorTom Lendacky <thomas.lendacky@amd.com>
Wed, 12 Aug 2020 20:21:42 +0000 (15:21 -0500)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Mon, 17 Aug 2020 02:46:39 +0000 (02:46 +0000)
BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198

The flash detection routine will attempt to determine how the flash
device behaves (e.g. ROM, RAM, Flash). But when SEV-ES is enabled and
the flash device behaves as a ROM device (meaning it is marked read-only
by the hypervisor), this check may result in an infinite nested page fault
because of the attempted write. Since the instruction cannot be emulated
when SEV-ES is enabled, the RIP is never advanced, resulting in repeated
nested page faults.

When SEV-ES is enabled, exit the flash detection early and assume that
the FD behaves as Flash. This will result in QemuFlashWrite() being called
to store EFI variables, which will also result in an infinite nested page
fault when the write is performed. In this case, update QemuFlashWrite()
to use the VMGEXIT MMIO write support to have the hypervisor perform the
write without having to emulate the instruction.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Regression-tested-by: Laszlo Ersek <lersek@redhat.com>
OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashSmm.c

index 72cabba4357d9ee1a6d97844569cbef18036547d..8bb2325157eacee3003d205b5ce54a2f605ec749 100644 (file)
@@ -38,6 +38,7 @@
 [Packages]\r
   MdePkg/MdePkg.dec\r
   MdeModulePkg/MdeModulePkg.dec\r
+  UefiCpuPkg/UefiCpuPkg.dec\r
   OvmfPkg/OvmfPkg.dec\r
 \r
 [LibraryClasses]\r
@@ -52,6 +53,7 @@
   UefiBootServicesTableLib\r
   UefiDriverEntryPoint\r
   UefiRuntimeLib\r
+  VmgExitLib\r
 \r
 [Guids]\r
   gEfiEventVirtualAddressChangeGuid   # ALWAYS_CONSUMED\r
index 1b0d6c053f1aa526460519fd100f8c26a2acda1a..0d29bf701aca1fb2424c3e4d9a847150d879882d 100644 (file)
@@ -9,6 +9,7 @@
 \r
 #include <Library/BaseMemoryLib.h>\r
 #include <Library/DebugLib.h>\r
+#include <Library/MemEncryptSevLib.h>\r
 #include <Library/PcdLib.h>\r
 \r
 #include "QemuFlash.h"\r
@@ -80,6 +81,21 @@ QemuFlashDetected (
 \r
   DEBUG ((DEBUG_INFO, "QEMU Flash: Attempting flash detection at %p\n", Ptr));\r
 \r
+  if (MemEncryptSevEsIsEnabled ()) {\r
+    //\r
+    // When SEV-ES is enabled, the check below can result in an infinite\r
+    // loop with respect to a nested page fault. When the memslot is mapped\r
+    // read-only, the nested page table entry is read-only. The check below\r
+    // will cause a nested page fault that cannot be emulated, causing\r
+    // the instruction to retried over and over. For SEV-ES, acknowledge that\r
+    // the FD appears as ROM and not as FLASH, but report FLASH anyway because\r
+    // FLASH behavior can be simulated using VMGEXIT.\r
+    //\r
+    DEBUG ((DEBUG_INFO,\r
+      "QEMU Flash: SEV-ES enabled, assuming FD behaves as FLASH\n"));\r
+    return TRUE;\r
+  }\r
+\r
   OriginalUint8 = *Ptr;\r
   *Ptr = CLEAR_STATUS_CMD;\r
   ProbeUint8 = *Ptr;\r
@@ -181,8 +197,9 @@ QemuFlashWrite (
   //\r
   Ptr = QemuFlashPtr (Lba, Offset);\r
   for (Loop = 0; Loop < *NumBytes; Loop++) {\r
-    *Ptr = WRITE_BYTE_CMD;\r
-    *Ptr = Buffer[Loop];\r
+    QemuFlashPtrWrite (Ptr, WRITE_BYTE_CMD);\r
+    QemuFlashPtrWrite (Ptr, Buffer[Loop]);\r
+\r
     Ptr++;\r
   }\r
 \r
@@ -190,7 +207,7 @@ QemuFlashWrite (
   // Restore flash to read mode\r
   //\r
   if (*NumBytes > 0) {\r
-    *(Ptr - 1) = READ_ARRAY_CMD;\r
+    QemuFlashPtrWrite (Ptr - 1, READ_ARRAY_CMD);\r
   }\r
 \r
   return EFI_SUCCESS;\r
index f1afabcbe6ae73d190c5e1446296e020e48cb6ca..219d0d6e83cfdf0848b788f64695c9a6d8539ac5 100644 (file)
@@ -89,5 +89,18 @@ QemuFlashBeforeProbe (
   IN  UINTN                   FdBlockCount\r
   );\r
 \r
+/**\r
+  Write to QEMU Flash\r
+\r
+  @param[in] Ptr    Pointer to the location to write.\r
+  @param[in] Value  The value to write.\r
+\r
+**/\r
+VOID\r
+QemuFlashPtrWrite (\r
+  IN        volatile UINT8    *Ptr,\r
+  IN        UINT8             Value\r
+  );\r
+\r
 #endif\r
 \r
index 5aabe9d7b59c94e547f1ba681153a722b54da916..565383ee26d2324acc1a747d92f0d4825c6a5fd8 100644 (file)
@@ -10,6 +10,9 @@
 **/\r
 \r
 #include <Library/UefiRuntimeLib.h>\r
+#include <Library/MemEncryptSevLib.h>\r
+#include <Library/VmgExitLib.h>\r
+#include <Register/Amd/Msr.h>\r
 \r
 #include "QemuFlash.h"\r
 \r
@@ -32,3 +35,40 @@ QemuFlashBeforeProbe (
   // Do nothing\r
   //\r
 }\r
+\r
+/**\r
+  Write to QEMU Flash\r
+\r
+  @param[in] Ptr    Pointer to the location to write.\r
+  @param[in] Value  The value to write.\r
+\r
+**/\r
+VOID\r
+QemuFlashPtrWrite (\r
+  IN        volatile UINT8    *Ptr,\r
+  IN        UINT8             Value\r
+  )\r
+{\r
+  if (MemEncryptSevEsIsEnabled ()) {\r
+    MSR_SEV_ES_GHCB_REGISTER  Msr;\r
+    GHCB                      *Ghcb;\r
+\r
+    Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);\r
+    Ghcb = Msr.Ghcb;\r
+\r
+    //\r
+    // Writing to flash is emulated by the hypervisor through the use of write\r
+    // protection. This won't work for an SEV-ES guest because the write won't\r
+    // be recognized as a true MMIO write, which would result in the required\r
+    // #VC exception. Instead, use the the VMGEXIT MMIO write support directly\r
+    // to perform the update.\r
+    //\r
+    VmgInit (Ghcb);\r
+    Ghcb->SharedBuffer[0] = Value;\r
+    Ghcb->SaveArea.SwScratch = (UINT64) (UINTN) Ghcb->SharedBuffer;\r
+    VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, (UINT64) (UINTN) Ptr, 1);\r
+    VmgDone (Ghcb);\r
+  } else {\r
+    *Ptr = Value;\r
+  }\r
+}\r
index 7eb426e03855749a269ea983fc8b1189247649cd..7eb80bfeffae591dcc1506b0a46a4962beb08006 100644 (file)
@@ -46,3 +46,19 @@ QemuFlashBeforeProbe (
              );\r
   ASSERT_EFI_ERROR (Status);\r
 }\r
+\r
+/**\r
+  Write to QEMU Flash\r
+\r
+  @param[in] Ptr    Pointer to the location to write.\r
+  @param[in] Value  The value to write.\r
+\r
+**/\r
+VOID\r
+QemuFlashPtrWrite (\r
+  IN        volatile UINT8    *Ptr,\r
+  IN        UINT8             Value\r
+  )\r
+{\r
+  *Ptr = Value;\r
+}\r