alder_lake_bios/Insyde/InsydeModulePkg/Universal/CommonPolicy/LegacyBiosPlatformDxe/GetRoutingTable.c

645 lines
20 KiB
C

/** @file
The EFI Legacy BIOS Patform Protocol member function
;******************************************************************************
;* Copyright (c) 2012 - 2020, 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 "LegacyBiosPlatformDxe.h"
#include <Library/KernelConfigLib.h>
#include <Protocol/LegacyInterrupt.h>
#include <Library/DxeChipsetSvcLib.h>
#include <Library/PciSegmentLib.h>
//
// PIRQ_A, PIRQ_B....PIRQ_H, total 8 PIRQs
//
#define MaxPIRQIndex 8
#define ErrorSlotNum 0xffff
#define PIRQ_DATA_NOT_SET(a) ((a == 0) || (a == 0x1F))
#define MULTILAYER_VIRTUAL_BUS 0x80
#define MAX_PCI_SLOT_NUM 8
BOOLEAN mGetRoutingInfo = FALSE;
IRQ_ROUTING_TABLE *mIrqRoutingTableInfoPtr;
LEGACY_MODIFY_PIR_TABLE *mVirtualBusTablePtr;
UINT8 mVirtualBusTableEntryNumber;
UINT8 *mPirqPriorityTablePtr;
UINT8 mPirqPriorityTableEntryNumber;
EFI_LEGACY_IRQ_PRIORITY_TABLE_ENTRY *mIrqPoolTablePtr;
UINT8 mIrqPoolTableNumber;
static
EFI_STATUS
CheckIRQ (
);
static
EFI_STATUS
ModifyIrqTable (
);
static
UINTN
SlotNumToPIrq (
IN UINTN SlotNum
);
static
VOID
MarkIrqAsLegacyUsed (
IN UINT8 IrqNumber
);
/**
Returns information associated with PCI IRQ routing.
This function returns the following information associated with PCI IRQ routing:
* An IRQ routing table and number of entries in the table.
* The $PIR table and its size.
* A list of PCI IRQs and the priority order to assign them.
@param This The protocol instance pointer.
@param RoutingTable The pointer to PCI IRQ Routing table.
This location is the $PIR table minus the header.
@param RoutingTableEntries The number of entries in table.
@param LocalPirqTable $PIR table.
@param PirqTableSize $PIR table size.
@param LocalIrqPriorityTable A list of interrupts in priority order to assign.
@param IrqPriorityTableEntries The number of entries in the priority table.
@retval EFI_SUCCESS Data was successfully returned.
**/
EFI_STATUS
EFIAPI
GetRoutingTable (
IN EFI_LEGACY_BIOS_PLATFORM_PROTOCOL *This,
OUT VOID **RoutingTable,
OUT UINTN *RoutingTableEntries,
OUT VOID **LocalPirqTable, OPTIONAL
OUT UINTN *PirqTableSize, OPTIONAL
OUT VOID **LocalIrqPriorityTable, OPTIONAL
OUT UINTN *IrqPriorityTableEntries OPTIONAL
)
{
UINT16 PTableSize;
UINT32 Index;
UINT8 Bus;
UINT8 Device;
UINT8 Function;
UINT8 Checksum;
UINT8 *Ptr;
EFI_STATUS Status;
EFI_LEGACY_INTERRUPT_PROTOCOL *LegacyInterrupt;
Checksum = 0;
if (!mGetRoutingInfo) {
Status = ModifyIrqTable ();
if (Status != EFI_SUCCESS) {
return EFI_UNSUPPORTED;
}
}
if (LocalPirqTable != NULL) {
PTableSize = sizeof (EFI_LEGACY_PIRQ_TABLE_HEADER) +
sizeof (EFI_LEGACY_IRQ_ROUTING_ENTRY) *
(UINT16)mIrqRoutingTableInfoPtr->MaxRoutingTableCount;
Status = gBS->LocateProtocol (
&gEfiLegacyInterruptProtocolGuid,
NULL,
(VOID **)&LegacyInterrupt
);
ASSERT_EFI_ERROR (Status);
LegacyInterrupt->GetLocation (
LegacyInterrupt,
&Bus,
&Device,
&Function
);
//
// Update fields in $PIR table header
//
mIrqRoutingTableInfoPtr->IrqRoutingTableHeaderPtr->TableSize = PTableSize;
mIrqRoutingTableInfoPtr->IrqRoutingTableHeaderPtr->Bus = Bus;
mIrqRoutingTableInfoPtr->IrqRoutingTableHeaderPtr->DevFun = (UINT8) ((Device << 3) + Function);
Ptr = (UINT8 *) (mIrqRoutingTableInfoPtr->IrqRoutingTableHeaderPtr);
for (Index = 0; Index < PTableSize; Index++) {
Checksum = (UINT8) (Checksum + (UINT8) *Ptr);
Ptr += 1;
}
Checksum = (UINT8) (0x00 - Checksum);
mIrqRoutingTableInfoPtr->IrqRoutingTableHeaderPtr->Checksum = Checksum;
*LocalPirqTable = (VOID *) (mIrqRoutingTableInfoPtr->IrqRoutingTableHeaderPtr);
*PirqTableSize = PTableSize;
}
*RoutingTable = mIrqRoutingTableInfoPtr->IrqRoutingTablePtr;
*RoutingTableEntries = mIrqRoutingTableInfoPtr->MaxRoutingTableCount;
if (LocalIrqPriorityTable != NULL) {
*LocalIrqPriorityTable = mIrqPoolTablePtr ;
*IrqPriorityTableEntries = mIrqPoolTableNumber;
}
return EFI_SUCCESS;
}
/**
Translates the given PIRQ accounting for bridge.
This function translates the given PIRQ back through all buses, if required,
and returns the true PIRQ and associated IRQ.
@param This The protocol instance pointer.
@param PciBus The PCI bus number for this device.
@param PciDevice The PCI device number for this device.
@param PciFunction The PCI function number for this device.
@param Pirq Input is PIRQ reported by device, and output is true PIRQ.
@param PciIrq The IRQ already assigned to the PIRQ, or the IRQ to be
assigned to the PIRQ.
@retval EFI_SUCCESS The PIRQ was translated.
**/
EFI_STATUS
EFIAPI
TranslatePirq (
IN EFI_LEGACY_BIOS_PLATFORM_PROTOCOL *This,
IN UINTN PciBus,
IN UINTN PciDevice,
IN UINTN PciFunction,
IN OUT UINT8 *Pirq,
OUT UINT8 *PciIrq
)
{
EFI_LEGACY_INTERRUPT_PROTOCOL *LegacyInterrupt;
EFI_STATUS Status;
UINTN Index;
UINTN Index1;
UINT8 LocalPirq;
UINT8 PirqData;
UINTN MaxIrqEntriesCount;
EFI_LEGACY_IRQ_ROUTING_ENTRY *IrqEntryTablePtr;
UINT8 *PirqLinkValuePtr;
UINT8 PirqIndex;
if (!mGetRoutingInfo) {
Status = ModifyIrqTable ();
if (Status != EFI_SUCCESS) {
return EFI_UNSUPPORTED;
}
}
Status = gBS->LocateProtocol (
&gEfiLegacyInterruptProtocolGuid,
NULL,
(VOID **)&LegacyInterrupt
);
ASSERT_EFI_ERROR (Status);
MaxIrqEntriesCount = mIrqRoutingTableInfoPtr->MaxRoutingTableCount;
IrqEntryTablePtr = mIrqRoutingTableInfoPtr->IrqRoutingTablePtr;
PirqLinkValuePtr = mIrqRoutingTableInfoPtr->PirqLinkValuePtr;
LocalPirq = (UINT8) (*Pirq);
Status = EFI_NOT_FOUND;
for (Index = 0; Index < MaxIrqEntriesCount; Index++) {
if ((IrqEntryTablePtr[Index].Bus == PciBus) &&
(IrqEntryTablePtr[Index].Device == PciDevice)
) {
for (PirqIndex = 0; PirqIndex < MaxPIRQIndex; PirqIndex++) {
if (IrqEntryTablePtr[Index].PirqEntry[LocalPirq].Pirq == PirqLinkValuePtr[PirqIndex]) {
LocalPirq = PirqIndex;
break;
}
}
LegacyInterrupt->ReadPirq (LegacyInterrupt, LocalPirq, &PirqData);
//
// In order to make the following code more readable, macros are used
//
#define CURRENT_IRQ (mPirqPriorityTablePtr[LocalPirq])
#define CHECKED_IRQ (mIrqPoolTablePtr [Index1].Irq)
#define CHECKED_IRQ_USED (mIrqPoolTablePtr [Index1].Used)
//
// Try to select a IRQ (1~15) for the PIRQ
//
//
// Different platforms has different default value of PirqData.
// So, PIRQ_DATA_NOT_SET should be defined to be the default value returned from ReadPirq in platform layer.
//
while (PIRQ_DATA_NOT_SET (PirqData)) { // Loop until PirqData is choosed
if (CURRENT_IRQ != 0) { // This might be the IRQ we could choose
for (Index1 = 0; Index1 < mIrqPoolTableNumber; Index1++) {
//
// This loop is to make sure the selected CURRENT_IRQ existed in IrqPriorityTable[]
//
if (CURRENT_IRQ == CHECKED_IRQ) {
break;
}
if (CHECKED_IRQ == 0) {
CHECKED_IRQ = CURRENT_IRQ;
break;
}
}
if (Index1 < mIrqPoolTableNumber) {
if (CHECKED_IRQ_USED == LEGACY_USED) {
//
// This IRQ is already used by legacy devices
// Clean the priority IRQ
//
CURRENT_IRQ = 0;
continue;
}
PirqData = CURRENT_IRQ;
CHECKED_IRQ_USED = PCI_USED;
}
} else {
//
// Select a IRQ from IrqPriorityTable[]
//
for (Index1 = 0; Index1 < mIrqPoolTableNumber; Index1++) {
if ((CHECKED_IRQ != 0) && (CHECKED_IRQ_USED == PCI_UNUSED)) {
PirqData = CHECKED_IRQ;
CHECKED_IRQ_USED = PCI_USED;
break;
}
}
}
if (PIRQ_DATA_NOT_SET (PirqData)) {
//
// No unused interrpts, so start reusing them by clear CHECKED_IRQ_USED
//
for (Index1 = 0; Index1 < mIrqPoolTableNumber; Index1++) {
if (CHECKED_IRQ_USED == PCI_USED) {
CHECKED_IRQ_USED = PCI_UNUSED;
}
}
}
}
*PciIrq = PirqData;
*Pirq = LocalPirq;
//
// It is supposed there is only one bus/dev defined in the routing table
//
Status = EFI_SUCCESS;
break;
}
}
return Status;
}
/**
Modifies the IrqRoutingEntry to reflect correct bus.
**/
static
EFI_STATUS
ModifyIrqTable (
)
{
UINT64 Address;
EFI_LEGACY_IRQ_ROUTING_ENTRY *IrqRoutingPtr;
UINTN Index;
UINTN Index1;
UINT8 TempData;
UINTN IndexTempEntry, TempEntrySize;
EFI_LEGACY_IRQ_ROUTING_ENTRY *TempRoutingEntry;
UINT8 ReScan = FALSE;
if ((mIrqRoutingTableInfoPtr == NULL) || (mVirtualBusTablePtr == NULL) || (mVirtualBusTableEntryNumber == 0)){
return EFI_UNSUPPORTED;
}
IrqRoutingPtr = mIrqRoutingTableInfoPtr->IrqRoutingTablePtr;
//
// Firstly, changing the multilayer virtual bus number in the table - mVirtualBusTablePtr
//
for (Index1 = 0; Index1 < mVirtualBusTableEntryNumber; Index1++) {
//
// Find an entry with real BridgeBus number
//
if (ReScan == TRUE) {
ReScan = FALSE;
Index1 = 0;
}
if ((mVirtualBusTablePtr[Index1].VirtualSecondaryBus & MULTILAYER_VIRTUAL_BUS) != 0) {
continue;
}
Address = PCI_SEGMENT_LIB_ADDRESS (
0,
mVirtualBusTablePtr[Index1].BridgeBus,
mVirtualBusTablePtr[Index1].BridgeDevice,
mVirtualBusTablePtr[Index1].BridgeFunction,
PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET
);
TempData = PciSegmentRead8 (Address);
if (TempData == 0xFF) {
continue;
}
for (Index = 0; Index < mVirtualBusTableEntryNumber; Index++) {
//
// Check the multilayer virtual bus entry and update it if the real bus number is found
//
if ((mVirtualBusTablePtr[Index].VirtualSecondaryBus & MULTILAYER_VIRTUAL_BUS) == 0) {
continue;
}
if (mVirtualBusTablePtr[Index].BridgeBus != mVirtualBusTablePtr[Index1].VirtualSecondaryBus) {
continue;
}
mVirtualBusTablePtr[Index].VirtualSecondaryBus &= (0xFF - MULTILAYER_VIRTUAL_BUS);
mVirtualBusTablePtr[Index].BridgeBus = TempData;
//
// Rescan to avoid order dependence.
//
ReScan = TRUE;
break;
}
}
for (Index1 = 0; Index1 < mVirtualBusTableEntryNumber; Index1++) {
Address = PCI_SEGMENT_LIB_ADDRESS (
0,
mVirtualBusTablePtr[Index1].BridgeBus,
mVirtualBusTablePtr[Index1].BridgeDevice,
mVirtualBusTablePtr[Index1].BridgeFunction,
PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET
);
TempData = PciSegmentRead8 (Address);
for (Index = 0; Index < mIrqRoutingTableInfoPtr->MaxRoutingTableCount; Index++) {
//
// Verify virtual device, if it's bridge existed -> modify it's virtual bus number to real number (PIRQ Table)
//
if ((IrqRoutingPtr[Index].Bus == mVirtualBusTablePtr[Index1].VirtualSecondaryBus ) &&
(IrqRoutingPtr[Index].Reserved == 0xFF)) {
if (TempData != 0xFF) {
//
// Bridge exist, modify the virtual bus to a real one.
//
IrqRoutingPtr[Index].Bus = TempData; // real bus number come from it's bridge register: "Secondary Bus"
IrqRoutingPtr[Index].Reserved = 0x00; // this virtual device has been processed done.
} else {
//
// Bridge not exist, mark it and remove it from PIRQ Table later.
//
IrqRoutingPtr[Index].Reserved = 0xDD;
}
}
}
}
for (Index = 0; Index < mIrqRoutingTableInfoPtr->MaxRoutingTableCount; Index++) {
if (IrqRoutingPtr[Index].Reserved == 0xFF) {
DEBUG ((DEBUG_INFO, "IRQ Routing Table error\n"));
}
}
//
//Set priority IRQ routing for PCI slots
//
CheckIRQ();
//
// Remove nonexistent virtual device from mIrqRoutingHeader
//
TempEntrySize = mIrqRoutingTableInfoPtr->MaxRoutingTableCount * sizeof (EFI_LEGACY_IRQ_ROUTING_ENTRY);
TempRoutingEntry = AllocateZeroPool (TempEntrySize);
if (TempRoutingEntry == NULL) {
ASSERT (TempRoutingEntry != NULL);
return EFI_OUT_OF_RESOURCES;
}
for (Index = IndexTempEntry = 0; Index < mIrqRoutingTableInfoPtr->MaxRoutingTableCount; Index++) {
if (IrqRoutingPtr[Index].Reserved == 0x00) {
TempRoutingEntry[IndexTempEntry] = IrqRoutingPtr[Index];
IndexTempEntry++;
}
}
CopyMem ((VOID *) (UINTN)IrqRoutingPtr, TempRoutingEntry, TempEntrySize); // Cover whole original RountingEntry Table
mIrqRoutingTableInfoPtr->MaxRoutingTableCount = IndexTempEntry;
FreePool (TempRoutingEntry);
//
// Set the flag to indicate the initialization finished
//
mGetRoutingInfo = TRUE;
return EFI_SUCCESS;
}
/**
Check the IRQ setting of Pci slot in "Setup" Value, and record it.
**/
static
EFI_STATUS
CheckIRQ (
)
{
EFI_STATUS Status;
KERNEL_CONFIGURATION KernelConfiguration;
UINTN SlotNum;
//
// Parallel, Serial A, Serial B, IRQ setting
//
UINT8 IsaIRQ[2] = {0, 0};
UINTN Index;
UINT8 PciSlot[MAX_PCI_SLOT_NUM];
Status = GetKernelConfiguration (&KernelConfiguration);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Check Serial Port, IR, Parallel Port IRQ Setting PIrqPriority table
//
if (KernelConfiguration.ComPortA) {
IsaIRQ[0] = KernelConfiguration.ComPortAInterrupt;
MarkIrqAsLegacyUsed (KernelConfiguration.ComPortAInterrupt);
}
if (KernelConfiguration.ComPortB) {
IsaIRQ[1] = KernelConfiguration.ComPortBInterrupt;
MarkIrqAsLegacyUsed (KernelConfiguration.ComPortBInterrupt);
}
if (KernelConfiguration.Lpt1) {
MarkIrqAsLegacyUsed (KernelConfiguration.Lpt1Interrupt);
}
if (KernelConfiguration.Lpt2) {
MarkIrqAsLegacyUsed (KernelConfiguration.Lpt2Interrupt);
}
//
// Record the PCI slot IRQ priority in
//
CopyMem (PciSlot, &KernelConfiguration.PciSlot1, sizeof(PciSlot));
for (SlotNum = 0; SlotNum < MAX_PCI_SLOT_NUM; SlotNum++) {
if ((PciSlot[SlotNum] == 0) || (PciSlot[SlotNum] == IsaIRQ[0]) || (PciSlot[SlotNum] == IsaIRQ[1])) {
//
// No IRQ Priority setting or Used by Isa devices
//
continue;
}
//
// Check if IRQ be other PCI slot used
//
for (Index = 0; Index < MaxPIRQIndex; Index++) {
if (PciSlot[SlotNum] == mPirqPriorityTablePtr[Index]) {
break;
}
}
if (Index < MaxPIRQIndex) {
continue;
}
//
// change slot number to PIRQ
//
Index = SlotNumToPIrq (SlotNum);
if (Index == ErrorSlotNum) {
//
//This Pci Slot does not have Devices.
//
continue;
}
//
// Record PCI slot PIRQ Priority setting
//
mPirqPriorityTablePtr[Index] = PciSlot[SlotNum];
}
return EFI_SUCCESS;
}
/**
Check the InterruptPin of Pci slot
@param SlotNum which slot
@retval ErrorSlotNum No this device or error
@retval others InterruptPin (0=PIRQ_A, 1=PIRQ_B......)
**/
static
UINTN
SlotNumToPIrq (
IN UINTN SlotNum
)
{
UINT8 Buffer;
UINT64 Address;
PCI_TYPE00 PciConfigHeader;
UINT8 PirqIndex;
EFI_LEGACY_IRQ_ROUTING_ENTRY *RoutingTable;
UINTN RoutingTableIndex;
RoutingTableIndex = 0;
RoutingTable = mIrqRoutingTableInfoPtr->IrqRoutingTablePtr;
//
// Go through Routing Table and find out PCI slot IRQ routing Entry.
//
while (RoutingTableIndex < mIrqRoutingTableInfoPtr->MaxRoutingTableCount) {
if (RoutingTable[RoutingTableIndex].Slot == SlotNum ) {
break;
}
RoutingTableIndex++;
}
Address = PCI_SEGMENT_LIB_ADDRESS (
0,
mIrqRoutingTableInfoPtr->IrqRoutingTablePtr[RoutingTableIndex].Bus,
((mIrqRoutingTableInfoPtr->IrqRoutingTablePtr[RoutingTableIndex].Device >> 3) & 0x1f),
0,
0
);
PciSegmentReadBuffer (Address, sizeof (PciConfigHeader), &PciConfigHeader);
if (PciConfigHeader.Hdr.VendorId == 0xffff) {
return ErrorSlotNum;
}
Buffer = PciConfigHeader.Device.InterruptPin;
Buffer--;
for (PirqIndex = 0; PirqIndex < MaxPIRQIndex; PirqIndex++) {
if (mIrqRoutingTableInfoPtr->IrqRoutingTablePtr[RoutingTableIndex].PirqEntry[Buffer].Pirq == mIrqRoutingTableInfoPtr->PirqLinkValuePtr[PirqIndex]) {
Buffer = PirqIndex;
break;
}
}
return Buffer;
}
/**
Mark the designate Irq as Legacy Used
@param IrqNumber The IRQ which been used as Legacy IRQ.
**/
static
VOID
MarkIrqAsLegacyUsed (
IN UINT8 IrqNumber
)
{
UINTN Index;
Index = 0;
for (Index = 0; Index < mIrqPoolTableNumber; Index++) {
if (mIrqPoolTablePtr [Index].Irq == IrqNumber) {
mIrqPoolTablePtr [Index].Used = LEGACY_USED;
break;
}
}
return ;
}
/**
Retrieve platform specific IRQ Routing Information from ChipsetSvc
and provide these information for GetRoutingTable () and TranslatePirq ().
@retval EFI_UNSUPPORTED Get Routing Information fail.
@retval EFI_SUCCESS Get Routing Information completely.
**/
EFI_STATUS
InitIrqRoutingInformation (
)
{
EFI_STATUS Status;
mVirtualBusTablePtr = NULL;
mVirtualBusTableEntryNumber = 0;
mIrqPoolTablePtr = NULL;
mIrqPoolTableNumber = 0;
mPirqPriorityTablePtr = NULL;
mPirqPriorityTableEntryNumber = 0;
mIrqRoutingTableInfoPtr = NULL;
Status = DxeCsSvcIrqRoutingInformation( &mVirtualBusTablePtr, &mVirtualBusTableEntryNumber,
&mIrqPoolTablePtr, &mIrqPoolTableNumber,
&mPirqPriorityTablePtr, &mPirqPriorityTableEntryNumber,
&mIrqRoutingTableInfoPtr
);
return Status;
}