]> git.proxmox.com Git - mirror_edk2.git/commitdiff
UefiCpuPkg/MpInitLib: Use SEV-SNP AP Creation NAE event to launch APs
authorTom Lendacky <thomas.lendacky@amd.com>
Thu, 9 Dec 2021 03:28:00 +0000 (11:28 +0800)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Thu, 9 Dec 2021 06:28:10 +0000 (06:28 +0000)
BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275

Use the SEV-SNP AP Creation NAE event to create and launch APs under
SEV-SNP. This capability will be advertised in the SEV Hypervisor
Feature Support PCD (PcdSevEsHypervisorFeatures).

Cc: Michael Roth <michael.roth@amd.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Rahul Kumar <rahul1.kumar@intel.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Min Xu <min.m.xu@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Acked-by: Ray Ni <ray.ni@intel.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
UefiCpuPkg/Library/MpInitLib/Ia32/AmdSev.c [new file with mode: 0644]
UefiCpuPkg/Library/MpInitLib/MpLib.c
UefiCpuPkg/Library/MpInitLib/MpLib.h
UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
UefiCpuPkg/Library/MpInitLib/X64/AmdSev.c [new file with mode: 0644]

index de705bc54bb42b34bf7d4f03150e1e5b839b160b..e1cd0b35000831560d736a10b8f017597bbcae48 100644 (file)
 #\r
 \r
 [Sources.IA32]\r
+  Ia32/AmdSev.c\r
   Ia32/MpFuncs.nasm\r
 \r
 [Sources.X64]\r
+  X64/AmdSev.c\r
   X64/MpFuncs.nasm\r
 \r
 [Sources.common]\r
@@ -73,6 +75,7 @@
   gUefiCpuPkgTokenSpaceGuid.PcdCpuApLoopMode                           ## CONSUMES\r
   gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate                       ## SOMETIMES_CONSUMES\r
   gUefiCpuPkgTokenSpaceGuid.PcdCpuApStatusCheckIntervalInMicroSeconds  ## CONSUMES\r
+  gUefiCpuPkgTokenSpaceGuid.PcdGhcbHypervisorFeatures                  ## CONSUMES\r
   gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase                       ## SOMETIMES_CONSUMES\r
   gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard                      ## CONSUMES\r
   gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase                           ## CONSUMES\r
