summaryrefslogtreecommitdiffstats
path: root/OvmfPkg/Fdt/FdtPciHostBridgeLib/FdtPciHostBridgeLib.c
diff options
context:
space:
mode:
Diffstat (limited to 'OvmfPkg/Fdt/FdtPciHostBridgeLib/FdtPciHostBridgeLib.c')
-rw-r--r--OvmfPkg/Fdt/FdtPciHostBridgeLib/FdtPciHostBridgeLib.c417
1 files changed, 417 insertions, 0 deletions
diff --git a/OvmfPkg/Fdt/FdtPciHostBridgeLib/FdtPciHostBridgeLib.c b/OvmfPkg/Fdt/FdtPciHostBridgeLib/FdtPciHostBridgeLib.c
new file mode 100644
index 0000000000..0099b8e3c3
--- /dev/null
+++ b/OvmfPkg/Fdt/FdtPciHostBridgeLib/FdtPciHostBridgeLib.c
@@ -0,0 +1,417 @@
+/** @file
+ PCI Host Bridge Library instance for pci-ecam-generic DT nodes
+
+ Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#include <PiDxe.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PciHostBridgeLib.h>
+#include <Library/PciHostBridgeUtilityLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include <Protocol/FdtClient.h>
+#include <Protocol/PciRootBridgeIo.h>
+#include <Protocol/PciHostBridgeResourceAllocation.h>
+
+//
+// We expect the "ranges" property of "pci-host-ecam-generic" to consist of
+// records like this.
+//
+#pragma pack (1)
+typedef struct {
+ UINT32 Type;
+ UINT64 ChildBase;
+ UINT64 CpuBase;
+ UINT64 Size;
+} DTB_PCI_HOST_RANGE_RECORD;
+#pragma pack ()
+
+#define DTB_PCI_HOST_RANGE_RELOCATABLE BIT31
+#define DTB_PCI_HOST_RANGE_PREFETCHABLE BIT30
+#define DTB_PCI_HOST_RANGE_ALIASED BIT29
+#define DTB_PCI_HOST_RANGE_MMIO32 BIT25
+#define DTB_PCI_HOST_RANGE_MMIO64 (BIT25 | BIT24)
+#define DTB_PCI_HOST_RANGE_IO BIT24
+#define DTB_PCI_HOST_RANGE_TYPEMASK (BIT31 | BIT30 | BIT29 | BIT25 | BIT24)
+
+STATIC
+EFI_STATUS
+MapGcdMmioSpace (
+ IN UINT64 Base,
+ IN UINT64 Size
+ )
+{
+ EFI_STATUS Status;
+
+ Status = gDS->AddMemorySpace (EfiGcdMemoryTypeMemoryMappedIo, Base, Size,
+ EFI_MEMORY_UC);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR,
+ "%a: failed to add GCD memory space for region [0x%Lx+0x%Lx)\n",
+ __FUNCTION__, Base, Size));
+ return Status;
+ }
+
+ Status = gDS->SetMemorySpaceAttributes (Base, Size, EFI_MEMORY_UC);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR,
+ "%a: failed to set memory space attributes for region [0x%Lx+0x%Lx)\n",
+ __FUNCTION__, Base, Size));
+ }
+ return Status;
+}
+
+STATIC
+EFI_STATUS
+ProcessPciHost (
+ OUT UINT64 *IoBase,
+ OUT UINT64 *IoSize,
+ OUT UINT64 *Mmio32Base,
+ OUT UINT64 *Mmio32Size,
+ OUT UINT64 *Mmio64Base,
+ OUT UINT64 *Mmio64Size,
+ OUT UINT32 *BusMin,
+ OUT UINT32 *BusMax
+ )
+{
+ FDT_CLIENT_PROTOCOL *FdtClient;
+ INT32 Node;
+ UINT64 ConfigBase, ConfigSize;
+ CONST VOID *Prop;
+ UINT32 Len;
+ UINT32 RecordIdx;
+ EFI_STATUS Status;
+ UINT64 IoTranslation;
+ UINT64 Mmio32Translation;
+ UINT64 Mmio64Translation;
+
+ //
+ // The following output arguments are initialized only in
+ // order to suppress '-Werror=maybe-uninitialized' warnings
+ // *incorrectly* emitted by some gcc versions.
+ //
+ *IoBase = 0;
+ *Mmio32Base = 0;
+ *Mmio64Base = MAX_UINT64;
+ *BusMin = 0;
+ *BusMax = 0;
+
+ //
+ // *IoSize, *Mmio##Size and IoTranslation are initialized to zero because the
+ // logic below requires it. However, since they are also affected by the issue
+ // reported above, they are initialized early.
+ //
+ *IoSize = 0;
+ *Mmio32Size = 0;
+ *Mmio64Size = 0;
+ IoTranslation = 0;
+
+ Status = gBS->LocateProtocol (&gFdtClientProtocolGuid, NULL,
+ (VOID **)&FdtClient);
+ ASSERT_EFI_ERROR (Status);
+
+ Status = FdtClient->FindCompatibleNode (FdtClient, "pci-host-ecam-generic",
+ &Node);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_INFO,
+ "%a: No 'pci-host-ecam-generic' compatible DT node found\n",
+ __FUNCTION__));
+ return EFI_NOT_FOUND;
+ }
+
+ DEBUG_CODE (
+ INT32 Tmp;
+
+ //
+ // A DT can legally describe multiple PCI host bridges, but we are not
+ // equipped to deal with that. So assert that there is only one.
+ //
+ Status = FdtClient->FindNextCompatibleNode (FdtClient,
+ "pci-host-ecam-generic", Node, &Tmp);
+ ASSERT (Status == EFI_NOT_FOUND);
+ );
+
+ Status = FdtClient->GetNodeProperty (FdtClient, Node, "reg", &Prop, &Len);
+ if (EFI_ERROR (Status) || Len != 2 * sizeof (UINT64)) {
+ DEBUG ((EFI_D_ERROR, "%a: 'reg' property not found or invalid\n",
+ __FUNCTION__));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ //
+ // Fetch the ECAM window.
+ //
+ ConfigBase = SwapBytes64 (((CONST UINT64 *)Prop)[0]);
+ ConfigSize = SwapBytes64 (((CONST UINT64 *)Prop)[1]);
+
+ //
+ // Fetch the bus range (note: inclusive).
+ //
+ Status = FdtClient->GetNodeProperty (FdtClient, Node, "bus-range", &Prop,
+ &Len);
+ if (EFI_ERROR (Status) || Len != 2 * sizeof (UINT32)) {
+ DEBUG ((EFI_D_ERROR, "%a: 'bus-range' not found or invalid\n",
+ __FUNCTION__));
+ return EFI_PROTOCOL_ERROR;
+ }
+ *BusMin = SwapBytes32 (((CONST UINT32 *)Prop)[0]);
+ *BusMax = SwapBytes32 (((CONST UINT32 *)Prop)[1]);
+
+ //
+ // Sanity check: the config space must accommodate all 4K register bytes of
+ // all 8 functions of all 32 devices of all buses.
+ //
+ if (*BusMax < *BusMin || *BusMax - *BusMin == MAX_UINT32 ||
+ DivU64x32 (ConfigSize, SIZE_4KB * 8 * 32) < *BusMax - *BusMin + 1) {
+ DEBUG ((EFI_D_ERROR, "%a: invalid 'bus-range' and/or 'reg'\n",
+ __FUNCTION__));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ //
+ // Iterate over "ranges".
+ //
+ Status = FdtClient->GetNodeProperty (FdtClient, Node, "ranges", &Prop, &Len);
+ if (EFI_ERROR (Status) || Len == 0 ||
+ Len % sizeof (DTB_PCI_HOST_RANGE_RECORD) != 0) {
+ DEBUG ((EFI_D_ERROR, "%a: 'ranges' not found or invalid\n", __FUNCTION__));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ for (RecordIdx = 0; RecordIdx < Len / sizeof (DTB_PCI_HOST_RANGE_RECORD);
+ ++RecordIdx) {
+ CONST DTB_PCI_HOST_RANGE_RECORD *Record;
+
+ Record = (CONST DTB_PCI_HOST_RANGE_RECORD *)Prop + RecordIdx;
+ switch (SwapBytes32 (Record->Type) & DTB_PCI_HOST_RANGE_TYPEMASK) {
+ case DTB_PCI_HOST_RANGE_IO:
+ *IoBase = SwapBytes64 (Record->ChildBase);
+ *IoSize = SwapBytes64 (Record->Size);
+ IoTranslation = SwapBytes64 (Record->CpuBase) - *IoBase;
+
+ ASSERT (PcdGet64 (PcdPciIoTranslation) == IoTranslation);
+ break;
+
+ case DTB_PCI_HOST_RANGE_MMIO32:
+ *Mmio32Base = SwapBytes64 (Record->ChildBase);
+ *Mmio32Size = SwapBytes64 (Record->Size);
+ Mmio32Translation = SwapBytes64 (Record->CpuBase) - *Mmio32Base;
+
+ if (*Mmio32Base > MAX_UINT32 || *Mmio32Size > MAX_UINT32 ||
+ *Mmio32Base + *Mmio32Size > SIZE_4GB) {
+ DEBUG ((EFI_D_ERROR, "%a: MMIO32 space invalid\n", __FUNCTION__));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ ASSERT (PcdGet64 (PcdPciMmio32Translation) == Mmio32Translation);
+
+ if (Mmio32Translation != 0) {
+ DEBUG ((EFI_D_ERROR, "%a: unsupported nonzero MMIO32 translation "
+ "0x%Lx\n", __FUNCTION__, Mmio32Translation));
+ return EFI_UNSUPPORTED;
+ }
+
+ break;
+
+ case DTB_PCI_HOST_RANGE_MMIO64:
+ *Mmio64Base = SwapBytes64 (Record->ChildBase);
+ *Mmio64Size = SwapBytes64 (Record->Size);
+ Mmio64Translation = SwapBytes64 (Record->CpuBase) - *Mmio64Base;
+
+ ASSERT (PcdGet64 (PcdPciMmio64Translation) == Mmio64Translation);
+
+ if (Mmio64Translation != 0) {
+ DEBUG ((EFI_D_ERROR, "%a: unsupported nonzero MMIO64 translation "
+ "0x%Lx\n", __FUNCTION__, Mmio64Translation));
+ return EFI_UNSUPPORTED;
+ }
+
+ break;
+ }
+ }
+ if (*IoSize == 0 || *Mmio32Size == 0) {
+ DEBUG ((EFI_D_ERROR, "%a: %a space empty\n", __FUNCTION__,
+ (*IoSize == 0) ? "IO" : "MMIO32"));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ //
+ // The dynamic PCD PcdPciExpressBaseAddress should have already been set,
+ // and should match the value we found in the DT node.
+ //
+ ASSERT (PcdGet64 (PcdPciExpressBaseAddress) == ConfigBase);
+
+ DEBUG ((EFI_D_INFO, "%a: Config[0x%Lx+0x%Lx) Bus[0x%x..0x%x] "
+ "Io[0x%Lx+0x%Lx)@0x%Lx Mem32[0x%Lx+0x%Lx)@0x0 Mem64[0x%Lx+0x%Lx)@0x0\n",
+ __FUNCTION__, ConfigBase, ConfigSize, *BusMin, *BusMax, *IoBase, *IoSize,
+ IoTranslation, *Mmio32Base, *Mmio32Size, *Mmio64Base, *Mmio64Size));
+
+ // Map the ECAM space in the GCD memory map
+ Status = MapGcdMmioSpace (ConfigBase, ConfigSize);
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Map the MMIO window that provides I/O access - the PCI host bridge code
+ // is not aware of this translation and so it will only map the I/O view
+ // in the GCD I/O map.
+ //
+ Status = MapGcdMmioSpace (*IoBase + IoTranslation, *IoSize);
+ ASSERT_EFI_ERROR (Status);
+
+ return Status;
+}
+
+/**
+ Return all the root bridge instances in an array.
+
+ @param Count Return the count of root bridge instances.
+
+ @return All the root bridge instances in an array.
+ The array should be passed into PciHostBridgeFreeRootBridges()
+ when it's not used.
+**/
+PCI_ROOT_BRIDGE *
+EFIAPI
+PciHostBridgeGetRootBridges (
+ UINTN *Count
+ )
+{
+ UINT64 IoBase, IoSize;
+ UINT64 Mmio32Base, Mmio32Size;
+ UINT64 Mmio64Base, Mmio64Size;
+ UINT32 BusMin, BusMax;
+ EFI_STATUS Status;
+ UINT64 Attributes;
+ UINT64 AllocationAttributes;
+ PCI_ROOT_BRIDGE_APERTURE Io;
+ PCI_ROOT_BRIDGE_APERTURE Mem;
+ PCI_ROOT_BRIDGE_APERTURE MemAbove4G;
+ PCI_ROOT_BRIDGE_APERTURE PMem;
+ PCI_ROOT_BRIDGE_APERTURE PMemAbove4G;
+
+ if (PcdGet64 (PcdPciExpressBaseAddress) == 0) {
+ DEBUG ((EFI_D_INFO, "%a: PCI host bridge not present\n", __FUNCTION__));
+
+ *Count = 0;
+ return NULL;
+ }
+
+ Status = ProcessPciHost (&IoBase, &IoSize, &Mmio32Base, &Mmio32Size,
+ &Mmio64Base, &Mmio64Size, &BusMin, &BusMax);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "%a: failed to discover PCI host bridge: %r\n",
+ __FUNCTION__, Status));
+ *Count = 0;
+ return NULL;
+ }
+
+ ZeroMem (&Io, sizeof (Io));
+ ZeroMem (&Mem, sizeof (Mem));
+ ZeroMem (&MemAbove4G, sizeof (MemAbove4G));
+ ZeroMem (&PMem, sizeof (PMem));
+ ZeroMem (&PMemAbove4G, sizeof (PMemAbove4G));
+
+ Attributes = EFI_PCI_ATTRIBUTE_ISA_IO_16 |
+ EFI_PCI_ATTRIBUTE_ISA_MOTHERBOARD_IO |
+ EFI_PCI_ATTRIBUTE_VGA_IO_16 |
+ EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO_16;
+
+ AllocationAttributes = EFI_PCI_HOST_BRIDGE_COMBINE_MEM_PMEM;
+
+ Io.Base = IoBase;
+ Io.Limit = IoBase + IoSize - 1;
+ Mem.Base = Mmio32Base;
+ Mem.Limit = Mmio32Base + Mmio32Size - 1;
+
+ if (sizeof (UINTN) == sizeof (UINT64)) {
+ MemAbove4G.Base = Mmio64Base;
+ MemAbove4G.Limit = Mmio64Base + Mmio64Size - 1;
+ if (Mmio64Size > 0) {
+ AllocationAttributes |= EFI_PCI_HOST_BRIDGE_MEM64_DECODE;
+ }
+ } else {
+ //
+ // UEFI mandates a 1:1 virtual-to-physical mapping, so on a 32-bit
+ // architecture such as ARM, we will not be able to access 64-bit MMIO
+ // BARs unless they are allocated below 4 GB. So ignore the range above
+ // 4 GB in this case.
+ //
+ MemAbove4G.Base = MAX_UINT64;
+ MemAbove4G.Limit = 0;
+ }
+
+ //
+ // No separate ranges for prefetchable and non-prefetchable BARs
+ //
+ PMem.Base = MAX_UINT64;
+ PMem.Limit = 0;
+ PMemAbove4G.Base = MAX_UINT64;
+ PMemAbove4G.Limit = 0;
+
+ return PciHostBridgeUtilityGetRootBridges (
+ Count,
+ Attributes,
+ AllocationAttributes,
+ TRUE,
+ FALSE,
+ BusMin,
+ BusMax,
+ &Io,
+ &Mem,
+ &MemAbove4G,
+ &PMem,
+ &PMemAbove4G
+ );
+}
+
+/**
+ Free the root bridge instances array returned from
+ PciHostBridgeGetRootBridges().
+
+ @param Bridges The root bridge instances array.
+ @param Count The count of the array.
+**/
+VOID
+EFIAPI
+PciHostBridgeFreeRootBridges (
+ PCI_ROOT_BRIDGE *Bridges,
+ UINTN Count
+ )
+{
+ PciHostBridgeUtilityFreeRootBridges (Bridges, Count);
+}
+
+/**
+ Inform the platform that the resource conflict happens.
+
+ @param HostBridgeHandle Handle of the Host Bridge.
+ @param Configuration Pointer to PCI I/O and PCI memory resource
+ descriptors. The Configuration contains the resources
+ for all the root bridges. The resource for each root
+ bridge is terminated with END descriptor and an
+ additional END is appended indicating the end of the
+ entire resources. The resource descriptor field
+ values follow the description in
+ EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL
+ .SubmitResources().
+**/
+VOID
+EFIAPI
+PciHostBridgeResourceConflict (
+ EFI_HANDLE HostBridgeHandle,
+ VOID *Configuration
+ )
+{
+ PciHostBridgeUtilityResourceConflict (Configuration);
+}