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