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