index 02bc9c2bd2c4f763ba471ffb222b0ced8a8cea89..60d14a5a0e101e7c71c368903bc3c9390f110833 100644 (file)
@@ -93,7 +93,14 @@ GetWakeupBuffer (
   EFI_PHYSICAL_ADDRESS  StartAddress;\r
   EFI_MEMORY_TYPE       MemoryType;\r
 \r
-  if (ConfidentialComputingGuestHas (CCAttrAmdSevEs)) {\r
+  if (ConfidentialComputingGuestHas (CCAttrAmdSevEs) &&\r
+      !ConfidentialComputingGuestHas (CCAttrAmdSevSnp))\r
+  {\r
+    //\r
+    // An SEV-ES-only guest requires the memory to be reserved. SEV-SNP, which\r
+    // is also considered SEV-ES, uses a different AP startup method, though,\r
+    // which does not have the same requirement.\r
+    //\r
     MemoryType = EfiReservedMemoryType;\r
   } else {\r
     MemoryType = EfiBootServicesData;\r
@@ -380,7 +387,7 @@ RelocateApLoop (
   MpInitLibWhoAmI (&ProcessorNumber);\r
   CpuMpData    = GetCpuMpData ();\r
   MwaitSupport = IsMwaitSupport ();\r
-  if (CpuMpData->SevEsIsEnabled) {\r
+  if (CpuMpData->UseSevEsAPMethod) {\r
     StackStart = CpuMpData->SevEsAPResetStackStart;\r
   } else {\r
     StackStart = mReservedTopOfApStack;\r
@@ -430,7 +437,7 @@ MpInitChangeApLoopCallback (
     CpuPause ();\r
   }\r
 \r
-  if (CpuMpData->SevEsIsEnabled && (CpuMpData->WakeupBuffer != (UINTN)-1)) {\r
+  if (CpuMpData->UseSevEsAPMethod && (CpuMpData->WakeupBuffer != (UINTN)-1)) {\r
     //\r
     // There are APs present. Re-use reserved memory area below 1MB from\r
     // WakeupBuffer as the area to be used for transitioning to 16-bit mode\r
diff --git a/UefiCpuPkg/Library/MpInitLib/Ia32/AmdSev.c b/UefiCpuPkg/Library/MpInitLib/Ia32/AmdSev.c
new file mode 100644 (file)
index 0000000..c831442
--- /dev/null
@@ -0,0 +1,70 @@
+/** @file\r
+\r
+  AMD SEV helper function.\r
+\r
+  Copyright (c) 2021, AMD Incorporated. All rights reserved.<BR>\r
+\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include "MpLib.h"\r
+\r
+/**\r
+  Create an SEV-SNP AP save area (VMSA) for use in running the vCPU.\r
+\r
+  @param[in]  CpuMpData        Pointer to CPU MP Data\r
+  @param[in]  CpuData          Pointer to CPU AP Data\r
+  @param[in]  ApicId           APIC ID of the vCPU\r
+**/\r
+VOID\r
+SevSnpCreateSaveArea (\r
+  IN CPU_MP_DATA  *CpuMpData,\r
+  IN CPU_AP_DATA  *CpuData,\r
+  UINT32          ApicId\r
+  )\r
+{\r
+  //\r
+  // SEV-SNP is not support on 32-bit build.\r
+  //\r
+  ASSERT (FALSE);\r
+}\r
+\r
+/**\r
+  Create SEV-SNP APs.\r
+\r
+  @param[in]  CpuMpData        Pointer to CPU MP Data\r
+  @param[in]  ProcessorNumber  The handle number of specified processor\r
+                               (-1 for all APs)\r
+**/\r
+VOID\r
+SevSnpCreateAP (\r
+  IN CPU_MP_DATA  *CpuMpData,\r
+  IN INTN         ProcessorNumber\r
+  )\r
+{\r
+  //\r
+  // SEV-SNP is not support on 32-bit build.\r
+  //\r
+  ASSERT (FALSE);\r
+}\r
+\r
+/**\r
+  Issue RMPADJUST to adjust the VMSA attribute of an SEV-SNP page.\r
+\r
+  @param[in]  PageAddress\r
+  @param[in]  VmsaPage\r
+\r
+  @return  RMPADJUST return value\r
+**/\r
+UINT32\r
+SevSnpRmpAdjust (\r
+  IN  EFI_PHYSICAL_ADDRESS  PageAddress,\r
+  IN  BOOLEAN               VmsaPage\r
+  )\r
+{\r
+  //\r
+  // RMPADJUST is not supported in 32-bit mode\r
+  //\r
+  return RETURN_UNSUPPORTED;\r
+}\r
index b73a6e9a0ffc0f49462f956505cecdeb617a92e5..4a73787ee43a649548e05964f6b04f24b059c83f 100644 (file)
@@ -295,10 +295,12 @@ GetApLoopMode (
       ApLoopMode = ApInHltLoop;\r
     }\r
 \r
-    if (ConfidentialComputingGuestHas (CCAttrAmdSevEs)) {\r
+    if (ConfidentialComputingGuestHas (CCAttrAmdSevEs) &&\r
+        !ConfidentialComputingGuestHas (CCAttrAmdSevSnp))\r
+    {\r
       //\r
-      // For SEV-ES, force AP in Hlt-loop mode in order to use the GHCB\r
-      // protocol for starting APs\r
+      // For SEV-ES (SEV-SNP is also considered SEV-ES), force AP in Hlt-loop\r
+      // mode in order to use the GHCB protocol for starting APs\r
       //\r
       ApLoopMode = ApInHltLoop;\r
     }\r
@@ -763,7 +765,7 @@ ApWakeupFunction (
       // to allow the APs to issue an AP_RESET_HOLD before the BSP possibly\r
       // performs another INIT-SIPI-SIPI sequence.\r
       //\r
-      if (!CpuMpData->SevEsIsEnabled) {\r
+      if (!CpuMpData->UseSevEsAPMethod) {\r
         InterlockedDecrement ((UINT32 *)&CpuMpData->MpCpuExchangeInfo->NumApsExecuting);\r
       }\r
     }\r
@@ -777,7 +779,7 @@ ApWakeupFunction (
       //\r
       while (TRUE) {\r
         DisableInterrupts ();\r
-        if (CpuMpData->SevEsIsEnabled) {\r
+        if (CpuMpData->UseSevEsAPMethod) {\r
           SevEsPlaceApHlt (CpuMpData);\r
         } else {\r
           CpuSleep ();\r
@@ -1061,9 +1063,13 @@ AllocateResetVector (
                                     );\r
     //\r
     // The AP reset stack is only used by SEV-ES guests. Do not allocate it\r
-    // if SEV-ES is not enabled.\r
+    // if SEV-ES is not enabled. An SEV-SNP guest is also considered\r
+    // an SEV-ES guest, but uses a different method of AP startup, eliminating\r
+    // the need for the allocation.\r
     //\r
-    if (ConfidentialComputingGuestHas (CCAttrAmdSevEs)) {\r
+    if (ConfidentialComputingGuestHas (CCAttrAmdSevEs) &&\r
+        !ConfidentialComputingGuestHas (CCAttrAmdSevSnp))\r
+    {\r
       //\r
       // Stack location is based on ProcessorNumber, so use the total number\r
       // of processors for calculating the total stack area.\r
@@ -1114,7 +1120,7 @@ FreeResetVector (
   // perform the restore as this will overwrite memory which has data\r
   // needed by SEV-ES.\r
   //\r
-  if (!CpuMpData->SevEsIsEnabled) {\r
+  if (!CpuMpData->UseSevEsAPMethod) {\r
     RestoreWakeupBuffer (CpuMpData);\r
   }\r
 }\r
@@ -1193,7 +1199,7 @@ WakeUpAP (
 \r
     if (ResetVectorRequired) {\r
       //\r
-      // For SEV-ES, the initial AP boot address will be defined by\r
+      // For SEV-ES and SEV-SNP, the initial AP boot address will be defined by\r
       // PcdSevEsWorkAreaBase. The Segment/Rip must be the jump address\r
       // from the original INIT-SIPI-SIPI.\r
       //\r
@@ -1203,8 +1209,14 @@ WakeUpAP (
 \r
       //\r
       // Wakeup all APs\r
+      //   Must use the INIT-SIPI-SIPI method for initial configuration in\r
+      //   order to obtain the APIC ID.\r
       //\r
-      SendInitSipiSipiAllExcludingSelf ((UINT32)ExchangeInfo->BufferStart);\r
+      if (CpuMpData->SevSnpIsEnabled && (CpuMpData->InitFlag != ApInitConfig)) {\r
+        SevSnpCreateAP (CpuMpData, -1);\r
+      } else {\r
+        SendInitSipiSipiAllExcludingSelf ((UINT32)ExchangeInfo->BufferStart);\r
+      }\r
     }\r
 \r
     if (CpuMpData->InitFlag == ApInitConfig) {\r
@@ -1295,7 +1307,7 @@ WakeUpAP (
       CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob;\r
 \r
       //\r
-      // For SEV-ES, the initial AP boot address will be defined by\r
+      // For SEV-ES and SEV-SNP, the initial AP boot address will be defined by\r
       // PcdSevEsWorkAreaBase. The Segment/Rip must be the jump address\r
       // from the original INIT-SIPI-SIPI.\r
       //\r
@@ -1303,10 +1315,14 @@ WakeUpAP (
         SetSevEsJumpTable (ExchangeInfo->BufferStart);\r
       }\r
 \r
-      SendInitSipiSipi (\r
-        CpuInfoInHob[ProcessorNumber].ApicId,\r
-        (UINT32)ExchangeInfo->BufferStart\r
-        );\r
+      if (CpuMpData->SevSnpIsEnabled && (CpuMpData->InitFlag != ApInitConfig)) {\r
+        SevSnpCreateAP (CpuMpData, (INTN)ProcessorNumber);\r
+      } else {\r
+        SendInitSipiSipi (\r
+          CpuInfoInHob[ProcessorNumber].ApicId,\r
+          (UINT32)ExchangeInfo->BufferStart\r
+          );\r
+      }\r
     }\r
 \r
     //\r
@@ -1855,10 +1871,15 @@ MpInitLibInitialize (
   CpuMpData->CpuData          = (CPU_AP_DATA *)(CpuMpData + 1);\r
   CpuMpData->CpuInfoInHob     = (UINT64)(UINTN)(CpuMpData->CpuData + MaxLogicalProcessorNumber);\r
   InitializeSpinLock (&CpuMpData->MpLock);\r
-  CpuMpData->SevEsIsEnabled  = ConfidentialComputingGuestHas (CCAttrAmdSevEs);\r
-  CpuMpData->SevSnpIsEnabled = ConfidentialComputingGuestHas (CCAttrAmdSevSnp);\r
-  CpuMpData->SevEsAPBuffer   = (UINTN)-1;\r
-  CpuMpData->GhcbBase        = PcdGet64 (PcdGhcbBase);\r
+  CpuMpData->SevEsIsEnabled   = ConfidentialComputingGuestHas (CCAttrAmdSevEs);\r
+  CpuMpData->SevSnpIsEnabled  = ConfidentialComputingGuestHas (CCAttrAmdSevSnp);\r
+  CpuMpData->SevEsAPBuffer    = (UINTN)-1;\r
+  CpuMpData->GhcbBase         = PcdGet64 (PcdGhcbBase);\r
+  CpuMpData->UseSevEsAPMethod = CpuMpData->SevEsIsEnabled && !CpuMpData->SevSnpIsEnabled;\r
+\r
+  if (CpuMpData->SevSnpIsEnabled) {\r
+    ASSERT ((PcdGet64 (PcdGhcbHypervisorFeatures) & GHCB_HV_FEATURES_SNP_AP_CREATE) == GHCB_HV_FEATURES_SNP_AP_CREATE);\r
+  }\r
 \r
   //\r
   // Make sure no memory usage outside of the allocated buffer.\r
index be67cd88ec46159864f9a59a526e6355df88bb5b..7d84a56fbc5168f1d4846a2bb222de90e7f5db20 100644 (file)
@@ -15,6 +15,7 @@
 \r
 #include <Register/Intel/Cpuid.h>\r
 #include <Register/Amd/Cpuid.h>\r
+#include <Register/Amd/Ghcb.h>\r
 #include <Register/Intel/Msr.h>\r
 #include <Register/Intel/LocalApic.h>\r
 #include <Register/Intel/Microcode.h>\r
@@ -150,6 +151,7 @@ typedef struct {
   UINT8                     PlatformId;\r
   UINT64                    MicrocodeEntryAddr;\r
   UINT32                    MicrocodeRevision;\r
+  SEV_ES_SAVE_AREA          *SevEsSaveArea;\r
 } CPU_AP_DATA;\r
 \r
 //\r
@@ -294,6 +296,7 @@ struct _CPU_MP_DATA {
 \r
   BOOLEAN        SevEsIsEnabled;\r
   BOOLEAN        SevSnpIsEnabled;\r
+  BOOLEAN        UseSevEsAPMethod;\r
   UINTN          SevEsAPBuffer;\r
   UINTN          SevEsAPResetStackStart;\r
   CPU_MP_DATA    *NewCpuMpData;\r
@@ -799,4 +802,45 @@ FillExchangeInfoDataSevEs (
   IN volatile MP_CPU_EXCHANGE_INFO  *ExchangeInfo\r
   );\r
 \r
+/**\r
+  Issue RMPADJUST to adjust the VMSA attribute of an SEV-SNP page.\r
+\r
+  @param[in]  PageAddress\r
+  @param[in]  VmsaPage\r
+\r
+  @return  RMPADJUST return value\r
+**/\r
+UINT32\r
+SevSnpRmpAdjust (\r
+  IN  EFI_PHYSICAL_ADDRESS  PageAddress,\r
+  IN  BOOLEAN               VmsaPage\r
+  );\r
+\r
+/**\r
+  Create an SEV-SNP AP save area (VMSA) for use in running the vCPU.\r
+\r
+  @param[in]  CpuMpData        Pointer to CPU MP Data\r
+  @param[in]  CpuData          Pointer to CPU AP Data\r
+  @param[in]  ApicId           APIC ID of the vCPU\r
+**/\r
+VOID\r
+SevSnpCreateSaveArea (\r
+  IN CPU_MP_DATA  *CpuMpData,\r
+  IN CPU_AP_DATA  *CpuData,\r
+  UINT32          ApicId\r
+  );\r
+\r
+/**\r
+  Create SEV-SNP APs.\r
+\r
+  @param[in]  CpuMpData        Pointer to CPU MP Data\r
+  @param[in]  ProcessorNumber  The handle number of specified processor\r
+                               (-1 for all APs)\r
+**/\r
+VOID\r
+SevSnpCreateAP (\r
+  IN CPU_MP_DATA  *CpuMpData,\r
+  IN INTN         ProcessorNumber\r
+  );\r
+\r
 #endif\r
index b7e15ee023f0291c7c267212e19faf34a7a9cd40..5facf4db94997beb879e3ff5cfbae37b35b3c425 100644 (file)
 #\r
 \r
 [Sources.IA32]\r
+  Ia32/AmdSev.c\r
   Ia32/MpFuncs.nasm\r
 \r
 [Sources.X64]\r
+  X64/AmdSev.c\r
   X64/MpFuncs.nasm\r
 \r
 [Sources.common]\r
@@ -64,6 +66,7 @@
   gUefiCpuPkgTokenSpaceGuid.PcdCpuApLoopMode                       ## CONSUMES\r
   gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate                   ## SOMETIMES_CONSUMES\r
   gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase                   ## SOMETIMES_CONSUMES\r
+  gUefiCpuPkgTokenSpaceGuid.PcdGhcbHypervisorFeatures              ## CONSUMES\r
   gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase                       ## CONSUMES\r
   gEfiMdePkgTokenSpaceGuid.PcdConfidentialComputingGuestAttr       ## CONSUMES\r
 \r
diff --git a/UefiCpuPkg/Library/MpInitLib/X64/AmdSev.c b/UefiCpuPkg/Library/MpInitLib/X64/AmdSev.c
new file mode 100644 (file)
index 0000000..a3cd377
--- /dev/null
@@ -0,0 +1,263 @@
+/** @file\r
+\r
+  AMD SEV helper function.\r
+\r
+  Copyright (c) 2021, AMD Incorporated. All rights reserved.<BR>\r
+\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include "MpLib.h"\r
+#include <Library/VmgExitLib.h>\r
+#include <Register/Amd/Fam17Msr.h>\r
+#include <Register/Amd/Ghcb.h>\r
+\r
+/**\r
+  Create an SEV-SNP AP save area (VMSA) for use in running the vCPU.\r
+\r
+  @param[in]  CpuMpData        Pointer to CPU MP Data\r
+  @param[in]  CpuData          Pointer to CPU AP Data\r
+  @param[in]  ApicId           APIC ID of the vCPU\r
+**/\r
+VOID\r
+SevSnpCreateSaveArea (\r
+  IN CPU_MP_DATA  *CpuMpData,\r
+  IN CPU_AP_DATA  *CpuData,\r
+  UINT32          ApicId\r
+  )\r
+{\r
+  SEV_ES_SAVE_AREA          *SaveArea;\r
+  IA32_CR0                  ApCr0;\r
+  IA32_CR0                  ResetCr0;\r
+  IA32_CR4                  ApCr4;\r
+  IA32_CR4                  ResetCr4;\r
+  UINTN                     StartIp;\r
+  UINT8                     SipiVector;\r
+  UINT32                    RmpAdjustStatus;\r
+  UINT64                    VmgExitStatus;\r
+  MSR_SEV_ES_GHCB_REGISTER  Msr;\r
+  GHCB                      *Ghcb;\r
+  BOOLEAN                   InterruptState;\r
+  UINT64                    ExitInfo1;\r
+  UINT64                    ExitInfo2;\r
+\r
+  //\r
+  // Allocate a single page for the SEV-ES Save Area and initialize it.\r
+  //\r
+  SaveArea = AllocateReservedPages (1);\r
+  if (!SaveArea) {\r
+    return;\r
+  }\r
+\r
+  ZeroMem (SaveArea, EFI_PAGE_SIZE);\r
+\r
+  //\r
+  // Propogate the CR0.NW and CR0.CD setting to the AP\r
+  //\r
+  ResetCr0.UintN = 0x00000010;\r
+  ApCr0.UintN    = CpuData->VolatileRegisters.Cr0;\r
+  if (ApCr0.Bits.NW) {\r
+    ResetCr0.Bits.NW = 1;\r
+  }\r
+\r
+  if (ApCr0.Bits.CD) {\r
+    ResetCr0.Bits.CD = 1;\r
+  }\r
+\r
+  //\r
+  // Propagate the CR4.MCE setting to the AP\r
+  //\r
+  ResetCr4.UintN = 0;\r
+  ApCr4.UintN    = CpuData->VolatileRegisters.Cr4;\r
+  if (ApCr4.Bits.MCE) {\r
+    ResetCr4.Bits.MCE = 1;\r
+  }\r
+\r
+  //\r
+  // Convert the start IP into a SIPI Vector\r
+  //\r
+  StartIp    = CpuMpData->MpCpuExchangeInfo->BufferStart;\r
+  SipiVector = (UINT8)(StartIp >> 12);\r
+\r
+  //\r
+  // Set the CS:RIP value based on the start IP\r
+  //\r
+  SaveArea->Cs.Base                    = SipiVector << 12;\r
+  SaveArea->Cs.Selector                = SipiVector << 8;\r
+  SaveArea->Cs.Limit                   = 0xFFFF;\r
+  SaveArea->Cs.Attributes.Bits.Present = 1;\r
+  SaveArea->Cs.Attributes.Bits.Sbit    = 1;\r
+  SaveArea->Cs.Attributes.Bits.Type    = SEV_ES_RESET_CODE_SEGMENT_TYPE;\r
+  SaveArea->Rip                        = StartIp & 0xFFF;\r
+\r
+  //\r
+  // Set the remaining values as defined in APM for INIT\r
+  //\r
+  SaveArea->Ds.Limit                   = 0xFFFF;\r
+  SaveArea->Ds.Attributes.Bits.Present = 1;\r
+  SaveArea->Ds.Attributes.Bits.Sbit    = 1;\r
+  SaveArea->Ds.Attributes.Bits.Type    = SEV_ES_RESET_DATA_SEGMENT_TYPE;\r
+  SaveArea->Es                         = SaveArea->Ds;\r
+  SaveArea->Fs                         = SaveArea->Ds;\r
+  SaveArea->Gs                         = SaveArea->Ds;\r
+  SaveArea->Ss                         = SaveArea->Ds;\r
+\r
+  SaveArea->Gdtr.Limit                   = 0xFFFF;\r
+  SaveArea->Ldtr.Limit                   = 0xFFFF;\r
+  SaveArea->Ldtr.Attributes.Bits.Present = 1;\r
+  SaveArea->Ldtr.Attributes.Bits.Type    = SEV_ES_RESET_LDT_TYPE;\r
+  SaveArea->Idtr.Limit                   = 0xFFFF;\r
+  SaveArea->Tr.Limit                     = 0xFFFF;\r
+  SaveArea->Ldtr.Attributes.Bits.Present = 1;\r
+  SaveArea->Ldtr.Attributes.Bits.Type    = SEV_ES_RESET_TSS_TYPE;\r
+\r
+  SaveArea->Efer   = 0x1000;\r
+  SaveArea->Cr4    = ResetCr4.UintN;\r
+  SaveArea->Cr0    = ResetCr0.UintN;\r
+  SaveArea->Dr7    = 0x0400;\r
+  SaveArea->Dr6    = 0xFFFF0FF0;\r
+  SaveArea->Rflags = 0x0002;\r
+  SaveArea->GPat   = 0x0007040600070406ULL;\r
+  SaveArea->XCr0   = 0x0001;\r
+  SaveArea->Mxcsr  = 0x1F80;\r
+  SaveArea->X87Ftw = 0x5555;\r
+  SaveArea->X87Fcw = 0x0040;\r
+\r
+  //\r
+  // Set the SEV-SNP specific fields for the save area:\r
+  //   VMPL - always VMPL0\r
+  //   SEV_FEATURES - equivalent to the SEV_STATUS MSR right shifted 2 bits\r
+  //\r
+  SaveArea->Vmpl        = 0;\r
+  SaveArea->SevFeatures = AsmReadMsr64 (MSR_SEV_STATUS) >> 2;\r
+\r
+  //\r
+  // To turn the page into a recognized VMSA page, issue RMPADJUST:\r
+  //   Target VMPL but numerically higher than current VMPL\r
+  //   Target PermissionMask is not used\r
+  //\r
+  RmpAdjustStatus = SevSnpRmpAdjust (\r
+                      (EFI_PHYSICAL_ADDRESS)(UINTN)SaveArea,\r
+                      TRUE\r
+                      );\r
+  ASSERT (RmpAdjustStatus == 0);\r
+\r
+  ExitInfo1  = (UINT64)ApicId << 32;\r
+  ExitInfo1 |= SVM_VMGEXIT_SNP_AP_CREATE;\r
+  ExitInfo2  = (UINT64)(UINTN)SaveArea;\r
+\r
+  Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);\r
+  Ghcb                    = Msr.Ghcb;\r
+\r
+  VmgInit (Ghcb, &InterruptState);\r
+  Ghcb->SaveArea.Rax = SaveArea->SevFeatures;\r
+  VmgSetOffsetValid (Ghcb, GhcbRax);\r
+  VmgExitStatus = VmgExit (\r
+                    Ghcb,\r
+                    SVM_EXIT_SNP_AP_CREATION,\r
+                    ExitInfo1,\r
+                    ExitInfo2\r
+                    );\r
+  VmgDone (Ghcb, InterruptState);\r
+\r
+  ASSERT (VmgExitStatus == 0);\r
+  if (VmgExitStatus != 0) {\r
+    RmpAdjustStatus = SevSnpRmpAdjust (\r
+                        (EFI_PHYSICAL_ADDRESS)(UINTN)SaveArea,\r
+                        FALSE\r
+                        );\r
+    if (RmpAdjustStatus == 0) {\r
+      FreePages (SaveArea, 1);\r
+    } else {\r
+      DEBUG ((DEBUG_INFO, "SEV-SNP: RMPADJUST failed, leaking VMSA page\n"));\r
+    }\r
+\r
+    SaveArea = NULL;\r
+  }\r
+\r
+  if (CpuData->SevEsSaveArea) {\r
+    RmpAdjustStatus = SevSnpRmpAdjust (\r
+                        (EFI_PHYSICAL_ADDRESS)(UINTN)CpuData->SevEsSaveArea,\r
+                        FALSE\r
+                        );\r
+    if (RmpAdjustStatus == 0) {\r
+      FreePages (CpuData->SevEsSaveArea, 1);\r
+    } else {\r
+      DEBUG ((DEBUG_INFO, "SEV-SNP: RMPADJUST failed, leaking VMSA page\n"));\r
+    }\r
+  }\r
+\r
+  CpuData->SevEsSaveArea = SaveArea;\r
+}\r
+\r
+/**\r
+  Create SEV-SNP APs.\r
+\r
+  @param[in]  CpuMpData        Pointer to CPU MP Data\r
+  @param[in]  ProcessorNumber  The handle number of specified processor\r
+                               (-1 for all APs)\r
+**/\r
+VOID\r
+SevSnpCreateAP (\r
+  IN CPU_MP_DATA  *CpuMpData,\r
+  IN INTN         ProcessorNumber\r
+  )\r
+{\r
+  CPU_INFO_IN_HOB  *CpuInfoInHob;\r
+  CPU_AP_DATA      *CpuData;\r
+  UINTN            Index;\r
+  UINT32           ApicId;\r
+\r
+  ASSERT (CpuMpData->MpCpuExchangeInfo->BufferStart < 0x100000);\r
+\r
+  CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob;\r
+\r
+  if (ProcessorNumber < 0) {\r
+    for (Index = 0; Index < CpuMpData->CpuCount; Index++) {\r
+      if (Index != CpuMpData->BspNumber) {\r
+        CpuData = &CpuMpData->CpuData[Index];\r
+        ApicId  = CpuInfoInHob[Index].ApicId,\r
+        SevSnpCreateSaveArea (CpuMpData, CpuData, ApicId);\r
+      }\r
+    }\r
+  } else {\r
+    Index   = (UINTN)ProcessorNumber;\r
+    CpuData = &CpuMpData->CpuData[Index];\r
+    ApicId  = CpuInfoInHob[ProcessorNumber].ApicId,\r
+    SevSnpCreateSaveArea (CpuMpData, CpuData, ApicId);\r
+  }\r
+}\r
+\r
+/**\r
+  Issue RMPADJUST to adjust the VMSA attribute of an SEV-SNP page.\r
+\r
+  @param[in]  PageAddress\r
+  @param[in]  VmsaPage\r
+\r
+  @return  RMPADJUST return value\r
+**/\r
+UINT32\r
+SevSnpRmpAdjust (\r
+  IN  EFI_PHYSICAL_ADDRESS  PageAddress,\r
+  IN  BOOLEAN               VmsaPage\r
+  )\r
+{\r
+  UINT64  Rdx;\r
+\r
+  //\r
+  // The RMPADJUST instruction is used to set or clear the VMSA bit for a\r
+  // page. The VMSA change is only made when running at VMPL0 and is ignored\r
+  // otherwise. If too low a target VMPL is specified, the instruction can\r
+  // succeed without changing the VMSA bit when not running at VMPL0. Using a\r
+  // target VMPL level of 1, RMPADJUST will return a FAIL_PERMISSION error if\r
+  // not running at VMPL0, thus ensuring that the VMSA bit is set appropriately\r
+  // when no error is returned.\r
+  //\r
+  Rdx = 1;\r
+  if (VmsaPage) {\r
+    Rdx |= RMPADJUST_VMSA_PAGE_BIT;\r
+  }\r
+\r
+  return AsmRmpAdjust ((UINT64)PageAddress, 0, Rdx);\r
+}\r