1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
|
/** @file
AMD SEV helper function.
Copyright (c) 2021 - 2024, AMD Incorporated. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "MpLib.h"
#include <Library/CcExitLib.h>
#include <Library/AmdSvsmLib.h>
#include <Register/Amd/Fam17Msr.h>
#include <Register/Amd/Ghcb.h>
#define _IS_ALIGNED(x, y) (ALIGN_POINTER((x), (y)) == (x))
/**
Perform the requested AP Creation action.
@param[in] SaveArea Pointer to VM save area (VMSA)
@param[in] ApicId APIC ID of the vCPU
@param[in] Action AP action to perform
@retval TRUE Action completed successfully
@retval FALSE Action did not complete successfully
**/
STATIC
BOOLEAN
SevSnpPerformApAction (
IN SEV_ES_SAVE_AREA *SaveArea,
IN UINT32 ApicId,
IN UINTN Action
)
{
MSR_SEV_ES_GHCB_REGISTER Msr;
GHCB *Ghcb;
BOOLEAN InterruptState;
UINT64 ExitInfo1;
UINT64 ExitInfo2;
UINT64 VmgExitStatus;
EFI_STATUS VmsaStatus;
if (Action == SVM_VMGEXIT_SNP_AP_CREATE) {
//
// Turn the page into a recognized VMSA page.
//
VmsaStatus = AmdSvsmSnpVmsaRmpAdjust (SaveArea, ApicId, TRUE);
if (EFI_ERROR (VmsaStatus)) {
DEBUG ((DEBUG_INFO, "SEV-SNP: RMPADJUST failed for VMSA creation\n"));
ASSERT (FALSE);
return FALSE;
}
}
ExitInfo1 = (UINT64)ApicId << 32;
ExitInfo1 |= Action;
ExitInfo2 = (UINT64)(UINTN)SaveArea;
Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
Ghcb = Msr.Ghcb;
CcExitVmgInit (Ghcb, &InterruptState);
if (Action == SVM_VMGEXIT_SNP_AP_CREATE) {
Ghcb->SaveArea.Rax = SaveArea->SevFeatures;
CcExitVmgSetOffsetValid (Ghcb, GhcbRax);
}
VmgExitStatus = CcExitVmgExit (
Ghcb,
SVM_EXIT_SNP_AP_CREATION,
ExitInfo1,
ExitInfo2
);
CcExitVmgDone (Ghcb, InterruptState);
if (VmgExitStatus != 0) {
DEBUG ((DEBUG_INFO, "SEV-SNP: AP Destroy failed\n"));
ASSERT (FALSE);
return FALSE;
}
if (Action == SVM_VMGEXIT_SNP_AP_DESTROY) {
//
// Make the current VMSA not runnable and accessible to be
// reprogrammed.
//
VmsaStatus = AmdSvsmSnpVmsaRmpAdjust (SaveArea, ApicId, FALSE);
if (EFI_ERROR (VmsaStatus)) {
DEBUG ((DEBUG_INFO, "SEV-SNP: RMPADJUST failed for VMSA reset\n"));
ASSERT (FALSE);
return FALSE;
}
}
return TRUE;
}
/**
Create an SEV-SNP AP save area (VMSA) for use in running the vCPU.
@param[in] CpuMpData Pointer to CPU MP Data
@param[in] CpuData Pointer to CPU AP Data
@param[in] ApicId APIC ID of the vCPU
**/
VOID
SevSnpCreateSaveArea (
IN CPU_MP_DATA *CpuMpData,
IN CPU_AP_DATA *CpuData,
UINT32 ApicId
)
{
UINT8 *Pages;
SEV_ES_SAVE_AREA *SaveArea;
IA32_CR0 ApCr0;
IA32_CR0 ResetCr0;
IA32_CR4 ApCr4;
IA32_CR4 ResetCr4;
UINTN StartIp;
UINT8 SipiVector;
if (CpuData->SevEsSaveArea == NULL) {
//
// Allocate a page for the SEV-ES Save Area and initialize it. Due to AMD
// erratum #1467 (VMSA cannot be on a 2MB boundary), allocate an extra page
// to choose from to work around the issue.
//
Pages = AllocateReservedPages (2);
if (!Pages) {
return;
}
//
// Since page allocation works by allocating downward in the address space,
// try to always free the first (lower address) page to limit possible holes
// in the memory map. So, if the address of the second page is 2MB aligned,
// then use the first page and free the second page. Otherwise, free the
// first page and use the second page.
//
if (_IS_ALIGNED (Pages + EFI_PAGE_SIZE, SIZE_2MB)) {
SaveArea = (SEV_ES_SAVE_AREA *)Pages;
FreePages (Pages + EFI_PAGE_SIZE, 1);
} else {
SaveArea = (SEV_ES_SAVE_AREA *)(Pages + EFI_PAGE_SIZE);
FreePages (Pages, 1);
}
CpuData->SevEsSaveArea = SaveArea;
} else {
SaveArea = CpuData->SevEsSaveArea;
//
// Tell the hypervisor to not use the current VMSA
//
if (!SevSnpPerformApAction (SaveArea, ApicId, SVM_VMGEXIT_SNP_AP_DESTROY)) {
return;
}
}
ZeroMem (SaveArea, EFI_PAGE_SIZE);
//
// Propogate the CR0.NW and CR0.CD setting to the AP
//
ResetCr0.UintN = 0x00000010;
ApCr0.UintN = CpuData->VolatileRegisters.Cr0;
if (ApCr0.Bits.NW) {
ResetCr0.Bits.NW = 1;
}
if (ApCr0.Bits.CD) {
ResetCr0.Bits.CD = 1;
}
//
// Propagate the CR4.MCE setting to the AP
//
ResetCr4.UintN = 0;
ApCr4.UintN = CpuData->VolatileRegisters.Cr4;
if (ApCr4.Bits.MCE) {
ResetCr4.Bits.MCE = 1;
}
//
// Convert the start IP into a SIPI Vector
//
StartIp = CpuMpData->MpCpuExchangeInfo->BufferStart;
SipiVector = (UINT8)(StartIp >> 12);
//
// Set the CS:RIP value based on the start IP
//
SaveArea->Cs.Base = SipiVector << 12;
SaveArea->Cs.Selector = SipiVector << 8;
SaveArea->Cs.Limit = 0xFFFF;
SaveArea->Cs.Attributes.Bits.Present = 1;
SaveArea->Cs.Attributes.Bits.Sbit = 1;
SaveArea->Cs.Attributes.Bits.Type = SEV_ES_RESET_CODE_SEGMENT_TYPE;
SaveArea->Rip = StartIp & 0xFFF;
//
// Set the remaining values as defined in APM for INIT
//
SaveArea->Ds.Limit = 0xFFFF;
SaveArea->Ds.Attributes.Bits.Present = 1;
SaveArea->Ds.Attributes.Bits.Sbit = 1;
SaveArea->Ds.Attributes.Bits.Type = SEV_ES_RESET_DATA_SEGMENT_TYPE;
SaveArea->Es = SaveArea->Ds;
SaveArea->Fs = SaveArea->Ds;
SaveArea->Gs = SaveArea->Ds;
SaveArea->Ss = SaveArea->Ds;
SaveArea->Gdtr.Limit = 0xFFFF;
SaveArea->Ldtr.Limit = 0xFFFF;
SaveArea->Ldtr.Attributes.Bits.Present = 1;
SaveArea->Ldtr.Attributes.Bits.Type = SEV_ES_RESET_LDT_TYPE;
SaveArea->Idtr.Limit = 0xFFFF;
SaveArea->Tr.Limit = 0xFFFF;
SaveArea->Ldtr.Attributes.Bits.Present = 1;
SaveArea->Ldtr.Attributes.Bits.Type = SEV_ES_RESET_TSS_TYPE;
SaveArea->Efer = 0x1000;
SaveArea->Cr4 = ResetCr4.UintN;
SaveArea->Cr0 = ResetCr0.UintN;
SaveArea->Dr7 = 0x0400;
SaveArea->Dr6 = 0xFFFF0FF0;
SaveArea->Rflags = 0x0002;
SaveArea->GPat = 0x0007040600070406ULL;
SaveArea->XCr0 = 0x0001;
SaveArea->Mxcsr = 0x1F80;
SaveArea->X87Ftw = 0x5555;
SaveArea->X87Fcw = 0x0040;
//
// Set the SEV-SNP specific fields for the save area:
// VMPL - always VMPL0
// SEV_FEATURES - equivalent to the SEV_STATUS MSR right shifted 2 bits
//
SaveArea->Vmpl = 0;
SaveArea->SevFeatures = AsmReadMsr64 (MSR_SEV_STATUS) >> 2;
SevSnpPerformApAction (SaveArea, ApicId, SVM_VMGEXIT_SNP_AP_CREATE);
}
/**
Create SEV-SNP APs.
@param[in] CpuMpData Pointer to CPU MP Data
@param[in] ProcessorNumber The handle number of specified processor
(-1 for all APs)
**/
VOID
SevSnpCreateAP (
IN CPU_MP_DATA *CpuMpData,
IN INTN ProcessorNumber
)
{
CPU_INFO_IN_HOB *CpuInfoInHob;
CPU_AP_DATA *CpuData;
UINTN Index;
UINTN MaxIndex;
UINT32 ApicId;
EFI_HOB_GUID_TYPE *GuidHob;
GHCB_APIC_IDS *GhcbApicIds;
ASSERT (CpuMpData->MpCpuExchangeInfo->BufferStart < 0x100000);
CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob;
if (ProcessorNumber < 0) {
if (CpuMpData->InitFlag == ApInitConfig) {
//
// APs have not been started, so CpuCount is not "known" yet. Use the
// retrieved APIC IDs to start the APs and fill out the MpLib CPU
// information properly. CanUseSevSnpCreateAP() guarantees we have a
// HOB when InitFlag is ApInitConfig.
//
GuidHob = GetFirstGuidHob (&gGhcbApicIdsGuid);
GhcbApicIds = (GHCB_APIC_IDS *)(*(UINTN *)GET_GUID_HOB_DATA (GuidHob));
MaxIndex = MIN (GhcbApicIds->NumEntries, PcdGet32 (PcdCpuMaxLogicalProcessorNumber));
} else {
//
// APs have been previously started.
//
MaxIndex = CpuMpData->CpuCount;
}
for (Index = 0; Index < MaxIndex; Index++) {
if (Index != CpuMpData->BspNumber) {
CpuData = &CpuMpData->CpuData[Index];
if (CpuMpData->InitFlag == ApInitConfig) {
ApicId = GhcbApicIds->ApicIds[Index];
//
// For the first boot, use the BSP register information.
//
CopyMem (
&CpuData->VolatileRegisters,
&CpuMpData->CpuData[0].VolatileRegisters,
sizeof (CpuData->VolatileRegisters)
);
} else {
ApicId = CpuInfoInHob[Index].ApicId;
}
SevSnpCreateSaveArea (CpuMpData, CpuData, ApicId);
}
}
} else {
Index = (UINTN)ProcessorNumber;
CpuData = &CpuMpData->CpuData[Index];
ApicId = CpuInfoInHob[ProcessorNumber].ApicId,
SevSnpCreateSaveArea (CpuMpData, CpuData, ApicId);
}
}
/**
Determine if the SEV-SNP AP Create protocol should be used.
@param[in] CpuMpData Pointer to CPU MP Data
@retval TRUE Use SEV-SNP AP Create protocol
@retval FALSE Do not use SEV-SNP AP Create protocol
**/
BOOLEAN
CanUseSevSnpCreateAP (
IN CPU_MP_DATA *CpuMpData
)
{
//
// The AP Create protocol is used for an SEV-SNP guest if
// - The initial configuration has been performed already or
// - The APIC IDs GUIDed HOB is non-zero.
//
if (!CpuMpData->SevSnpIsEnabled) {
return FALSE;
}
if ((CpuMpData->InitFlag == ApInitConfig) && (GetFirstGuidHob (&gGhcbApicIdsGuid) == NULL)) {
return FALSE;
}
return TRUE;
}
|