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