]> git.proxmox.com Git - mirror_edk2.git/commitdiff
OvmfPkg/Sec: Add #VC exception handling for Sec phase
authorTom Lendacky <thomas.lendacky@amd.com>
Wed, 12 Aug 2020 20:21:41 +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

An SEV-ES guest will generate a #VC exception when it encounters a
non-automatic exit (NAE) event. It is expected that the #VC exception
handler will communicate with the hypervisor using the GHCB to handle
the NAE event.

NAE events can occur during the Sec phase, so initialize exception
handling early in the OVMF Sec support.

Before establishing the exception handling, validate that the supported
version of the SEV-ES protocol in OVMF is supported by the hypervisor.

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/Sec/SecMain.c
OvmfPkg/Sec/SecMain.inf

index 6dea6e771a29db4d388b55bbee0f473cc19960e6..169c04b9cec71a6dc16712039c9afaf2af1c696e 100644 (file)
@@ -24,6 +24,9 @@
 #include <Library/PeCoffExtraActionLib.h>\r
 #include <Library/ExtractGuidedSectionLib.h>\r
 #include <Library/LocalApicLib.h>\r
+#include <Library/CpuExceptionHandlerLib.h>\r
+#include <Register/Amd/Ghcb.h>\r
+#include <Register/Amd/Msr.h>\r
 \r
 #include <Ppi/TemporaryRamSupport.h>\r
 \r
@@ -34,6 +37,10 @@ typedef struct _SEC_IDT_TABLE {
   IA32_IDT_GATE_DESCRIPTOR  IdtTable[SEC_IDT_ENTRY_COUNT];\r
 } SEC_IDT_TABLE;\r
 \r
