alder_lake_bios/Insyde/InsydeModulePkg/Universal/CommonPolicy/AspmOverrideDxe/AspmOverrideDxe.c

649 lines
19 KiB
C

/** @file
A DXE driver for override the device ASPM setting.
;******************************************************************************
;* Copyright (c) 2016 - 2019, Insyde Software Corp. All Rights Reserved.
;*
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
;* transmit, broadcast, present, recite, release, license or otherwise exploit
;* any part of this publication in any form, by any means, without the prior
;* written permission of Insyde Software Corporation.
;*
;******************************************************************************
*/
#include "AspmOverrideDxe.h"
#include <Library/PciExpressLib.h>
#include <Library/SmmServicesTableLib.h>
#include <Library/MemoryAllocationLib.h>
PCIE_BRIDGE_MAP_TABLE *mPcieBridgeMap; //To record each bridge's info. of current platform
PCIE_DEVICE_MAP_TABLE *mPcieDeviceMap; //To record each Dev's info. under specific pcie bridge of current platform
EFI_OVERRIDE_ASPM_PROTOCOL mOverrideAspmProtocol;
EFI_SMM_SYSTEM_TABLE2 *mSmst;
/**
A DXE driver for override the device ASPM setting.
@param ImageHandle Handle for this drivers loaded image protocol.
@param SystemTable EFI system table
@retval EFI_SUCCESS The driver installed without error.
**/
EFI_STATUS
EFIAPI
AspmOverrideEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_HANDLE Handle;
BOOLEAN InSmm;
EFI_SMM_SW_DISPATCH2_PROTOCOL *SwDispatch;
EFI_SMM_SW_REGISTER_CONTEXT SwContext;
EFI_HANDLE SwHandle;
EFI_EVENT ReadyToBootEvent;
EFI_SMM_BASE2_PROTOCOL *SmmBase;
//
// SMM check
//
Status = gBS->LocateProtocol (
&gEfiSmmBase2ProtocolGuid,
NULL,
(VOID **)&SmmBase
);
if (!EFI_ERROR (Status)) {
SmmBase->InSmm (SmmBase, &InSmm);
} else {
InSmm = FALSE;
}
if (!InSmm) {
//
// Regest event to override ASPM register before boot
//
Status = EfiCreateEventReadyToBootEx (
(TPL_CALLBACK - 1),
AspmOverrideReadyToBootCallBack,
NULL,
&ReadyToBootEvent
);
ASSERT_EFI_ERROR (Status);
if (EFI_ERROR(Status)) {
return Status;
}
} else { //In SMM
//
// Get Smm Syatem Table
//
Status = SmmBase->GetSmstLocation(
SmmBase,
&mSmst
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Install OverrideAspmProtocol to provide OverrideAspmFunction in SMM
//
mOverrideAspmProtocol.OverrideAspmFunc = OverrideAspmFunction;
Handle = NULL;
Status = mSmst->SmmInstallProtocolInterface (
&Handle,
&gEfiOverrideAspmProtocolGuid,
EFI_NATIVE_INTERFACE,
&mOverrideAspmProtocol
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Install OverrideAspmProtocol for ECP SMM drivers.
// Should be removed when no more ECP.
//
EcpAspmOverrideSupported();
//
// Locate the SMM SW dispatch protocol to register a SMI callback function to execute ASPM override
//
Status = mSmst->SmmLocateProtocol (
&gEfiSmmSwDispatch2ProtocolGuid,
NULL,
(VOID **)&SwDispatch
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "Locate the SMM SW dispatch protocol(%r)\n", Status));
return Status;
}
SwContext.SwSmiInputValue = SW_SMI_ASPM_OVERRIDE;
Status = SwDispatch->Register (
SwDispatch,
SmmOverrideAspmFunction,
&SwContext,
&SwHandle
);
if (EFI_ERROR (Status)) {
return Status;
}
}
return Status;
}
/**
A call back function to trigger a SMI to execute ASPM override when signal a ReadyToBoot event.
@param Event Event instance (unused)
@param Context Event Context (It is not used)
@retval void
**/
VOID
EFIAPI
AspmOverrideReadyToBootCallBack (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
EFI_SMM_CONTROL2_PROTOCOL *SmmControl;
UINT8 SmiDataValue;
Status = gBS->LocateProtocol (
&gEfiSmmControl2ProtocolGuid,
NULL,
(VOID **)&SmmControl
);
if (EFI_ERROR (Status)) {
return;
}
SmiDataValue = SW_SMI_ASPM_OVERRIDE;
SmmControl->Trigger(
SmmControl,
&SmiDataValue,
NULL,
0,
0
);
gBS->CloseEvent (Event);
}
/**
A callback function to call OverrideAspmFunction() to execute ASPM setting modification.
@param DispatchHandle Unused
@param Context Unused
@param CommBuffer Unused
@param CommBufferSize Unused
@retval EFI_SUCCESS This function execute successfully
**/
EFI_STATUS
EFIAPI
SmmOverrideAspmFunction (
IN EFI_HANDLE DispatchHandle,
IN CONST VOID *Context,
IN OUT VOID *CommBuffer,
IN OUT UINTN *CommBufferSize
)
{
OverrideAspmFunction ();
return EFI_SUCCESS;
}
/**
This function get the project ASPM setting and collect PCIE bridge and device information to set the ASPM register.
@retval EFI_SUCCESS This function override ASPM setting successfully.
@retval EFI_UNSUPPORTED No any PCIE bridge be found, so it is unsupported.
**/
EFI_STATUS
OverrideAspmFunction (
VOID
)
{
EFI_STATUS Status;
PCIE_ASPM_DEV_INFO *PcieAspmDevs;
PcieAspmDevs = PcdGetPtr(PcdPcieAspmDevs);
Status = EFI_SUCCESS;
//
// OemServices
//
DEBUG_OEM_SVC ((DEBUG_INFO, "OemKernelServices Call: OemSvcGetAspmOverrideInfo \n"));
Status = OemSvcGetAspmOverrideInfo (
&PcieAspmDevs
);
DEBUG_OEM_SVC ((DEBUG_INFO, "OemKernelServices OemSvcGetAspmOverrideInfo Status %r\n", Status));
if (!EFI_ERROR (Status)) {
return EFI_SUCCESS;
}
if (PcieAspmDevs[0].VendorId == PCIE_ASPM_DEV_END_OF_TABLE) {
return EFI_SUCCESS;
}
//
// Collect all pcie bridges and pcie devices info. on current platform.
//
if (!CreatPCIeBridgeDeviceMap ()) {
//
// Can't find any pcie bridge, don't need to executive overridden.
//
return EFI_NOT_FOUND;
}
Status = OverrideDevice (PcieAspmDevs);
return Status;
}
/**
A function to search the ASPM register location (offset) by using the capability register
to trace and identify if the capability ID match 0x10.
@param Bus The PCIE bus number of the searching device
@param Dev The PCIE device number of the searching device
@param Fun The PCIE function number of the searching device
@retval 0 No PCIExpress capability register
@retval Register The PCIExpress capability register offset of given PCIE device
**/
UINT16
FindLinkCtrlOffset (
IN UINT8 Bus,
IN UINT8 Dev,
IN UINT8 Fun
)
{
UINT16 Reg;
UINT16 Cap;
UINT16 Data16;
Data16 = PciExpressRead16 (PCI_EXPRESS_LIB_ADDRESS (Bus, Dev, Fun, PCI_CAPBILITY_POINTER_OFFSET));
if (((Data16 & 0x00FF) == 0x00) || ((Data16 & 0x00FF) == 0xFF)) {
return 0x00;
} else {
Data16 &= 0x00FF;
Data16 <<= 8;
}
do {
Reg = (Data16 >> 8);
Data16 = PciExpressRead16 (PCI_EXPRESS_LIB_ADDRESS (Bus, Dev, Fun, Reg));
Cap = (Data16 & 0x00FF);
} while ((Cap != EFI_PCI_CAPABILITY_ID_PCIEXP) && ( (Data16 >> 8) != 0x00));
if (Cap != EFI_PCI_CAPABILITY_ID_PCIEXP) {
return 0x00;
} else {
return (Reg + LINK_CONTROL_REG);
}
}
/**
A function to override the ASPM register of searched PCIE bridge and device according OemServices setting.
@param PcieAspmDevs A data pointer to a group data will be used to modify device ASPM setting
@retval EFI_SUCCESS To override ASPM setting is successful.
**/
EFI_STATUS
OverrideDevice (
IN PCIE_ASPM_DEV_INFO *PcieAspmDevs
)
{
EFI_STATUS Status;
UINT8 BusIndex;
UINT8 DevIndex;
UINT8 Index;
UINT8 BridgeIndex;
UINT8 RootDev;
UINT8 RootFunc;
UINT8 RootASPM;
UINT8 DeviceBus;
UINT8 DeviceDev;
UINT8 DeviceASPM;
UINT16 LinkCtrlOffset;
UINT8 PcieDeviceIndex;
UINT8 AspmMask = 0xFC;
RootDev = 0;
RootFunc = 0;
Status = EFI_SUCCESS;
for (BusIndex = 0; mPcieDeviceMap[BusIndex].Bus != 0xFF; BusIndex++) {
for (DevIndex = 0; mPcieDeviceMap[BusIndex].PcieDev[DevIndex].Dev != 0xFF; DevIndex++) {
Index = 0;
while (PcieAspmDevs[Index].VendorId != PCIE_ASPM_DEV_END_OF_TABLE) {
if ((PcieAspmDevs[Index].VendorId == mPcieDeviceMap[BusIndex].PcieDev[DevIndex].VenderId) &&
(PcieAspmDevs[Index].DeviceId == mPcieDeviceMap[BusIndex].PcieDev[DevIndex].DeviceId) &&
((PcieAspmDevs[Index].RevId == 0xFF) ||
(PcieAspmDevs[Index].RevId == mPcieDeviceMap[BusIndex].PcieDev[DevIndex].RevId))) {
DeviceBus = mPcieDeviceMap[BusIndex].Bus;
DeviceDev = mPcieDeviceMap[BusIndex].PcieDev[DevIndex].Dev;
for (BridgeIndex = 0; mPcieBridgeMap[BridgeIndex].Dev != 0xFF; BridgeIndex++) {
if (mPcieBridgeMap[BridgeIndex].SecondaryBusNum == DeviceBus){
RootDev = mPcieBridgeMap[BridgeIndex].Dev;
RootFunc = mPcieBridgeMap[BridgeIndex].Func;
break;
}
}
RootASPM = PcieAspmDevs[Index].RootAspm;
DeviceASPM = PcieAspmDevs[Index].EndpointAspm;
//
// Step01. Search Link Ctrl. if "not found" then "ignor"
//
LinkCtrlOffset = FindLinkCtrlOffset (0, RootDev, RootFunc);
if ( LinkCtrlOffset == 0x00) {
continue;
}
//
// Step02. Override Link Ctrl of Bridge
//
PciExpressAndThenOr8 (
PCI_EXPRESS_LIB_ADDRESS (0, RootDev, RootFunc, LinkCtrlOffset),
AspmMask,
RootASPM
);
//
//Step03. Override Link Ctrl of devices under this bridge
//
for (PcieDeviceIndex = 0; mPcieDeviceMap[BusIndex].PcieDev[PcieDeviceIndex].Dev != 0xFF; PcieDeviceIndex++) {
LinkCtrlOffset = FindLinkCtrlOffset (
mPcieDeviceMap[BusIndex].Bus,
mPcieDeviceMap[BusIndex].PcieDev[PcieDeviceIndex].Dev,
mPcieDeviceMap[BusIndex].PcieDev[PcieDeviceIndex].Fnc
);
if ( LinkCtrlOffset == 0x00) {
continue;
}
PciExpressAndThenOr8 (
PCI_EXPRESS_LIB_ADDRESS (
mPcieDeviceMap[BusIndex].Bus,
mPcieDeviceMap[BusIndex].PcieDev[PcieDeviceIndex].Dev,
mPcieDeviceMap[BusIndex].PcieDev[PcieDeviceIndex].Fnc,
LinkCtrlOffset),
AspmMask,
DeviceASPM
);
}
break;
}
Index++;
}
}
}
mSmst->SmmFreePool (mPcieBridgeMap);
mPcieBridgeMap = NULL;
mSmst->SmmFreePool (mPcieDeviceMap);
mPcieDeviceMap = NULL;
return Status;
}
/**
A function to search and record all PCIE bridge, and scan all PCIE devices under these bridge.
@param VOID
@retval TRUE PCIE bridge exist.
@retval FALSE No PCIE bridge be found.
**/
BOOLEAN
CreatPCIeBridgeDeviceMap (
VOID
)
{
BOOLEAN BridgeExist;
BridgeExist = FALSE; //Assume that all bridges are disabled
if (SearchPCIeBridge ()) { //Scan Pcie bridge
SearchPCIeDevice (); //If "found pcie bridge" then "scan pcie devices under these bridge"
BridgeExist = TRUE; //At least one bridge is enabled
}
return BridgeExist;
}
/**
A function to search all PCIE bridge exist or not and record existent bridge in mPcieBridgeMap.
@param VOID
@retval TRUE PCIE bridge exist.
@retval FALSE No PCIE bridge be found.
**/
BOOLEAN
SearchPCIeBridge (
VOID
)
{
UINT8 DeviceIndex;
UINT8 FunctionIndex;
UINT8 MaxFunctionNum;
UINT32 RegData;
UINT8 HeaderType;
UINT8 SecondaryBusNum;
UINT8 TotalBridges;
TotalBridges = 0;
for (DeviceIndex = 0x00; DeviceIndex <= PCI_MAX_DEVICE; DeviceIndex++) { //0x1f: Max Pcie bridge count on BUS0
RegData = PciExpressRead32 (PCI_EXPRESS_LIB_ADDRESS (0, DeviceIndex, 0, PCI_VENDOR_ID_OFFSET));
if (RegData == 0xFFFFFFFF) {
continue;
}
//
// Check Multi-Function
//
HeaderType = PciExpressRead8 (PCI_EXPRESS_LIB_ADDRESS (0, DeviceIndex, 0, PCI_HEADER_TYPE_OFFSET));
MaxFunctionNum = ((HeaderType & HEADER_TYPE_MULTI_FUNCTION) == 0) ? 0 : PCI_MAX_FUNC;
for (FunctionIndex = 0; FunctionIndex <= MaxFunctionNum; FunctionIndex++) {
RegData = PciExpressRead32 (PCI_EXPRESS_LIB_ADDRESS (0, DeviceIndex, FunctionIndex, PCI_REVISION_ID_OFFSET));
//
// Class code: 060400 & 060401 => PCI to PCI bridge
//
if ((RegData & 0xFFFFFF00) != 0x06040000 && (RegData & 0xFFFFFF00) != 0x06040100) {
continue;
}
//
// if FindLinkCtrlOffset != 0x00 => Belong to PCIE (only check belong to PCIE)
//
if (FindLinkCtrlOffset (0, DeviceIndex, FunctionIndex) == 0x00) {
continue;
}
mPcieBridgeMap = ReallocatePool (
(TotalBridges + 1) * sizeof (PCIE_BRIDGE_MAP_TABLE),
(TotalBridges + 2) * sizeof (PCIE_BRIDGE_MAP_TABLE),
mPcieBridgeMap
);
if (mPcieBridgeMap == NULL) {
return FALSE;
}
SecondaryBusNum = PciExpressRead8 (PCI_EXPRESS_LIB_ADDRESS (0, DeviceIndex, FunctionIndex, PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET));
mPcieBridgeMap[TotalBridges].Dev = DeviceIndex;
mPcieBridgeMap[TotalBridges].Func = FunctionIndex;
mPcieBridgeMap[TotalBridges].SecondaryBusNum = SecondaryBusNum;
TotalBridges++;
}
}
if (TotalBridges == 0) {
return FALSE;
}
mPcieBridgeMap[TotalBridges].Dev = 0xFF;
mPcieBridgeMap[TotalBridges].Func = 0xFF;
mPcieBridgeMap[TotalBridges].SecondaryBusNum = 0xFF;
//
// Since bridge exist, allocate space for the mPcieDeviceMap to record each Dev's info under each pcie bridge.
//
mPcieDeviceMap = AllocatePool ((TotalBridges + 1) * sizeof (PCIE_DEVICE_MAP_TABLE));
if (mPcieDeviceMap == NULL) {
FreePool (mPcieBridgeMap);
mPcieBridgeMap = NULL;
return FALSE;
}
return TRUE;
}
/**
A function to search all PCIE device exist or not under mPcieBridgeMap and record existent device in mPcieDeviceMap..
@param VOID
@retval VOID
**/
VOID
SearchPCIeDevice (
VOID
)
{
UINT8 ArrayIndex;
UINT32 PcieDeviceIndex;
UINT8 DevIndex;
UINT8 FncIndex;
UINT32 Data32;
UINT32 Data8;
UINT16 VenderId, DeviceId;
UINT8 RevId;
//
//Step01. Scan Pcie devices by mPcieBridgeMap specified.
// And then put the result into mPcieDeviceMap.
ArrayIndex = 0x00;
while (mPcieBridgeMap[ArrayIndex].Dev != 0xFF) {
mPcieDeviceMap[ArrayIndex].Bus = mPcieBridgeMap[ArrayIndex].SecondaryBusNum;
PcieDeviceIndex = 0x0000;
for (DevIndex= 0; DevIndex <= PCI_MAX_DEVICE; DevIndex++) {
VenderId = 0x0000;
DeviceId = 0x0000;
Data32 = PciExpressRead32 (
PCI_EXPRESS_LIB_ADDRESS (
mPcieBridgeMap[ArrayIndex].SecondaryBusNum,
DevIndex,
0,
PCI_VENDOR_ID_OFFSET
)
);
if (Data32 == 0xFFFFFFFF) {
continue;
}
Data8 = PciExpressRead8 (PCI_EXPRESS_LIB_ADDRESS (mPcieBridgeMap[ArrayIndex].SecondaryBusNum, DevIndex, 0, PCI_HEADER_TYPE_OFFSET));
if ( (Data8 & HEADER_TYPE_MULTI_FUNCTION) != 0x00) { //Multi function
for (FncIndex = 0; FncIndex <= PCI_MAX_FUNC; FncIndex++) {
Data32 = PciExpressRead32 (
PCI_EXPRESS_LIB_ADDRESS (
mPcieBridgeMap[ArrayIndex].SecondaryBusNum,
DevIndex,
FncIndex,
PCI_VENDOR_ID_OFFSET
)
);
if (Data32 == 0xFFFFFFFF) {
continue;
}
VenderId = (UINT16)Data32;
DeviceId = (UINT16)(Data32 >> 16);
RevId = PciExpressRead8 (PCI_EXPRESS_LIB_ADDRESS (mPcieBridgeMap[ArrayIndex].SecondaryBusNum, DevIndex, FncIndex, PCI_REVISION_ID_OFFSET));
mPcieDeviceMap[ArrayIndex].PcieDev[PcieDeviceIndex].Dev = DevIndex;
mPcieDeviceMap[ArrayIndex].PcieDev[PcieDeviceIndex].Fnc = FncIndex;
mPcieDeviceMap[ArrayIndex].PcieDev[PcieDeviceIndex].VenderId = VenderId;
mPcieDeviceMap[ArrayIndex].PcieDev[PcieDeviceIndex].DeviceId = DeviceId;
mPcieDeviceMap[ArrayIndex].PcieDev[PcieDeviceIndex].RevId = RevId;
PcieDeviceIndex++;
}
} else {
FncIndex = 0x00;
VenderId = (UINT16)Data32;
DeviceId = (UINT16)(Data32 >> 16);
RevId = PciExpressRead8 (PCI_EXPRESS_LIB_ADDRESS (mPcieBridgeMap[ArrayIndex].SecondaryBusNum, DevIndex, 0x00, PCI_REVISION_ID_OFFSET));
mPcieDeviceMap[ArrayIndex].PcieDev[PcieDeviceIndex].Dev = DevIndex;
mPcieDeviceMap[ArrayIndex].PcieDev[PcieDeviceIndex].Fnc = FncIndex;
mPcieDeviceMap[ArrayIndex].PcieDev[PcieDeviceIndex].VenderId = VenderId;
mPcieDeviceMap[ArrayIndex].PcieDev[PcieDeviceIndex].DeviceId = DeviceId;
mPcieDeviceMap[ArrayIndex].PcieDev[PcieDeviceIndex].RevId = RevId;
PcieDeviceIndex++;
}
}
mPcieDeviceMap[ArrayIndex].PcieDev[PcieDeviceIndex].Dev = 0xFF;
mPcieDeviceMap[ArrayIndex].PcieDev[PcieDeviceIndex].Fnc = 0xFF;
mPcieDeviceMap[ArrayIndex].PcieDev[PcieDeviceIndex].VenderId = 0xFFFF;
mPcieDeviceMap[ArrayIndex].PcieDev[PcieDeviceIndex].DeviceId = 0xFFFF;
mPcieDeviceMap[ArrayIndex].PcieDev[PcieDeviceIndex].RevId = 0xFF;
ArrayIndex++;
}
mPcieDeviceMap[ArrayIndex].Bus = 0xFF;
return;
}
/**
Support the Ecp driver.
@param PrivateData The pointer to SMM_OEM_SERVICES_INSTANCE.
@retval EFI_SUCCESS Install SmmOemServices Protocol on gBS and SmmRuntimeProtocol successfully.
@retval other Some error occurred when installing SmmOemServices Protocol.
**/
EFI_STATUS
EcpAspmOverrideSupported (
VOID
)
{
EFI_STATUS Status;
EFI_HANDLE Handle;
Handle = NULL;
Status = gSmst->SmmInstallProtocolInterface (
&Handle,
&gEfiOverrideAspmProtocolGuid,
EFI_NATIVE_INTERFACE,
&mOverrideAspmProtocol
);
if (EFI_ERROR(Status)) {
return Status;
}
return Status;
}