]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/IoMmuDxe/AmdSevIoMmu.c
OvmfPkg/IoMmuDxe: IoMmuAllocateBuffer(): nicer and more informative DEBUGs
[mirror_edk2.git] / OvmfPkg / IoMmuDxe / AmdSevIoMmu.c
CommitLineData
f9d129e6
BS
1/** @file\r
2\r
812568fb
LE
3 The protocol provides support to allocate, free, map and umap a DMA buffer\r
4 for bus master (e.g PciHostBridge). When SEV is enabled, the DMA operations\r
5 must be performed on unencrypted buffer hence we use a bounce buffer to map\r
6 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
11 This program and the accompanying materials are licensed and made available\r
12 under the terms and conditions of the BSD License which accompanies this\r
13 distribution. The full text of the license may be found at\r
14 http://opensource.org/licenses/bsd-license.php\r
15\r
16 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
17 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
18\r
19**/\r
20\r
21#include "AmdSevIoMmu.h"\r
22\r
d0c9afea
LE
23#define MAP_INFO_SIG SIGNATURE_64 ('M', 'A', 'P', '_', 'I', 'N', 'F', 'O')\r
24\r
f9d129e6 25typedef struct {\r
d0c9afea
LE
26 UINT64 Signature;\r
27 LIST_ENTRY Link;\r
f9d129e6
BS
28 EDKII_IOMMU_OPERATION Operation;\r
29 UINTN NumberOfBytes;\r
30 UINTN NumberOfPages;\r
c7ef2ed2 31 EFI_PHYSICAL_ADDRESS CryptedAddress;\r
dc194ce3 32 EFI_PHYSICAL_ADDRESS PlainTextAddress;\r
f9d129e6
BS
33} MAP_INFO;\r
34\r
d0c9afea
LE
35//\r
36// List of MAP_INFO structures recycled by Unmap().\r
37//\r
38// Recycled MAP_INFO structures are equally good for future recycling and\r
39// freeing.\r
40//\r
41STATIC LIST_ENTRY mRecycledMapInfos = INITIALIZE_LIST_HEAD_VARIABLE (\r
42 mRecycledMapInfos\r
43 );\r
44\r
58e68140
LE
45#define COMMON_BUFFER_SIG SIGNATURE_64 ('C', 'M', 'N', 'B', 'U', 'F', 'F', 'R')\r
46\r
2ad6ba80
LE
47//\r
48// ASCII names for EDKII_IOMMU_OPERATION constants, for debug logging.\r
49//\r
50STATIC CONST CHAR8 * CONST\r
51mBusMasterOperationName[EdkiiIoMmuOperationMaximum] = {\r
52 "Read",\r
53 "Write",\r
54 "CommonBuffer",\r
55 "Read64",\r
56 "Write64",\r
57 "CommonBuffer64"\r
58};\r
59\r
58e68140
LE
60//\r
61// The following structure enables Map() and Unmap() to perform in-place\r
62// decryption and encryption, respectively, for BusMasterCommonBuffer[64]\r
63// operations, without dynamic memory allocation or release.\r
64//\r
65// Both COMMON_BUFFER_HEADER and COMMON_BUFFER_HEADER.StashBuffer are allocated\r
66// by AllocateBuffer() and released by FreeBuffer().\r
67//\r
68#pragma pack (1)\r
69typedef struct {\r
70 UINT64 Signature;\r
71\r
72 //\r
73 // Always allocated from EfiBootServicesData type memory, and always\r
74 // encrypted.\r
75 //\r
76 VOID *StashBuffer;\r
77\r
78 //\r
79 // Followed by the actual common buffer, starting at the next page.\r
80 //\r
81} COMMON_BUFFER_HEADER;\r
82#pragma pack ()\r
f9d129e6
BS
83\r
84/**\r
812568fb
LE
85 Provides the controller-specific addresses required to access system memory\r
86 from a DMA bus master. On SEV guest, the DMA operations must be performed on\r
87 shared buffer hence we allocate a bounce buffer to map the HostAddress to a\r
88 DeviceAddress. The Encryption attribute is removed from the DeviceAddress\r
89 buffer.\r
f9d129e6
BS
90\r
91 @param This The protocol instance pointer.\r
92 @param Operation Indicates if the bus master is going to read or\r
93 write to system memory.\r
812568fb
LE
94 @param HostAddress The system memory address to map to the PCI\r
95 controller.\r
f9d129e6 96 @param NumberOfBytes On input the number of bytes to map. On output\r
812568fb
LE
97 the number of bytes that were mapped.\r
98 @param DeviceAddress The resulting map address for the bus master\r
99 PCI controller to use to access the hosts\r
100 HostAddress.\r
f9d129e6
BS
101 @param Mapping A resulting value to pass to Unmap().\r
102\r
812568fb
LE
103 @retval EFI_SUCCESS The range was mapped for the returned\r
104 NumberOfBytes.\r
105 @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common\r
106 buffer.\r
f9d129e6 107 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
812568fb
LE
108 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a\r
109 lack of resources.\r
110 @retval EFI_DEVICE_ERROR The system hardware could not map the requested\r
111 address.\r
f9d129e6
BS
112\r
113**/\r
114EFI_STATUS\r
115EFIAPI\r
116IoMmuMap (\r
117 IN EDKII_IOMMU_PROTOCOL *This,\r
118 IN EDKII_IOMMU_OPERATION Operation,\r
119 IN VOID *HostAddress,\r
120 IN OUT UINTN *NumberOfBytes,\r
121 OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,\r
122 OUT VOID **Mapping\r
123 )\r
124{\r
125 EFI_STATUS Status;\r
d0c9afea 126 LIST_ENTRY *RecycledMapInfo;\r
f9d129e6 127 MAP_INFO *MapInfo;\r
f9d129e6 128 EFI_ALLOCATE_TYPE AllocateType;\r
58e68140
LE
129 COMMON_BUFFER_HEADER *CommonBufferHeader;\r
130 VOID *DecryptionSource;\r
f9d129e6 131\r
2ad6ba80
LE
132 DEBUG ((\r
133 DEBUG_VERBOSE,\r
134 "%a: Operation=%a Host=0x%p Bytes=0x%Lx\n",\r
135 __FUNCTION__,\r
136 ((Operation >= 0 &&\r
137 Operation < ARRAY_SIZE (mBusMasterOperationName)) ?\r
138 mBusMasterOperationName[Operation] :\r
139 "Invalid"),\r
140 HostAddress,\r
141 (UINT64)((NumberOfBytes == NULL) ? 0 : *NumberOfBytes)\r
142 ));\r
143\r
f9d129e6
BS
144 if (HostAddress == NULL || NumberOfBytes == NULL || DeviceAddress == NULL ||\r
145 Mapping == NULL) {\r
146 return EFI_INVALID_PARAMETER;\r
147 }\r
148\r
f9d129e6
BS
149 //\r
150 // Allocate a MAP_INFO structure to remember the mapping when Unmap() is\r
151 // called later.\r
152 //\r
d0c9afea
LE
153 RecycledMapInfo = GetFirstNode (&mRecycledMapInfos);\r
154 if (RecycledMapInfo == &mRecycledMapInfos) {\r
155 //\r
156 // No recycled MAP_INFO structure, allocate a new one.\r
157 //\r
158 MapInfo = AllocatePool (sizeof (MAP_INFO));\r
159 if (MapInfo == NULL) {\r
160 Status = EFI_OUT_OF_RESOURCES;\r
161 goto Failed;\r
162 }\r
163 } else {\r
164 MapInfo = CR (RecycledMapInfo, MAP_INFO, Link, MAP_INFO_SIG);\r
165 RemoveEntryList (RecycledMapInfo);\r
f9d129e6
BS
166 }\r
167\r
168 //\r
e130229c 169 // Initialize the MAP_INFO structure, except the PlainTextAddress field\r
f9d129e6 170 //\r
d0c9afea
LE
171 ZeroMem (&MapInfo->Link, sizeof MapInfo->Link);\r
172 MapInfo->Signature = MAP_INFO_SIG;\r
f9d129e6
BS
173 MapInfo->Operation = Operation;\r
174 MapInfo->NumberOfBytes = *NumberOfBytes;\r
175 MapInfo->NumberOfPages = EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes);\r
e130229c 176 MapInfo->CryptedAddress = (UINTN)HostAddress;\r
f9d129e6
BS
177\r
178 //\r
e130229c 179 // In the switch statement below, we point "MapInfo->PlainTextAddress" to the\r
58e68140 180 // plaintext buffer, according to Operation. We also set "DecryptionSource".\r
e130229c
LE
181 //\r
182 MapInfo->PlainTextAddress = MAX_ADDRESS;\r
183 AllocateType = AllocateAnyPages;\r
58e68140 184 DecryptionSource = (VOID *)(UINTN)MapInfo->CryptedAddress;\r
e130229c
LE
185 switch (Operation) {\r
186 //\r
187 // For BusMasterRead[64] and BusMasterWrite[64] operations, a bounce buffer\r
188 // is necessary regardless of whether the original (crypted) buffer crosses\r
189 // the 4GB limit or not -- we have to allocate a separate plaintext buffer.\r
190 // The only variable is whether the plaintext buffer should be under 4GB.\r
f9d129e6 191 //\r
e130229c
LE
192 case EdkiiIoMmuOperationBusMasterRead:\r
193 case EdkiiIoMmuOperationBusMasterWrite:\r
194 MapInfo->PlainTextAddress = BASE_4GB - 1;\r
195 AllocateType = AllocateMaxAddress;\r
196 //\r
197 // fall through\r
198 //\r
199 case EdkiiIoMmuOperationBusMasterRead64:\r
200 case EdkiiIoMmuOperationBusMasterWrite64:\r
201 //\r
202 // Allocate the implicit plaintext bounce buffer.\r
203 //\r
204 Status = gBS->AllocatePages (\r
205 AllocateType,\r
206 EfiBootServicesData,\r
207 MapInfo->NumberOfPages,\r
208 &MapInfo->PlainTextAddress\r
209 );\r
210 if (EFI_ERROR (Status)) {\r
211 goto FreeMapInfo;\r
212 }\r
213 break;\r
214\r
215 //\r
58e68140
LE
216 // For BusMasterCommonBuffer[64] operations, a to-be-plaintext buffer and a\r
217 // stash buffer (for in-place decryption) have been allocated already, with\r
218 // AllocateBuffer(). We only check whether the address of the to-be-plaintext\r
219 // buffer is low enough for the requested operation.\r
e130229c
LE
220 //\r
221 case EdkiiIoMmuOperationBusMasterCommonBuffer:\r
222 if ((MapInfo->CryptedAddress > BASE_4GB) ||\r
223 (EFI_PAGES_TO_SIZE (MapInfo->NumberOfPages) >\r
224 BASE_4GB - MapInfo->CryptedAddress)) {\r
225 //\r
226 // CommonBuffer operations cannot be remapped. If the common buffer is\r
227 // above 4GB, then it is not possible to generate a mapping, so return an\r
228 // error.\r
229 //\r
230 Status = EFI_UNSUPPORTED;\r
231 goto FreeMapInfo;\r
232 }\r
233 //\r
234 // fall through\r
235 //\r
236 case EdkiiIoMmuOperationBusMasterCommonBuffer64:\r
237 //\r
58e68140 238 // The buffer at MapInfo->CryptedAddress comes from AllocateBuffer().\r
e130229c
LE
239 //\r
240 MapInfo->PlainTextAddress = MapInfo->CryptedAddress;\r
e130229c 241 //\r
58e68140
LE
242 // Stash the crypted data.\r
243 //\r
244 CommonBufferHeader = (COMMON_BUFFER_HEADER *)(\r
245 (UINTN)MapInfo->CryptedAddress - EFI_PAGE_SIZE\r
246 );\r
247 ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG);\r
248 CopyMem (\r
249 CommonBufferHeader->StashBuffer,\r
250 (VOID *)(UINTN)MapInfo->CryptedAddress,\r
251 MapInfo->NumberOfBytes\r
252 );\r
253 //\r
254 // Point "DecryptionSource" to the stash buffer so that we decrypt\r
255 // it to the original location, after the switch statement.\r
e130229c 256 //\r
58e68140
LE
257 DecryptionSource = CommonBufferHeader->StashBuffer;\r
258 break;\r
e130229c
LE
259\r
260 default:\r
261 //\r
262 // Operation is invalid\r
263 //\r
264 Status = EFI_INVALID_PARAMETER;\r
265 goto FreeMapInfo;\r
f9d129e6
BS
266 }\r
267\r
268 //\r
e130229c 269 // Clear the memory encryption mask on the plaintext buffer.\r
f9d129e6 270 //\r
812568fb
LE
271 Status = MemEncryptSevClearPageEncMask (\r
272 0,\r
dc194ce3 273 MapInfo->PlainTextAddress,\r
812568fb
LE
274 MapInfo->NumberOfPages,\r
275 TRUE\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
f9d129e6 290 if (Operation == EdkiiIoMmuOperationBusMasterRead ||\r
58e68140
LE
291 Operation == EdkiiIoMmuOperationBusMasterRead64 ||\r
292 Operation == EdkiiIoMmuOperationBusMasterCommonBuffer ||\r
293 Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) {\r
f9d129e6 294 CopyMem (\r
dc194ce3 295 (VOID *) (UINTN) MapInfo->PlainTextAddress,\r
58e68140 296 DecryptionSource,\r
f9d129e6
BS
297 MapInfo->NumberOfBytes\r
298 );\r
299 }\r
300\r
301 //\r
e130229c 302 // Populate output parameters.\r
f9d129e6 303 //\r
dc194ce3 304 *DeviceAddress = MapInfo->PlainTextAddress;\r
f9d129e6
BS
305 *Mapping = MapInfo;\r
306\r
812568fb
LE
307 DEBUG ((\r
308 DEBUG_VERBOSE,\r
2ad6ba80 309 "%a: Mapping=0x%p Device(PlainText)=0x%Lx Crypted=0x%Lx Pages=0x%Lx\n",\r
812568fb 310 __FUNCTION__,\r
2ad6ba80 311 MapInfo,\r
dc194ce3 312 MapInfo->PlainTextAddress,\r
c7ef2ed2 313 MapInfo->CryptedAddress,\r
2ad6ba80 314 (UINT64)MapInfo->NumberOfPages\r
812568fb 315 ));\r
f9d129e6
BS
316\r
317 return EFI_SUCCESS;\r
e130229c
LE
318\r
319FreeMapInfo:\r
320 FreePool (MapInfo);\r
321\r
322Failed:\r
323 *NumberOfBytes = 0;\r
324 return Status;\r
f9d129e6
BS
325}\r
326\r
327/**\r
328 Completes the Map() operation and releases any corresponding resources.\r
329\r
330 @param This The protocol instance pointer.\r
331 @param Mapping The mapping value returned from Map().\r
332\r
333 @retval EFI_SUCCESS The range was unmapped.\r
812568fb
LE
334 @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by\r
335 Map().\r
336 @retval EFI_DEVICE_ERROR The data was not committed to the target system\r
337 memory.\r
f9d129e6
BS
338**/\r
339EFI_STATUS\r
340EFIAPI\r
341IoMmuUnmap (\r
342 IN EDKII_IOMMU_PROTOCOL *This,\r
343 IN VOID *Mapping\r
344 )\r
345{\r
346 MAP_INFO *MapInfo;\r
347 EFI_STATUS Status;\r
58e68140
LE
348 COMMON_BUFFER_HEADER *CommonBufferHeader;\r
349 VOID *EncryptionTarget;\r
f9d129e6 350\r
a1d6a9dc
LE
351 DEBUG ((DEBUG_VERBOSE, "%a: Mapping=0x%p\n", __FUNCTION__, Mapping));\r
352\r
f9d129e6
BS
353 if (Mapping == NULL) {\r
354 return EFI_INVALID_PARAMETER;\r
355 }\r
356\r
58e68140
LE
357 MapInfo = (MAP_INFO *)Mapping;\r
358\r
f9d129e6 359 //\r
58e68140 360 // set CommonBufferHeader to suppress incorrect compiler/analyzer warnings\r
f9d129e6 361 //\r
58e68140 362 CommonBufferHeader = NULL;\r
f9d129e6
BS
363\r
364 //\r
58e68140
LE
365 // For BusMasterWrite[64] operations and BusMasterCommonBuffer[64] operations\r
366 // we have to encrypt the results, ultimately to the original place (i.e.,\r
367 // "MapInfo->CryptedAddress").\r
f9d129e6 368 //\r
58e68140
LE
369 // For BusMasterCommonBuffer[64] operations however, this encryption has to\r
370 // land in-place, so divert the encryption to the stash buffer first.\r
371 //\r
372 EncryptionTarget = (VOID *)(UINTN)MapInfo->CryptedAddress;\r
373\r
374 switch (MapInfo->Operation) {\r
375 case EdkiiIoMmuOperationBusMasterCommonBuffer:\r
376 case EdkiiIoMmuOperationBusMasterCommonBuffer64:\r
377 ASSERT (MapInfo->PlainTextAddress == MapInfo->CryptedAddress);\r
378\r
379 CommonBufferHeader = (COMMON_BUFFER_HEADER *)(\r
380 (UINTN)MapInfo->PlainTextAddress - EFI_PAGE_SIZE\r
381 );\r
382 ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG);\r
383 EncryptionTarget = CommonBufferHeader->StashBuffer;\r
384 //\r
385 // fall through\r
386 //\r
387\r
388 case EdkiiIoMmuOperationBusMasterWrite:\r
389 case EdkiiIoMmuOperationBusMasterWrite64:\r
f9d129e6 390 CopyMem (\r
58e68140 391 EncryptionTarget,\r
dc194ce3 392 (VOID *) (UINTN) MapInfo->PlainTextAddress,\r
f9d129e6
BS
393 MapInfo->NumberOfBytes\r
394 );\r
58e68140
LE
395 break;\r
396\r
397 default:\r
398 //\r
399 // nothing to encrypt after BusMasterRead[64] operations\r
400 //\r
401 break;\r
f9d129e6
BS
402 }\r
403\r
f9d129e6 404 //\r
58e68140
LE
405 // Restore the memory encryption mask on the area we used to hold the\r
406 // plaintext.\r
f9d129e6 407 //\r
812568fb
LE
408 Status = MemEncryptSevSetPageEncMask (\r
409 0,\r
dc194ce3 410 MapInfo->PlainTextAddress,\r
812568fb
LE
411 MapInfo->NumberOfPages,\r
412 TRUE\r
413 );\r
f1658838
LE
414 ASSERT_EFI_ERROR (Status);\r
415 if (EFI_ERROR (Status)) {\r
416 CpuDeadLoop ();\r
417 }\r
f9d129e6
BS
418\r
419 //\r
58e68140
LE
420 // For BusMasterCommonBuffer[64] operations, copy the stashed data to the\r
421 // original (now encrypted) location.\r
422 //\r
423 // For all other operations, fill the late bounce buffer (which existed as\r
424 // plaintext at some point) with zeros, and then release it.\r
425 //\r
426 if (MapInfo->Operation == EdkiiIoMmuOperationBusMasterCommonBuffer ||\r
427 MapInfo->Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) {\r
428 CopyMem (\r
429 (VOID *)(UINTN)MapInfo->CryptedAddress,\r
430 CommonBufferHeader->StashBuffer,\r
431 MapInfo->NumberOfBytes\r
432 );\r
d0c9afea
LE
433\r
434 //\r
435 // Recycle the MAP_INFO structure.\r
436 //\r
437 InsertTailList (&mRecycledMapInfos, &MapInfo->Link);\r
58e68140
LE
438 } else {\r
439 ZeroMem (\r
440 (VOID *)(UINTN)MapInfo->PlainTextAddress,\r
441 EFI_PAGES_TO_SIZE (MapInfo->NumberOfPages)\r
442 );\r
443 gBS->FreePages (MapInfo->PlainTextAddress, MapInfo->NumberOfPages);\r
d0c9afea
LE
444\r
445 //\r
446 // Free the MAP_INFO structure.\r
447 //\r
448 FreePool (MapInfo);\r
58e68140
LE
449 }\r
450\r
f9d129e6
BS
451 return EFI_SUCCESS;\r
452}\r
453\r
454/**\r
455 Allocates pages that are suitable for an OperationBusMasterCommonBuffer or\r
456 OperationBusMasterCommonBuffer64 mapping.\r
457\r
458 @param This The protocol instance pointer.\r
459 @param Type This parameter is not used and must be ignored.\r
812568fb
LE
460 @param MemoryType The type of memory to allocate,\r
461 EfiBootServicesData or EfiRuntimeServicesData.\r
f9d129e6 462 @param Pages The number of pages to allocate.\r
812568fb
LE
463 @param HostAddress A pointer to store the base system memory\r
464 address of the allocated range.\r
465 @param Attributes The requested bit mask of attributes for the\r
466 allocated range.\r
f9d129e6
BS
467\r
468 @retval EFI_SUCCESS The requested memory pages were allocated.\r
812568fb
LE
469 @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal\r
470 attribute bits are MEMORY_WRITE_COMBINE and\r
471 MEMORY_CACHED.\r
f9d129e6
BS
472 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
473 @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.\r
474\r
475**/\r
476EFI_STATUS\r
477EFIAPI\r
478IoMmuAllocateBuffer (\r
479 IN EDKII_IOMMU_PROTOCOL *This,\r
480 IN EFI_ALLOCATE_TYPE Type,\r
481 IN EFI_MEMORY_TYPE MemoryType,\r
482 IN UINTN Pages,\r
483 IN OUT VOID **HostAddress,\r
484 IN UINT64 Attributes\r
485 )\r
486{\r
487 EFI_STATUS Status;\r
488 EFI_PHYSICAL_ADDRESS PhysicalAddress;\r
58e68140
LE
489 VOID *StashBuffer;\r
490 UINTN CommonBufferPages;\r
491 COMMON_BUFFER_HEADER *CommonBufferHeader;\r
f9d129e6 492\r
80ddd336
LE
493 DEBUG ((\r
494 DEBUG_VERBOSE,\r
495 "%a: MemoryType=%u Pages=0x%Lx Attributes=0x%Lx\n",\r
496 __FUNCTION__,\r
497 (UINT32)MemoryType,\r
498 (UINT64)Pages,\r
499 Attributes\r
500 ));\r
501\r
f9d129e6
BS
502 //\r
503 // Validate Attributes\r
504 //\r
505 if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) {\r
506 return EFI_UNSUPPORTED;\r
507 }\r
508\r
509 //\r
510 // Check for invalid inputs\r
511 //\r
512 if (HostAddress == NULL) {\r
513 return EFI_INVALID_PARAMETER;\r
514 }\r
515\r
516 //\r
517 // The only valid memory types are EfiBootServicesData and\r
518 // EfiRuntimeServicesData\r
519 //\r
520 if (MemoryType != EfiBootServicesData &&\r
521 MemoryType != EfiRuntimeServicesData) {\r
522 return EFI_INVALID_PARAMETER;\r
523 }\r
524\r
58e68140
LE
525 //\r
526 // We'll need a header page for the COMMON_BUFFER_HEADER structure.\r
527 //\r
528 if (Pages > MAX_UINTN - 1) {\r
529 return EFI_OUT_OF_RESOURCES;\r
530 }\r
531 CommonBufferPages = Pages + 1;\r
532\r
533 //\r
534 // Allocate the stash in EfiBootServicesData type memory.\r
535 //\r
536 // Map() will temporarily save encrypted data in the stash for\r
537 // BusMasterCommonBuffer[64] operations, so the data can be decrypted to the\r
538 // original location.\r
539 //\r
540 // Unmap() will temporarily save plaintext data in the stash for\r
541 // BusMasterCommonBuffer[64] operations, so the data can be encrypted to the\r
542 // original location.\r
543 //\r
544 // StashBuffer always resides in encrypted memory.\r
545 //\r
546 StashBuffer = AllocatePages (Pages);\r
547 if (StashBuffer == NULL) {\r
548 return EFI_OUT_OF_RESOURCES;\r
549 }\r
550\r
f9d129e6
BS
551 PhysicalAddress = (UINTN)-1;\r
552 if ((Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0) {\r
553 //\r
554 // Limit allocations to memory below 4GB\r
555 //\r
556 PhysicalAddress = SIZE_4GB - 1;\r
557 }\r
558 Status = gBS->AllocatePages (\r
559 AllocateMaxAddress,\r
560 MemoryType,\r
58e68140 561 CommonBufferPages,\r
f9d129e6
BS
562 &PhysicalAddress\r
563 );\r
58e68140
LE
564 if (EFI_ERROR (Status)) {\r
565 goto FreeStashBuffer;\r
f9d129e6
BS
566 }\r
567\r
58e68140
LE
568 CommonBufferHeader = (VOID *)(UINTN)PhysicalAddress;\r
569 PhysicalAddress += EFI_PAGE_SIZE;\r
570\r
571 CommonBufferHeader->Signature = COMMON_BUFFER_SIG;\r
572 CommonBufferHeader->StashBuffer = StashBuffer;\r
573\r
574 *HostAddress = (VOID *)(UINTN)PhysicalAddress;\r
575\r
812568fb
LE
576 DEBUG ((\r
577 DEBUG_VERBOSE,\r
80ddd336 578 "%a: Host=0x%Lx Stash=0x%p\n",\r
812568fb
LE
579 __FUNCTION__,\r
580 PhysicalAddress,\r
80ddd336 581 StashBuffer\r
812568fb 582 ));\r
58e68140
LE
583 return EFI_SUCCESS;\r
584\r
585FreeStashBuffer:\r
586 FreePages (StashBuffer, Pages);\r
f9d129e6
BS
587 return Status;\r
588}\r
589\r
590/**\r
591 Frees memory that was allocated with AllocateBuffer().\r
592\r
593 @param This The protocol instance pointer.\r
594 @param Pages The number of pages to free.\r
812568fb
LE
595 @param HostAddress The base system memory address of the allocated\r
596 range.\r
f9d129e6
BS
597\r
598 @retval EFI_SUCCESS The requested memory pages were freed.\r
812568fb
LE
599 @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and\r
600 Pages was not allocated with AllocateBuffer().\r
f9d129e6
BS
601\r
602**/\r
603EFI_STATUS\r
604EFIAPI\r
605IoMmuFreeBuffer (\r
606 IN EDKII_IOMMU_PROTOCOL *This,\r
607 IN UINTN Pages,\r
608 IN VOID *HostAddress\r
609 )\r
610{\r
58e68140
LE
611 UINTN CommonBufferPages;\r
612 COMMON_BUFFER_HEADER *CommonBufferHeader;\r
613\r
614 CommonBufferPages = Pages + 1;\r
615 CommonBufferHeader = (COMMON_BUFFER_HEADER *)(\r
616 (UINTN)HostAddress - EFI_PAGE_SIZE\r
617 );\r
f9d129e6
BS
618\r
619 //\r
58e68140 620 // Check the signature.\r
f9d129e6 621 //\r
58e68140
LE
622 ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG);\r
623 if (CommonBufferHeader->Signature != COMMON_BUFFER_SIG) {\r
624 return EFI_INVALID_PARAMETER;\r
625 }\r
626\r
627 //\r
628 // Free the stash buffer. This buffer was always encrypted, so no need to\r
629 // zero it.\r
630 //\r
631 FreePages (CommonBufferHeader->StashBuffer, Pages);\r
f9d129e6 632\r
812568fb
LE
633 DEBUG ((\r
634 DEBUG_VERBOSE,\r
635 "%a Address 0x%Lx Pages 0x%Lx\n",\r
636 __FUNCTION__,\r
60aa3a0e
LE
637 (UINT64)(UINTN)HostAddress,\r
638 (UINT64)Pages\r
812568fb 639 ));\r
58e68140
LE
640\r
641 //\r
642 // Release the common buffer itself. Unmap() has re-encrypted it in-place, so\r
643 // no need to zero it.\r
644 //\r
645 return gBS->FreePages ((UINTN)CommonBufferHeader, CommonBufferPages);\r
f9d129e6
BS
646}\r
647\r
648\r
649/**\r
650 Set IOMMU attribute for a system memory.\r
651\r
652 If the IOMMU protocol exists, the system memory cannot be used\r
653 for DMA by default.\r
654\r
655 When a device requests a DMA access for a system memory,\r
656 the device driver need use SetAttribute() to update the IOMMU\r
657 attribute to request DMA access (read and/or write).\r
658\r
659 The DeviceHandle is used to identify which device submits the request.\r
812568fb
LE
660 The IOMMU implementation need translate the device path to an IOMMU device\r
661 ID, and set IOMMU hardware register accordingly.\r
f9d129e6
BS
662 1) DeviceHandle can be a standard PCI device.\r
663 The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ.\r
664 The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE.\r
812568fb
LE
665 The memory for BusMasterCommonBuffer need set\r
666 EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE.\r
667 After the memory is used, the memory need set 0 to keep it being\r
668 protected.\r
f9d129e6 669 2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).\r
812568fb
LE
670 The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or\r
671 EDKII_IOMMU_ACCESS_WRITE.\r
f9d129e6
BS
672\r
673 @param[in] This The protocol instance pointer.\r
812568fb
LE
674 @param[in] DeviceHandle The device who initiates the DMA access\r
675 request.\r
f9d129e6
BS
676 @param[in] Mapping The mapping value returned from Map().\r
677 @param[in] IoMmuAccess The IOMMU access.\r
678\r
812568fb
LE
679 @retval EFI_SUCCESS The IoMmuAccess is set for the memory range\r
680 specified by DeviceAddress and Length.\r
f9d129e6 681 @retval EFI_INVALID_PARAMETER DeviceHandle is an invalid handle.\r
812568fb
LE
682 @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by\r
683 Map().\r
684 @retval EFI_INVALID_PARAMETER IoMmuAccess specified an illegal combination\r
685 of access.\r
f9d129e6 686 @retval EFI_UNSUPPORTED DeviceHandle is unknown by the IOMMU.\r
812568fb
LE
687 @retval EFI_UNSUPPORTED The bit mask of IoMmuAccess is not supported\r
688 by the IOMMU.\r
689 @retval EFI_UNSUPPORTED The IOMMU does not support the memory range\r
690 specified by Mapping.\r
691 @retval EFI_OUT_OF_RESOURCES There are not enough resources available to\r
692 modify the IOMMU access.\r
693 @retval EFI_DEVICE_ERROR The IOMMU device reported an error while\r
694 attempting the operation.\r
f9d129e6
BS
695\r
696**/\r
697EFI_STATUS\r
698EFIAPI\r
699IoMmuSetAttribute (\r
700 IN EDKII_IOMMU_PROTOCOL *This,\r
701 IN EFI_HANDLE DeviceHandle,\r
702 IN VOID *Mapping,\r
703 IN UINT64 IoMmuAccess\r
704 )\r
705{\r
706 return EFI_UNSUPPORTED;\r
707}\r
708\r
709EDKII_IOMMU_PROTOCOL mAmdSev = {\r
710 EDKII_IOMMU_PROTOCOL_REVISION,\r
711 IoMmuSetAttribute,\r
712 IoMmuMap,\r
713 IoMmuUnmap,\r
714 IoMmuAllocateBuffer,\r
715 IoMmuFreeBuffer,\r
716};\r
717\r
718/**\r
719 Initialize Iommu Protocol.\r
720\r
721**/\r
db125079 722EFI_STATUS\r
f9d129e6
BS
723EFIAPI\r
724AmdSevInstallIoMmuProtocol (\r
725 VOID\r
726 )\r
727{\r
728 EFI_STATUS Status;\r
729 EFI_HANDLE Handle;\r
730\r
731 Handle = NULL;\r
732 Status = gBS->InstallMultipleProtocolInterfaces (\r
733 &Handle,\r
734 &gEdkiiIoMmuProtocolGuid, &mAmdSev,\r
735 NULL\r
736 );\r
db125079 737 return Status;\r
f9d129e6 738}\r