/** @file
@copyright
Copyright 1999 - 2021 Intel Corporation.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include "PciHostBridge.h"
#include "PciRootBridge.h"
#include "PciRebalance.h"
extern EFI_IIO_UDS_PROTOCOL *mIioUds;
/**
Return TRUE if the specified socket/stack combination exists,
otherwise return FALSE
@param Socket - the socket to be checked
@param Stack - the stack of the socket to be checked
@retval TRUE - the socket/stack combination exists
@retval FALSE - the socket/stack combination does not exist
*/
STATIC BOOLEAN
IsStackPresent (
UINT8 Socket,
UINT8 Stack
)
{
BOOLEAN Result;
UINT64 Mask;
ASSERT (Socket < ARRAY_SIZE (mIioUds->IioUdsPtr->PlatformData.CpuQpiInfo)); // simple overrun check
if (Socket >= ARRAY_SIZE (mIioUds->IioUdsPtr->PlatformData.CpuQpiInfo)) {
Result = FALSE;
goto err_exit;
}
//
// if the StackPresentBitmap is a single byte, then we can track 8 stacks,
// the sizeof will tell us how many bytes we have, we scale by 8 to
// determine the maximum number of stacks we can track. Stacks larger
// than this are not present essentially by definition, but could also
// be a sign that we need a wider type to store the information; hence we
// assert
//
ASSERT (Stack < 8 * sizeof(mIioUds->IioUdsPtr->PlatformData.CpuQpiInfo[0].stackPresentBitmap));
if (Stack >= 8 * sizeof(mIioUds->IioUdsPtr->PlatformData.CpuQpiInfo[0].stackPresentBitmap)) {
Result = FALSE;
goto err_exit;
}
Mask = 1;
Mask <<= Stack;
Result = (Mask & mIioUds->IioUdsPtr->PlatformData.CpuQpiInfo[Socket].stackPresentBitmap) != 0;
err_exit:
return Result;
}
/**
Adjust resource assignment among sockets to fit the IO
resources from the PCI() devices in the system
@param SocketResources - CPU_RESOURCE structure pointer that stores all resources need per socket
@param ResourceType - type of resource that requires alignment
@param ValidSockets - Number of Valid Sockets, need it
to calculate how resources need to
be split
@retval EFI_SUCCESS - Succeed.
@retval EFI_OUT_OF_RESOURCES - Not enough resources to be adjusted within the socket.
**/
EFI_STATUS
AdjustSocketIo (
CPU_RESOURCE *SocketResources,
UINT8 ResourceType,
UINT8 ValidSockets
)
{
UINT64 Base; ///< Next base I/O port number to use
UINT64 Limit; ///< Most recent limit for I/O port numbers
CONST UINT64 MaxLimit = ((UINT64)1 << 16) - 1; ///< Maximum value for limit; used to ensure we don't overflow
CPU_RESOURCE *CurSocketResources; ///< Pointer to the CPU_RESOURE structure for the CPU we
///< are examining
STACK_RESOURCE *CurStackResources; ///< Pointer to the STACK_RESOURCE structure for the CPU/Stack
///< we are examining
UINT16 NumFreePorts; ///< Number of i/o ports allocated to sockets that are not used
UINT8 LastStack; ///< Last enabled stack of the last enabled socket
CONST UINT8 LastSocketIndex = ValidSockets - 1; ///< Index of the last socket
UINT64 TotalResourceSize;
UINT64 ResourceSize;
UINT8 Socket; ///< Loop variable used to iterate over the sockets
UINT8 Stack; ///< Loop variable used to iterate over the stacks of a given socket
NumFreePorts = 0;
for (Socket = 0; Socket < ValidSockets; Socket++) {
CurSocketResources = &SocketResources[Socket];
if (CurSocketResources->IoResourceNeeds == 0 && CurSocketResources->IoResourcesLeft != 0) {
ASSERT (NumFreePorts < NumFreePorts + CurSocketResources->IoResourcesLeft + 1); // check for overflow
NumFreePorts += CurSocketResources->IoResourcesLeft + 1;
CurSocketResources->IoResourcesLeft = 0;
}
}
for (Socket = 0; Socket < ValidSockets; Socket++) {
CurSocketResources = &SocketResources[Socket];
if (CurSocketResources->IoResourceNeeds != 0 && NumFreePorts >= CurSocketResources->IoResourceNeeds) {
ASSERT (NumFreePorts > NumFreePorts - CurSocketResources->IoResourceNeeds); // check for underflow
NumFreePorts -= CurSocketResources->IoResourceNeeds;
CurSocketResources->IoResourceNeeds = 0;
}
}
LastStack = LastStackOfSocket (LastSocketIndex);
if (NumFreePorts > 0) {
CurStackResources = &SocketResources[LastSocketIndex].StackRes[LastStack];
if (CurStackResources->NumIoPortsDesired != 0) {
CurStackResources->NumIoPortsDesired += NumFreePorts;
} else {
CurStackResources->NumIoPortsDesired += NumFreePorts - 1;
}
}
//
// Verify all resource requested can fit into the systems address range.
//
TotalResourceSize = 0;
for (Socket = 0; Socket < ValidSockets; Socket ++) {
LastStackWithResources (&SocketResources[Socket], Socket, ResourceType, &LastStack, &ResourceSize);
TotalResourceSize += ResourceSize;
}
DEBUG ((DEBUG_INFO, "Total Request IO Range = %xh\n", TotalResourceSize));
DEBUG ((DEBUG_INFO, "Total System IO Range = %xh\n", MaxLimit));
if (TotalResourceSize > MaxLimit) {
//
// Not enough system resources to support the request.
// Remove all request to update NVRAM variable for this resource type.
//
for (Socket = 0; Socket < ValidSockets; Socket ++) {
for (Stack = 0; Stack < MAX_IIO_STACK; Stack ++) {
if (!(mIioUds->IioUdsPtr->PlatformData.CpuQpiInfo[Socket].stackPresentBitmap & (1 << Stack))) {
continue;
}
SocketResources[Socket].StackRes[Stack].NeedIoUpdate = 0;
}
}
DEBUG ((DEBUG_ERROR, "ERROR: Out of adjustable IO resources. Can't adjust across sockets\n"));
return EFI_OUT_OF_RESOURCES;
}
DEBUG((DEBUG_ERROR, "Assigning new socket i/o range...\n"));
Base = mIioUds->IioUdsPtr->PlatformData.IIO_resource[0].PciResourceIoBase;
Limit = Base; // assume no resources are allocated
for (Socket = 0, CurSocketResources = SocketResources; Socket < ValidSockets; Socket++, CurSocketResources++) {
DEBUG ((DEBUG_INFO, "socket = %d, Base = %x, Limit =%x, MaxLimit = %x\n", Socket, Base, Limit, MaxLimit));
ASSERT (Base < MaxLimit);
CurSocketResources->IoBase = (UINT16)Base;
DEBUG ((DEBUG_INFO, "set socket io base to %x\n", Base));
for (Stack = 0, CurStackResources = CurSocketResources->StackRes;
Stack < MAX_IIO_STACK;
Stack++, CurStackResources++) {
if (!IsStackPresent (Socket, Stack)) {
DEBUG ((DEBUG_INFO, " Stack %d not present, setting base/limit to 0xffff/0\n", Stack));
CurStackResources->IoBase = 0xffff;
CurStackResources->IoLimit = 0;
continue;
}
if (CurStackResources->NumIoPortsDesired == 0) {
DEBUG ((DEBUG_INFO, " Stack %d doesn't need i/o resources, setting base/limit to 0xffff/0\n", Stack));
CurStackResources->IoBase = 0xffff;
CurStackResources->IoLimit = 0;
CurStackResources->NeedIoUpdate = TRUE;
continue;
}
DEBUG((DEBUG_INFO, " Stack %d setting i/o base to %x, ports desired was %x\n",
Stack, Base, CurStackResources->NumIoPortsDesired));
ASSERT (Base < MaxLimit);
CurStackResources->IoBase = (UINT16)Base;
Limit = Base + CurStackResources->NumIoPortsDesired;
DEBUG ((DEBUG_INFO, " limit set to %x (var and stack)\n", Limit));
ASSERT (Base <= Limit);
ASSERT (Limit <= MaxLimit);
CurStackResources->IoLimit = (UINT16)Limit;
CurStackResources->NeedIoUpdate = TRUE;
Base = Limit + 1;
DEBUG ((DEBUG_INFO, " Base variable updated to %x\n", Base));
}
ASSERT (Limit <= MaxLimit);
DEBUG ((DEBUG_INFO, " Socket %d limit set to %x\n", Socket, Limit));
CurSocketResources->IoLimit = (UINT16)Limit;
}
DEBUG ((DEBUG_INFO, "Dumping new I/O requests\n"));
for (Socket = 0, CurSocketResources = SocketResources; Socket < ValidSockets; Socket++, CurSocketResources++) {
DEBUG((DEBUG_INFO, "socket %d %x/%x\n", Socket, CurSocketResources->IoBase, CurSocketResources->IoLimit));
for (Stack = 0, CurStackResources = CurSocketResources->StackRes;
Stack < MAX_IIO_STACK;
Stack++, CurStackResources++) {
DEBUG ((DEBUG_INFO, "%d/%d: %x/%x\n", Socket, Stack, CurStackResources->IoBase, CurStackResources->IoLimit));
}
}
return EFI_SUCCESS;
}