MdeModulePkg/NonDiscoverable: fix memory override bug
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / NonDiscoverablePciDeviceDxe / NonDiscoverablePciDeviceIo.c
CommitLineData
a42e6d44
AB
1/** @file\r
2\r
3 Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>\r
4 Copyright (c) 2016, Linaro, Ltd. All rights reserved.<BR>\r
5\r
6 This program and the accompanying materials\r
7 are licensed and made available under the terms and conditions of the BSD License\r
8 which accompanies this distribution. The full text of the license may be found at\r
9 http://opensource.org/licenses/bsd-license.php\r
10\r
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13\r
14**/\r
15\r
16#include "NonDiscoverablePciDeviceIo.h"\r
17\r
16296a12
AB
18#include <Library/DxeServicesTableLib.h>\r
19\r
a42e6d44
AB
20#include <IndustryStandard/Acpi.h>\r
21\r
22#include <Protocol/PciRootBridgeIo.h>\r
23\r
24typedef struct {\r
25 EFI_PHYSICAL_ADDRESS AllocAddress;\r
26 VOID *HostAddress;\r
27 EFI_PCI_IO_PROTOCOL_OPERATION Operation;\r
28 UINTN NumberOfBytes;\r
29} NON_DISCOVERABLE_PCI_DEVICE_MAP_INFO;\r
30\r
43b7cd61
DB
31/**\r
32 Get the resource associated with BAR number 'BarIndex'.\r
33\r
34 @param Dev Point to the NON_DISCOVERABLE_PCI_DEVICE instance.\r
35 @param BarIndex The BAR index of the standard PCI Configuration header to use as the\r
36 base address for the memory operation to perform.\r
37 @param Descriptor Points to the address space descriptor\r
38**/\r
a42e6d44
AB
39STATIC\r
40EFI_STATUS\r
41GetBarResource (\r
42 IN NON_DISCOVERABLE_PCI_DEVICE *Dev,\r
43 IN UINT8 BarIndex,\r
44 OUT EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR **Descriptor\r
45 )\r
46{\r
47 EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Desc;\r
48\r
49 if (BarIndex < Dev->BarOffset) {\r
50 return EFI_NOT_FOUND;\r
51 }\r
52\r
8b78de74 53 BarIndex -= (UINT8)Dev->BarOffset;\r
a42e6d44
AB
54\r
55 for (Desc = Dev->Device->Resources;\r
56 Desc->Desc != ACPI_END_TAG_DESCRIPTOR;\r
57 Desc = (VOID *)((UINT8 *)Desc + Desc->Len + 3)) {\r
58\r
59 if (BarIndex == 0) {\r
60 *Descriptor = Desc;\r
61 return EFI_SUCCESS;\r
62 }\r
63\r
64 BarIndex -= 1;\r
65 }\r
66 return EFI_NOT_FOUND;\r
67}\r
68\r
43b7cd61
DB
69/**\r
70 Reads from the memory space of a PCI controller. Returns either when the polling exit criteria is\r
71 satisfied or after a defined duration.\r
72\r
73 @param This A pointer to the EFI_PCI_IO_PROTOCOL instance.\r
74 @param Width Signifies the width of the memory or I/O operations.\r
75 @param BarIndex The BAR index of the standard PCI Configuration header to use as the\r
76 base address for the memory operation to perform.\r
77 @param Offset The offset within the selected BAR to start the memory operation.\r
78 @param Mask Mask used for the polling criteria.\r
79 @param Value The comparison value used for the polling exit criteria.\r
80 @param Delay The number of 100 ns units to poll.\r
81 @param Result Pointer to the last value read from the memory location.\r
82\r
83**/\r
a42e6d44
AB
84STATIC\r
85EFI_STATUS\r
8b78de74 86EFIAPI\r
a42e6d44
AB
87PciIoPollMem (\r
88 IN EFI_PCI_IO_PROTOCOL *This,\r
89 IN EFI_PCI_IO_PROTOCOL_WIDTH Width,\r
90 IN UINT8 BarIndex,\r
91 IN UINT64 Offset,\r
92 IN UINT64 Mask,\r
93 IN UINT64 Value,\r
94 IN UINT64 Delay,\r
95 OUT UINT64 *Result\r
96 )\r
97{\r
98 ASSERT (FALSE);\r
99 return EFI_UNSUPPORTED;\r
100}\r
101\r
43b7cd61
DB
102/**\r
103 Reads from the memory space of a PCI controller. Returns either when the polling exit criteria is\r
104 satisfied or after a defined duration.\r
105\r
106 @param This A pointer to the EFI_PCI_IO_PROTOCOL instance.\r
107 @param Width Signifies the width of the memory or I/O operations.\r
108 @param BarIndex The BAR index of the standard PCI Configuration header to use as the\r
109 base address for the memory operation to perform.\r
110 @param Offset The offset within the selected BAR to start the memory operation.\r
111 @param Mask Mask used for the polling criteria.\r
112 @param Value The comparison value used for the polling exit criteria.\r
113 @param Delay The number of 100 ns units to poll.\r
114 @param Result Pointer to the last value read from the memory location.\r
115\r
116**/\r
a42e6d44
AB
117STATIC\r
118EFI_STATUS\r
8b78de74 119EFIAPI\r
a42e6d44
AB
120PciIoPollIo (\r
121 IN EFI_PCI_IO_PROTOCOL *This,\r
122 IN EFI_PCI_IO_PROTOCOL_WIDTH Width,\r
123 IN UINT8 BarIndex,\r
124 IN UINT64 Offset,\r
125 IN UINT64 Mask,\r
126 IN UINT64 Value,\r
127 IN UINT64 Delay,\r
128 OUT UINT64 *Result\r
129 )\r
130{\r
131 ASSERT (FALSE);\r
132 return EFI_UNSUPPORTED;\r
133}\r
134\r
43b7cd61
DB
135/**\r
136 Enable a PCI driver to access PCI controller registers in the PCI memory or I/O space.\r
137\r
138 @param Width Signifies the width of the memory or I/O operations.\r
139 @param Count The number of memory or I/O operations to perform.\r
140 @param DstStride The stride of the destination buffer.\r
141 @param Dst For read operations, the destination buffer to store the results. For write\r
142 operations, the destination buffer to write data to.\r
143 @param SrcStride The stride of the source buffer.\r
144 @param Src For read operations, the source buffer to read data from. For write\r
145 operations, the source buffer to write data from.\r
146\r
147 @retval EFI_SUCCESS The data was read from or written to the PCI controller.\r
148 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
149\r
150**/\r
a42e6d44
AB
151STATIC\r
152EFI_STATUS\r
8b78de74 153EFIAPI\r
a42e6d44
AB
154PciIoMemRW (\r
155 IN EFI_PCI_IO_PROTOCOL_WIDTH Width,\r
156 IN UINTN Count,\r
157 IN UINTN DstStride,\r
158 IN VOID *Dst,\r
159 IN UINTN SrcStride,\r
160 OUT CONST VOID *Src\r
161 )\r
162{\r
163 volatile UINT8 *Dst8;\r
164 volatile UINT16 *Dst16;\r
165 volatile UINT32 *Dst32;\r
166 volatile CONST UINT8 *Src8;\r
167 volatile CONST UINT16 *Src16;\r
168 volatile CONST UINT32 *Src32;\r
169\r
170 //\r
171 // Loop for each iteration and move the data\r
172 //\r
173 switch (Width & 0x3) {\r
174 case EfiPciWidthUint8:\r
175 Dst8 = (UINT8 *)Dst;\r
176 Src8 = (UINT8 *)Src;\r
177 for (;Count > 0; Count--, Dst8 += DstStride, Src8 += SrcStride) {\r
178 *Dst8 = *Src8;\r
179 }\r
180 break;\r
181 case EfiPciWidthUint16:\r
182 Dst16 = (UINT16 *)Dst;\r
183 Src16 = (UINT16 *)Src;\r
184 for (;Count > 0; Count--, Dst16 += DstStride, Src16 += SrcStride) {\r
185 *Dst16 = *Src16;\r
186 }\r
187 break;\r
188 case EfiPciWidthUint32:\r
189 Dst32 = (UINT32 *)Dst;\r
190 Src32 = (UINT32 *)Src;\r
191 for (;Count > 0; Count--, Dst32 += DstStride, Src32 += SrcStride) {\r
192 *Dst32 = *Src32;\r
193 }\r
194 break;\r
195 default:\r
196 return EFI_INVALID_PARAMETER;\r
197 }\r
198\r
199 return EFI_SUCCESS;\r
200}\r
201\r
43b7cd61
DB
202/**\r
203 Enable a PCI driver to access PCI controller registers in the PCI memory or I/O space.\r
204\r
205 @param This A pointer to the EFI_PCI_IO_PROTOCOL instance.\r
206 @param Width Signifies the width of the memory or I/O operations.\r
207 @param BarIndex The BAR index of the standard PCI Configuration header to use as the\r
208 base address for the memory or I/O operation to perform.\r
209 @param Offset The offset within the selected BAR to start the memory or I/O operation.\r
210 @param Count The number of memory or I/O operations to perform.\r
211 @param Buffer For read operations, the destination buffer to store the results. For write\r
212 operations, the source buffer to write data from.\r
213\r
214 @retval EFI_SUCCESS The data was read from or written to the PCI controller.\r
215 @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller.\r
216 @retval EFI_UNSUPPORTED The address range specified by Offset, Width, and Count is not\r
217 valid for the PCI BAR specified by BarIndex.\r
218 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.\r
219 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
220\r
221**/\r
a42e6d44
AB
222STATIC\r
223EFI_STATUS\r
8b78de74 224EFIAPI\r
a42e6d44
AB
225PciIoMemRead (\r
226 IN EFI_PCI_IO_PROTOCOL *This,\r
227 IN EFI_PCI_IO_PROTOCOL_WIDTH Width,\r
228 IN UINT8 BarIndex,\r
229 IN UINT64 Offset,\r
230 IN UINTN Count,\r
231 IN OUT VOID *Buffer\r
232 )\r
233{\r
234 NON_DISCOVERABLE_PCI_DEVICE *Dev;\r
235 UINTN AlignMask;\r
236 VOID *Address;\r
237 EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Desc;\r
238 EFI_STATUS Status;\r
239\r
240 if (Buffer == NULL) {\r
241 return EFI_INVALID_PARAMETER;\r
242 }\r
243\r
244 Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This);\r
245\r
246 //\r
247 // Only allow accesses to the BARs we emulate\r
248 //\r
249 Status = GetBarResource (Dev, BarIndex, &Desc);\r
250 if (EFI_ERROR (Status)) {\r
251 return Status;\r
252 }\r
253\r
254 if (Offset + (Count << (Width & 0x3)) > Desc->AddrLen) {\r
255 return EFI_UNSUPPORTED;\r
256 }\r
257\r
258 Address = (VOID *)(UINTN)(Desc->AddrRangeMin + Offset);\r
259 AlignMask = (1 << (Width & 0x03)) - 1;\r
260 if ((UINTN)Address & AlignMask) {\r
261 return EFI_INVALID_PARAMETER;\r
262 }\r
263\r
264 switch (Width) {\r
82bf462e
HW
265 case EfiPciIoWidthUint8:\r
266 case EfiPciIoWidthUint16:\r
267 case EfiPciIoWidthUint32:\r
268 case EfiPciIoWidthUint64:\r
a42e6d44
AB
269 return PciIoMemRW (Width, Count, 1, Buffer, 1, Address);\r
270\r
82bf462e
HW
271 case EfiPciIoWidthFifoUint8:\r
272 case EfiPciIoWidthFifoUint16:\r
273 case EfiPciIoWidthFifoUint32:\r
274 case EfiPciIoWidthFifoUint64:\r
a42e6d44
AB
275 return PciIoMemRW (Width, Count, 1, Buffer, 0, Address);\r
276\r
82bf462e
HW
277 case EfiPciIoWidthFillUint8:\r
278 case EfiPciIoWidthFillUint16:\r
279 case EfiPciIoWidthFillUint32:\r
280 case EfiPciIoWidthFillUint64:\r
a42e6d44
AB
281 return PciIoMemRW (Width, Count, 0, Buffer, 1, Address);\r
282\r
283 default:\r
284 break;\r
285 }\r
286 return EFI_INVALID_PARAMETER;\r
287}\r
288\r
43b7cd61
DB
289/**\r
290 Enable a PCI driver to access PCI controller registers in the PCI memory or I/O space.\r
291\r
292 @param This A pointer to the EFI_PCI_IO_PROTOCOL instance.\r
293 @param Width Signifies the width of the memory or I/O operations.\r
294 @param BarIndex The BAR index of the standard PCI Configuration header to use as the\r
295 base address for the memory or I/O operation to perform.\r
296 @param Offset The offset within the selected BAR to start the memory or I/O operation.\r
297 @param Count The number of memory or I/O operations to perform.\r
298 @param Buffer For read operations, the destination buffer to store the results. For write\r
299 operations, the source buffer to write data from.\r
300\r
301 @retval EFI_SUCCESS The data was read from or written to the PCI controller.\r
302 @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller.\r
303 @retval EFI_UNSUPPORTED The address range specified by Offset, Width, and Count is not\r
304 valid for the PCI BAR specified by BarIndex.\r
305 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.\r
306 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
307\r
308**/\r
a42e6d44
AB
309STATIC\r
310EFI_STATUS\r
8b78de74 311EFIAPI\r
a42e6d44
AB
312PciIoMemWrite (\r
313 IN EFI_PCI_IO_PROTOCOL *This,\r
314 IN EFI_PCI_IO_PROTOCOL_WIDTH Width,\r
315 IN UINT8 BarIndex,\r
316 IN UINT64 Offset,\r
317 IN UINTN Count,\r
318 IN OUT VOID *Buffer\r
319 )\r
320{\r
321 NON_DISCOVERABLE_PCI_DEVICE *Dev;\r
322 UINTN AlignMask;\r
323 VOID *Address;\r
324 EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Desc;\r
325 EFI_STATUS Status;\r
326\r
327 if (Buffer == NULL) {\r
328 return EFI_INVALID_PARAMETER;\r
329 }\r
330\r
331 Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This);\r
332\r
333 //\r
334 // Only allow accesses to the BARs we emulate\r
335 //\r
336 Status = GetBarResource (Dev, BarIndex, &Desc);\r
337 if (EFI_ERROR (Status)) {\r
338 return Status;\r
339 }\r
340\r
341 if (Offset + (Count << (Width & 0x3)) > Desc->AddrLen) {\r
342 return EFI_UNSUPPORTED;\r
343 }\r
344\r
345 Address = (VOID *)(UINTN)(Desc->AddrRangeMin + Offset);\r
346 AlignMask = (1 << (Width & 0x03)) - 1;\r
347 if ((UINTN)Address & AlignMask) {\r
348 return EFI_INVALID_PARAMETER;\r
349 }\r
350\r
351 switch (Width) {\r
82bf462e
HW
352 case EfiPciIoWidthUint8:\r
353 case EfiPciIoWidthUint16:\r
354 case EfiPciIoWidthUint32:\r
355 case EfiPciIoWidthUint64:\r
a42e6d44
AB
356 return PciIoMemRW (Width, Count, 1, Address, 1, Buffer);\r
357\r
82bf462e
HW
358 case EfiPciIoWidthFifoUint8:\r
359 case EfiPciIoWidthFifoUint16:\r
360 case EfiPciIoWidthFifoUint32:\r
361 case EfiPciIoWidthFifoUint64:\r
a42e6d44
AB
362 return PciIoMemRW (Width, Count, 0, Address, 1, Buffer);\r
363\r
82bf462e
HW
364 case EfiPciIoWidthFillUint8:\r
365 case EfiPciIoWidthFillUint16:\r
366 case EfiPciIoWidthFillUint32:\r
367 case EfiPciIoWidthFillUint64:\r
a42e6d44
AB
368 return PciIoMemRW (Width, Count, 1, Address, 0, Buffer);\r
369\r
370 default:\r
371 break;\r
372 }\r
373 return EFI_INVALID_PARAMETER;\r
374}\r
375\r
43b7cd61
DB
376/**\r
377 Enable a PCI driver to access PCI controller registers in the PCI memory or I/O space.\r
378\r
379 @param This A pointer to the EFI_PCI_IO_PROTOCOL instance.\r
380 @param Width Signifies the width of the memory or I/O operations.\r
381 @param BarIndex The BAR index of the standard PCI Configuration header to use as the\r
382 base address for the memory or I/O operation to perform.\r
383 @param Offset The offset within the selected BAR to start the memory or I/O operation.\r
384 @param Count The number of memory or I/O operations to perform.\r
385 @param Buffer For read operations, the destination buffer to store the results. For write\r
386 operations, the source buffer to write data from.\r
387\r
388**/\r
a42e6d44
AB
389STATIC\r
390EFI_STATUS\r
8b78de74 391EFIAPI\r
a42e6d44
AB
392PciIoIoRead (\r
393 IN EFI_PCI_IO_PROTOCOL *This,\r
394 IN EFI_PCI_IO_PROTOCOL_WIDTH Width,\r
395 IN UINT8 BarIndex,\r
396 IN UINT64 Offset,\r
397 IN UINTN Count,\r
398 IN OUT VOID *Buffer\r
399 )\r
400{\r
401 ASSERT (FALSE);\r
402 return EFI_UNSUPPORTED;\r
403}\r
404\r
43b7cd61
DB
405/**\r
406 Enable a PCI driver to access PCI controller registers in the PCI memory or I/O space.\r
407\r
408 @param This A pointer to the EFI_PCI_IO_PROTOCOL instance.\r
409 @param Width Signifies the width of the memory or I/O operations.\r
410 @param BarIndex The BAR index of the standard PCI Configuration header to use as the\r
411 base address for the memory or I/O operation to perform.\r
412 @param Offset The offset within the selected BAR to start the memory or I/O operation.\r
413 @param Count The number of memory or I/O operations to perform.\r
414 @param Buffer For read operations, the destination buffer to store the results. For write\r
415 operations, the source buffer to write data from.\r
416\r
417**/\r
a42e6d44
AB
418STATIC\r
419EFI_STATUS\r
8b78de74 420EFIAPI\r
a42e6d44
AB
421PciIoIoWrite (\r
422 IN EFI_PCI_IO_PROTOCOL *This,\r
423 IN EFI_PCI_IO_PROTOCOL_WIDTH Width,\r
424 IN UINT8 BarIndex,\r
425 IN UINT64 Offset,\r
426 IN UINTN Count,\r
427 IN OUT VOID *Buffer\r
428 )\r
429{\r
430 ASSERT (FALSE);\r
431 return EFI_UNSUPPORTED;\r
432}\r
433\r
43b7cd61
DB
434/**\r
435 Enable a PCI driver to access PCI config space.\r
436\r
437 @param This A pointer to the EFI_PCI_IO_PROTOCOL instance.\r
438 @param Width Signifies the width of the memory or I/O operations.\r
439 @param Offset The offset within the selected BAR to start the memory or I/O operation.\r
440 @param Count The number of memory or I/O operations to perform.\r
441 @param Buffer For read operations, the destination buffer to store the results. For write\r
442 operations, the source buffer to write data from.\r
443\r
444**/\r
a42e6d44
AB
445STATIC\r
446EFI_STATUS\r
8b78de74 447EFIAPI\r
a42e6d44
AB
448PciIoPciRead (\r
449 IN EFI_PCI_IO_PROTOCOL *This,\r
450 IN EFI_PCI_IO_PROTOCOL_WIDTH Width,\r
451 IN UINT32 Offset,\r
452 IN UINTN Count,\r
453 IN OUT VOID *Buffer\r
454 )\r
455{\r
456 NON_DISCOVERABLE_PCI_DEVICE *Dev;\r
457 VOID *Address;\r
458 UINTN Length;\r
459\r
460 if (Width < 0 || Width >= EfiPciIoWidthMaximum || Buffer == NULL) {\r
461 return EFI_INVALID_PARAMETER;\r
462 }\r
463\r
464 Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This);\r
465 Address = (UINT8 *)&Dev->ConfigSpace + Offset;\r
466 Length = Count << ((UINTN)Width & 0x3);\r
467\r
710d9e69
HG
468 if (Offset >= sizeof (Dev->ConfigSpace)) {\r
469 ZeroMem (Buffer, Length);\r
470 return EFI_SUCCESS;\r
471 }\r
472\r
a42e6d44
AB
473 if (Offset + Length > sizeof (Dev->ConfigSpace)) {\r
474 //\r
475 // Read all zeroes for config space accesses beyond the first\r
476 // 64 bytes\r
477 //\r
478 Length -= sizeof (Dev->ConfigSpace) - Offset;\r
479 ZeroMem ((UINT8 *)Buffer + sizeof (Dev->ConfigSpace) - Offset, Length);\r
480\r
481 Count -= Length >> ((UINTN)Width & 0x3);\r
482 }\r
483 return PciIoMemRW (Width, Count, 1, Buffer, 1, Address);\r
484}\r
485\r
43b7cd61
DB
486/**\r
487 Enable a PCI driver to access PCI config space.\r
488\r
489 @param This A pointer to the EFI_PCI_IO_PROTOCOL instance.\r
490 @param Width Signifies the width of the memory or I/O operations.\r
491 @param Offset The offset within the selected BAR to start the memory or I/O operation.\r
492 @param Count The number of memory or I/O operations to perform.\r
493 @param Buffer For read operations, the destination buffer to store the results. For write\r
494 operations, the source buffer to write data from\r
495\r
496 @retval EFI_SUCCESS The data was read from or written to the PCI controller.\r
497 @retval EFI_UNSUPPORTED The address range specified by Offset, Width, and Count is not\r
498 valid for the PCI BAR specified by BarIndex.\r
499 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
500\r
501**/\r
a42e6d44
AB
502STATIC\r
503EFI_STATUS\r
8b78de74 504EFIAPI\r
a42e6d44
AB
505PciIoPciWrite (\r
506 IN EFI_PCI_IO_PROTOCOL *This,\r
507 IN EFI_PCI_IO_PROTOCOL_WIDTH Width,\r
508 IN UINT32 Offset,\r
509 IN UINTN Count,\r
510 IN OUT VOID *Buffer\r
511 )\r
512{\r
513 NON_DISCOVERABLE_PCI_DEVICE *Dev;\r
514 VOID *Address;\r
515\r
516 if (Width < 0 || Width >= EfiPciIoWidthMaximum || Buffer == NULL) {\r
517 return EFI_INVALID_PARAMETER;\r
518 }\r
519\r
520 Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This);\r
521 Address = (UINT8 *)&Dev->ConfigSpace + Offset;\r
522\r
523 if (Offset + (Count << ((UINTN)Width & 0x3)) > sizeof (Dev->ConfigSpace)) {\r
524 return EFI_UNSUPPORTED;\r
525 }\r
526\r
527 return PciIoMemRW (Width, Count, 1, Address, 1, Buffer);\r
528}\r
529\r
43b7cd61
DB
530/**\r
531 Enables a PCI driver to copy one region of PCI memory space to another region of PCI\r
532 memory space.\r
533\r
534 @param This A pointer to the EFI_PCI_IO_PROTOCOL instance.\r
535 @param Width Signifies the width of the memory operations.\r
536 @param DestBarIndex The BAR index in the standard PCI Configuration header to use as the\r
537 base address for the memory operation to perform.\r
538 @param DestOffset The destination offset within the BAR specified by DestBarIndex to\r
539 start the memory writes for the copy operation.\r
540 @param SrcBarIndex The BAR index in the standard PCI Configuration header to use as the\r
541 base address for the memory operation to perform.\r
542 @param SrcOffset The source offset within the BAR specified by SrcBarIndex to start\r
543 the memory reads for the copy operation.\r
544 @param Count The number of memory operations to perform. Bytes moved is Width\r
545 size * Count, starting at DestOffset and SrcOffset.\r
546\r
547**/\r
a42e6d44
AB
548STATIC\r
549EFI_STATUS\r
8b78de74 550EFIAPI\r
a42e6d44
AB
551PciIoCopyMem (\r
552 IN EFI_PCI_IO_PROTOCOL *This,\r
553 IN EFI_PCI_IO_PROTOCOL_WIDTH Width,\r
554 IN UINT8 DestBarIndex,\r
555 IN UINT64 DestOffset,\r
556 IN UINT8 SrcBarIndex,\r
557 IN UINT64 SrcOffset,\r
558 IN UINTN Count\r
559 )\r
560{\r
561 ASSERT (FALSE);\r
562 return EFI_UNSUPPORTED;\r
563}\r
564\r
43b7cd61
DB
565/**\r
566 Provides the PCI controller-specific addresses needed to access system memory.\r
567\r
568 @param This A pointer to the EFI_PCI_IO_PROTOCOL instance.\r
569 @param Operation Indicates if the bus master is going to read or write to system memory.\r
570 @param HostAddress The system memory address to map to the PCI controller.\r
571 @param NumberOfBytes On input the number of bytes to map. On output the number of bytes\r
572 that were mapped.\r
573 @param DeviceAddress The resulting map address for the bus master PCI controller to use to\r
574 access the hosts HostAddress.\r
575 @param Mapping A resulting value to pass to Unmap().\r
576\r
577 @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes.\r
578 @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer.\r
579 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
580 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.\r
581 @retval EFI_DEVICE_ERROR The system hardware could not map the requested address.\r
582\r
583**/\r
a42e6d44
AB
584STATIC\r
585EFI_STATUS\r
8b78de74 586EFIAPI\r
a42e6d44
AB
587CoherentPciIoMap (\r
588 IN EFI_PCI_IO_PROTOCOL *This,\r
589 IN EFI_PCI_IO_PROTOCOL_OPERATION Operation,\r
590 IN VOID *HostAddress,\r
591 IN OUT UINTN *NumberOfBytes,\r
592 OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,\r
593 OUT VOID **Mapping\r
594 )\r
595{\r
596 NON_DISCOVERABLE_PCI_DEVICE *Dev;\r
597 EFI_STATUS Status;\r
598 NON_DISCOVERABLE_PCI_DEVICE_MAP_INFO *MapInfo;\r
599\r
600 //\r
601 // If HostAddress exceeds 4 GB, and this device does not support 64-bit DMA\r
602 // addressing, we need to allocate a bounce buffer and copy over the data.\r
603 //\r
604 Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This);\r
605 if ((Dev->Attributes & EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0 &&\r
b494cf96 606 (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress + *NumberOfBytes > SIZE_4GB) {\r
a42e6d44
AB
607\r
608 //\r
609 // Bounce buffering is not possible for consistent mappings\r
610 //\r
611 if (Operation == EfiPciIoOperationBusMasterCommonBuffer) {\r
612 return EFI_UNSUPPORTED;\r
613 }\r
614\r
615 MapInfo = AllocatePool (sizeof *MapInfo);\r
616 if (MapInfo == NULL) {\r
617 return EFI_OUT_OF_RESOURCES;\r
618 }\r
619\r
620 MapInfo->AllocAddress = MAX_UINT32;\r
621 MapInfo->HostAddress = HostAddress;\r
622 MapInfo->Operation = Operation;\r
623 MapInfo->NumberOfBytes = *NumberOfBytes;\r
624\r
625 Status = gBS->AllocatePages (AllocateMaxAddress, EfiBootServicesData,\r
626 EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes),\r
627 &MapInfo->AllocAddress);\r
628 if (EFI_ERROR (Status)) {\r
629 //\r
630 // If we fail here, it is likely because the system has no memory below\r
631 // 4 GB to begin with. There is not much we can do about that other than\r
632 // fail the map request.\r
633 //\r
634 FreePool (MapInfo);\r
635 return EFI_DEVICE_ERROR;\r
636 }\r
637 if (Operation == EfiPciIoOperationBusMasterRead) {\r
638 gBS->CopyMem ((VOID *)(UINTN)MapInfo->AllocAddress, HostAddress,\r
639 *NumberOfBytes);\r
640 }\r
641 *DeviceAddress = MapInfo->AllocAddress;\r
642 *Mapping = MapInfo;\r
643 } else {\r
644 *DeviceAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress;\r
645 *Mapping = NULL;\r
646 }\r
647 return EFI_SUCCESS;\r
648}\r
649\r
43b7cd61
DB
650/**\r
651 Completes the Map() operation and releases any corresponding resources.\r
652\r
653 @param This A pointer to the EFI_PCI_IO_PROTOCOL instance.\r
654 @param Mapping The mapping value returned from Map().\r
655\r
656 @retval EFI_SUCCESS The range was unmapped.\r
657\r
658**/\r
a42e6d44
AB
659STATIC\r
660EFI_STATUS\r
8b78de74 661EFIAPI\r
a42e6d44
AB
662CoherentPciIoUnmap (\r
663 IN EFI_PCI_IO_PROTOCOL *This,\r
664 IN VOID *Mapping\r
665 )\r
666{\r
667 NON_DISCOVERABLE_PCI_DEVICE_MAP_INFO *MapInfo;\r
668\r
669 MapInfo = Mapping;\r
670 if (MapInfo != NULL) {\r
671 if (MapInfo->Operation == EfiPciIoOperationBusMasterWrite) {\r
672 gBS->CopyMem (MapInfo->HostAddress, (VOID *)(UINTN)MapInfo->AllocAddress,\r
673 MapInfo->NumberOfBytes);\r
674 }\r
675 gBS->FreePages (MapInfo->AllocAddress,\r
676 EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes));\r
677 FreePool (MapInfo);\r
678 }\r
679 return EFI_SUCCESS;\r
680}\r
681\r
43b7cd61
DB
682/**\r
683 Allocates pages.\r
684\r
685 @param This A pointer to the EFI_PCI_IO_PROTOCOL instance.\r
686 @param Type This parameter is not used and must be ignored.\r
687 @param MemoryType The type of memory to allocate, EfiBootServicesData or\r
688 EfiRuntimeServicesData.\r
689 @param Pages The number of pages to allocate.\r
690 @param HostAddress A pointer to store the base system memory address of the\r
691 allocated range.\r
692 @param Attributes The requested bit mask of attributes for the allocated range.\r
693\r
694 @retval EFI_SUCCESS The requested memory pages were allocated.\r
695 @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are\r
696 MEMORY_WRITE_COMBINE and MEMORY_CACHED.\r
697 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
698 @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.\r
699\r
700**/\r
a42e6d44
AB
701STATIC\r
702EFI_STATUS\r
8b78de74 703EFIAPI\r
a42e6d44
AB
704CoherentPciIoAllocateBuffer (\r
705 IN EFI_PCI_IO_PROTOCOL *This,\r
706 IN EFI_ALLOCATE_TYPE Type,\r
707 IN EFI_MEMORY_TYPE MemoryType,\r
708 IN UINTN Pages,\r
709 OUT VOID **HostAddress,\r
710 IN UINT64 Attributes\r
711 )\r
712{\r
713 NON_DISCOVERABLE_PCI_DEVICE *Dev;\r
714 EFI_PHYSICAL_ADDRESS AllocAddress;\r
715 EFI_ALLOCATE_TYPE AllocType;\r
716 EFI_STATUS Status;\r
717\r
718 if ((Attributes & ~(EFI_PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE |\r
719 EFI_PCI_ATTRIBUTE_MEMORY_CACHED)) != 0) {\r
720 return EFI_UNSUPPORTED;\r
721 }\r
722\r
723 //\r
724 // Allocate below 4 GB if the dual address cycle attribute has not\r
725 // been set. If the system has no memory available below 4 GB, there\r
726 // is little we can do except propagate the error.\r
727 //\r
728 Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This);\r
729 if ((Dev->Attributes & EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0) {\r
730 AllocAddress = MAX_UINT32;\r
731 AllocType = AllocateMaxAddress;\r
732 } else {\r
733 AllocType = AllocateAnyPages;\r
734 }\r
735\r
736 Status = gBS->AllocatePages (AllocType, MemoryType, Pages, &AllocAddress);\r
737 if (!EFI_ERROR (Status)) {\r
738 *HostAddress = (VOID *)(UINTN)AllocAddress;\r
739 }\r
740 return Status;\r
741}\r
742\r
43b7cd61
DB
743/**\r
744 Frees memory that was allocated in function CoherentPciIoAllocateBuffer ().\r
745\r
746 @param This A pointer to the EFI_PCI_IO_PROTOCOL instance.\r
747 @param Pages The number of pages to free.\r
748 @param HostAddress The base system memory address of the allocated range.\r
749\r
750 @retval EFI_SUCCESS The requested memory pages were freed.\r
751\r
752**/\r
a42e6d44
AB
753STATIC\r
754EFI_STATUS\r
8b78de74 755EFIAPI\r
a42e6d44
AB
756CoherentPciIoFreeBuffer (\r
757 IN EFI_PCI_IO_PROTOCOL *This,\r
758 IN UINTN Pages,\r
759 IN VOID *HostAddress\r
760 )\r
761{\r
762 FreePages (HostAddress, Pages);\r
763 return EFI_SUCCESS;\r
764}\r
765\r
43b7cd61
DB
766/**\r
767 Frees memory that was allocated in function NonCoherentPciIoAllocateBuffer ().\r
768\r
769 @param This A pointer to the EFI_PCI_IO_PROTOCOL instance.\r
770 @param Pages The number of pages to free.\r
771 @param HostAddress The base system memory address of the allocated range.\r
772\r
773 @retval EFI_SUCCESS The requested memory pages were freed.\r
774 @retval others The operation contain some errors.\r
775\r
776**/\r
16296a12
AB
777STATIC\r
778EFI_STATUS\r
779EFIAPI\r
780NonCoherentPciIoFreeBuffer (\r
781 IN EFI_PCI_IO_PROTOCOL *This,\r
782 IN UINTN Pages,\r
783 IN VOID *HostAddress\r
784 )\r
785{\r
786 NON_DISCOVERABLE_PCI_DEVICE *Dev;\r
787 LIST_ENTRY *Entry;\r
788 EFI_STATUS Status;\r
789 NON_DISCOVERABLE_DEVICE_UNCACHED_ALLOCATION *Alloc;\r
790 BOOLEAN Found;\r
791\r
792 Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This);\r
793\r
794 Found = FALSE;\r
1525ff38 795 Alloc = NULL;\r
16296a12
AB
796\r
797 //\r
798 // Find the uncached allocation list entry associated\r
799 // with this allocation\r
800 //\r
801 for (Entry = Dev->UncachedAllocationList.ForwardLink;\r
802 Entry != &Dev->UncachedAllocationList;\r
803 Entry = Entry->ForwardLink) {\r
804\r
805 Alloc = BASE_CR (Entry, NON_DISCOVERABLE_DEVICE_UNCACHED_ALLOCATION, List);\r
806 if (Alloc->HostAddress == HostAddress && Alloc->NumPages == Pages) {\r
807 //\r
808 // We are freeing the exact allocation we were given\r
809 // before by AllocateBuffer()\r
810 //\r
811 Found = TRUE;\r
812 break;\r
813 }\r
814 }\r
815\r
816 if (!Found) {\r
817 ASSERT_EFI_ERROR (EFI_NOT_FOUND);\r
818 return EFI_NOT_FOUND;\r
819 }\r
820\r
821 RemoveEntryList (&Alloc->List);\r
822\r
823 Status = gDS->SetMemorySpaceAttributes (\r
824 (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress,\r
825 EFI_PAGES_TO_SIZE (Pages),\r
826 Alloc->Attributes);\r
827 if (EFI_ERROR (Status)) {\r
828 goto FreeAlloc;\r
829 }\r
830\r
831 //\r
832 // If we fail to restore the original attributes, it is better to leak the\r
833 // memory than to return it to the heap\r
834 //\r
835 FreePages (HostAddress, Pages);\r
836\r
837FreeAlloc:\r
838 FreePool (Alloc);\r
839 return Status;\r
840}\r
841\r
43b7cd61
DB
842/**\r
843 Allocates pages.\r
844\r
845 @param This A pointer to the EFI_PCI_IO_PROTOCOL instance.\r
846 @param Type This parameter is not used and must be ignored.\r
847 @param MemoryType The type of memory to allocate, EfiBootServicesData or\r
848 EfiRuntimeServicesData.\r
849 @param Pages The number of pages to allocate.\r
850 @param HostAddress A pointer to store the base system memory address of the\r
851 allocated range.\r
852 @param Attributes The requested bit mask of attributes for the allocated range.\r
853\r
854 @retval EFI_SUCCESS The requested memory pages were allocated.\r
855 @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are\r
856 MEMORY_WRITE_COMBINE and MEMORY_CACHED.\r
857 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
858 @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.\r
859\r
860**/\r
16296a12
AB
861STATIC\r
862EFI_STATUS\r
863EFIAPI\r
864NonCoherentPciIoAllocateBuffer (\r
865 IN EFI_PCI_IO_PROTOCOL *This,\r
866 IN EFI_ALLOCATE_TYPE Type,\r
867 IN EFI_MEMORY_TYPE MemoryType,\r
868 IN UINTN Pages,\r
869 OUT VOID **HostAddress,\r
870 IN UINT64 Attributes\r
871 )\r
872{\r
873 NON_DISCOVERABLE_PCI_DEVICE *Dev;\r
874 EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor;\r
875 EFI_STATUS Status;\r
876 UINT64 MemType;\r
877 NON_DISCOVERABLE_DEVICE_UNCACHED_ALLOCATION *Alloc;\r
878 VOID *AllocAddress;\r
879\r
880 Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This);\r
881\r
882 Status = CoherentPciIoAllocateBuffer (This, Type, MemoryType, Pages,\r
883 &AllocAddress, Attributes);\r
884 if (EFI_ERROR (Status)) {\r
885 return Status;\r
886 }\r
887\r
888 Status = gDS->GetMemorySpaceDescriptor (\r
889 (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress,\r
890 &GcdDescriptor);\r
891 if (EFI_ERROR (Status)) {\r
892 goto FreeBuffer;\r
893 }\r
894\r
895 if ((GcdDescriptor.Capabilities & (EFI_MEMORY_WC | EFI_MEMORY_UC)) == 0) {\r
896 Status = EFI_UNSUPPORTED;\r
897 goto FreeBuffer;\r
898 }\r
899\r
900 //\r
901 // Set the preferred memory attributes\r
902 //\r
903 if ((Attributes & EFI_PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE) != 0 ||\r
904 (GcdDescriptor.Capabilities & EFI_MEMORY_UC) == 0) {\r
905 //\r
906 // Use write combining if it was requested, or if it is the only\r
907 // type supported by the region.\r
908 //\r
909 MemType = EFI_MEMORY_WC;\r
910 } else {\r
911 MemType = EFI_MEMORY_UC;\r
912 }\r
913\r
914 Alloc = AllocatePool (sizeof *Alloc);\r
915 if (Alloc == NULL) {\r
916 goto FreeBuffer;\r
917 }\r
918\r
919 Alloc->HostAddress = AllocAddress;\r
920 Alloc->NumPages = Pages;\r
921 Alloc->Attributes = GcdDescriptor.Attributes;\r
922\r
923 //\r
924 // Record this allocation in the linked list, so we\r
925 // can restore the memory space attributes later\r
926 //\r
927 InsertHeadList (&Dev->UncachedAllocationList, &Alloc->List);\r
928\r
929 Status = gDS->SetMemorySpaceAttributes (\r
930 (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress,\r
931 EFI_PAGES_TO_SIZE (Pages),\r
932 MemType);\r
933 if (EFI_ERROR (Status)) {\r
934 goto RemoveList;\r
935 }\r
936\r
937 Status = mCpu->FlushDataCache (\r
938 mCpu,\r
939 (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress,\r
940 EFI_PAGES_TO_SIZE (Pages),\r
941 EfiCpuFlushTypeInvalidate);\r
942 if (EFI_ERROR (Status)) {\r
943 goto RemoveList;\r
944 }\r
945\r
946 *HostAddress = AllocAddress;\r
947\r
948 return EFI_SUCCESS;\r
949\r
950RemoveList:\r
951 RemoveEntryList (&Alloc->List);\r
952 FreePool (Alloc);\r
953\r
954FreeBuffer:\r
955 CoherentPciIoFreeBuffer (This, Pages, AllocAddress);\r
956 return Status;\r
957}\r
958\r
43b7cd61
DB
959/**\r
960 Provides the PCI controller-specific addresses needed to access system memory.\r
961\r
962 @param This A pointer to the EFI_PCI_IO_PROTOCOL instance.\r
963 @param Operation Indicates if the bus master is going to read or write to system memory.\r
964 @param HostAddress The system memory address to map to the PCI controller.\r
965 @param NumberOfBytes On input the number of bytes to map. On output the number of bytes\r
966 that were mapped.\r
967 @param DeviceAddress The resulting map address for the bus master PCI controller to use to\r
968 access the hosts HostAddress.\r
969 @param Mapping A resulting value to pass to Unmap().\r
970\r
971 @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes.\r
972 @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer.\r
973 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
974 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.\r
975 @retval EFI_DEVICE_ERROR The system hardware could not map the requested address.\r
976\r
977**/\r
16296a12
AB
978STATIC\r
979EFI_STATUS\r
980EFIAPI\r
981NonCoherentPciIoMap (\r
982 IN EFI_PCI_IO_PROTOCOL *This,\r
983 IN EFI_PCI_IO_PROTOCOL_OPERATION Operation,\r
984 IN VOID *HostAddress,\r
985 IN OUT UINTN *NumberOfBytes,\r
986 OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,\r
987 OUT VOID **Mapping\r
988 )\r
989{\r
990 NON_DISCOVERABLE_PCI_DEVICE *Dev;\r
991 EFI_STATUS Status;\r
992 NON_DISCOVERABLE_PCI_DEVICE_MAP_INFO *MapInfo;\r
993 UINTN AlignMask;\r
994 VOID *AllocAddress;\r
995 EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor;\r
996 BOOLEAN Bounce;\r
997\r
998 MapInfo = AllocatePool (sizeof *MapInfo);\r
999 if (MapInfo == NULL) {\r
1000 return EFI_OUT_OF_RESOURCES;\r
1001 }\r
1002\r
1003 MapInfo->HostAddress = HostAddress;\r
1004 MapInfo->Operation = Operation;\r
1005 MapInfo->NumberOfBytes = *NumberOfBytes;\r
1006\r
1007 Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This);\r
1008\r
1009 //\r
1010 // If this device does not support 64-bit DMA addressing, we need to allocate\r
1011 // a bounce buffer and copy over the data in case HostAddress >= 4 GB.\r
1012 //\r
1013 Bounce = ((Dev->Attributes & EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0 &&\r
b494cf96 1014 (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress + *NumberOfBytes > SIZE_4GB);\r
16296a12
AB
1015\r
1016 if (!Bounce) {\r
1017 switch (Operation) {\r
1018 case EfiPciIoOperationBusMasterRead:\r
1019 case EfiPciIoOperationBusMasterWrite:\r
1020 //\r
1021 // For streaming DMA, it is sufficient if the buffer is aligned to\r
1022 // the CPUs DMA buffer alignment.\r
1023 //\r
1024 AlignMask = mCpu->DmaBufferAlignment - 1;\r
1025 if ((((UINTN) HostAddress | *NumberOfBytes) & AlignMask) == 0) {\r
1026 break;\r
1027 }\r
1028 // fall through\r
1029\r
1030 case EfiPciIoOperationBusMasterCommonBuffer:\r
1031 //\r
1032 // Check whether the host address refers to an uncached mapping.\r
1033 //\r
1034 Status = gDS->GetMemorySpaceDescriptor (\r
1035 (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress,\r
1036 &GcdDescriptor);\r
1037 if (EFI_ERROR (Status) ||\r
1038 (GcdDescriptor.Attributes & (EFI_MEMORY_WB|EFI_MEMORY_WT)) != 0) {\r
1039 Bounce = TRUE;\r
1040 }\r
1041 break;\r
1042\r
1043 default:\r
1044 ASSERT (FALSE);\r
1045 }\r
1046 }\r
1047\r
1048 if (Bounce) {\r
1049 if (Operation == EfiPciIoOperationBusMasterCommonBuffer) {\r
1050 Status = EFI_DEVICE_ERROR;\r
1051 goto FreeMapInfo;\r
1052 }\r
1053\r
1054 Status = NonCoherentPciIoAllocateBuffer (This, AllocateAnyPages,\r
1055 EfiBootServicesData, EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes),\r
1056 &AllocAddress, EFI_PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE);\r
1057 if (EFI_ERROR (Status)) {\r
1058 goto FreeMapInfo;\r
1059 }\r
1060 MapInfo->AllocAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress;\r
1061 if (Operation == EfiPciIoOperationBusMasterRead) {\r
1062 gBS->CopyMem (AllocAddress, HostAddress, *NumberOfBytes);\r
1063 }\r
1064 *DeviceAddress = MapInfo->AllocAddress;\r
1065 } else {\r
1066 MapInfo->AllocAddress = 0;\r
1067 *DeviceAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress;\r
1068\r
1069 //\r
1070 // We are not using a bounce buffer: the mapping is sufficiently\r
1071 // aligned to allow us to simply flush the caches. Note that cleaning\r
1072 // the caches is necessary for both data directions:\r
1073 // - for bus master read, we want the latest data to be present\r
1074 // in main memory\r
1075 // - for bus master write, we don't want any stale dirty cachelines that\r
1076 // may be written back unexpectedly, and clobber the data written to\r
1077 // main memory by the device.\r
1078 //\r
1079 mCpu->FlushDataCache (mCpu, (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress,\r
1080 *NumberOfBytes, EfiCpuFlushTypeWriteBack);\r
1081 }\r
1082\r
1083 *Mapping = MapInfo;\r
1084 return EFI_SUCCESS;\r
1085\r
1086FreeMapInfo:\r
1087 FreePool (MapInfo);\r
1088\r
1089 return Status;\r
1090}\r
1091\r
43b7cd61
DB
1092/**\r
1093 Completes the Map() operation and releases any corresponding resources.\r
1094\r
1095 @param This A pointer to the EFI_PCI_IO_PROTOCOL instance.\r
1096 @param Mapping The mapping value returned from Map().\r
1097\r
1098 @retval EFI_SUCCESS The range was unmapped.\r
1099\r
1100**/\r
16296a12
AB
1101STATIC\r
1102EFI_STATUS\r
1103EFIAPI\r
1104NonCoherentPciIoUnmap (\r
1105 IN EFI_PCI_IO_PROTOCOL *This,\r
1106 IN VOID *Mapping\r
1107 )\r
1108{\r
1109 NON_DISCOVERABLE_PCI_DEVICE_MAP_INFO *MapInfo;\r
1110\r
1111 if (Mapping == NULL) {\r
1112 return EFI_DEVICE_ERROR;\r
1113 }\r
1114\r
1115 MapInfo = Mapping;\r
1116 if (MapInfo->AllocAddress != 0) {\r
1117 //\r
1118 // We are using a bounce buffer: copy back the data if necessary,\r
1119 // and free the buffer.\r
1120 //\r
1121 if (MapInfo->Operation == EfiPciIoOperationBusMasterWrite) {\r
1122 gBS->CopyMem (MapInfo->HostAddress, (VOID *)(UINTN)MapInfo->AllocAddress,\r
1123 MapInfo->NumberOfBytes);\r
1124 }\r
1125 NonCoherentPciIoFreeBuffer (This,\r
1126 EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes),\r
1127 (VOID *)(UINTN)MapInfo->AllocAddress);\r
1128 } else {\r
1129 //\r
1130 // We are *not* using a bounce buffer: if this is a bus master write,\r
1131 // we have to invalidate the caches so the CPU will see the uncached\r
1132 // data written by the device.\r
1133 //\r
1134 if (MapInfo->Operation == EfiPciIoOperationBusMasterWrite) {\r
1135 mCpu->FlushDataCache (mCpu,\r
1136 (EFI_PHYSICAL_ADDRESS)(UINTN)MapInfo->HostAddress,\r
1137 MapInfo->NumberOfBytes, EfiCpuFlushTypeInvalidate);\r
1138 }\r
1139 }\r
1140 FreePool (MapInfo);\r
1141 return EFI_SUCCESS;\r
1142}\r
a42e6d44 1143\r
43b7cd61
DB
1144/**\r
1145 Flushes all PCI posted write transactions from a PCI host bridge to system memory.\r
1146\r
1147 @param This A pointer to the EFI_PCI_IO_PROTOCOL instance.\r
1148\r
1149**/\r
a42e6d44
AB
1150STATIC\r
1151EFI_STATUS\r
8b78de74 1152EFIAPI\r
a42e6d44
AB
1153PciIoFlush (\r
1154 IN EFI_PCI_IO_PROTOCOL *This\r
1155 )\r
1156{\r
1157 return EFI_SUCCESS;\r
1158}\r
1159\r
43b7cd61
DB
1160/**\r
1161 Retrieves this PCI controller's current PCI bus number, device number, and function number.\r
1162\r
1163 @param This A pointer to the EFI_PCI_IO_PROTOCOL instance.\r
1164 @param SegmentNumber The PCI controller's current PCI segment number.\r
1165 @param BusNumber The PCI controller's current PCI bus number.\r
1166 @param DeviceNumber The PCI controller's current PCI device number.\r
1167 @param FunctionNumber The PCI controller's current PCI function number.\r
1168\r
1169 @retval EFI_SUCCESS The PCI controller location was returned.\r
1170 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
1171\r
1172**/\r
a42e6d44
AB
1173STATIC\r
1174EFI_STATUS\r
8b78de74 1175EFIAPI\r
a42e6d44
AB
1176PciIoGetLocation (\r
1177 IN EFI_PCI_IO_PROTOCOL *This,\r
1178 OUT UINTN *SegmentNumber,\r
1179 OUT UINTN *BusNumber,\r
1180 OUT UINTN *DeviceNumber,\r
1181 OUT UINTN *FunctionNumber\r
1182 )\r
1183{\r
1184 if (SegmentNumber == NULL ||\r
1185 BusNumber == NULL ||\r
1186 DeviceNumber == NULL ||\r
1187 FunctionNumber == NULL) {\r
1188 return EFI_INVALID_PARAMETER;\r
1189 }\r
1190\r
1191 *SegmentNumber = 0;\r
1192 *BusNumber = 0xff;\r
1193 *DeviceNumber = 0;\r
1194 *FunctionNumber = 0;\r
1195\r
1196 return EFI_SUCCESS;\r
1197}\r
1198\r
43b7cd61
DB
1199/**\r
1200 Performs an operation on the attributes that this PCI controller supports. The operations include\r
1201 getting the set of supported attributes, retrieving the current attributes, setting the current\r
1202 attributes, enabling attributes, and disabling attributes.\r
1203\r
1204 @param This A pointer to the EFI_PCI_IO_PROTOCOL instance.\r
1205 @param Operation The operation to perform on the attributes for this PCI controller.\r
1206 @param Attributes The mask of attributes that are used for Set, Enable, and Disable\r
1207 operations.\r
1208 @param Result A pointer to the result mask of attributes that are returned for the Get\r
1209 and Supported operations.\r
1210\r
1211 @retval EFI_SUCCESS The operation on the PCI controller's attributes was completed.\r
1212 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
1213 @retval EFI_UNSUPPORTED one or more of the bits set in\r
1214 Attributes are not supported by this PCI controller or one of\r
1215 its parent bridges when Operation is Set, Enable or Disable.\r
1216\r
1217**/\r
a42e6d44
AB
1218STATIC\r
1219EFI_STATUS\r
8b78de74 1220EFIAPI\r
a42e6d44
AB
1221PciIoAttributes (\r
1222 IN EFI_PCI_IO_PROTOCOL *This,\r
1223 IN EFI_PCI_IO_PROTOCOL_ATTRIBUTE_OPERATION Operation,\r
1224 IN UINT64 Attributes,\r
1225 OUT UINT64 *Result OPTIONAL\r
1226 )\r
1227{\r
1228 NON_DISCOVERABLE_PCI_DEVICE *Dev;\r
1229 BOOLEAN Enable;\r
1230\r
1231 Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This);\r
1232\r
1233 Enable = FALSE;\r
1234 switch (Operation) {\r
1235 case EfiPciIoAttributeOperationGet:\r
1236 if (Result == NULL) {\r
1237 return EFI_INVALID_PARAMETER;\r
1238 }\r
1239 *Result = Dev->Attributes;\r
1240 break;\r
1241\r
1242 case EfiPciIoAttributeOperationSupported:\r
1243 if (Result == NULL) {\r
1244 return EFI_INVALID_PARAMETER;\r
1245 }\r
1246 *Result = EFI_PCI_DEVICE_ENABLE | EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE;\r
1247 break;\r
1248\r
1249 case EfiPciIoAttributeOperationEnable:\r
1250 Attributes |= Dev->Attributes;\r
1251 case EfiPciIoAttributeOperationSet:\r
1252 Enable = ((~Dev->Attributes & Attributes) & EFI_PCI_DEVICE_ENABLE) != 0;\r
1253 Dev->Attributes = Attributes;\r
1254 break;\r
1255\r
1256 case EfiPciIoAttributeOperationDisable:\r
1257 Dev->Attributes &= ~Attributes;\r
1258 break;\r
1259\r
1260 default:\r
1261 return EFI_INVALID_PARAMETER;\r
1262 };\r
1263\r
1264 //\r
1265 // If we're setting any of the EFI_PCI_DEVICE_ENABLE bits, perform\r
1266 // the device specific initialization now.\r
1267 //\r
1268 if (Enable && !Dev->Enabled && Dev->Device->Initialize != NULL) {\r
1269 Dev->Device->Initialize (Dev->Device);\r
1270 Dev->Enabled = TRUE;\r
1271 }\r
1272 return EFI_SUCCESS;\r
1273}\r
1274\r
43b7cd61
DB
1275/**\r
1276 Gets the attributes that this PCI controller supports setting on a BAR using\r
1277 SetBarAttributes(), and retrieves the list of resource descriptors for a BAR.\r
1278\r
1279 @param This A pointer to the EFI_PCI_IO_PROTOCOL instance.\r
1280 @param BarIndex The BAR index of the standard PCI Configuration header to use as the\r
1281 base address for resource range. The legal range for this field is 0..5.\r
1282 @param Supports A pointer to the mask of attributes that this PCI controller supports\r
1283 setting for this BAR with SetBarAttributes().\r
1284 @param Resources A pointer to the ACPI 2.0 resource descriptors that describe the current\r
1285 configuration of this BAR of the PCI controller.\r
1286\r
1287 @retval EFI_SUCCESS If Supports is not NULL, then the attributes that the PCI\r
1288 controller supports are returned in Supports. If Resources\r
1289 is not NULL, then the ACPI 2.0 resource descriptors that the PCI\r
1290 controller is currently using are returned in Resources.\r
1291 @retval EFI_INVALID_PARAMETER Both Supports and Attributes are NULL.\r
1292 @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller.\r
1293 @retval EFI_OUT_OF_RESOURCES There are not enough resources available to allocate\r
1294 Resources.\r
1295\r
1296**/\r
a42e6d44
AB
1297STATIC\r
1298EFI_STATUS\r
8b78de74 1299EFIAPI\r
a42e6d44
AB
1300PciIoGetBarAttributes (\r
1301 IN EFI_PCI_IO_PROTOCOL *This,\r
1302 IN UINT8 BarIndex,\r
1303 OUT UINT64 *Supports OPTIONAL,\r
1304 OUT VOID **Resources OPTIONAL\r
1305 )\r
1306{\r
1307 NON_DISCOVERABLE_PCI_DEVICE *Dev;\r
5f16ecdb
DB
1308 EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor;\r
1309 EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *BarDesc;\r
a42e6d44
AB
1310 EFI_ACPI_END_TAG_DESCRIPTOR *End;\r
1311 EFI_STATUS Status;\r
1312\r
1313 if (Supports == NULL && Resources == NULL) {\r
1314 return EFI_INVALID_PARAMETER;\r
1315 }\r
1316\r
1317 Dev = NON_DISCOVERABLE_PCI_DEVICE_FROM_PCI_IO(This);\r
1318\r
1319 Status = GetBarResource (Dev, BarIndex, &BarDesc);\r
1320 if (EFI_ERROR (Status)) {\r
1321 return Status;\r
1322 }\r
1323\r
1324 //\r
1325 // Don't expose any configurable attributes for our emulated BAR\r
1326 //\r
1327 if (Supports != NULL) {\r
1328 *Supports = 0;\r
1329 }\r
1330\r
1331 if (Resources != NULL) {\r
1332 Descriptor = AllocatePool (sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) +\r
1333 sizeof (EFI_ACPI_END_TAG_DESCRIPTOR));\r
1334 if (Descriptor == NULL) {\r
1335 return EFI_OUT_OF_RESOURCES;\r
1336 }\r
1337\r
1338 CopyMem (Descriptor, BarDesc, sizeof *Descriptor);\r
1339\r
1340 End = (EFI_ACPI_END_TAG_DESCRIPTOR *) (Descriptor + 1);\r
1341 End->Desc = ACPI_END_TAG_DESCRIPTOR;\r
1342 End->Checksum = 0;\r
1343\r
1344 *Resources = Descriptor;\r
1345 }\r
1346 return EFI_SUCCESS;\r
1347}\r
1348\r
43b7cd61
DB
1349/**\r
1350 Sets the attributes for a range of a BAR on a PCI controller.\r
1351\r
1352 @param This A pointer to the EFI_PCI_IO_PROTOCOL instance.\r
1353 @param Attributes The mask of attributes to set for the resource range specified by\r
1354 BarIndex, Offset, and Length.\r
1355 @param BarIndex The BAR index of the standard PCI Configuration header to use as the\r
1356 base address for resource range. The legal range for this field is 0..5.\r
1357 @param Offset A pointer to the BAR relative base address of the resource range to be\r
1358 modified by the attributes specified by Attributes.\r
1359 @param Length A pointer to the length of the resource range to be modified by the\r
1360 attributes specified by Attributes.\r
1361**/\r
a42e6d44
AB
1362STATIC\r
1363EFI_STATUS\r
8b78de74 1364EFIAPI\r
a42e6d44
AB
1365PciIoSetBarAttributes (\r
1366 IN EFI_PCI_IO_PROTOCOL *This,\r
1367 IN UINT64 Attributes,\r
1368 IN UINT8 BarIndex,\r
1369 IN OUT UINT64 *Offset,\r
1370 IN OUT UINT64 *Length\r
1371 )\r
1372{\r
1373 ASSERT (FALSE);\r
1374 return EFI_UNSUPPORTED;\r
1375}\r
1376\r
1377STATIC CONST EFI_PCI_IO_PROTOCOL PciIoTemplate =\r
1378{\r
1379 PciIoPollMem,\r
1380 PciIoPollIo,\r
1381 { PciIoMemRead, PciIoMemWrite },\r
1382 { PciIoIoRead, PciIoIoWrite },\r
1383 { PciIoPciRead, PciIoPciWrite },\r
1384 PciIoCopyMem,\r
1385 CoherentPciIoMap,\r
1386 CoherentPciIoUnmap,\r
1387 CoherentPciIoAllocateBuffer,\r
1388 CoherentPciIoFreeBuffer,\r
1389 PciIoFlush,\r
1390 PciIoGetLocation,\r
1391 PciIoAttributes,\r
1392 PciIoGetBarAttributes,\r
1393 PciIoSetBarAttributes,\r
1394 0,\r
1395 0\r
1396};\r
1397\r
43b7cd61
DB
1398/**\r
1399 Initialize PciIo Protocol.\r
1400\r
1401 @param Dev Point to NON_DISCOVERABLE_PCI_DEVICE instance.\r
1402\r
1403**/\r
a42e6d44
AB
1404VOID\r
1405InitializePciIoProtocol (\r
1406 NON_DISCOVERABLE_PCI_DEVICE *Dev\r
1407 )\r
1408{\r
1409 EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Desc;\r
1410 INTN Idx;\r
1411\r
16296a12
AB
1412 InitializeListHead (&Dev->UncachedAllocationList);\r
1413\r
a42e6d44
AB
1414 Dev->ConfigSpace.Hdr.VendorId = PCI_ID_VENDOR_UNKNOWN;\r
1415 Dev->ConfigSpace.Hdr.DeviceId = PCI_ID_DEVICE_DONTCARE;\r
1416\r
1417 // Copy protocol structure\r
1418 CopyMem(&Dev->PciIo, &PciIoTemplate, sizeof PciIoTemplate);\r
1419\r
16296a12
AB
1420 if (Dev->Device->DmaType == NonDiscoverableDeviceDmaTypeNonCoherent) {\r
1421 Dev->PciIo.AllocateBuffer = NonCoherentPciIoAllocateBuffer;\r
1422 Dev->PciIo.FreeBuffer = NonCoherentPciIoFreeBuffer;\r
1423 Dev->PciIo.Map = NonCoherentPciIoMap;\r
1424 Dev->PciIo.Unmap = NonCoherentPciIoUnmap;\r
1425 }\r
1426\r
a42e6d44
AB
1427 if (CompareGuid (Dev->Device->Type, &gEdkiiNonDiscoverableAhciDeviceGuid)) {\r
1428 Dev->ConfigSpace.Hdr.ClassCode[0] = PCI_IF_MASS_STORAGE_AHCI;\r
1429 Dev->ConfigSpace.Hdr.ClassCode[1] = PCI_CLASS_MASS_STORAGE_SATADPA;\r
1430 Dev->ConfigSpace.Hdr.ClassCode[2] = PCI_CLASS_MASS_STORAGE;\r
1431 Dev->BarOffset = 5;\r
1432 } else if (CompareGuid (Dev->Device->Type,\r
1433 &gEdkiiNonDiscoverableEhciDeviceGuid)) {\r
1434 Dev->ConfigSpace.Hdr.ClassCode[0] = PCI_IF_EHCI;\r
1435 Dev->ConfigSpace.Hdr.ClassCode[1] = PCI_CLASS_SERIAL_USB;\r
1436 Dev->ConfigSpace.Hdr.ClassCode[2] = PCI_CLASS_SERIAL;\r
1437 Dev->BarOffset = 0;\r
1438 } else if (CompareGuid (Dev->Device->Type,\r
1439 &gEdkiiNonDiscoverableNvmeDeviceGuid)) {\r
1440 Dev->ConfigSpace.Hdr.ClassCode[0] = 0x2; // PCI_IF_NVMHCI\r
1441 Dev->ConfigSpace.Hdr.ClassCode[1] = 0x8; // PCI_CLASS_MASS_STORAGE_NVM\r
1442 Dev->ConfigSpace.Hdr.ClassCode[2] = PCI_CLASS_MASS_STORAGE;\r
1443 Dev->BarOffset = 0;\r
1444 } else if (CompareGuid (Dev->Device->Type,\r
1445 &gEdkiiNonDiscoverableOhciDeviceGuid)) {\r
1446 Dev->ConfigSpace.Hdr.ClassCode[0] = PCI_IF_OHCI;\r
1447 Dev->ConfigSpace.Hdr.ClassCode[1] = PCI_CLASS_SERIAL_USB;\r
1448 Dev->ConfigSpace.Hdr.ClassCode[2] = PCI_CLASS_SERIAL;\r
1449 Dev->BarOffset = 0;\r
1450 } else if (CompareGuid (Dev->Device->Type,\r
1451 &gEdkiiNonDiscoverableSdhciDeviceGuid)) {\r
1452 Dev->ConfigSpace.Hdr.ClassCode[0] = 0x0; // don't care\r
1453 Dev->ConfigSpace.Hdr.ClassCode[1] = PCI_SUBCLASS_SD_HOST_CONTROLLER;\r
1454 Dev->ConfigSpace.Hdr.ClassCode[2] = PCI_CLASS_SYSTEM_PERIPHERAL;\r
1455 Dev->BarOffset = 0;\r
1456 } else if (CompareGuid (Dev->Device->Type,\r
1457 &gEdkiiNonDiscoverableXhciDeviceGuid)) {\r
1458 Dev->ConfigSpace.Hdr.ClassCode[0] = PCI_IF_XHCI;\r
1459 Dev->ConfigSpace.Hdr.ClassCode[1] = PCI_CLASS_SERIAL_USB;\r
1460 Dev->ConfigSpace.Hdr.ClassCode[2] = PCI_CLASS_SERIAL;\r
1461 Dev->BarOffset = 0;\r
1462 } else if (CompareGuid (Dev->Device->Type,\r
1463 &gEdkiiNonDiscoverableUhciDeviceGuid)) {\r
1464 Dev->ConfigSpace.Hdr.ClassCode[0] = PCI_IF_UHCI;\r
1465 Dev->ConfigSpace.Hdr.ClassCode[1] = PCI_CLASS_SERIAL_USB;\r
1466 Dev->ConfigSpace.Hdr.ClassCode[2] = PCI_CLASS_SERIAL;\r
1467 Dev->BarOffset = 0;\r
1468 } else if (CompareGuid (Dev->Device->Type,\r
1469 &gEdkiiNonDiscoverableUfsDeviceGuid)) {\r
1470 Dev->ConfigSpace.Hdr.ClassCode[0] = 0x0; // don't care\r
1471 Dev->ConfigSpace.Hdr.ClassCode[1] = 0x9; // UFS controller subclass;\r
1472 Dev->ConfigSpace.Hdr.ClassCode[2] = PCI_CLASS_MASS_STORAGE;\r
1473 Dev->BarOffset = 0;\r
1474 } else {\r
1475 ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);\r
1476 }\r
1477\r
1478 //\r
1479 // Iterate over the resources to populate the virtual BARs\r
1480 //\r
1481 Idx = Dev->BarOffset;\r
1482 for (Desc = Dev->Device->Resources, Dev->BarCount = 0;\r
1483 Desc->Desc != ACPI_END_TAG_DESCRIPTOR;\r
1484 Desc = (VOID *)((UINT8 *)Desc + Desc->Len + 3)) {\r
1485\r
1486 ASSERT (Desc->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR);\r
1487 ASSERT (Desc->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM);\r
1488\r
1489 if (Idx >= PCI_MAX_BARS ||\r
1490 (Idx == PCI_MAX_BARS - 1 && Desc->AddrSpaceGranularity == 64)) {\r
1491 DEBUG ((DEBUG_ERROR,\r
1492 "%a: resource count exceeds number of emulated BARs\n",\r
1493 __FUNCTION__));\r
1494 ASSERT (FALSE);\r
1495 break;\r
1496 }\r
1497\r
1498 Dev->ConfigSpace.Device.Bar[Idx] = (UINT32)Desc->AddrRangeMin;\r
1499 Dev->BarCount++;\r
1500\r
1501 if (Desc->AddrSpaceGranularity == 64) {\r
1502 Dev->ConfigSpace.Device.Bar[Idx] |= 0x4;\r
1503 Dev->ConfigSpace.Device.Bar[++Idx] = (UINT32)RShiftU64 (\r
1504 Desc->AddrRangeMin, 32);\r
1505 }\r
1506 }\r
1507}\r