CloverBootloader/OvmfPkg/Library/HardwareInfoLib/HardwareInfoPciHostBridgeLib.c

509 lines
14 KiB
C

/**@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;
}