]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/IoMmuDxe/AmdSevIoMmu.c
UefiPayloadPkg: Change OPTIONAL keyword usage style
[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
adfa3327 255 MapInfo->NumberOfPages\r
812568fb 256 );\r
f1658838
LE
257 ASSERT_EFI_ERROR (Status);\r
258 if (EFI_ERROR (Status)) {\r
259 CpuDeadLoop ();\r
260 }\r
f9d129e6
BS
261\r
262 //\r
263 // If this is a read operation from the Bus Master's point of view,\r
264 // then copy the contents of the real buffer into the mapped buffer\r
265 // so the Bus Master can read the contents of the real buffer.\r
266 //\r
58e68140
LE
267 // For BusMasterCommonBuffer[64] operations, the CopyMem() below will decrypt\r
268 // the original data (from the stash buffer) back to the original location.\r
269 //\r
f9d129e6 270 if (Operation == EdkiiIoMmuOperationBusMasterRead ||\r
58e68140
LE
271 Operation == EdkiiIoMmuOperationBusMasterRead64 ||\r
272 Operation == EdkiiIoMmuOperationBusMasterCommonBuffer ||\r
273 Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) {\r
f9d129e6 274 CopyMem (\r
dc194ce3 275 (VOID *) (UINTN) MapInfo->PlainTextAddress,\r
58e68140 276 DecryptionSource,\r
f9d129e6
BS
277 MapInfo->NumberOfBytes\r
278 );\r
279 }\r
280\r
9ed745b9
LE
281 //\r
282 // Track all MAP_INFO structures.\r
283 //\r
284 InsertHeadList (&mMapInfos, &MapInfo->Link);\r
f9d129e6 285 //\r
e130229c 286 // Populate output parameters.\r
f9d129e6 287 //\r
dc194ce3 288 *DeviceAddress = MapInfo->PlainTextAddress;\r
f9d129e6
BS
289 *Mapping = MapInfo;\r
290\r
812568fb
LE
291 DEBUG ((\r
292 DEBUG_VERBOSE,\r
2ad6ba80 293 "%a: Mapping=0x%p Device(PlainText)=0x%Lx Crypted=0x%Lx Pages=0x%Lx\n",\r
812568fb 294 __FUNCTION__,\r
2ad6ba80 295 MapInfo,\r
dc194ce3 296 MapInfo->PlainTextAddress,\r
c7ef2ed2 297 MapInfo->CryptedAddress,\r
2ad6ba80 298 (UINT64)MapInfo->NumberOfPages\r
812568fb 299 ));\r
f9d129e6
BS
300\r
301 return EFI_SUCCESS;\r
e130229c
LE
302\r
303FreeMapInfo:\r
304 FreePool (MapInfo);\r
305\r
306Failed:\r
307 *NumberOfBytes = 0;\r
308 return Status;\r
f9d129e6
BS
309}\r
310\r
311/**\r
312 Completes the Map() operation and releases any corresponding resources.\r
313\r
550acd08
LE
314 This is an internal worker function that only extends the Map() API with\r
315 the MemoryMapLocked parameter.\r
316\r
f9d129e6
BS
317 @param This The protocol instance pointer.\r
318 @param Mapping The mapping value returned from Map().\r
550acd08
LE
319 @param MemoryMapLocked The function is executing on the stack of\r
320 gBS->ExitBootServices(); changes to the UEFI\r
321 memory map are forbidden.\r
f9d129e6
BS
322\r
323 @retval EFI_SUCCESS The range was unmapped.\r
812568fb
LE
324 @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by\r
325 Map().\r
326 @retval EFI_DEVICE_ERROR The data was not committed to the target system\r
327 memory.\r
f9d129e6 328**/\r
550acd08 329STATIC\r
f9d129e6
BS
330EFI_STATUS\r
331EFIAPI\r
550acd08 332IoMmuUnmapWorker (\r
f9d129e6 333 IN EDKII_IOMMU_PROTOCOL *This,\r
550acd08
LE
334 IN VOID *Mapping,\r
335 IN BOOLEAN MemoryMapLocked\r
f9d129e6
BS
336 )\r
337{\r
338 MAP_INFO *MapInfo;\r
339 EFI_STATUS Status;\r
58e68140
LE
340 COMMON_BUFFER_HEADER *CommonBufferHeader;\r
341 VOID *EncryptionTarget;\r
f9d129e6 342\r
550acd08
LE
343 DEBUG ((\r
344 DEBUG_VERBOSE,\r
345 "%a: Mapping=0x%p MemoryMapLocked=%d\n",\r
346 __FUNCTION__,\r
347 Mapping,\r
348 MemoryMapLocked\r
349 ));\r
a1d6a9dc 350\r
f9d129e6
BS
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
f9d129e6 402 //\r
58e68140
LE
403 // Restore the memory encryption mask on the area we used to hold the\r
404 // plaintext.\r
f9d129e6 405 //\r
812568fb
LE
406 Status = MemEncryptSevSetPageEncMask (\r
407 0,\r
dc194ce3 408 MapInfo->PlainTextAddress,\r
adfa3327 409 MapInfo->NumberOfPages\r
812568fb 410 );\r
f1658838
LE
411 ASSERT_EFI_ERROR (Status);\r
412 if (EFI_ERROR (Status)) {\r
413 CpuDeadLoop ();\r
414 }\r
f9d129e6
BS
415\r
416 //\r
58e68140
LE
417 // For BusMasterCommonBuffer[64] operations, copy the stashed data to the\r
418 // original (now encrypted) location.\r
419 //\r
420 // For all other operations, fill the late bounce buffer (which existed as\r
550acd08
LE
421 // plaintext at some point) with zeros, and then release it (unless the UEFI\r
422 // memory map is locked).\r
58e68140
LE
423 //\r
424 if (MapInfo->Operation == EdkiiIoMmuOperationBusMasterCommonBuffer ||\r
425 MapInfo->Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) {\r
426 CopyMem (\r
427 (VOID *)(UINTN)MapInfo->CryptedAddress,\r
428 CommonBufferHeader->StashBuffer,\r
429 MapInfo->NumberOfBytes\r
430 );\r
431 } else {\r
432 ZeroMem (\r
433 (VOID *)(UINTN)MapInfo->PlainTextAddress,\r
434 EFI_PAGES_TO_SIZE (MapInfo->NumberOfPages)\r
435 );\r
550acd08
LE
436 if (!MemoryMapLocked) {\r
437 gBS->FreePages (MapInfo->PlainTextAddress, MapInfo->NumberOfPages);\r
438 }\r
58e68140
LE
439 }\r
440\r
9ed745b9 441 //\r
550acd08
LE
442 // Forget the MAP_INFO structure, then free it (unless the UEFI memory map is\r
443 // locked).\r
9ed745b9
LE
444 //\r
445 RemoveEntryList (&MapInfo->Link);\r
550acd08
LE
446 if (!MemoryMapLocked) {\r
447 FreePool (MapInfo);\r
448 }\r
9ed745b9 449\r
f9d129e6
BS
450 return EFI_SUCCESS;\r
451}\r
452\r
550acd08
LE
453/**\r
454 Completes the Map() operation and releases any corresponding resources.\r
455\r
456 @param This The protocol instance pointer.\r
457 @param Mapping The mapping value returned from Map().\r
458\r
459 @retval EFI_SUCCESS The range was unmapped.\r
460 @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by\r
461 Map().\r
462 @retval EFI_DEVICE_ERROR The data was not committed to the target system\r
463 memory.\r
464**/\r
465EFI_STATUS\r
466EFIAPI\r
467IoMmuUnmap (\r
468 IN EDKII_IOMMU_PROTOCOL *This,\r
469 IN VOID *Mapping\r
470 )\r
471{\r
472 return IoMmuUnmapWorker (\r
473 This,\r
474 Mapping,\r
475 FALSE // MemoryMapLocked\r
476 );\r
477}\r
478\r
f9d129e6
BS
479/**\r
480 Allocates pages that are suitable for an OperationBusMasterCommonBuffer or\r
481 OperationBusMasterCommonBuffer64 mapping.\r
482\r
483 @param This The protocol instance pointer.\r
484 @param Type This parameter is not used and must be ignored.\r
812568fb
LE
485 @param MemoryType The type of memory to allocate,\r
486 EfiBootServicesData or EfiRuntimeServicesData.\r
f9d129e6 487 @param Pages The number of pages to allocate.\r
812568fb
LE
488 @param HostAddress A pointer to store the base system memory\r
489 address of the allocated range.\r
490 @param Attributes The requested bit mask of attributes for the\r
491 allocated range.\r
f9d129e6
BS
492\r
493 @retval EFI_SUCCESS The requested memory pages were allocated.\r
812568fb
LE
494 @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal\r
495 attribute bits are MEMORY_WRITE_COMBINE and\r
496 MEMORY_CACHED.\r
f9d129e6
BS
497 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
498 @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.\r
499\r
500**/\r
501EFI_STATUS\r
502EFIAPI\r
503IoMmuAllocateBuffer (\r
504 IN EDKII_IOMMU_PROTOCOL *This,\r
505 IN EFI_ALLOCATE_TYPE Type,\r
506 IN EFI_MEMORY_TYPE MemoryType,\r
507 IN UINTN Pages,\r
508 IN OUT VOID **HostAddress,\r
509 IN UINT64 Attributes\r
510 )\r
511{\r
512 EFI_STATUS Status;\r
513 EFI_PHYSICAL_ADDRESS PhysicalAddress;\r
58e68140
LE
514 VOID *StashBuffer;\r
515 UINTN CommonBufferPages;\r
516 COMMON_BUFFER_HEADER *CommonBufferHeader;\r
f9d129e6 517\r
80ddd336
LE
518 DEBUG ((\r
519 DEBUG_VERBOSE,\r
520 "%a: MemoryType=%u Pages=0x%Lx Attributes=0x%Lx\n",\r
521 __FUNCTION__,\r
522 (UINT32)MemoryType,\r
523 (UINT64)Pages,\r
524 Attributes\r
525 ));\r
526\r
f9d129e6
BS
527 //\r
528 // Validate Attributes\r
529 //\r
530 if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) {\r
531 return EFI_UNSUPPORTED;\r
532 }\r
533\r
534 //\r
535 // Check for invalid inputs\r
536 //\r
537 if (HostAddress == NULL) {\r
538 return EFI_INVALID_PARAMETER;\r
539 }\r
540\r
541 //\r
542 // The only valid memory types are EfiBootServicesData and\r
543 // EfiRuntimeServicesData\r
544 //\r
545 if (MemoryType != EfiBootServicesData &&\r
546 MemoryType != EfiRuntimeServicesData) {\r
547 return EFI_INVALID_PARAMETER;\r
548 }\r
549\r
58e68140
LE
550 //\r
551 // We'll need a header page for the COMMON_BUFFER_HEADER structure.\r
552 //\r
553 if (Pages > MAX_UINTN - 1) {\r
554 return EFI_OUT_OF_RESOURCES;\r
555 }\r
556 CommonBufferPages = Pages + 1;\r
557\r
558 //\r
559 // Allocate the stash in EfiBootServicesData type memory.\r
560 //\r
561 // Map() will temporarily save encrypted data in the stash for\r
562 // BusMasterCommonBuffer[64] operations, so the data can be decrypted to the\r
563 // original location.\r
564 //\r
565 // Unmap() will temporarily save plaintext data in the stash for\r
566 // BusMasterCommonBuffer[64] operations, so the data can be encrypted to the\r
567 // original location.\r
568 //\r
569 // StashBuffer always resides in encrypted memory.\r
570 //\r
571 StashBuffer = AllocatePages (Pages);\r
572 if (StashBuffer == NULL) {\r
573 return EFI_OUT_OF_RESOURCES;\r
574 }\r
575\r
f9d129e6
BS
576 PhysicalAddress = (UINTN)-1;\r
577 if ((Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0) {\r
578 //\r
579 // Limit allocations to memory below 4GB\r
580 //\r
581 PhysicalAddress = SIZE_4GB - 1;\r
582 }\r
583 Status = gBS->AllocatePages (\r
584 AllocateMaxAddress,\r
585 MemoryType,\r
58e68140 586 CommonBufferPages,\r
f9d129e6
BS
587 &PhysicalAddress\r
588 );\r
58e68140
LE
589 if (EFI_ERROR (Status)) {\r
590 goto FreeStashBuffer;\r
f9d129e6
BS
591 }\r
592\r
58e68140
LE
593 CommonBufferHeader = (VOID *)(UINTN)PhysicalAddress;\r
594 PhysicalAddress += EFI_PAGE_SIZE;\r
595\r
596 CommonBufferHeader->Signature = COMMON_BUFFER_SIG;\r
597 CommonBufferHeader->StashBuffer = StashBuffer;\r
598\r
599 *HostAddress = (VOID *)(UINTN)PhysicalAddress;\r
600\r
812568fb
LE
601 DEBUG ((\r
602 DEBUG_VERBOSE,\r
80ddd336 603 "%a: Host=0x%Lx Stash=0x%p\n",\r
812568fb
LE
604 __FUNCTION__,\r
605 PhysicalAddress,\r
80ddd336 606 StashBuffer\r
812568fb 607 ));\r
58e68140
LE
608 return EFI_SUCCESS;\r
609\r
610FreeStashBuffer:\r
611 FreePages (StashBuffer, Pages);\r
f9d129e6
BS
612 return Status;\r
613}\r
614\r
615/**\r
616 Frees memory that was allocated with AllocateBuffer().\r
617\r
618 @param This The protocol instance pointer.\r
619 @param Pages The number of pages to free.\r
812568fb
LE
620 @param HostAddress The base system memory address of the allocated\r
621 range.\r
f9d129e6
BS
622\r
623 @retval EFI_SUCCESS The requested memory pages were freed.\r
812568fb
LE
624 @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and\r
625 Pages was not allocated with AllocateBuffer().\r
f9d129e6
BS
626\r
627**/\r
628EFI_STATUS\r
629EFIAPI\r
630IoMmuFreeBuffer (\r
631 IN EDKII_IOMMU_PROTOCOL *This,\r
632 IN UINTN Pages,\r
633 IN VOID *HostAddress\r
634 )\r
635{\r
58e68140
LE
636 UINTN CommonBufferPages;\r
637 COMMON_BUFFER_HEADER *CommonBufferHeader;\r
638\r
1afbb85f
LE
639 DEBUG ((\r
640 DEBUG_VERBOSE,\r
641 "%a: Host=0x%p Pages=0x%Lx\n",\r
642 __FUNCTION__,\r
643 HostAddress,\r
644 (UINT64)Pages\r
645 ));\r
646\r
58e68140
LE
647 CommonBufferPages = Pages + 1;\r
648 CommonBufferHeader = (COMMON_BUFFER_HEADER *)(\r
649 (UINTN)HostAddress - EFI_PAGE_SIZE\r
650 );\r
f9d129e6
BS
651\r
652 //\r
58e68140 653 // Check the signature.\r
f9d129e6 654 //\r
58e68140
LE
655 ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG);\r
656 if (CommonBufferHeader->Signature != COMMON_BUFFER_SIG) {\r
657 return EFI_INVALID_PARAMETER;\r
658 }\r
659\r
660 //\r
661 // Free the stash buffer. This buffer was always encrypted, so no need to\r
662 // zero it.\r
663 //\r
664 FreePages (CommonBufferHeader->StashBuffer, Pages);\r
f9d129e6 665\r
58e68140
LE
666 //\r
667 // Release the common buffer itself. Unmap() has re-encrypted it in-place, so\r
668 // no need to zero it.\r
669 //\r
670 return gBS->FreePages ((UINTN)CommonBufferHeader, CommonBufferPages);\r
f9d129e6
BS
671}\r
672\r
673\r
674/**\r
675 Set IOMMU attribute for a system memory.\r
676\r
677 If the IOMMU protocol exists, the system memory cannot be used\r
678 for DMA by default.\r
679\r
680 When a device requests a DMA access for a system memory,\r
681 the device driver need use SetAttribute() to update the IOMMU\r
682 attribute to request DMA access (read and/or write).\r
683\r
684 The DeviceHandle is used to identify which device submits the request.\r
812568fb
LE
685 The IOMMU implementation need translate the device path to an IOMMU device\r
686 ID, and set IOMMU hardware register accordingly.\r
f9d129e6
BS
687 1) DeviceHandle can be a standard PCI device.\r
688 The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ.\r
689 The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE.\r
812568fb
LE
690 The memory for BusMasterCommonBuffer need set\r
691 EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE.\r
692 After the memory is used, the memory need set 0 to keep it being\r
693 protected.\r
f9d129e6 694 2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).\r
812568fb
LE
695 The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or\r
696 EDKII_IOMMU_ACCESS_WRITE.\r
f9d129e6
BS
697\r
698 @param[in] This The protocol instance pointer.\r
812568fb
LE
699 @param[in] DeviceHandle The device who initiates the DMA access\r
700 request.\r
f9d129e6
BS
701 @param[in] Mapping The mapping value returned from Map().\r
702 @param[in] IoMmuAccess The IOMMU access.\r
703\r
812568fb
LE
704 @retval EFI_SUCCESS The IoMmuAccess is set for the memory range\r
705 specified by DeviceAddress and Length.\r
f9d129e6 706 @retval EFI_INVALID_PARAMETER DeviceHandle is an invalid handle.\r
812568fb
LE
707 @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by\r
708 Map().\r
709 @retval EFI_INVALID_PARAMETER IoMmuAccess specified an illegal combination\r
710 of access.\r
f9d129e6 711 @retval EFI_UNSUPPORTED DeviceHandle is unknown by the IOMMU.\r
812568fb
LE
712 @retval EFI_UNSUPPORTED The bit mask of IoMmuAccess is not supported\r
713 by the IOMMU.\r
714 @retval EFI_UNSUPPORTED The IOMMU does not support the memory range\r
715 specified by Mapping.\r
716 @retval EFI_OUT_OF_RESOURCES There are not enough resources available to\r
717 modify the IOMMU access.\r
718 @retval EFI_DEVICE_ERROR The IOMMU device reported an error while\r
719 attempting the operation.\r
f9d129e6
BS
720\r
721**/\r
722EFI_STATUS\r
723EFIAPI\r
724IoMmuSetAttribute (\r
725 IN EDKII_IOMMU_PROTOCOL *This,\r
726 IN EFI_HANDLE DeviceHandle,\r
727 IN VOID *Mapping,\r
728 IN UINT64 IoMmuAccess\r
729 )\r
730{\r
731 return EFI_UNSUPPORTED;\r
732}\r
733\r
734EDKII_IOMMU_PROTOCOL mAmdSev = {\r
735 EDKII_IOMMU_PROTOCOL_REVISION,\r
736 IoMmuSetAttribute,\r
737 IoMmuMap,\r
738 IoMmuUnmap,\r
739 IoMmuAllocateBuffer,\r
740 IoMmuFreeBuffer,\r
741};\r
742\r
7aee391f
LE
743/**\r
744 Notification function that is queued when gBS->ExitBootServices() signals the\r
745 EFI_EVENT_GROUP_EXIT_BOOT_SERVICES event group. This function signals another\r
746 event, received as Context, and returns.\r
747\r
748 Signaling an event in this context is safe. The UEFI spec allows\r
749 gBS->SignalEvent() to return EFI_SUCCESS only; EFI_OUT_OF_RESOURCES is not\r
750 listed, hence memory is not allocated. The edk2 implementation also does not\r
751 release memory (and we only have to care about the edk2 implementation\r
752 because EDKII_IOMMU_PROTOCOL is edk2-specific anyway).\r
753\r
754 @param[in] Event Event whose notification function is being invoked.\r
755 Event is permitted to request the queueing of this\r
756 function at TPL_CALLBACK or TPL_NOTIFY task\r
757 priority level.\r
758\r
759 @param[in] EventToSignal Identifies the EFI_EVENT to signal. EventToSignal\r
760 is permitted to request the queueing of its\r
761 notification function only at TPL_CALLBACK level.\r
762**/\r
763STATIC\r
764VOID\r
765EFIAPI\r
766AmdSevExitBoot (\r
767 IN EFI_EVENT Event,\r
768 IN VOID *EventToSignal\r
769 )\r
770{\r
771 //\r
772 // (1) The NotifyFunctions of all the events in\r
773 // EFI_EVENT_GROUP_EXIT_BOOT_SERVICES will have been queued before\r
774 // AmdSevExitBoot() is entered.\r
775 //\r
776 // (2) AmdSevExitBoot() is executing minimally at TPL_CALLBACK.\r
777 //\r
778 // (3) AmdSevExitBoot() has been queued in unspecified order relative to the\r
779 // NotifyFunctions of all the other events in\r
780 // EFI_EVENT_GROUP_EXIT_BOOT_SERVICES whose NotifyTpl is the same as\r
781 // Event's.\r
782 //\r
783 // Consequences:\r
784 //\r
785 // - If Event's NotifyTpl is TPL_CALLBACK, then some other NotifyFunctions\r
786 // queued at TPL_CALLBACK may be invoked after AmdSevExitBoot() returns.\r
787 //\r
788 // - If Event's NotifyTpl is TPL_NOTIFY, then some other NotifyFunctions\r
789 // queued at TPL_NOTIFY may be invoked after AmdSevExitBoot() returns; plus\r
790 // *all* NotifyFunctions queued at TPL_CALLBACK will be invoked strictly\r
791 // after all NotifyFunctions queued at TPL_NOTIFY, including\r
792 // AmdSevExitBoot(), have been invoked.\r
793 //\r
794 // - By signaling EventToSignal here, whose NotifyTpl is TPL_CALLBACK, we\r
795 // queue EventToSignal's NotifyFunction after the NotifyFunctions of *all*\r
796 // events in EFI_EVENT_GROUP_EXIT_BOOT_SERVICES.\r
797 //\r
798 DEBUG ((DEBUG_VERBOSE, "%a\n", __FUNCTION__));\r
799 gBS->SignalEvent (EventToSignal);\r
800}\r
801\r
802/**\r
803 Notification function that is queued after the notification functions of all\r
804 events in the EFI_EVENT_GROUP_EXIT_BOOT_SERVICES event group. The same memory\r
805 map restrictions apply.\r
806\r
807 This function unmaps all currently existing IOMMU mappings.\r
808\r
809 @param[in] Event Event whose notification function is being invoked. Event\r
810 is permitted to request the queueing of this function\r
811 only at TPL_CALLBACK task priority level.\r
812\r
813 @param[in] Context Ignored.\r
814**/\r
815STATIC\r
816VOID\r
817EFIAPI\r
818AmdSevUnmapAllMappings (\r
819 IN EFI_EVENT Event,\r
820 IN VOID *Context\r
821 )\r
822{\r
823 LIST_ENTRY *Node;\r
824 LIST_ENTRY *NextNode;\r
825 MAP_INFO *MapInfo;\r
826\r
827 DEBUG ((DEBUG_VERBOSE, "%a\n", __FUNCTION__));\r
828\r
829 //\r
830 // All drivers that had set up IOMMU mappings have halted their respective\r
831 // controllers by now; tear down the mappings.\r
832 //\r
833 for (Node = GetFirstNode (&mMapInfos); Node != &mMapInfos; Node = NextNode) {\r
834 NextNode = GetNextNode (&mMapInfos, Node);\r
835 MapInfo = CR (Node, MAP_INFO, Link, MAP_INFO_SIG);\r
836 IoMmuUnmapWorker (\r
837 &mAmdSev, // This\r
838 MapInfo, // Mapping\r
839 TRUE // MemoryMapLocked\r
840 );\r
841 }\r
842}\r
843\r
f9d129e6
BS
844/**\r
845 Initialize Iommu Protocol.\r
846\r
847**/\r
db125079 848EFI_STATUS\r
f9d129e6
BS
849EFIAPI\r
850AmdSevInstallIoMmuProtocol (\r
851 VOID\r
852 )\r
853{\r
854 EFI_STATUS Status;\r
7aee391f
LE
855 EFI_EVENT UnmapAllMappingsEvent;\r
856 EFI_EVENT ExitBootEvent;\r
f9d129e6
BS
857 EFI_HANDLE Handle;\r
858\r
7aee391f
LE
859 //\r
860 // Create the "late" event whose notification function will tear down all\r
861 // left-over IOMMU mappings.\r
862 //\r
863 Status = gBS->CreateEvent (\r
864 EVT_NOTIFY_SIGNAL, // Type\r
865 TPL_CALLBACK, // NotifyTpl\r
866 AmdSevUnmapAllMappings, // NotifyFunction\r
867 NULL, // NotifyContext\r
868 &UnmapAllMappingsEvent // Event\r
869 );\r
870 if (EFI_ERROR (Status)) {\r
871 return Status;\r
872 }\r
873\r
874 //\r
875 // Create the event whose notification function will be queued by\r
876 // gBS->ExitBootServices() and will signal the event created above.\r
877 //\r
878 Status = gBS->CreateEvent (\r
879 EVT_SIGNAL_EXIT_BOOT_SERVICES, // Type\r
880 TPL_CALLBACK, // NotifyTpl\r
881 AmdSevExitBoot, // NotifyFunction\r
882 UnmapAllMappingsEvent, // NotifyContext\r
883 &ExitBootEvent // Event\r
884 );\r
885 if (EFI_ERROR (Status)) {\r
886 goto CloseUnmapAllMappingsEvent;\r
887 }\r
888\r
f9d129e6
BS
889 Handle = NULL;\r
890 Status = gBS->InstallMultipleProtocolInterfaces (\r
891 &Handle,\r
892 &gEdkiiIoMmuProtocolGuid, &mAmdSev,\r
893 NULL\r
894 );\r
7aee391f
LE
895 if (EFI_ERROR (Status)) {\r
896 goto CloseExitBootEvent;\r
897 }\r
898\r
899 return EFI_SUCCESS;\r
900\r
901CloseExitBootEvent:\r
902 gBS->CloseEvent (ExitBootEvent);\r
903\r
904CloseUnmapAllMappingsEvent:\r
905 gBS->CloseEvent (UnmapAllMappingsEvent);\r
906\r
db125079 907 return Status;\r
f9d129e6 908}\r