2 PCI Host Bridge Library instance for pci-ecam-generic DT nodes
4 Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>
6 This program and the accompanying materials are licensed and made available
7 under the terms and conditions of the BSD License which accompanies this
8 distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php.
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR
17 #include <Library/PciHostBridgeLib.h>
18 #include <Library/DebugLib.h>
19 #include <Library/DevicePathLib.h>
20 #include <Library/MemoryAllocationLib.h>
21 #include <Library/PcdLib.h>
22 #include <Library/UefiBootServicesTableLib.h>
24 #include <Protocol/FdtClient.h>
25 #include <Protocol/PciRootBridgeIo.h>
26 #include <Protocol/PciHostBridgeResourceAllocation.h>
30 ACPI_HID_DEVICE_PATH AcpiDevicePath
;
31 EFI_DEVICE_PATH_PROTOCOL EndDevicePath
;
32 } EFI_PCI_ROOT_BRIDGE_DEVICE_PATH
;
35 STATIC EFI_PCI_ROOT_BRIDGE_DEVICE_PATH mEfiPciRootBridgeDevicePath
= {
41 (UINT8
) (sizeof(ACPI_HID_DEVICE_PATH
)),
42 (UINT8
) ((sizeof(ACPI_HID_DEVICE_PATH
)) >> 8)
51 END_ENTIRE_DEVICE_PATH_SUBTYPE
,
53 END_DEVICE_PATH_LENGTH
,
59 GLOBAL_REMOVE_IF_UNREFERENCED
60 CHAR16
*mPciHostBridgeLibAcpiAddressSpaceTypeStr
[] = {
61 L
"Mem", L
"I/O", L
"Bus"
65 // We expect the "ranges" property of "pci-host-ecam-generic" to consist of
74 } DTB_PCI_HOST_RANGE_RECORD
;
77 #define DTB_PCI_HOST_RANGE_RELOCATABLE BIT31
78 #define DTB_PCI_HOST_RANGE_PREFETCHABLE BIT30
79 #define DTB_PCI_HOST_RANGE_ALIASED BIT29
80 #define DTB_PCI_HOST_RANGE_MMIO32 BIT25
81 #define DTB_PCI_HOST_RANGE_MMIO64 (BIT25 | BIT24)
82 #define DTB_PCI_HOST_RANGE_IO BIT24
83 #define DTB_PCI_HOST_RANGE_TYPEMASK (BIT31 | BIT30 | BIT29 | BIT25 | BIT24)
96 FDT_CLIENT_PROTOCOL
*FdtClient
;
98 UINT64 ConfigBase
, ConfigSize
;
103 UINT64 IoTranslation
;
104 UINT64 MmioTranslation
;
107 // The following output arguments are initialized only in
108 // order to suppress '-Werror=maybe-uninitialized' warnings
109 // *incorrectly* emitted by some gcc versions.
117 // *IoSize, *MmioSize and IoTranslation are initialized to zero because the
118 // logic below requires it. However, since they are also affected by the issue
119 // reported above, they are initialized early.
125 Status
= gBS
->LocateProtocol (&gFdtClientProtocolGuid
, NULL
,
126 (VOID
**)&FdtClient
);
127 ASSERT_EFI_ERROR (Status
);
129 Status
= FdtClient
->FindCompatibleNode (FdtClient
, "pci-host-ecam-generic",
131 if (EFI_ERROR (Status
)) {
133 "%a: No 'pci-host-ecam-generic' compatible DT node found\n",
135 return EFI_NOT_FOUND
;
142 // A DT can legally describe multiple PCI host bridges, but we are not
143 // equipped to deal with that. So assert that there is only one.
145 Status
= FdtClient
->FindNextCompatibleNode (FdtClient
,
146 "pci-host-ecam-generic", Node
, &Tmp
);
147 ASSERT (Status
== EFI_NOT_FOUND
);
150 Status
= FdtClient
->GetNodeProperty (FdtClient
, Node
, "reg", &Prop
, &Len
);
151 if (EFI_ERROR (Status
) || Len
!= 2 * sizeof (UINT64
)) {
152 DEBUG ((EFI_D_ERROR
, "%a: 'reg' property not found or invalid\n",
154 return EFI_PROTOCOL_ERROR
;
158 // Fetch the ECAM window.
160 ConfigBase
= SwapBytes64 (((CONST UINT64
*)Prop
)[0]);
161 ConfigSize
= SwapBytes64 (((CONST UINT64
*)Prop
)[1]);
164 // Fetch the bus range (note: inclusive).
166 Status
= FdtClient
->GetNodeProperty (FdtClient
, Node
, "bus-range", &Prop
,
168 if (EFI_ERROR (Status
) || Len
!= 2 * sizeof (UINT32
)) {
169 DEBUG ((EFI_D_ERROR
, "%a: 'bus-range' not found or invalid\n",
171 return EFI_PROTOCOL_ERROR
;
173 *BusMin
= SwapBytes32 (((CONST UINT32
*)Prop
)[0]);
174 *BusMax
= SwapBytes32 (((CONST UINT32
*)Prop
)[1]);
177 // Sanity check: the config space must accommodate all 4K register bytes of
178 // all 8 functions of all 32 devices of all buses.
180 if (*BusMax
< *BusMin
|| *BusMax
- *BusMin
== MAX_UINT32
||
181 DivU64x32 (ConfigSize
, SIZE_4KB
* 8 * 32) < *BusMax
- *BusMin
+ 1) {
182 DEBUG ((EFI_D_ERROR
, "%a: invalid 'bus-range' and/or 'reg'\n",
184 return EFI_PROTOCOL_ERROR
;
188 // Iterate over "ranges".
190 Status
= FdtClient
->GetNodeProperty (FdtClient
, Node
, "ranges", &Prop
, &Len
);
191 if (EFI_ERROR (Status
) || Len
== 0 ||
192 Len
% sizeof (DTB_PCI_HOST_RANGE_RECORD
) != 0) {
193 DEBUG ((EFI_D_ERROR
, "%a: 'ranges' not found or invalid\n", __FUNCTION__
));
194 return EFI_PROTOCOL_ERROR
;
197 for (RecordIdx
= 0; RecordIdx
< Len
/ sizeof (DTB_PCI_HOST_RANGE_RECORD
);
199 CONST DTB_PCI_HOST_RANGE_RECORD
*Record
;
201 Record
= (CONST DTB_PCI_HOST_RANGE_RECORD
*)Prop
+ RecordIdx
;
202 switch (SwapBytes32 (Record
->Type
) & DTB_PCI_HOST_RANGE_TYPEMASK
) {
203 case DTB_PCI_HOST_RANGE_IO
:
204 *IoBase
= SwapBytes64 (Record
->ChildBase
);
205 *IoSize
= SwapBytes64 (Record
->Size
);
206 IoTranslation
= SwapBytes64 (Record
->CpuBase
) - *IoBase
;
208 ASSERT (PcdGet64 (PcdPciIoTranslation
) == IoTranslation
);
211 case DTB_PCI_HOST_RANGE_MMIO32
:
212 *MmioBase
= SwapBytes64 (Record
->ChildBase
);
213 *MmioSize
= SwapBytes64 (Record
->Size
);
214 MmioTranslation
= SwapBytes64 (Record
->CpuBase
) - *MmioBase
;
216 if (*MmioBase
> MAX_UINT32
|| *MmioSize
> MAX_UINT32
||
217 *MmioBase
+ *MmioSize
> SIZE_4GB
) {
218 DEBUG ((EFI_D_ERROR
, "%a: MMIO32 space invalid\n", __FUNCTION__
));
219 return EFI_PROTOCOL_ERROR
;
222 ASSERT (PcdGet64 (PcdPciMmio32Translation
) == MmioTranslation
);
224 if (MmioTranslation
!= 0) {
225 DEBUG ((EFI_D_ERROR
, "%a: unsupported nonzero MMIO32 translation "
226 "0x%Lx\n", __FUNCTION__
, MmioTranslation
));
227 return EFI_UNSUPPORTED
;
233 if (*IoSize
== 0 || *MmioSize
== 0) {
234 DEBUG ((EFI_D_ERROR
, "%a: %a space empty\n", __FUNCTION__
,
235 (*IoSize
== 0) ? "IO" : "MMIO32"));
236 return EFI_PROTOCOL_ERROR
;
240 // The dynamic PCD PcdPciExpressBaseAddress should have already been set,
241 // and should match the value we found in the DT node.
243 ASSERT (PcdGet64 (PcdPciExpressBaseAddress
) == ConfigBase
);
245 DEBUG ((EFI_D_INFO
, "%a: Config[0x%Lx+0x%Lx) Bus[0x%x..0x%x] "
246 "Io[0x%Lx+0x%Lx)@0x%Lx Mem[0x%Lx+0x%Lx)@0x0\n", __FUNCTION__
, ConfigBase
,
247 ConfigSize
, *BusMin
, *BusMax
, *IoBase
, *IoSize
, IoTranslation
, *MmioBase
,
252 STATIC PCI_ROOT_BRIDGE mRootBridge
;
255 Return all the root bridge instances in an array.
257 @param Count Return the count of root bridge instances.
259 @return All the root bridge instances in an array.
260 The array should be passed into PciHostBridgeFreeRootBridges()
265 PciHostBridgeGetRootBridges (
269 UINT64 IoBase
, IoSize
;
270 UINT64 Mmio32Base
, Mmio32Size
;
271 UINT32 BusMin
, BusMax
;
274 if (PcdGet64 (PcdPciExpressBaseAddress
) == 0) {
275 DEBUG ((EFI_D_INFO
, "%a: PCI host bridge not present\n", __FUNCTION__
));
281 Status
= ProcessPciHost (&IoBase
, &IoSize
, &Mmio32Base
, &Mmio32Size
, &BusMin
,
283 if (EFI_ERROR (Status
)) {
284 DEBUG ((EFI_D_ERROR
, "%a: failed to discover PCI host bridge: %r\n",
285 __FUNCTION__
, Status
));
292 mRootBridge
.Segment
= 0;
293 mRootBridge
.Supports
= EFI_PCI_ATTRIBUTE_ISA_IO_16
|
294 EFI_PCI_ATTRIBUTE_ISA_MOTHERBOARD_IO
|
295 EFI_PCI_ATTRIBUTE_VGA_IO_16
|
296 EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO_16
;
297 mRootBridge
.Attributes
= mRootBridge
.Supports
;
299 mRootBridge
.DmaAbove4G
= FALSE
;
300 mRootBridge
.NoExtendedConfigSpace
= FALSE
;
301 mRootBridge
.ResourceAssigned
= FALSE
;
303 mRootBridge
.AllocationAttributes
= EFI_PCI_HOST_BRIDGE_COMBINE_MEM_PMEM
;
305 mRootBridge
.Bus
.Base
= BusMin
;
306 mRootBridge
.Bus
.Limit
= BusMax
;
307 mRootBridge
.Io
.Base
= IoBase
;
308 mRootBridge
.Io
.Limit
= IoBase
+ IoSize
- 1;
309 mRootBridge
.Mem
.Base
= Mmio32Base
;
310 mRootBridge
.Mem
.Limit
= Mmio32Base
+ Mmio32Size
- 1;
311 mRootBridge
.MemAbove4G
.Base
= MAX_UINT64
;
312 mRootBridge
.MemAbove4G
.Limit
= 0;
315 // No separate ranges for prefetchable and non-prefetchable BARs
317 mRootBridge
.PMem
.Base
= MAX_UINT64
;
318 mRootBridge
.PMem
.Limit
= 0;
319 mRootBridge
.PMemAbove4G
.Base
= MAX_UINT64
;
320 mRootBridge
.PMemAbove4G
.Limit
= 0;
322 mRootBridge
.DevicePath
= (EFI_DEVICE_PATH_PROTOCOL
*)&mEfiPciRootBridgeDevicePath
;
328 Free the root bridge instances array returned from
329 PciHostBridgeGetRootBridges().
331 @param Bridges The root bridge instances array.
332 @param Count The count of the array.
336 PciHostBridgeFreeRootBridges (
337 PCI_ROOT_BRIDGE
*Bridges
,
345 Inform the platform that the resource conflict happens.
347 @param HostBridgeHandle Handle of the Host Bridge.
348 @param Configuration Pointer to PCI I/O and PCI memory resource
349 descriptors. The Configuration contains the resources
350 for all the root bridges. The resource for each root
351 bridge is terminated with END descriptor and an
352 additional END is appended indicating the end of the
353 entire resources. The resource descriptor field
354 values follow the description in
355 EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL
360 PciHostBridgeResourceConflict (
361 EFI_HANDLE HostBridgeHandle
,
365 EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR
*Descriptor
;
366 UINTN RootBridgeIndex
;
367 DEBUG ((EFI_D_ERROR
, "PciHostBridge: Resource conflict happens!\n"));
370 Descriptor
= (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR
*) Configuration
;
371 while (Descriptor
->Desc
== ACPI_ADDRESS_SPACE_DESCRIPTOR
) {
372 DEBUG ((EFI_D_ERROR
, "RootBridge[%d]:\n", RootBridgeIndex
++));
373 for (; Descriptor
->Desc
== ACPI_ADDRESS_SPACE_DESCRIPTOR
; Descriptor
++) {
374 ASSERT (Descriptor
->ResType
<
375 (sizeof (mPciHostBridgeLibAcpiAddressSpaceTypeStr
) /
376 sizeof (mPciHostBridgeLibAcpiAddressSpaceTypeStr
[0])
379 DEBUG ((EFI_D_ERROR
, " %s: Length/Alignment = 0x%lx / 0x%lx\n",
380 mPciHostBridgeLibAcpiAddressSpaceTypeStr
[Descriptor
->ResType
],
381 Descriptor
->AddrLen
, Descriptor
->AddrRangeMax
383 if (Descriptor
->ResType
== ACPI_ADDRESS_SPACE_TYPE_MEM
) {
384 DEBUG ((EFI_D_ERROR
, " Granularity/SpecificFlag = %ld / %02x%s\n",
385 Descriptor
->AddrSpaceGranularity
, Descriptor
->SpecificFlag
,
386 ((Descriptor
->SpecificFlag
&
387 EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE
388 ) != 0) ? L
" (Prefetchable)" : L
""
393 // Skip the END descriptor for root bridge
395 ASSERT (Descriptor
->Desc
== ACPI_END_TAG_DESCRIPTOR
);
396 Descriptor
= (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR
*)(
397 (EFI_ACPI_END_TAG_DESCRIPTOR
*)Descriptor
+ 1