]> git.proxmox.com Git - mirror_edk2.git/blame - IntelSiliconPkg/Feature/VTd/IntelVTdDxe/BmDma.c
IntelSiliconPkg IntelVTdDxe: Signal AcpiNotificationFunc() initially
[mirror_edk2.git] / IntelSiliconPkg / Feature / VTd / IntelVTdDxe / BmDma.c
CommitLineData
c049fc99
JY
1/** @file\r
2 BmDma related function\r
3\r
4 Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
5 This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution. The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php\r
9\r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14\r
15#include <PiDxe.h>\r
16\r
17#include <Protocol/IoMmu.h>\r
18\r
19#include <Library/BaseLib.h>\r
20#include <Library/DebugLib.h>\r
21#include <Library/BaseMemoryLib.h>\r
22#include <Library/MemoryAllocationLib.h>\r
23#include <Library/UefiBootServicesTableLib.h>\r
24\r
25// TBD: May make it a policy\r
26#define DMA_MEMORY_TOP MAX_UINTN\r
27//#define DMA_MEMORY_TOP 0x0000000001FFFFFFULL\r
28\r
29#define MAP_INFO_SIGNATURE SIGNATURE_32 ('D', 'M', 'A', 'P')\r
30typedef struct {\r
31 UINT32 Signature;\r
32 LIST_ENTRY Link;\r
33 EDKII_IOMMU_OPERATION Operation;\r
34 UINTN NumberOfBytes;\r
35 UINTN NumberOfPages;\r
36 EFI_PHYSICAL_ADDRESS HostAddress;\r
37 EFI_PHYSICAL_ADDRESS DeviceAddress;\r
38} MAP_INFO;\r
39#define MAP_INFO_FROM_LINK(a) CR (a, MAP_INFO, Link, MAP_INFO_SIGNATURE)\r
40\r
41LIST_ENTRY gMaps = INITIALIZE_LIST_HEAD_VARIABLE(gMaps);\r
42\r
43/**\r
44 Provides the controller-specific addresses required to access system memory from a\r
45 DMA bus master.\r
46\r
47 @param This The protocol instance pointer.\r
48 @param Operation Indicates if the bus master is going to read or write to system memory.\r
49 @param HostAddress The system memory address to map to the PCI controller.\r
50 @param NumberOfBytes On input the number of bytes to map. On output the number of bytes\r
51 that were mapped.\r
52 @param DeviceAddress The resulting map address for the bus master PCI controller to use to\r
53 access the hosts HostAddress.\r
54 @param Mapping A resulting value to pass to Unmap().\r
55\r
56 @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes.\r
57 @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer.\r
58 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
59 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.\r
60 @retval EFI_DEVICE_ERROR The system hardware could not map the requested address.\r
61\r
62**/\r
63EFI_STATUS\r
64EFIAPI\r
65IoMmuMap (\r
66 IN EDKII_IOMMU_PROTOCOL *This,\r
67 IN EDKII_IOMMU_OPERATION Operation,\r
68 IN VOID *HostAddress,\r
69 IN OUT UINTN *NumberOfBytes,\r
70 OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,\r
71 OUT VOID **Mapping\r
72 )\r
73{\r
74 EFI_STATUS Status;\r
75 EFI_PHYSICAL_ADDRESS PhysicalAddress;\r
76 MAP_INFO *MapInfo;\r
77 EFI_PHYSICAL_ADDRESS DmaMemoryTop;\r
78 BOOLEAN NeedRemap;\r
79\r
94fb621d 80 if (NumberOfBytes == NULL || DeviceAddress == NULL ||\r
c049fc99
JY
81 Mapping == NULL) {\r
82 DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_INVALID_PARAMETER));\r
83 return EFI_INVALID_PARAMETER;\r
84 }\r
85\r
94fb621d
JY
86 DEBUG ((DEBUG_VERBOSE, "IoMmuMap: ==> 0x%08x - 0x%08x (%x)\n", HostAddress, *NumberOfBytes, Operation));\r
87\r
c049fc99
JY
88 //\r
89 // Make sure that Operation is valid\r
90 //\r
91 if ((UINT32) Operation >= EdkiiIoMmuOperationMaximum) {\r
92 DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_INVALID_PARAMETER));\r
93 return EFI_INVALID_PARAMETER;\r
94 }\r
95 NeedRemap = FALSE;\r
96 PhysicalAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress;\r
97\r
98 DmaMemoryTop = DMA_MEMORY_TOP;\r
99\r
100 //\r
101 // Alignment check\r
102 //\r
103 if ((*NumberOfBytes != ALIGN_VALUE(*NumberOfBytes, SIZE_4KB)) ||\r
104 (PhysicalAddress != ALIGN_VALUE(PhysicalAddress, SIZE_4KB))) {\r
105 if ((Operation == EdkiiIoMmuOperationBusMasterCommonBuffer) ||\r
106 (Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64)) {\r
107 //\r
108 // The input buffer might be a subset from IoMmuAllocateBuffer.\r
109 // Skip the check.\r
110 //\r
111 } else {\r
112 NeedRemap = TRUE;\r
113 }\r
114 }\r
115\r
116 if ((PhysicalAddress + *NumberOfBytes) >= DMA_MEMORY_TOP) {\r
117 NeedRemap = TRUE;\r
118 }\r
119\r
120 if (((Operation != EdkiiIoMmuOperationBusMasterRead64 &&\r
121 Operation != EdkiiIoMmuOperationBusMasterWrite64 &&\r
122 Operation != EdkiiIoMmuOperationBusMasterCommonBuffer64)) &&\r
123 ((PhysicalAddress + *NumberOfBytes) > SIZE_4GB)) {\r
124 //\r
125 // If the root bridge or the device cannot handle performing DMA above\r
126 // 4GB but any part of the DMA transfer being mapped is above 4GB, then\r
127 // map the DMA transfer to a buffer below 4GB.\r
128 //\r
129 NeedRemap = TRUE;\r
130 DmaMemoryTop = MIN (DmaMemoryTop, SIZE_4GB - 1);\r
131 }\r
132\r
133 if (Operation == EdkiiIoMmuOperationBusMasterCommonBuffer ||\r
134 Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) {\r
135 if (NeedRemap) {\r
136 //\r
137 // Common Buffer operations can not be remapped. If the common buffer\r
94fb621d 138 // is above 4GB, then it is not possible to generate a mapping, so return\r
c049fc99
JY
139 // an error.\r
140 //\r
141 DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_UNSUPPORTED));\r
142 return EFI_UNSUPPORTED;\r
143 }\r
144 }\r
145\r
146 //\r
147 // Allocate a MAP_INFO structure to remember the mapping when Unmap() is\r
148 // called later.\r
149 //\r
150 MapInfo = AllocatePool (sizeof (MAP_INFO));\r
151 if (MapInfo == NULL) {\r
152 *NumberOfBytes = 0;\r
153 DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_OUT_OF_RESOURCES));\r
154 return EFI_OUT_OF_RESOURCES;\r
155 }\r
156\r
157 //\r
158 // Initialize the MAP_INFO structure\r
159 //\r
160 MapInfo->Signature = MAP_INFO_SIGNATURE;\r
161 MapInfo->Operation = Operation;\r
162 MapInfo->NumberOfBytes = *NumberOfBytes;\r
163 MapInfo->NumberOfPages = EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes);\r
164 MapInfo->HostAddress = PhysicalAddress;\r
165 MapInfo->DeviceAddress = DmaMemoryTop;\r
166\r
167 //\r
168 // Allocate a buffer below 4GB to map the transfer to.\r
169 //\r
170 if (NeedRemap) {\r
171 Status = gBS->AllocatePages (\r
172 AllocateMaxAddress,\r
173 EfiBootServicesData,\r
174 MapInfo->NumberOfPages,\r
175 &MapInfo->DeviceAddress\r
176 );\r
177 if (EFI_ERROR (Status)) {\r
178 FreePool (MapInfo);\r
179 *NumberOfBytes = 0;\r
180 DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", Status));\r
181 return Status;\r
182 }\r
183\r
184 //\r
185 // If this is a read operation from the Bus Master's point of view,\r
186 // then copy the contents of the real buffer into the mapped buffer\r
187 // so the Bus Master can read the contents of the real buffer.\r
188 //\r
189 if (Operation == EdkiiIoMmuOperationBusMasterRead ||\r
190 Operation == EdkiiIoMmuOperationBusMasterRead64) {\r
191 CopyMem (\r
192 (VOID *) (UINTN) MapInfo->DeviceAddress,\r
193 (VOID *) (UINTN) MapInfo->HostAddress,\r
194 MapInfo->NumberOfBytes\r
195 );\r
196 }\r
197 } else {\r
198 MapInfo->DeviceAddress = MapInfo->HostAddress;\r
199 }\r
200\r
201 InsertTailList (&gMaps, &MapInfo->Link);\r
202\r
203 //\r
204 // The DeviceAddress is the address of the maped buffer below 4GB\r
205 //\r
206 *DeviceAddress = MapInfo->DeviceAddress;\r
207 //\r
208 // Return a pointer to the MAP_INFO structure in Mapping\r
209 //\r
210 *Mapping = MapInfo;\r
211\r
212 DEBUG ((DEBUG_VERBOSE, "IoMmuMap: 0x%08x - 0x%08x <==\n", *DeviceAddress, *Mapping));\r
213\r
214 return EFI_SUCCESS;\r
215}\r
216\r
217/**\r
218 Completes the Map() operation and releases any corresponding resources.\r
219\r
220 @param This The protocol instance pointer.\r
221 @param Mapping The mapping value returned from Map().\r
222\r
223 @retval EFI_SUCCESS The range was unmapped.\r
224 @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().\r
225 @retval EFI_DEVICE_ERROR The data was not committed to the target system memory.\r
226**/\r
227EFI_STATUS\r
228EFIAPI\r
229IoMmuUnmap (\r
230 IN EDKII_IOMMU_PROTOCOL *This,\r
231 IN VOID *Mapping\r
232 )\r
233{\r
234 MAP_INFO *MapInfo;\r
235 LIST_ENTRY *Link;\r
236\r
237 DEBUG ((DEBUG_VERBOSE, "IoMmuUnmap: 0x%08x\n", Mapping));\r
238\r
239 if (Mapping == NULL) {\r
240 DEBUG ((DEBUG_ERROR, "IoMmuUnmap: %r\n", EFI_INVALID_PARAMETER));\r
241 return EFI_INVALID_PARAMETER;\r
242 }\r
243\r
244 MapInfo = NULL;\r
245 for (Link = GetFirstNode (&gMaps)\r
246 ; !IsNull (&gMaps, Link)\r
247 ; Link = GetNextNode (&gMaps, Link)\r
248 ) {\r
249 MapInfo = MAP_INFO_FROM_LINK (Link);\r
250 if (MapInfo == Mapping) {\r
251 break;\r
252 }\r
253 }\r
254 //\r
255 // Mapping is not a valid value returned by Map()\r
256 //\r
257 if (MapInfo != Mapping) {\r
258 DEBUG ((DEBUG_ERROR, "IoMmuUnmap: %r\n", EFI_INVALID_PARAMETER));\r
259 return EFI_INVALID_PARAMETER;\r
260 }\r
261 RemoveEntryList (&MapInfo->Link);\r
262\r
263 if (MapInfo->DeviceAddress != MapInfo->HostAddress) {\r
264 //\r
265 // If this is a write operation from the Bus Master's point of view,\r
266 // then copy the contents of the mapped buffer into the real buffer\r
267 // so the processor can read the contents of the real buffer.\r
268 //\r
269 if (MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite ||\r
270 MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite64) {\r
271 CopyMem (\r
272 (VOID *) (UINTN) MapInfo->HostAddress,\r
273 (VOID *) (UINTN) MapInfo->DeviceAddress,\r
274 MapInfo->NumberOfBytes\r
275 );\r
276 }\r
277\r
278 //\r
279 // Free the mapped buffer and the MAP_INFO structure.\r
280 //\r
281 gBS->FreePages (MapInfo->DeviceAddress, MapInfo->NumberOfPages);\r
282 }\r
283\r
284 FreePool (Mapping);\r
285 return EFI_SUCCESS;\r
286}\r
287\r
288/**\r
289 Allocates pages that are suitable for an OperationBusMasterCommonBuffer or\r
290 OperationBusMasterCommonBuffer64 mapping.\r
291\r
292 @param This The protocol instance pointer.\r
293 @param Type This parameter is not used and must be ignored.\r
294 @param MemoryType The type of memory to allocate, EfiBootServicesData or\r
295 EfiRuntimeServicesData.\r
296 @param Pages The number of pages to allocate.\r
297 @param HostAddress A pointer to store the base system memory address of the\r
298 allocated range.\r
299 @param Attributes The requested bit mask of attributes for the allocated range.\r
300\r
301 @retval EFI_SUCCESS The requested memory pages were allocated.\r
302 @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are\r
34e18d17 303 MEMORY_WRITE_COMBINE, MEMORY_CACHED and DUAL_ADDRESS_CYCLE.\r
c049fc99
JY
304 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
305 @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.\r
306\r
307**/\r
308EFI_STATUS\r
309EFIAPI\r
310IoMmuAllocateBuffer (\r
311 IN EDKII_IOMMU_PROTOCOL *This,\r
312 IN EFI_ALLOCATE_TYPE Type,\r
313 IN EFI_MEMORY_TYPE MemoryType,\r
314 IN UINTN Pages,\r
315 IN OUT VOID **HostAddress,\r
316 IN UINT64 Attributes\r
317 )\r
318{\r
319 EFI_STATUS Status;\r
320 EFI_PHYSICAL_ADDRESS PhysicalAddress;\r
321\r
322 DEBUG ((DEBUG_VERBOSE, "IoMmuAllocateBuffer: ==> 0x%08x\n", Pages));\r
323\r
324 //\r
325 // Validate Attributes\r
326 //\r
327 if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) {\r
328 DEBUG ((DEBUG_ERROR, "IoMmuAllocateBuffer: %r\n", EFI_UNSUPPORTED));\r
329 return EFI_UNSUPPORTED;\r
330 }\r
331\r
332 //\r
333 // Check for invalid inputs\r
334 //\r
335 if (HostAddress == NULL) {\r
336 DEBUG ((DEBUG_ERROR, "IoMmuAllocateBuffer: %r\n", EFI_INVALID_PARAMETER));\r
337 return EFI_INVALID_PARAMETER;\r
338 }\r
339\r
340 //\r
341 // The only valid memory types are EfiBootServicesData and\r
342 // EfiRuntimeServicesData\r
343 //\r
344 if (MemoryType != EfiBootServicesData &&\r
345 MemoryType != EfiRuntimeServicesData) {\r
346 DEBUG ((DEBUG_ERROR, "IoMmuAllocateBuffer: %r\n", EFI_INVALID_PARAMETER));\r
347 return EFI_INVALID_PARAMETER;\r
348 }\r
349\r
350 PhysicalAddress = DMA_MEMORY_TOP;\r
351 if ((Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0) {\r
352 //\r
353 // Limit allocations to memory below 4GB\r
354 //\r
355 PhysicalAddress = MIN (PhysicalAddress, SIZE_4GB - 1);\r
356 }\r
357 Status = gBS->AllocatePages (\r
358 AllocateMaxAddress,\r
359 MemoryType,\r
360 Pages,\r
361 &PhysicalAddress\r
362 );\r
363 if (!EFI_ERROR (Status)) {\r
364 *HostAddress = (VOID *) (UINTN) PhysicalAddress;\r
365 }\r
366\r
367 DEBUG ((DEBUG_VERBOSE, "IoMmuAllocateBuffer: 0x%08x <==\n", *HostAddress));\r
368\r
369 return Status;\r
370}\r
371\r
372/**\r
373 Frees memory that was allocated with AllocateBuffer().\r
374\r
375 @param This The protocol instance pointer.\r
376 @param Pages The number of pages to free.\r
377 @param HostAddress The base system memory address of the allocated range.\r
378\r
379 @retval EFI_SUCCESS The requested memory pages were freed.\r
380 @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages\r
381 was not allocated with AllocateBuffer().\r
382\r
383**/\r
384EFI_STATUS\r
385EFIAPI\r
386IoMmuFreeBuffer (\r
387 IN EDKII_IOMMU_PROTOCOL *This,\r
388 IN UINTN Pages,\r
389 IN VOID *HostAddress\r
390 )\r
391{\r
392 DEBUG ((DEBUG_VERBOSE, "IoMmuFreeBuffer: 0x%\n", Pages));\r
393 return gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress, Pages);\r
394}\r
395\r
396/**\r
397 Get device information from mapping.\r
398\r
399 @param[in] Mapping The mapping.\r
400 @param[out] DeviceAddress The device address of the mapping.\r
401 @param[out] NumberOfPages The number of pages of the mapping.\r
402\r
403 @retval EFI_SUCCESS The device information is returned.\r
404 @retval EFI_INVALID_PARAMETER The mapping is invalid.\r
405**/\r
406EFI_STATUS\r
407GetDeviceInfoFromMapping (\r
408 IN VOID *Mapping,\r
409 OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,\r
410 OUT UINTN *NumberOfPages\r
411 )\r
412{\r
413 MAP_INFO *MapInfo;\r
414 LIST_ENTRY *Link;\r
415\r
416 if (Mapping == NULL) {\r
417 return EFI_INVALID_PARAMETER;\r
418 }\r
419\r
420 MapInfo = NULL;\r
421 for (Link = GetFirstNode (&gMaps)\r
422 ; !IsNull (&gMaps, Link)\r
423 ; Link = GetNextNode (&gMaps, Link)\r
424 ) {\r
425 MapInfo = MAP_INFO_FROM_LINK (Link);\r
426 if (MapInfo == Mapping) {\r
427 break;\r
428 }\r
429 }\r
430 //\r
431 // Mapping is not a valid value returned by Map()\r
432 //\r
433 if (MapInfo != Mapping) {\r
434 return EFI_INVALID_PARAMETER;\r
435 }\r
436\r
437 *DeviceAddress = MapInfo->DeviceAddress;\r
438 *NumberOfPages = MapInfo->NumberOfPages;\r
439 return EFI_SUCCESS;\r
440}\r
441\r