1 | /** @file
|
---|
2 |
|
---|
3 | Generic non-coherent implementation of DmaLib.h
|
---|
4 |
|
---|
5 | Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>
|
---|
6 | Copyright (c) 2015 - 2017, Linaro, Ltd. All rights reserved.<BR>
|
---|
7 |
|
---|
8 | SPDX-License-Identifier: BSD-2-Clause-Patent
|
---|
9 |
|
---|
10 | **/
|
---|
11 |
|
---|
12 | #include <PiDxe.h>
|
---|
13 | #include <Library/BaseLib.h>
|
---|
14 | #include <Library/DebugLib.h>
|
---|
15 | #include <Library/DmaLib.h>
|
---|
16 | #include <Library/DxeServicesTableLib.h>
|
---|
17 | #include <Library/MemoryAllocationLib.h>
|
---|
18 | #include <Library/UefiBootServicesTableLib.h>
|
---|
19 | #include <Library/IoLib.h>
|
---|
20 | #include <Library/BaseMemoryLib.h>
|
---|
21 |
|
---|
22 | #include <Protocol/Cpu.h>
|
---|
23 |
|
---|
24 | typedef struct {
|
---|
25 | EFI_PHYSICAL_ADDRESS HostAddress;
|
---|
26 | VOID *BufferAddress;
|
---|
27 | UINTN NumberOfBytes;
|
---|
28 | DMA_MAP_OPERATION Operation;
|
---|
29 | BOOLEAN DoubleBuffer;
|
---|
30 | } MAP_INFO_INSTANCE;
|
---|
31 |
|
---|
32 | typedef struct {
|
---|
33 | LIST_ENTRY Link;
|
---|
34 | VOID *HostAddress;
|
---|
35 | UINTN NumPages;
|
---|
36 | UINT64 Attributes;
|
---|
37 | } UNCACHED_ALLOCATION;
|
---|
38 |
|
---|
39 | STATIC EFI_CPU_ARCH_PROTOCOL *mCpu;
|
---|
40 | STATIC LIST_ENTRY UncachedAllocationList;
|
---|
41 |
|
---|
42 | STATIC PHYSICAL_ADDRESS mDmaHostAddressLimit;
|
---|
43 |
|
---|
44 | STATIC
|
---|
45 | PHYSICAL_ADDRESS
|
---|
46 | HostToDeviceAddress (
|
---|
47 | IN VOID *Address
|
---|
48 | )
|
---|
49 | {
|
---|
50 | return (PHYSICAL_ADDRESS)(UINTN)Address + PcdGet64 (PcdDmaDeviceOffset);
|
---|
51 | }
|
---|
52 |
|
---|
53 | /**
|
---|
54 | Allocates one or more 4KB pages of a certain memory type at a specified
|
---|
55 | alignment.
|
---|
56 |
|
---|
57 | Allocates the number of 4KB pages specified by Pages of a certain memory type
|
---|
58 | with an alignment specified by Alignment. The allocated buffer is returned.
|
---|
59 | If Pages is 0, then NULL is returned. If there is not enough memory at the
|
---|
60 | specified alignment remaining to satisfy the request, then NULL is returned.
|
---|
61 | If Alignment is not a power of two and Alignment is not zero, then ASSERT().
|
---|
62 | If Pages plus EFI_SIZE_TO_PAGES (Alignment) overflows, then ASSERT().
|
---|
63 |
|
---|
64 | @param MemoryType The type of memory to allocate.
|
---|
65 | @param Pages The number of 4 KB pages to allocate.
|
---|
66 | @param Alignment The requested alignment of the allocation.
|
---|
67 | Must be a power of two.
|
---|
68 | If Alignment is zero, then byte alignment is
|
---|
69 | used.
|
---|
70 |
|
---|
71 | @return A pointer to the allocated buffer or NULL if allocation fails.
|
---|
72 |
|
---|
73 | **/
|
---|
74 | STATIC
|
---|
75 | VOID *
|
---|
76 | InternalAllocateAlignedPages (
|
---|
77 | IN EFI_MEMORY_TYPE MemoryType,
|
---|
78 | IN UINTN Pages,
|
---|
79 | IN UINTN Alignment
|
---|
80 | )
|
---|
81 | {
|
---|
82 | EFI_STATUS Status;
|
---|
83 | EFI_PHYSICAL_ADDRESS Memory;
|
---|
84 | UINTN AlignedMemory;
|
---|
85 | UINTN AlignmentMask;
|
---|
86 | UINTN UnalignedPages;
|
---|
87 | UINTN RealPages;
|
---|
88 |
|
---|
89 | //
|
---|
90 | // Alignment must be a power of two or zero.
|
---|
91 | //
|
---|
92 | ASSERT ((Alignment & (Alignment - 1)) == 0);
|
---|
93 |
|
---|
94 | if (Pages == 0) {
|
---|
95 | return NULL;
|
---|
96 | }
|
---|
97 |
|
---|
98 | if (Alignment > EFI_PAGE_SIZE) {
|
---|
99 | //
|
---|
100 | // Calculate the total number of pages since alignment is larger than page
|
---|
101 | // size.
|
---|
102 | //
|
---|
103 | AlignmentMask = Alignment - 1;
|
---|
104 | RealPages = Pages + EFI_SIZE_TO_PAGES (Alignment);
|
---|
105 | //
|
---|
106 | // Make sure that Pages plus EFI_SIZE_TO_PAGES (Alignment) does not
|
---|
107 | // overflow.
|
---|
108 | //
|
---|
109 | ASSERT (RealPages > Pages);
|
---|
110 |
|
---|
111 | Memory = mDmaHostAddressLimit;
|
---|
112 | Status = gBS->AllocatePages (
|
---|
113 | AllocateMaxAddress,
|
---|
114 | MemoryType,
|
---|
115 | RealPages,
|
---|
116 | &Memory
|
---|
117 | );
|
---|
118 | if (EFI_ERROR (Status)) {
|
---|
119 | return NULL;
|
---|
120 | }
|
---|
121 |
|
---|
122 | AlignedMemory = ((UINTN)Memory + AlignmentMask) & ~AlignmentMask;
|
---|
123 | UnalignedPages = EFI_SIZE_TO_PAGES (AlignedMemory - (UINTN)Memory);
|
---|
124 | if (UnalignedPages > 0) {
|
---|
125 | //
|
---|
126 | // Free first unaligned page(s).
|
---|
127 | //
|
---|
128 | Status = gBS->FreePages (Memory, UnalignedPages);
|
---|
129 | ASSERT_EFI_ERROR (Status);
|
---|
130 | }
|
---|
131 |
|
---|
132 | Memory = AlignedMemory + EFI_PAGES_TO_SIZE (Pages);
|
---|
133 | UnalignedPages = RealPages - Pages - UnalignedPages;
|
---|
134 | if (UnalignedPages > 0) {
|
---|
135 | //
|
---|
136 | // Free last unaligned page(s).
|
---|
137 | //
|
---|
138 | Status = gBS->FreePages (Memory, UnalignedPages);
|
---|
139 | ASSERT_EFI_ERROR (Status);
|
---|
140 | }
|
---|
141 | } else {
|
---|
142 | //
|
---|
143 | // Do not over-allocate pages in this case.
|
---|
144 | //
|
---|
145 | Memory = mDmaHostAddressLimit;
|
---|
146 | Status = gBS->AllocatePages (
|
---|
147 | AllocateMaxAddress,
|
---|
148 | MemoryType,
|
---|
149 | Pages,
|
---|
150 | &Memory
|
---|
151 | );
|
---|
152 | if (EFI_ERROR (Status)) {
|
---|
153 | return NULL;
|
---|
154 | }
|
---|
155 |
|
---|
156 | AlignedMemory = (UINTN)Memory;
|
---|
157 | }
|
---|
158 |
|
---|
159 | return (VOID *)AlignedMemory;
|
---|
160 | }
|
---|
161 |
|
---|
162 | /**
|
---|
163 | Provides the DMA controller-specific addresses needed to access system memory.
|
---|
164 |
|
---|
165 | Operation is relative to the DMA bus master.
|
---|
166 |
|
---|
167 | @param Operation Indicates if the bus master is going to read or
|
---|
168 | write to system memory.
|
---|
169 | @param HostAddress The system memory address to map to the DMA
|
---|
170 | controller.
|
---|
171 | @param NumberOfBytes On input the number of bytes to map. On output
|
---|
172 | the number of bytes that were mapped.
|
---|
173 | @param DeviceAddress The resulting map address for the bus master
|
---|
174 | controller to use to access the host's
|
---|
175 | HostAddress.
|
---|
176 | @param Mapping A resulting value to pass to Unmap().
|
---|
177 |
|
---|
178 | @retval EFI_SUCCESS The range was mapped for the returned
|
---|
179 | NumberOfBytes.
|
---|
180 | @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common
|
---|
181 | buffer.
|
---|
182 | @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
|
---|
183 | @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
|
---|
184 | of resources.
|
---|
185 | @retval EFI_DEVICE_ERROR The system hardware could not map the requested
|
---|
186 | address.
|
---|
187 |
|
---|
188 | **/
|
---|
189 | EFI_STATUS
|
---|
190 | EFIAPI
|
---|
191 | DmaMap (
|
---|
192 | IN DMA_MAP_OPERATION Operation,
|
---|
193 | IN VOID *HostAddress,
|
---|
194 | IN OUT UINTN *NumberOfBytes,
|
---|
195 | OUT PHYSICAL_ADDRESS *DeviceAddress,
|
---|
196 | OUT VOID **Mapping
|
---|
197 | )
|
---|
198 | {
|
---|
199 | EFI_STATUS Status;
|
---|
200 | MAP_INFO_INSTANCE *Map;
|
---|
201 | VOID *Buffer;
|
---|
202 | EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor;
|
---|
203 | UINTN AllocSize;
|
---|
204 |
|
---|
205 | if ((HostAddress == NULL) ||
|
---|
206 | (NumberOfBytes == NULL) ||
|
---|
207 | (DeviceAddress == NULL) ||
|
---|
208 | (Mapping == NULL))
|
---|
209 | {
|
---|
210 | return EFI_INVALID_PARAMETER;
|
---|
211 | }
|
---|
212 |
|
---|
213 | if (Operation >= MapOperationMaximum) {
|
---|
214 | return EFI_INVALID_PARAMETER;
|
---|
215 | }
|
---|
216 |
|
---|
217 | *DeviceAddress = HostToDeviceAddress (HostAddress);
|
---|
218 |
|
---|
219 | // Remember range so we can flush on the other side
|
---|
220 | Map = AllocatePool (sizeof (MAP_INFO_INSTANCE));
|
---|
221 | if (Map == NULL) {
|
---|
222 | return EFI_OUT_OF_RESOURCES;
|
---|
223 | }
|
---|
224 |
|
---|
225 | if (((UINTN)HostAddress + *NumberOfBytes) > mDmaHostAddressLimit) {
|
---|
226 | if (Operation == MapOperationBusMasterCommonBuffer) {
|
---|
227 | goto CommonBufferError;
|
---|
228 | }
|
---|
229 |
|
---|
230 | AllocSize = ALIGN_VALUE (*NumberOfBytes, mCpu->DmaBufferAlignment);
|
---|
231 | Map->BufferAddress = InternalAllocateAlignedPages (
|
---|
232 | EfiBootServicesData,
|
---|
233 | EFI_SIZE_TO_PAGES (AllocSize),
|
---|
234 | mCpu->DmaBufferAlignment
|
---|
235 | );
|
---|
236 | if (Map->BufferAddress == NULL) {
|
---|
237 | Status = EFI_OUT_OF_RESOURCES;
|
---|
238 | goto FreeMapInfo;
|
---|
239 | }
|
---|
240 |
|
---|
241 | if (Operation == MapOperationBusMasterRead) {
|
---|
242 | CopyMem (Map->BufferAddress, (VOID *)(UINTN)HostAddress, *NumberOfBytes);
|
---|
243 | }
|
---|
244 |
|
---|
245 | mCpu->FlushDataCache (
|
---|
246 | mCpu,
|
---|
247 | (UINTN)Map->BufferAddress,
|
---|
248 | AllocSize,
|
---|
249 | EfiCpuFlushTypeWriteBack
|
---|
250 | );
|
---|
251 |
|
---|
252 | *DeviceAddress = HostToDeviceAddress (Map->BufferAddress);
|
---|
253 | } else if ((Operation != MapOperationBusMasterRead) &&
|
---|
254 | ((((UINTN)HostAddress & (mCpu->DmaBufferAlignment - 1)) != 0) ||
|
---|
255 | ((*NumberOfBytes & (mCpu->DmaBufferAlignment - 1)) != 0)))
|
---|
256 | {
|
---|
257 | // Get the cacheability of the region
|
---|
258 | Status = gDS->GetMemorySpaceDescriptor ((UINTN)HostAddress, &GcdDescriptor);
|
---|
259 | if (EFI_ERROR (Status)) {
|
---|
260 | goto FreeMapInfo;
|
---|
261 | }
|
---|
262 |
|
---|
263 | // If the mapped buffer is not an uncached buffer
|
---|
264 | if ((GcdDescriptor.Attributes & (EFI_MEMORY_WB | EFI_MEMORY_WT)) != 0) {
|
---|
265 | //
|
---|
266 | // Operations of type MapOperationBusMasterCommonBuffer are only allowed
|
---|
267 | // on uncached buffers.
|
---|
268 | //
|
---|
269 | if (Operation == MapOperationBusMasterCommonBuffer) {
|
---|
270 | goto CommonBufferError;
|
---|
271 | }
|
---|
272 |
|
---|
273 | //
|
---|
274 | // If the buffer does not fill entire cache lines we must double buffer
|
---|
275 | // into a suitably aligned allocation that allows us to invalidate the
|
---|
276 | // cache without running the risk of corrupting adjacent unrelated data.
|
---|
277 | // Note that pool allocations are guaranteed to be 8 byte aligned, so
|
---|
278 | // we only have to add (alignment - 8) worth of padding.
|
---|
279 | //
|
---|
280 | Map->DoubleBuffer = TRUE;
|
---|
281 | AllocSize = ALIGN_VALUE (*NumberOfBytes, mCpu->DmaBufferAlignment) +
|
---|
282 | (mCpu->DmaBufferAlignment - 8);
|
---|
283 | Map->BufferAddress = AllocatePool (AllocSize);
|
---|
284 | if (Map->BufferAddress == NULL) {
|
---|
285 | Status = EFI_OUT_OF_RESOURCES;
|
---|
286 | goto FreeMapInfo;
|
---|
287 | }
|
---|
288 |
|
---|
289 | Buffer = ALIGN_POINTER (Map->BufferAddress, mCpu->DmaBufferAlignment);
|
---|
290 | *DeviceAddress = HostToDeviceAddress (Buffer);
|
---|
291 |
|
---|
292 | //
|
---|
293 | // Get rid of any dirty cachelines covering the double buffer. This
|
---|
294 | // prevents them from being written back unexpectedly, potentially
|
---|
295 | // overwriting the data we receive from the device.
|
---|
296 | //
|
---|
297 | mCpu->FlushDataCache (
|
---|
298 | mCpu,
|
---|
299 | (UINTN)Buffer,
|
---|
300 | *NumberOfBytes,
|
---|
301 | EfiCpuFlushTypeWriteBack
|
---|
302 | );
|
---|
303 | } else {
|
---|
304 | Map->DoubleBuffer = FALSE;
|
---|
305 | }
|
---|
306 | } else {
|
---|
307 | Map->DoubleBuffer = FALSE;
|
---|
308 |
|
---|
309 | DEBUG_CODE_BEGIN ();
|
---|
310 |
|
---|
311 | //
|
---|
312 | // The operation type check above only executes if the buffer happens to be
|
---|
313 | // misaligned with respect to CWG, but even if it is aligned, we should not
|
---|
314 | // allow arbitrary buffers to be used for creating consistent mappings.
|
---|
315 | // So duplicate the check here when running in DEBUG mode, just to assert
|
---|
316 | // that we are not trying to create a consistent mapping for cached memory.
|
---|
317 | //
|
---|
318 | Status = gDS->GetMemorySpaceDescriptor ((UINTN)HostAddress, &GcdDescriptor);
|
---|
319 | ASSERT_EFI_ERROR (Status);
|
---|
320 |
|
---|
321 | ASSERT (
|
---|
322 | Operation != MapOperationBusMasterCommonBuffer ||
|
---|
323 | (GcdDescriptor.Attributes & (EFI_MEMORY_WB | EFI_MEMORY_WT)) == 0
|
---|
324 | );
|
---|
325 |
|
---|
326 | DEBUG_CODE_END ();
|
---|
327 |
|
---|
328 | // Flush the Data Cache (should not have any effect if the memory region is
|
---|
329 | // uncached)
|
---|
330 | mCpu->FlushDataCache (
|
---|
331 | mCpu,
|
---|
332 | (UINTN)HostAddress,
|
---|
333 | *NumberOfBytes,
|
---|
334 | EfiCpuFlushTypeWriteBackInvalidate
|
---|
335 | );
|
---|
336 | }
|
---|
337 |
|
---|
338 | Map->HostAddress = (UINTN)HostAddress;
|
---|
339 | Map->NumberOfBytes = *NumberOfBytes;
|
---|
340 | Map->Operation = Operation;
|
---|
341 |
|
---|
342 | *Mapping = Map;
|
---|
343 |
|
---|
344 | return EFI_SUCCESS;
|
---|
345 |
|
---|
346 | CommonBufferError:
|
---|
347 | DEBUG ((
|
---|
348 | DEBUG_ERROR,
|
---|
349 | "%a: Operation type 'MapOperationBusMasterCommonBuffer' is only "
|
---|
350 | "supported\non memory regions that were allocated using "
|
---|
351 | "DmaAllocateBuffer ()\n",
|
---|
352 | __func__
|
---|
353 | ));
|
---|
354 | Status = EFI_UNSUPPORTED;
|
---|
355 | FreeMapInfo:
|
---|
356 | FreePool (Map);
|
---|
357 |
|
---|
358 | return Status;
|
---|
359 | }
|
---|
360 |
|
---|
361 | /**
|
---|
362 | Completes the DmaMapBusMasterRead(), DmaMapBusMasterWrite(), or
|
---|
363 | DmaMapBusMasterCommonBuffer() operation and releases any corresponding
|
---|
364 | resources.
|
---|
365 |
|
---|
366 | @param Mapping The mapping value returned from DmaMap*().
|
---|
367 |
|
---|
368 | @retval EFI_SUCCESS The range was unmapped.
|
---|
369 | @retval EFI_DEVICE_ERROR The data was not committed to the target system
|
---|
370 | memory.
|
---|
371 | @retval EFI_INVALID_PARAMETER An inconsistency was detected between the
|
---|
372 | mapping type and the DoubleBuffer field
|
---|
373 |
|
---|
374 | **/
|
---|
375 | EFI_STATUS
|
---|
376 | EFIAPI
|
---|
377 | DmaUnmap (
|
---|
378 | IN VOID *Mapping
|
---|
379 | )
|
---|
380 | {
|
---|
381 | MAP_INFO_INSTANCE *Map;
|
---|
382 | EFI_STATUS Status;
|
---|
383 | VOID *Buffer;
|
---|
384 | UINTN AllocSize;
|
---|
385 |
|
---|
386 | if (Mapping == NULL) {
|
---|
387 | ASSERT (FALSE);
|
---|
388 | return EFI_INVALID_PARAMETER;
|
---|
389 | }
|
---|
390 |
|
---|
391 | Map = (MAP_INFO_INSTANCE *)Mapping;
|
---|
392 |
|
---|
393 | Status = EFI_SUCCESS;
|
---|
394 | if (((UINTN)Map->HostAddress + Map->NumberOfBytes) > mDmaHostAddressLimit) {
|
---|
395 | AllocSize = ALIGN_VALUE (Map->NumberOfBytes, mCpu->DmaBufferAlignment);
|
---|
396 | if (Map->Operation == MapOperationBusMasterWrite) {
|
---|
397 | mCpu->FlushDataCache (
|
---|
398 | mCpu,
|
---|
399 | (UINTN)Map->BufferAddress,
|
---|
400 | AllocSize,
|
---|
401 | EfiCpuFlushTypeInvalidate
|
---|
402 | );
|
---|
403 | CopyMem (
|
---|
404 | (VOID *)(UINTN)Map->HostAddress,
|
---|
405 | Map->BufferAddress,
|
---|
406 | Map->NumberOfBytes
|
---|
407 | );
|
---|
408 | }
|
---|
409 |
|
---|
410 | FreePages (Map->BufferAddress, EFI_SIZE_TO_PAGES (AllocSize));
|
---|
411 | } else if (Map->DoubleBuffer) {
|
---|
412 | ASSERT (Map->Operation == MapOperationBusMasterWrite);
|
---|
413 |
|
---|
414 | if (Map->Operation != MapOperationBusMasterWrite) {
|
---|
415 | Status = EFI_INVALID_PARAMETER;
|
---|
416 | } else {
|
---|
417 | Buffer = ALIGN_POINTER (Map->BufferAddress, mCpu->DmaBufferAlignment);
|
---|
418 |
|
---|
419 | mCpu->FlushDataCache (
|
---|
420 | mCpu,
|
---|
421 | (UINTN)Buffer,
|
---|
422 | Map->NumberOfBytes,
|
---|
423 | EfiCpuFlushTypeInvalidate
|
---|
424 | );
|
---|
425 |
|
---|
426 | CopyMem ((VOID *)(UINTN)Map->HostAddress, Buffer, Map->NumberOfBytes);
|
---|
427 |
|
---|
428 | FreePool (Map->BufferAddress);
|
---|
429 | }
|
---|
430 | } else {
|
---|
431 | if (Map->Operation == MapOperationBusMasterWrite) {
|
---|
432 | //
|
---|
433 | // Make sure we read buffer from uncached memory and not the cache
|
---|
434 | //
|
---|
435 | mCpu->FlushDataCache (
|
---|
436 | mCpu,
|
---|
437 | Map->HostAddress,
|
---|
438 | Map->NumberOfBytes,
|
---|
439 | EfiCpuFlushTypeInvalidate
|
---|
440 | );
|
---|
441 | }
|
---|
442 | }
|
---|
443 |
|
---|
444 | FreePool (Map);
|
---|
445 |
|
---|
446 | return Status;
|
---|
447 | }
|
---|
448 |
|
---|
449 | /**
|
---|
450 | Allocates pages that are suitable for an DmaMap() of type
|
---|
451 | MapOperationBusMasterCommonBuffer mapping.
|
---|
452 |
|
---|
453 | @param MemoryType The type of memory to allocate,
|
---|
454 | EfiBootServicesData or EfiRuntimeServicesData.
|
---|
455 | @param Pages The number of pages to allocate.
|
---|
456 | @param HostAddress A pointer to store the base system memory
|
---|
457 | address of the allocated range.
|
---|
458 |
|
---|
459 | @retval EFI_SUCCESS The requested memory pages were allocated.
|
---|
460 | @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
|
---|
461 | @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.
|
---|
462 |
|
---|
463 | **/
|
---|
464 | EFI_STATUS
|
---|
465 | EFIAPI
|
---|
466 | DmaAllocateBuffer (
|
---|
467 | IN EFI_MEMORY_TYPE MemoryType,
|
---|
468 | IN UINTN Pages,
|
---|
469 | OUT VOID **HostAddress
|
---|
470 | )
|
---|
471 | {
|
---|
472 | return DmaAllocateAlignedBuffer (MemoryType, Pages, 0, HostAddress);
|
---|
473 | }
|
---|
474 |
|
---|
475 | /**
|
---|
476 | Allocates pages that are suitable for an DmaMap() of type
|
---|
477 | MapOperationBusMasterCommonBuffer mapping, at the requested alignment.
|
---|
478 |
|
---|
479 | @param MemoryType The type of memory to allocate,
|
---|
480 | EfiBootServicesData or EfiRuntimeServicesData.
|
---|
481 | @param Pages The number of pages to allocate.
|
---|
482 | @param Alignment Alignment in bytes of the base of the returned
|
---|
483 | buffer (must be a power of 2)
|
---|
484 | @param HostAddress A pointer to store the base system memory
|
---|
485 | address of the allocated range.
|
---|
486 |
|
---|
487 | @retval EFI_SUCCESS The requested memory pages were allocated.
|
---|
488 | @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
|
---|
489 | @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.
|
---|
490 |
|
---|
491 | **/
|
---|
492 | EFI_STATUS
|
---|
493 | EFIAPI
|
---|
494 | DmaAllocateAlignedBuffer (
|
---|
495 | IN EFI_MEMORY_TYPE MemoryType,
|
---|
496 | IN UINTN Pages,
|
---|
497 | IN UINTN Alignment,
|
---|
498 | OUT VOID **HostAddress
|
---|
499 | )
|
---|
500 | {
|
---|
501 | EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor;
|
---|
502 | VOID *Allocation;
|
---|
503 | UINT64 Attributes;
|
---|
504 | UNCACHED_ALLOCATION *Alloc;
|
---|
505 | EFI_STATUS Status;
|
---|
506 |
|
---|
507 | Attributes = EFI_MEMORY_XP;
|
---|
508 |
|
---|
509 | if (Alignment == 0) {
|
---|
510 | Alignment = EFI_PAGE_SIZE;
|
---|
511 | }
|
---|
512 |
|
---|
513 | if ((HostAddress == NULL) ||
|
---|
514 | ((Alignment & (Alignment - 1)) != 0))
|
---|
515 | {
|
---|
516 | return EFI_INVALID_PARAMETER;
|
---|
517 | }
|
---|
518 |
|
---|
519 | if ((MemoryType == EfiBootServicesData) ||
|
---|
520 | (MemoryType == EfiRuntimeServicesData))
|
---|
521 | {
|
---|
522 | Allocation = InternalAllocateAlignedPages (MemoryType, Pages, Alignment);
|
---|
523 | } else {
|
---|
524 | return EFI_INVALID_PARAMETER;
|
---|
525 | }
|
---|
526 |
|
---|
527 | if (Allocation == NULL) {
|
---|
528 | return EFI_OUT_OF_RESOURCES;
|
---|
529 | }
|
---|
530 |
|
---|
531 | // Get the cacheability of the region
|
---|
532 | Status = gDS->GetMemorySpaceDescriptor ((UINTN)Allocation, &GcdDescriptor);
|
---|
533 | if (EFI_ERROR (Status)) {
|
---|
534 | goto FreeBuffer;
|
---|
535 | }
|
---|
536 |
|
---|
537 | // Choose a suitable uncached memory type that is supported by the region
|
---|
538 | if (GcdDescriptor.Capabilities & EFI_MEMORY_WC) {
|
---|
539 | Attributes |= EFI_MEMORY_WC;
|
---|
540 | } else if (GcdDescriptor.Capabilities & EFI_MEMORY_UC) {
|
---|
541 | Attributes |= EFI_MEMORY_UC;
|
---|
542 | } else {
|
---|
543 | Status = EFI_UNSUPPORTED;
|
---|
544 | goto FreeBuffer;
|
---|
545 | }
|
---|
546 |
|
---|
547 | Alloc = AllocatePool (sizeof *Alloc);
|
---|
548 | if (Alloc == NULL) {
|
---|
549 | goto FreeBuffer;
|
---|
550 | }
|
---|
551 |
|
---|
552 | Alloc->HostAddress = Allocation;
|
---|
553 | Alloc->NumPages = Pages;
|
---|
554 | Alloc->Attributes = GcdDescriptor.Attributes;
|
---|
555 |
|
---|
556 | InsertHeadList (&UncachedAllocationList, &Alloc->Link);
|
---|
557 |
|
---|
558 | // Ensure that EFI_MEMORY_XP is in the capability set
|
---|
559 | if ((GcdDescriptor.Capabilities & EFI_MEMORY_XP) != EFI_MEMORY_XP) {
|
---|
560 | Status = gDS->SetMemorySpaceCapabilities (
|
---|
561 | (PHYSICAL_ADDRESS)(UINTN)Allocation,
|
---|
562 | EFI_PAGES_TO_SIZE (Pages),
|
---|
563 | GcdDescriptor.Capabilities | EFI_MEMORY_XP
|
---|
564 | );
|
---|
565 |
|
---|
566 | // if we were to fail setting the capability, this would indicate an internal failure of the GCD code. We should
|
---|
567 | // assert here to let a platform know something went crazy, but for a release build we can let the allocation occur
|
---|
568 | // without the EFI_MEMORY_XP bit set, as that was the existing behavior
|
---|
569 | if (EFI_ERROR (Status)) {
|
---|
570 | DEBUG ((
|
---|
571 | DEBUG_ERROR,
|
---|
572 | "%a failed to set EFI_MEMORY_XP capability on 0x%llx for length 0x%llx. Attempting to allocate without XP set.\n",
|
---|
573 | __func__,
|
---|
574 | Allocation,
|
---|
575 | EFI_PAGES_TO_SIZE (Pages)
|
---|
576 | ));
|
---|
577 |
|
---|
578 | ASSERT_EFI_ERROR (Status);
|
---|
579 |
|
---|
580 | Attributes &= ~EFI_MEMORY_XP;
|
---|
581 | }
|
---|
582 | }
|
---|
583 |
|
---|
584 | // Remap the region with the new attributes and mark it non-executable
|
---|
585 | Status = gDS->SetMemorySpaceAttributes (
|
---|
586 | (PHYSICAL_ADDRESS)(UINTN)Allocation,
|
---|
587 | EFI_PAGES_TO_SIZE (Pages),
|
---|
588 | Attributes
|
---|
589 | );
|
---|
590 | if (EFI_ERROR (Status)) {
|
---|
591 | goto FreeAlloc;
|
---|
592 | }
|
---|
593 |
|
---|
594 | Status = mCpu->FlushDataCache (
|
---|
595 | mCpu,
|
---|
596 | (PHYSICAL_ADDRESS)(UINTN)Allocation,
|
---|
597 | EFI_PAGES_TO_SIZE (Pages),
|
---|
598 | EfiCpuFlushTypeInvalidate
|
---|
599 | );
|
---|
600 | if (EFI_ERROR (Status)) {
|
---|
601 | goto FreeAlloc;
|
---|
602 | }
|
---|
603 |
|
---|
604 | *HostAddress = Allocation;
|
---|
605 |
|
---|
606 | return EFI_SUCCESS;
|
---|
607 |
|
---|
608 | FreeAlloc:
|
---|
609 | RemoveEntryList (&Alloc->Link);
|
---|
610 | FreePool (Alloc);
|
---|
611 |
|
---|
612 | FreeBuffer:
|
---|
613 | FreePages (Allocation, Pages);
|
---|
614 | return Status;
|
---|
615 | }
|
---|
616 |
|
---|
617 | /**
|
---|
618 | Frees memory that was allocated with DmaAllocateBuffer().
|
---|
619 |
|
---|
620 | @param Pages The number of pages to free.
|
---|
621 | @param HostAddress The base system memory address of the allocated
|
---|
622 | range.
|
---|
623 |
|
---|
624 | @retval EFI_SUCCESS The requested memory pages were freed.
|
---|
625 | @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and
|
---|
626 | Pages was not allocated with
|
---|
627 | DmaAllocateBuffer().
|
---|
628 |
|
---|
629 | **/
|
---|
630 | EFI_STATUS
|
---|
631 | EFIAPI
|
---|
632 | DmaFreeBuffer (
|
---|
633 | IN UINTN Pages,
|
---|
634 | IN VOID *HostAddress
|
---|
635 | )
|
---|
636 | {
|
---|
637 | LIST_ENTRY *Link;
|
---|
638 | UNCACHED_ALLOCATION *Alloc;
|
---|
639 | BOOLEAN Found;
|
---|
640 | EFI_STATUS Status;
|
---|
641 |
|
---|
642 | if (HostAddress == NULL) {
|
---|
643 | return EFI_INVALID_PARAMETER;
|
---|
644 | }
|
---|
645 |
|
---|
646 | for (Link = GetFirstNode (&UncachedAllocationList), Found = FALSE;
|
---|
647 | !IsNull (&UncachedAllocationList, Link);
|
---|
648 | Link = GetNextNode (&UncachedAllocationList, Link))
|
---|
649 | {
|
---|
650 | Alloc = BASE_CR (Link, UNCACHED_ALLOCATION, Link);
|
---|
651 | if ((Alloc->HostAddress == HostAddress) && (Alloc->NumPages == Pages)) {
|
---|
652 | Found = TRUE;
|
---|
653 | break;
|
---|
654 | }
|
---|
655 | }
|
---|
656 |
|
---|
657 | if (!Found) {
|
---|
658 | ASSERT (FALSE);
|
---|
659 | return EFI_INVALID_PARAMETER;
|
---|
660 | }
|
---|
661 |
|
---|
662 | RemoveEntryList (&Alloc->Link);
|
---|
663 |
|
---|
664 | Status = gDS->SetMemorySpaceAttributes (
|
---|
665 | (PHYSICAL_ADDRESS)(UINTN)HostAddress,
|
---|
666 | EFI_PAGES_TO_SIZE (Pages),
|
---|
667 | Alloc->Attributes
|
---|
668 | );
|
---|
669 | if (EFI_ERROR (Status)) {
|
---|
670 | goto FreeAlloc;
|
---|
671 | }
|
---|
672 |
|
---|
673 | //
|
---|
674 | // If we fail to restore the original attributes, it is better to leak the
|
---|
675 | // memory than to return it to the heap
|
---|
676 | //
|
---|
677 | FreePages (HostAddress, Pages);
|
---|
678 |
|
---|
679 | FreeAlloc:
|
---|
680 | FreePool (Alloc);
|
---|
681 | return Status;
|
---|
682 | }
|
---|
683 |
|
---|
684 | EFI_STATUS
|
---|
685 | EFIAPI
|
---|
686 | NonCoherentDmaLibConstructor (
|
---|
687 | IN EFI_HANDLE ImageHandle,
|
---|
688 | IN EFI_SYSTEM_TABLE *SystemTable
|
---|
689 | )
|
---|
690 | {
|
---|
691 | InitializeListHead (&UncachedAllocationList);
|
---|
692 |
|
---|
693 | //
|
---|
694 | // Ensure that the combination of DMA addressing offset and limit produces
|
---|
695 | // a sane value.
|
---|
696 | //
|
---|
697 | ASSERT (PcdGet64 (PcdDmaDeviceLimit) > PcdGet64 (PcdDmaDeviceOffset));
|
---|
698 |
|
---|
699 | mDmaHostAddressLimit = PcdGet64 (PcdDmaDeviceLimit) -
|
---|
700 | PcdGet64 (PcdDmaDeviceOffset);
|
---|
701 |
|
---|
702 | // Get the Cpu protocol for later use
|
---|
703 | return gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&mCpu);
|
---|
704 | }
|
---|