]> git.proxmox.com Git - mirror_edk2.git/blob - IntelSiliconPkg/Feature/VTd/IntelVTdDxe/BmDma.c
IntelSiliconPkg IntelVTdDxe: Fix incorrect code to clear VTd error
[mirror_edk2.git] / IntelSiliconPkg / Feature / VTd / IntelVTdDxe / BmDma.c
1 /** @file
2 BmDma related function
3
4 Copyright (c) 2017 - 2018, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "DmaProtection.h"
16
17 // TBD: May make it a policy
18 #define DMA_MEMORY_TOP MAX_UINTN
19 //#define DMA_MEMORY_TOP 0x0000000001FFFFFFULL
20
21 #define MAP_HANDLE_INFO_SIGNATURE SIGNATURE_32 ('H', 'M', 'A', 'P')
22 typedef struct {
23 UINT32 Signature;
24 LIST_ENTRY Link;
25 EFI_HANDLE DeviceHandle;
26 UINT64 IoMmuAccess;
27 } MAP_HANDLE_INFO;
28 #define MAP_HANDLE_INFO_FROM_LINK(a) CR (a, MAP_HANDLE_INFO, Link, MAP_HANDLE_INFO_SIGNATURE)
29
30 #define MAP_INFO_SIGNATURE SIGNATURE_32 ('D', 'M', 'A', 'P')
31 typedef struct {
32 UINT32 Signature;
33 LIST_ENTRY Link;
34 EDKII_IOMMU_OPERATION Operation;
35 UINTN NumberOfBytes;
36 UINTN NumberOfPages;
37 EFI_PHYSICAL_ADDRESS HostAddress;
38 EFI_PHYSICAL_ADDRESS DeviceAddress;
39 LIST_ENTRY HandleList;
40 } MAP_INFO;
41 #define MAP_INFO_FROM_LINK(a) CR (a, MAP_INFO, Link, MAP_INFO_SIGNATURE)
42
43 LIST_ENTRY gMaps = INITIALIZE_LIST_HEAD_VARIABLE(gMaps);
44
45 /**
46 This function fills DeviceHandle/IoMmuAccess to the MAP_HANDLE_INFO,
47 based upon the DeviceAddress.
48
49 @param[in] DeviceHandle The device who initiates the DMA access request.
50 @param[in] DeviceAddress The base of device memory address to be used as the DMA memory.
51 @param[in] Length The length of device memory address to be used as the DMA memory.
52 @param[in] IoMmuAccess The IOMMU access.
53
54 **/
55 VOID
56 SyncDeviceHandleToMapInfo (
57 IN EFI_HANDLE DeviceHandle,
58 IN EFI_PHYSICAL_ADDRESS DeviceAddress,
59 IN UINT64 Length,
60 IN UINT64 IoMmuAccess
61 )
62 {
63 MAP_INFO *MapInfo;
64 MAP_HANDLE_INFO *MapHandleInfo;
65 LIST_ENTRY *Link;
66 EFI_TPL OriginalTpl;
67
68 //
69 // Find MapInfo according to DeviceAddress
70 //
71 OriginalTpl = gBS->RaiseTPL (VTD_TPL_LEVEL);
72 MapInfo = NULL;
73 for (Link = GetFirstNode (&gMaps)
74 ; !IsNull (&gMaps, Link)
75 ; Link = GetNextNode (&gMaps, Link)
76 ) {
77 MapInfo = MAP_INFO_FROM_LINK (Link);
78 if (MapInfo->DeviceAddress == DeviceAddress) {
79 break;
80 }
81 }
82 if ((MapInfo == NULL) || (MapInfo->DeviceAddress != DeviceAddress)) {
83 DEBUG ((DEBUG_ERROR, "SyncDeviceHandleToMapInfo: DeviceAddress(0x%lx) - not found\n", DeviceAddress));
84 gBS->RestoreTPL (OriginalTpl);
85 return ;
86 }
87
88 //
89 // Find MapHandleInfo according to DeviceHandle
90 //
91 MapHandleInfo = NULL;
92 for (Link = GetFirstNode (&MapInfo->HandleList)
93 ; !IsNull (&MapInfo->HandleList, Link)
94 ; Link = GetNextNode (&MapInfo->HandleList, Link)
95 ) {
96 MapHandleInfo = MAP_HANDLE_INFO_FROM_LINK (Link);
97 if (MapHandleInfo->DeviceHandle == DeviceHandle) {
98 break;
99 }
100 }
101 if ((MapHandleInfo != NULL) && (MapHandleInfo->DeviceHandle == DeviceHandle)) {
102 MapHandleInfo->IoMmuAccess = IoMmuAccess;
103 gBS->RestoreTPL (OriginalTpl);
104 return ;
105 }
106
107 //
108 // No DeviceHandle
109 // Initialize and insert the MAP_HANDLE_INFO structure
110 //
111 MapHandleInfo = AllocatePool (sizeof (MAP_HANDLE_INFO));
112 if (MapHandleInfo == NULL) {
113 DEBUG ((DEBUG_ERROR, "SyncDeviceHandleToMapInfo: %r\n", EFI_OUT_OF_RESOURCES));
114 gBS->RestoreTPL (OriginalTpl);
115 return ;
116 }
117
118 MapHandleInfo->Signature = MAP_HANDLE_INFO_SIGNATURE;
119 MapHandleInfo->DeviceHandle = DeviceHandle;
120 MapHandleInfo->IoMmuAccess = IoMmuAccess;
121
122 InsertTailList (&MapInfo->HandleList, &MapHandleInfo->Link);
123 gBS->RestoreTPL (OriginalTpl);
124
125 return ;
126 }
127
128 /**
129 Provides the controller-specific addresses required to access system memory from a
130 DMA bus master.
131
132 @param This The protocol instance pointer.
133 @param Operation Indicates if the bus master is going to read or write to system memory.
134 @param HostAddress The system memory address to map to the PCI controller.
135 @param NumberOfBytes On input the number of bytes to map. On output the number of bytes
136 that were mapped.
137 @param DeviceAddress The resulting map address for the bus master PCI controller to use to
138 access the hosts HostAddress.
139 @param Mapping A resulting value to pass to Unmap().
140
141 @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes.
142 @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer.
143 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
144 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
145 @retval EFI_DEVICE_ERROR The system hardware could not map the requested address.
146
147 **/
148 EFI_STATUS
149 EFIAPI
150 IoMmuMap (
151 IN EDKII_IOMMU_PROTOCOL *This,
152 IN EDKII_IOMMU_OPERATION Operation,
153 IN VOID *HostAddress,
154 IN OUT UINTN *NumberOfBytes,
155 OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
156 OUT VOID **Mapping
157 )
158 {
159 EFI_STATUS Status;
160 EFI_PHYSICAL_ADDRESS PhysicalAddress;
161 MAP_INFO *MapInfo;
162 EFI_PHYSICAL_ADDRESS DmaMemoryTop;
163 BOOLEAN NeedRemap;
164 EFI_TPL OriginalTpl;
165
166 if (NumberOfBytes == NULL || DeviceAddress == NULL ||
167 Mapping == NULL) {
168 DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_INVALID_PARAMETER));
169 return EFI_INVALID_PARAMETER;
170 }
171
172 DEBUG ((DEBUG_VERBOSE, "IoMmuMap: ==> 0x%08x - 0x%08x (%x)\n", HostAddress, *NumberOfBytes, Operation));
173
174 //
175 // Make sure that Operation is valid
176 //
177 if ((UINT32) Operation >= EdkiiIoMmuOperationMaximum) {
178 DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_INVALID_PARAMETER));
179 return EFI_INVALID_PARAMETER;
180 }
181 NeedRemap = FALSE;
182 PhysicalAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress;
183
184 DmaMemoryTop = DMA_MEMORY_TOP;
185
186 //
187 // Alignment check
188 //
189 if ((*NumberOfBytes != ALIGN_VALUE(*NumberOfBytes, SIZE_4KB)) ||
190 (PhysicalAddress != ALIGN_VALUE(PhysicalAddress, SIZE_4KB))) {
191 if ((Operation == EdkiiIoMmuOperationBusMasterCommonBuffer) ||
192 (Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64)) {
193 //
194 // The input buffer might be a subset from IoMmuAllocateBuffer.
195 // Skip the check.
196 //
197 } else {
198 NeedRemap = TRUE;
199 }
200 }
201
202 if ((PhysicalAddress + *NumberOfBytes) >= DMA_MEMORY_TOP) {
203 NeedRemap = TRUE;
204 }
205
206 if (((Operation != EdkiiIoMmuOperationBusMasterRead64 &&
207 Operation != EdkiiIoMmuOperationBusMasterWrite64 &&
208 Operation != EdkiiIoMmuOperationBusMasterCommonBuffer64)) &&
209 ((PhysicalAddress + *NumberOfBytes) > SIZE_4GB)) {
210 //
211 // If the root bridge or the device cannot handle performing DMA above
212 // 4GB but any part of the DMA transfer being mapped is above 4GB, then
213 // map the DMA transfer to a buffer below 4GB.
214 //
215 NeedRemap = TRUE;
216 DmaMemoryTop = MIN (DmaMemoryTop, SIZE_4GB - 1);
217 }
218
219 if (Operation == EdkiiIoMmuOperationBusMasterCommonBuffer ||
220 Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) {
221 if (NeedRemap) {
222 //
223 // Common Buffer operations can not be remapped. If the common buffer
224 // is above 4GB, then it is not possible to generate a mapping, so return
225 // an error.
226 //
227 DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_UNSUPPORTED));
228 return EFI_UNSUPPORTED;
229 }
230 }
231
232 //
233 // Allocate a MAP_INFO structure to remember the mapping when Unmap() is
234 // called later.
235 //
236 MapInfo = AllocatePool (sizeof (MAP_INFO));
237 if (MapInfo == NULL) {
238 *NumberOfBytes = 0;
239 DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_OUT_OF_RESOURCES));
240 return EFI_OUT_OF_RESOURCES;
241 }
242
243 //
244 // Initialize the MAP_INFO structure
245 //
246 MapInfo->Signature = MAP_INFO_SIGNATURE;
247 MapInfo->Operation = Operation;
248 MapInfo->NumberOfBytes = *NumberOfBytes;
249 MapInfo->NumberOfPages = EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes);
250 MapInfo->HostAddress = PhysicalAddress;
251 MapInfo->DeviceAddress = DmaMemoryTop;
252 InitializeListHead(&MapInfo->HandleList);
253
254 //
255 // Allocate a buffer below 4GB to map the transfer to.
256 //
257 if (NeedRemap) {
258 Status = gBS->AllocatePages (
259 AllocateMaxAddress,
260 EfiBootServicesData,
261 MapInfo->NumberOfPages,
262 &MapInfo->DeviceAddress
263 );
264 if (EFI_ERROR (Status)) {
265 FreePool (MapInfo);
266 *NumberOfBytes = 0;
267 DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", Status));
268 return Status;
269 }
270
271 //
272 // If this is a read operation from the Bus Master's point of view,
273 // then copy the contents of the real buffer into the mapped buffer
274 // so the Bus Master can read the contents of the real buffer.
275 //
276 if (Operation == EdkiiIoMmuOperationBusMasterRead ||
277 Operation == EdkiiIoMmuOperationBusMasterRead64) {
278 CopyMem (
279 (VOID *) (UINTN) MapInfo->DeviceAddress,
280 (VOID *) (UINTN) MapInfo->HostAddress,
281 MapInfo->NumberOfBytes
282 );
283 }
284 } else {
285 MapInfo->DeviceAddress = MapInfo->HostAddress;
286 }
287
288 OriginalTpl = gBS->RaiseTPL (VTD_TPL_LEVEL);
289 InsertTailList (&gMaps, &MapInfo->Link);
290 gBS->RestoreTPL (OriginalTpl);
291
292 //
293 // The DeviceAddress is the address of the maped buffer below 4GB
294 //
295 *DeviceAddress = MapInfo->DeviceAddress;
296 //
297 // Return a pointer to the MAP_INFO structure in Mapping
298 //
299 *Mapping = MapInfo;
300
301 DEBUG ((DEBUG_VERBOSE, "IoMmuMap: 0x%08x - 0x%08x <==\n", *DeviceAddress, *Mapping));
302
303 return EFI_SUCCESS;
304 }
305
306 /**
307 Completes the Map() operation and releases any corresponding resources.
308
309 @param This The protocol instance pointer.
310 @param Mapping The mapping value returned from Map().
311
312 @retval EFI_SUCCESS The range was unmapped.
313 @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().
314 @retval EFI_DEVICE_ERROR The data was not committed to the target system memory.
315 **/
316 EFI_STATUS
317 EFIAPI
318 IoMmuUnmap (
319 IN EDKII_IOMMU_PROTOCOL *This,
320 IN VOID *Mapping
321 )
322 {
323 MAP_INFO *MapInfo;
324 MAP_HANDLE_INFO *MapHandleInfo;
325 LIST_ENTRY *Link;
326 EFI_TPL OriginalTpl;
327
328 DEBUG ((DEBUG_VERBOSE, "IoMmuUnmap: 0x%08x\n", Mapping));
329
330 if (Mapping == NULL) {
331 DEBUG ((DEBUG_ERROR, "IoMmuUnmap: %r\n", EFI_INVALID_PARAMETER));
332 return EFI_INVALID_PARAMETER;
333 }
334
335 OriginalTpl = gBS->RaiseTPL (VTD_TPL_LEVEL);
336 MapInfo = NULL;
337 for (Link = GetFirstNode (&gMaps)
338 ; !IsNull (&gMaps, Link)
339 ; Link = GetNextNode (&gMaps, Link)
340 ) {
341 MapInfo = MAP_INFO_FROM_LINK (Link);
342 if (MapInfo == Mapping) {
343 break;
344 }
345 }
346 //
347 // Mapping is not a valid value returned by Map()
348 //
349 if (MapInfo != Mapping) {
350 gBS->RestoreTPL (OriginalTpl);
351 DEBUG ((DEBUG_ERROR, "IoMmuUnmap: %r\n", EFI_INVALID_PARAMETER));
352 return EFI_INVALID_PARAMETER;
353 }
354 RemoveEntryList (&MapInfo->Link);
355 gBS->RestoreTPL (OriginalTpl);
356
357 //
358 // remove all nodes in MapInfo->HandleList
359 //
360 while (!IsListEmpty (&MapInfo->HandleList)) {
361 MapHandleInfo = MAP_HANDLE_INFO_FROM_LINK (MapInfo->HandleList.ForwardLink);
362 RemoveEntryList (&MapHandleInfo->Link);
363 FreePool (MapHandleInfo);
364 }
365
366 if (MapInfo->DeviceAddress != MapInfo->HostAddress) {
367 //
368 // If this is a write operation from the Bus Master's point of view,
369 // then copy the contents of the mapped buffer into the real buffer
370 // so the processor can read the contents of the real buffer.
371 //
372 if (MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite ||
373 MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite64) {
374 CopyMem (
375 (VOID *) (UINTN) MapInfo->HostAddress,
376 (VOID *) (UINTN) MapInfo->DeviceAddress,
377 MapInfo->NumberOfBytes
378 );
379 }
380
381 //
382 // Free the mapped buffer and the MAP_INFO structure.
383 //
384 gBS->FreePages (MapInfo->DeviceAddress, MapInfo->NumberOfPages);
385 }
386
387 FreePool (Mapping);
388 return EFI_SUCCESS;
389 }
390
391 /**
392 Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
393 OperationBusMasterCommonBuffer64 mapping.
394
395 @param This The protocol instance pointer.
396 @param Type This parameter is not used and must be ignored.
397 @param MemoryType The type of memory to allocate, EfiBootServicesData or
398 EfiRuntimeServicesData.
399 @param Pages The number of pages to allocate.
400 @param HostAddress A pointer to store the base system memory address of the
401 allocated range.
402 @param Attributes The requested bit mask of attributes for the allocated range.
403
404 @retval EFI_SUCCESS The requested memory pages were allocated.
405 @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are
406 MEMORY_WRITE_COMBINE, MEMORY_CACHED and DUAL_ADDRESS_CYCLE.
407 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
408 @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.
409
410 **/
411 EFI_STATUS
412 EFIAPI
413 IoMmuAllocateBuffer (
414 IN EDKII_IOMMU_PROTOCOL *This,
415 IN EFI_ALLOCATE_TYPE Type,
416 IN EFI_MEMORY_TYPE MemoryType,
417 IN UINTN Pages,
418 IN OUT VOID **HostAddress,
419 IN UINT64 Attributes
420 )
421 {
422 EFI_STATUS Status;
423 EFI_PHYSICAL_ADDRESS PhysicalAddress;
424
425 DEBUG ((DEBUG_VERBOSE, "IoMmuAllocateBuffer: ==> 0x%08x\n", Pages));
426
427 //
428 // Validate Attributes
429 //
430 if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) {
431 DEBUG ((DEBUG_ERROR, "IoMmuAllocateBuffer: %r\n", EFI_UNSUPPORTED));
432 return EFI_UNSUPPORTED;
433 }
434
435 //
436 // Check for invalid inputs
437 //
438 if (HostAddress == NULL) {
439 DEBUG ((DEBUG_ERROR, "IoMmuAllocateBuffer: %r\n", EFI_INVALID_PARAMETER));
440 return EFI_INVALID_PARAMETER;
441 }
442
443 //
444 // The only valid memory types are EfiBootServicesData and
445 // EfiRuntimeServicesData
446 //
447 if (MemoryType != EfiBootServicesData &&
448 MemoryType != EfiRuntimeServicesData) {
449 DEBUG ((DEBUG_ERROR, "IoMmuAllocateBuffer: %r\n", EFI_INVALID_PARAMETER));
450 return EFI_INVALID_PARAMETER;
451 }
452
453 PhysicalAddress = DMA_MEMORY_TOP;
454 if ((Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0) {
455 //
456 // Limit allocations to memory below 4GB
457 //
458 PhysicalAddress = MIN (PhysicalAddress, SIZE_4GB - 1);
459 }
460 Status = gBS->AllocatePages (
461 AllocateMaxAddress,
462 MemoryType,
463 Pages,
464 &PhysicalAddress
465 );
466 if (!EFI_ERROR (Status)) {
467 *HostAddress = (VOID *) (UINTN) PhysicalAddress;
468 }
469
470 DEBUG ((DEBUG_VERBOSE, "IoMmuAllocateBuffer: 0x%08x <==\n", *HostAddress));
471
472 return Status;
473 }
474
475 /**
476 Frees memory that was allocated with AllocateBuffer().
477
478 @param This The protocol instance pointer.
479 @param Pages The number of pages to free.
480 @param HostAddress The base system memory address of the allocated range.
481
482 @retval EFI_SUCCESS The requested memory pages were freed.
483 @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages
484 was not allocated with AllocateBuffer().
485
486 **/
487 EFI_STATUS
488 EFIAPI
489 IoMmuFreeBuffer (
490 IN EDKII_IOMMU_PROTOCOL *This,
491 IN UINTN Pages,
492 IN VOID *HostAddress
493 )
494 {
495 DEBUG ((DEBUG_VERBOSE, "IoMmuFreeBuffer: 0x%\n", Pages));
496 return gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress, Pages);
497 }
498
499 /**
500 Get device information from mapping.
501
502 @param[in] Mapping The mapping.
503 @param[out] DeviceAddress The device address of the mapping.
504 @param[out] NumberOfPages The number of pages of the mapping.
505
506 @retval EFI_SUCCESS The device information is returned.
507 @retval EFI_INVALID_PARAMETER The mapping is invalid.
508 **/
509 EFI_STATUS
510 GetDeviceInfoFromMapping (
511 IN VOID *Mapping,
512 OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
513 OUT UINTN *NumberOfPages
514 )
515 {
516 MAP_INFO *MapInfo;
517 LIST_ENTRY *Link;
518
519 if (Mapping == NULL) {
520 return EFI_INVALID_PARAMETER;
521 }
522
523 MapInfo = NULL;
524 for (Link = GetFirstNode (&gMaps)
525 ; !IsNull (&gMaps, Link)
526 ; Link = GetNextNode (&gMaps, Link)
527 ) {
528 MapInfo = MAP_INFO_FROM_LINK (Link);
529 if (MapInfo == Mapping) {
530 break;
531 }
532 }
533 //
534 // Mapping is not a valid value returned by Map()
535 //
536 if (MapInfo != Mapping) {
537 return EFI_INVALID_PARAMETER;
538 }
539
540 *DeviceAddress = MapInfo->DeviceAddress;
541 *NumberOfPages = MapInfo->NumberOfPages;
542 return EFI_SUCCESS;
543 }
544