4 Copyright (c) 2017, 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.
17 #include <Protocol/IoMmu.h>
19 #include <Library/BaseLib.h>
20 #include <Library/DebugLib.h>
21 #include <Library/BaseMemoryLib.h>
22 #include <Library/MemoryAllocationLib.h>
23 #include <Library/UefiBootServicesTableLib.h>
25 // TBD: May make it a policy
26 #define DMA_MEMORY_TOP MAX_UINTN
27 //#define DMA_MEMORY_TOP 0x0000000001FFFFFFULL
29 #define MAP_INFO_SIGNATURE SIGNATURE_32 ('D', 'M', 'A', 'P')
33 EDKII_IOMMU_OPERATION Operation
;
36 EFI_PHYSICAL_ADDRESS HostAddress
;
37 EFI_PHYSICAL_ADDRESS DeviceAddress
;
39 #define MAP_INFO_FROM_LINK(a) CR (a, MAP_INFO, Link, MAP_INFO_SIGNATURE)
41 LIST_ENTRY gMaps
= INITIALIZE_LIST_HEAD_VARIABLE(gMaps
);
44 Provides the controller-specific addresses required to access system memory from a
47 @param This The protocol instance pointer.
48 @param Operation Indicates if the bus master is going to read or write to system memory.
49 @param HostAddress The system memory address to map to the PCI controller.
50 @param NumberOfBytes On input the number of bytes to map. On output the number of bytes
52 @param DeviceAddress The resulting map address for the bus master PCI controller to use to
53 access the hosts HostAddress.
54 @param Mapping A resulting value to pass to Unmap().
56 @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes.
57 @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer.
58 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
59 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
60 @retval EFI_DEVICE_ERROR The system hardware could not map the requested address.
66 IN EDKII_IOMMU_PROTOCOL
*This
,
67 IN EDKII_IOMMU_OPERATION Operation
,
69 IN OUT UINTN
*NumberOfBytes
,
70 OUT EFI_PHYSICAL_ADDRESS
*DeviceAddress
,
75 EFI_PHYSICAL_ADDRESS PhysicalAddress
;
77 EFI_PHYSICAL_ADDRESS DmaMemoryTop
;
80 if (NumberOfBytes
== NULL
|| DeviceAddress
== NULL
||
82 DEBUG ((DEBUG_ERROR
, "IoMmuMap: %r\n", EFI_INVALID_PARAMETER
));
83 return EFI_INVALID_PARAMETER
;
86 DEBUG ((DEBUG_VERBOSE
, "IoMmuMap: ==> 0x%08x - 0x%08x (%x)\n", HostAddress
, *NumberOfBytes
, Operation
));
89 // Make sure that Operation is valid
91 if ((UINT32
) Operation
>= EdkiiIoMmuOperationMaximum
) {
92 DEBUG ((DEBUG_ERROR
, "IoMmuMap: %r\n", EFI_INVALID_PARAMETER
));
93 return EFI_INVALID_PARAMETER
;
96 PhysicalAddress
= (EFI_PHYSICAL_ADDRESS
) (UINTN
) HostAddress
;
98 DmaMemoryTop
= DMA_MEMORY_TOP
;
103 if ((*NumberOfBytes
!= ALIGN_VALUE(*NumberOfBytes
, SIZE_4KB
)) ||
104 (PhysicalAddress
!= ALIGN_VALUE(PhysicalAddress
, SIZE_4KB
))) {
105 if ((Operation
== EdkiiIoMmuOperationBusMasterCommonBuffer
) ||
106 (Operation
== EdkiiIoMmuOperationBusMasterCommonBuffer64
)) {
108 // The input buffer might be a subset from IoMmuAllocateBuffer.
116 if ((PhysicalAddress
+ *NumberOfBytes
) >= DMA_MEMORY_TOP
) {
120 if (((Operation
!= EdkiiIoMmuOperationBusMasterRead64
&&
121 Operation
!= EdkiiIoMmuOperationBusMasterWrite64
&&
122 Operation
!= EdkiiIoMmuOperationBusMasterCommonBuffer64
)) &&
123 ((PhysicalAddress
+ *NumberOfBytes
) > SIZE_4GB
)) {
125 // If the root bridge or the device cannot handle performing DMA above
126 // 4GB but any part of the DMA transfer being mapped is above 4GB, then
127 // map the DMA transfer to a buffer below 4GB.
130 DmaMemoryTop
= MIN (DmaMemoryTop
, SIZE_4GB
- 1);
133 if (Operation
== EdkiiIoMmuOperationBusMasterCommonBuffer
||
134 Operation
== EdkiiIoMmuOperationBusMasterCommonBuffer64
) {
137 // Common Buffer operations can not be remapped. If the common buffer
138 // is above 4GB, then it is not possible to generate a mapping, so return
141 DEBUG ((DEBUG_ERROR
, "IoMmuMap: %r\n", EFI_UNSUPPORTED
));
142 return EFI_UNSUPPORTED
;
147 // Allocate a MAP_INFO structure to remember the mapping when Unmap() is
150 MapInfo
= AllocatePool (sizeof (MAP_INFO
));
151 if (MapInfo
== NULL
) {
153 DEBUG ((DEBUG_ERROR
, "IoMmuMap: %r\n", EFI_OUT_OF_RESOURCES
));
154 return EFI_OUT_OF_RESOURCES
;
158 // Initialize the MAP_INFO structure
160 MapInfo
->Signature
= MAP_INFO_SIGNATURE
;
161 MapInfo
->Operation
= Operation
;
162 MapInfo
->NumberOfBytes
= *NumberOfBytes
;
163 MapInfo
->NumberOfPages
= EFI_SIZE_TO_PAGES (MapInfo
->NumberOfBytes
);
164 MapInfo
->HostAddress
= PhysicalAddress
;
165 MapInfo
->DeviceAddress
= DmaMemoryTop
;
168 // Allocate a buffer below 4GB to map the transfer to.
171 Status
= gBS
->AllocatePages (
174 MapInfo
->NumberOfPages
,
175 &MapInfo
->DeviceAddress
177 if (EFI_ERROR (Status
)) {
180 DEBUG ((DEBUG_ERROR
, "IoMmuMap: %r\n", Status
));
185 // If this is a read operation from the Bus Master's point of view,
186 // then copy the contents of the real buffer into the mapped buffer
187 // so the Bus Master can read the contents of the real buffer.
189 if (Operation
== EdkiiIoMmuOperationBusMasterRead
||
190 Operation
== EdkiiIoMmuOperationBusMasterRead64
) {
192 (VOID
*) (UINTN
) MapInfo
->DeviceAddress
,
193 (VOID
*) (UINTN
) MapInfo
->HostAddress
,
194 MapInfo
->NumberOfBytes
198 MapInfo
->DeviceAddress
= MapInfo
->HostAddress
;
201 InsertTailList (&gMaps
, &MapInfo
->Link
);
204 // The DeviceAddress is the address of the maped buffer below 4GB
206 *DeviceAddress
= MapInfo
->DeviceAddress
;
208 // Return a pointer to the MAP_INFO structure in Mapping
212 DEBUG ((DEBUG_VERBOSE
, "IoMmuMap: 0x%08x - 0x%08x <==\n", *DeviceAddress
, *Mapping
));
218 Completes the Map() operation and releases any corresponding resources.
220 @param This The protocol instance pointer.
221 @param Mapping The mapping value returned from Map().
223 @retval EFI_SUCCESS The range was unmapped.
224 @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().
225 @retval EFI_DEVICE_ERROR The data was not committed to the target system memory.
230 IN EDKII_IOMMU_PROTOCOL
*This
,
237 DEBUG ((DEBUG_VERBOSE
, "IoMmuUnmap: 0x%08x\n", Mapping
));
239 if (Mapping
== NULL
) {
240 DEBUG ((DEBUG_ERROR
, "IoMmuUnmap: %r\n", EFI_INVALID_PARAMETER
));
241 return EFI_INVALID_PARAMETER
;
245 for (Link
= GetFirstNode (&gMaps
)
246 ; !IsNull (&gMaps
, Link
)
247 ; Link
= GetNextNode (&gMaps
, Link
)
249 MapInfo
= MAP_INFO_FROM_LINK (Link
);
250 if (MapInfo
== Mapping
) {
255 // Mapping is not a valid value returned by Map()
257 if (MapInfo
!= Mapping
) {
258 DEBUG ((DEBUG_ERROR
, "IoMmuUnmap: %r\n", EFI_INVALID_PARAMETER
));
259 return EFI_INVALID_PARAMETER
;
261 RemoveEntryList (&MapInfo
->Link
);
263 if (MapInfo
->DeviceAddress
!= MapInfo
->HostAddress
) {
265 // If this is a write operation from the Bus Master's point of view,
266 // then copy the contents of the mapped buffer into the real buffer
267 // so the processor can read the contents of the real buffer.
269 if (MapInfo
->Operation
== EdkiiIoMmuOperationBusMasterWrite
||
270 MapInfo
->Operation
== EdkiiIoMmuOperationBusMasterWrite64
) {
272 (VOID
*) (UINTN
) MapInfo
->HostAddress
,
273 (VOID
*) (UINTN
) MapInfo
->DeviceAddress
,
274 MapInfo
->NumberOfBytes
279 // Free the mapped buffer and the MAP_INFO structure.
281 gBS
->FreePages (MapInfo
->DeviceAddress
, MapInfo
->NumberOfPages
);
289 Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
290 OperationBusMasterCommonBuffer64 mapping.
292 @param This The protocol instance pointer.
293 @param Type This parameter is not used and must be ignored.
294 @param MemoryType The type of memory to allocate, EfiBootServicesData or
295 EfiRuntimeServicesData.
296 @param Pages The number of pages to allocate.
297 @param HostAddress A pointer to store the base system memory address of the
299 @param Attributes The requested bit mask of attributes for the allocated range.
301 @retval EFI_SUCCESS The requested memory pages were allocated.
302 @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are
303 MEMORY_WRITE_COMBINE and MEMORY_CACHED.
304 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
305 @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.
310 IoMmuAllocateBuffer (
311 IN EDKII_IOMMU_PROTOCOL
*This
,
312 IN EFI_ALLOCATE_TYPE Type
,
313 IN EFI_MEMORY_TYPE MemoryType
,
315 IN OUT VOID
**HostAddress
,
320 EFI_PHYSICAL_ADDRESS PhysicalAddress
;
322 DEBUG ((DEBUG_VERBOSE
, "IoMmuAllocateBuffer: ==> 0x%08x\n", Pages
));
325 // Validate Attributes
327 if ((Attributes
& EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER
) != 0) {
328 DEBUG ((DEBUG_ERROR
, "IoMmuAllocateBuffer: %r\n", EFI_UNSUPPORTED
));
329 return EFI_UNSUPPORTED
;
333 // Check for invalid inputs
335 if (HostAddress
== NULL
) {
336 DEBUG ((DEBUG_ERROR
, "IoMmuAllocateBuffer: %r\n", EFI_INVALID_PARAMETER
));
337 return EFI_INVALID_PARAMETER
;
341 // The only valid memory types are EfiBootServicesData and
342 // EfiRuntimeServicesData
344 if (MemoryType
!= EfiBootServicesData
&&
345 MemoryType
!= EfiRuntimeServicesData
) {
346 DEBUG ((DEBUG_ERROR
, "IoMmuAllocateBuffer: %r\n", EFI_INVALID_PARAMETER
));
347 return EFI_INVALID_PARAMETER
;
350 PhysicalAddress
= DMA_MEMORY_TOP
;
351 if ((Attributes
& EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE
) == 0) {
353 // Limit allocations to memory below 4GB
355 PhysicalAddress
= MIN (PhysicalAddress
, SIZE_4GB
- 1);
357 Status
= gBS
->AllocatePages (
363 if (!EFI_ERROR (Status
)) {
364 *HostAddress
= (VOID
*) (UINTN
) PhysicalAddress
;
367 DEBUG ((DEBUG_VERBOSE
, "IoMmuAllocateBuffer: 0x%08x <==\n", *HostAddress
));
373 Frees memory that was allocated with AllocateBuffer().
375 @param This The protocol instance pointer.
376 @param Pages The number of pages to free.
377 @param HostAddress The base system memory address of the allocated range.
379 @retval EFI_SUCCESS The requested memory pages were freed.
380 @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages
381 was not allocated with AllocateBuffer().
387 IN EDKII_IOMMU_PROTOCOL
*This
,
392 DEBUG ((DEBUG_VERBOSE
, "IoMmuFreeBuffer: 0x%\n", Pages
));
393 return gBS
->FreePages ((EFI_PHYSICAL_ADDRESS
) (UINTN
) HostAddress
, Pages
);
397 Get device information from mapping.
399 @param[in] Mapping The mapping.
400 @param[out] DeviceAddress The device address of the mapping.
401 @param[out] NumberOfPages The number of pages of the mapping.
403 @retval EFI_SUCCESS The device information is returned.
404 @retval EFI_INVALID_PARAMETER The mapping is invalid.
407 GetDeviceInfoFromMapping (
409 OUT EFI_PHYSICAL_ADDRESS
*DeviceAddress
,
410 OUT UINTN
*NumberOfPages
416 if (Mapping
== NULL
) {
417 return EFI_INVALID_PARAMETER
;
421 for (Link
= GetFirstNode (&gMaps
)
422 ; !IsNull (&gMaps
, Link
)
423 ; Link
= GetNextNode (&gMaps
, Link
)
425 MapInfo
= MAP_INFO_FROM_LINK (Link
);
426 if (MapInfo
== Mapping
) {
431 // Mapping is not a valid value returned by Map()
433 if (MapInfo
!= Mapping
) {
434 return EFI_INVALID_PARAMETER
;
437 *DeviceAddress
= MapInfo
->DeviceAddress
;
438 *NumberOfPages
= MapInfo
->NumberOfPages
;