// Implementation\r
//\r
\r
+STATIC\r
+VOID\r
+SetLinuxPciProbeOnlyProperty (\r
+ IN FDT_CLIENT_PROTOCOL *FdtClient\r
+ )\r
+{\r
+ INT32 Node;\r
+ UINT32 Tmp;\r
+ EFI_STATUS Status;\r
+\r
+ if (!FeaturePcdGet (PcdPureAcpiBoot)) {\r
+ //\r
+ // Set the /chosen/linux,pci-probe-only property to 1, so that the PCI\r
+ // setup we will perform in the firmware is honored by the Linux OS,\r
+ // rather than torn down and done from scratch. This is generally a more\r
+ // sensible approach, and aligns with what ACPI based OSes do typically.\r
+ //\r
+ // In case we are exposing an emulated VGA PCI device to the guest, which\r
+ // may subsequently get exposed via the Graphics Output protocol and\r
+ // driven as an efifb by Linux, we need this setting to prevent the\r
+ // framebuffer from becoming unresponsive.\r
+ //\r
+ Status = FdtClient->GetOrInsertChosenNode (FdtClient, &Node);\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ Tmp = SwapBytes32 (1);\r
+ Status = FdtClient->SetNodeProperty (FdtClient, Node,\r
+ "linux,pci-probe-only", &Tmp, sizeof (Tmp));\r
+ }\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_WARN,\r
+ "Failed to set /chosen/linux,pci-probe-only property\n"));\r
+ }\r
+ }\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 *IoTranslation,\r
+ OUT UINT64 *MmioBase,\r
+ OUT UINT64 *MmioSize,\r
+ OUT UINT64 *MmioTranslation,\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
+\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
+ *IoTranslation = 0;\r
+ *MmioBase = 0;\r
+ *MmioTranslation = 0;\r
+ *BusMin = 0;\r
+ *BusMax = 0;\r
+\r
+ //\r
+ // *IoSize and *MmioSize are initialized to zero because the logic below\r
+ // requires it. However, since they are also affected by the issue reported\r
+ // above, they are initialized early.\r
+ //\r
+ *IoSize = 0;\r
+ *MmioSize = 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
+ 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
+ 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
+ SetLinuxPciProbeOnlyProperty (FdtClient);\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)@0x%Lx\n", __FUNCTION__, ConfigBase,\r
+ ConfigSize, *BusMin, *BusMax, *IoBase, *IoSize, *IoTranslation, *MmioBase,\r
+ *MmioSize, *MmioTranslation));\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
/**\r
Entry point of this driver\r
\r
UINTN Loop2;\r
PCI_HOST_BRIDGE_INSTANCE *HostBridge;\r
PCI_ROOT_BRIDGE_INSTANCE *PrivateData;\r
+ UINT64 IoBase, IoSize, IoTranslation;\r
+ UINT64 MmioBase, MmioSize, MmioTranslation;\r
+ UINT32 BusMin, BusMax;\r
\r
if (PcdGet64 (PcdPciExpressBaseAddress) == 0) {\r
DEBUG ((EFI_D_INFO, "%a: PCI host bridge not present\n", __FUNCTION__));\r
return EFI_ABORTED;\r
}\r
\r
+ Status = ProcessPciHost (&IoBase, &IoSize, &IoTranslation, &MmioBase,\r
+ &MmioSize, &MmioTranslation, &BusMin, &BusMax);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
mDriverImageHandle = ImageHandle;\r
\r
- mResAperture[0][0].BusBase = PcdGet32 (PcdPciBusMin);\r
- mResAperture[0][0].BusLimit = PcdGet32 (PcdPciBusMax);\r
+ mResAperture[0][0].BusBase = BusMin;\r
+ mResAperture[0][0].BusLimit = BusMax;\r
\r
- mResAperture[0][0].MemBase = PcdGet32 (PcdPciMmio32Base);\r
- mResAperture[0][0].MemLimit = (UINT64)PcdGet32 (PcdPciMmio32Base) +\r
- PcdGet32 (PcdPciMmio32Size) - 1;\r
+ mResAperture[0][0].MemBase = MmioBase;\r
+ mResAperture[0][0].MemLimit = MmioBase + MmioSize - 1;\r
\r
- mResAperture[0][0].IoBase = PcdGet64 (PcdPciIoBase);\r
- mResAperture[0][0].IoLimit = PcdGet64 (PcdPciIoBase) +\r
- PcdGet64 (PcdPciIoSize) - 1;\r
- mResAperture[0][0].IoTranslation = PcdGet64 (PcdPciIoTranslation);\r
+ mResAperture[0][0].IoBase = IoBase;\r
+ mResAperture[0][0].IoLimit = IoBase + IoSize - 1;\r
+ mResAperture[0][0].IoTranslation = IoTranslation;\r
\r
//\r
// Add IO and MMIO memory space, so that resources can be allocated in the\r
//\r
Status = gDS->AddIoSpace (\r
EfiGcdIoTypeIo,\r
- PcdGet64 (PcdPciIoBase),\r
- PcdGet64 (PcdPciIoSize)\r
+ IoBase,\r
+ IoSize\r
);\r
ASSERT_EFI_ERROR (Status);\r
\r
\r
Status = gDS->AddMemorySpace (\r
EfiGcdMemoryTypeMemoryMappedIo,\r
- PcdGet32 (PcdPciMmio32Base),\r
- PcdGet32 (PcdPciMmio32Size),\r
+ MmioBase,\r
+ MmioSize,\r
MmioAttributes\r
);\r
if (EFI_ERROR (Status)) {\r
}\r
\r
Status = gDS->SetMemorySpaceAttributes (\r
- PcdGet32 (PcdPciMmio32Base),\r
- PcdGet32 (PcdPciMmio32Size),\r
+ MmioBase,\r
+ MmioSize,\r
MmioAttributes\r
);\r
if (EFI_ERROR (Status)) {\r