2 PCI Host Bridge Library instance for pci-ecam-generic DT nodes
4 Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
10 #include <Library/BaseMemoryLib.h>
11 #include <Library/DebugLib.h>
12 #include <Library/DevicePathLib.h>
13 #include <Library/DxeServicesTableLib.h>
14 #include <Library/MemoryAllocationLib.h>
15 #include <Library/PcdLib.h>
16 #include <Library/PciHostBridgeLib.h>
17 #include <Library/PciHostBridgeUtilityLib.h>
18 #include <Library/UefiBootServicesTableLib.h>
20 #include <Protocol/FdtClient.h>
21 #include <Protocol/PciRootBridgeIo.h>
22 #include <Protocol/PciHostBridgeResourceAllocation.h>
25 // We expect the "ranges" property of "pci-host-ecam-generic" to consist of
34 } DTB_PCI_HOST_RANGE_RECORD
;
37 #define DTB_PCI_HOST_RANGE_RELOCATABLE BIT31
38 #define DTB_PCI_HOST_RANGE_PREFETCHABLE BIT30
39 #define DTB_PCI_HOST_RANGE_ALIASED BIT29
40 #define DTB_PCI_HOST_RANGE_MMIO32 BIT25
41 #define DTB_PCI_HOST_RANGE_MMIO64 (BIT25 | BIT24)
42 #define DTB_PCI_HOST_RANGE_IO BIT24
43 #define DTB_PCI_HOST_RANGE_TYPEMASK (BIT31 | BIT30 | BIT29 | BIT25 | BIT24)
54 Status
= gDS
->AddMemorySpace (
55 EfiGcdMemoryTypeMemoryMappedIo
,
60 if (EFI_ERROR (Status
)) {
63 "%a: failed to add GCD memory space for region [0x%Lx+0x%Lx)\n",
71 Status
= gDS
->SetMemorySpaceAttributes (Base
, Size
, EFI_MEMORY_UC
);
72 if (EFI_ERROR (Status
)) {
75 "%a: failed to set memory space attributes for region [0x%Lx+0x%Lx)\n",
90 OUT UINT64
*Mmio32Base
,
91 OUT UINT64
*Mmio32Size
,
92 OUT UINT64
*Mmio64Base
,
93 OUT UINT64
*Mmio64Size
,
98 FDT_CLIENT_PROTOCOL
*FdtClient
;
100 UINT64 ConfigBase
, ConfigSize
;
105 UINT64 IoTranslation
;
106 UINT64 Mmio32Translation
;
107 UINT64 Mmio64Translation
;
110 // The following output arguments are initialized only in
111 // order to suppress '-Werror=maybe-uninitialized' warnings
112 // *incorrectly* emitted by some gcc versions.
116 *Mmio64Base
= MAX_UINT64
;
121 // *IoSize, *Mmio##Size and IoTranslation are initialized to zero because the
122 // logic below requires it. However, since they are also affected by the issue
123 // reported above, they are initialized early.
130 Status
= gBS
->LocateProtocol (
131 &gFdtClientProtocolGuid
,
135 ASSERT_EFI_ERROR (Status
);
137 Status
= FdtClient
->FindCompatibleNode (
139 "pci-host-ecam-generic",
142 if (EFI_ERROR (Status
)) {
145 "%a: No 'pci-host-ecam-generic' compatible DT node found\n",
148 return EFI_NOT_FOUND
;
155 // A DT can legally describe multiple PCI host bridges, but we are not
156 // equipped to deal with that. So assert that there is only one.
158 Status
= FdtClient
->FindNextCompatibleNode (
160 "pci-host-ecam-generic",
164 ASSERT (Status
== EFI_NOT_FOUND
);
167 Status
= FdtClient
->GetNodeProperty (FdtClient
, Node
, "reg", &Prop
, &Len
);
168 if (EFI_ERROR (Status
) || (Len
!= 2 * sizeof (UINT64
))) {
171 "%a: 'reg' property not found or invalid\n",
174 return EFI_PROTOCOL_ERROR
;
178 // Fetch the ECAM window.
180 ConfigBase
= SwapBytes64 (((CONST UINT64
*)Prop
)[0]);
181 ConfigSize
= SwapBytes64 (((CONST UINT64
*)Prop
)[1]);
184 // Fetch the bus range (note: inclusive).
186 Status
= FdtClient
->GetNodeProperty (
193 if (EFI_ERROR (Status
) || (Len
!= 2 * sizeof (UINT32
))) {
196 "%a: 'bus-range' not found or invalid\n",
199 return EFI_PROTOCOL_ERROR
;
202 *BusMin
= SwapBytes32 (((CONST UINT32
*)Prop
)[0]);
203 *BusMax
= SwapBytes32 (((CONST UINT32
*)Prop
)[1]);
206 // Sanity check: the config space must accommodate all 4K register bytes of
207 // all 8 functions of all 32 devices of all buses.
209 if ((*BusMax
< *BusMin
) || (*BusMax
- *BusMin
== MAX_UINT32
) ||
210 (DivU64x32 (ConfigSize
, SIZE_4KB
* 8 * 32) < *BusMax
- *BusMin
+ 1))
214 "%a: invalid 'bus-range' and/or 'reg'\n",
217 return EFI_PROTOCOL_ERROR
;
221 // Iterate over "ranges".
223 Status
= FdtClient
->GetNodeProperty (FdtClient
, Node
, "ranges", &Prop
, &Len
);
224 if (EFI_ERROR (Status
) || (Len
== 0) ||
225 (Len
% sizeof (DTB_PCI_HOST_RANGE_RECORD
) != 0))
227 DEBUG ((DEBUG_ERROR
, "%a: 'ranges' not found or invalid\n", __FUNCTION__
));
228 return EFI_PROTOCOL_ERROR
;
231 for (RecordIdx
= 0; RecordIdx
< Len
/ sizeof (DTB_PCI_HOST_RANGE_RECORD
);
234 CONST DTB_PCI_HOST_RANGE_RECORD
*Record
;
236 Record
= (CONST DTB_PCI_HOST_RANGE_RECORD
*)Prop
+ RecordIdx
;
237 switch (SwapBytes32 (Record
->Type
) & DTB_PCI_HOST_RANGE_TYPEMASK
) {
238 case DTB_PCI_HOST_RANGE_IO
:
239 *IoBase
= SwapBytes64 (Record
->ChildBase
);
240 *IoSize
= SwapBytes64 (Record
->Size
);
241 IoTranslation
= SwapBytes64 (Record
->CpuBase
) - *IoBase
;
243 ASSERT (PcdGet64 (PcdPciIoTranslation
) == IoTranslation
);
246 case DTB_PCI_HOST_RANGE_MMIO32
:
247 *Mmio32Base
= SwapBytes64 (Record
->ChildBase
);
248 *Mmio32Size
= SwapBytes64 (Record
->Size
);
249 Mmio32Translation
= SwapBytes64 (Record
->CpuBase
) - *Mmio32Base
;
251 if ((*Mmio32Base
> MAX_UINT32
) || (*Mmio32Size
> MAX_UINT32
) ||
252 (*Mmio32Base
+ *Mmio32Size
> SIZE_4GB
))
254 DEBUG ((DEBUG_ERROR
, "%a: MMIO32 space invalid\n", __FUNCTION__
));
255 return EFI_PROTOCOL_ERROR
;
258 ASSERT (PcdGet64 (PcdPciMmio32Translation
) == Mmio32Translation
);
260 if (Mmio32Translation
!= 0) {
263 "%a: unsupported nonzero MMIO32 translation "
268 return EFI_UNSUPPORTED
;
273 case DTB_PCI_HOST_RANGE_MMIO64
:
274 *Mmio64Base
= SwapBytes64 (Record
->ChildBase
);
275 *Mmio64Size
= SwapBytes64 (Record
->Size
);
276 Mmio64Translation
= SwapBytes64 (Record
->CpuBase
) - *Mmio64Base
;
278 ASSERT (PcdGet64 (PcdPciMmio64Translation
) == Mmio64Translation
);
280 if (Mmio64Translation
!= 0) {
283 "%a: unsupported nonzero MMIO64 translation "
288 return EFI_UNSUPPORTED
;
295 if ((*IoSize
== 0) || (*Mmio32Size
== 0)) {
298 "%a: %a space empty\n",
300 (*IoSize
== 0) ? "IO" : "MMIO32"
302 return EFI_PROTOCOL_ERROR
;
306 // The dynamic PCD PcdPciExpressBaseAddress should have already been set,
307 // and should match the value we found in the DT node.
309 ASSERT (PcdGet64 (PcdPciExpressBaseAddress
) == ConfigBase
);
313 "%a: Config[0x%Lx+0x%Lx) Bus[0x%x..0x%x] "
314 "Io[0x%Lx+0x%Lx)@0x%Lx Mem32[0x%Lx+0x%Lx)@0x0 Mem64[0x%Lx+0x%Lx)@0x0\n",
329 // Map the ECAM space in the GCD memory map
330 Status
= MapGcdMmioSpace (ConfigBase
, ConfigSize
);
331 ASSERT_EFI_ERROR (Status
);
332 if (EFI_ERROR (Status
)) {
337 // Map the MMIO window that provides I/O access - the PCI host bridge code
338 // is not aware of this translation and so it will only map the I/O view
339 // in the GCD I/O map.
341 Status
= MapGcdMmioSpace (*IoBase
+ IoTranslation
, *IoSize
);
342 ASSERT_EFI_ERROR (Status
);
348 Return all the root bridge instances in an array.
350 @param Count Return the count of root bridge instances.
352 @return All the root bridge instances in an array.
353 The array should be passed into PciHostBridgeFreeRootBridges()
358 PciHostBridgeGetRootBridges (
362 UINT64 IoBase
, IoSize
;
363 UINT64 Mmio32Base
, Mmio32Size
;
364 UINT64 Mmio64Base
, Mmio64Size
;
365 UINT32 BusMin
, BusMax
;
368 UINT64 AllocationAttributes
;
369 PCI_ROOT_BRIDGE_APERTURE Io
;
370 PCI_ROOT_BRIDGE_APERTURE Mem
;
371 PCI_ROOT_BRIDGE_APERTURE MemAbove4G
;
372 PCI_ROOT_BRIDGE_APERTURE PMem
;
373 PCI_ROOT_BRIDGE_APERTURE PMemAbove4G
;
375 if (PcdGet64 (PcdPciExpressBaseAddress
) == 0) {
376 DEBUG ((DEBUG_INFO
, "%a: PCI host bridge not present\n", __FUNCTION__
));
382 Status
= ProcessPciHost (
392 if (EFI_ERROR (Status
)) {
395 "%a: failed to discover PCI host bridge: %r\n",
403 ZeroMem (&Io
, sizeof (Io
));
404 ZeroMem (&Mem
, sizeof (Mem
));
405 ZeroMem (&MemAbove4G
, sizeof (MemAbove4G
));
406 ZeroMem (&PMem
, sizeof (PMem
));
407 ZeroMem (&PMemAbove4G
, sizeof (PMemAbove4G
));
409 Attributes
= EFI_PCI_ATTRIBUTE_ISA_IO_16
|
410 EFI_PCI_ATTRIBUTE_ISA_MOTHERBOARD_IO
|
411 EFI_PCI_ATTRIBUTE_VGA_IO_16
|
412 EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO_16
;
414 AllocationAttributes
= EFI_PCI_HOST_BRIDGE_COMBINE_MEM_PMEM
;
417 Io
.Limit
= IoBase
+ IoSize
- 1;
418 Mem
.Base
= Mmio32Base
;
419 Mem
.Limit
= Mmio32Base
+ Mmio32Size
- 1;
421 if (sizeof (UINTN
) == sizeof (UINT64
)) {
422 MemAbove4G
.Base
= Mmio64Base
;
423 MemAbove4G
.Limit
= Mmio64Base
+ Mmio64Size
- 1;
424 if (Mmio64Size
> 0) {
425 AllocationAttributes
|= EFI_PCI_HOST_BRIDGE_MEM64_DECODE
;
429 // UEFI mandates a 1:1 virtual-to-physical mapping, so on a 32-bit
430 // architecture such as ARM, we will not be able to access 64-bit MMIO
431 // BARs unless they are allocated below 4 GB. So ignore the range above
432 // 4 GB in this case.
434 MemAbove4G
.Base
= MAX_UINT64
;
435 MemAbove4G
.Limit
= 0;
439 // No separate ranges for prefetchable and non-prefetchable BARs
441 PMem
.Base
= MAX_UINT64
;
443 PMemAbove4G
.Base
= MAX_UINT64
;
444 PMemAbove4G
.Limit
= 0;
446 return PciHostBridgeUtilityGetRootBridges (
449 AllocationAttributes
,
463 Free the root bridge instances array returned from
464 PciHostBridgeGetRootBridges().
466 @param Bridges The root bridge instances array.
467 @param Count The count of the array.
471 PciHostBridgeFreeRootBridges (
472 PCI_ROOT_BRIDGE
*Bridges
,
476 PciHostBridgeUtilityFreeRootBridges (Bridges
, Count
);
480 Inform the platform that the resource conflict happens.
482 @param HostBridgeHandle Handle of the Host Bridge.
483 @param Configuration Pointer to PCI I/O and PCI memory resource
484 descriptors. The Configuration contains the resources
485 for all the root bridges. The resource for each root
486 bridge is terminated with END descriptor and an
487 additional END is appended indicating the end of the
488 entire resources. The resource descriptor field
489 values follow the description in
490 EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL
495 PciHostBridgeResourceConflict (
496 EFI_HANDLE HostBridgeHandle
,
500 PciHostBridgeUtilityResourceConflict (Configuration
);