VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/Firmware/OvmfPkg/PlatformPei/AmdSev.c@ 107675

最後變更 在這個檔案從107675是 105670,由 vboxsync 提交於 7 月 前

Devices/EFI/FirmwareNew: Merge edk2-stable-202405 and make it build on aarch64, bugref:4643

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette