]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/IoMmuDxe/AmdSevIoMmu.c
OvmfPkg/IoMmuDxe: IoMmuMap(): log nicer and more informative DEBUG msgs
[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
BS
350\r
351 if (Mapping == NULL) {\r
352 return EFI_INVALID_PARAMETER;\r
353 }\r
354\r
58e68140
LE
355 MapInfo = (MAP_INFO *)Mapping;\r
356\r
f9d129e6 357 //\r
58e68140 358 // set CommonBufferHeader to suppress incorrect compiler/analyzer warnings\r
f9d129e6 359 //\r
58e68140 360 CommonBufferHeader = NULL;\r
f9d129e6
BS
361\r
362 //\r
58e68140
LE
363 // For BusMasterWrite[64] operations and BusMasterCommonBuffer[64] operations\r
364 // we have to encrypt the results, ultimately to the original place (i.e.,\r
365 // "MapInfo->CryptedAddress").\r
f9d129e6 366 //\r
58e68140
LE
367 // For BusMasterCommonBuffer[64] operations however, this encryption has to\r
368 // land in-place, so divert the encryption to the stash buffer first.\r
369 //\r
370 EncryptionTarget = (VOID *)(UINTN)MapInfo->CryptedAddress;\r
371\r
372 switch (MapInfo->Operation) {\r
373 case EdkiiIoMmuOperationBusMasterCommonBuffer:\r
374 case EdkiiIoMmuOperationBusMasterCommonBuffer64:\r
375 ASSERT (MapInfo->PlainTextAddress == MapInfo->CryptedAddress);\r
376\r
377 CommonBufferHeader = (COMMON_BUFFER_HEADER *)(\r
378 (UINTN)MapInfo->PlainTextAddress - EFI_PAGE_SIZE\r
379 );\r
380 ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG);\r
381 EncryptionTarget = CommonBufferHeader->StashBuffer;\r
382 //\r
383 // fall through\r
384 //\r
385\r
386 case EdkiiIoMmuOperationBusMasterWrite:\r
387 case EdkiiIoMmuOperationBusMasterWrite64:\r
f9d129e6 388 CopyMem (\r
58e68140 389 EncryptionTarget,\r
dc194ce3 390 (VOID *) (UINTN) MapInfo->PlainTextAddress,\r
f9d129e6
BS
391 MapInfo->NumberOfBytes\r
392 );\r
58e68140
LE
393 break;\r
394\r
395 default:\r
396 //\r
397 // nothing to encrypt after BusMasterRead[64] operations\r
398 //\r
399 break;\r
f9d129e6
BS
400 }\r
401\r
812568fb
LE
402 DEBUG ((\r
403 DEBUG_VERBOSE,\r
c7ef2ed2 404 "%a PlainText 0x%Lx Crypted 0x%Lx Pages 0x%Lx Bytes 0x%Lx\n",\r
812568fb 405 __FUNCTION__,\r
dc194ce3 406 MapInfo->PlainTextAddress,\r
c7ef2ed2 407 MapInfo->CryptedAddress,\r
60aa3a0e
LE
408 (UINT64)MapInfo->NumberOfPages,\r
409 (UINT64)MapInfo->NumberOfBytes\r
812568fb 410 ));\r
58e68140 411\r
f9d129e6 412 //\r
58e68140
LE
413 // Restore the memory encryption mask on the area we used to hold the\r
414 // plaintext.\r
f9d129e6 415 //\r
812568fb
LE
416 Status = MemEncryptSevSetPageEncMask (\r
417 0,\r
dc194ce3 418 MapInfo->PlainTextAddress,\r
812568fb
LE
419 MapInfo->NumberOfPages,\r
420 TRUE\r
421 );\r
f1658838
LE
422 ASSERT_EFI_ERROR (Status);\r
423 if (EFI_ERROR (Status)) {\r
424 CpuDeadLoop ();\r
425 }\r
f9d129e6
BS
426\r
427 //\r
58e68140
LE
428 // For BusMasterCommonBuffer[64] operations, copy the stashed data to the\r
429 // original (now encrypted) location.\r
430 //\r
431 // For all other operations, fill the late bounce buffer (which existed as\r
432 // plaintext at some point) with zeros, and then release it.\r
433 //\r
434 if (MapInfo->Operation == EdkiiIoMmuOperationBusMasterCommonBuffer ||\r
435 MapInfo->Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) {\r
436 CopyMem (\r
437 (VOID *)(UINTN)MapInfo->CryptedAddress,\r
438 CommonBufferHeader->StashBuffer,\r
439 MapInfo->NumberOfBytes\r
440 );\r
d0c9afea
LE
441\r
442 //\r
443 // Recycle the MAP_INFO structure.\r
444 //\r
445 InsertTailList (&mRecycledMapInfos, &MapInfo->Link);\r
58e68140
LE
446 } else {\r
447 ZeroMem (\r
448 (VOID *)(UINTN)MapInfo->PlainTextAddress,\r
449 EFI_PAGES_TO_SIZE (MapInfo->NumberOfPages)\r
450 );\r
451 gBS->FreePages (MapInfo->PlainTextAddress, MapInfo->NumberOfPages);\r
d0c9afea
LE
452\r
453 //\r
454 // Free the MAP_INFO structure.\r
455 //\r
456 FreePool (MapInfo);\r
58e68140
LE
457 }\r
458\r
f9d129e6
BS
459 return EFI_SUCCESS;\r
460}\r
461\r
462/**\r
463 Allocates pages that are suitable for an OperationBusMasterCommonBuffer or\r
464 OperationBusMasterCommonBuffer64 mapping.\r
465\r
466 @param This The protocol instance pointer.\r
467 @param Type This parameter is not used and must be ignored.\r
812568fb
LE
468 @param MemoryType The type of memory to allocate,\r
469 EfiBootServicesData or EfiRuntimeServicesData.\r
f9d129e6 470 @param Pages The number of pages to allocate.\r
812568fb
LE
471 @param HostAddress A pointer to store the base system memory\r
472 address of the allocated range.\r
473 @param Attributes The requested bit mask of attributes for the\r
474 allocated range.\r
f9d129e6
BS
475\r
476 @retval EFI_SUCCESS The requested memory pages were allocated.\r
812568fb
LE
477 @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal\r
478 attribute bits are MEMORY_WRITE_COMBINE and\r
479 MEMORY_CACHED.\r
f9d129e6
BS
480 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
481 @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.\r
482\r
483**/\r
484EFI_STATUS\r
485EFIAPI\r
486IoMmuAllocateBuffer (\r
487 IN EDKII_IOMMU_PROTOCOL *This,\r
488 IN EFI_ALLOCATE_TYPE Type,\r
489 IN EFI_MEMORY_TYPE MemoryType,\r
490 IN UINTN Pages,\r
491 IN OUT VOID **HostAddress,\r
492 IN UINT64 Attributes\r
493 )\r
494{\r
495 EFI_STATUS Status;\r
496 EFI_PHYSICAL_ADDRESS PhysicalAddress;\r
58e68140
LE
497 VOID *StashBuffer;\r
498 UINTN CommonBufferPages;\r
499 COMMON_BUFFER_HEADER *CommonBufferHeader;\r
f9d129e6
BS
500\r
501 //\r
502 // Validate Attributes\r
503 //\r
504 if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) {\r
505 return EFI_UNSUPPORTED;\r
506 }\r
507\r
508 //\r
509 // Check for invalid inputs\r
510 //\r
511 if (HostAddress == NULL) {\r
512 return EFI_INVALID_PARAMETER;\r
513 }\r
514\r
515 //\r
516 // The only valid memory types are EfiBootServicesData and\r
517 // EfiRuntimeServicesData\r
518 //\r
519 if (MemoryType != EfiBootServicesData &&\r
520 MemoryType != EfiRuntimeServicesData) {\r
521 return EFI_INVALID_PARAMETER;\r
522 }\r
523\r
58e68140
LE
524 //\r
525 // We'll need a header page for the COMMON_BUFFER_HEADER structure.\r
526 //\r
527 if (Pages > MAX_UINTN - 1) {\r
528 return EFI_OUT_OF_RESOURCES;\r
529 }\r
530 CommonBufferPages = Pages + 1;\r
531\r
532 //\r
533 // Allocate the stash in EfiBootServicesData type memory.\r
534 //\r
535 // Map() will temporarily save encrypted data in the stash for\r
536 // BusMasterCommonBuffer[64] operations, so the data can be decrypted to the\r
537 // original location.\r
538 //\r
539 // Unmap() will temporarily save plaintext data in the stash for\r
540 // BusMasterCommonBuffer[64] operations, so the data can be encrypted to the\r
541 // original location.\r
542 //\r
543 // StashBuffer always resides in encrypted memory.\r
544 //\r
545 StashBuffer = AllocatePages (Pages);\r
546 if (StashBuffer == NULL) {\r
547 return EFI_OUT_OF_RESOURCES;\r
548 }\r
549\r
f9d129e6
BS
550 PhysicalAddress = (UINTN)-1;\r
551 if ((Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0) {\r
552 //\r
553 // Limit allocations to memory below 4GB\r
554 //\r
555 PhysicalAddress = SIZE_4GB - 1;\r
556 }\r
557 Status = gBS->AllocatePages (\r
558 AllocateMaxAddress,\r
559 MemoryType,\r
58e68140 560 CommonBufferPages,\r
f9d129e6
BS
561 &PhysicalAddress\r
562 );\r
58e68140
LE
563 if (EFI_ERROR (Status)) {\r
564 goto FreeStashBuffer;\r
f9d129e6
BS
565 }\r
566\r
58e68140
LE
567 CommonBufferHeader = (VOID *)(UINTN)PhysicalAddress;\r
568 PhysicalAddress += EFI_PAGE_SIZE;\r
569\r
570 CommonBufferHeader->Signature = COMMON_BUFFER_SIG;\r
571 CommonBufferHeader->StashBuffer = StashBuffer;\r
572\r
573 *HostAddress = (VOID *)(UINTN)PhysicalAddress;\r
574\r
812568fb
LE
575 DEBUG ((\r
576 DEBUG_VERBOSE,\r
577 "%a Address 0x%Lx Pages 0x%Lx\n",\r
578 __FUNCTION__,\r
579 PhysicalAddress,\r
60aa3a0e 580 (UINT64)Pages\r
812568fb 581 ));\r
58e68140
LE
582 return EFI_SUCCESS;\r
583\r
584FreeStashBuffer:\r
585 FreePages (StashBuffer, Pages);\r
f9d129e6
BS
586 return Status;\r
587}\r
588\r
589/**\r
590 Frees memory that was allocated with AllocateBuffer().\r
591\r
592 @param This The protocol instance pointer.\r
593 @param Pages The number of pages to free.\r
812568fb
LE
594 @param HostAddress The base system memory address of the allocated\r
595 range.\r
f9d129e6
BS
596\r
597 @retval EFI_SUCCESS The requested memory pages were freed.\r
812568fb
LE
598 @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and\r
599 Pages was not allocated with AllocateBuffer().\r
f9d129e6
BS
600\r
601**/\r
602EFI_STATUS\r
603EFIAPI\r
604IoMmuFreeBuffer (\r
605 IN EDKII_IOMMU_PROTOCOL *This,\r
606 IN UINTN Pages,\r
607 IN VOID *HostAddress\r
608 )\r
609{\r
58e68140
LE
610 UINTN CommonBufferPages;\r
611 COMMON_BUFFER_HEADER *CommonBufferHeader;\r
612\r
613 CommonBufferPages = Pages + 1;\r
614 CommonBufferHeader = (COMMON_BUFFER_HEADER *)(\r
615 (UINTN)HostAddress - EFI_PAGE_SIZE\r
616 );\r
f9d129e6
BS
617\r
618 //\r
58e68140 619 // Check the signature.\r
f9d129e6 620 //\r
58e68140
LE
621 ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG);\r
622 if (CommonBufferHeader->Signature != COMMON_BUFFER_SIG) {\r
623 return EFI_INVALID_PARAMETER;\r
624 }\r
625\r
626 //\r
627 // Free the stash buffer. This buffer was always encrypted, so no need to\r
628 // zero it.\r
629 //\r
630 FreePages (CommonBufferHeader->StashBuffer, Pages);\r
f9d129e6 631\r
812568fb
LE
632 DEBUG ((\r
633 DEBUG_VERBOSE,\r
634 "%a Address 0x%Lx Pages 0x%Lx\n",\r
635 __FUNCTION__,\r
60aa3a0e
LE
636 (UINT64)(UINTN)HostAddress,\r
637 (UINT64)Pages\r
812568fb 638 ));\r
58e68140
LE
639\r
640 //\r
641 // Release the common buffer itself. Unmap() has re-encrypted it in-place, so\r
642 // no need to zero it.\r
643 //\r
644 return gBS->FreePages ((UINTN)CommonBufferHeader, CommonBufferPages);\r
f9d129e6
BS
645}\r
646\r
647\r
648/**\r
649 Set IOMMU attribute for a system memory.\r
650\r
651 If the IOMMU protocol exists, the system memory cannot be used\r
652 for DMA by default.\r
653\r
654 When a device requests a DMA access for a system memory,\r
655 the device driver need use SetAttribute() to update the IOMMU\r
656 attribute to request DMA access (read and/or write).\r
657\r
658 The DeviceHandle is used to identify which device submits the request.\r
812568fb
LE
659 The IOMMU implementation need translate the device path to an IOMMU device\r
660 ID, and set IOMMU hardware register accordingly.\r
f9d129e6
BS
661 1) DeviceHandle can be a standard PCI device.\r
662 The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ.\r
663 The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE.\r
812568fb
LE
664 The memory for BusMasterCommonBuffer need set\r
665 EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE.\r
666 After the memory is used, the memory need set 0 to keep it being\r
667 protected.\r
f9d129e6 668 2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).\r
812568fb
LE
669 The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or\r
670 EDKII_IOMMU_ACCESS_WRITE.\r
f9d129e6
BS
671\r
672 @param[in] This The protocol instance pointer.\r
812568fb
LE
673 @param[in] DeviceHandle The device who initiates the DMA access\r
674 request.\r
f9d129e6
BS
675 @param[in] Mapping The mapping value returned from Map().\r
676 @param[in] IoMmuAccess The IOMMU access.\r
677\r
812568fb
LE
678 @retval EFI_SUCCESS The IoMmuAccess is set for the memory range\r
679 specified by DeviceAddress and Length.\r
f9d129e6 680 @retval EFI_INVALID_PARAMETER DeviceHandle is an invalid handle.\r
812568fb
LE
681 @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by\r
682 Map().\r
683 @retval EFI_INVALID_PARAMETER IoMmuAccess specified an illegal combination\r
684 of access.\r
f9d129e6 685 @retval EFI_UNSUPPORTED DeviceHandle is unknown by the IOMMU.\r
812568fb
LE
686 @retval EFI_UNSUPPORTED The bit mask of IoMmuAccess is not supported\r
687 by the IOMMU.\r
688 @retval EFI_UNSUPPORTED The IOMMU does not support the memory range\r
689 specified by Mapping.\r
690 @retval EFI_OUT_OF_RESOURCES There are not enough resources available to\r
691 modify the IOMMU access.\r
692 @retval EFI_DEVICE_ERROR The IOMMU device reported an error while\r
693 attempting the operation.\r
f9d129e6
BS
694\r
695**/\r
696EFI_STATUS\r
697EFIAPI\r
698IoMmuSetAttribute (\r
699 IN EDKII_IOMMU_PROTOCOL *This,\r
700 IN EFI_HANDLE DeviceHandle,\r
701 IN VOID *Mapping,\r
702 IN UINT64 IoMmuAccess\r
703 )\r
704{\r
705 return EFI_UNSUPPORTED;\r
706}\r
707\r
708EDKII_IOMMU_PROTOCOL mAmdSev = {\r
709 EDKII_IOMMU_PROTOCOL_REVISION,\r
710 IoMmuSetAttribute,\r
711 IoMmuMap,\r
712 IoMmuUnmap,\r
713 IoMmuAllocateBuffer,\r
714 IoMmuFreeBuffer,\r
715};\r
716\r
717/**\r
718 Initialize Iommu Protocol.\r
719\r
720**/\r
db125079 721EFI_STATUS\r
f9d129e6
BS
722EFIAPI\r
723AmdSevInstallIoMmuProtocol (\r
724 VOID\r
725 )\r
726{\r
727 EFI_STATUS Status;\r
728 EFI_HANDLE Handle;\r
729\r
730 Handle = NULL;\r
731 Status = gBS->InstallMultipleProtocolInterfaces (\r
732 &Handle,\r
733 &gEdkiiIoMmuProtocolGuid, &mAmdSev,\r
734 NULL\r
735 );\r
db125079 736 return Status;\r
f9d129e6 737}\r