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