mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-29 12:35:53 +01:00
509 lines
14 KiB
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;
|
|
}
|