]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Fdt/FdtPciHostBridgeLib/FdtPciHostBridgeLib.c
OvmfPkg: Apply uncrustify changes
[mirror_edk2.git] / OvmfPkg / Fdt / FdtPciHostBridgeLib / FdtPciHostBridgeLib.c
CommitLineData
d4cb9a30
AB
1/** @file\r
2 PCI Host Bridge Library instance for pci-ecam-generic DT nodes\r
3\r
4 Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>\r
5\r
9792fb0e 6 SPDX-License-Identifier: BSD-2-Clause-Patent\r
d4cb9a30
AB
7\r
8**/\r
9#include <PiDxe.h>\r
aa445ce0 10#include <Library/BaseMemoryLib.h>\r
d4cb9a30
AB
11#include <Library/DebugLib.h>\r
12#include <Library/DevicePathLib.h>\r
66e06a72 13#include <Library/DxeServicesTableLib.h>\r
d4cb9a30
AB
14#include <Library/MemoryAllocationLib.h>\r
15#include <Library/PcdLib.h>\r
166a32d0
JC
16#include <Library/PciHostBridgeLib.h>\r
17#include <Library/PciHostBridgeUtilityLib.h>\r
d4cb9a30
AB
18#include <Library/UefiBootServicesTableLib.h>\r
19\r
20#include <Protocol/FdtClient.h>\r
21#include <Protocol/PciRootBridgeIo.h>\r
22#include <Protocol/PciHostBridgeResourceAllocation.h>\r
23\r
d4cb9a30
AB
24//\r
25// We expect the "ranges" property of "pci-host-ecam-generic" to consist of\r
26// records like this.\r
27//\r
28#pragma pack (1)\r
29typedef struct {\r
ac0a286f
MK
30 UINT32 Type;\r
31 UINT64 ChildBase;\r
32 UINT64 CpuBase;\r
33 UINT64 Size;\r
d4cb9a30
AB
34} DTB_PCI_HOST_RANGE_RECORD;\r
35#pragma pack ()\r
36\r
ac0a286f
MK
37#define DTB_PCI_HOST_RANGE_RELOCATABLE BIT31\r
38#define DTB_PCI_HOST_RANGE_PREFETCHABLE BIT30\r
39#define DTB_PCI_HOST_RANGE_ALIASED BIT29\r
40#define DTB_PCI_HOST_RANGE_MMIO32 BIT25\r
41#define DTB_PCI_HOST_RANGE_MMIO64 (BIT25 | BIT24)\r
42#define DTB_PCI_HOST_RANGE_IO BIT24\r
43#define DTB_PCI_HOST_RANGE_TYPEMASK (BIT31 | BIT30 | BIT29 | BIT25 | BIT24)\r
d4cb9a30 44\r
66e06a72
AB
45STATIC\r
46EFI_STATUS\r
47MapGcdMmioSpace (\r
ac0a286f
MK
48 IN UINT64 Base,\r
49 IN UINT64 Size\r
66e06a72
AB
50 )\r
51{\r
ac0a286f
MK
52 EFI_STATUS Status;\r
53\r
54 Status = gDS->AddMemorySpace (\r
55 EfiGcdMemoryTypeMemoryMappedIo,\r
56 Base,\r
57 Size,\r
58 EFI_MEMORY_UC\r
59 );\r
66e06a72 60 if (EFI_ERROR (Status)) {\r
ac0a286f
MK
61 DEBUG ((\r
62 DEBUG_ERROR,\r
66e06a72 63 "%a: failed to add GCD memory space for region [0x%Lx+0x%Lx)\n",\r
ac0a286f
MK
64 __FUNCTION__,\r
65 Base,\r
66 Size\r
67 ));\r
66e06a72
AB
68 return Status;\r
69 }\r
70\r
71 Status = gDS->SetMemorySpaceAttributes (Base, Size, EFI_MEMORY_UC);\r
72 if (EFI_ERROR (Status)) {\r
ac0a286f
MK
73 DEBUG ((\r
74 DEBUG_ERROR,\r
66e06a72 75 "%a: failed to set memory space attributes for region [0x%Lx+0x%Lx)\n",\r
ac0a286f
MK
76 __FUNCTION__,\r
77 Base,\r
78 Size\r
79 ));\r
66e06a72 80 }\r
ac0a286f 81\r
66e06a72
AB
82 return Status;\r
83}\r
84\r
d4cb9a30
AB
85STATIC\r
86EFI_STATUS\r
87ProcessPciHost (\r
ac0a286f
MK
88 OUT UINT64 *IoBase,\r
89 OUT UINT64 *IoSize,\r
90 OUT UINT64 *Mmio32Base,\r
91 OUT UINT64 *Mmio32Size,\r
92 OUT UINT64 *Mmio64Base,\r
93 OUT UINT64 *Mmio64Size,\r
94 OUT UINT32 *BusMin,\r
95 OUT UINT32 *BusMax\r
d4cb9a30
AB
96 )\r
97{\r
ac0a286f
MK
98 FDT_CLIENT_PROTOCOL *FdtClient;\r
99 INT32 Node;\r
100 UINT64 ConfigBase, ConfigSize;\r
101 CONST VOID *Prop;\r
102 UINT32 Len;\r
103 UINT32 RecordIdx;\r
104 EFI_STATUS Status;\r
105 UINT64 IoTranslation;\r
106 UINT64 Mmio32Translation;\r
107 UINT64 Mmio64Translation;\r
d4cb9a30
AB
108\r
109 //\r
110 // The following output arguments are initialized only in\r
111 // order to suppress '-Werror=maybe-uninitialized' warnings\r
112 // *incorrectly* emitted by some gcc versions.\r
113 //\r
ac0a286f 114 *IoBase = 0;\r
9d64ac23
AB
115 *Mmio32Base = 0;\r
116 *Mmio64Base = MAX_UINT64;\r
ac0a286f
MK
117 *BusMin = 0;\r
118 *BusMax = 0;\r
d4cb9a30
AB
119\r
120 //\r
9d64ac23 121 // *IoSize, *Mmio##Size and IoTranslation are initialized to zero because the\r
d4cb9a30
AB
122 // logic below requires it. However, since they are also affected by the issue\r
123 // reported above, they are initialized early.\r
124 //\r
ac0a286f
MK
125 *IoSize = 0;\r
126 *Mmio32Size = 0;\r
127 *Mmio64Size = 0;\r
d4cb9a30
AB
128 IoTranslation = 0;\r
129\r
ac0a286f
MK
130 Status = gBS->LocateProtocol (\r
131 &gFdtClientProtocolGuid,\r
132 NULL,\r
133 (VOID **)&FdtClient\r
134 );\r
d4cb9a30
AB
135 ASSERT_EFI_ERROR (Status);\r
136\r
ac0a286f
MK
137 Status = FdtClient->FindCompatibleNode (\r
138 FdtClient,\r
139 "pci-host-ecam-generic",\r
140 &Node\r
141 );\r
d4cb9a30 142 if (EFI_ERROR (Status)) {\r
ac0a286f
MK
143 DEBUG ((\r
144 DEBUG_INFO,\r
d4cb9a30 145 "%a: No 'pci-host-ecam-generic' compatible DT node found\n",\r
ac0a286f
MK
146 __FUNCTION__\r
147 ));\r
d4cb9a30
AB
148 return EFI_NOT_FOUND;\r
149 }\r
150\r
151 DEBUG_CODE (\r
152 INT32 Tmp;\r
153\r
154 //\r
155 // A DT can legally describe multiple PCI host bridges, but we are not\r
156 // equipped to deal with that. So assert that there is only one.\r
157 //\r
ac0a286f
MK
158 Status = FdtClient->FindNextCompatibleNode (\r
159 FdtClient,\r
160 "pci-host-ecam-generic",\r
161 Node,\r
162 &Tmp\r
163 );\r
d4cb9a30 164 ASSERT (Status == EFI_NOT_FOUND);\r
ac0a286f 165 );\r
d4cb9a30
AB
166\r
167 Status = FdtClient->GetNodeProperty (FdtClient, Node, "reg", &Prop, &Len);\r
ac0a286f
MK
168 if (EFI_ERROR (Status) || (Len != 2 * sizeof (UINT64))) {\r
169 DEBUG ((\r
170 DEBUG_ERROR,\r
171 "%a: 'reg' property not found or invalid\n",\r
172 __FUNCTION__\r
173 ));\r
d4cb9a30
AB
174 return EFI_PROTOCOL_ERROR;\r
175 }\r
176\r
177 //\r
178 // Fetch the ECAM window.\r
179 //\r
180 ConfigBase = SwapBytes64 (((CONST UINT64 *)Prop)[0]);\r
181 ConfigSize = SwapBytes64 (((CONST UINT64 *)Prop)[1]);\r
182\r
183 //\r
184 // Fetch the bus range (note: inclusive).\r
185 //\r
ac0a286f
MK
186 Status = FdtClient->GetNodeProperty (\r
187 FdtClient,\r
188 Node,\r
189 "bus-range",\r
190 &Prop,\r
191 &Len\r
192 );\r
193 if (EFI_ERROR (Status) || (Len != 2 * sizeof (UINT32))) {\r
194 DEBUG ((\r
195 DEBUG_ERROR,\r
196 "%a: 'bus-range' not found or invalid\n",\r
197 __FUNCTION__\r
198 ));\r
d4cb9a30
AB
199 return EFI_PROTOCOL_ERROR;\r
200 }\r
ac0a286f 201\r
d4cb9a30
AB
202 *BusMin = SwapBytes32 (((CONST UINT32 *)Prop)[0]);\r
203 *BusMax = SwapBytes32 (((CONST UINT32 *)Prop)[1]);\r
204\r
205 //\r
206 // Sanity check: the config space must accommodate all 4K register bytes of\r
207 // all 8 functions of all 32 devices of all buses.\r
208 //\r
ac0a286f
MK
209 if ((*BusMax < *BusMin) || (*BusMax - *BusMin == MAX_UINT32) ||\r
210 (DivU64x32 (ConfigSize, SIZE_4KB * 8 * 32) < *BusMax - *BusMin + 1))\r
211 {\r
212 DEBUG ((\r
213 DEBUG_ERROR,\r
214 "%a: invalid 'bus-range' and/or 'reg'\n",\r
215 __FUNCTION__\r
216 ));\r
d4cb9a30
AB
217 return EFI_PROTOCOL_ERROR;\r
218 }\r
219\r
220 //\r
221 // Iterate over "ranges".\r
222 //\r
223 Status = FdtClient->GetNodeProperty (FdtClient, Node, "ranges", &Prop, &Len);\r
ac0a286f
MK
224 if (EFI_ERROR (Status) || (Len == 0) ||\r
225 (Len % sizeof (DTB_PCI_HOST_RANGE_RECORD) != 0))\r
226 {\r
47719926 227 DEBUG ((DEBUG_ERROR, "%a: 'ranges' not found or invalid\n", __FUNCTION__));\r
d4cb9a30
AB
228 return EFI_PROTOCOL_ERROR;\r
229 }\r
230\r
231 for (RecordIdx = 0; RecordIdx < Len / sizeof (DTB_PCI_HOST_RANGE_RECORD);\r
ac0a286f
MK
232 ++RecordIdx)\r
233 {\r
234 CONST DTB_PCI_HOST_RANGE_RECORD *Record;\r
d4cb9a30
AB
235\r
236 Record = (CONST DTB_PCI_HOST_RANGE_RECORD *)Prop + RecordIdx;\r
237 switch (SwapBytes32 (Record->Type) & DTB_PCI_HOST_RANGE_TYPEMASK) {\r
ac0a286f
MK
238 case DTB_PCI_HOST_RANGE_IO:\r
239 *IoBase = SwapBytes64 (Record->ChildBase);\r
240 *IoSize = SwapBytes64 (Record->Size);\r
241 IoTranslation = SwapBytes64 (Record->CpuBase) - *IoBase;\r
242\r
243 ASSERT (PcdGet64 (PcdPciIoTranslation) == IoTranslation);\r
244 break;\r
245\r
246 case DTB_PCI_HOST_RANGE_MMIO32:\r
247 *Mmio32Base = SwapBytes64 (Record->ChildBase);\r
248 *Mmio32Size = SwapBytes64 (Record->Size);\r
249 Mmio32Translation = SwapBytes64 (Record->CpuBase) - *Mmio32Base;\r
250\r
251 if ((*Mmio32Base > MAX_UINT32) || (*Mmio32Size > MAX_UINT32) ||\r
252 (*Mmio32Base + *Mmio32Size > SIZE_4GB))\r
253 {\r
254 DEBUG ((DEBUG_ERROR, "%a: MMIO32 space invalid\n", __FUNCTION__));\r
255 return EFI_PROTOCOL_ERROR;\r
256 }\r
257\r
258 ASSERT (PcdGet64 (PcdPciMmio32Translation) == Mmio32Translation);\r
259\r
260 if (Mmio32Translation != 0) {\r
261 DEBUG ((\r
262 DEBUG_ERROR,\r
263 "%a: unsupported nonzero MMIO32 translation "\r
264 "0x%Lx\n",\r
265 __FUNCTION__,\r
266 Mmio32Translation\r
267 ));\r
268 return EFI_UNSUPPORTED;\r
269 }\r
270\r
271 break;\r
272\r
273 case DTB_PCI_HOST_RANGE_MMIO64:\r
274 *Mmio64Base = SwapBytes64 (Record->ChildBase);\r
275 *Mmio64Size = SwapBytes64 (Record->Size);\r
276 Mmio64Translation = SwapBytes64 (Record->CpuBase) - *Mmio64Base;\r
277\r
278 ASSERT (PcdGet64 (PcdPciMmio64Translation) == Mmio64Translation);\r
279\r
280 if (Mmio64Translation != 0) {\r
281 DEBUG ((\r
282 DEBUG_ERROR,\r
283 "%a: unsupported nonzero MMIO64 translation "\r
284 "0x%Lx\n",\r
285 __FUNCTION__,\r
286 Mmio64Translation\r
287 ));\r
288 return EFI_UNSUPPORTED;\r
289 }\r
290\r
291 break;\r
d4cb9a30
AB
292 }\r
293 }\r
ac0a286f
MK
294\r
295 if ((*IoSize == 0) || (*Mmio32Size == 0)) {\r
296 DEBUG ((\r
297 DEBUG_ERROR,\r
298 "%a: %a space empty\n",\r
299 __FUNCTION__,\r
300 (*IoSize == 0) ? "IO" : "MMIO32"\r
301 ));\r
d4cb9a30
AB
302 return EFI_PROTOCOL_ERROR;\r
303 }\r
304\r
305 //\r
306 // The dynamic PCD PcdPciExpressBaseAddress should have already been set,\r
307 // and should match the value we found in the DT node.\r
308 //\r
309 ASSERT (PcdGet64 (PcdPciExpressBaseAddress) == ConfigBase);\r
310\r
ac0a286f
MK
311 DEBUG ((\r
312 DEBUG_INFO,\r
313 "%a: Config[0x%Lx+0x%Lx) Bus[0x%x..0x%x] "\r
9d64ac23 314 "Io[0x%Lx+0x%Lx)@0x%Lx Mem32[0x%Lx+0x%Lx)@0x0 Mem64[0x%Lx+0x%Lx)@0x0\n",\r
ac0a286f
MK
315 __FUNCTION__,\r
316 ConfigBase,\r
317 ConfigSize,\r
318 *BusMin,\r
319 *BusMax,\r
320 *IoBase,\r
321 *IoSize,\r
322 IoTranslation,\r
323 *Mmio32Base,\r
324 *Mmio32Size,\r
325 *Mmio64Base,\r
326 *Mmio64Size\r
327 ));\r
66e06a72
AB
328\r
329 // Map the ECAM space in the GCD memory map\r
330 Status = MapGcdMmioSpace (ConfigBase, ConfigSize);\r
331 ASSERT_EFI_ERROR (Status);\r
332 if (EFI_ERROR (Status)) {\r
333 return Status;\r
334 }\r
335\r
336 //\r
337 // Map the MMIO window that provides I/O access - the PCI host bridge code\r
338 // is not aware of this translation and so it will only map the I/O view\r
339 // in the GCD I/O map.\r
340 //\r
341 Status = MapGcdMmioSpace (*IoBase + IoTranslation, *IoSize);\r
342 ASSERT_EFI_ERROR (Status);\r
343\r
344 return Status;\r
d4cb9a30
AB
345}\r
346\r
d4cb9a30
AB
347/**\r
348 Return all the root bridge instances in an array.\r
349\r
350 @param Count Return the count of root bridge instances.\r
351\r
352 @return All the root bridge instances in an array.\r
353 The array should be passed into PciHostBridgeFreeRootBridges()\r
354 when it's not used.\r
355**/\r
356PCI_ROOT_BRIDGE *\r
357EFIAPI\r
358PciHostBridgeGetRootBridges (\r
ac0a286f 359 UINTN *Count\r
d4cb9a30
AB
360 )\r
361{\r
ac0a286f
MK
362 UINT64 IoBase, IoSize;\r
363 UINT64 Mmio32Base, Mmio32Size;\r
364 UINT64 Mmio64Base, Mmio64Size;\r
365 UINT32 BusMin, BusMax;\r
366 EFI_STATUS Status;\r
367 UINT64 Attributes;\r
368 UINT64 AllocationAttributes;\r
369 PCI_ROOT_BRIDGE_APERTURE Io;\r
370 PCI_ROOT_BRIDGE_APERTURE Mem;\r
371 PCI_ROOT_BRIDGE_APERTURE MemAbove4G;\r
372 PCI_ROOT_BRIDGE_APERTURE PMem;\r
373 PCI_ROOT_BRIDGE_APERTURE PMemAbove4G;\r
d4cb9a30
AB
374\r
375 if (PcdGet64 (PcdPciExpressBaseAddress) == 0) {\r
47719926 376 DEBUG ((DEBUG_INFO, "%a: PCI host bridge not present\n", __FUNCTION__));\r
d4cb9a30
AB
377\r
378 *Count = 0;\r
379 return NULL;\r
380 }\r
381\r
ac0a286f
MK
382 Status = ProcessPciHost (\r
383 &IoBase,\r
384 &IoSize,\r
385 &Mmio32Base,\r
386 &Mmio32Size,\r
387 &Mmio64Base,\r
388 &Mmio64Size,\r
389 &BusMin,\r
390 &BusMax\r
391 );\r
d4cb9a30 392 if (EFI_ERROR (Status)) {\r
ac0a286f
MK
393 DEBUG ((\r
394 DEBUG_ERROR,\r
395 "%a: failed to discover PCI host bridge: %r\n",\r
396 __FUNCTION__,\r
397 Status\r
398 ));\r
d4cb9a30
AB
399 *Count = 0;\r
400 return NULL;\r
401 }\r
402\r
aa445ce0
JC
403 ZeroMem (&Io, sizeof (Io));\r
404 ZeroMem (&Mem, sizeof (Mem));\r
405 ZeroMem (&MemAbove4G, sizeof (MemAbove4G));\r
406 ZeroMem (&PMem, sizeof (PMem));\r
407 ZeroMem (&PMemAbove4G, sizeof (PMemAbove4G));\r
d4cb9a30 408\r
ac0a286f
MK
409 Attributes = EFI_PCI_ATTRIBUTE_ISA_IO_16 |\r
410 EFI_PCI_ATTRIBUTE_ISA_MOTHERBOARD_IO |\r
411 EFI_PCI_ATTRIBUTE_VGA_IO_16 |\r
412 EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO_16;\r
d4cb9a30 413\r
aa445ce0 414 AllocationAttributes = EFI_PCI_HOST_BRIDGE_COMBINE_MEM_PMEM;\r
d4cb9a30 415\r
ac0a286f
MK
416 Io.Base = IoBase;\r
417 Io.Limit = IoBase + IoSize - 1;\r
418 Mem.Base = Mmio32Base;\r
419 Mem.Limit = Mmio32Base + Mmio32Size - 1;\r
9d64ac23
AB
420\r
421 if (sizeof (UINTN) == sizeof (UINT64)) {\r
ac0a286f
MK
422 MemAbove4G.Base = Mmio64Base;\r
423 MemAbove4G.Limit = Mmio64Base + Mmio64Size - 1;\r
9d64ac23 424 if (Mmio64Size > 0) {\r
aa445ce0 425 AllocationAttributes |= EFI_PCI_HOST_BRIDGE_MEM64_DECODE;\r
9d64ac23
AB
426 }\r
427 } else {\r
428 //\r
429 // UEFI mandates a 1:1 virtual-to-physical mapping, so on a 32-bit\r
430 // architecture such as ARM, we will not be able to access 64-bit MMIO\r
431 // BARs unless they are allocated below 4 GB. So ignore the range above\r
432 // 4 GB in this case.\r
433 //\r
ac0a286f
MK
434 MemAbove4G.Base = MAX_UINT64;\r
435 MemAbove4G.Limit = 0;\r
9d64ac23 436 }\r
d4cb9a30
AB
437\r
438 //\r
439 // No separate ranges for prefetchable and non-prefetchable BARs\r
440 //\r
ac0a286f
MK
441 PMem.Base = MAX_UINT64;\r
442 PMem.Limit = 0;\r
443 PMemAbove4G.Base = MAX_UINT64;\r
444 PMemAbove4G.Limit = 0;\r
aa445ce0 445\r
c4cbd864 446 return PciHostBridgeUtilityGetRootBridges (\r
ac0a286f
MK
447 Count,\r
448 Attributes,\r
449 AllocationAttributes,\r
450 TRUE,\r
451 FALSE,\r
452 BusMin,\r
453 BusMax,\r
454 &Io,\r
455 &Mem,\r
456 &MemAbove4G,\r
457 &PMem,\r
458 &PMemAbove4G\r
459 );\r
d4cb9a30
AB
460}\r
461\r
462/**\r
463 Free the root bridge instances array returned from\r
464 PciHostBridgeGetRootBridges().\r
465\r
466 @param Bridges The root bridge instances array.\r
467 @param Count The count of the array.\r
468**/\r
469VOID\r
470EFIAPI\r
471PciHostBridgeFreeRootBridges (\r
ac0a286f
MK
472 PCI_ROOT_BRIDGE *Bridges,\r
473 UINTN Count\r
d4cb9a30
AB
474 )\r
475{\r
c4cbd864 476 PciHostBridgeUtilityFreeRootBridges (Bridges, Count);\r
d4cb9a30
AB
477}\r
478\r
479/**\r
480 Inform the platform that the resource conflict happens.\r
481\r
482 @param HostBridgeHandle Handle of the Host Bridge.\r
483 @param Configuration Pointer to PCI I/O and PCI memory resource\r
484 descriptors. The Configuration contains the resources\r
485 for all the root bridges. The resource for each root\r
486 bridge is terminated with END descriptor and an\r
487 additional END is appended indicating the end of the\r
488 entire resources. The resource descriptor field\r
489 values follow the description in\r
490 EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL\r
491 .SubmitResources().\r
492**/\r
493VOID\r
494EFIAPI\r
495PciHostBridgeResourceConflict (\r
ac0a286f
MK
496 EFI_HANDLE HostBridgeHandle,\r
497 VOID *Configuration\r
d4cb9a30
AB
498 )\r
499{\r
166a32d0 500 PciHostBridgeUtilityResourceConflict (Configuration);\r
d4cb9a30 501}\r