Skip to content

Commit

Permalink
OvmfPkg/QemuFlashFvbServicesRuntimeDxe: Bypass flash detection with S…
Browse files Browse the repository at this point in the history
…EV-ES

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>
  • Loading branch information
tlendacky authored and mergify[bot] committed Aug 17, 2020
1 parent e2db781 commit 437eb3f
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
UefiCpuPkg/UefiCpuPkg.dec
OvmfPkg/OvmfPkg.dec

[LibraryClasses]
Expand All @@ -52,6 +53,7 @@
UefiBootServicesTableLib
UefiDriverEntryPoint
UefiRuntimeLib
VmgExitLib

[Guids]
gEfiEventVirtualAddressChangeGuid # ALWAYS_CONSUMED
Expand Down
23 changes: 20 additions & 3 deletions OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/MemEncryptSevLib.h>
#include <Library/PcdLib.h>

#include "QemuFlash.h"
Expand Down Expand Up @@ -80,6 +81,21 @@ QemuFlashDetected (

DEBUG ((DEBUG_INFO, "QEMU Flash: Attempting flash detection at %p\n", Ptr));

if (MemEncryptSevEsIsEnabled ()) {
//
// When SEV-ES is enabled, the check below can result in an infinite
// loop with respect to a nested page fault. When the memslot is mapped
// read-only, the nested page table entry is read-only. The check below
// will cause a nested page fault that cannot be emulated, causing
// the instruction to retried over and over. For SEV-ES, acknowledge that
// the FD appears as ROM and not as FLASH, but report FLASH anyway because
// FLASH behavior can be simulated using VMGEXIT.
//
DEBUG ((DEBUG_INFO,
"QEMU Flash: SEV-ES enabled, assuming FD behaves as FLASH\n"));
return TRUE;
}

OriginalUint8 = *Ptr;
*Ptr = CLEAR_STATUS_CMD;
ProbeUint8 = *Ptr;
Expand Down Expand Up @@ -181,16 +197,17 @@ QemuFlashWrite (
//
Ptr = QemuFlashPtr (Lba, Offset);
for (Loop = 0; Loop < *NumBytes; Loop++) {
*Ptr = WRITE_BYTE_CMD;
*Ptr = Buffer[Loop];
QemuFlashPtrWrite (Ptr, WRITE_BYTE_CMD);
QemuFlashPtrWrite (Ptr, Buffer[Loop]);

Ptr++;
}

//
// Restore flash to read mode
//
if (*NumBytes > 0) {
*(Ptr - 1) = READ_ARRAY_CMD;
QemuFlashPtrWrite (Ptr - 1, READ_ARRAY_CMD);
}

return EFI_SUCCESS;
Expand Down
13 changes: 13 additions & 0 deletions OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,5 +89,18 @@ QemuFlashBeforeProbe (
IN UINTN FdBlockCount
);

/**
Write to QEMU Flash
@param[in] Ptr Pointer to the location to write.
@param[in] Value The value to write.
**/
VOID
QemuFlashPtrWrite (
IN volatile UINT8 *Ptr,
IN UINT8 Value
);

#endif

40 changes: 40 additions & 0 deletions OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
**/

#include <Library/UefiRuntimeLib.h>
#include <Library/MemEncryptSevLib.h>
#include <Library/VmgExitLib.h>
#include <Register/Amd/Msr.h>

#include "QemuFlash.h"

Expand All @@ -32,3 +35,40 @@ QemuFlashBeforeProbe (
// Do nothing
//
}

/**
Write to QEMU Flash
@param[in] Ptr Pointer to the location to write.
@param[in] Value The value to write.
**/
VOID
QemuFlashPtrWrite (
IN volatile UINT8 *Ptr,
IN UINT8 Value
)
{
if (MemEncryptSevEsIsEnabled ()) {
MSR_SEV_ES_GHCB_REGISTER Msr;
GHCB *Ghcb;

Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
Ghcb = Msr.Ghcb;

//
// Writing to flash is emulated by the hypervisor through the use of write
// protection. This won't work for an SEV-ES guest because the write won't
// be recognized as a true MMIO write, which would result in the required
// #VC exception. Instead, use the the VMGEXIT MMIO write support directly
// to perform the update.
//
VmgInit (Ghcb);
Ghcb->SharedBuffer[0] = Value;
Ghcb->SaveArea.SwScratch = (UINT64) (UINTN) Ghcb->SharedBuffer;
VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, (UINT64) (UINTN) Ptr, 1);
VmgDone (Ghcb);
} else {
*Ptr = Value;
}
}
16 changes: 16 additions & 0 deletions OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashSmm.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,19 @@ QemuFlashBeforeProbe (
);
ASSERT_EFI_ERROR (Status);
}

/**
Write to QEMU Flash
@param[in] Ptr Pointer to the location to write.
@param[in] Value The value to write.
**/
VOID
QemuFlashPtrWrite (
IN volatile UINT8 *Ptr,
IN UINT8 Value
)
{
*Ptr = Value;
}

0 comments on commit 437eb3f

Please sign in to comment.