]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/AmdSevDxe/AmdSevDxe.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[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 gBS->CloseEvent (mAcceptAllMemoryEvent);
134 return Status;
135 }
136
137 VOID
138 EFIAPI
139 ResolveUnacceptedMemory (
140 IN EFI_EVENT Event,
141 IN VOID *Context
142 )
143 {
144 EFI_STATUS Status;
145
146 if (!mAcceptAllMemoryAtEBS) {
147 return;
148 }
149
150 Status = AcceptAllMemory ();
151 ASSERT_EFI_ERROR (Status);
152 }
153
154 STATIC
155 EFI_STATUS
156 EFIAPI
157 AllowUnacceptedMemory (
158 IN OVMF_SEV_MEMORY_ACCEPTANCE_PROTOCOL *This
159 )
160 {
161 mAcceptAllMemoryAtEBS = FALSE;
162 return EFI_SUCCESS;
163 }
164
165 STATIC
166 OVMF_SEV_MEMORY_ACCEPTANCE_PROTOCOL
167 mMemoryAcceptanceProtocol = { AllowUnacceptedMemory };
168
169 STATIC EDKII_MEMORY_ACCEPT_PROTOCOL mMemoryAcceptProtocol = {
170 AmdSevMemoryAccept
171 };
172
173 EFI_STATUS
174 EFIAPI
175 AmdSevDxeEntryPoint (
176 IN EFI_HANDLE ImageHandle,
177 IN EFI_SYSTEM_TABLE *SystemTable
178 )
179 {
180 EFI_STATUS Status;
181 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *AllDescMap;
182 UINTN NumEntries;
183 UINTN Index;
184
185 //
186 // Do nothing when SEV is not enabled
187 //
188 if (!MemEncryptSevIsEnabled ()) {
189 return EFI_UNSUPPORTED;
190 }
191
192 //
193 // Iterate through the GCD map and clear the C-bit from MMIO and NonExistent
194 // memory space. The NonExistent memory space will be used for mapping the
195 // MMIO space added later (eg PciRootBridge). By clearing both known MMIO and
196 // NonExistent memory space can gurantee that current and furture MMIO adds
197 // will have C-bit cleared.
198 //
199 Status = gDS->GetMemorySpaceMap (&NumEntries, &AllDescMap);
200 if (!EFI_ERROR (Status)) {
201 for (Index = 0; Index < NumEntries; Index++) {
202 CONST EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Desc;
203
204 Desc = &AllDescMap[Index];
205 if ((Desc->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) ||
206 (Desc->GcdMemoryType == EfiGcdMemoryTypeNonExistent))
207 {
208 Status = MemEncryptSevClearMmioPageEncMask (
209 0,
210 Desc->BaseAddress,
211 EFI_SIZE_TO_PAGES (Desc->Length)
212 );
213 ASSERT_EFI_ERROR (Status);
214 }
215 }
216
217 FreePool (AllDescMap);
218 }
219
220 //
221 // If PCI Express is enabled, the MMCONFIG area has been reserved, rather
222 // than marked as MMIO, and so the C-bit won't be cleared by the above walk
223 // through the GCD map. Check for the MMCONFIG area and clear the C-bit for
224 // the range.
225 //
226 if (PcdGet16 (PcdOvmfHostBridgePciDevId) == INTEL_Q35_MCH_DEVICE_ID) {
227 Status = MemEncryptSevClearMmioPageEncMask (
228 0,
229 FixedPcdGet64 (PcdPciExpressBaseAddress),
230 EFI_SIZE_TO_PAGES (SIZE_256MB)
231 );
232
233 ASSERT_EFI_ERROR (Status);
234 }
235
236 //
237 // When SMM is enabled, clear the C-bit from SMM Saved State Area
238 //
239 // NOTES: The SavedStateArea address cleared here is before SMBASE
240 // relocation. Currently, we do not clear the SavedStateArea address after
241 // SMBASE is relocated due to the following reasons:
242 //
243 // 1) Guest BIOS never access the relocated SavedStateArea.
244 //
245 // 2) The C-bit works on page-aligned address, but the SavedStateArea
246 // address is not a page-aligned. Theoretically, we could roundup the address
247 // and clear the C-bit of aligned address but looking carefully we found
248 // that some portion of the page contains code -- which will causes a bigger
249 // issues for SEV guest. When SEV is enabled, all the code must be encrypted
250 // otherwise hardware will cause trap.
251 //
252 // We restore the C-bit for this SMM Saved State Area after SMBASE relocation
253 // is completed (See OvmfPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.c).
254 //
255 if (FeaturePcdGet (PcdSmmSmramRequire)) {
256 UINTN MapPagesBase;
257 UINTN MapPagesCount;
258
259 Status = MemEncryptSevLocateInitialSmramSaveStateMapPages (
260 &MapPagesBase,
261 &MapPagesCount
262 );
263 ASSERT_EFI_ERROR (Status);
264
265 //
266 // Although these pages were set aside (i.e., allocated) by PlatformPei, we
267 // could be after a warm reboot from the OS. Don't leak any stale OS data
268 // to the hypervisor.
269 //
270 ZeroMem ((VOID *)MapPagesBase, EFI_PAGES_TO_SIZE (MapPagesCount));
271
272 Status = MemEncryptSevClearPageEncMask (
273 0, // Cr3BaseAddress -- use current CR3
274 MapPagesBase, // BaseAddress
275 MapPagesCount // NumPages
276 );
277 if (EFI_ERROR (Status)) {
278 DEBUG ((
279 DEBUG_ERROR,
280 "%a: MemEncryptSevClearPageEncMask(): %r\n",
281 __FUNCTION__,
282 Status
283 ));
284 ASSERT (FALSE);
285 CpuDeadLoop ();
286 }
287 }
288
289 if (MemEncryptSevSnpIsEnabled ()) {
290 //
291 // Memory acceptance began being required in SEV-SNP, so install the
292 // memory accept protocol implementation for a SEV-SNP active guest.
293 //
294 Status = gBS->InstallMultipleProtocolInterfaces (
295 &mAmdSevDxeHandle,
296 &gEdkiiMemoryAcceptProtocolGuid,
297 &mMemoryAcceptProtocol,
298 &gOvmfSevMemoryAcceptanceProtocolGuid,
299 &mMemoryAcceptanceProtocol,
300 NULL
301 );
302 ASSERT_EFI_ERROR (Status);
303
304 // SEV-SNP support does not automatically imply unaccepted memory support,
305 // so make ExitBootServices accept all unaccepted memory if support is
306 // not communicated.
307 Status = gBS->CreateEventEx (
308 EVT_NOTIFY_SIGNAL,
309 TPL_CALLBACK,
310 ResolveUnacceptedMemory,
311 NULL,
312 &gEfiEventBeforeExitBootServicesGuid,
313 &mAcceptAllMemoryEvent
314 );
315
316 if (EFI_ERROR (Status)) {
317 DEBUG ((DEBUG_ERROR, "AllowUnacceptedMemory event creation for EventBeforeExitBootServices failed.\n"));
318 }
319
320 //
321 // If its SEV-SNP active guest then install the CONFIDENTIAL_COMPUTING_SEV_SNP_BLOB.
322 // It contains the location for both the Secrets and CPUID page.
323 //
324 return gBS->InstallConfigurationTable (
325 &gConfidentialComputingSevSnpBlobGuid,
326 &mSnpBootDxeTable
327 );
328 }
329
330 return EFI_SUCCESS;
331 }