]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/Library/MpInitLib/X64/AmdSev.c
4c4f81af7c22b234d2ca1c42693b96db87b126dc
[mirror_edk2.git] / UefiCpuPkg / Library / MpInitLib / X64 / AmdSev.c
1 /** @file
2
3 AMD SEV helper function.
4
5 Copyright (c) 2021, AMD Incorporated. All rights reserved.<BR>
6
7 SPDX-License-Identifier: BSD-2-Clause-Patent
8
9 **/
10
11 #include "MpLib.h"
12 #include <Library/CcExitLib.h>
13 #include <Register/Amd/Fam17Msr.h>
14 #include <Register/Amd/Ghcb.h>
15
16 /**
17 Create an SEV-SNP AP save area (VMSA) for use in running the vCPU.
18
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
22 **/
23 VOID
24 SevSnpCreateSaveArea (
25 IN CPU_MP_DATA *CpuMpData,
26 IN CPU_AP_DATA *CpuData,
27 UINT32 ApicId
28 )
29 {
30 SEV_ES_SAVE_AREA *SaveArea;
31 IA32_CR0 ApCr0;
32 IA32_CR0 ResetCr0;
33 IA32_CR4 ApCr4;
34 IA32_CR4 ResetCr4;
35 UINTN StartIp;
36 UINT8 SipiVector;
37 UINT32 RmpAdjustStatus;
38 UINT64 VmgExitStatus;
39 MSR_SEV_ES_GHCB_REGISTER Msr;
40 GHCB *Ghcb;
41 BOOLEAN InterruptState;
42 UINT64 ExitInfo1;
43 UINT64 ExitInfo2;
44
45 //
46 // Allocate a single page for the SEV-ES Save Area and initialize it.
47 //
48 SaveArea = AllocateReservedPages (1);
49 if (!SaveArea) {
50 return;
51 }
52
53 ZeroMem (SaveArea, EFI_PAGE_SIZE);
54
55 //
56 // Propogate the CR0.NW and CR0.CD setting to the AP
57 //
58 ResetCr0.UintN = 0x00000010;
59 ApCr0.UintN = CpuData->VolatileRegisters.Cr0;
60 if (ApCr0.Bits.NW) {
61 ResetCr0.Bits.NW = 1;
62 }
63
64 if (ApCr0.Bits.CD) {
65 ResetCr0.Bits.CD = 1;
66 }
67
68 //
69 // Propagate the CR4.MCE setting to the AP
70 //
71 ResetCr4.UintN = 0;
72 ApCr4.UintN = CpuData->VolatileRegisters.Cr4;
73 if (ApCr4.Bits.MCE) {
74 ResetCr4.Bits.MCE = 1;
75 }
76
77 //
78 // Convert the start IP into a SIPI Vector
79 //
80 StartIp = CpuMpData->MpCpuExchangeInfo->BufferStart;
81 SipiVector = (UINT8)(StartIp >> 12);
82
83 //
84 // Set the CS:RIP value based on the start IP
85 //
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;
93
94 //
95 // Set the remaining values as defined in APM for INIT
96 //
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;
105
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;
114
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;
126
127 //
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
131 //
132 SaveArea->Vmpl = 0;
133 SaveArea->SevFeatures = AsmReadMsr64 (MSR_SEV_STATUS) >> 2;
134
135 //
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
139 //
140 RmpAdjustStatus = SevSnpRmpAdjust (
141 (EFI_PHYSICAL_ADDRESS)(UINTN)SaveArea,
142 TRUE
143 );
144 ASSERT (RmpAdjustStatus == 0);
145
146 ExitInfo1 = (UINT64)ApicId << 32;
147 ExitInfo1 |= SVM_VMGEXIT_SNP_AP_CREATE;
148 ExitInfo2 = (UINT64)(UINTN)SaveArea;
149
150 Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
151 Ghcb = Msr.Ghcb;
152
153 VmgInit (Ghcb, &InterruptState);
154 Ghcb->SaveArea.Rax = SaveArea->SevFeatures;
155 VmgSetOffsetValid (Ghcb, GhcbRax);
156 VmgExitStatus = VmgExit (
157 Ghcb,
158 SVM_EXIT_SNP_AP_CREATION,
159 ExitInfo1,
160 ExitInfo2
161 );
162 VmgDone (Ghcb, InterruptState);
163
164 ASSERT (VmgExitStatus == 0);
165 if (VmgExitStatus != 0) {
166 RmpAdjustStatus = SevSnpRmpAdjust (
167 (EFI_PHYSICAL_ADDRESS)(UINTN)SaveArea,
168 FALSE
169 );
170 if (RmpAdjustStatus == 0) {
171 FreePages (SaveArea, 1);
172 } else {
173 DEBUG ((DEBUG_INFO, "SEV-SNP: RMPADJUST failed, leaking VMSA page\n"));
174 }
175
176 SaveArea = NULL;
177 }
178
179 if (CpuData->SevEsSaveArea) {
180 RmpAdjustStatus = SevSnpRmpAdjust (
181 (EFI_PHYSICAL_ADDRESS)(UINTN)CpuData->SevEsSaveArea,
182 FALSE
183 );
184 if (RmpAdjustStatus == 0) {
185 FreePages (CpuData->SevEsSaveArea, 1);
186 } else {
187 DEBUG ((DEBUG_INFO, "SEV-SNP: RMPADJUST failed, leaking VMSA page\n"));
188 }
189 }
190
191 CpuData->SevEsSaveArea = SaveArea;
192 }
193
194 /**
195 Create SEV-SNP APs.
196
197 @param[in] CpuMpData Pointer to CPU MP Data
198 @param[in] ProcessorNumber The handle number of specified processor
199 (-1 for all APs)
200 **/
201 VOID
202 SevSnpCreateAP (
203 IN CPU_MP_DATA *CpuMpData,
204 IN INTN ProcessorNumber
205 )
206 {
207 CPU_INFO_IN_HOB *CpuInfoInHob;
208 CPU_AP_DATA *CpuData;
209 UINTN Index;
210 UINT32 ApicId;
211
212 ASSERT (CpuMpData->MpCpuExchangeInfo->BufferStart < 0x100000);
213
214 CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob;
215
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);
222 }
223 }
224 } else {
225 Index = (UINTN)ProcessorNumber;
226 CpuData = &CpuMpData->CpuData[Index];
227 ApicId = CpuInfoInHob[ProcessorNumber].ApicId,
228 SevSnpCreateSaveArea (CpuMpData, CpuData, ApicId);
229 }
230 }
231
232 /**
233 Issue RMPADJUST to adjust the VMSA attribute of an SEV-SNP page.
234
235 @param[in] PageAddress
236 @param[in] VmsaPage
237
238 @return RMPADJUST return value
239 **/
240 UINT32
241 SevSnpRmpAdjust (
242 IN EFI_PHYSICAL_ADDRESS PageAddress,
243 IN BOOLEAN VmsaPage
244 )
245 {
246 UINT64 Rdx;
247
248 //
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.
256 //
257 Rdx = 1;
258 if (VmsaPage) {
259 Rdx |= RMPADJUST_VMSA_PAGE_BIT;
260 }
261
262 return AsmRmpAdjust ((UINT64)PageAddress, 0, Rdx);
263 }