2 Initialize Secure Encrypted Virtualization (SEV) support
4 Copyright (c) 2017 - 2020, Advanced Micro Devices. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
10 // The package level header files this module uses
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>
21 #include <Register/Amd/Msr.h>
22 #include <Register/Intel/SmramSaveStateMap.h>
23 #include <Library/CcExitLib.h>
24 #include <ConfidentialComputingGuestAttr.h>
30 GetHypervisorFeature (
35 Initialize SEV-SNP support if running as an SEV-SNP guest.
44 EFI_PEI_HOB_POINTERS Hob
;
45 EFI_HOB_RESOURCE_DESCRIPTOR
*ResourceHob
;
49 if (!MemEncryptSevSnpIsEnabled ()) {
54 // Query the hypervisor feature using the CcExitVmgExit and set the value in the
55 // hypervisor features PCD.
57 HvFeatures
= GetHypervisorFeature ();
58 PcdStatus
= PcdSet64S (PcdGhcbHypervisorFeatures
, HvFeatures
);
59 ASSERT_RETURN_ERROR (PcdStatus
);
62 // Iterate through the system RAM and validate it.
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
;
68 if (ResourceHob
->ResourceType
== EFI_RESOURCE_SYSTEM_MEMORY
) {
69 if (ResourceHob
->PhysicalStart
>= SIZE_4GB
) {
70 ResourceHob
->ResourceType
= BZ3937_EFI_RESOURCE_MEMORY_UNACCEPTED
;
74 MemEncryptSevSnpPreValidateSystemRam (
75 ResourceHob
->PhysicalStart
,
76 EFI_SIZE_TO_PAGES ((UINTN
)ResourceHob
->ResourceLength
)
84 Handle an SEV-SNP/GHCB protocol check failure.
86 Notify the hypervisor using the VMGEXIT instruction that the SEV-SNP guest
87 wishes to be terminated.
89 @param[in] ReasonCode Reason code to provide to the hypervisor for the
95 SevEsProtocolFailure (
99 MSR_SEV_ES_GHCB_REGISTER Msr
;
102 // Use the GHCB MSR Protocol to request termination by the hypervisor
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
);
117 Get the hypervisor features bitmap
122 GetHypervisorFeature (
128 MSR_SEV_ES_GHCB_REGISTER Msr
;
129 BOOLEAN InterruptState
;
132 Msr
.GhcbPhysicalAddress
= AsmReadMsr64 (MSR_SEV_ES_GHCB
);
136 // Initialize the GHCB
138 CcExitVmgInit (Ghcb
, &InterruptState
);
141 // Query the Hypervisor Features.
143 Status
= CcExitVmgExit (Ghcb
, SVM_EXIT_HYPERVISOR_FEATURES
, 0, 0);
145 SevEsProtocolFailure (GHCB_TERMINATE_GHCB_GENERAL
);
148 Features
= Ghcb
->SaveArea
.SwExitInfo2
;
150 CcExitVmgDone (Ghcb
, InterruptState
);
157 This function can be used to register the GHCB GPA.
159 @param[in] Address The physical address to be registered.
165 IN EFI_PHYSICAL_ADDRESS Address
168 MSR_SEV_ES_GHCB_REGISTER Msr
;
169 MSR_SEV_ES_GHCB_REGISTER CurrentMsr
;
172 // Save the current MSR Value
174 CurrentMsr
.GhcbPhysicalAddress
= AsmReadMsr64 (MSR_SEV_ES_GHCB
);
177 // Use the GHCB MSR Protocol to request to register the GPA.
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
);
185 Msr
.GhcbPhysicalAddress
= AsmReadMsr64 (MSR_SEV_ES_GHCB
);
188 // If hypervisor responded with a different GPA than requested then fail.
190 if ((Msr
.GhcbGpaRegister
.Function
!= GHCB_INFO_GHCB_GPA_REGISTER_RESPONSE
) ||
191 ((Msr
.GhcbPhysicalAddress
& ~EFI_PAGE_MASK
) != Address
))
193 SevEsProtocolFailure (GHCB_TERMINATE_GHCB_GENERAL
);
199 AsmWriteMsr64 (MSR_SEV_ES_GHCB
, CurrentMsr
.GhcbPhysicalAddress
);
204 Initialize SEV-ES support if running as an SEV-ES guest.
210 IN EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
214 PHYSICAL_ADDRESS GhcbBasePa
;
216 UINT8
*GhcbBackupBase
;
217 UINT8
*GhcbBackupPages
;
218 UINTN GhcbBackupPageCount
;
219 SEV_ES_PER_CPU_DATA
*SevEsData
;
221 RETURN_STATUS Status
;
222 IA32_DESCRIPTOR Gdtr
;
225 if (!MemEncryptSevEsIsEnabled ()) {
229 Status
= PcdSetBoolS (PcdSevEsIsEnabled
, TRUE
);
230 ASSERT_RETURN_ERROR (Status
);
233 // Allocate GHCB and per-CPU variable pages.
234 // Since the pages must survive across the UEFI to OS transition
235 // make them reserved.
237 GhcbPageCount
= PlatformInfoHob
->PcdCpuMaxLogicalProcessorNumber
* 2;
238 GhcbBase
= AllocateReservedPages (GhcbPageCount
);
239 ASSERT (GhcbBase
!= NULL
);
241 GhcbBasePa
= (PHYSICAL_ADDRESS
)(UINTN
)GhcbBase
;
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.
248 for (PageCount
= 0; PageCount
< GhcbPageCount
; PageCount
+= 2) {
249 Status
= MemEncryptSevClearPageEncMask (
251 GhcbBasePa
+ EFI_PAGES_TO_SIZE (PageCount
),
254 ASSERT_RETURN_ERROR (Status
);
257 ZeroMem (GhcbBase
, EFI_PAGES_TO_SIZE (GhcbPageCount
));
259 Status
= PcdSet64S (PcdGhcbBase
, GhcbBasePa
);
260 ASSERT_RETURN_ERROR (Status
);
261 Status
= PcdSet64S (PcdGhcbSize
, EFI_PAGES_TO_SIZE (GhcbPageCount
));
262 ASSERT_RETURN_ERROR (Status
);
266 "SEV-ES is enabled, %lu GHCB pages allocated starting at 0x%p\n",
267 (UINT64
)GhcbPageCount
,
272 // Allocate #VC recursion backup pages. The number of backup pages needed is
273 // one less than the maximum VC count.
275 GhcbBackupPageCount
= PlatformInfoHob
->PcdCpuMaxLogicalProcessorNumber
* (VMGEXIT_MAXIMUM_VC_COUNT
- 1);
276 GhcbBackupBase
= AllocatePages (GhcbBackupPageCount
);
277 ASSERT (GhcbBackupBase
!= NULL
);
279 GhcbBackupPages
= GhcbBackupBase
;
280 for (PageCount
= 1; PageCount
< GhcbPageCount
; PageCount
+= 2) {
282 (SEV_ES_PER_CPU_DATA
*)(GhcbBase
+ EFI_PAGES_TO_SIZE (PageCount
));
283 SevEsData
->GhcbBackupPages
= GhcbBackupPages
;
285 GhcbBackupPages
+= EFI_PAGE_SIZE
* (VMGEXIT_MAXIMUM_VC_COUNT
- 1);
290 "SEV-ES is enabled, %lu GHCB backup pages allocated starting at 0x%p\n",
291 (UINT64
)GhcbBackupPageCount
,
296 // SEV-SNP guest requires that GHCB GPA must be registered before using it.
298 if (MemEncryptSevSnpIsEnabled ()) {
299 GhcbRegister (GhcbBasePa
);
302 AsmWriteMsr64 (MSR_SEV_ES_GHCB
, GhcbBasePa
);
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.
311 Status
= MemEncryptSevSetPageEncMask (
312 0, // Cr3 -- use system Cr3
313 FixedPcdGet32 (PcdOvmfSecGhcbBase
), // BaseAddress
316 ASSERT_RETURN_ERROR (Status
);
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.
327 Gdt
= AllocatePages (EFI_SIZE_TO_PAGES ((UINTN
)Gdtr
.Limit
+ 1));
328 ASSERT (Gdt
!= NULL
);
330 CopyMem (Gdt
, (VOID
*)Gdtr
.Base
, Gdtr
.Limit
+ 1);
331 Gdtr
.Base
= (UINTN
)Gdt
;
332 AsmWriteGdtr (&Gdtr
);
337 Function checks if SEV support is available, if present then it sets
338 the dynamic PcdPteMemoryEncryptionAddressOrMask with memory encryption mask.
343 IN OUT EFI_HOB_PLATFORM_INFO
*PlatformInfoHob
346 UINT64 EncryptionMask
;
347 RETURN_STATUS PcdStatus
;
350 // Check if SEV is enabled
352 if (!MemEncryptSevIsEnabled ()) {
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.
362 AmdSevSnpInitialize ();
365 // Set Memory Encryption Mask PCD
367 EncryptionMask
= MemEncryptSevGetEncryptionMask ();
368 PcdStatus
= PcdSet64S (PcdPteMemoryEncryptionAddressOrMask
, EncryptionMask
);
369 ASSERT_RETURN_ERROR (PcdStatus
);
371 DEBUG ((DEBUG_INFO
, "SEV is enabled (mask 0x%lx)\n", EncryptionMask
));
374 // Set Pcd to Deny the execution of option ROM when security
377 PcdStatus
= PcdSet32S (PcdOptionRomImageVerificationPolicy
, 0x4);
378 ASSERT_RETURN_ERROR (PcdStatus
);
381 // When SMM is required, cover the pages containing the initial SMRAM Save
382 // State Map with a memory allocation HOB:
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
390 if (PlatformInfoHob
->SmmSmramRequire
&& (PlatformInfoHob
->BootMode
!= BOOT_ON_S3_RESUME
)) {
391 RETURN_STATUS LocateMapStatus
;
395 LocateMapStatus
= MemEncryptSevLocateInitialSmramSaveStateMapPages (
399 ASSERT_RETURN_ERROR (LocateMapStatus
);
401 if (PlatformInfoHob
->Q35SmramAtDefaultSmbase
) {
403 // The initial SMRAM Save State Map has been covered as part of a larger
404 // reserved memory allocation in InitializeRamRegions().
406 ASSERT (SMM_DEFAULT_SMBASE
<= MapPagesBase
);
408 (MapPagesBase
+ EFI_PAGES_TO_SIZE (MapPagesCount
) <=
409 SMM_DEFAULT_SMBASE
+ MCH_DEFAULT_SMBASE_SIZE
)
412 BuildMemoryAllocationHob (
413 MapPagesBase
, // BaseAddress
414 EFI_PAGES_TO_SIZE (MapPagesCount
), // Length
415 EfiBootServicesData
// MemoryType
421 // Check and perform SEV-ES initialization if required.
423 AmdSevEsInitialize (PlatformInfoHob
);
426 // Set the Confidential computing attr PCD to communicate which SEV
427 // technology is active.
429 if (MemEncryptSevSnpIsEnabled ()) {
430 PcdStatus
= PcdSet64S (PcdConfidentialComputingGuestAttr
, CCAttrAmdSevSnp
);
431 } else if (MemEncryptSevEsIsEnabled ()) {
432 PcdStatus
= PcdSet64S (PcdConfidentialComputingGuestAttr
, CCAttrAmdSevEs
);
434 PcdStatus
= PcdSet64S (PcdConfidentialComputingGuestAttr
, CCAttrAmdSev
);
437 ASSERT_RETURN_ERROR (PcdStatus
);
441 The function performs SEV specific region initialization.
449 if (MemEncryptSevSnpIsEnabled ()) {
451 // If SEV-SNP is enabled, reserve the Secrets and CPUID memory area.
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.
458 BuildMemoryAllocationHob (
459 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfSnpSecretsBase
),
460 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfSnpSecretsSize
),
461 EfiReservedMemoryType
463 BuildMemoryAllocationHob (
464 (EFI_PHYSICAL_ADDRESS
)(UINTN
)PcdGet32 (PcdOvmfCpuidBase
),
465 (UINT64
)(UINTN
)PcdGet32 (PcdOvmfCpuidSize
),
466 EfiReservedMemoryType