+typedef struct _SEC_SEV_ES_WORK_AREA {\r
+  UINT8  SevEsEnabled;\r
+} SEC_SEV_ES_WORK_AREA;\r
+\r
 VOID\r
 EFIAPI\r
 SecStartupPhase2 (\r
@@ -712,6 +719,120 @@ FindAndReportEntryPoints (
   return;\r
 }\r
 \r
+/**\r
+  Handle an SEV-ES/GHCB protocol check failure.\r
+\r
+  Notify the hypervisor using the VMGEXIT instruction that the SEV-ES guest\r
+  wishes to be terminated.\r
+\r
+  @param[in] ReasonCode  Reason code to provide to the hypervisor for the\r
+                         termination request.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+SevEsProtocolFailure (\r
+  IN UINT8  ReasonCode\r
+  )\r
+{\r
+  MSR_SEV_ES_GHCB_REGISTER  Msr;\r
+\r
+  //\r
+  // Use the GHCB MSR Protocol to request termination by the hypervisor\r
+  //\r
+  Msr.GhcbPhysicalAddress = 0;\r
+  Msr.GhcbTerminate.Function = GHCB_INFO_TERMINATE_REQUEST;\r
+  Msr.GhcbTerminate.ReasonCodeSet = GHCB_TERMINATE_GHCB;\r
+  Msr.GhcbTerminate.ReasonCode = ReasonCode;\r
+  AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);\r
+\r
+  AsmVmgExit ();\r
+\r
+  ASSERT (FALSE);\r
+  CpuDeadLoop ();\r
+}\r
+\r
+/**\r
+  Validate the SEV-ES/GHCB protocol level.\r
+\r
+  Verify that the level of SEV-ES/GHCB protocol supported by the hypervisor\r
+  and the guest intersect. If they don't intersect, request termination.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+SevEsProtocolCheck (\r
+  VOID\r
+  )\r
+{\r
+  MSR_SEV_ES_GHCB_REGISTER  Msr;\r
+  GHCB                      *Ghcb;\r
+\r
+  //\r
+  // Use the GHCB MSR Protocol to obtain the GHCB SEV-ES Information for\r
+  // protocol checking\r
+  //\r
+  Msr.GhcbPhysicalAddress = 0;\r
+  Msr.GhcbInfo.Function = GHCB_INFO_SEV_INFO_GET;\r
+  AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);\r
+\r
+  AsmVmgExit ();\r
+\r
+  Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);\r
+\r
+  if (Msr.GhcbInfo.Function != GHCB_INFO_SEV_INFO) {\r
+    SevEsProtocolFailure (GHCB_TERMINATE_GHCB_GENERAL);\r
+  }\r
+\r
+  if (Msr.GhcbProtocol.SevEsProtocolMin > Msr.GhcbProtocol.SevEsProtocolMax) {\r
+    SevEsProtocolFailure (GHCB_TERMINATE_GHCB_PROTOCOL);\r
+  }\r
+\r
+  if ((Msr.GhcbProtocol.SevEsProtocolMin > GHCB_VERSION_MAX) ||\r
+      (Msr.GhcbProtocol.SevEsProtocolMax < GHCB_VERSION_MIN)) {\r
+    SevEsProtocolFailure (GHCB_TERMINATE_GHCB_PROTOCOL);\r
+  }\r
+\r
+  //\r
+  // SEV-ES protocol checking succeeded, set the initial GHCB address\r
+  //\r
+  Msr.GhcbPhysicalAddress = FixedPcdGet32 (PcdOvmfSecGhcbBase);\r
+  AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);\r
+\r
+  Ghcb = Msr.Ghcb;\r
+  SetMem (Ghcb, sizeof (*Ghcb), 0);\r
+\r
+  //\r
+  // Set the version to the maximum that can be supported\r
+  //\r
+  Ghcb->ProtocolVersion = MIN (Msr.GhcbProtocol.SevEsProtocolMax, GHCB_VERSION_MAX);\r
+  Ghcb->GhcbUsage = GHCB_STANDARD_USAGE;\r
+}\r
+\r
+/**\r
+  Determine if SEV-ES is active.\r
+\r
+  During early booting, SEV-ES support code will set a flag to indicate that\r
+  SEV-ES is enabled. Return the value of this flag as an indicator that SEV-ES\r
+  is enabled.\r
+\r
+  @retval TRUE   SEV-ES is enabled\r
+  @retval FALSE  SEV-ES is not enabled\r
+\r
+**/\r
+STATIC\r
+BOOLEAN\r
+SevEsIsEnabled (\r
+  VOID\r
+  )\r
+{\r
+  SEC_SEV_ES_WORK_AREA  *SevEsWorkArea;\r
+\r
+  SevEsWorkArea = (SEC_SEV_ES_WORK_AREA *) FixedPcdGet32 (PcdSevEsWorkAreaBase);\r
+\r
+  return ((SevEsWorkArea != NULL) && (SevEsWorkArea->SevEsEnabled != 0));\r
+}\r
+\r
 VOID\r
 EFIAPI\r
 SecCoreStartupWithStack (\r
@@ -737,8 +858,56 @@ SecCoreStartupWithStack (
     Table[Index] = 0;\r
   }\r
 \r
+  //\r
+  // Initialize IDT - Since this is before library constructors are called,\r
+  // we use a loop rather than CopyMem.\r
+  //\r
+  IdtTableInStack.PeiService = NULL;\r
+  for (Index = 0; Index < SEC_IDT_ENTRY_COUNT; Index ++) {\r
+    UINT8  *Src;\r
+    UINT8  *Dst;\r
+    UINTN  Byte;\r
+\r
+    Src = (UINT8 *) &mIdtEntryTemplate;\r
+    Dst = (UINT8 *) &IdtTableInStack.IdtTable[Index];\r
+    for (Byte = 0; Byte < sizeof (mIdtEntryTemplate); Byte++) {\r
+      Dst[Byte] = Src[Byte];\r
+    }\r
+  }\r
+\r
+  IdtDescriptor.Base  = (UINTN)&IdtTableInStack.IdtTable;\r
+  IdtDescriptor.Limit = (UINT16)(sizeof (IdtTableInStack.IdtTable) - 1);\r
+\r
+  if (SevEsIsEnabled ()) {\r
+    SevEsProtocolCheck ();\r
+\r
+    //\r
+    // For SEV-ES guests, the exception handler is needed before calling\r
+    // ProcessLibraryConstructorList() because some of the library constructors\r
+    // perform some functions that result in #VC exceptions being generated.\r
+    //\r
+    // Due to this code executing before library constructors, *all* library\r
+    // API calls are theoretically interface contract violations. However,\r
+    // because this is SEC (executing in flash), those constructors cannot\r
+    // write variables with static storage duration anyway. Furthermore, only\r
+    // a small, restricted set of APIs, such as AsmWriteIdtr() and\r
+    // InitializeCpuExceptionHandlers(), are called, where we require that the\r
+    // underlying library not require constructors to have been invoked and\r
+    // that the library instance not trigger any #VC exceptions.\r
+    //\r
+    AsmWriteIdtr (&IdtDescriptor);\r
+    InitializeCpuExceptionHandlers (NULL);\r
+  }\r
+\r
   ProcessLibraryConstructorList (NULL, NULL);\r
 \r
+  if (!SevEsIsEnabled ()) {\r
+    //\r
+    // For non SEV-ES guests, just load the IDTR.\r
+    //\r
+    AsmWriteIdtr (&IdtDescriptor);\r
+  }\r
+\r
   DEBUG ((DEBUG_INFO,\r
     "SecCoreStartupWithStack(0x%x, 0x%x)\n",\r
     (UINT32)(UINTN)BootFv,\r
@@ -751,19 +920,6 @@ SecCoreStartupWithStack (
   //\r
   InitializeFloatingPointUnits ();\r
 \r
-  //\r
-  // Initialize IDT\r
-  //\r
-  IdtTableInStack.PeiService = NULL;\r
-  for (Index = 0; Index < SEC_IDT_ENTRY_COUNT; Index ++) {\r
-    CopyMem (&IdtTableInStack.IdtTable[Index], &mIdtEntryTemplate, sizeof (mIdtEntryTemplate));\r
-  }\r
-\r
-  IdtDescriptor.Base  = (UINTN)&IdtTableInStack.IdtTable;\r
-  IdtDescriptor.Limit = (UINT16)(sizeof (IdtTableInStack.IdtTable) - 1);\r
-\r
-  AsmWriteIdtr (&IdtDescriptor);\r
-\r
 #if defined (MDE_CPU_X64)\r
   //\r
   // ASSERT that the Page Tables were set by the reset vector code to\r
index 63ba4cb555fbd01092e92266292125fbf2c23d57..7f78dcee27728e10ef153cb67c1f3a58c227f150 100644 (file)
   PeCoffExtraActionLib\r
   ExtractGuidedSectionLib\r
   LocalApicLib\r
+  CpuExceptionHandlerLib\r
 \r
 [Ppis]\r
   gEfiTemporaryRamSupportPpiGuid                # PPI ALWAYS_PRODUCED\r
 \r
 [Pcd]\r
+  gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase\r
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfPeiMemFvBase\r
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfPeiMemFvSize\r
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfDxeMemFvBase\r
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfDxeMemFvSize\r
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase\r
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize\r
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase\r
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase\r
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize\r