/**
* @file RegAccessPeim.c
* @brief Register Access PEIM
*
* Provides register read/write/modify services during the PEI phase.
* Handles address translation for IIO (Integrated IO), CHA, PCU, Ubox,
* and other CPU/internal bus registers across multiple sockets.
*
* Source path: CpRcPkg/Universal/RegAccess/Pei/RegAccess.c
* Build path: Build/HR6N0XMLK/DEBUG_VS2015/IA32/.../RegAccessPeim.pdb
*/
#include "RegAccessPeim.h"
/* ========================================================================
* PEI Register Access PPI GUID
* GUID data at unk_FFDB09BC location
* ======================================================================== */
extern EFI_GUID gRegAccessPpiGuid;
/* ========================================================================
* Register access function dispatch tables
* ======================================================================== */
/**
* Dispatch table for RegAccessReadRegister.
* Address: 0xFFDB05B0 (funcs_FFDAED70)
* Indexed by (Descriptor->ExtInfo >> 8) & 0xF (access width/box type)
*/
typedef EFI_STATUS (*REG_ACCESS_READ_FUNC)(REG_ACCESS_DESC *Desc, UINT64 *Value);
extern REG_ACCESS_READ_FUNC gRegAccessReadDispatchTable[];
/**
* Dispatch table for RegAccessWriteRegister.
* Address: 0xFFDB05BC (funcs_FFDAED59)
*/
typedef EFI_STATUS (*REG_ACCESS_WRITE_FUNC)(REG_ACCESS_DESC *Desc, UINT64 Value);
extern REG_ACCESS_WRITE_FUNC gRegAccessWriteDispatchTable[];
/**
* Dispatch table for RegAccessReadModify.
* Address: 0xFFDB05C8 (funcs_FFDAED8B)
*/
typedef EFI_STATUS (*REG_ACCESS_RM_FUNC)(REG_ACCESS_DESC *Desc, UINT64 AndMask, UINT64 OrMask);
extern REG_ACCESS_RM_FUNC gRegAccessReadModifyDispatchTable[];
/**
* Dispatch table for RegAccessGetConfigSpace.
* Address: 0xFFDB05D4 (funcs_FFDAEDA9)
*/
typedef VOID (*REG_ACCESS_CFG_FUNC)(UINT32 a, UINT32 b, REG_ACCESS_DESC *Desc, REG_ACCESS_DESC **Out);
extern REG_ACCESS_CFG_FUNC gRegAccessConfigDispatchTable[];
/* ========================================================================
* Hardware topology byte tables
* ======================================================================== */
/* Access width per box type: byte_FFDB05E0
* Values: 1=byte, 2=word, 4=dword, 8=qword */
extern const UINT8 gRegAccessWidth[];
/* Element stride per box type: byte_FFDB05EC */
extern const UINT8 gRegAccessStride[];
/* Functional block to table index mapping arrays: byte_FFDB090C - byte_FFDB099C
* Organized as 168-byte rows per CPU type variant */
extern const UINT8 gFuncBlockToIndex_Socket0[]; /* byte_FFDB090C */
extern const UINT8 gFuncBlockToIndex_Socket1[]; /* byte_FFDB0944 (168*1 offset) */
/* Device number mapping tables */
extern const UINT8 gDeviceNum_CHA[]; /* byte_FFDB0944 (also used by CHA) */
extern const UINT8 gDeviceNum_M2MEM[]; /* byte_FFDB094C */
extern const UINT8 gDeviceNum_MC[]; /* byte_FFDB0954 */
extern const UINT8 gDeviceNum_PCU[]; /* byte_FFDB095C */
extern const UINT8 gDeviceNum_IIO[]; /* byte_FFDB0964 */
extern const UINT8 gDeviceNum_KTI[]; /* byte_FFDB096C */
extern const UINT8 gDeviceNum_IIODfx[]; /* byte_FFDB099C (0xFF = invalid) */
/* KTI instance to device mapping */
extern const UINT8 gKtiInstanceToDevice[]; /* byte_FFDB09B4 */
/* ========================================================================
* PEI module globals (.data section)
* ======================================================================== */
extern REG_ACCESS_DESC *gRegAccessConfigSpace;
extern UINT32 gRegAccessPpiDescriptorData[];
/* ========================================================================
* Utility: library-style memset replacement
* Equivalent to memset(buf, value, count)
* Utility helper
* ======================================================================== */
VOID *
EFIAPI
RegAccessMemSet (
VOID *Buffer,
UINTN Count,
UINT8 Value
)
{
return memset (Buffer, Value, Count);
}
/* ========================================================================
* Utility: library-style memmove replacement
* Handles overlapping regions
* Utility helper
* ======================================================================== */
CHAR8 *
EFIAPI
RegAccessMemMove (
CHAR8 *Destination,
CONST CHAR8 *Source,
UINTN Count
)
{
UINTN WordCount = Count >> 2;
UINTN Remainder = Count & 3;
if (Source < Destination && &Source[Count - 1] >= Destination) {
/* Overlapping: copy backward */
Source = &Source[Count - 1];
Destination = &Destination[Count - 1];
} else {
/* Non-overlapping: copy dwords first */
CopyMem (Destination, Source, WordCount * 4);
Source = &Source[WordCount * 4];
Destination = &Destination[WordCount * 4];
}
CopyMem (Destination, Source, Remainder);
return Destination;
}
/* ========================================================================
* Utility: fill 64-bit values into array
* Utility helper
* Parameters:
* Base - destination array base (UINT64*)
* Index - starting index, decremented to 0
* ValueLow, ValueHigh - the 64-bit value to write
* ======================================================================== */
INTN
EFIAPI
RegAccessFill64 (
UINT64 *Base,
INTN Index,
UINT32 ValueLow,
UINT32 ValueHigh
)
{
do {
Base[Index - 1] = ValueLow | ((UINT64)ValueHigh << 32);
} while (--Index);
return (INTN)Base;
}
/* ========================================================================
* Utility: memset32 wrapper
* Utility helper
* ======================================================================== */
VOID *
EFIAPI
RegAccessMemSet32 (
VOID *Buffer,
UINTN Count,
UINT32 Value
)
{
return memset32 (Buffer, Value, Count);
}
/* ========================================================================
* PEI Services: Get PEI Services Table Pointer
* Returns the PeiServices pointer via IDT-based lookup
* PEI services helper
* ======================================================================== */
INTN
EFIAPI
GetPeiServicesTablePointer (
VOID
)
{
IA32_DESCRIPTOR Idtr;
UINTN *Ptr;
AsmReadIdtr (&Idtr);
Ptr = (UINTN *)(*(UINTN *)(Idtr.Base + 2) - 4);
if (*Ptr == 0) {
DEBUG ((EFI_D_ERROR, "PeiServices == NULL\n"));
ASSERT (FALSE);
}
return *Ptr;
}
/* ========================================================================
* PEI Services: Report Debug/Assert Message
* Debug helper
* ======================================================================== */
VOID
EFIAPI
ReportAssertMessage (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Message,
...
)
{
VA_LIST Args;
INTN PeiServices;
INTN (*DebugFunc)(UINTN, CONST CHAR8 *, VA_LIST);
PeiServices = GetPeiServicesTablePointer ();
if (PeiServices == 0) {
return;
}
DebugFunc = (VOID *)(*(UINTN *)PeiServices + 4);
if ((GetDebugErrorLevel () & ErrorLevel) != 0) {
VA_START (Args, Message);
DebugFunc (ErrorLevel, Message, Args);
VA_END (Args);
}
}
/* ========================================================================
* PEI Services: Debug Print (simple)
* Debug helper
* ======================================================================== */
INTN
EFIAPI
DebugPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
INTN PeiServices;
INTN (*PrintFunc)(UINTN, CONST CHAR8 *, VA_LIST);
PeiServices = GetPeiServicesTablePointer ();
if (PeiServices == 0) {
return 0;
}
VA_LIST Args;
VA_START (Args, Format);
PrintFunc = (VOID *)(*(UINTN *)PeiServices + 4);
VA_END (Args);
return PrintFunc (ErrorLevel, Format, Args);
}
/* ========================================================================
* Get Debug Error Level via CMOS
* Reads CMOS index 0x4A to determine current debug print mask
* Debug helper
* ======================================================================== */
INTN
EFIAPI
GetDebugErrorLevel (
VOID
)
{
UINT8 CmosValue;
UINT8 ResultVar;
IoWrite8 (0x70, (IoRead8 (0x70) & 0x80) | 0x4A);
CmosValue = IoRead8 (0x71);
ResultVar = CmosValue;
if (CmosValue <= 3) {
goto CheckDone;
}
ResultVar = CmosValue;
if (CmosValue == 0) {
ResultVar = (MEMORY[0xFDAF0490] & 2) | 1;
goto CheckDone;
}
CheckDone:
if (ResultVar == (UINT8)-1) return 0;
if (ResultVar == 1) return EFI_ALREADY_STARTED;
return EFI_UNSUPPORTED;
}
/* ========================================================================
* Utility: Always returns TRUE (debug enabled)
* Utility helper
* ======================================================================== */
BOOLEAN
EFIAPI
IsDebugEnabled (
VOID
)
{
return TRUE;
}
/* ========================================================================
* Utility: Boolean conversion
* Utility helper
* ======================================================================== */
BOOLEAN
EFIAPI
BoolCheck (
IN UINTN Value
)
{
return Value != 0;
}
/* ========================================================================
* Cpu Dead Loop (infinite loop on error assertion)
* Assertion helper
* ======================================================================== */
VOID
EFIAPI
CpuDeadLoopEx (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Message,
IN UINTN Status
)
{
if (Status != 0) {
if ((Status & 0xFFFFFF00) == 0xFFFFFF00) {
Status = (UINT8)Status;
}
if ((Status & 0xFFFF0000) == 0xFFFF0000) {
Status = (UINT16)Status;
}
if (IsDebugEnabled () && BoolCheck (0x80000000)) {
ReportAssertMessage (0x80000000, Message, Status);
}
}
while (TRUE);
}
/* ========================================================================
* IO Memory Read (aligned memory access)
* MMIO helper
* ======================================================================== */
INTN
EFIAPI
IoMemRead (
IN UINTN AccessWidth,
IN VOID *Address,
OUT UINT64 *Value
)
{
switch (AccessWidth) {
case 0: /* 1 byte */
*(UINT8 *)Value = *(volatile UINT8 *)Address;
break;
case 1: /* 2 bytes */
ASSERT (((UINTN)Address & 1) == 0);
*(UINT16 *)Value = *(volatile UINT16 *)Address;
break;
default: /* 8 bytes */
ASSERT (((UINTN)Address & 7) == 0);
Value[0] = *(volatile UINT64 *)Address;
break;
}
return 0;
}
/* ========================================================================
* IO Memory Write (aligned memory access)
* MMIO helper
* ======================================================================== */
INTN
EFIAPI
IoMemWrite (
IN UINTN AccessWidth,
OUT VOID *Address,
IN UINT64 *Value
)
{
switch (AccessWidth) {
case 0: /* 1 byte */
*(volatile UINT8 *)Address = *(UINT8 *)Value;
break;
case 1: /* 2 bytes */
ASSERT (((UINTN)Address & 1) == 0);
*(volatile UINT16 *)Address = *(UINT16 *)Value;
break;
default: /* 4 or 8 bytes */
ASSERT (((UINTN)Address & 7) == 0);
*(volatile UINT32 *)Address = *(UINT32 *)Value;
Address[4] = Value[1];
break;
}
return 0;
}
/* ========================================================================
* Bitwise merge (used by ReadModify)
* Bitfield helper
* ======================================================================== */
UINTN
EFIAPI
BitFieldMerge (
IN OUT UINT64 *Value,
IN UINTN AndMask,
IN UINTN OrMask,
IN UINT8 ByteCount
)
{
UINT8 Result[8];
UINT8 SrcParts[8];
UINT8 DstParts[8];
UINTN i;
/* Initialize source and destination parts */
for (i = 0; i < ByteCount; i++) {
SrcParts[i] = (AndMask != 0) ? ((UINT8 *)&Value)[i + Offset] : 0xFF;
DstParts[i] = (OrMask != 0) ? ((UINT8 *)(&Value))[Offset2 + i] : 0;
Result[i] = DstParts[i] | (SrcParts[i] & AND[i]);
}
/* Write back */
*Value = *(UINT64 *)&Result;
return *Value;
}
/* ========================================================================
* Shift Left 64 (with bounds check)
* Shift helper
* ======================================================================== */
UINT64
EFIAPI
InternalLShift64 (
IN UINT64 Value,
IN UINTN ShiftCount
)
{
ASSERT (ShiftCount < 64);
if (ShiftCount >= 64) {
CpuDeadLoop (0x80000000, "ASSERT_EFI_ERROR (Status = %r)\n", EFI_UNSUPPORTED);
}
if ((ShiftCount & 0x20) != 0) {
return Value << (ShiftCount & 0x1F);
}
return Value << (ShiftCount & 0x1F);
}
/* ========================================================================
* Shift Right 64 (with bounds check)
* Shift helper
* ======================================================================== */
UINT64
EFIAPI
InternalRShift64 (
IN UINT64 Value,
IN UINTN ShiftCount
)
{
ASSERT (ShiftCount < 64);
if ((ShiftCount & 0x20) != 0) {
return (UINT64)(UINT32)(Value >> (ShiftCount & 0x1F));
}
return Value >> (ShiftCount & 0x1F);
}
/* ========================================================================
* Read IDTR (sidt instruction wrapper)
* Utility helper
* ======================================================================== */
VOID
EFIAPI
InternalReadIdtr (
OUT IA32_DESCRIPTOR *Idtr
)
{
ASSERT (Idtr != NULL);
AsmReadIdtr (Idtr);
}
/* ========================================================================
* PCD Protocol: Get PCD value via PCD protocol
* PCD helper
* ======================================================================== */
VOID *
EFIAPI
PcdGetProtocol (
IN UINTN TokenNumber
)
{
INTN PeiServices;
EFI_STATUS Status;
VOID *PcdProtocol;
PeiServices = GetPeiServicesTablePointer ();
Status = (*PeiServices->LocateProtocol) (&gPcdPpiGuid, NULL, &PcdProtocol);
if (EFI_ERROR (Status)) {
CpuDeadLoop (0x80000000, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
}
return PcdProtocol;
}
/* ========================================================================
* PCD Get Pointer wrapper (token 5)
* PCD helper
* ======================================================================== */
VOID *
EFIAPI
PcdGetPtr (
IN UINTN TokenNumber
)
{
VOID *PcdProtocol;
VOID *Value;
PcdProtocol = PcdGetProtocol (TokenNumber);
Value = ((PCD_PPI_PROTOCOL *)PcdProtocol)->GetPtr (TokenNumber);
return Value;
}
/* ========================================================================
* PCD Get Size wrapper (token 6)
* PCD helper
* ======================================================================== */
UINTN
EFIAPI
PcdGetSize (
IN UINTN TokenNumber
)
{
VOID *PcdProtocol;
PcdProtocol = PcdGetProtocol (TokenNumber);
return ((PCD_PPI_PROTOCOL *)PcdProtocol)->GetSize (TokenNumber);
}
/* ========================================================================
* PCD Set Ptr/Size (allocate PCD data buffer)
* PCD helper
* ======================================================================== */
EFI_STATUS
EFIAPI
PcdSetRegisterAccess (
IN REG_ACCESS_DESC *Desc,
IN UINTN ElementCount
)
{
UINTN PcdSize;
PcdSize = PcdGetSize (6, 0);
Desc->AddressLow = 16 * ElementCount + 16;
if (Desc->AddressHigh == 0 && Desc->AddressLow == 0) {
*((UINTN *)(PcdSize + 16)) = PcdGetPtr (5);
*((UINTN *)(PcdSize + 20)) = 0;
}
return EFI_SUCCESS;
}
/* ========================================================================
* Register Access: Lookup PCD data pointer
* Address translation helper
* ======================================================================== */
VOID *
EFIAPI
RegAccessLookupTablePointer (
IN REG_ACCESS_DESC *Desc
)
{
VOID *PcdTable;
UINT32 PcdAddrLow;
UINT32 PcdAddrHigh;
UINTN PcdSize;
UINT16 SocketIndex;
SocketIndex = Desc->AddressLow & 0xFFFF;
if (Desc->InstanceData != NULL) {
PcdAddrLow = *(UINT32 *)(Desc->InstanceData + 4 * SocketIndex + 0x3E638);
PcdAddrHigh = *(UINT32 *)(Desc->InstanceData + 4 * SocketIndex + 0x3E648);
} else {
PcdSize = PcdGetSize (6, 0);
if (*(UINT32 *)(PcdSize + 4) != 0) {
PcdAddrLow = *(UINT32 *)(PcdSize + 20 + 16 * SocketIndex);
PcdAddrHigh = *(UINT32 *)(PcdSize + 16 + 16 * SocketIndex);
} else {
PcdSetRegisterAccess (gRegAccessConfigSpace, 8);
PcdAddrLow = gRegAccessConfigSpace[4 * SocketIndex].AddressLow;
PcdAddrHigh = gRegAccessConfigSpace[4 * SocketIndex].AddressHigh;
if (PcdAddrHigh == 0 && PcdAddrLow == 0) {
PcdAddrHigh = PcdGetPtr (5);
PcdAddrLow = 0;
}
}
}
if (PcdAddrHigh == 0 && PcdAddrLow == 0) {
return PcdGetPtr (5);
}
return (VOID *)(UINTN)PcdAddrHigh;
}
/* ========================================================================
* Address translation page table walk
* Address translation helper
* ======================================================================== */
/* ========================================================================
* Translate Socket & Box Type to page table index (high-level)
* Address translation helper
* Returns a byte index into the socket/box-specific page table
* ======================================================================== */
UINTN
EFIAPI
CpuTypeToTableIndex (
IN UINTN SocketId,
IN UINTN CpuType,
IN UINTN BoxInstance,
IN UINTN FunctionalBlock
)
{
/* Validated by the translation helper on failure. */
return 0;
}
/* ========================================================================
* Device number lookup (address translation step 2)
* Address translation helper
* ======================================================================== */
UINTN
EFIAPI
CpuTypeToDeviceIndex (
IN UINTN SocketId,
IN UINTN CpuType,
IN UINTN BoxInstance,
IN UINTN FunctionalBlock
)
{
UINTN Index = 0;
UINT8 IsSimpleCpu = *(UINT8 *)(CpuConfigTable + 44);
UINT8 KtiPerSocket = *(UINT8 *)(CpuConfigTable + 60);
switch (CpuType) {
case 0: /* CHA */
if (!IsSimpleCpu && BoxInstance < 28) {
if (BoxInstance < 8) return 9;
if (BoxInstance < 16) return 10;
return 11 - (BoxInstance < 24);
}
break;
case 1: /* M2MEM */
if (!IsSimpleCpu && BoxInstance < 2) {
Index = BoxInstance + 8;
} else {
CpuDeadLoop (SocketId, "Invalid M2MEM Box Instance Number %d. \n", BoxInstance);
}
return Index;
case 2: /* CHA (alt mapping) */
if (!IsSimpleCpu && BoxInstance < 28) {
if (BoxInstance < 8) return 20;
if (BoxInstance < 16) return 21;
return 23 - (BoxInstance < 24);
}
break;
case 3: /* CHABC */
case 4: /* PCU */
case 5: /* VCU */
if (!IsSimpleCpu && BoxInstance == 0) {
Index = (CpuType == 3) ? 29 : (CpuType == 4) ? 30 : 31;
} else {
CpuDeadLoop(...);
}
return Index;
case 7: /* IIO generic */
case 11:
if (!IsSimpleCpu) {
if (BoxInstance <= 1) Index = 10;
else if (BoxInstance == 2) Index = 11;
else if (BoxInstance <= 4) Index = 12;
else if (BoxInstance == 5) Index = 13;
} else {
CpuDeadLoop(SocketId, "Invalid Cpu type.\n", 0);
}
return Index;
/* Remaining cases follow the same translation pattern. */
}
return 0;
}
/* ========================================================================
* Functional block number lookup (address translation step 3)
* Address translation helper
* ======================================================================== */
UINTN
EFIAPI
FuncBlockToTableIndex (
IN UINTN SocketId,
IN UINTN CpuType,
IN UINTN BoxInstance,
IN UINTN FunctionalBlock
)
{
UINTN Index = 0;
UINT8 IsSimpleCpu;
UINT8 KtiPerSocket;
IsSimpleCpu = *(UINT8 *)(CpuConfigTable + 44);
KtiPerSocket = *(UINT8 *)(CpuConfigTable + 60);
switch (CpuType) {
case 0: case 1: case 2: /* CHA/M2MEM/CHA-alt */
if (!IsSimpleCpu && BoxInstance < 28) {
Index = BoxInstance & 7;
}
return Index;
case 3: case 4: case 5: case 6: case 13:
case 14: case 15: case 16: case 17: case 19:
Index = gFuncBlockToIndex_Socket0[168 * IsSimpleCpu + 8 * CpuType + FunctionalBlock];
break;
case 7: /* IIO generic */
Index = FunctionalBlock + gDeviceNum_IIO[168 * IsSimpleCpu + BoxInstance % KtiPerSocket];
break;
/* ... more cases */
}
if (Index > 7) {
CpuDeadLoop(SocketId, "Invalid Functional Block...", FunctionalBlock);
}
return Index;
}
/* ========================================================================
* Address translation: page table walk (3-level)
* Combines socket, box type, instance, and functional block into
* a final page table index.
* Address translation helper
* ======================================================================== */
UINTN
EFIAPI
TranslateAddressWalk (
IN REG_ACCESS_DESC *Descriptor,
IN UINTN PageTableBase,
OUT UINT64 *TranslatedAddress
)
{
UINT8 SocketId;
UINT8 CpuType;
UINT8 BoxInstance;
UINT8 FuncBlock;
SocketId = *(UINT8 *)(Descriptor + 5);
CpuType = *(UINT8 *)(Descriptor + 3);
BoxInstance = *(UINT8 *)(Descriptor + 4);
FuncBlock = *(UINT8 *)(Descriptor + 2);
*TranslatedAddress ^= (CpuTypeToTableIndex(SocketId, CpuType, BoxInstance, FuncBlock, PageTableBase) << 20);
*TranslatedAddress ^= (CpuTypeToDeviceIndex(SocketId, CpuType, BoxInstance, FuncBlock, PageTableBase) << 15);
*TranslatedAddress ^= (FuncBlockToTableIndex(SocketId, CpuType, BoxInstance, FuncBlock, PageTableBase) << 12);
return SocketId;
}
/* ========================================================================
* Validate register range and alignment
* Validation helper
* Returns 0 on success, EFI error code on failure
* ======================================================================== */
EFI_STATUS
EFIAPI
RegAccessValidateRange (
IN UINTN AccessWidth,
IN UINT64 Address,
IN UINTN Count,
IN UINTN Buffer
)
{
UINTN AccessBytes;
if (Buffer == 0 || AccessWidth >= 12) {
return EFI_INVALID_PARAMETER;
}
AccessBytes = gRegAccessWidth[AccessWidth & 3];
if ((Address & (AccessBytes - 1)) != 0) {
return EFI_ALIGNMENT_ERROR; /* misaligned */
}
if (Count == 0) {
if (Address >> 32 != 0) {
return EFI_ALIGNMENT_ERROR; /* out of range for 32-bit */
}
/* Range check: count-1 fits in accessible space */
if (InternalLShift64 (1, Count) < (Count - 1)) {
return EFI_ALIGNMENT_ERROR;
}
}
/* Verify buffer alignment */
if ((Buffer & (MIN(AccessBytes, 4) - 1)) != 0) {
return EFI_ALIGNMENT_ERROR;
}
return EFI_SUCCESS;
}
/* ========================================================================
* DISPATCH: RegAccess Read Register (via dispatch table)
* Dispatch helper
* ======================================================================== */
EFI_STATUS
EFIAPI
RegAccessReadRegister (
IN REG_ACCESS_DESC *Descriptor,
OUT UINT64 *Value
)
{
return gRegAccessReadDispatchTable[
(Descriptor->ExtInfo >> 8) & REG_ACCESS_BOX_TYPE_MASK
] (Descriptor, Value);
}
/* ========================================================================
* DISPATCH: RegAccess Write Register (via dispatch table)
* Dispatch helper
* ======================================================================== */
EFI_STATUS
EFIAPI
RegAccessWriteRegister (
IN REG_ACCESS_DESC *Descriptor,
IN UINT64 Value
)
{
return gRegAccessWriteDispatchTable[
(Descriptor->ExtInfo >> 8) & REG_ACCESS_BOX_TYPE_MASK
] (Descriptor, Value);
}
/* ========================================================================
* DISPATCH: RegAccess Read Modify (via dispatch table)
* Dispatch helper
* ======================================================================== */
EFI_STATUS
EFIAPI
RegAccessReadModify (
IN REG_ACCESS_DESC *Descriptor,
IN UINT64 AndMask,
IN UINT64 OrMask
)
{
return gRegAccessReadModifyDispatchTable[
(Descriptor->ExtInfo >> 8) & REG_ACCESS_BOX_TYPE_MASK
] (Descriptor, AndMask, OrMask);
}
/* ========================================================================
* DISPATCH: RegAccess Get Config Space
* Dispatch helper
* ======================================================================== */
VOID
EFIAPI
RegAccessGetConfigSpace (
OUT REG_ACCESS_DESC *Descriptor
)
{
gRegAccessConfigDispatchTable[
(Descriptor->ExtInfo >> 8) & REG_ACCESS_BOX_TYPE_MASK
] (0, 0, Descriptor, &Descriptor);
}
/* ========================================================================
* IMPL: RegAccess Read Register (box type dispatch = 0)
* Reads from translated address via IoMemRead
* Implementation helper
* ======================================================================== */
EFI_STATUS
EFIAPI
RegAccessReadImpl (
IN REG_ACCESS_DESC *Descriptor,
OUT UINT64 *Value
)
{
UINT64 TranslatedAddress;
UINT64 *AddressPtr;
RegAccessTranslateAddress (Descriptor, &TranslatedAddress);
AddressPtr = (UINT64 *)(UINTN)TranslatedAddress;
IoMemRead (
(Descriptor->ExtInfo >> 8) & 0xF,
AddressPtr,
Value
);
return EFI_SUCCESS;
}
/* ========================================================================
* IMPL: RegAccess Write Register (box type dispatch = 0)
* Writes to translated address via IoMemWrite
* Implementation helper
* ======================================================================== */
EFI_STATUS
EFIAPI
RegAccessWriteImpl (
IN REG_ACCESS_DESC *Descriptor,
IN UINT64 Value
)
{
UINT64 TranslatedAddress;
UINT64 *AddressPtr;
RegAccessTranslateAddress (Descriptor, &TranslatedAddress);
AddressPtr = (UINT64 *)(UINTN)TranslatedAddress;
IoMemWrite (
(Descriptor->ExtInfo >> 8) & 0xF,
AddressPtr,
&Value
);
return EFI_SUCCESS;
}
/* ========================================================================
* IMPL: RegAccess Read Modify (box type dispatch = 0)
* Reads, applies AND/OR mask, writes back
* Implementation helper
* ======================================================================== */
EFI_STATUS
EFIAPI
RegAccessReadModifyImpl (
IN REG_ACCESS_DESC *Descriptor,
IN UINT64 AndMask,
IN UINT64 OrMask
)
{
UINT64 Value[2];
UINT8 ByteMask;
RegAccessReadImpl (Descriptor, Value);
ByteMask = (UINT8)(UINTN)&Value[-1] + ((Descriptor->ExtInfo >> 8) & 0xF);
BitFieldMerge (Value, AndMask, OrMask, ByteMask);
RegAccessWriteImpl (Descriptor, Value);
return EFI_SUCCESS;
}
/* ========================================================================
* IMPL: Read multiple elements via IoMemRead (box type dispatch = 1)
* Implementation helper
* ======================================================================== */
EFI_STATUS
EFIAPI
RegAccessReadMultiImpl (
IN REG_ACCESS_DESC *Descriptor,
OUT VOID *Buffer
)
{
UINTN Count;
UINT64 TranslatedAddress;
UINT64 *SrcPtr;
UINT64 *DstPtr;
UINTN ElementSize;
UINTN Stride;
Count = Descriptor->Count;
RegAccessTranslateAddress (Descriptor, &TranslatedAddress);
SrcPtr = (UINT64 *)(UINTN)TranslatedAddress;
if (RegAccessValidateRange (
(Descriptor->ExtInfo >> 8) & 0xF,
TranslatedAddress,
Count,
(UINTN)Buffer
) < 0) {
return EFI_INVALID_PARAMETER;
}
{
UINTN BoxType = (Descriptor->ExtInfo >> 8) & 0xF;
ElementSize = gRegAccessWidth[BoxType];
Stride = gRegAccessStride[BoxType];
DstPtr = (UINT64 *)Buffer;
while (Count--) {
IoMemRead (
(Descriptor->ExtInfo >> 8) & 3,
SrcPtr,
DstPtr
);
DstPtr = (UINT64 *)((UINT8 *)DstPtr + Stride);
SrcPtr = (UINT64 *)((UINT8 *)SrcPtr + ElementSize);
}
}
return EFI_SUCCESS;
}
/* ========================================================================
* IMPL: Write multiple elements via IoMemWrite (box type dispatch = 1)
* Implementation helper
* ======================================================================== */
EFI_STATUS
EFIAPI
RegAccessWriteMultiImpl (
IN REG_ACCESS_DESC *Descriptor,
IN VOID *Buffer
)
{
UINTN Count;
UINT64 TranslatedAddress;
UINT64 *SrcPtr;
UINT64 *DstPtr;
UINTN ElementSize;
UINTN Stride;
Count = Descriptor->Count;
RegAccessTranslateAddress (Descriptor, &TranslatedAddress);
SrcPtr = (UINT64 *)(UINTN)TranslatedAddress;
if (RegAccessValidateRange (...) < 0) {
return EFI_INVALID_PARAMETER;
}
{
UINTN BoxType = (Descriptor->ExtInfo >> 8) & 0xF;
ElementSize = gRegAccessWidth[BoxType];
Stride = gRegAccessStride[BoxType];
DstPtr = (UINT64 *)Buffer;
while (Count--) {
IoMemWrite (
(Descriptor->ExtInfo >> 8) & 3,
DstPtr,
SrcPtr
);
DstPtr = (UINT64 *)((UINT8 *)DstPtr + Stride);
SrcPtr = (UINT64 *)((UINT8 *)SrcPtr + ElementSize);
}
}
return EFI_SUCCESS;
}
/* ========================================================================
* IMPL: Get MMIO address from descriptor (box type dispatch = 0)
* Implementation helper
* ======================================================================== */
INTN
EFIAPI
RegAccessTranslateAddress (
IN REG_ACCESS_DESC *Descriptor,
OUT UINT64 *TranslatedAddress
)
{
UINTN TablePtr;
TablePtr = RegAccessLookupTablePointer (Descriptor);
*TranslatedAddress = TablePtr + (Descriptor->AddressLow & 0xFFFFFFF);
return *TranslatedAddress;
}
/* ========================================================================
* Page table address translation (full path)
* Address translation helper
* ======================================================================== */
EFI_STATUS
EFIAPI
RegAccessTranslateFull (
IN REG_ACCESS_DESC *Descriptor,
OUT UINT64 *TranslatedAddress
)
{
UINTN PageTable;
UINT32 OriginalLow;
UINT32 PageTableInfo[3];
PageTable = Descriptor->InstanceData;
OriginalLow = Descriptor->AddressLow;
/* Check if address needs translation */
if (PageTable != 0 && (OriginalLow & 0x8000) != 0) {
Descriptor->AddressLow = OriginalLow & 0xFFFF0000;
}
/* Encode box type from address bits 13-14 */
Descriptor->ExtInfo &= 0xFFFFF0FF;
Descriptor->ExtInfo |= ((OriginalLow >> 13) & 3) << 8;
/* Check cached translation */
if (PageTable != 0
&& *(UINT8 *)(PageTable + 0x998C0) != 0
&& *(UINT32 *)(PageTable + 0x998B4) == (OriginalLow & 0xFFFFF000)
&& *(UINT32 *)(PageTable + 0x998B8) == Descriptor->AddressHigh) {
/* Cached hit */
*TranslatedAddress = (*(UINT32 *)(PageTable + 0x998BC) & 0xFFFFF000)
| (OriginalLow & 0xFFF);
} else {
/* Page walk */
ZeroMem (PageTableInfo, sizeof(PageTableInfo));
TranslateAddressWalk (Descriptor, PageTable + 0x3E5F8, PageTableInfo);
PageTableInfo[0] ^= (Descriptor->AddressLow & 0xFFF) ^ (OriginalLow & 0xFFF);
PageTableInfo[0] = (PageTableInfo[0] ^ (OriginalLow ^ PageTableInfo[0]) & 0xFFF);
Descriptor->InstanceData = (VOID *)(UINTN)PageTable;
*TranslatedAddress = RegAccessLookupTablePointer (PageTableInfo)
+ (PageTableInfo[0] & 0xFFFFFFF);
/* Cache translation */
if (PageTable != 0) {
*(UINT32 *)(PageTable + 0x998B4) = OriginalLow & 0xFFFFF000;
*(UINT32 *)(PageTable + 0x998B8) = Descriptor->AddressHigh;
*(UINT32 *)(PageTable + 0x998BC) = *TranslatedAddress & 0xFFFFF000;
}
}
return EFI_SUCCESS;
}
/* ========================================================================
* Thunk helper
* ======================================================================== */
INTN
EFIAPI
RegAccessGetConfigSpaceThunk (
IN REG_ACCESS_DESC *Descriptor,
OUT REG_ACCESS_DESC *ConfigSpace
)
{
UINT64 *AddressPtr = NULL;
RegAccessTranslateFull (0, 0, Descriptor, &AddressPtr);
IoMemRead (
(Descriptor->ExtInfo >> 8) & 0xF,
AddressPtr,
ConfigSpace
);
return 0;
}
/* ========================================================================
* Write config space via translation and IoMemWrite
* Thunk helper
* ======================================================================== */
INTN
EFIAPI
RegAccessWriteConfigThunk (
IN REG_ACCESS_DESC *Descriptor,
OUT REG_ACCESS_DESC *ConfigSpace
)
{
UINT64 *AddressPtr = NULL;
RegAccessTranslateFull (0, 0, Descriptor, &AddressPtr);
IoMemWrite (
(Descriptor->ExtInfo >> 8) & 0xF,
AddressPtr,
ConfigSpace
);
return 0;
}
/* ========================================================================
* Thunk: ReadModify with AND/OR via translation
* Thunk helper
* ======================================================================== */
INTN
EFIAPI
RegAccessReadModifyThunk (
IN REG_ACCESS_DESC *Descriptor,
IN UINT64 AndMask,
IN UINT64 OrMask
)
{
UINT64 Value[2];
UINT64 *AddressPtr = NULL;
UINT32 BoxTypeInfo = 134480385; /* magic constant */
UINT8 ByteCount;
RegAccessTranslateFull (0, 0, Descriptor, &AddressPtr);
IoMemRead ((Descriptor->ExtInfo >> 8) & 0xF, AddressPtr, Value);
ByteCount = *((UINT8 *)&BoxTypeInfo + ((Descriptor->ExtInfo >> 8) & 0xF));
BitFieldMerge (Value, AndMask, OrMask, ByteCount);
IoMemWrite ((Descriptor->ExtInfo >> 8) & 0xF, AddressPtr, Value);
return 0;
}
/* ========================================================================
* Address translation helper (strip high bits)
* Address helper
* ======================================================================== */
UINT32
EFIAPI
RegAccessStripHighBits (
IN UINTN PageTable,
IN UINT32 Address
)
{
return Address & 0xFFFF0000;
}
/* ========================================================================
* Module Entry Point (PEIM)
* Registers the RegAccess PPI
* Address: _ModuleEntryPoint (0xFFDAED01)
* ======================================================================== */
EFI_STATUS
EFIAPI
RegAccessEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_PEI_PPI_DESCRIPTOR *PpiDescriptor;
/* Save PEI services table pointer */
gImageHandle = ImageHandle;
gST = SystemTable;
gBS = SystemTable->BootServices;
gRT = SystemTable->RuntimeServices;
/* Initialize PCD protocol */
PcdGetProtocol (5);
/* Register the RegAccess PPI */
DEBUG ((EFI_D_INFO, "UBA:SETUPConfigUpdate-TypeLightningRidgeEXRP\n"));
Status = (*gBS->InstallPpi) (&gRegAccessPpiDescriptor);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
return Status;
}
/* ========================================================================
* PCD Get PPI via PeiServices->LocatePpi
* Equivalent to PeiPcdLib GetPcdProtocol()
* PCD helper
* ======================================================================== */
VOID *
EFIAPI
PcdGetProtocol (
IN UINTN TokenNumber
)
{
PEI_PCD_PPI *PcdPpi;
EFI_STATUS Status;
INTN PeiServices;
PeiServices = GetPeiServicesTablePointer ();
Status = (*PeiServices->LocatePpi) (&gEfiPeiPcdPpiGuid, &PpiDescriptor, &PcdPpi);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
return (VOID *)PcdPpi;
}