3 AMD SEV helper function.
5 Copyright (c) 2021, AMD Incorporated. All rights reserved.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
12 #include <Library/CcExitLib.h>
13 #include <Register/Amd/Fam17Msr.h>
14 #include <Register/Amd/Ghcb.h>
17 Create an SEV-SNP AP save area (VMSA) for use in running the vCPU.
19 @param[in] CpuMpData Pointer to CPU MP Data
20 @param[in] CpuData Pointer to CPU AP Data
21 @param[in] ApicId APIC ID of the vCPU
24 SevSnpCreateSaveArea (
25 IN CPU_MP_DATA
*CpuMpData
,
26 IN CPU_AP_DATA
*CpuData
,
30 SEV_ES_SAVE_AREA
*SaveArea
;
37 UINT32 RmpAdjustStatus
;
39 MSR_SEV_ES_GHCB_REGISTER Msr
;
41 BOOLEAN InterruptState
;
46 // Allocate a single page for the SEV-ES Save Area and initialize it.
48 SaveArea
= AllocateReservedPages (1);
53 ZeroMem (SaveArea
, EFI_PAGE_SIZE
);
56 // Propogate the CR0.NW and CR0.CD setting to the AP
58 ResetCr0
.UintN
= 0x00000010;
59 ApCr0
.UintN
= CpuData
->VolatileRegisters
.Cr0
;
69 // Propagate the CR4.MCE setting to the AP
72 ApCr4
.UintN
= CpuData
->VolatileRegisters
.Cr4
;
74 ResetCr4
.Bits
.MCE
= 1;
78 // Convert the start IP into a SIPI Vector
80 StartIp
= CpuMpData
->MpCpuExchangeInfo
->BufferStart
;
81 SipiVector
= (UINT8
)(StartIp
>> 12);
84 // Set the CS:RIP value based on the start IP
86 SaveArea
->Cs
.Base
= SipiVector
<< 12;
87 SaveArea
->Cs
.Selector
= SipiVector
<< 8;
88 SaveArea
->Cs
.Limit
= 0xFFFF;
89 SaveArea
->Cs
.Attributes
.Bits
.Present
= 1;
90 SaveArea
->Cs
.Attributes
.Bits
.Sbit
= 1;
91 SaveArea
->Cs
.Attributes
.Bits
.Type
= SEV_ES_RESET_CODE_SEGMENT_TYPE
;
92 SaveArea
->Rip
= StartIp
& 0xFFF;
95 // Set the remaining values as defined in APM for INIT
97 SaveArea
->Ds
.Limit
= 0xFFFF;
98 SaveArea
->Ds
.Attributes
.Bits
.Present
= 1;
99 SaveArea
->Ds
.Attributes
.Bits
.Sbit
= 1;
100 SaveArea
->Ds
.Attributes
.Bits
.Type
= SEV_ES_RESET_DATA_SEGMENT_TYPE
;
101 SaveArea
->Es
= SaveArea
->Ds
;
102 SaveArea
->Fs
= SaveArea
->Ds
;
103 SaveArea
->Gs
= SaveArea
->Ds
;
104 SaveArea
->Ss
= SaveArea
->Ds
;
106 SaveArea
->Gdtr
.Limit
= 0xFFFF;
107 SaveArea
->Ldtr
.Limit
= 0xFFFF;
108 SaveArea
->Ldtr
.Attributes
.Bits
.Present
= 1;
109 SaveArea
->Ldtr
.Attributes
.Bits
.Type
= SEV_ES_RESET_LDT_TYPE
;
110 SaveArea
->Idtr
.Limit
= 0xFFFF;
111 SaveArea
->Tr
.Limit
= 0xFFFF;
112 SaveArea
->Ldtr
.Attributes
.Bits
.Present
= 1;
113 SaveArea
->Ldtr
.Attributes
.Bits
.Type
= SEV_ES_RESET_TSS_TYPE
;
115 SaveArea
->Efer
= 0x1000;
116 SaveArea
->Cr4
= ResetCr4
.UintN
;
117 SaveArea
->Cr0
= ResetCr0
.UintN
;
118 SaveArea
->Dr7
= 0x0400;
119 SaveArea
->Dr6
= 0xFFFF0FF0;
120 SaveArea
->Rflags
= 0x0002;
121 SaveArea
->GPat
= 0x0007040600070406ULL
;
122 SaveArea
->XCr0
= 0x0001;
123 SaveArea
->Mxcsr
= 0x1F80;
124 SaveArea
->X87Ftw
= 0x5555;
125 SaveArea
->X87Fcw
= 0x0040;
128 // Set the SEV-SNP specific fields for the save area:
129 // VMPL - always VMPL0
130 // SEV_FEATURES - equivalent to the SEV_STATUS MSR right shifted 2 bits
133 SaveArea
->SevFeatures
= AsmReadMsr64 (MSR_SEV_STATUS
) >> 2;
136 // To turn the page into a recognized VMSA page, issue RMPADJUST:
137 // Target VMPL but numerically higher than current VMPL
138 // Target PermissionMask is not used
140 RmpAdjustStatus
= SevSnpRmpAdjust (
141 (EFI_PHYSICAL_ADDRESS
)(UINTN
)SaveArea
,
144 ASSERT (RmpAdjustStatus
== 0);
146 ExitInfo1
= (UINT64
)ApicId
<< 32;
147 ExitInfo1
|= SVM_VMGEXIT_SNP_AP_CREATE
;
148 ExitInfo2
= (UINT64
)(UINTN
)SaveArea
;
150 Msr
.GhcbPhysicalAddress
= AsmReadMsr64 (MSR_SEV_ES_GHCB
);
153 VmgInit (Ghcb
, &InterruptState
);
154 Ghcb
->SaveArea
.Rax
= SaveArea
->SevFeatures
;
155 VmgSetOffsetValid (Ghcb
, GhcbRax
);
156 VmgExitStatus
= VmgExit (
158 SVM_EXIT_SNP_AP_CREATION
,
162 VmgDone (Ghcb
, InterruptState
);
164 ASSERT (VmgExitStatus
== 0);
165 if (VmgExitStatus
!= 0) {
166 RmpAdjustStatus
= SevSnpRmpAdjust (
167 (EFI_PHYSICAL_ADDRESS
)(UINTN
)SaveArea
,
170 if (RmpAdjustStatus
== 0) {
171 FreePages (SaveArea
, 1);
173 DEBUG ((DEBUG_INFO
, "SEV-SNP: RMPADJUST failed, leaking VMSA page\n"));
179 if (CpuData
->SevEsSaveArea
) {
180 RmpAdjustStatus
= SevSnpRmpAdjust (
181 (EFI_PHYSICAL_ADDRESS
)(UINTN
)CpuData
->SevEsSaveArea
,
184 if (RmpAdjustStatus
== 0) {
185 FreePages (CpuData
->SevEsSaveArea
, 1);
187 DEBUG ((DEBUG_INFO
, "SEV-SNP: RMPADJUST failed, leaking VMSA page\n"));
191 CpuData
->SevEsSaveArea
= SaveArea
;
197 @param[in] CpuMpData Pointer to CPU MP Data
198 @param[in] ProcessorNumber The handle number of specified processor
203 IN CPU_MP_DATA
*CpuMpData
,
204 IN INTN ProcessorNumber
207 CPU_INFO_IN_HOB
*CpuInfoInHob
;
208 CPU_AP_DATA
*CpuData
;
212 ASSERT (CpuMpData
->MpCpuExchangeInfo
->BufferStart
< 0x100000);
214 CpuInfoInHob
= (CPU_INFO_IN_HOB
*)(UINTN
)CpuMpData
->CpuInfoInHob
;
216 if (ProcessorNumber
< 0) {
217 for (Index
= 0; Index
< CpuMpData
->CpuCount
; Index
++) {
218 if (Index
!= CpuMpData
->BspNumber
) {
219 CpuData
= &CpuMpData
->CpuData
[Index
];
220 ApicId
= CpuInfoInHob
[Index
].ApicId
,
221 SevSnpCreateSaveArea (CpuMpData
, CpuData
, ApicId
);
225 Index
= (UINTN
)ProcessorNumber
;
226 CpuData
= &CpuMpData
->CpuData
[Index
];
227 ApicId
= CpuInfoInHob
[ProcessorNumber
].ApicId
,
228 SevSnpCreateSaveArea (CpuMpData
, CpuData
, ApicId
);
233 Issue RMPADJUST to adjust the VMSA attribute of an SEV-SNP page.
235 @param[in] PageAddress
238 @return RMPADJUST return value
242 IN EFI_PHYSICAL_ADDRESS PageAddress
,
249 // The RMPADJUST instruction is used to set or clear the VMSA bit for a
250 // page. The VMSA change is only made when running at VMPL0 and is ignored
251 // otherwise. If too low a target VMPL is specified, the instruction can
252 // succeed without changing the VMSA bit when not running at VMPL0. Using a
253 // target VMPL level of 1, RMPADJUST will return a FAIL_PERMISSION error if
254 // not running at VMPL0, thus ensuring that the VMSA bit is set appropriately
255 // when no error is returned.
259 Rdx
|= RMPADJUST_VMSA_PAGE_BIT
;
262 return AsmRmpAdjust ((UINT64
)PageAddress
, 0, Rdx
);