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