]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/Library/HardwareInfoLib/HardwareInfoPciHostBridgeLib.c
OvmfPkg/Library: Create base HardwareInfoLib for PCI Host Bridges
[mirror_edk2.git] / OvmfPkg / Library / HardwareInfoLib / HardwareInfoPciHostBridgeLib.c
diff --git a/OvmfPkg/Library/HardwareInfoLib/HardwareInfoPciHostBridgeLib.c b/OvmfPkg/Library/HardwareInfoLib/HardwareInfoPciHostBridgeLib.c
new file mode 100644 (file)
index 0000000..26cd435
--- /dev/null
@@ -0,0 +1,508 @@
+/**@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