/** @file
LegacyInterrupt.c - Purley/Lewisburg PCH Legacy Interrupt DXE driver.
Installs the EFI_LEGACY_INTERRUPT_PROTOCOL for managing legacy
PCI interrupt routing on Intel Purley (Lewisburg PCH) platforms.
Source: PurleySktPkg/SouthClusterLbg/LegacyInterrupt/Dxe/
Image: 0123_LegacyInterrupt.efi
*/
#include "LegacyInterrupt.h"
//
// GUID definitions used by this module
//
// 0x12c0 - Unknown vendor GUID
// 0x12d0 - gMmPciUsraProtocolGuid (from CpRcPkg/DxeMmPciBaseLib)
// 0x12e0 - gEfiPcdProtocolGuid (11B34006-D85B-4D0A-A290-D5A571310EF7)
// 0x12f0 - gEfiLegacyInterruptProtocolGuid (31CE593D-108A-485D-ADB2-78F21F2966BE)
// 0x1300 - gEfiHobListGuid (7739F24C-93D7-11D4-9A3A-0090273FC14D)
// 0x1310 - gEfiDxeServicesTableGuid (05AD34BA-6F02-4214-952E-4DA0398E2BB9)
//
// Function pointer table for LegacyInterrupt protocol
// (at offset 0x1320 in .data)
//
STATIC CONST LEGACY_INTERRUPT_PROTOCOL_INSTANCE mLegacyInterruptInterface = {
LegacyInterruptGetStatus, // offset 0x1320 -> 0x638
LegacyInterruptGetVector, // offset 0x1328 -> 0x640
LegacyInterruptReadVector, // offset 0x1330 -> 0x650
LegacyInterruptWriteVector // offset 0x1338 -> 0x680
};
//
// Global state variables
//
// 0x1340 - mPchStepping (UINT8, initially 3 = PCH_STEPPING_UNKNOWN)
// 0x1348 - Protocol image handle (initially NULL)
//
STATIC UINT8 mPchStepping = PCH_STEPPING_UNKNOWN;
#pragma section(".data", read, write)
//
// Library constructor - initializes UEFI boot/libraries
//
EFI_STATUS
EFIAPI
LibConstructor (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
// Save globals
gImageHandle = ImageHandle;
if (ImageHandle == NULL) {
DebugAssert (__FILE__, __LINE__, "gImageHandle != ((void *) 0)");
}
gSystemTable = SystemTable;
if (SystemTable == NULL) {
DebugAssert (__FILE__, __LINE__, "gST != ((void *) 0)");
}
gBootServices = SystemTable->BootServices;
if (gBootServices == NULL) {
DebugAssert (__FILE__, __LINE__, "gBS != ((void *) 0)");
}
gRuntimeServices = SystemTable->RuntimeServices;
if (gRuntimeServices == NULL) {
DebugAssert (__FILE__, __LINE__, "gRT != ((void *) 0)");
}
// Get DxeServicesTable from system configuration
Status = EfiGetSystemConfigurationTable (
&gEfiDxeServicesTableGuid,
(VOID **)&gDxeServicesTable
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
DebugAssert (__FILE__, __LINE__, "!EFI_ERROR (Status)");
}
if (gDxeServicesTable == NULL) {
DebugAssert (__FILE__, __LINE__, "gDS != ((void *) 0)");
}
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
DebugAssert (__FILE__, __LINE__, "!EFI_ERROR (Status)");
}
// Locate MmPciUsra protocol (for PCI config access via MMIO)
if (mPciUsra == NULL) {
Status = gBootServices->LocateProtocol (
&gMmPciUsraProtocolGuid,
NULL,
&mPciUsra
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
DebugAssert (__FILE__, __LINE__, "!EFI_ERROR (Status)");
}
if (mPciUsra == NULL) {
DebugAssert (__FILE__, __LINE__, "mPciUsra != ((void *) 0)");
}
}
// Get HOB list
GetHobList ();
// Get PCD protocol
mPcd = GetPcdProtocol ();
// Read PCD: PcdLegacyInterruptPchStepping (token=5)
return PcdGet8 (5);
}
//
// Install the LegacyInterrupt protocol
//
EFI_STATUS
LegacyInterruptInstall (
VOID
)
{
EFI_STATUS Status;
DEBUG ((DEBUG_INFO, "LegacyInterruptInstall() Start\n"));
// Check if protocol already installed
Status = gBootServices->LocateProtocol (
&gEfiLegacyInterruptProtocolGuid,
NULL,
(VOID **)&mLegacyInterruptInterface
);
if (!EFI_ERROR (Status)) {
DebugAssert (
__FILE__,
178,
"&gEfiLegacyInterruptProtocolGuid already installed in database"
);
}
// Install protocol interface
Status = gBootServices->InstallProtocolInterface (
&gImageHandle,
&gEfiLegacyInterruptProtocolGuid,
EFI_NATIVE_INTERFACE,
(VOID *)&mLegacyInterruptInterface
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
DebugAssert (__FILE__, 189, "!EFI_ERROR (Status)");
}
DEBUG ((DEBUG_INFO, "LegacyInterruptInstall() End\n"));
return Status;
}
//
// EFI_LEGACY_INTERRUPT_PROTOCOL.GetStatus
//
EFI_STATUS
EFIAPI
LegacyInterruptGetStatus (
IN EFI_LEGACY_INTERRUPT_PROTOCOL *This,
OUT UINT8 *Status
)
{
*Status = 8; // PIC mode, 8 IRQ lines active
return EFI_SUCCESS;
}
//
// EFI_LEGACY_INTERRUPT_PROTOCOL.GetVector
//
EFI_STATUS
EFIAPI
LegacyInterruptGetVector (
IN EFI_LEGACY_INTERRUPT_PROTOCOL *This,
OUT UINT8 *Vector,
OUT UINT8 *LevelMask,
OUT UINT8 *LevelSense
)
{
*Vector = 0; // Default IRQ0
*LevelMask = 31; // All IRQs level-triggered (bitmask: IRQ0-IRQ4)
*LevelSense = 0; // Active high
return EFI_SUCCESS;
}
//
// EFI_LEGACY_INTERRUPT_PROTOCOL.ReadVector
//
EFI_STATUS
EFIAPI
LegacyInterruptReadVector (
IN EFI_LEGACY_INTERRUPT_PROTOCOL *This,
IN UINT8 Vector,
OUT UINT8 *Value
)
{
if (Vector >= 8) {
return EFI_INVALID_PARAMETER;
}
// Read from LPC interrupt control register
*Value = MmioRead8 (LPC_ADDR (Vector + LEGACY_INTERRUPT_BASE_PORT)) & 0x7F;
return EFI_SUCCESS;
}
//
// EFI_LEGACY_INTERRUPT_PROTOCOL.WriteVector
//
EFI_STATUS
EFIAPI
LegacyInterruptWriteVector (
IN EFI_LEGACY_INTERRUPT_PROTOCOL *This,
IN UINT8 Vector,
IN UINT8 Value
)
{
if (Vector >= 8) {
return EFI_INVALID_PARAMETER;
}
LpcWriteRegister (
(UINT16)(Vector + LEGACY_INTERRUPT_BASE_PORT),
Value
);
return EFI_SUCCESS;
}
//
// Write to LPC register via MMIO
//
VOID
LpcWriteRegister (
UINT16 Offset,
UINT8 Value
)
{
// Call PCH detection (lazy init)
PchGetStepping ();
// Write MMIO register in LPC bridge space
MmioWrite8 (LPC_ADDR (Offset), Value);
}
//
// Get the DebugOutput protocol instance (lazy init)
//
VOID *
GetDebugOutputProtocol (
VOID
)
{
UINTN Pages;
EFI_STATUS Status;
if (mDebugOutputProtocol == NULL) {
Pages = gBootServices->GetMemoryMap (&Pages, NULL, NULL, NULL, NULL);
gBootServices->AllocatePool (EfiBootServicesData, Pages, &mMemoryMapBuffer);
if (Pages <= 16) {
Status = gBootServices->LocateProtocol (
&gEfiDebugOutputProtocolGuid,
NULL,
&mDebugOutputProtocol
);
if (EFI_ERROR (Status)) {
mDebugOutputProtocol = NULL;
}
return mDebugOutputProtocol;
}
return NULL;
}
return mDebugOutputProtocol;
}
//
// Debug print function with board/platform detection
//
UINT8
DebugPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
VA_LIST Marker;
EFI_DEBUG_OUTPUT_PROTOCOL *DebugProtocol;
UINT64 DebugMask;
UINT8 NmiValue;
UINT8 BoardType;
DebugProtocol = (EFI_DEBUG_OUTPUT_PROTOCOL *)GetDebugOutputProtocol ();
DebugMask = 0;
if (DebugProtocol != NULL) {
// Read CMOS status for board detection
NmiValue = IoRead8 (RTC_INDEX_PORT);
IoWrite8 (RTC_INDEX_PORT, (NmiValue & RTC_NMI_MASK) | RTC_INDEX_NMI_DISABLE);
BoardType = IoRead8 (RTC_DATA_PORT);
if (BoardType > 3) {
if (BoardType == 0) {
BoardType = (IoRead8 ((UINT8 *)0xFDAF0490) & 2) | 1;
}
}
if ((BoardType - 1) <= 0xFD) {
DebugMask = 0x80000000;
if (BoardType == 1) {
DebugMask = 0x80000004;
}
}
if ((DebugMask & ErrorLevel) != 0) {
VA_START (Marker, Format);
DebugProtocol->OutputString (ErrorLevel, Format, Marker);
VA_END (Marker);
}
}
}
//
// Debug assert handler
//
VOID
DebugAssert (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *Description
)
{
VOID *DebugProtocol;
DebugProtocol = GetDebugOutputProtocol ();
if (DebugProtocol != NULL) {
((EFI_DEBUG_OUTPUT_PROTOCOL *)DebugProtocol)->Assert (
FileName,
LineNumber,
Description
);
}
}
//
// Read 16-bit value from I/O port
//
UINT16
EFIAPI
IoRead16 (
IN UINT16 *Address
)
{
if (((UINTN)Address & 1) != 0) {
DebugAssert (__FILE__, 151, "(Address & 1) == 0");
}
return *Address;
}
//
// Get system configuration table by GUID
//
EFI_STATUS
EfiGetSystemConfigurationTable (
IN EFI_GUID *TableGuid,
OUT VOID **Table
)
{
UINTN Index;
UINTN NumberOfEntries;
EFI_CONFIGURATION_TABLE *ConfigurationTable;
if (TableGuid == NULL) {
DebugAssert (__FILE__, 97, "TableGuid != ((void *) 0)");
}
if (Table == NULL) {
DebugAssert (__FILE__, 98, "Table != ((void *) 0)");
}
*Table = NULL;
NumberOfEntries = gSystemTable->NumberOfTableEntries;
ConfigurationTable = gSystemTable->ConfigurationTable;
if (ConfigurationTable == NULL) {
return EFI_NOT_FOUND;
}
for (Index = 0; Index < NumberOfEntries; Index++) {
if (CompareGuid (TableGuid, &ConfigurationTable[Index].VendorGuid)) {
*Table = ConfigurationTable[Index].VendorTable;
return EFI_SUCCESS;
}
}
return EFI_NOT_FOUND;
}
//
// MMIO PCI config read (via MmPciUsra protocol)
//
UINT32
MmPciReadConfig (
VOID
)
{
UINT32 PciAddress[6];
PciAddress[0] = 0xF8000; // Base address
PciAddress[1] = 0; // Bus 0
PciAddress[2] = 0x200; // Device 0x20, Func 0
PciAddress[3] = 0;
// Call into MmPciUsra protocol
return ((MM_PCI_USRA_PROTOCOL *)mPciUsra)->ReadConfig (PciAddress);
}
//
// Get HOB list pointer
//
VOID *
GetHobList (
VOID
)
{
EFI_STATUS Status;
if (mHobList == NULL) {
Status = EfiGetSystemConfigurationTable (
&gEfiHobListGuid,
&mHobList
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
DebugAssert (__FILE__, 54, "!EFI_ERROR (Status)");
}
if (mHobList == NULL) {
DebugAssert (__FILE__, 55, "mHobList != ((void *) 0)");
}
}
return mHobList;
}
//
// Get PCD protocol pointer
//
PCD_PROTOCOL *
GetPcdProtocol (
VOID
)
{
EFI_STATUS Status;
if (mPcd == NULL) {
Status = gBootServices->LocateProtocol (
&gEfiPcdProtocolGuid,
NULL,
(VOID **)&mPcd
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
DebugAssert (__FILE__, 78, "!EFI_ERROR (Status)");
}
if (mPcd == NULL) {
DebugAssert (__FILE__, 79, "mPcd != ((void *) 0)");
}
}
return mPcd;
}
//
// Compare two GUIDs (via unaligned 64-bit read)
//
BOOLEAN
CompareGuid (
IN EFI_GUID *Guid1,
IN EFI_GUID *Guid2
)
{
UINT64 Guid1Low;
UINT64 Guid1High;
UINT64 Guid2Low;
UINT64 Guid2High;
// Read as 64-bit values (handles unaligned)
Guid1Low = ReadUnaligned64 (Guid1);
Guid1High = ReadUnaligned64 ((VOID *)((UINT8 *)Guid1 + 8));
Guid2Low = ReadUnaligned64 (Guid2);
Guid2High = ReadUnaligned64 ((VOID *)((UINT8 *)Guid2 + 8));
return (Guid1Low == Guid2Low && Guid1High == Guid2High);
}
//
// Determine PCH stepping based on LPC device ID
//
UINT8
PchGetStepping (
VOID
)
{
UINT32 PciData;
UINT16 LpcDeviceId;
if (mPchStepping == PCH_STEPPING_UNKNOWN) {
// Read LPC device ID via MMIO PCI config
PciData = MmPciReadConfig ();
LpcDeviceId = IoRead16 ((UINT16 *)(PciData + 2));
if (((LpcDeviceId + 0x5E40) & LPC_DEVICE_ID_H_MASK) != 0) {
// Check if LP-series
if ((UINT16)(LpcDeviceId + 0x62C0) <= 8) {
// Bit test: LPC device IDs matching LP
mPchStepping = PCH_STEPPING_LP;
} else {
DEBUG ((DEBUG_ERROR, "Unsupported PCH SKU, LpcDeviceId: 0x%04x!\n", LpcDeviceId));
DebugAssert (__FILE__, 252, "((BOOLEAN)(0==1))");
}
} else {
mPchStepping = PCH_STEPPING_H;
}
}
return mPchStepping;
}
//
// Read unaligned 64-bit value
//
UINT64
ReadUnaligned64 (
IN CONST VOID *Buffer
)
{
if (Buffer == NULL) {
DebugAssert (__FILE__, 192, "Buffer != ((void *) 0)");
}
return *(const UINT64 *)Buffer;
}
//
// Driver entry point
//
EFI_STATUS
EFIAPI
LegacyInterruptEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
Status = LibConstructor (ImageHandle, SystemTable);
if (EFI_ERROR (Status)) {
return Status;
}
Status = LegacyInterruptInstall ();
return Status;
}