• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   Generic ARM implementation of DmaLib.h
3 
4   Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>
5 
6   This program and the accompanying materials
7   are licensed and made available under the terms and conditions of the BSD License
8   which accompanies this distribution.  The full text of the license may be found at
9   http://opensource.org/licenses/bsd-license.php
10 
11   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 
14 **/
15 
16 #include <PiDxe.h>
17 #include <Library/DebugLib.h>
18 #include <Library/DmaLib.h>
19 #include <Library/DxeServicesTableLib.h>
20 #include <Library/MemoryAllocationLib.h>
21 #include <Library/UefiBootServicesTableLib.h>
22 #include <Library/UncachedMemoryAllocationLib.h>
23 #include <Library/IoLib.h>
24 #include <Library/BaseMemoryLib.h>
25 
26 #include <Protocol/Cpu.h>
27 
28 typedef struct {
29   EFI_PHYSICAL_ADDRESS      HostAddress;
30   VOID                      *BufferAddress;
31   UINTN                     NumberOfBytes;
32   DMA_MAP_OPERATION         Operation;
33   BOOLEAN                   DoubleBuffer;
34 } MAP_INFO_INSTANCE;
35 
36 
37 
38 STATIC EFI_CPU_ARCH_PROTOCOL      *mCpu;
39 
40 STATIC
41 PHYSICAL_ADDRESS
HostToDeviceAddress(IN PHYSICAL_ADDRESS HostAddress)42 HostToDeviceAddress (
43   IN  PHYSICAL_ADDRESS  HostAddress
44   )
45 {
46   return HostAddress + PcdGet64 (PcdArmDmaDeviceOffset);
47 }
48 
49 /**
50   Provides the DMA controller-specific addresses needed to access system memory.
51 
52   Operation is relative to the DMA bus master.
53 
54   @param  Operation             Indicates if the bus master is going to read or write to system memory.
55   @param  HostAddress           The system memory address to map to the DMA controller.
56   @param  NumberOfBytes         On input the number of bytes to map. On output the number of bytes
57                                 that were mapped.
58   @param  DeviceAddress         The resulting map address for the bus master controller to use to
59                                 access the hosts HostAddress.
60   @param  Mapping               A resulting value to pass to Unmap().
61 
62   @retval EFI_SUCCESS           The range was mapped for the returned NumberOfBytes.
63   @retval EFI_UNSUPPORTED       The HostAddress cannot be mapped as a common buffer.
64   @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
65   @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.
66   @retval EFI_DEVICE_ERROR      The system hardware could not map the requested address.
67 
68 **/
69 EFI_STATUS
70 EFIAPI
DmaMap(IN DMA_MAP_OPERATION Operation,IN VOID * HostAddress,IN OUT UINTN * NumberOfBytes,OUT PHYSICAL_ADDRESS * DeviceAddress,OUT VOID ** Mapping)71 DmaMap (
72   IN     DMA_MAP_OPERATION              Operation,
73   IN     VOID                           *HostAddress,
74   IN OUT UINTN                          *NumberOfBytes,
75   OUT    PHYSICAL_ADDRESS               *DeviceAddress,
76   OUT    VOID                           **Mapping
77   )
78 {
79   EFI_STATUS                      Status;
80   MAP_INFO_INSTANCE               *Map;
81   VOID                            *Buffer;
82   EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor;
83 
84   if (HostAddress == NULL || NumberOfBytes == NULL || DeviceAddress == NULL || Mapping == NULL ) {
85     return EFI_INVALID_PARAMETER;
86   }
87 
88   if (Operation >= MapOperationMaximum) {
89     return EFI_INVALID_PARAMETER;
90   }
91 
92   //
93   // The debug implementation of UncachedMemoryAllocationLib in ArmPkg returns
94   // a virtual uncached alias, and unmaps the cached ID mapping of the buffer,
95   // in order to catch inadvertent references to the cached mapping.
96   // Since HostToDeviceAddress () expects ID mapped input addresses, convert
97   // the host address to an ID mapped address first.
98   //
99   *DeviceAddress = HostToDeviceAddress (ConvertToPhysicalAddress (HostAddress));
100 
101   // Remember range so we can flush on the other side
102   Map = AllocatePool (sizeof (MAP_INFO_INSTANCE));
103   if (Map == NULL) {
104     return  EFI_OUT_OF_RESOURCES;
105   }
106 
107   if ((((UINTN)HostAddress & (mCpu->DmaBufferAlignment - 1)) != 0) ||
108       ((*NumberOfBytes & (mCpu->DmaBufferAlignment - 1)) != 0)) {
109 
110     // Get the cacheability of the region
111     Status = gDS->GetMemorySpaceDescriptor ((UINTN)HostAddress, &GcdDescriptor);
112     if (EFI_ERROR(Status)) {
113       goto FreeMapInfo;
114     }
115 
116     // If the mapped buffer is not an uncached buffer
117     if ((GcdDescriptor.Attributes & (EFI_MEMORY_WB | EFI_MEMORY_WT)) != 0) {
118       //
119       // Operations of type MapOperationBusMasterCommonBuffer are only allowed
120       // on uncached buffers.
121       //
122       if (Operation == MapOperationBusMasterCommonBuffer) {
123         DEBUG ((EFI_D_ERROR,
124           "%a: Operation type 'MapOperationBusMasterCommonBuffer' is only supported\n"
125           "on memory regions that were allocated using DmaAllocateBuffer ()\n",
126           __FUNCTION__));
127         Status = EFI_UNSUPPORTED;
128         goto FreeMapInfo;
129       }
130 
131       //
132       // If the buffer does not fill entire cache lines we must double buffer into
133       // uncached memory. Device (PCI) address becomes uncached page.
134       //
135       Map->DoubleBuffer  = TRUE;
136       Status = DmaAllocateBuffer (EfiBootServicesData, EFI_SIZE_TO_PAGES (*NumberOfBytes), &Buffer);
137       if (EFI_ERROR (Status)) {
138         goto FreeMapInfo;
139       }
140 
141       if (Operation == MapOperationBusMasterRead) {
142         CopyMem (Buffer, HostAddress, *NumberOfBytes);
143       }
144 
145       *DeviceAddress = HostToDeviceAddress (ConvertToPhysicalAddress (Buffer));
146       Map->BufferAddress = Buffer;
147     } else {
148       Map->DoubleBuffer  = FALSE;
149     }
150   } else {
151     Map->DoubleBuffer  = FALSE;
152 
153     DEBUG_CODE_BEGIN ();
154 
155     //
156     // The operation type check above only executes if the buffer happens to be
157     // misaligned with respect to CWG, but even if it is aligned, we should not
158     // allow arbitrary buffers to be used for creating consistent mappings.
159     // So duplicate the check here when running in DEBUG mode, just to assert
160     // that we are not trying to create a consistent mapping for cached memory.
161     //
162     Status = gDS->GetMemorySpaceDescriptor ((UINTN)HostAddress, &GcdDescriptor);
163     ASSERT_EFI_ERROR(Status);
164 
165     ASSERT (Operation != MapOperationBusMasterCommonBuffer ||
166             (GcdDescriptor.Attributes & (EFI_MEMORY_WB | EFI_MEMORY_WT)) == 0);
167 
168     DEBUG_CODE_END ();
169 
170     // Flush the Data Cache (should not have any effect if the memory region is uncached)
171     mCpu->FlushDataCache (mCpu, (UINTN)HostAddress, *NumberOfBytes,
172             EfiCpuFlushTypeWriteBackInvalidate);
173   }
174 
175   Map->HostAddress   = (UINTN)HostAddress;
176   Map->NumberOfBytes = *NumberOfBytes;
177   Map->Operation     = Operation;
178 
179   *Mapping = Map;
180 
181   return EFI_SUCCESS;
182 
183 FreeMapInfo:
184   FreePool (Map);
185 
186   return Status;
187 }
188 
189 
190 /**
191   Completes the DmaMapBusMasterRead(), DmaMapBusMasterWrite(), or DmaMapBusMasterCommonBuffer()
192   operation and releases any corresponding resources.
193 
194   @param  Mapping               The mapping value returned from DmaMap*().
195 
196   @retval EFI_SUCCESS           The range was unmapped.
197   @retval EFI_DEVICE_ERROR      The data was not committed to the target system memory.
198   @retval EFI_INVALID_PARAMETER An inconsistency was detected between the mapping type
199                                 and the DoubleBuffer field
200 
201 **/
202 EFI_STATUS
203 EFIAPI
DmaUnmap(IN VOID * Mapping)204 DmaUnmap (
205   IN  VOID                         *Mapping
206   )
207 {
208   MAP_INFO_INSTANCE *Map;
209   EFI_STATUS        Status;
210 
211   if (Mapping == NULL) {
212     ASSERT (FALSE);
213     return EFI_INVALID_PARAMETER;
214   }
215 
216   Map = (MAP_INFO_INSTANCE *)Mapping;
217 
218   Status = EFI_SUCCESS;
219   if (Map->DoubleBuffer) {
220     ASSERT (Map->Operation != MapOperationBusMasterCommonBuffer);
221 
222     if (Map->Operation == MapOperationBusMasterCommonBuffer) {
223       Status = EFI_INVALID_PARAMETER;
224     } else if (Map->Operation == MapOperationBusMasterWrite) {
225       CopyMem ((VOID *)(UINTN)Map->HostAddress, Map->BufferAddress,
226         Map->NumberOfBytes);
227     }
228 
229     DmaFreeBuffer (EFI_SIZE_TO_PAGES (Map->NumberOfBytes), Map->BufferAddress);
230 
231   } else {
232     if (Map->Operation == MapOperationBusMasterWrite) {
233       //
234       // Make sure we read buffer from uncached memory and not the cache
235       //
236       mCpu->FlushDataCache (mCpu, Map->HostAddress, Map->NumberOfBytes,
237               EfiCpuFlushTypeInvalidate);
238     }
239   }
240 
241   FreePool (Map);
242 
243   return Status;
244 }
245 
246 /**
247   Allocates pages that are suitable for an DmaMap() of type MapOperationBusMasterCommonBuffer.
248   mapping.
249 
250   @param  MemoryType            The type of memory to allocate, EfiBootServicesData or
251                                 EfiRuntimeServicesData.
252   @param  Pages                 The number of pages to allocate.
253   @param  HostAddress           A pointer to store the base system memory address of the
254                                 allocated range.
255 
256                                 @retval EFI_SUCCESS           The requested memory pages were allocated.
257   @retval EFI_UNSUPPORTED       Attributes is unsupported. The only legal attribute bits are
258                                 MEMORY_WRITE_COMBINE and MEMORY_CACHED.
259   @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
260   @retval EFI_OUT_OF_RESOURCES  The memory pages could not be allocated.
261 
262 **/
263 EFI_STATUS
264 EFIAPI
DmaAllocateBuffer(IN EFI_MEMORY_TYPE MemoryType,IN UINTN Pages,OUT VOID ** HostAddress)265 DmaAllocateBuffer (
266   IN  EFI_MEMORY_TYPE              MemoryType,
267   IN  UINTN                        Pages,
268   OUT VOID                         **HostAddress
269   )
270 {
271   VOID    *Allocation;
272 
273   if (HostAddress == NULL) {
274     return EFI_INVALID_PARAMETER;
275   }
276 
277   //
278   // The only valid memory types are EfiBootServicesData and EfiRuntimeServicesData
279   //
280   // We used uncached memory to keep coherency
281   //
282   if (MemoryType == EfiBootServicesData) {
283     Allocation = UncachedAllocatePages (Pages);
284   } else if (MemoryType == EfiRuntimeServicesData) {
285     Allocation = UncachedAllocateRuntimePages (Pages);
286   } else {
287     return EFI_INVALID_PARAMETER;
288   }
289 
290   if (Allocation == NULL) {
291     return EFI_OUT_OF_RESOURCES;
292   }
293 
294   *HostAddress = Allocation;
295 
296   return EFI_SUCCESS;
297 }
298 
299 
300 /**
301   Frees memory that was allocated with DmaAllocateBuffer().
302 
303   @param  Pages                 The number of pages to free.
304   @param  HostAddress           The base system memory address of the allocated range.
305 
306   @retval EFI_SUCCESS           The requested memory pages were freed.
307   @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages
308                                 was not allocated with DmaAllocateBuffer().
309 
310 **/
311 EFI_STATUS
312 EFIAPI
DmaFreeBuffer(IN UINTN Pages,IN VOID * HostAddress)313 DmaFreeBuffer (
314   IN  UINTN                        Pages,
315   IN  VOID                         *HostAddress
316   )
317 {
318   if (HostAddress == NULL) {
319      return EFI_INVALID_PARAMETER;
320   }
321 
322   UncachedFreePages (HostAddress, Pages);
323   return EFI_SUCCESS;
324 }
325 
326 
327 EFI_STATUS
328 EFIAPI
ArmDmaLibConstructor(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)329 ArmDmaLibConstructor (
330   IN EFI_HANDLE       ImageHandle,
331   IN EFI_SYSTEM_TABLE *SystemTable
332   )
333 {
334   EFI_STATUS              Status;
335 
336   // Get the Cpu protocol for later use
337   Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&mCpu);
338   ASSERT_EFI_ERROR(Status);
339 
340   return Status;
341 }
342 
343