]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/PlatformPei/AmdSev.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / OvmfPkg / PlatformPei / AmdSev.c
1 /**@file
2 Initialize Secure Encrypted Virtualization (SEV) support
3
4 Copyright (c) 2017 - 2020, Advanced Micro Devices. All rights reserved.<BR>
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9 //
10 // The package level header files this module uses
11 //
12 #include <IndustryStandard/Q35MchIch9.h>
13 #include <Library/BaseMemoryLib.h>
14 #include <Library/DebugLib.h>
15 #include <Library/HobLib.h>
16 #include <Library/MemEncryptSevLib.h>
17 #include <Library/MemoryAllocationLib.h>
18 #include <Library/PcdLib.h>
19 #include <Pi/PrePiHob.h>
20 #include <PiPei.h>
21 #include <Register/Amd/Msr.h>
22 #include <Register/Intel/SmramSaveStateMap.h>
23 #include <Library/CcExitLib.h>
24 #include <ConfidentialComputingGuestAttr.h>
25
26 #include "Platform.h"
27
28 STATIC
29 UINT64
30 GetHypervisorFeature (
31 VOID
32 );
33
34 /**
35 Initialize SEV-SNP support if running as an SEV-SNP guest.
36
37 **/
38 STATIC
39 VOID
40 AmdSevSnpInitialize (
41 VOID
42 )
43 {
44 EFI_PEI_HOB_POINTERS Hob;
45 EFI_HOB_RESOURCE_DESCRIPTOR *ResourceHob;
46 UINT64 HvFeatures;
47 EFI_STATUS PcdStatus;
48
49 if (!MemEncryptSevSnpIsEnabled ()) {
50 return;
51 }
52
53 //
54 // Query the hypervisor feature using the CcExitVmgExit and set the value in the
55 // hypervisor features PCD.
56 //
57 HvFeatures = GetHypervisorFeature ();
58 PcdStatus = PcdSet64S (PcdGhcbHypervisorFeatures, HvFeatures);
59 ASSERT_RETURN_ERROR (PcdStatus);
60
61 //
62 // Iterate through the system RAM and validate it.
63 //
64 for (Hob.Raw = GetHobList (); !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) {
65 if ((Hob.Raw != NULL) && (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR)) {
66 ResourceHob = Hob.ResourceDescriptor;
67
68 if (ResourceHob->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) {
69 if (ResourceHob->PhysicalStart >= SIZE_4GB) {
70 ResourceHob->ResourceType = BZ3937_EFI_RESOURCE_MEMORY_UNACCEPTED;
71 continue;
72 }
73
74 MemEncryptSevSnpPreValidateSystemRam (
75 ResourceHob->PhysicalStart,
76 EFI_SIZE_TO_PAGES ((UINTN)ResourceHob->ResourceLength)
77 );
78 }
79 }
80 }
81 }
82
83 /**
84 Handle an SEV-SNP/GHCB protocol check failure.
85
86 Notify the hypervisor using the VMGEXIT instruction that the SEV-SNP guest
87 wishes to be terminated.
88
89 @param[in] ReasonCode Reason code to provide to the hypervisor for the
90 termination request.
91
92 **/
93 STATIC
94 VOID
95 SevEsProtocolFailure (
96 IN UINT8 ReasonCode
97 )
98 {
99 MSR_SEV_ES_GHCB_REGISTER Msr;
100
101 //
102 // Use the GHCB MSR Protocol to request termination by the hypervisor
103 //
104 Msr.GhcbPhysicalAddress = 0;
105 Msr.GhcbTerminate.Function = GHCB_INFO_TERMINATE_REQUEST;
106 Msr.GhcbTerminate.ReasonCodeSet = GHCB_TERMINATE_GHCB;
107 Msr.GhcbTerminate.ReasonCode = ReasonCode;
108 AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);
109
110 AsmVmgExit ();
111
112 ASSERT (FALSE);
113 CpuDeadLoop ();
114 }
115
116 /**
117 Get the hypervisor features bitmap
118
119 **/
120 STATIC
121 UINT64
122 GetHypervisorFeature (
123 VOID
124 )
125 {
126 UINT64 Status;
127 GHCB *Ghcb;
128 MSR_SEV_ES_GHCB_REGISTER Msr;
129 BOOLEAN InterruptState;
130 UINT64 Features;
131
132 Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
133 Ghcb = Msr.Ghcb;
134
135 //
136 // Initialize the GHCB
137 //
138 CcExitVmgInit (Ghcb, &InterruptState);
139
140 //
141 // Query the Hypervisor Features.
142 //
143 Status = CcExitVmgExit (Ghcb, SVM_EXIT_HYPERVISOR_FEATURES, 0, 0);
144 if ((Status != 0)) {
145 SevEsProtocolFailure (GHCB_TERMINATE_GHCB_GENERAL);
146 }
147
148 Features = Ghcb->SaveArea.SwExitInfo2;
149
150 CcExitVmgDone (Ghcb, InterruptState);
151
152 return Features;
153 }
154
155 /**
156
157 This function can be used to register the GHCB GPA.
158
159 @param[in] Address The physical address to be registered.
160
161 **/
162 STATIC
163 VOID
164 GhcbRegister (
165 IN EFI_PHYSICAL_ADDRESS Address
166 )
167 {
168 MSR_SEV_ES_GHCB_REGISTER Msr;
169 MSR_SEV_ES_GHCB_REGISTER CurrentMsr;
170
171 //
172 // Save the current MSR Value
173 //
174 CurrentMsr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
175
176 //
177 // Use the GHCB MSR Protocol to request to register the GPA.
178 //
179 Msr.GhcbPhysicalAddress = Address & ~EFI_PAGE_MASK;
180 Msr.GhcbGpaRegister.Function = GHCB_INFO_GHCB_GPA_REGISTER_REQUEST;
181 AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);
182
183 AsmVmgExit ();
184
185 Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
186
187 //
188 // If hypervisor responded with a different GPA than requested then fail.
189 //
190 if ((Msr.GhcbGpaRegister.Function != GHCB_INFO_GHCB_GPA_REGISTER_RESPONSE) ||
191 ((Msr.GhcbPhysicalAddress & ~EFI_PAGE_MASK) != Address))
192 {
193 SevEsProtocolFailure (GHCB_TERMINATE_GHCB_GENERAL);
194 }
195
196 //
197 // Restore the MSR
198 //
199 AsmWriteMsr64 (MSR_SEV_ES_GHCB, CurrentMsr.GhcbPhysicalAddress);
200 }
201
202 /**
203
204 Initialize SEV-ES support if running as an SEV-ES guest.
205
206 **/
207 STATIC
208 VOID
209 AmdSevEsInitialize (
210 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
211 )
212 {
213 UINT8 *GhcbBase;
214 PHYSICAL_ADDRESS GhcbBasePa;
215 UINTN GhcbPageCount;
216 UINT8 *GhcbBackupBase;
217 UINT8 *GhcbBackupPages;
218 UINTN GhcbBackupPageCount;
219 SEV_ES_PER_CPU_DATA *SevEsData;
220 UINTN PageCount;
221 RETURN_STATUS Status;
222 IA32_DESCRIPTOR Gdtr;
223 VOID *Gdt;
224
225 if (!MemEncryptSevEsIsEnabled ()) {
226 return;
227 }
228
229 Status = PcdSetBoolS (PcdSevEsIsEnabled, TRUE);
230 ASSERT_RETURN_ERROR (Status);
231
232 //
233 // Allocate GHCB and per-CPU variable pages.
234 // Since the pages must survive across the UEFI to OS transition
235 // make them reserved.
236 //
237 GhcbPageCount = PlatformInfoHob->PcdCpuMaxLogicalProcessorNumber * 2;
238 GhcbBase = AllocateReservedPages (GhcbPageCount);
239 ASSERT (GhcbBase != NULL);
240
241 GhcbBasePa = (PHYSICAL_ADDRESS)(UINTN)GhcbBase;
242
243 //
244 // Each vCPU gets two consecutive pages, the first is the GHCB and the
245 // second is the per-CPU variable page. Loop through the allocation and
246 // only clear the encryption mask for the GHCB pages.
247 //
248 for (PageCount = 0; PageCount < GhcbPageCount; PageCount += 2) {
249 Status = MemEncryptSevClearPageEncMask (
250 0,
251 GhcbBasePa + EFI_PAGES_TO_SIZE (PageCount),
252 1
253 );
254 ASSERT_RETURN_ERROR (Status);
255 }
256
257 ZeroMem (GhcbBase, EFI_PAGES_TO_SIZE (GhcbPageCount));
258
259 Status = PcdSet64S (PcdGhcbBase, GhcbBasePa);
260 ASSERT_RETURN_ERROR (Status);
261 Status = PcdSet64S (PcdGhcbSize, EFI_PAGES_TO_SIZE (GhcbPageCount));
262 ASSERT_RETURN_ERROR (Status);
263
264 DEBUG ((
265 DEBUG_INFO,
266 "SEV-ES is enabled, %lu GHCB pages allocated starting at 0x%p\n",
267 (UINT64)GhcbPageCount,
268 GhcbBase
269 ));
270
271 //
272 // Allocate #VC recursion backup pages. The number of backup pages needed is
273 // one less than the maximum VC count.
274 //
275 GhcbBackupPageCount = PlatformInfoHob->PcdCpuMaxLogicalProcessorNumber * (VMGEXIT_MAXIMUM_VC_COUNT - 1);
276 GhcbBackupBase = AllocatePages (GhcbBackupPageCount);
277 ASSERT (GhcbBackupBase != NULL);
278
279 GhcbBackupPages = GhcbBackupBase;
280 for (PageCount = 1; PageCount < GhcbPageCount; PageCount += 2) {
281 SevEsData =
282 (SEV_ES_PER_CPU_DATA *)(GhcbBase + EFI_PAGES_TO_SIZE (PageCount));
283 SevEsData->GhcbBackupPages = GhcbBackupPages;
284
285 GhcbBackupPages += EFI_PAGE_SIZE * (VMGEXIT_MAXIMUM_VC_COUNT - 1);
286 }
287
288 DEBUG ((
289 DEBUG_INFO,
290 "SEV-ES is enabled, %lu GHCB backup pages allocated starting at 0x%p\n",
291 (UINT64)GhcbBackupPageCount,
292 GhcbBackupBase
293 ));
294
295 //
296 // SEV-SNP guest requires that GHCB GPA must be registered before using it.
297 //
298 if (MemEncryptSevSnpIsEnabled ()) {
299 GhcbRegister (GhcbBasePa);
300 }
301
302 AsmWriteMsr64 (MSR_SEV_ES_GHCB, GhcbBasePa);
303
304 //
305 // Now that the PEI GHCB is set up, the SEC GHCB page is no longer necessary
306 // to keep shared. Later, it is exposed to the OS as EfiConventionalMemory, so
307 // it needs to be marked private. The size of the region is hardcoded in
308 // OvmfPkg/ResetVector/ResetVector.nasmb in the definition of
309 // SNP_SEC_MEM_BASE_DESC_2.
310 //
311 Status = MemEncryptSevSetPageEncMask (
312 0, // Cr3 -- use system Cr3
313 FixedPcdGet32 (PcdOvmfSecGhcbBase), // BaseAddress
314 1 // NumPages
315 );
316 ASSERT_RETURN_ERROR (Status);
317
318 //
319 // The SEV support will clear the C-bit from non-RAM areas. The early GDT
320 // lives in a non-RAM area, so when an exception occurs (like a #VC) the GDT
321 // will be read as un-encrypted even though it was created before the C-bit
322 // was cleared (encrypted). This will result in a failure to be able to
323 // handle the exception.
324 //
325 AsmReadGdtr (&Gdtr);
326
327 Gdt = AllocatePages (EFI_SIZE_TO_PAGES ((UINTN)Gdtr.Limit + 1));
328 ASSERT (Gdt != NULL);
329
330 CopyMem (Gdt, (VOID *)Gdtr.Base, Gdtr.Limit + 1);
331 Gdtr.Base = (UINTN)Gdt;
332 AsmWriteGdtr (&Gdtr);
333 }
334
335 /**
336
337 Function checks if SEV support is available, if present then it sets
338 the dynamic PcdPteMemoryEncryptionAddressOrMask with memory encryption mask.
339
340 **/
341 VOID
342 AmdSevInitialize (
343 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
344 )
345 {
346 UINT64 EncryptionMask;
347 RETURN_STATUS PcdStatus;
348
349 //
350 // Check if SEV is enabled
351 //
352 if (!MemEncryptSevIsEnabled ()) {
353 return;
354 }
355
356 //
357 // Check and perform SEV-SNP initialization if required. This need to be
358 // done before the GHCB page is made shared in the AmdSevEsInitialize(). This
359 // is because the system RAM must be validated before it is made shared.
360 // The AmdSevSnpInitialize() validates the system RAM.
361 //
362 AmdSevSnpInitialize ();
363
364 //
365 // Set Memory Encryption Mask PCD
366 //
367 EncryptionMask = MemEncryptSevGetEncryptionMask ();
368 PcdStatus = PcdSet64S (PcdPteMemoryEncryptionAddressOrMask, EncryptionMask);
369 ASSERT_RETURN_ERROR (PcdStatus);
370
371 DEBUG ((DEBUG_INFO, "SEV is enabled (mask 0x%lx)\n", EncryptionMask));
372
373 //
374 // Set Pcd to Deny the execution of option ROM when security
375 // violation.
376 //
377 PcdStatus = PcdSet32S (PcdOptionRomImageVerificationPolicy, 0x4);
378 ASSERT_RETURN_ERROR (PcdStatus);
379
380 //
381 // When SMM is required, cover the pages containing the initial SMRAM Save
382 // State Map with a memory allocation HOB:
383 //
384 // There's going to be a time interval between our decrypting those pages for
385 // SMBASE relocation and re-encrypting the same pages after SMBASE
386 // relocation. We shall ensure that the DXE phase stay away from those pages
387 // until after re-encryption, in order to prevent an information leak to the
388 // hypervisor.
389 //
390 if (PlatformInfoHob->SmmSmramRequire && (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME)) {
391 RETURN_STATUS LocateMapStatus;
392 UINTN MapPagesBase;
393 UINTN MapPagesCount;
394
395 LocateMapStatus = MemEncryptSevLocateInitialSmramSaveStateMapPages (
396 &MapPagesBase,
397 &MapPagesCount
398 );
399 ASSERT_RETURN_ERROR (LocateMapStatus);
400
401 if (PlatformInfoHob->Q35SmramAtDefaultSmbase) {
402 //
403 // The initial SMRAM Save State Map has been covered as part of a larger
404 // reserved memory allocation in InitializeRamRegions().
405 //
406 ASSERT (SMM_DEFAULT_SMBASE <= MapPagesBase);
407 ASSERT (
408 (MapPagesBase + EFI_PAGES_TO_SIZE (MapPagesCount) <=
409 SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE)
410 );
411 } else {
412 BuildMemoryAllocationHob (
413 MapPagesBase, // BaseAddress
414 EFI_PAGES_TO_SIZE (MapPagesCount), // Length
415 EfiBootServicesData // MemoryType
416 );
417 }
418 }
419
420 //
421 // Check and perform SEV-ES initialization if required.
422 //
423 AmdSevEsInitialize (PlatformInfoHob);
424
425 //
426 // Set the Confidential computing attr PCD to communicate which SEV
427 // technology is active.
428 //
429 if (MemEncryptSevSnpIsEnabled ()) {
430 PcdStatus = PcdSet64S (PcdConfidentialComputingGuestAttr, CCAttrAmdSevSnp);
431 } else if (MemEncryptSevEsIsEnabled ()) {
432 PcdStatus = PcdSet64S (PcdConfidentialComputingGuestAttr, CCAttrAmdSevEs);
433 } else {
434 PcdStatus = PcdSet64S (PcdConfidentialComputingGuestAttr, CCAttrAmdSev);
435 }
436
437 ASSERT_RETURN_ERROR (PcdStatus);
438 }
439
440 /**
441 The function performs SEV specific region initialization.
442
443 **/
444 VOID
445 SevInitializeRam (
446 VOID
447 )
448 {
449 if (MemEncryptSevSnpIsEnabled ()) {
450 //
451 // If SEV-SNP is enabled, reserve the Secrets and CPUID memory area.
452 //
453 // This memory range is given to the PSP by the hypervisor to populate
454 // the information used during the SNP VM boots, and it need to persist
455 // across the kexec boots. Mark it as EfiReservedMemoryType so that
456 // the guest firmware and OS does not use it as a system memory.
457 //
458 BuildMemoryAllocationHob (
459 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSnpSecretsBase),
460 (UINT64)(UINTN)PcdGet32 (PcdOvmfSnpSecretsSize),
461 EfiReservedMemoryType
462 );
463 BuildMemoryAllocationHob (
464 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfCpuidBase),
465 (UINT64)(UINTN)PcdGet32 (PcdOvmfCpuidSize),
466 EfiReservedMemoryType
467 );
468 }
469 }