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