]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/IoMmuDxe/AmdSevIoMmu.c
OvmfPkg/IoMmuDxe: Reserve shared memory region for DMA operation
[mirror_edk2.git] / OvmfPkg / IoMmuDxe / AmdSevIoMmu.c
CommitLineData
f9d129e6
BS
1/** @file\r
2\r
812568fb 3 The protocol provides support to allocate, free, map and umap a DMA buffer\r
25201821
MX
4 for bus master (e.g PciHostBridge). When SEV or TDX is enabled, the DMA\r
5 operations must be performed on unencrypted buffer hence we use a bounce\r
6 buffer to map the guest buffer into an unencrypted DMA buffer.\r
f9d129e6
BS
7\r
8 Copyright (c) 2017, AMD Inc. All rights reserved.<BR>\r
9 Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
10\r
b26f0cf9 11 SPDX-License-Identifier: BSD-2-Clause-Patent\r
f9d129e6
BS
12\r
13**/\r
14\r
25201821
MX
15#include <Library/PcdLib.h>\r
16#include <ConfidentialComputingGuestAttr.h>\r
f9d129e6 17#include "AmdSevIoMmu.h"\r
c4e76d2f 18#include "IoMmuInternal.h"\r
f9d129e6 19\r
d0c9afea 20//\r
9ed745b9
LE
21// List of the MAP_INFO structures that have been set up by IoMmuMap() and not\r
22// yet torn down by IoMmuUnmap(). The list represents the full set of mappings\r
23// currently in effect.\r
d0c9afea 24//\r
ac0a286f 25STATIC LIST_ENTRY mMapInfos = INITIALIZE_LIST_HEAD_VARIABLE (mMapInfos);\r
d0c9afea 26\r
c4e76d2f
MX
27//\r
28// Indicate if the feature of reserved memory is supported in DMA operation.\r
29//\r
30BOOLEAN mReservedSharedMemSupported = FALSE;\r
58e68140 31\r
2ad6ba80
LE
32//\r
33// ASCII names for EDKII_IOMMU_OPERATION constants, for debug logging.\r
34//\r
ac0a286f 35STATIC CONST CHAR8 *CONST\r
2ad6ba80
LE
36mBusMasterOperationName[EdkiiIoMmuOperationMaximum] = {\r
37 "Read",\r
38 "Write",\r
39 "CommonBuffer",\r
40 "Read64",\r
41 "Write64",\r
42 "CommonBuffer64"\r
43};\r
44\r
f9d129e6 45/**\r
812568fb 46 Provides the controller-specific addresses required to access system memory\r
25201821 47 from a DMA bus master. On SEV/TDX guest, the DMA operations must be performed on\r
812568fb
LE
48 shared buffer hence we allocate a bounce buffer to map the HostAddress to a\r
49 DeviceAddress. The Encryption attribute is removed from the DeviceAddress\r
50 buffer.\r
f9d129e6
BS
51\r
52 @param This The protocol instance pointer.\r
53 @param Operation Indicates if the bus master is going to read or\r
54 write to system memory.\r
812568fb
LE
55 @param HostAddress The system memory address to map to the PCI\r
56 controller.\r
f9d129e6 57 @param NumberOfBytes On input the number of bytes to map. On output\r
812568fb
LE
58 the number of bytes that were mapped.\r
59 @param DeviceAddress The resulting map address for the bus master\r
60 PCI controller to use to access the hosts\r
61 HostAddress.\r
f9d129e6
BS
62 @param Mapping A resulting value to pass to Unmap().\r
63\r
812568fb
LE
64 @retval EFI_SUCCESS The range was mapped for the returned\r
65 NumberOfBytes.\r
66 @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common\r
67 buffer.\r
f9d129e6 68 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
812568fb
LE
69 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a\r
70 lack of resources.\r
71 @retval EFI_DEVICE_ERROR The system hardware could not map the requested\r
72 address.\r
f9d129e6
BS
73\r
74**/\r
75EFI_STATUS\r
76EFIAPI\r
77IoMmuMap (\r
ac0a286f
MK
78 IN EDKII_IOMMU_PROTOCOL *This,\r
79 IN EDKII_IOMMU_OPERATION Operation,\r
80 IN VOID *HostAddress,\r
81 IN OUT UINTN *NumberOfBytes,\r
82 OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,\r
83 OUT VOID **Mapping\r
f9d129e6
BS
84 )\r
85{\r
ac0a286f
MK
86 EFI_STATUS Status;\r
87 MAP_INFO *MapInfo;\r
88 EFI_ALLOCATE_TYPE AllocateType;\r
89 COMMON_BUFFER_HEADER *CommonBufferHeader;\r
90 VOID *DecryptionSource;\r
f9d129e6 91\r
2ad6ba80
LE
92 DEBUG ((\r
93 DEBUG_VERBOSE,\r
94 "%a: Operation=%a Host=0x%p Bytes=0x%Lx\n",\r
95 __FUNCTION__,\r
96 ((Operation >= 0 &&\r
97 Operation < ARRAY_SIZE (mBusMasterOperationName)) ?\r
98 mBusMasterOperationName[Operation] :\r
99 "Invalid"),\r
100 HostAddress,\r
101 (UINT64)((NumberOfBytes == NULL) ? 0 : *NumberOfBytes)\r
102 ));\r
103\r
ac0a286f
MK
104 if ((HostAddress == NULL) || (NumberOfBytes == NULL) || (DeviceAddress == NULL) ||\r
105 (Mapping == NULL))\r
106 {\r
f9d129e6
BS
107 return EFI_INVALID_PARAMETER;\r
108 }\r
109\r
c4e76d2f
MX
110 Status = EFI_SUCCESS;\r
111\r
f9d129e6
BS
112 //\r
113 // Allocate a MAP_INFO structure to remember the mapping when Unmap() is\r
114 // called later.\r
115 //\r
9ed745b9
LE
116 MapInfo = AllocatePool (sizeof (MAP_INFO));\r
117 if (MapInfo == NULL) {\r
118 Status = EFI_OUT_OF_RESOURCES;\r
119 goto Failed;\r
f9d129e6
BS
120 }\r
121\r
122 //\r
e130229c 123 // Initialize the MAP_INFO structure, except the PlainTextAddress field\r
f9d129e6 124 //\r
d0c9afea 125 ZeroMem (&MapInfo->Link, sizeof MapInfo->Link);\r
c4e76d2f
MX
126 MapInfo->Signature = MAP_INFO_SIG;\r
127 MapInfo->Operation = Operation;\r
128 MapInfo->NumberOfBytes = *NumberOfBytes;\r
129 MapInfo->NumberOfPages = EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes);\r
130 MapInfo->CryptedAddress = (UINTN)HostAddress;\r
131 MapInfo->ReservedMemBitmap = 0;\r
f9d129e6
BS
132\r
133 //\r
e130229c 134 // In the switch statement below, we point "MapInfo->PlainTextAddress" to the\r
58e68140 135 // plaintext buffer, according to Operation. We also set "DecryptionSource".\r
e130229c
LE
136 //\r
137 MapInfo->PlainTextAddress = MAX_ADDRESS;\r
ac0a286f
MK
138 AllocateType = AllocateAnyPages;\r
139 DecryptionSource = (VOID *)(UINTN)MapInfo->CryptedAddress;\r
e130229c 140 switch (Operation) {\r
e130229c 141 //\r
ac0a286f
MK
142 // For BusMasterRead[64] and BusMasterWrite[64] operations, a bounce buffer\r
143 // is necessary regardless of whether the original (crypted) buffer crosses\r
144 // the 4GB limit or not -- we have to allocate a separate plaintext buffer.\r
145 // The only variable is whether the plaintext buffer should be under 4GB.\r
e130229c 146 //\r
ac0a286f
MK
147 case EdkiiIoMmuOperationBusMasterRead:\r
148 case EdkiiIoMmuOperationBusMasterWrite:\r
149 MapInfo->PlainTextAddress = BASE_4GB - 1;\r
150 AllocateType = AllocateMaxAddress;\r
e130229c 151 //\r
ac0a286f 152 // fall through\r
e130229c 153 //\r
ac0a286f
MK
154 case EdkiiIoMmuOperationBusMasterRead64:\r
155 case EdkiiIoMmuOperationBusMasterWrite64:\r
e130229c 156 //\r
ac0a286f 157 // Allocate the implicit plaintext bounce buffer.\r
e130229c 158 //\r
c4e76d2f
MX
159 Status = IoMmuAllocateBounceBuffer (\r
160 AllocateType,\r
161 EfiBootServicesData,\r
162 MapInfo\r
163 );\r
ac0a286f
MK
164 if (EFI_ERROR (Status)) {\r
165 goto FreeMapInfo;\r
166 }\r
167\r
168 break;\r
169\r
58e68140 170 //\r
ac0a286f
MK
171 // For BusMasterCommonBuffer[64] operations, a to-be-plaintext buffer and a\r
172 // stash buffer (for in-place decryption) have been allocated already, with\r
173 // AllocateBuffer(). We only check whether the address of the to-be-plaintext\r
174 // buffer is low enough for the requested operation.\r
e130229c 175 //\r
ac0a286f
MK
176 case EdkiiIoMmuOperationBusMasterCommonBuffer:\r
177 if ((MapInfo->CryptedAddress > BASE_4GB) ||\r
178 (EFI_PAGES_TO_SIZE (MapInfo->NumberOfPages) >\r
179 BASE_4GB - MapInfo->CryptedAddress))\r
180 {\r
181 //\r
182 // CommonBuffer operations cannot be remapped. If the common buffer is\r
183 // above 4GB, then it is not possible to generate a mapping, so return an\r
184 // error.\r
185 //\r
186 Status = EFI_UNSUPPORTED;\r
187 goto FreeMapInfo;\r
188 }\r
e130229c 189\r
e130229c 190 //\r
ac0a286f 191 // fall through\r
e130229c 192 //\r
ac0a286f
MK
193 case EdkiiIoMmuOperationBusMasterCommonBuffer64:\r
194 //\r
195 // The buffer at MapInfo->CryptedAddress comes from AllocateBuffer().\r
196 //\r
197 MapInfo->PlainTextAddress = MapInfo->CryptedAddress;\r
198 //\r
199 // Stash the crypted data.\r
200 //\r
201 CommonBufferHeader = (COMMON_BUFFER_HEADER *)(\r
202 (UINTN)MapInfo->CryptedAddress - EFI_PAGE_SIZE\r
203 );\r
204 ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG);\r
205 CopyMem (\r
206 CommonBufferHeader->StashBuffer,\r
207 (VOID *)(UINTN)MapInfo->CryptedAddress,\r
208 MapInfo->NumberOfBytes\r
209 );\r
210 //\r
211 // Point "DecryptionSource" to the stash buffer so that we decrypt\r
212 // it to the original location, after the switch statement.\r
213 //\r
c4e76d2f
MX
214 DecryptionSource = CommonBufferHeader->StashBuffer;\r
215 MapInfo->ReservedMemBitmap = CommonBufferHeader->ReservedMemBitmap;\r
ac0a286f
MK
216 break;\r
217\r
218 default:\r
219 //\r
220 // Operation is invalid\r
221 //\r
222 Status = EFI_INVALID_PARAMETER;\r
223 goto FreeMapInfo;\r
f9d129e6
BS
224 }\r
225\r
25201821
MX
226 if (CC_GUEST_IS_SEV (PcdGet64 (PcdConfidentialComputingGuestAttr))) {\r
227 //\r
228 // Clear the memory encryption mask on the plaintext buffer.\r
229 //\r
230 Status = MemEncryptSevClearPageEncMask (\r
231 0,\r
232 MapInfo->PlainTextAddress,\r
233 MapInfo->NumberOfPages\r
234 );\r
235 } else if (CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr))) {\r
236 //\r
237 // Set the memory shared bit.\r
c4e76d2f
MX
238 // If MapInfo->ReservedMemBitmap is 0, it means the bounce buffer is not allocated\r
239 // from the pre-allocated shared memory, so it must be converted to shared memory here.\r
25201821 240 //\r
c4e76d2f
MX
241 if (MapInfo->ReservedMemBitmap == 0) {\r
242 Status = MemEncryptTdxSetPageSharedBit (\r
243 0,\r
244 MapInfo->PlainTextAddress,\r
245 MapInfo->NumberOfPages\r
246 );\r
247 }\r
25201821
MX
248 } else {\r
249 ASSERT (FALSE);\r
250 }\r
251\r
f1658838
LE
252 ASSERT_EFI_ERROR (Status);\r
253 if (EFI_ERROR (Status)) {\r
254 CpuDeadLoop ();\r
255 }\r
f9d129e6
BS
256\r
257 //\r
258 // If this is a read operation from the Bus Master's point of view,\r
259 // then copy the contents of the real buffer into the mapped buffer\r
260 // so the Bus Master can read the contents of the real buffer.\r
261 //\r
58e68140
LE
262 // For BusMasterCommonBuffer[64] operations, the CopyMem() below will decrypt\r
263 // the original data (from the stash buffer) back to the original location.\r
264 //\r
ac0a286f
MK
265 if ((Operation == EdkiiIoMmuOperationBusMasterRead) ||\r
266 (Operation == EdkiiIoMmuOperationBusMasterRead64) ||\r
267 (Operation == EdkiiIoMmuOperationBusMasterCommonBuffer) ||\r
268 (Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64))\r
269 {\r
f9d129e6 270 CopyMem (\r
ac0a286f 271 (VOID *)(UINTN)MapInfo->PlainTextAddress,\r
58e68140 272 DecryptionSource,\r
f9d129e6
BS
273 MapInfo->NumberOfBytes\r
274 );\r
275 }\r
276\r
9ed745b9
LE
277 //\r
278 // Track all MAP_INFO structures.\r
279 //\r
280 InsertHeadList (&mMapInfos, &MapInfo->Link);\r
f9d129e6 281 //\r
e130229c 282 // Populate output parameters.\r
f9d129e6 283 //\r
dc194ce3 284 *DeviceAddress = MapInfo->PlainTextAddress;\r
f9d129e6
BS
285 *Mapping = MapInfo;\r
286\r
812568fb
LE
287 DEBUG ((\r
288 DEBUG_VERBOSE,\r
c4e76d2f 289 "%a: Mapping=0x%p Device(PlainText)=0x%Lx Crypted=0x%Lx Pages=0x%Lx, ReservedMemBitmap=0x%Lx\n",\r
812568fb 290 __FUNCTION__,\r
2ad6ba80 291 MapInfo,\r
dc194ce3 292 MapInfo->PlainTextAddress,\r
c7ef2ed2 293 MapInfo->CryptedAddress,\r
c4e76d2f
MX
294 (UINT64)MapInfo->NumberOfPages,\r
295 MapInfo->ReservedMemBitmap\r
812568fb 296 ));\r
f9d129e6
BS
297\r
298 return EFI_SUCCESS;\r
e130229c
LE
299\r
300FreeMapInfo:\r
301 FreePool (MapInfo);\r
302\r
303Failed:\r
304 *NumberOfBytes = 0;\r
305 return Status;\r
f9d129e6
BS
306}\r
307\r
308/**\r
309 Completes the Map() operation and releases any corresponding resources.\r
310\r
550acd08
LE
311 This is an internal worker function that only extends the Map() API with\r
312 the MemoryMapLocked parameter.\r
313\r
f9d129e6
BS
314 @param This The protocol instance pointer.\r
315 @param Mapping The mapping value returned from Map().\r
550acd08
LE
316 @param MemoryMapLocked The function is executing on the stack of\r
317 gBS->ExitBootServices(); changes to the UEFI\r
318 memory map are forbidden.\r
f9d129e6
BS
319\r
320 @retval EFI_SUCCESS The range was unmapped.\r
812568fb
LE
321 @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by\r
322 Map().\r
323 @retval EFI_DEVICE_ERROR The data was not committed to the target system\r
324 memory.\r
f9d129e6 325**/\r
550acd08 326STATIC\r
f9d129e6
BS
327EFI_STATUS\r
328EFIAPI\r
550acd08 329IoMmuUnmapWorker (\r
ac0a286f
MK
330 IN EDKII_IOMMU_PROTOCOL *This,\r
331 IN VOID *Mapping,\r
332 IN BOOLEAN MemoryMapLocked\r
f9d129e6
BS
333 )\r
334{\r
ac0a286f
MK
335 MAP_INFO *MapInfo;\r
336 EFI_STATUS Status;\r
337 COMMON_BUFFER_HEADER *CommonBufferHeader;\r
338 VOID *EncryptionTarget;\r
f9d129e6 339\r
550acd08
LE
340 DEBUG ((\r
341 DEBUG_VERBOSE,\r
342 "%a: Mapping=0x%p MemoryMapLocked=%d\n",\r
343 __FUNCTION__,\r
344 Mapping,\r
345 MemoryMapLocked\r
346 ));\r
a1d6a9dc 347\r
f9d129e6
BS
348 if (Mapping == NULL) {\r
349 return EFI_INVALID_PARAMETER;\r
350 }\r
351\r
58e68140 352 MapInfo = (MAP_INFO *)Mapping;\r
25201821 353 Status = EFI_SUCCESS;\r
f9d129e6 354 //\r
58e68140 355 // set CommonBufferHeader to suppress incorrect compiler/analyzer warnings\r
f9d129e6 356 //\r
58e68140 357 CommonBufferHeader = NULL;\r
f9d129e6
BS
358\r
359 //\r
58e68140
LE
360 // For BusMasterWrite[64] operations and BusMasterCommonBuffer[64] operations\r
361 // we have to encrypt the results, ultimately to the original place (i.e.,\r
362 // "MapInfo->CryptedAddress").\r
f9d129e6 363 //\r
58e68140
LE
364 // For BusMasterCommonBuffer[64] operations however, this encryption has to\r
365 // land in-place, so divert the encryption to the stash buffer first.\r
366 //\r
367 EncryptionTarget = (VOID *)(UINTN)MapInfo->CryptedAddress;\r
368\r
369 switch (MapInfo->Operation) {\r
ac0a286f
MK
370 case EdkiiIoMmuOperationBusMasterCommonBuffer:\r
371 case EdkiiIoMmuOperationBusMasterCommonBuffer64:\r
372 ASSERT (MapInfo->PlainTextAddress == MapInfo->CryptedAddress);\r
373\r
374 CommonBufferHeader = (COMMON_BUFFER_HEADER *)(\r
375 (UINTN)MapInfo->PlainTextAddress - EFI_PAGE_SIZE\r
376 );\r
377 ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG);\r
378 EncryptionTarget = CommonBufferHeader->StashBuffer;\r
58e68140
LE
379 //\r
380 // fall through\r
381 //\r
382\r
ac0a286f
MK
383 case EdkiiIoMmuOperationBusMasterWrite:\r
384 case EdkiiIoMmuOperationBusMasterWrite64:\r
385 CopyMem (\r
386 EncryptionTarget,\r
387 (VOID *)(UINTN)MapInfo->PlainTextAddress,\r
388 MapInfo->NumberOfBytes\r
389 );\r
390 break;\r
58e68140 391\r
ac0a286f
MK
392 default:\r
393 //\r
394 // nothing to encrypt after BusMasterRead[64] operations\r
395 //\r
396 break;\r
f9d129e6
BS
397 }\r
398\r
25201821
MX
399 if (CC_GUEST_IS_SEV (PcdGet64 (PcdConfidentialComputingGuestAttr))) {\r
400 //\r
401 // Restore the memory encryption mask on the area we used to hold the\r
402 // plaintext.\r
403 //\r
404 Status = MemEncryptSevSetPageEncMask (\r
405 0,\r
406 MapInfo->PlainTextAddress,\r
407 MapInfo->NumberOfPages\r
408 );\r
409 } else if (CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr))) {\r
410 //\r
411 // Restore the memory shared bit mask on the area we used to hold the\r
412 // plaintext.\r
413 //\r
c4e76d2f
MX
414 if (MapInfo->ReservedMemBitmap == 0) {\r
415 Status = MemEncryptTdxClearPageSharedBit (\r
416 0,\r
417 MapInfo->PlainTextAddress,\r
418 MapInfo->NumberOfPages\r
419 );\r
420 }\r
25201821
MX
421 } else {\r
422 ASSERT (FALSE);\r
423 }\r
424\r
f1658838
LE
425 ASSERT_EFI_ERROR (Status);\r
426 if (EFI_ERROR (Status)) {\r
427 CpuDeadLoop ();\r
428 }\r
f9d129e6
BS
429\r
430 //\r
58e68140
LE
431 // For BusMasterCommonBuffer[64] operations, copy the stashed data to the\r
432 // original (now encrypted) location.\r
433 //\r
434 // For all other operations, fill the late bounce buffer (which existed as\r
550acd08
LE
435 // plaintext at some point) with zeros, and then release it (unless the UEFI\r
436 // memory map is locked).\r
58e68140 437 //\r
ac0a286f
MK
438 if ((MapInfo->Operation == EdkiiIoMmuOperationBusMasterCommonBuffer) ||\r
439 (MapInfo->Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64))\r
440 {\r
58e68140
LE
441 CopyMem (\r
442 (VOID *)(UINTN)MapInfo->CryptedAddress,\r
443 CommonBufferHeader->StashBuffer,\r
444 MapInfo->NumberOfBytes\r
445 );\r
446 } else {\r
447 ZeroMem (\r
448 (VOID *)(UINTN)MapInfo->PlainTextAddress,\r
449 EFI_PAGES_TO_SIZE (MapInfo->NumberOfPages)\r
450 );\r
c4e76d2f 451\r
550acd08 452 if (!MemoryMapLocked) {\r
c4e76d2f 453 IoMmuFreeBounceBuffer (MapInfo);\r
550acd08 454 }\r
58e68140
LE
455 }\r
456\r
9ed745b9 457 //\r
550acd08
LE
458 // Forget the MAP_INFO structure, then free it (unless the UEFI memory map is\r
459 // locked).\r
9ed745b9
LE
460 //\r
461 RemoveEntryList (&MapInfo->Link);\r
550acd08
LE
462 if (!MemoryMapLocked) {\r
463 FreePool (MapInfo);\r
464 }\r
9ed745b9 465\r
f9d129e6
BS
466 return EFI_SUCCESS;\r
467}\r
468\r
550acd08
LE
469/**\r
470 Completes the Map() operation and releases any corresponding resources.\r
471\r
472 @param This The protocol instance pointer.\r
473 @param Mapping The mapping value returned from Map().\r
474\r
475 @retval EFI_SUCCESS The range was unmapped.\r
476 @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by\r
477 Map().\r
478 @retval EFI_DEVICE_ERROR The data was not committed to the target system\r
479 memory.\r
480**/\r
481EFI_STATUS\r
482EFIAPI\r
483IoMmuUnmap (\r
ac0a286f
MK
484 IN EDKII_IOMMU_PROTOCOL *This,\r
485 IN VOID *Mapping\r
550acd08
LE
486 )\r
487{\r
488 return IoMmuUnmapWorker (\r
489 This,\r
490 Mapping,\r
491 FALSE // MemoryMapLocked\r
492 );\r
493}\r
494\r
f9d129e6
BS
495/**\r
496 Allocates pages that are suitable for an OperationBusMasterCommonBuffer or\r
497 OperationBusMasterCommonBuffer64 mapping.\r
498\r
499 @param This The protocol instance pointer.\r
500 @param Type This parameter is not used and must be ignored.\r
812568fb
LE
501 @param MemoryType The type of memory to allocate,\r
502 EfiBootServicesData or EfiRuntimeServicesData.\r
f9d129e6 503 @param Pages The number of pages to allocate.\r
812568fb
LE
504 @param HostAddress A pointer to store the base system memory\r
505 address of the allocated range.\r
506 @param Attributes The requested bit mask of attributes for the\r
507 allocated range.\r
f9d129e6
BS
508\r
509 @retval EFI_SUCCESS The requested memory pages were allocated.\r
812568fb
LE
510 @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal\r
511 attribute bits are MEMORY_WRITE_COMBINE and\r
512 MEMORY_CACHED.\r
f9d129e6
BS
513 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
514 @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.\r
515\r
516**/\r
517EFI_STATUS\r
518EFIAPI\r
519IoMmuAllocateBuffer (\r
ac0a286f
MK
520 IN EDKII_IOMMU_PROTOCOL *This,\r
521 IN EFI_ALLOCATE_TYPE Type,\r
522 IN EFI_MEMORY_TYPE MemoryType,\r
523 IN UINTN Pages,\r
524 IN OUT VOID **HostAddress,\r
525 IN UINT64 Attributes\r
f9d129e6
BS
526 )\r
527{\r
ac0a286f
MK
528 EFI_STATUS Status;\r
529 EFI_PHYSICAL_ADDRESS PhysicalAddress;\r
530 VOID *StashBuffer;\r
531 UINTN CommonBufferPages;\r
532 COMMON_BUFFER_HEADER *CommonBufferHeader;\r
c4e76d2f 533 UINT32 ReservedMemBitmap;\r
f9d129e6 534\r
80ddd336
LE
535 DEBUG ((\r
536 DEBUG_VERBOSE,\r
537 "%a: MemoryType=%u Pages=0x%Lx Attributes=0x%Lx\n",\r
538 __FUNCTION__,\r
539 (UINT32)MemoryType,\r
540 (UINT64)Pages,\r
541 Attributes\r
542 ));\r
543\r
f9d129e6
BS
544 //\r
545 // Validate Attributes\r
546 //\r
547 if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) {\r
548 return EFI_UNSUPPORTED;\r
549 }\r
550\r
551 //\r
552 // Check for invalid inputs\r
553 //\r
554 if (HostAddress == NULL) {\r
555 return EFI_INVALID_PARAMETER;\r
556 }\r
557\r
558 //\r
559 // The only valid memory types are EfiBootServicesData and\r
560 // EfiRuntimeServicesData\r
561 //\r
ac0a286f
MK
562 if ((MemoryType != EfiBootServicesData) &&\r
563 (MemoryType != EfiRuntimeServicesData))\r
564 {\r
f9d129e6
BS
565 return EFI_INVALID_PARAMETER;\r
566 }\r
567\r
58e68140
LE
568 //\r
569 // We'll need a header page for the COMMON_BUFFER_HEADER structure.\r
570 //\r
571 if (Pages > MAX_UINTN - 1) {\r
572 return EFI_OUT_OF_RESOURCES;\r
573 }\r
ac0a286f 574\r
58e68140
LE
575 CommonBufferPages = Pages + 1;\r
576\r
577 //\r
578 // Allocate the stash in EfiBootServicesData type memory.\r
579 //\r
580 // Map() will temporarily save encrypted data in the stash for\r
581 // BusMasterCommonBuffer[64] operations, so the data can be decrypted to the\r
582 // original location.\r
583 //\r
584 // Unmap() will temporarily save plaintext data in the stash for\r
585 // BusMasterCommonBuffer[64] operations, so the data can be encrypted to the\r
586 // original location.\r
587 //\r
588 // StashBuffer always resides in encrypted memory.\r
589 //\r
590 StashBuffer = AllocatePages (Pages);\r
591 if (StashBuffer == NULL) {\r
592 return EFI_OUT_OF_RESOURCES;\r
593 }\r
594\r
f9d129e6
BS
595 PhysicalAddress = (UINTN)-1;\r
596 if ((Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0) {\r
597 //\r
598 // Limit allocations to memory below 4GB\r
599 //\r
600 PhysicalAddress = SIZE_4GB - 1;\r
601 }\r
ac0a286f 602\r
c4e76d2f
MX
603 Status = IoMmuAllocateCommonBuffer (\r
604 MemoryType,\r
605 CommonBufferPages,\r
606 &PhysicalAddress,\r
607 &ReservedMemBitmap\r
608 );\r
609\r
58e68140
LE
610 if (EFI_ERROR (Status)) {\r
611 goto FreeStashBuffer;\r
f9d129e6
BS
612 }\r
613\r
58e68140 614 CommonBufferHeader = (VOID *)(UINTN)PhysicalAddress;\r
ac0a286f 615 PhysicalAddress += EFI_PAGE_SIZE;\r
58e68140 616\r
c4e76d2f
MX
617 CommonBufferHeader->Signature = COMMON_BUFFER_SIG;\r
618 CommonBufferHeader->StashBuffer = StashBuffer;\r
619 CommonBufferHeader->ReservedMemBitmap = ReservedMemBitmap;\r
58e68140
LE
620\r
621 *HostAddress = (VOID *)(UINTN)PhysicalAddress;\r
622\r
812568fb
LE
623 DEBUG ((\r
624 DEBUG_VERBOSE,\r
80ddd336 625 "%a: Host=0x%Lx Stash=0x%p\n",\r
812568fb
LE
626 __FUNCTION__,\r
627 PhysicalAddress,\r
80ddd336 628 StashBuffer\r
812568fb 629 ));\r
58e68140
LE
630 return EFI_SUCCESS;\r
631\r
632FreeStashBuffer:\r
633 FreePages (StashBuffer, Pages);\r
f9d129e6
BS
634 return Status;\r
635}\r
636\r
637/**\r
638 Frees memory that was allocated with AllocateBuffer().\r
639\r
640 @param This The protocol instance pointer.\r
641 @param Pages The number of pages to free.\r
812568fb
LE
642 @param HostAddress The base system memory address of the allocated\r
643 range.\r
f9d129e6
BS
644\r
645 @retval EFI_SUCCESS The requested memory pages were freed.\r
812568fb
LE
646 @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and\r
647 Pages was not allocated with AllocateBuffer().\r
f9d129e6
BS
648\r
649**/\r
650EFI_STATUS\r
651EFIAPI\r
652IoMmuFreeBuffer (\r
ac0a286f
MK
653 IN EDKII_IOMMU_PROTOCOL *This,\r
654 IN UINTN Pages,\r
655 IN VOID *HostAddress\r
f9d129e6
BS
656 )\r
657{\r
ac0a286f
MK
658 UINTN CommonBufferPages;\r
659 COMMON_BUFFER_HEADER *CommonBufferHeader;\r
58e68140 660\r
1afbb85f
LE
661 DEBUG ((\r
662 DEBUG_VERBOSE,\r
663 "%a: Host=0x%p Pages=0x%Lx\n",\r
664 __FUNCTION__,\r
665 HostAddress,\r
666 (UINT64)Pages\r
667 ));\r
668\r
ac0a286f 669 CommonBufferPages = Pages + 1;\r
58e68140 670 CommonBufferHeader = (COMMON_BUFFER_HEADER *)(\r
ac0a286f
MK
671 (UINTN)HostAddress - EFI_PAGE_SIZE\r
672 );\r
f9d129e6
BS
673\r
674 //\r
58e68140 675 // Check the signature.\r
f9d129e6 676 //\r
58e68140
LE
677 ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG);\r
678 if (CommonBufferHeader->Signature != COMMON_BUFFER_SIG) {\r
679 return EFI_INVALID_PARAMETER;\r
680 }\r
681\r
682 //\r
683 // Free the stash buffer. This buffer was always encrypted, so no need to\r
684 // zero it.\r
685 //\r
686 FreePages (CommonBufferHeader->StashBuffer, Pages);\r
f9d129e6 687\r
58e68140
LE
688 //\r
689 // Release the common buffer itself. Unmap() has re-encrypted it in-place, so\r
690 // no need to zero it.\r
691 //\r
c4e76d2f 692 return IoMmuFreeCommonBuffer (CommonBufferHeader, CommonBufferPages);\r
f9d129e6
BS
693}\r
694\r
f9d129e6
BS
695/**\r
696 Set IOMMU attribute for a system memory.\r
697\r
698 If the IOMMU protocol exists, the system memory cannot be used\r
699 for DMA by default.\r
700\r
701 When a device requests a DMA access for a system memory,\r
702 the device driver need use SetAttribute() to update the IOMMU\r
703 attribute to request DMA access (read and/or write).\r
704\r
705 The DeviceHandle is used to identify which device submits the request.\r
812568fb
LE
706 The IOMMU implementation need translate the device path to an IOMMU device\r
707 ID, and set IOMMU hardware register accordingly.\r
f9d129e6
BS
708 1) DeviceHandle can be a standard PCI device.\r
709 The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ.\r
710 The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE.\r
812568fb
LE
711 The memory for BusMasterCommonBuffer need set\r
712 EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE.\r
713 After the memory is used, the memory need set 0 to keep it being\r
714 protected.\r
f9d129e6 715 2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).\r
812568fb
LE
716 The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or\r
717 EDKII_IOMMU_ACCESS_WRITE.\r
f9d129e6
BS
718\r
719 @param[in] This The protocol instance pointer.\r
812568fb
LE
720 @param[in] DeviceHandle The device who initiates the DMA access\r
721 request.\r
f9d129e6
BS
722 @param[in] Mapping The mapping value returned from Map().\r
723 @param[in] IoMmuAccess The IOMMU access.\r
724\r
812568fb
LE
725 @retval EFI_SUCCESS The IoMmuAccess is set for the memory range\r
726 specified by DeviceAddress and Length.\r
f9d129e6 727 @retval EFI_INVALID_PARAMETER DeviceHandle is an invalid handle.\r
812568fb
LE
728 @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by\r
729 Map().\r
730 @retval EFI_INVALID_PARAMETER IoMmuAccess specified an illegal combination\r
731 of access.\r
f9d129e6 732 @retval EFI_UNSUPPORTED DeviceHandle is unknown by the IOMMU.\r
812568fb
LE
733 @retval EFI_UNSUPPORTED The bit mask of IoMmuAccess is not supported\r
734 by the IOMMU.\r
735 @retval EFI_UNSUPPORTED The IOMMU does not support the memory range\r
736 specified by Mapping.\r
737 @retval EFI_OUT_OF_RESOURCES There are not enough resources available to\r
738 modify the IOMMU access.\r
739 @retval EFI_DEVICE_ERROR The IOMMU device reported an error while\r
740 attempting the operation.\r
f9d129e6
BS
741\r
742**/\r
743EFI_STATUS\r
744EFIAPI\r
745IoMmuSetAttribute (\r
746 IN EDKII_IOMMU_PROTOCOL *This,\r
747 IN EFI_HANDLE DeviceHandle,\r
748 IN VOID *Mapping,\r
749 IN UINT64 IoMmuAccess\r
750 )\r
751{\r
752 return EFI_UNSUPPORTED;\r
753}\r
754\r
25201821 755EDKII_IOMMU_PROTOCOL mIoMmu = {\r
f9d129e6
BS
756 EDKII_IOMMU_PROTOCOL_REVISION,\r
757 IoMmuSetAttribute,\r
758 IoMmuMap,\r
759 IoMmuUnmap,\r
760 IoMmuAllocateBuffer,\r
761 IoMmuFreeBuffer,\r
762};\r
763\r
7aee391f
LE
764/**\r
765 Notification function that is queued when gBS->ExitBootServices() signals the\r
766 EFI_EVENT_GROUP_EXIT_BOOT_SERVICES event group. This function signals another\r
767 event, received as Context, and returns.\r
768\r
769 Signaling an event in this context is safe. The UEFI spec allows\r
770 gBS->SignalEvent() to return EFI_SUCCESS only; EFI_OUT_OF_RESOURCES is not\r
771 listed, hence memory is not allocated. The edk2 implementation also does not\r
772 release memory (and we only have to care about the edk2 implementation\r
773 because EDKII_IOMMU_PROTOCOL is edk2-specific anyway).\r
774\r
775 @param[in] Event Event whose notification function is being invoked.\r
776 Event is permitted to request the queueing of this\r
777 function at TPL_CALLBACK or TPL_NOTIFY task\r
778 priority level.\r
779\r
780 @param[in] EventToSignal Identifies the EFI_EVENT to signal. EventToSignal\r
781 is permitted to request the queueing of its\r
782 notification function only at TPL_CALLBACK level.\r
783**/\r
784STATIC\r
785VOID\r
786EFIAPI\r
25201821 787IoMmuExitBoot (\r
ac0a286f
MK
788 IN EFI_EVENT Event,\r
789 IN VOID *EventToSignal\r
7aee391f
LE
790 )\r
791{\r
792 //\r
793 // (1) The NotifyFunctions of all the events in\r
794 // EFI_EVENT_GROUP_EXIT_BOOT_SERVICES will have been queued before\r
25201821 795 // IoMmuExitBoot() is entered.\r
7aee391f 796 //\r
25201821 797 // (2) IoMmuExitBoot() is executing minimally at TPL_CALLBACK.\r
7aee391f 798 //\r
25201821 799 // (3) IoMmuExitBoot() has been queued in unspecified order relative to the\r
7aee391f
LE
800 // NotifyFunctions of all the other events in\r
801 // EFI_EVENT_GROUP_EXIT_BOOT_SERVICES whose NotifyTpl is the same as\r
802 // Event's.\r
803 //\r
804 // Consequences:\r
805 //\r
806 // - If Event's NotifyTpl is TPL_CALLBACK, then some other NotifyFunctions\r
25201821 807 // queued at TPL_CALLBACK may be invoked after IoMmuExitBoot() returns.\r
7aee391f
LE
808 //\r
809 // - If Event's NotifyTpl is TPL_NOTIFY, then some other NotifyFunctions\r
25201821 810 // queued at TPL_NOTIFY may be invoked after IoMmuExitBoot() returns; plus\r
7aee391f
LE
811 // *all* NotifyFunctions queued at TPL_CALLBACK will be invoked strictly\r
812 // after all NotifyFunctions queued at TPL_NOTIFY, including\r
25201821 813 // IoMmuExitBoot(), have been invoked.\r
7aee391f
LE
814 //\r
815 // - By signaling EventToSignal here, whose NotifyTpl is TPL_CALLBACK, we\r
816 // queue EventToSignal's NotifyFunction after the NotifyFunctions of *all*\r
817 // events in EFI_EVENT_GROUP_EXIT_BOOT_SERVICES.\r
818 //\r
819 DEBUG ((DEBUG_VERBOSE, "%a\n", __FUNCTION__));\r
820 gBS->SignalEvent (EventToSignal);\r
821}\r
822\r
823/**\r
824 Notification function that is queued after the notification functions of all\r
825 events in the EFI_EVENT_GROUP_EXIT_BOOT_SERVICES event group. The same memory\r
826 map restrictions apply.\r
827\r
828 This function unmaps all currently existing IOMMU mappings.\r
829\r
830 @param[in] Event Event whose notification function is being invoked. Event\r
831 is permitted to request the queueing of this function\r
832 only at TPL_CALLBACK task priority level.\r
833\r
834 @param[in] Context Ignored.\r
835**/\r
836STATIC\r
837VOID\r
838EFIAPI\r
25201821 839IoMmuUnmapAllMappings (\r
ac0a286f
MK
840 IN EFI_EVENT Event,\r
841 IN VOID *Context\r
7aee391f
LE
842 )\r
843{\r
ac0a286f
MK
844 LIST_ENTRY *Node;\r
845 LIST_ENTRY *NextNode;\r
846 MAP_INFO *MapInfo;\r
7aee391f
LE
847\r
848 DEBUG ((DEBUG_VERBOSE, "%a\n", __FUNCTION__));\r
849\r
850 //\r
851 // All drivers that had set up IOMMU mappings have halted their respective\r
852 // controllers by now; tear down the mappings.\r
853 //\r
854 for (Node = GetFirstNode (&mMapInfos); Node != &mMapInfos; Node = NextNode) {\r
855 NextNode = GetNextNode (&mMapInfos, Node);\r
ac0a286f 856 MapInfo = CR (Node, MAP_INFO, Link, MAP_INFO_SIG);\r
7aee391f 857 IoMmuUnmapWorker (\r
25201821 858 &mIoMmu, // This\r
7aee391f
LE
859 MapInfo, // Mapping\r
860 TRUE // MemoryMapLocked\r
861 );\r
862 }\r
c4e76d2f
MX
863\r
864 //\r
865 // Release the reserved shared memory as well.\r
866 //\r
867 IoMmuReleaseReservedSharedMem (TRUE);\r
7aee391f
LE
868}\r
869\r
f9d129e6
BS
870/**\r
871 Initialize Iommu Protocol.\r
872\r
873**/\r
db125079 874EFI_STATUS\r
f9d129e6 875EFIAPI\r
25201821 876InstallIoMmuProtocol (\r
f9d129e6
BS
877 VOID\r
878 )\r
879{\r
880 EFI_STATUS Status;\r
7aee391f
LE
881 EFI_EVENT UnmapAllMappingsEvent;\r
882 EFI_EVENT ExitBootEvent;\r
f9d129e6
BS
883 EFI_HANDLE Handle;\r
884\r
7aee391f
LE
885 //\r
886 // Create the "late" event whose notification function will tear down all\r
887 // left-over IOMMU mappings.\r
888 //\r
889 Status = gBS->CreateEvent (\r
890 EVT_NOTIFY_SIGNAL, // Type\r
891 TPL_CALLBACK, // NotifyTpl\r
25201821 892 IoMmuUnmapAllMappings, // NotifyFunction\r
7aee391f
LE
893 NULL, // NotifyContext\r
894 &UnmapAllMappingsEvent // Event\r
895 );\r
896 if (EFI_ERROR (Status)) {\r
897 return Status;\r
898 }\r
899\r
900 //\r
901 // Create the event whose notification function will be queued by\r
902 // gBS->ExitBootServices() and will signal the event created above.\r
903 //\r
904 Status = gBS->CreateEvent (\r
905 EVT_SIGNAL_EXIT_BOOT_SERVICES, // Type\r
906 TPL_CALLBACK, // NotifyTpl\r
25201821 907 IoMmuExitBoot, // NotifyFunction\r
7aee391f
LE
908 UnmapAllMappingsEvent, // NotifyContext\r
909 &ExitBootEvent // Event\r
910 );\r
911 if (EFI_ERROR (Status)) {\r
912 goto CloseUnmapAllMappingsEvent;\r
913 }\r
914\r
f9d129e6
BS
915 Handle = NULL;\r
916 Status = gBS->InstallMultipleProtocolInterfaces (\r
917 &Handle,\r
ac0a286f 918 &gEdkiiIoMmuProtocolGuid,\r
25201821 919 &mIoMmu,\r
f9d129e6
BS
920 NULL\r
921 );\r
7aee391f
LE
922 if (EFI_ERROR (Status)) {\r
923 goto CloseExitBootEvent;\r
924 }\r
925\r
c4e76d2f
MX
926 //\r
927 // Currently only Tdx guest support Reserved shared memory for DMA operation.\r
928 //\r
929 if (CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr))) {\r
930 mReservedSharedMemSupported = TRUE;\r
931 Status = IoMmuInitReservedSharedMem ();\r
932 if (EFI_ERROR (Status)) {\r
933 mReservedSharedMemSupported = FALSE;\r
934 } else {\r
935 DEBUG ((DEBUG_INFO, "%a: Feature of reserved memory for DMA is supported.\n", __FUNCTION__));\r
936 }\r
937 }\r
938\r
7aee391f
LE
939 return EFI_SUCCESS;\r
940\r
941CloseExitBootEvent:\r
942 gBS->CloseEvent (ExitBootEvent);\r
943\r
944CloseUnmapAllMappingsEvent:\r
945 gBS->CloseEvent (UnmapAllMappingsEvent);\r
946\r
db125079 947 return Status;\r
f9d129e6 948}\r