--- /dev/null
+/**@file\r
+ Hardware info library with types and accessors to parse information about\r
+ PCI host bridges.\r
+\r
+ Copyright 2021 - 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include <Library/DebugLib.h>\r
+\r
+#include "HardwareInfoPciHostBridgeLib.h"\r
+\r
+#define IS_RANGE_INVALID(Start, Size, MaxValue) (Start >= MaxValue || Size == 0)\r
+\r
+/**\r
+ Calculate the last (inclusive) address of a range.\r
+\r
+ @param[in] Start First address of the range\r
+ @param[in] Size Size of the range\r
+ @return Last address of the range\r
+**/\r
+STATIC\r
+UINT64\r
+GetRangeEnd (\r
+ IN UINT64 Start,\r
+ IN UINT64 Size,\r
+ IN UINT64 MaxValue\r
+ )\r
+{\r
+ if (IS_RANGE_INVALID (Start, Size, MaxValue)) {\r
+ return 0;\r
+ }\r
+\r
+ return Start + Size - 1;\r
+}\r
+\r
+/**\r
+ Internal helper to update LastAddress if the Limit address\r
+ of the Mem aperture is higher than the provided value.\r
+\r
+ @param[in] Mem Pointer to aperture whose limit is\r
+ to be compared against accumulative\r
+ last address.\r
+ @param[out] LastAddress Pointer to accumulative last address\r
+ to be updated if Limit is higher\r
+**/\r
+STATIC\r
+VOID\r
+UpdateLastAddressIfHigher (\r
+ IN PCI_ROOT_BRIDGE_APERTURE *Mem,\r
+ OUT UINT64 *LastAddress\r
+ )\r
+{\r
+ if (Mem->Limit > *LastAddress) {\r
+ *LastAddress = Mem->Limit;\r
+ }\r
+}\r
+\r
+EFI_STATUS\r
+HardwareInfoPciHostBridgeLastMmioAddress (\r
+ IN CONST HOST_BRIDGE_INFO *HostBridge,\r
+ IN UINTN DataSize,\r
+ IN BOOLEAN HighMem,\r
+ OUT UINT64 *LastMmioAddress\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ PCI_ROOT_BRIDGE_APERTURE Mem;\r
+ PCI_ROOT_BRIDGE_APERTURE MemAbove4G;\r
+ PCI_ROOT_BRIDGE_APERTURE PMem;\r
+ PCI_ROOT_BRIDGE_APERTURE PMemAbove4G;\r
+\r
+ if (LastMmioAddress == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Set the output to the lowest possible value so that if some step fails\r
+ // the overall outcome reflects no information found\r
+ //\r
+ *LastMmioAddress = 0;\r
+\r
+ Status = HardwareInfoPciHostBridgeGetApertures (\r
+ HostBridge,\r
+ DataSize,\r
+ NULL,\r
+ &Mem,\r
+ &MemAbove4G,\r
+ &PMem,\r
+ &PMemAbove4G,\r
+ NULL\r
+ );\r
+\r
+ //\r
+ // Forward error to caller but ignore warnings given that, very likely,\r
+ // the host bridge will have a PIO aperture we are explicitly\r
+ // ignoring here since we care only about MMIO resources.\r
+ //\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (HighMem) {\r
+ UpdateLastAddressIfHigher (&MemAbove4G, LastMmioAddress);\r
+ UpdateLastAddressIfHigher (&PMemAbove4G, LastMmioAddress);\r
+ } else {\r
+ UpdateLastAddressIfHigher (&Mem, LastMmioAddress);\r
+ UpdateLastAddressIfHigher (&PMem, LastMmioAddress);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Set the boundaries of an aperture to invalid values having\r
+ size zero and start MaxValue (yields Start > Limit which\r
+ depicts an invalid range)\r
+\r
+ @param[in] MaxValue Max value of the aperture's range (depends\r
+ on the data type)\r
+ @param[out] Aperture Aperture object to invalidate\r
+**/\r
+STATIC\r
+VOID\r
+InvalidateRootBridgeAperture (\r
+ OUT PCI_ROOT_BRIDGE_APERTURE *Aperture\r
+ )\r
+{\r
+ if (Aperture == NULL) {\r
+ return;\r
+ }\r
+\r
+ Aperture->Base = MAX_UINT64;\r
+ Aperture->Limit = 0;\r
+}\r
+\r
+/**\r
+ Fill a PCI ROOT BRIDGE APERTURE with the proper values calculated\r
+ from the provided start and size.\r
+\r
+ @param[in] Start Start address of the aperture\r
+ @param[in] Size Size, in bytes, of the aperture\r
+ @param[in] MaxValue Max value a valid address could take and which\r
+ represents an invalid start address.\r
+ @param[out] Aperture Pointer to the aperture to be filled\r
+\r
+ @retval EFI_SUCCESS Aperture was filled successfully\r
+ @retval EFI_INVALID_PARAMETER Range depicted by Start and Size is\r
+ valid but ignored because aperture\r
+ pointer is NULL\r
+ @retval EFI_WARN_BUFFER_TOO_SMALL Aperture pointer is invalid but the\r
+ range also is so no harm.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+FillHostBridgeAperture (\r
+ IN UINT64 Start,\r
+ IN UINT64 Size,\r
+ IN UINT64 MaxValue,\r
+ OUT PCI_ROOT_BRIDGE_APERTURE *Aperture\r
+ )\r
+{\r
+ UINT64 End;\r
+\r
+ End = GetRangeEnd (Start, Size, MaxValue);\r
+\r
+ if (Aperture == NULL) {\r
+ if (!IS_RANGE_INVALID (Start, Size, MaxValue)) {\r
+ //\r
+ // Report an error to the caller since the range specified in\r
+ // the host bridge's resources is non-empty but the provided\r
+ // aperture pointer is null, thus the valid range is ignored.\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ return EFI_WARN_BUFFER_TOO_SMALL;\r
+ }\r
+\r
+ if (IS_RANGE_INVALID (Start, Size, MaxValue)) {\r
+ //\r
+ // Fill Aperture with invalid range values to signal the\r
+ // absence of an address space (empty range)\r
+ //\r
+ InvalidateRootBridgeAperture (Aperture);\r
+ } else {\r
+ Aperture->Base = Start;\r
+ Aperture->Limit = End;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Merge 2 ranges (normal and prefetchable) into a single aperture\r
+ comprehending the addresses encompassed by both of them. If both\r
+ ranges are not empty they must be contiguous for correctness.\r
+\r
+ @param[in] Start Range start address\r
+ @param[in] Size Range size in bytes\r
+ @param[in] PStart Prefetchable range start address\r
+ @param[in] PSize Prefetchable range size in bytes\r
+ @param[in] MaxValue Max value a valid address could take and which\r
+ represents an invalid start address.\r
+ @param[out] Aperture Pointer to the aperture to be filled\r
+\r
+ @retval EFI_SUCCESS Aperture was filled successfully\r
+ @retval EFI_INVALID_PARAMETER Either range depicted by Start, Size\r
+ or PStart, PSize or both are valid\r
+ but ignored because aperture pointer\r
+ is NULL\r
+ @retval EFI_WARN_BUFFER_TOO_SMALL Aperture pointer is invalid but both\r
+ ranges are too so no harm.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+MergeHostBridgeApertures (\r
+ IN UINT64 Start,\r
+ IN UINT64 Size,\r
+ IN UINT64 PStart,\r
+ IN UINT64 PSize,\r
+ IN UINT64 MaxValue,\r
+ OUT PCI_ROOT_BRIDGE_APERTURE *Aperture\r
+ )\r
+{\r
+ UINT64 PEnd;\r
+\r
+ if (Aperture == NULL) {\r
+ if (!IS_RANGE_INVALID (Start, Size, MaxValue) ||\r
+ !IS_RANGE_INVALID (PStart, PSize, MaxValue))\r
+ {\r
+ //\r
+ // Report an error to the caller since the range specified in\r
+ // the host bridge's resources is non-empty but the provided\r
+ // aperture pointer is null, thus the valid range is ignored.\r
+ //\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ return EFI_WARN_BUFFER_TOO_SMALL;\r
+ }\r
+\r
+ //\r
+ // Start from an empty range (Limit < Base)\r
+ //\r
+ InvalidateRootBridgeAperture (Aperture);\r
+\r
+ if (!IS_RANGE_INVALID (Start, Size, MaxValue)) {\r
+ Aperture->Base = Start;\r
+ Aperture->Limit = Start + Size - 1;\r
+ }\r
+\r
+ if (!IS_RANGE_INVALID (PStart, PSize, MaxValue)) {\r
+ PEnd = PStart + PSize - 1;\r
+\r
+ if (PStart < Aperture->Base) {\r
+ Aperture->Base = PStart;\r
+ }\r
+\r
+ if (PEnd > Aperture->Limit) {\r
+ Aperture->Limit = PEnd;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+HardwareInfoPciHostBridgeGetBusNrRange (\r
+ IN CONST HOST_BRIDGE_INFO *HostBridge,\r
+ IN UINTN DataSize,\r
+ OUT UINTN *BusNrStart,\r
+ OUT UINTN *BusNrLast\r
+ )\r
+{\r
+ if ((HostBridge == NULL) || (DataSize == 0) ||\r
+ (BusNrStart == NULL) || (BusNrLast == NULL))\r
+ {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // For now only version 0 is supported\r
+ //\r
+ if (HostBridge->Version != 0) {\r
+ return EFI_INCOMPATIBLE_VERSION;\r
+ }\r
+\r
+ *BusNrStart = HostBridge->BusNrStart;\r
+ *BusNrLast = HostBridge->BusNrLast;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+HardwareInfoPciHostBridgeGetApertures (\r
+ IN CONST HOST_BRIDGE_INFO *HostBridge,\r
+ IN UINTN DataSize,\r
+ OUT PCI_ROOT_BRIDGE_APERTURE *Io,\r
+ OUT PCI_ROOT_BRIDGE_APERTURE *Mem,\r
+ OUT PCI_ROOT_BRIDGE_APERTURE *MemAbove4G,\r
+ OUT PCI_ROOT_BRIDGE_APERTURE *PMem,\r
+ OUT PCI_ROOT_BRIDGE_APERTURE *PMemAbove4G,\r
+ OUT PCI_ROOT_BRIDGE_APERTURE *PcieConfig\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ BOOLEAN StickyError;\r
+\r
+ StickyError = FALSE;\r
+ if ((HostBridge == NULL) || (DataSize == 0)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // For now only version 0 is supported\r
+ //\r
+ if (HostBridge->Version != 0) {\r
+ return EFI_INCOMPATIBLE_VERSION;\r
+ }\r
+\r
+ Status = FillHostBridgeAperture (\r
+ HostBridge->IoStart,\r
+ HostBridge->IoSize,\r
+ MAX_UINT32,\r
+ Io\r
+ );\r
+ StickyError |= EFI_ERROR (Status);\r
+\r
+ Status = FillHostBridgeAperture (\r
+ HostBridge->PcieConfigStart,\r
+ HostBridge->PcieConfigSize,\r
+ MAX_UINT64,\r
+ PcieConfig\r
+ );\r
+ StickyError |= EFI_ERROR (Status);\r
+\r
+ if (HostBridge->Flags.Bits.CombineMemPMem) {\r
+ Status = MergeHostBridgeApertures (\r
+ HostBridge->MemStart,\r
+ HostBridge->MemSize,\r
+ HostBridge->PMemStart,\r
+ HostBridge->PMemSize,\r
+ MAX_UINT32,\r
+ Mem\r
+ );\r
+ StickyError |= EFI_ERROR (Status);\r
+\r
+ Status = MergeHostBridgeApertures (\r
+ HostBridge->MemAbove4GStart,\r
+ HostBridge->MemAbove4GSize,\r
+ HostBridge->PMemAbove4GStart,\r
+ HostBridge->PMemAbove4GSize,\r
+ MAX_UINT64,\r
+ MemAbove4G\r
+ );\r
+ StickyError |= EFI_ERROR (Status);\r
+\r
+ //\r
+ // Invalidate unused apertures\r
+ //\r
+ InvalidateRootBridgeAperture (PMem);\r
+ InvalidateRootBridgeAperture (PMemAbove4G);\r
+ } else {\r
+ Status = FillHostBridgeAperture (\r
+ HostBridge->MemStart,\r
+ HostBridge->MemSize,\r
+ MAX_UINT32,\r
+ Mem\r
+ );\r
+ StickyError |= EFI_ERROR (Status);\r
+\r
+ Status = FillHostBridgeAperture (\r
+ HostBridge->PMemStart,\r
+ HostBridge->PMemSize,\r
+ MAX_UINT32,\r
+ PMem\r
+ );\r
+ StickyError |= EFI_ERROR (Status);\r
+\r
+ Status = FillHostBridgeAperture (\r
+ HostBridge->MemAbove4GStart,\r
+ HostBridge->MemAbove4GSize,\r
+ MAX_UINT64,\r
+ MemAbove4G\r
+ );\r
+ StickyError |= EFI_ERROR (Status);\r
+\r
+ Status = FillHostBridgeAperture (\r
+ HostBridge->PMemAbove4GStart,\r
+ HostBridge->PMemAbove4GSize,\r
+ MAX_UINT64,\r
+ PMem\r
+ );\r
+ StickyError |= EFI_ERROR (Status);\r
+ }\r
+\r
+ if (StickyError) {\r
+ //\r
+ // If any function returned an error it is due to a valid range\r
+ // specified in the host bridge that was ignored due to a NULL\r
+ // pointer. Translate it to a warning to allow for calling with\r
+ // only a subset of the apertures.\r
+ //\r
+ return EFI_WARN_STALE_DATA;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+HardwareInfoPciHostBridgeGetFlags (\r
+ IN CONST HOST_BRIDGE_INFO *HostBridge,\r
+ IN UINTN DataSize,\r
+ OUT UINT64 *Attributes OPTIONAL,\r
+ OUT BOOLEAN *DmaAbove4G OPTIONAL,\r
+ OUT BOOLEAN *NoExtendedConfigSpace OPTIONAL,\r
+ OUT BOOLEAN *CombineMemPMem OPTIONAL\r
+ )\r
+{\r
+ if ((HostBridge == NULL) || (DataSize == 0)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // For now only version 0 is supported\r
+ //\r
+ if (HostBridge->Version != 0) {\r
+ return EFI_INCOMPATIBLE_VERSION;\r
+ }\r
+\r
+ if (Attributes) {\r
+ *Attributes = HostBridge->Attributes;\r
+ }\r
+\r
+ if (DmaAbove4G) {\r
+ *DmaAbove4G = !!HostBridge->Flags.Bits.DmaAbove4G;\r
+ }\r
+\r
+ if (NoExtendedConfigSpace) {\r
+ *NoExtendedConfigSpace = !!HostBridge->Flags.Bits.NoExtendedConfigSpace;\r
+ }\r
+\r
+ if (CombineMemPMem) {\r
+ *CombineMemPMem = !!HostBridge->Flags.Bits.CombineMemPMem;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+HardwareInfoPciHostBridgeGet (\r
+ IN CONST HOST_BRIDGE_INFO *HostBridge,\r
+ IN UINTN DataSize,\r
+ OUT UINTN *BusNrStart,\r
+ OUT UINTN *BusNrLast,\r
+ OUT UINT64 *Attributes OPTIONAL,\r
+ OUT BOOLEAN *DmaAbove4G OPTIONAL,\r
+ OUT BOOLEAN *NoExtendedConfigSpace OPTIONAL,\r
+ OUT BOOLEAN *CombineMemPMem OPTIONAL,\r
+ OUT PCI_ROOT_BRIDGE_APERTURE *Io OPTIONAL,\r
+ OUT PCI_ROOT_BRIDGE_APERTURE *Mem OPTIONAL,\r
+ OUT PCI_ROOT_BRIDGE_APERTURE *MemAbove4G OPTIONAL,\r
+ OUT PCI_ROOT_BRIDGE_APERTURE *PMem OPTIONAL,\r
+ OUT PCI_ROOT_BRIDGE_APERTURE *PMemAbove4G OPTIONAL,\r
+ OUT PCI_ROOT_BRIDGE_APERTURE *PcieConfig OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ Status = HardwareInfoPciHostBridgeGetBusNrRange (\r
+ HostBridge,\r
+ DataSize,\r
+ BusNrStart,\r
+ BusNrLast\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = HardwareInfoPciHostBridgeGetFlags (\r
+ HostBridge,\r
+ DataSize,\r
+ Attributes,\r
+ DmaAbove4G,\r
+ NoExtendedConfigSpace,\r
+ CombineMemPMem\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = HardwareInfoPciHostBridgeGetApertures (\r
+ HostBridge,\r
+ DataSize,\r
+ Io,\r
+ Mem,\r
+ MemAbove4G,\r
+ PMem,\r
+ PMemAbove4G,\r
+ PcieConfig\r
+ );\r
+\r
+ return Status;\r
+}\r