]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/AmdSevDxe/AmdSevDxe.c
OvmfPkg: Add memory acceptance event in AmdSevDxe
[mirror_edk2.git] / OvmfPkg / AmdSevDxe / AmdSevDxe.c
CommitLineData
24e4ad75
BS
1/** @file\r
2\r
3 AMD Sev Dxe driver. This driver is dispatched early in DXE, due to being list\r
c16d4e35
LE
4 in APRIORI. It clears C-bit from MMIO and NonExistent Memory space when SEV\r
5 is enabled.\r
24e4ad75 6\r
84cddd70 7 Copyright (c) 2017 - 2020, AMD Inc. All rights reserved.<BR>\r
24e4ad75 8\r
b26f0cf9 9 SPDX-License-Identifier: BSD-2-Clause-Patent\r
24e4ad75
BS
10\r
11**/\r
12\r
84cddd70 13#include <IndustryStandard/Q35MchIch9.h>\r
5e2e5647
LE
14#include <Library/BaseLib.h>\r
15#include <Library/BaseMemoryLib.h>\r
c6073a0e 16#include <Library/DebugLib.h>\r
24e4ad75
BS
17#include <Library/DxeServicesTableLib.h>\r
18#include <Library/MemEncryptSevLib.h>\r
c6073a0e 19#include <Library/MemoryAllocationLib.h>\r
67484aed
BS
20#include <Library/UefiBootServicesTableLib.h>\r
21#include <Guid/ConfidentialComputingSevSnpBlob.h>\r
5e2e5647 22#include <Library/PcdLib.h>\r
a00e2e55 23#include <Pi/PrePiDxeCis.h>\r
59aa48bb 24#include <Protocol/MemoryAccept.h>\r
24e4ad75 25\r
67484aed
BS
26STATIC CONFIDENTIAL_COMPUTING_SNP_BLOB_LOCATION mSnpBootDxeTable = {\r
27 SIGNATURE_32 ('A', 'M', 'D', 'E'),\r
28 1,\r
29 0,\r
30 (UINT64)(UINTN)FixedPcdGet32 (PcdOvmfSnpSecretsBase),\r
31 FixedPcdGet32 (PcdOvmfSnpSecretsSize),\r
32 (UINT64)(UINTN)FixedPcdGet32 (PcdOvmfCpuidBase),\r
33 FixedPcdGet32 (PcdOvmfCpuidSize),\r
34};\r
35\r
59aa48bb
SW
36STATIC EFI_HANDLE mAmdSevDxeHandle = NULL;\r
37\r
a00e2e55
DG
38STATIC BOOLEAN mAcceptAllMemoryAtEBS = TRUE;\r
39\r
40STATIC EFI_EVENT mAcceptAllMemoryEvent = NULL;\r
41\r
59aa48bb
SW
42#define IS_ALIGNED(x, y) ((((x) & ((y) - 1)) == 0))\r
43\r
44STATIC\r
45EFI_STATUS\r
46EFIAPI\r
47AmdSevMemoryAccept (\r
48 IN EDKII_MEMORY_ACCEPT_PROTOCOL *This,\r
49 IN EFI_PHYSICAL_ADDRESS StartAddress,\r
50 IN UINTN Size\r
51 )\r
52{\r
53 //\r
54 // The StartAddress must be page-aligned, and the Size must be a positive\r
55 // multiple of SIZE_4KB. Use an assert instead of returning an erros since\r
56 // this is an EDK2-internal protocol.\r
57 //\r
58 ASSERT (IS_ALIGNED (StartAddress, SIZE_4KB));\r
59 ASSERT (IS_ALIGNED (Size, SIZE_4KB));\r
60 ASSERT (Size != 0);\r
61\r
62 MemEncryptSevSnpPreValidateSystemRam (\r
63 StartAddress,\r
64 EFI_SIZE_TO_PAGES (Size)\r
65 );\r
66\r
67 return EFI_SUCCESS;\r
68}\r
69\r
a00e2e55
DG
70STATIC\r
71EFI_STATUS\r
72AcceptAllMemory (\r
73 VOID\r
74 )\r
75{\r
76 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *AllDescMap;\r
77 UINTN NumEntries;\r
78 UINTN Index;\r
79 EFI_STATUS Status;\r
80\r
81 DEBUG ((DEBUG_INFO, "Accepting all memory\n"));\r
82\r
83 /*\r
84 * Get a copy of the memory space map to iterate over while\r
85 * changing the map.\r
86 */\r
87 Status = gDS->GetMemorySpaceMap (&NumEntries, &AllDescMap);\r
88 if (EFI_ERROR (Status)) {\r
89 return Status;\r
90 }\r
91\r
92 for (Index = 0; Index < NumEntries; Index++) {\r
93 CONST EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Desc;\r
94\r
95 Desc = &AllDescMap[Index];\r
96 if (Desc->GcdMemoryType != EFI_GCD_MEMORY_TYPE_UNACCEPTED) {\r
97 continue;\r
98 }\r
99\r
100 Status = AmdSevMemoryAccept (\r
101 NULL,\r
102 Desc->BaseAddress,\r
103 Desc->Length\r
104 );\r
105 if (EFI_ERROR (Status)) {\r
106 break;\r
107 }\r
108\r
109 Status = gDS->RemoveMemorySpace (Desc->BaseAddress, Desc->Length);\r
110 if (EFI_ERROR (Status)) {\r
111 break;\r
112 }\r
113\r
114 Status = gDS->AddMemorySpace (\r
115 EfiGcdMemoryTypeSystemMemory,\r
116 Desc->BaseAddress,\r
117 Desc->Length,\r
118 EFI_MEMORY_CPU_CRYPTO | EFI_MEMORY_XP | EFI_MEMORY_RO | EFI_MEMORY_RP\r
119 );\r
120 if (EFI_ERROR (Status)) {\r
121 break;\r
122 }\r
123 }\r
124\r
125 gBS->FreePool (AllDescMap);\r
126 return Status;\r
127}\r
128\r
129VOID\r
130EFIAPI\r
131ResolveUnacceptedMemory (\r
132 IN EFI_EVENT Event,\r
133 IN VOID *Context\r
134 )\r
135{\r
136 EFI_STATUS Status;\r
137\r
138 if (!mAcceptAllMemoryAtEBS) {\r
139 return;\r
140 }\r
141\r
142 Status = AcceptAllMemory ();\r
143 ASSERT_EFI_ERROR (Status);\r
144}\r
145\r
59aa48bb
SW
146STATIC EDKII_MEMORY_ACCEPT_PROTOCOL mMemoryAcceptProtocol = {\r
147 AmdSevMemoryAccept\r
148};\r
149\r
24e4ad75
BS
150EFI_STATUS\r
151EFIAPI\r
152AmdSevDxeEntryPoint (\r
ac0a286f
MK
153 IN EFI_HANDLE ImageHandle,\r
154 IN EFI_SYSTEM_TABLE *SystemTable\r
24e4ad75
BS
155 )\r
156{\r
157 EFI_STATUS Status;\r
158 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *AllDescMap;\r
159 UINTN NumEntries;\r
160 UINTN Index;\r
161\r
162 //\r
163 // Do nothing when SEV is not enabled\r
164 //\r
165 if (!MemEncryptSevIsEnabled ()) {\r
166 return EFI_UNSUPPORTED;\r
167 }\r
168\r
169 //\r
170 // Iterate through the GCD map and clear the C-bit from MMIO and NonExistent\r
c16d4e35
LE
171 // memory space. The NonExistent memory space will be used for mapping the\r
172 // MMIO space added later (eg PciRootBridge). By clearing both known MMIO and\r
24e4ad75
BS
173 // NonExistent memory space can gurantee that current and furture MMIO adds\r
174 // will have C-bit cleared.\r
175 //\r
176 Status = gDS->GetMemorySpaceMap (&NumEntries, &AllDescMap);\r
177 if (!EFI_ERROR (Status)) {\r
178 for (Index = 0; Index < NumEntries; Index++) {\r
ac0a286f 179 CONST EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Desc;\r
24e4ad75
BS
180\r
181 Desc = &AllDescMap[Index];\r
ac0a286f
MK
182 if ((Desc->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) ||\r
183 (Desc->GcdMemoryType == EfiGcdMemoryTypeNonExistent))\r
184 {\r
c394fa4c 185 Status = MemEncryptSevClearMmioPageEncMask (\r
c16d4e35
LE
186 0,\r
187 Desc->BaseAddress,\r
c394fa4c 188 EFI_SIZE_TO_PAGES (Desc->Length)\r
c16d4e35 189 );\r
24e4ad75
BS
190 ASSERT_EFI_ERROR (Status);\r
191 }\r
192 }\r
193\r
194 FreePool (AllDescMap);\r
195 }\r
196\r
84cddd70
TL
197 //\r
198 // If PCI Express is enabled, the MMCONFIG area has been reserved, rather\r
199 // than marked as MMIO, and so the C-bit won't be cleared by the above walk\r
200 // through the GCD map. Check for the MMCONFIG area and clear the C-bit for\r
201 // the range.\r
202 //\r
203 if (PcdGet16 (PcdOvmfHostBridgePciDevId) == INTEL_Q35_MCH_DEVICE_ID) {\r
c394fa4c 204 Status = MemEncryptSevClearMmioPageEncMask (\r
84cddd70
TL
205 0,\r
206 FixedPcdGet64 (PcdPciExpressBaseAddress),\r
c394fa4c 207 EFI_SIZE_TO_PAGES (SIZE_256MB)\r
84cddd70
TL
208 );\r
209\r
210 ASSERT_EFI_ERROR (Status);\r
211 }\r
212\r
5e2e5647
LE
213 //\r
214 // When SMM is enabled, clear the C-bit from SMM Saved State Area\r
215 //\r
216 // NOTES: The SavedStateArea address cleared here is before SMBASE\r
217 // relocation. Currently, we do not clear the SavedStateArea address after\r
218 // SMBASE is relocated due to the following reasons:\r
219 //\r
220 // 1) Guest BIOS never access the relocated SavedStateArea.\r
221 //\r
222 // 2) The C-bit works on page-aligned address, but the SavedStateArea\r
223 // address is not a page-aligned. Theoretically, we could roundup the address\r
224 // and clear the C-bit of aligned address but looking carefully we found\r
225 // that some portion of the page contains code -- which will causes a bigger\r
226 // issues for SEV guest. When SEV is enabled, all the code must be encrypted\r
227 // otherwise hardware will cause trap.\r
228 //\r
229 // We restore the C-bit for this SMM Saved State Area after SMBASE relocation\r
230 // is completed (See OvmfPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.c).\r
231 //\r
232 if (FeaturePcdGet (PcdSmmSmramRequire)) {\r
ac0a286f
MK
233 UINTN MapPagesBase;\r
234 UINTN MapPagesCount;\r
5e2e5647
LE
235\r
236 Status = MemEncryptSevLocateInitialSmramSaveStateMapPages (\r
237 &MapPagesBase,\r
238 &MapPagesCount\r
239 );\r
240 ASSERT_EFI_ERROR (Status);\r
241\r
242 //\r
243 // Although these pages were set aside (i.e., allocated) by PlatformPei, we\r
244 // could be after a warm reboot from the OS. Don't leak any stale OS data\r
245 // to the hypervisor.\r
246 //\r
247 ZeroMem ((VOID *)MapPagesBase, EFI_PAGES_TO_SIZE (MapPagesCount));\r
248\r
249 Status = MemEncryptSevClearPageEncMask (\r
250 0, // Cr3BaseAddress -- use current CR3\r
251 MapPagesBase, // BaseAddress\r
adfa3327 252 MapPagesCount // NumPages\r
5e2e5647
LE
253 );\r
254 if (EFI_ERROR (Status)) {\r
ac0a286f
MK
255 DEBUG ((\r
256 DEBUG_ERROR,\r
257 "%a: MemEncryptSevClearPageEncMask(): %r\n",\r
258 __FUNCTION__,\r
259 Status\r
260 ));\r
5e2e5647
LE
261 ASSERT (FALSE);\r
262 CpuDeadLoop ();\r
263 }\r
264 }\r
265\r
67484aed 266 if (MemEncryptSevSnpIsEnabled ()) {\r
59aa48bb
SW
267 //\r
268 // Memory acceptance began being required in SEV-SNP, so install the\r
269 // memory accept protocol implementation for a SEV-SNP active guest.\r
270 //\r
271 Status = gBS->InstallProtocolInterface (\r
272 &mAmdSevDxeHandle,\r
273 &gEdkiiMemoryAcceptProtocolGuid,\r
274 EFI_NATIVE_INTERFACE,\r
275 &mMemoryAcceptProtocol\r
276 );\r
277 ASSERT_EFI_ERROR (Status);\r
278\r
a00e2e55
DG
279 // SEV-SNP support does not automatically imply unaccepted memory support,\r
280 // so make ExitBootServices accept all unaccepted memory if support is\r
281 // not communicated.\r
282 Status = gBS->CreateEventEx (\r
283 EVT_NOTIFY_SIGNAL,\r
284 TPL_CALLBACK,\r
285 ResolveUnacceptedMemory,\r
286 NULL,\r
287 &gEfiEventBeforeExitBootServicesGuid,\r
288 &mAcceptAllMemoryEvent\r
289 );\r
290\r
291 if (EFI_ERROR (Status)) {\r
292 DEBUG ((DEBUG_ERROR, "AllowUnacceptedMemory event creation for EventBeforeExitBootServices failed.\n"));\r
293 }\r
294\r
59aa48bb
SW
295 //\r
296 // If its SEV-SNP active guest then install the CONFIDENTIAL_COMPUTING_SEV_SNP_BLOB.\r
297 // It contains the location for both the Secrets and CPUID page.\r
298 //\r
67484aed
BS
299 return gBS->InstallConfigurationTable (\r
300 &gConfidentialComputingSevSnpBlobGuid,\r
301 &mSnpBootDxeTable\r
302 );\r
303 }\r
304\r
24e4ad75
BS
305 return EFI_SUCCESS;\r
306}\r