#\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
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
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
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
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
--- /dev/null
+/** @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
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
// 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
//\r
while (TRUE) {\r
DisableInterrupts ();\r
- if (CpuMpData->SevEsIsEnabled) {\r
+ if (CpuMpData->UseSevEsAPMethod) {\r
SevEsPlaceApHlt (CpuMpData);\r
} else {\r
CpuSleep ();\r
);\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
// 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
\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
\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
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
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
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
\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
UINT8 PlatformId;\r
UINT64 MicrocodeEntryAddr;\r
UINT32 MicrocodeRevision;\r
+ SEV_ES_SAVE_AREA *SevEsSaveArea;\r
} CPU_AP_DATA;\r
\r
//\r
\r
BOOLEAN SevEsIsEnabled;\r
BOOLEAN SevSnpIsEnabled;\r
+ BOOLEAN UseSevEsAPMethod;\r
UINTN SevEsAPBuffer;\r
UINTN SevEsAPResetStackStart;\r
CPU_MP_DATA *NewCpuMpData;\r
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
#\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
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
--- /dev/null
+/** @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