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