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
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.
15 #include "DmaProtection.h"
17 // TBD: May make it a policy
18 #define DMA_MEMORY_TOP MAX_UINTN
19 //#define DMA_MEMORY_TOP 0x0000000001FFFFFFULL
21 #define MAP_HANDLE_INFO_SIGNATURE SIGNATURE_32 ('H', 'M', 'A', 'P')
25 EFI_HANDLE DeviceHandle
;
28 #define MAP_HANDLE_INFO_FROM_LINK(a) CR (a, MAP_HANDLE_INFO, Link, MAP_HANDLE_INFO_SIGNATURE)
30 #define MAP_INFO_SIGNATURE SIGNATURE_32 ('D', 'M', 'A', 'P')
34 EDKII_IOMMU_OPERATION Operation
;
37 EFI_PHYSICAL_ADDRESS HostAddress
;
38 EFI_PHYSICAL_ADDRESS DeviceAddress
;
39 LIST_ENTRY HandleList
;
41 #define MAP_INFO_FROM_LINK(a) CR (a, MAP_INFO, Link, MAP_INFO_SIGNATURE)
43 LIST_ENTRY gMaps
= INITIALIZE_LIST_HEAD_VARIABLE(gMaps
);
46 This function fills DeviceHandle/IoMmuAccess to the MAP_HANDLE_INFO,
47 based upon the DeviceAddress.
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.
56 SyncDeviceHandleToMapInfo (
57 IN EFI_HANDLE DeviceHandle
,
58 IN EFI_PHYSICAL_ADDRESS DeviceAddress
,
64 MAP_HANDLE_INFO
*MapHandleInfo
;
69 // Find MapInfo according to DeviceAddress
71 OriginalTpl
= gBS
->RaiseTPL (VTD_TPL_LEVEL
);
73 for (Link
= GetFirstNode (&gMaps
)
74 ; !IsNull (&gMaps
, Link
)
75 ; Link
= GetNextNode (&gMaps
, Link
)
77 MapInfo
= MAP_INFO_FROM_LINK (Link
);
78 if (MapInfo
->DeviceAddress
== DeviceAddress
) {
82 if ((MapInfo
== NULL
) || (MapInfo
->DeviceAddress
!= DeviceAddress
)) {
83 DEBUG ((DEBUG_ERROR
, "SyncDeviceHandleToMapInfo: DeviceAddress(0x%lx) - not found\n", DeviceAddress
));
84 gBS
->RestoreTPL (OriginalTpl
);
89 // Find MapHandleInfo according to DeviceHandle
92 for (Link
= GetFirstNode (&MapInfo
->HandleList
)
93 ; !IsNull (&MapInfo
->HandleList
, Link
)
94 ; Link
= GetNextNode (&MapInfo
->HandleList
, Link
)
96 MapHandleInfo
= MAP_HANDLE_INFO_FROM_LINK (Link
);
97 if (MapHandleInfo
->DeviceHandle
== DeviceHandle
) {
101 if ((MapHandleInfo
!= NULL
) && (MapHandleInfo
->DeviceHandle
== DeviceHandle
)) {
102 MapHandleInfo
->IoMmuAccess
= IoMmuAccess
;
103 gBS
->RestoreTPL (OriginalTpl
);
109 // Initialize and insert the MAP_HANDLE_INFO structure
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
);
118 MapHandleInfo
->Signature
= MAP_HANDLE_INFO_SIGNATURE
;
119 MapHandleInfo
->DeviceHandle
= DeviceHandle
;
120 MapHandleInfo
->IoMmuAccess
= IoMmuAccess
;
122 InsertTailList (&MapInfo
->HandleList
, &MapHandleInfo
->Link
);
123 gBS
->RestoreTPL (OriginalTpl
);
129 Provides the controller-specific addresses required to access system memory from a
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
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().
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.
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
,
160 EFI_PHYSICAL_ADDRESS PhysicalAddress
;
162 EFI_PHYSICAL_ADDRESS DmaMemoryTop
;
166 if (NumberOfBytes
== NULL
|| DeviceAddress
== NULL
||
168 DEBUG ((DEBUG_ERROR
, "IoMmuMap: %r\n", EFI_INVALID_PARAMETER
));
169 return EFI_INVALID_PARAMETER
;
172 DEBUG ((DEBUG_VERBOSE
, "IoMmuMap: ==> 0x%08x - 0x%08x (%x)\n", HostAddress
, *NumberOfBytes
, Operation
));
175 // Make sure that Operation is valid
177 if ((UINT32
) Operation
>= EdkiiIoMmuOperationMaximum
) {
178 DEBUG ((DEBUG_ERROR
, "IoMmuMap: %r\n", EFI_INVALID_PARAMETER
));
179 return EFI_INVALID_PARAMETER
;
182 PhysicalAddress
= (EFI_PHYSICAL_ADDRESS
) (UINTN
) HostAddress
;
184 DmaMemoryTop
= DMA_MEMORY_TOP
;
189 if ((*NumberOfBytes
!= ALIGN_VALUE(*NumberOfBytes
, SIZE_4KB
)) ||
190 (PhysicalAddress
!= ALIGN_VALUE(PhysicalAddress
, SIZE_4KB
))) {
191 if ((Operation
== EdkiiIoMmuOperationBusMasterCommonBuffer
) ||
192 (Operation
== EdkiiIoMmuOperationBusMasterCommonBuffer64
)) {
194 // The input buffer might be a subset from IoMmuAllocateBuffer.
202 if ((PhysicalAddress
+ *NumberOfBytes
) >= DMA_MEMORY_TOP
) {
206 if (((Operation
!= EdkiiIoMmuOperationBusMasterRead64
&&
207 Operation
!= EdkiiIoMmuOperationBusMasterWrite64
&&
208 Operation
!= EdkiiIoMmuOperationBusMasterCommonBuffer64
)) &&
209 ((PhysicalAddress
+ *NumberOfBytes
) > SIZE_4GB
)) {
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.
216 DmaMemoryTop
= MIN (DmaMemoryTop
, SIZE_4GB
- 1);
219 if (Operation
== EdkiiIoMmuOperationBusMasterCommonBuffer
||
220 Operation
== EdkiiIoMmuOperationBusMasterCommonBuffer64
) {
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
227 DEBUG ((DEBUG_ERROR
, "IoMmuMap: %r\n", EFI_UNSUPPORTED
));
228 return EFI_UNSUPPORTED
;
233 // Allocate a MAP_INFO structure to remember the mapping when Unmap() is
236 MapInfo
= AllocatePool (sizeof (MAP_INFO
));
237 if (MapInfo
== NULL
) {
239 DEBUG ((DEBUG_ERROR
, "IoMmuMap: %r\n", EFI_OUT_OF_RESOURCES
));
240 return EFI_OUT_OF_RESOURCES
;
244 // Initialize the MAP_INFO structure
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
);
255 // Allocate a buffer below 4GB to map the transfer to.
258 Status
= gBS
->AllocatePages (
261 MapInfo
->NumberOfPages
,
262 &MapInfo
->DeviceAddress
264 if (EFI_ERROR (Status
)) {
267 DEBUG ((DEBUG_ERROR
, "IoMmuMap: %r\n", Status
));
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.
276 if (Operation
== EdkiiIoMmuOperationBusMasterRead
||
277 Operation
== EdkiiIoMmuOperationBusMasterRead64
) {
279 (VOID
*) (UINTN
) MapInfo
->DeviceAddress
,
280 (VOID
*) (UINTN
) MapInfo
->HostAddress
,
281 MapInfo
->NumberOfBytes
285 MapInfo
->DeviceAddress
= MapInfo
->HostAddress
;
288 OriginalTpl
= gBS
->RaiseTPL (VTD_TPL_LEVEL
);
289 InsertTailList (&gMaps
, &MapInfo
->Link
);
290 gBS
->RestoreTPL (OriginalTpl
);
293 // The DeviceAddress is the address of the maped buffer below 4GB
295 *DeviceAddress
= MapInfo
->DeviceAddress
;
297 // Return a pointer to the MAP_INFO structure in Mapping
301 DEBUG ((DEBUG_VERBOSE
, "IoMmuMap: 0x%08x - 0x%08x <==\n", *DeviceAddress
, *Mapping
));
307 Completes the Map() operation and releases any corresponding resources.
309 @param This The protocol instance pointer.
310 @param Mapping The mapping value returned from Map().
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.
319 IN EDKII_IOMMU_PROTOCOL
*This
,
324 MAP_HANDLE_INFO
*MapHandleInfo
;
328 DEBUG ((DEBUG_VERBOSE
, "IoMmuUnmap: 0x%08x\n", Mapping
));
330 if (Mapping
== NULL
) {
331 DEBUG ((DEBUG_ERROR
, "IoMmuUnmap: %r\n", EFI_INVALID_PARAMETER
));
332 return EFI_INVALID_PARAMETER
;
335 OriginalTpl
= gBS
->RaiseTPL (VTD_TPL_LEVEL
);
337 for (Link
= GetFirstNode (&gMaps
)
338 ; !IsNull (&gMaps
, Link
)
339 ; Link
= GetNextNode (&gMaps
, Link
)
341 MapInfo
= MAP_INFO_FROM_LINK (Link
);
342 if (MapInfo
== Mapping
) {
347 // Mapping is not a valid value returned by Map()
349 if (MapInfo
!= Mapping
) {
350 gBS
->RestoreTPL (OriginalTpl
);
351 DEBUG ((DEBUG_ERROR
, "IoMmuUnmap: %r\n", EFI_INVALID_PARAMETER
));
352 return EFI_INVALID_PARAMETER
;
354 RemoveEntryList (&MapInfo
->Link
);
355 gBS
->RestoreTPL (OriginalTpl
);
358 // remove all nodes in MapInfo->HandleList
360 while (!IsListEmpty (&MapInfo
->HandleList
)) {
361 MapHandleInfo
= MAP_HANDLE_INFO_FROM_LINK (MapInfo
->HandleList
.ForwardLink
);
362 RemoveEntryList (&MapHandleInfo
->Link
);
363 FreePool (MapHandleInfo
);
366 if (MapInfo
->DeviceAddress
!= MapInfo
->HostAddress
) {
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.
372 if (MapInfo
->Operation
== EdkiiIoMmuOperationBusMasterWrite
||
373 MapInfo
->Operation
== EdkiiIoMmuOperationBusMasterWrite64
) {
375 (VOID
*) (UINTN
) MapInfo
->HostAddress
,
376 (VOID
*) (UINTN
) MapInfo
->DeviceAddress
,
377 MapInfo
->NumberOfBytes
382 // Free the mapped buffer and the MAP_INFO structure.
384 gBS
->FreePages (MapInfo
->DeviceAddress
, MapInfo
->NumberOfPages
);
392 Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
393 OperationBusMasterCommonBuffer64 mapping.
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
402 @param Attributes The requested bit mask of attributes for the allocated range.
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.
413 IoMmuAllocateBuffer (
414 IN EDKII_IOMMU_PROTOCOL
*This
,
415 IN EFI_ALLOCATE_TYPE Type
,
416 IN EFI_MEMORY_TYPE MemoryType
,
418 IN OUT VOID
**HostAddress
,
423 EFI_PHYSICAL_ADDRESS PhysicalAddress
;
425 DEBUG ((DEBUG_VERBOSE
, "IoMmuAllocateBuffer: ==> 0x%08x\n", Pages
));
428 // Validate Attributes
430 if ((Attributes
& EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER
) != 0) {
431 DEBUG ((DEBUG_ERROR
, "IoMmuAllocateBuffer: %r\n", EFI_UNSUPPORTED
));
432 return EFI_UNSUPPORTED
;
436 // Check for invalid inputs
438 if (HostAddress
== NULL
) {
439 DEBUG ((DEBUG_ERROR
, "IoMmuAllocateBuffer: %r\n", EFI_INVALID_PARAMETER
));
440 return EFI_INVALID_PARAMETER
;
444 // The only valid memory types are EfiBootServicesData and
445 // EfiRuntimeServicesData
447 if (MemoryType
!= EfiBootServicesData
&&
448 MemoryType
!= EfiRuntimeServicesData
) {
449 DEBUG ((DEBUG_ERROR
, "IoMmuAllocateBuffer: %r\n", EFI_INVALID_PARAMETER
));
450 return EFI_INVALID_PARAMETER
;
453 PhysicalAddress
= DMA_MEMORY_TOP
;
454 if ((Attributes
& EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE
) == 0) {
456 // Limit allocations to memory below 4GB
458 PhysicalAddress
= MIN (PhysicalAddress
, SIZE_4GB
- 1);
460 Status
= gBS
->AllocatePages (
466 if (!EFI_ERROR (Status
)) {
467 *HostAddress
= (VOID
*) (UINTN
) PhysicalAddress
;
470 DEBUG ((DEBUG_VERBOSE
, "IoMmuAllocateBuffer: 0x%08x <==\n", *HostAddress
));
476 Frees memory that was allocated with AllocateBuffer().
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.
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().
490 IN EDKII_IOMMU_PROTOCOL
*This
,
495 DEBUG ((DEBUG_VERBOSE
, "IoMmuFreeBuffer: 0x%\n", Pages
));
496 return gBS
->FreePages ((EFI_PHYSICAL_ADDRESS
) (UINTN
) HostAddress
, Pages
);
500 Get device information from mapping.
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.
506 @retval EFI_SUCCESS The device information is returned.
507 @retval EFI_INVALID_PARAMETER The mapping is invalid.
510 GetDeviceInfoFromMapping (
512 OUT EFI_PHYSICAL_ADDRESS
*DeviceAddress
,
513 OUT UINTN
*NumberOfPages
519 if (Mapping
== NULL
) {
520 return EFI_INVALID_PARAMETER
;
524 for (Link
= GetFirstNode (&gMaps
)
525 ; !IsNull (&gMaps
, Link
)
526 ; Link
= GetNextNode (&gMaps
, Link
)
528 MapInfo
= MAP_INFO_FROM_LINK (Link
);
529 if (MapInfo
== Mapping
) {
534 // Mapping is not a valid value returned by Map()
536 if (MapInfo
!= Mapping
) {
537 return EFI_INVALID_PARAMETER
;
540 *DeviceAddress
= MapInfo
->DeviceAddress
;
541 *NumberOfPages
= MapInfo
->NumberOfPages
;