]> git.proxmox.com Git - mirror_edk2.git/blob - IntelSiliconPkg/IntelVTdDxe/BmDma.c
MdeModulePkg/XhciPei: Support IoMmu.
[mirror_edk2.git] / IntelSiliconPkg / IntelVTdDxe / BmDma.c
1 /** @file
2 BmDma related function
3
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
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 <PiDxe.h>
16
17 #include <Protocol/IoMmu.h>
18
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>
24
25 // TBD: May make it a policy
26 #define DMA_MEMORY_TOP MAX_UINTN
27 //#define DMA_MEMORY_TOP 0x0000000001FFFFFFULL
28
29 #define MAP_INFO_SIGNATURE SIGNATURE_32 ('D', 'M', 'A', 'P')
30 typedef struct {
31 UINT32 Signature;
32 LIST_ENTRY Link;
33 EDKII_IOMMU_OPERATION Operation;
34 UINTN NumberOfBytes;
35 UINTN NumberOfPages;
36 EFI_PHYSICAL_ADDRESS HostAddress;
37 EFI_PHYSICAL_ADDRESS DeviceAddress;
38 } MAP_INFO;
39 #define MAP_INFO_FROM_LINK(a) CR (a, MAP_INFO, Link, MAP_INFO_SIGNATURE)
40
41 LIST_ENTRY gMaps = INITIALIZE_LIST_HEAD_VARIABLE(gMaps);
42
43 /**
44 Provides the controller-specific addresses required to access system memory from a
45 DMA bus master.
46
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
51 that were mapped.
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().
55
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.
61
62 **/
63 EFI_STATUS
64 EFIAPI
65 IoMmuMap (
66 IN EDKII_IOMMU_PROTOCOL *This,
67 IN EDKII_IOMMU_OPERATION Operation,
68 IN VOID *HostAddress,
69 IN OUT UINTN *NumberOfBytes,
70 OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
71 OUT VOID **Mapping
72 )
73 {
74 EFI_STATUS Status;
75 EFI_PHYSICAL_ADDRESS PhysicalAddress;
76 MAP_INFO *MapInfo;
77 EFI_PHYSICAL_ADDRESS DmaMemoryTop;
78 BOOLEAN NeedRemap;
79
80 if (NumberOfBytes == NULL || DeviceAddress == NULL ||
81 Mapping == NULL) {
82 DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_INVALID_PARAMETER));
83 return EFI_INVALID_PARAMETER;
84 }
85
86 DEBUG ((DEBUG_VERBOSE, "IoMmuMap: ==> 0x%08x - 0x%08x (%x)\n", HostAddress, *NumberOfBytes, Operation));
87
88 //
89 // Make sure that Operation is valid
90 //
91 if ((UINT32) Operation >= EdkiiIoMmuOperationMaximum) {
92 DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_INVALID_PARAMETER));
93 return EFI_INVALID_PARAMETER;
94 }
95 NeedRemap = FALSE;
96 PhysicalAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress;
97
98 DmaMemoryTop = DMA_MEMORY_TOP;
99
100 //
101 // Alignment check
102 //
103 if ((*NumberOfBytes != ALIGN_VALUE(*NumberOfBytes, SIZE_4KB)) ||
104 (PhysicalAddress != ALIGN_VALUE(PhysicalAddress, SIZE_4KB))) {
105 if ((Operation == EdkiiIoMmuOperationBusMasterCommonBuffer) ||
106 (Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64)) {
107 //
108 // The input buffer might be a subset from IoMmuAllocateBuffer.
109 // Skip the check.
110 //
111 } else {
112 NeedRemap = TRUE;
113 }
114 }
115
116 if ((PhysicalAddress + *NumberOfBytes) >= DMA_MEMORY_TOP) {
117 NeedRemap = TRUE;
118 }
119
120 if (((Operation != EdkiiIoMmuOperationBusMasterRead64 &&
121 Operation != EdkiiIoMmuOperationBusMasterWrite64 &&
122 Operation != EdkiiIoMmuOperationBusMasterCommonBuffer64)) &&
123 ((PhysicalAddress + *NumberOfBytes) > SIZE_4GB)) {
124 //
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.
128 //
129 NeedRemap = TRUE;
130 DmaMemoryTop = MIN (DmaMemoryTop, SIZE_4GB - 1);
131 }
132
133 if (Operation == EdkiiIoMmuOperationBusMasterCommonBuffer ||
134 Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) {
135 if (NeedRemap) {
136 //
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
139 // an error.
140 //
141 DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_UNSUPPORTED));
142 return EFI_UNSUPPORTED;
143 }
144 }
145
146 //
147 // Allocate a MAP_INFO structure to remember the mapping when Unmap() is
148 // called later.
149 //
150 MapInfo = AllocatePool (sizeof (MAP_INFO));
151 if (MapInfo == NULL) {
152 *NumberOfBytes = 0;
153 DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_OUT_OF_RESOURCES));
154 return EFI_OUT_OF_RESOURCES;
155 }
156
157 //
158 // Initialize the MAP_INFO structure
159 //
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;
166
167 //
168 // Allocate a buffer below 4GB to map the transfer to.
169 //
170 if (NeedRemap) {
171 Status = gBS->AllocatePages (
172 AllocateMaxAddress,
173 EfiBootServicesData,
174 MapInfo->NumberOfPages,
175 &MapInfo->DeviceAddress
176 );
177 if (EFI_ERROR (Status)) {
178 FreePool (MapInfo);
179 *NumberOfBytes = 0;
180 DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", Status));
181 return Status;
182 }
183
184 //
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.
188 //
189 if (Operation == EdkiiIoMmuOperationBusMasterRead ||
190 Operation == EdkiiIoMmuOperationBusMasterRead64) {
191 CopyMem (
192 (VOID *) (UINTN) MapInfo->DeviceAddress,
193 (VOID *) (UINTN) MapInfo->HostAddress,
194 MapInfo->NumberOfBytes
195 );
196 }
197 } else {
198 MapInfo->DeviceAddress = MapInfo->HostAddress;
199 }
200
201 InsertTailList (&gMaps, &MapInfo->Link);
202
203 //
204 // The DeviceAddress is the address of the maped buffer below 4GB
205 //
206 *DeviceAddress = MapInfo->DeviceAddress;
207 //
208 // Return a pointer to the MAP_INFO structure in Mapping
209 //
210 *Mapping = MapInfo;
211
212 DEBUG ((DEBUG_VERBOSE, "IoMmuMap: 0x%08x - 0x%08x <==\n", *DeviceAddress, *Mapping));
213
214 return EFI_SUCCESS;
215 }
216
217 /**
218 Completes the Map() operation and releases any corresponding resources.
219
220 @param This The protocol instance pointer.
221 @param Mapping The mapping value returned from Map().
222
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.
226 **/
227 EFI_STATUS
228 EFIAPI
229 IoMmuUnmap (
230 IN EDKII_IOMMU_PROTOCOL *This,
231 IN VOID *Mapping
232 )
233 {
234 MAP_INFO *MapInfo;
235 LIST_ENTRY *Link;
236
237 DEBUG ((DEBUG_VERBOSE, "IoMmuUnmap: 0x%08x\n", Mapping));
238
239 if (Mapping == NULL) {
240 DEBUG ((DEBUG_ERROR, "IoMmuUnmap: %r\n", EFI_INVALID_PARAMETER));
241 return EFI_INVALID_PARAMETER;
242 }
243
244 MapInfo = NULL;
245 for (Link = GetFirstNode (&gMaps)
246 ; !IsNull (&gMaps, Link)
247 ; Link = GetNextNode (&gMaps, Link)
248 ) {
249 MapInfo = MAP_INFO_FROM_LINK (Link);
250 if (MapInfo == Mapping) {
251 break;
252 }
253 }
254 //
255 // Mapping is not a valid value returned by Map()
256 //
257 if (MapInfo != Mapping) {
258 DEBUG ((DEBUG_ERROR, "IoMmuUnmap: %r\n", EFI_INVALID_PARAMETER));
259 return EFI_INVALID_PARAMETER;
260 }
261 RemoveEntryList (&MapInfo->Link);
262
263 if (MapInfo->DeviceAddress != MapInfo->HostAddress) {
264 //
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.
268 //
269 if (MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite ||
270 MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite64) {
271 CopyMem (
272 (VOID *) (UINTN) MapInfo->HostAddress,
273 (VOID *) (UINTN) MapInfo->DeviceAddress,
274 MapInfo->NumberOfBytes
275 );
276 }
277
278 //
279 // Free the mapped buffer and the MAP_INFO structure.
280 //
281 gBS->FreePages (MapInfo->DeviceAddress, MapInfo->NumberOfPages);
282 }
283
284 FreePool (Mapping);
285 return EFI_SUCCESS;
286 }
287
288 /**
289 Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
290 OperationBusMasterCommonBuffer64 mapping.
291
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
298 allocated range.
299 @param Attributes The requested bit mask of attributes for the allocated range.
300
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.
306
307 **/
308 EFI_STATUS
309 EFIAPI
310 IoMmuAllocateBuffer (
311 IN EDKII_IOMMU_PROTOCOL *This,
312 IN EFI_ALLOCATE_TYPE Type,
313 IN EFI_MEMORY_TYPE MemoryType,
314 IN UINTN Pages,
315 IN OUT VOID **HostAddress,
316 IN UINT64 Attributes
317 )
318 {
319 EFI_STATUS Status;
320 EFI_PHYSICAL_ADDRESS PhysicalAddress;
321
322 DEBUG ((DEBUG_VERBOSE, "IoMmuAllocateBuffer: ==> 0x%08x\n", Pages));
323
324 //
325 // Validate Attributes
326 //
327 if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) {
328 DEBUG ((DEBUG_ERROR, "IoMmuAllocateBuffer: %r\n", EFI_UNSUPPORTED));
329 return EFI_UNSUPPORTED;
330 }
331
332 //
333 // Check for invalid inputs
334 //
335 if (HostAddress == NULL) {
336 DEBUG ((DEBUG_ERROR, "IoMmuAllocateBuffer: %r\n", EFI_INVALID_PARAMETER));
337 return EFI_INVALID_PARAMETER;
338 }
339
340 //
341 // The only valid memory types are EfiBootServicesData and
342 // EfiRuntimeServicesData
343 //
344 if (MemoryType != EfiBootServicesData &&
345 MemoryType != EfiRuntimeServicesData) {
346 DEBUG ((DEBUG_ERROR, "IoMmuAllocateBuffer: %r\n", EFI_INVALID_PARAMETER));
347 return EFI_INVALID_PARAMETER;
348 }
349
350 PhysicalAddress = DMA_MEMORY_TOP;
351 if ((Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0) {
352 //
353 // Limit allocations to memory below 4GB
354 //
355 PhysicalAddress = MIN (PhysicalAddress, SIZE_4GB - 1);
356 }
357 Status = gBS->AllocatePages (
358 AllocateMaxAddress,
359 MemoryType,
360 Pages,
361 &PhysicalAddress
362 );
363 if (!EFI_ERROR (Status)) {
364 *HostAddress = (VOID *) (UINTN) PhysicalAddress;
365 }
366
367 DEBUG ((DEBUG_VERBOSE, "IoMmuAllocateBuffer: 0x%08x <==\n", *HostAddress));
368
369 return Status;
370 }
371
372 /**
373 Frees memory that was allocated with AllocateBuffer().
374
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.
378
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().
382
383 **/
384 EFI_STATUS
385 EFIAPI
386 IoMmuFreeBuffer (
387 IN EDKII_IOMMU_PROTOCOL *This,
388 IN UINTN Pages,
389 IN VOID *HostAddress
390 )
391 {
392 DEBUG ((DEBUG_VERBOSE, "IoMmuFreeBuffer: 0x%\n", Pages));
393 return gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress, Pages);
394 }
395
396 /**
397 Get device information from mapping.
398
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.
402
403 @retval EFI_SUCCESS The device information is returned.
404 @retval EFI_INVALID_PARAMETER The mapping is invalid.
405 **/
406 EFI_STATUS
407 GetDeviceInfoFromMapping (
408 IN VOID *Mapping,
409 OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
410 OUT UINTN *NumberOfPages
411 )
412 {
413 MAP_INFO *MapInfo;
414 LIST_ENTRY *Link;
415
416 if (Mapping == NULL) {
417 return EFI_INVALID_PARAMETER;
418 }
419
420 MapInfo = NULL;
421 for (Link = GetFirstNode (&gMaps)
422 ; !IsNull (&gMaps, Link)
423 ; Link = GetNextNode (&gMaps, Link)
424 ) {
425 MapInfo = MAP_INFO_FROM_LINK (Link);
426 if (MapInfo == Mapping) {
427 break;
428 }
429 }
430 //
431 // Mapping is not a valid value returned by Map()
432 //
433 if (MapInfo != Mapping) {
434 return EFI_INVALID_PARAMETER;
435 }
436
437 *DeviceAddress = MapInfo->DeviceAddress;
438 *NumberOfPages = MapInfo->NumberOfPages;
439 return EFI_SUCCESS;
440 }
441