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