VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/Firmware/OvmfPkg/AmdSevDxe/AmdSevDxe.c@ 106129

Last change on this file since 106129 was 105670, checked in by vboxsync, 3 months ago

Devices/EFI/FirmwareNew: Merge edk2-stable-202405 and make it build on aarch64, bugref:4643

  • Property svn:eol-style set to native
File size: 10.5 KB
Line 
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
31STATIC
32EFI_STATUS
33AllocateConfidentialComputingBlob (
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
64STATIC EFI_HANDLE mAmdSevDxeHandle = NULL;
65
66STATIC BOOLEAN mAcceptAllMemoryAtEBS = TRUE;
67
68STATIC EFI_EVENT mAcceptAllMemoryEvent = NULL;
69
70STATIC
71EFI_STATUS
72EFIAPI
73AmdSevMemoryAccept (
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
96STATIC
97EFI_STATUS
98AcceptAllMemory (
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
158VOID
159EFIAPI
160ResolveUnacceptedMemory (
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
175STATIC
176EFI_STATUS
177EFIAPI
178AllowUnacceptedMemory (
179 IN OVMF_SEV_MEMORY_ACCEPTANCE_PROTOCOL *This
180 )
181{
182 mAcceptAllMemoryAtEBS = FALSE;
183 return EFI_SUCCESS;
184}
185
186STATIC
187OVMF_SEV_MEMORY_ACCEPTANCE_PROTOCOL
188 mMemoryAcceptanceProtocol = { AllowUnacceptedMemory };
189
190STATIC EDKII_MEMORY_ACCEPT_PROTOCOL mMemoryAcceptProtocol = {
191 AmdSevMemoryAccept
192};
193
194EFI_STATUS
195EFIAPI
196AmdSevDxeEntryPoint (
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}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette