--- /dev/null
+/** @file\r
+ PCI Host Bridge Library instance for pci-ecam-generic DT nodes\r
+\r
+ Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials are licensed and made available\r
+ under the terms and conditions of the BSD License which accompanies this\r
+ distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php.\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR\r
+ IMPLIED.\r
+\r
+**/\r
+#include <PiDxe.h>\r
+#include <Library/PciHostBridgeLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/DevicePathLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/PcdLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+\r
+#include <Protocol/FdtClient.h>\r
+#include <Protocol/PciRootBridgeIo.h>\r
+#include <Protocol/PciHostBridgeResourceAllocation.h>\r
+\r
+#pragma pack(1)\r
+typedef struct {\r
+ ACPI_HID_DEVICE_PATH AcpiDevicePath;\r
+ EFI_DEVICE_PATH_PROTOCOL EndDevicePath;\r
+} EFI_PCI_ROOT_BRIDGE_DEVICE_PATH;\r
+#pragma pack ()\r
+\r
+STATIC EFI_PCI_ROOT_BRIDGE_DEVICE_PATH mEfiPciRootBridgeDevicePath = {\r
+ {\r
+ {\r
+ ACPI_DEVICE_PATH,\r
+ ACPI_DP,\r
+ {\r
+ (UINT8) (sizeof(ACPI_HID_DEVICE_PATH)),\r
+ (UINT8) ((sizeof(ACPI_HID_DEVICE_PATH)) >> 8)\r
+ }\r
+ },\r
+ EISA_PNP_ID(0x0A03),\r
+ 0\r
+ },\r
+\r
+ {\r
+ END_DEVICE_PATH_TYPE,\r
+ END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
+ {\r
+ END_DEVICE_PATH_LENGTH,\r
+ 0\r
+ }\r
+ }\r
+};\r
+\r
+GLOBAL_REMOVE_IF_UNREFERENCED\r
+CHAR16 *mPciHostBridgeLibAcpiAddressSpaceTypeStr[] = {\r
+ L"Mem", L"I/O", L"Bus"\r
+};\r
+\r
+//\r
+// We expect the "ranges" property of "pci-host-ecam-generic" to consist of\r
+// records like this.\r
+//\r
+#pragma pack (1)\r
+typedef struct {\r
+ UINT32 Type;\r
+ UINT64 ChildBase;\r
+ UINT64 CpuBase;\r
+ UINT64 Size;\r
+} DTB_PCI_HOST_RANGE_RECORD;\r
+#pragma pack ()\r
+\r
+#define DTB_PCI_HOST_RANGE_RELOCATABLE BIT31\r
+#define DTB_PCI_HOST_RANGE_PREFETCHABLE BIT30\r
+#define DTB_PCI_HOST_RANGE_ALIASED BIT29\r
+#define DTB_PCI_HOST_RANGE_MMIO32 BIT25\r
+#define DTB_PCI_HOST_RANGE_MMIO64 (BIT25 | BIT24)\r
+#define DTB_PCI_HOST_RANGE_IO BIT24\r
+#define DTB_PCI_HOST_RANGE_TYPEMASK (BIT31 | BIT30 | BIT29 | BIT25 | BIT24)\r
+\r
+STATIC\r
+EFI_STATUS\r
+ProcessPciHost (\r
+ OUT UINT64 *IoBase,\r
+ OUT UINT64 *IoSize,\r
+ OUT UINT64 *MmioBase,\r
+ OUT UINT64 *MmioSize,\r
+ OUT UINT32 *BusMin,\r
+ OUT UINT32 *BusMax\r
+ )\r
+{\r
+ FDT_CLIENT_PROTOCOL *FdtClient;\r
+ INT32 Node;\r
+ UINT64 ConfigBase, ConfigSize;\r
+ CONST VOID *Prop;\r
+ UINT32 Len;\r
+ UINT32 RecordIdx;\r
+ EFI_STATUS Status;\r
+ UINT64 IoTranslation;\r
+ UINT64 MmioTranslation;\r
+\r
+ //\r
+ // The following output arguments are initialized only in\r
+ // order to suppress '-Werror=maybe-uninitialized' warnings\r
+ // *incorrectly* emitted by some gcc versions.\r
+ //\r
+ *IoBase = 0;\r
+ *MmioBase = 0;\r
+ *BusMin = 0;\r
+ *BusMax = 0;\r
+\r
+ //\r
+ // *IoSize, *MmioSize and IoTranslation are initialized to zero because the\r
+ // logic below requires it. However, since they are also affected by the issue\r
+ // reported above, they are initialized early.\r
+ //\r
+ *IoSize = 0;\r
+ *MmioSize = 0;\r
+ IoTranslation = 0;\r
+\r
+ Status = gBS->LocateProtocol (&gFdtClientProtocolGuid, NULL,\r
+ (VOID **)&FdtClient);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Status = FdtClient->FindCompatibleNode (FdtClient, "pci-host-ecam-generic",\r
+ &Node);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_INFO,\r
+ "%a: No 'pci-host-ecam-generic' compatible DT node found\n",\r
+ __FUNCTION__));\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ DEBUG_CODE (\r
+ INT32 Tmp;\r
+\r
+ //\r
+ // A DT can legally describe multiple PCI host bridges, but we are not\r
+ // equipped to deal with that. So assert that there is only one.\r
+ //\r
+ Status = FdtClient->FindNextCompatibleNode (FdtClient,\r
+ "pci-host-ecam-generic", Node, &Tmp);\r
+ ASSERT (Status == EFI_NOT_FOUND);\r
+ );\r
+\r
+ Status = FdtClient->GetNodeProperty (FdtClient, Node, "reg", &Prop, &Len);\r
+ if (EFI_ERROR (Status) || Len != 2 * sizeof (UINT64)) {\r
+ DEBUG ((EFI_D_ERROR, "%a: 'reg' property not found or invalid\n",\r
+ __FUNCTION__));\r
+ return EFI_PROTOCOL_ERROR;\r
+ }\r
+\r
+ //\r
+ // Fetch the ECAM window.\r
+ //\r
+ ConfigBase = SwapBytes64 (((CONST UINT64 *)Prop)[0]);\r
+ ConfigSize = SwapBytes64 (((CONST UINT64 *)Prop)[1]);\r
+\r
+ //\r
+ // Fetch the bus range (note: inclusive).\r
+ //\r
+ Status = FdtClient->GetNodeProperty (FdtClient, Node, "bus-range", &Prop,\r
+ &Len);\r
+ if (EFI_ERROR (Status) || Len != 2 * sizeof (UINT32)) {\r
+ DEBUG ((EFI_D_ERROR, "%a: 'bus-range' not found or invalid\n",\r
+ __FUNCTION__));\r
+ return EFI_PROTOCOL_ERROR;\r
+ }\r
+ *BusMin = SwapBytes32 (((CONST UINT32 *)Prop)[0]);\r
+ *BusMax = SwapBytes32 (((CONST UINT32 *)Prop)[1]);\r
+\r
+ //\r
+ // Sanity check: the config space must accommodate all 4K register bytes of\r
+ // all 8 functions of all 32 devices of all buses.\r
+ //\r
+ if (*BusMax < *BusMin || *BusMax - *BusMin == MAX_UINT32 ||\r
+ DivU64x32 (ConfigSize, SIZE_4KB * 8 * 32) < *BusMax - *BusMin + 1) {\r
+ DEBUG ((EFI_D_ERROR, "%a: invalid 'bus-range' and/or 'reg'\n",\r
+ __FUNCTION__));\r
+ return EFI_PROTOCOL_ERROR;\r
+ }\r
+\r
+ //\r
+ // Iterate over "ranges".\r
+ //\r
+ Status = FdtClient->GetNodeProperty (FdtClient, Node, "ranges", &Prop, &Len);\r
+ if (EFI_ERROR (Status) || Len == 0 ||\r
+ Len % sizeof (DTB_PCI_HOST_RANGE_RECORD) != 0) {\r
+ DEBUG ((EFI_D_ERROR, "%a: 'ranges' not found or invalid\n", __FUNCTION__));\r
+ return EFI_PROTOCOL_ERROR;\r
+ }\r
+\r
+ for (RecordIdx = 0; RecordIdx < Len / sizeof (DTB_PCI_HOST_RANGE_RECORD);\r
+ ++RecordIdx) {\r
+ CONST DTB_PCI_HOST_RANGE_RECORD *Record;\r
+\r
+ Record = (CONST DTB_PCI_HOST_RANGE_RECORD *)Prop + RecordIdx;\r
+ switch (SwapBytes32 (Record->Type) & DTB_PCI_HOST_RANGE_TYPEMASK) {\r
+ case DTB_PCI_HOST_RANGE_IO:\r
+ *IoBase = SwapBytes64 (Record->ChildBase);\r
+ *IoSize = SwapBytes64 (Record->Size);\r
+ IoTranslation = SwapBytes64 (Record->CpuBase) - *IoBase;\r
+\r
+ ASSERT (PcdGet64 (PcdPciIoTranslation) == IoTranslation);\r
+ break;\r
+\r
+ case DTB_PCI_HOST_RANGE_MMIO32:\r
+ *MmioBase = SwapBytes64 (Record->ChildBase);\r
+ *MmioSize = SwapBytes64 (Record->Size);\r
+ MmioTranslation = SwapBytes64 (Record->CpuBase) - *MmioBase;\r
+\r
+ if (*MmioBase > MAX_UINT32 || *MmioSize > MAX_UINT32 ||\r
+ *MmioBase + *MmioSize > SIZE_4GB) {\r
+ DEBUG ((EFI_D_ERROR, "%a: MMIO32 space invalid\n", __FUNCTION__));\r
+ return EFI_PROTOCOL_ERROR;\r
+ }\r
+\r
+ ASSERT (PcdGet64 (PcdPciMmio32Translation) == MmioTranslation);\r
+\r
+ if (MmioTranslation != 0) {\r
+ DEBUG ((EFI_D_ERROR, "%a: unsupported nonzero MMIO32 translation "\r
+ "0x%Lx\n", __FUNCTION__, MmioTranslation));\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ break;\r
+ }\r
+ }\r
+ if (*IoSize == 0 || *MmioSize == 0) {\r
+ DEBUG ((EFI_D_ERROR, "%a: %a space empty\n", __FUNCTION__,\r
+ (*IoSize == 0) ? "IO" : "MMIO32"));\r
+ return EFI_PROTOCOL_ERROR;\r
+ }\r
+\r
+ //\r
+ // The dynamic PCD PcdPciExpressBaseAddress should have already been set,\r
+ // and should match the value we found in the DT node.\r
+ //\r
+ ASSERT (PcdGet64 (PcdPciExpressBaseAddress) == ConfigBase);\r
+\r
+ DEBUG ((EFI_D_INFO, "%a: Config[0x%Lx+0x%Lx) Bus[0x%x..0x%x] "\r
+ "Io[0x%Lx+0x%Lx)@0x%Lx Mem[0x%Lx+0x%Lx)@0x0\n", __FUNCTION__, ConfigBase,\r
+ ConfigSize, *BusMin, *BusMax, *IoBase, *IoSize, IoTranslation, *MmioBase,\r
+ *MmioSize));\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+STATIC PCI_ROOT_BRIDGE mRootBridge;\r
+\r
+/**\r
+ Return all the root bridge instances in an array.\r
+\r
+ @param Count Return the count of root bridge instances.\r
+\r
+ @return All the root bridge instances in an array.\r
+ The array should be passed into PciHostBridgeFreeRootBridges()\r
+ when it's not used.\r
+**/\r
+PCI_ROOT_BRIDGE *\r
+EFIAPI\r
+PciHostBridgeGetRootBridges (\r
+ UINTN *Count\r
+ )\r
+{\r
+ UINT64 IoBase, IoSize;\r
+ UINT64 Mmio32Base, Mmio32Size;\r
+ UINT32 BusMin, BusMax;\r
+ EFI_STATUS Status;\r
+\r
+ if (PcdGet64 (PcdPciExpressBaseAddress) == 0) {\r
+ DEBUG ((EFI_D_INFO, "%a: PCI host bridge not present\n", __FUNCTION__));\r
+\r
+ *Count = 0;\r
+ return NULL;\r
+ }\r
+\r
+ Status = ProcessPciHost (&IoBase, &IoSize, &Mmio32Base, &Mmio32Size, &BusMin,\r
+ &BusMax);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "%a: failed to discover PCI host bridge: %r\n",\r
+ __FUNCTION__, Status));\r
+ *Count = 0;\r
+ return NULL;\r
+ }\r
+\r
+ *Count = 1;\r
+\r
+ mRootBridge.Segment = 0;\r
+ mRootBridge.Supports = EFI_PCI_ATTRIBUTE_ISA_IO_16 |\r
+ EFI_PCI_ATTRIBUTE_ISA_MOTHERBOARD_IO |\r
+ EFI_PCI_ATTRIBUTE_VGA_IO_16 |\r
+ EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO_16;\r
+ mRootBridge.Attributes = mRootBridge.Supports;\r
+\r
+ mRootBridge.DmaAbove4G = FALSE;\r
+ mRootBridge.NoExtendedConfigSpace = FALSE;\r
+ mRootBridge.ResourceAssigned = FALSE;\r
+\r
+ mRootBridge.AllocationAttributes = EFI_PCI_HOST_BRIDGE_COMBINE_MEM_PMEM;\r
+\r
+ mRootBridge.Bus.Base = BusMin;\r
+ mRootBridge.Bus.Limit = BusMax;\r
+ mRootBridge.Io.Base = IoBase;\r
+ mRootBridge.Io.Limit = IoBase + IoSize - 1;\r
+ mRootBridge.Mem.Base = Mmio32Base;\r
+ mRootBridge.Mem.Limit = Mmio32Base + Mmio32Size - 1;\r
+ mRootBridge.MemAbove4G.Base = MAX_UINT64;\r
+ mRootBridge.MemAbove4G.Limit = 0;\r
+\r
+ //\r
+ // No separate ranges for prefetchable and non-prefetchable BARs\r
+ //\r
+ mRootBridge.PMem.Base = MAX_UINT64;\r
+ mRootBridge.PMem.Limit = 0;\r
+ mRootBridge.PMemAbove4G.Base = MAX_UINT64;\r
+ mRootBridge.PMemAbove4G.Limit = 0;\r
+\r
+ mRootBridge.DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)&mEfiPciRootBridgeDevicePath;\r
+\r
+ return &mRootBridge;\r
+}\r
+\r
+/**\r
+ Free the root bridge instances array returned from\r
+ PciHostBridgeGetRootBridges().\r
+\r
+ @param Bridges The root bridge instances array.\r
+ @param Count The count of the array.\r
+**/\r
+VOID\r
+EFIAPI\r
+PciHostBridgeFreeRootBridges (\r
+ PCI_ROOT_BRIDGE *Bridges,\r
+ UINTN Count\r
+ )\r
+{\r
+ ASSERT (Count == 1);\r
+}\r
+\r
+/**\r
+ Inform the platform that the resource conflict happens.\r
+\r
+ @param HostBridgeHandle Handle of the Host Bridge.\r
+ @param Configuration Pointer to PCI I/O and PCI memory resource\r
+ descriptors. The Configuration contains the resources\r
+ for all the root bridges. The resource for each root\r
+ bridge is terminated with END descriptor and an\r
+ additional END is appended indicating the end of the\r
+ entire resources. The resource descriptor field\r
+ values follow the description in\r
+ EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL\r
+ .SubmitResources().\r
+**/\r
+VOID\r
+EFIAPI\r
+PciHostBridgeResourceConflict (\r
+ EFI_HANDLE HostBridgeHandle,\r
+ VOID *Configuration\r
+ )\r
+{\r
+ EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor;\r
+ UINTN RootBridgeIndex;\r
+ DEBUG ((EFI_D_ERROR, "PciHostBridge: Resource conflict happens!\n"));\r
+\r
+ RootBridgeIndex = 0;\r
+ Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Configuration;\r
+ while (Descriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR) {\r
+ DEBUG ((EFI_D_ERROR, "RootBridge[%d]:\n", RootBridgeIndex++));\r
+ for (; Descriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR; Descriptor++) {\r
+ ASSERT (Descriptor->ResType <\r
+ (sizeof (mPciHostBridgeLibAcpiAddressSpaceTypeStr) /\r
+ sizeof (mPciHostBridgeLibAcpiAddressSpaceTypeStr[0])\r
+ )\r
+ );\r
+ DEBUG ((EFI_D_ERROR, " %s: Length/Alignment = 0x%lx / 0x%lx\n",\r
+ mPciHostBridgeLibAcpiAddressSpaceTypeStr[Descriptor->ResType],\r
+ Descriptor->AddrLen, Descriptor->AddrRangeMax\r
+ ));\r
+ if (Descriptor->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM) {\r
+ DEBUG ((EFI_D_ERROR, " Granularity/SpecificFlag = %ld / %02x%s\n",\r
+ Descriptor->AddrSpaceGranularity, Descriptor->SpecificFlag,\r
+ ((Descriptor->SpecificFlag &\r
+ EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE\r
+ ) != 0) ? L" (Prefetchable)" : L""\r
+ ));\r
+ }\r
+ }\r
+ //\r
+ // Skip the END descriptor for root bridge\r
+ //\r
+ ASSERT (Descriptor->Desc == ACPI_END_TAG_DESCRIPTOR);\r
+ Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *)(\r
+ (EFI_ACPI_END_TAG_DESCRIPTOR *)Descriptor + 1\r
+ );\r
+ }\r
+}\r