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