/*=============================================================================
* StatusCodeDxe.c - Decompiled AMI StatusCode DXE Driver
*
* UEFI DXE driver from AmiModulePkg that implements the system status code
* reporting infrastructure. Routes status code events to registered listeners
* (serial, Port 80, video console, BMC). Provides ASSERT/error formatting,
* deferred buffered reporting, and progress display.
*
* Source: e:\hs\AmiModulePkg\AmiStatusCode\StatusCodeDxe.c
* Module: 0254_StatusCodeDxe (Lenovo ThinkSystem HR650X BIOS)
* Binary: StatusCodeDxe.efi (PE32+ X64, MSVC)
* IDA Base: 0x0, Image Size: 0xD000
*============================================================================*/
#include "StatusCodeDxe.h"
/*=============================================================================
* Forward Declarations
*============================================================================*/
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
);
EFI_STATUS
EFIAPI
StatusCodeDxeEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
);
/*=============================================================================
* Global Variables (.data / .bss section at 0x8000-0xA000)
*============================================================================*/
/* UEFI Boot Services Table Library singleton pointers (set by constructor) */
EFI_HANDLE gImageHandle = NULL; /* 0x9388 */
EFI_SYSTEM_TABLE *gSystemTable = NULL; /* 0x9378 / 0x93D8 */
EFI_BOOT_SERVICES *gBootServices = NULL; /* 0x9380 / 0x93C8 */
EFI_RUNTIME_SERVICES *gRuntimeServices = NULL; /* 0x9390 / 0x93D0 */
/* Saved runtime services copy for ExitBootServices use */
EFI_RUNTIME_SERVICES *gRuntimeServicesCopy = NULL; /* 0x9408 */
/* Status Code Database / Listener Pool (allocated on init) */
STATUS_CODE_DATABASE *gStatusCodeDatabase = NULL; /* 0x9440 */
/* Protocol handles / registration */
VOID *gStatusCodeProtocolNotify = NULL; /* 0x9400 */
VOID *gStatusCodeProtocolNotify2 = NULL; /* 0x9418 */
VOID *gAmiStatusCodePolicy = NULL; /* 0x9410 */
VOID *gAmiStatusCodePolicy2 = NULL; /* 0x9408 not used? */
/* Protocol notify registration keys */
VOID *gStatusCodeNotifyKey = NULL; /* 0x93A8 */
VOID *gBsNotifyKey = NULL; /* 0x93A0 */
/* HOB list pointer */
VOID *gHobList = NULL; /* 0x93C0 */
/* Protocol registration state flags */
UINT8 gStatusCodeProtocolRegistered = 0; /* byte_93E4 */
UINT8 gStatusCodeReportingEnabled = 0; /* byte_93F4 */
UINT8 gDeferredLockFlag = 0; /* byte_9308 */
UINT8 gReEntryBypass = 0; /* byte_9358 */
UINT8 gBufferOverflowFlag = 0; /* byte_92D0 */
UINT8 gBmcProgressDone = 0; /* byte_9421 */
UINT8 gBmcNetworkSeen = 0; /* byte_9420 */
/* Timer refcounts */
UINT32 gStatusCodeEnableCount = 0; /* dword_93E0 */
UINT32 gStatusCodeDisableCount = 0; /* dword_93F0 */
UINT32 gLastReportedValue = 0; /* dword_8124 */
UINT32 gConsoleTextColor = 0; /* dword_9318 */
/* Shared display/format buffer (2048 bytes) */
CHAR8 gDisplayBuffer[0x800] = { 0 }; /* byte_8188 */
UINT64 gVaListPlaceholder = 0; /* qword_8110 */
/* Serial and console protocol interfaces */
VOID *gSerialPortProtocol = NULL; /* qword_9300 */
VOID *gVgaClassProtocol = NULL; /* qword_9330 */
VOID *gBmcProtocol = NULL; /* qword_92D8 */
/*=============================================================================
* Exception handler filter tables (16-byte entries)
*============================================================================*/
/* First table: exception -> console/display notifier */
EXCEPTION_HANDLER_ENTRY gExceptionHandlers[8]; /* unk_9340 */
/* Second table: exception -> serial notifier */
EXCEPTION_HANDLER_ENTRY gSerialExceptionHandlers[8]; /* unk_9360 */
/*=============================================================================
* CPU Exception Names (from 0x6490 string pointer table)
*============================================================================*/
CONST CHAR8 *gCpuExceptionNames[20] = {
"Divide error", /* 0x00 */
"Debug", /* 0x01 */
"NMI", /* 0x02 */
"Breakpoint", /* 0x03 */
"Overflow", /* 0x04 */
"Bound", /* 0x05 */
"Invalid opcode", /* 0x06 */
"Double fault", /* 0x07 -> Actually 0x633F is "..."? No, */
/* 0x07=0x633F is the same ptr as 0x08? Actually the table at
* 0x6490 has: idx07=0x633F ("Double fault" truncated), idx08=0x6340 */
"Device not available", /* 0x07 (corrected from raw: at 0x633F there
* are overlapping strings from the R/O data) */
"Double fault", /* 0x08 */
"Coprocessor segment", /* 0x09 (same string as 0x07 in actual binary) */
"Invalid TSS", /* 0x0A */
"Segment not present", /* 0x0B */
"Stack fault", /* 0x0C */
"General protection", /* 0x0D */
"Page fault", /* 0x0E */
"Reserved", /* 0x0F (overlapping string) */
"x87 FPU error", /* 0x10 */
"Alignment check", /* 0x11 */
"Machine check", /* 0x12 */
"SIMD exception" /* 0x13 */
};
/*=============================================================================
* Status Code -> Error String Mapping Table (at 0x89C0)
* This is a UINT32/UINT64 packed table: each entry is 16 bytes
* {UINT32 TypeValue, UINT32 Reserved, CHAR8 *StringPtr}
*============================================================================*/
STATUS_CODE_STRING_MAP gStatusCodeStringMap[25] = {
/* TypeValue, Reserved, String */
{ 0x00051008, 0, "Flash update failed" },
{ 0x03041000, 0, "Some Architectural Protocols are not available" },
{ 0x00018001, 0, "DXE CPU initialization error" },
{ 0x00011002, 0, "CPU mismatch" },
{ 0x0001100A, 0, "Microcode updated failed" },
{ 0x00061001, 0, "DXE NB initialization error" },
{ 0x00061000, 0, "Bad battery" },
{ 0x00061002, 0, "DXE SB initialization error" },
{ 0x03051000, 0, "Not enough space for legacy OpROM" },
{ 0x03051001, 0, "Invalid password" },
{ 0x03051004, 0, "Invalid HDD password" },
{ 0x03051002, 0, "Boot option loading failed" },
{ 0x03051003, 0, "Boot option failed" },
{ 0x03111000, 0, "Reset protocol is not available" },
{ 0x02010009, 0, "PCI resource allocation failure(out of resources)" },
{ 0x02081002, 0, "HDD SMART error" },
{ 0x02080006, 0, "ATA controller error" },
{ 0x02080005, 0, "ATA device failure" },
{ 0x01011001, 0, "Stuck key" },
{ 0x01010006, 0, "Keyboard controller error" },
{ 0x01018000, 0, "Keyboard buffer full" },
{ 0x01030003, 0, "No ConOut" },
{ 0x01010003, 0, "No ConIn" },
{ 0x03058006, 0, "MBR or GPT is write protected" },
{ 0x0300000A, 0, "Bad Date/Time" },
};
/*=============================================================================
* Boot Init Function Table (funcs_1E8C at 0x6570)
*============================================================================*/
extern EFI_STATUS UartInitialize(EFI_HANDLE, EFI_SYSTEM_TABLE *); /* 0x273C */
extern EFI_STATUS RegisterStatusCodeProtocolNotify(EFI_HANDLE, EFI_SYSTEM_TABLE *); /* 0x23C8 */
extern EFI_STATUS RegisterConsoleListener(EFI_HANDLE, EFI_SYSTEM_TABLE *); /* 0x2174 */
BOOT_INIT_FUNCTION gBootInitFunctions[] = {
UartInitialize, /* 0x273C - serial baud config */
RegisterStatusCodeProtocolNotify, /* 0x23C8 - register protocol notify */
RegisterConsoleListener, /* 0x2174 - register console listener */
NULL /* terminator */
};
/*=============================================================================
* I/O Port Helpers
*============================================================================*/
STATIC
UINT32
EFIAPI
IoRead32 (
IN UINT16 Port
)
{
ASSERT ((Port & 3) == 0);
return __indword (Port);
}
STATIC
VOID
EFIAPI
IoWrite32 (
IN UINT16 Port,
IN UINT32 Value
)
{
ASSERT ((Port & 3) == 0);
__outdword (Port, Value);
}
STATIC
VOID
EFIAPI
IoWrite8 (
IN UINT16 Port,
IN UINT8 Value
)
{
__outbyte (Port, Value);
}
STATIC
UINT8
EFIAPI
IoRead8 (
IN UINT16 Port
)
{
return __inbyte (Port);
}
STATIC
VOID
EFIAPI
MicroSecondDelay (
VOID
)
{
/* 1us delay via port 0x61 read */
IoRead8 (0x61);
}
/*=============================================================================
* Extended Decode Enable/Disable (thunks at 0x10D0/0x10E0)
*============================================================================*/
STATIC
VOID
EFIAPI
ExtendedDecodeEnable (
VOID
)
{
}
STATIC
VOID
EFIAPI
ExtendedDecodeDisable (
VOID
)
{
}
/*=============================================================================
* COM2 Decode Check at 0x4F94
*============================================================================*/
STATIC
BOOLEAN
EFIAPI
IsCom2Decode (
VOID
)
{
return (IoRead8 (0x61) & 0x02) != 0;
}
/*=============================================================================
* PCD Access at 0x51E8 and 0x529C
*============================================================================*/
STATIC
UINT64
EFIAPI
GetPcdValue (
UINTN PcdToken
)
{
/* EDK2 GetPcd - external implementation */
return 0;
}
STATIC
VOID
EFIAPI
PcdSetValue (
VOID
)
{
/* EDK2 SetPcd - external implementation */
}
/*=============================================================================
* UART Baud Rate Initialization at 0x273C
*============================================================================*/
STATIC
EFI_STATUS
EFIAPI
UartInitialize (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
UINT8 CmosByte;
UINT32 DesiredBaud;
UINT32 Divisor;
UINT16 UartBase;
UINT8 Lcr;
UINT8 Lsr;
UINT16 CurrentDiv;
/* Read CMOS register 0x5C for baud rate index */
IoWrite8 (CMOS_ADDR_PORT, CMOS_UART_CFG_INDEX);
CmosByte = IoRead8 (CMOS_DATA_PORT);
switch (CmosByte) {
case CMOS_BAUD_9600: DesiredBaud = 9600; break;
case CMOS_BAUD_19200: DesiredBaud = 19200; break;
case CMOS_BAUD_38400: DesiredBaud = 38400; break;
case CMOS_BAUD_57600: DesiredBaud = 57600; break;
case CMOS_BAUD_115200: DesiredBaud = 115200; break;
default: DesiredBaud = 115200; break;
}
/* Divisor = 115200 / desired baud (0x1C200 = 115200) */
Divisor = 0x1C200 / DesiredBaud;
/* Check if COM2 is selected (CmosByte = 33 = 0x21) */
if (CmosByte == CMOS_COM2_SELECT) {
UartBase = UART_BASE_COM2; /* 0x2F8 */
} else {
UartBase = UART_BASE_COM1; /* 0x3F8 */
}
UartBase = 0x3F8; /* default in practice */
/* Check current UART config */
Lcr = IoRead8 (UartBase + UART_LCR);
IoWrite8 (UartBase + UART_LCR, Lcr | 0x80);
CurrentDiv = IoRead8 (UartBase + UART_DLL) |
(IoRead8 (UartBase + UART_DLM) << 8);
if (CurrentDiv != Divisor || (Lcr & 0x3F) != 3) {
/* Wait for transmitter to be empty */
do {
Lsr = IoRead8 (UartBase + UART_LSR);
} while ((Lsr & UART_LSR_BOTH_EMPTY) != UART_LSR_BOTH_EMPTY);
/* Set DLAB, write divisor */
IoWrite8 (UartBase + UART_LCR, 0x80);
IoWrite8 (UartBase + UART_DLM, (UINT8)(Divisor >> 8));
IoWrite8 (UartBase + UART_DLL, (UINT8)(Divisor & 0xFF));
/* 8N1 mode */
IoWrite8 (UartBase + UART_LCR, 0x03);
IoWrite8 (UartBase + UART_IER, 0x00);
/* Enable FIFO */
IoWrite8 (UartBase + UART_FCR, 0x00);
IoWrite8 (UartBase + UART_FCR, 0x01);
/* No flow control */
IoWrite8 (UartBase + UART_MCR, 0x00);
}
return EFI_SUCCESS;
}
/*=============================================================================
* UEFI Boot Services Table Library Constructor at 0x1178
*============================================================================*/
EFI_STATUS
EFIAPI
UefiBootServicesTableLibConstructor (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
//
// Save singleton pointers (gImageHandle, gST, gBS, gRT)
//
gImageHandle = ImageHandle;
if (gImageHandle == NULL) {
ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
}
gSystemTable = SystemTable;
if (gSystemTable == NULL) {
ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
}
gBootServices = SystemTable->BootServices;
if (gBootServices == NULL) {
ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
}
gRuntimeServices = SystemTable->RuntimeServices;
if (gRuntimeServices == NULL) {
ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
}
gRuntimeServicesCopy = gRuntimeServices;
//
// Register protocol notify for StatusCode protocol
//
gBootServices->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
(EFI_EVENT_NOTIFY)ProtocolNotifyHandler1,
NULL,
&gBsNotifyKey
);
gBootServices->RegisterProtocolNotify (
&gEfiStatusCodeProtocolGuid,
(EFI_EVENT)ProtocolNotifyHandler1,
&gStatusCodeNotifyKey
);
//
// Initialize HOB list pointer
//
HobLibInit ();
//
// LPC bridge configuration (if not already set via PCD)
//
if ((GetPcdValue (1024068) & 0x80) == 0LL) {
UINT32 SavedCfg;
BOOLEAN IsCom2;
IsCom2 = IsCom2Decode ();
/* Read/write LPC bridge config register 0x338 (D31:F0:Reg0x338) */
SavedCfg = IoRead32 (LPC_CF8); /* Dummy? */
IoWrite32 (LPC_CF8, 0x8000FA44);
{
UINT8 CfgByte;
CfgByte = IoRead8 (LPC_CFC);
IoWrite8 (LPC_CFC, CfgByte | 0x80);
}
IoWrite32 (LPC_CF8, 0x8000FA44);
IoWrite32 (LPC_CFC, IoRead32 (LPC_CFC) | 0x80);
/* Enable or disable extended decode */
if (IsCom2) {
ExtendedDecodeDisable ();
} else {
ExtendedDecodeEnable ();
}
}
//
// Wait for UART to stabilize (poll timer counter)
//
{
UINT32 TimerBase;
UINT32 Current;
UINT32 Timeout;
/* Read HPET/mmio timer at I/O port 0x508 (= 1288 decimal) */
Current = IoRead32 (0x508) & 0xFFFFFF;
MicroSecondDelay ();
TimerBase = Current;
/* Wait ~357us */
Timeout = Current + 357 - (IoRead32 (0x508) & 0xFFFFFF);
while ((INT32)(Timeout) > 0) {
MicroSecondDelay ();
Timeout = Current + 357 - (IoRead32 (0x508) & 0xFFFFFF);
}
MicroSecondDelay ();
}
if (IsCom2Decode ()) {
ExtendedDecodeDisable ();
} else {
ExtendedDecodeEnable ();
}
return EFI_SUCCESS;
}
/*=============================================================================
* HOB Library Init at 0x3A58
*============================================================================*/
STATIC
EFI_STATUS
HobLibInit (
VOID
)
{
EFI_GUID *HobList;
UINTN HobCount;
UINTN Index;
if (gHobList != NULL) {
return EFI_SUCCESS;
}
/* The HOB list address is at SystemTable + 0x68 (HobList field) */
HobList = *(EFI_GUID **)((UINT8 *)gSystemTable + 0x68);
HobCount = 0;
gHobList = NULL;
if (HobList != NULL) {
UINTN Count;
Count = *(UINTN *)((UINT8 *)gSystemTable + 0x68 + 8);
for (Index = 0; Index < Count; Index++) {
if (CompareMem (
HobList + Index,
&gEfiHobListGuid,
sizeof (EFI_GUID)
) == 0) {
gHobList = (VOID *)&HobList[Index];
break;
}
}
}
if (gHobList == NULL) {
ASSERT_EFI_ERROR (EFI_NOT_FOUND);
}
return EFI_SUCCESS;
}
/*=============================================================================
* Protocol State Initialize at 0x3C74
*============================================================================*/
STATIC
EFI_STATUS
ProtocolStateInit (
VOID
)
{
if (gStatusCodeProtocolRegistered) {
/* Protocol was already installed; locate it */
if (gStatusCodeProtocolNotify != NULL) {
return EFI_SUCCESS;
}
if (!gStatusCodeReportingEnabled && gAmiStatusCodePolicy != NULL) {
return ((EFI_STATUS (EFIAPI *)(VOID *, UINT32, VOID **))
gAmiStatusCodePolicy)(
&gEfiStatusCodeProtocolGuid,
0,
&gStatusCodeProtocolNotify
);
}
return EFI_NOT_FOUND;
}
/* Protocol not yet registered */
if (gStatusCodeProtocolNotify != NULL) {
return EFI_SUCCESS;
}
if (gStatusCodeReportingEnabled) {
return EFI_NOT_FOUND;
}
return gBootServices->LocateProtocol (
&gEfiStatusCodeProtocolGuid,
NULL,
&gStatusCodeProtocolNotify
);
}
/*=============================================================================
* Runtime Protocol State Initialize at 0x3CFC
*============================================================================*/
STATIC
EFI_STATUS
RuntimeProtocolStateInit (
VOID
)
{
EFI_STATUS Status;
if (gStatusCodeProtocolRegistered) {
if (gStatusCodeProtocolNotify2 != NULL) {
return EFI_SUCCESS;
}
if (gAmiStatusCodePolicy == NULL) {
return EFI_NOT_FOUND;
}
Status = ((EFI_STATUS (EFIAPI *)(VOID *, UINT32, VOID **))
gAmiStatusCodePolicy)(
&gEfiStatusCodeRuntimeProtocolGuid,
0,
&gStatusCodeProtocolNotify2
);
if (EFI_ERROR (Status)) {
gStatusCodeProtocolNotify2 = NULL;
}
return Status;
}
if (gStatusCodeProtocolNotify2 != NULL) {
return EFI_SUCCESS;
}
if (gStatusCodeReportingEnabled) {
return EFI_NOT_FOUND;
}
{
UINT32 Alignment;
/* EFI_BOOT_SERVICES.CheckBuffer at offset +24 */
Alignment = ((UINT32 (EFIAPI *)(UINT32))(
*(UINTN *)((UINTN)gBootServices + 24)
))(31);
if (Alignment > 0x10) {
return EFI_OUT_OF_RESOURCES;
}
Status = gBootServices->LocateProtocol (
&gEfiStatusCodeRuntimeProtocolGuid,
NULL,
&gStatusCodeProtocolNotify2
);
if (EFI_ERROR (Status)) {
gStatusCodeProtocolNotify2 = NULL;
}
}
return Status;
}
/*=============================================================================
* Protocol Notify Handler 1 at 0x3E38
*============================================================================*/
EFI_STATUS
EFIAPI
ProtocolNotifyHandler1 (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
gStatusCodeReportingEnabled = 1;
if (Context != NULL) {
Status = ((EFI_STATUS (EFIAPI *)(EFI_EVENT, VOID *))Context)(Event, NULL);
}
gStatusCodeEnableCount--;
if (gStatusCodeEnableCount == 0) {
gBootServices = NULL;
gRuntimeServicesCopy = gRuntimeServices;
}
return EFI_SUCCESS;
}
/*=============================================================================
* Protocol Notify Handler 2 at 0x3E74 - DxeSmmReadyToLock
*============================================================================*/
EFI_STATUS
EFIAPI
ProtocolNotifyHandler2 (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
if (Context != NULL) {
Status = ((EFI_STATUS (EFIAPI *)(EFI_EVENT, VOID *))Context)(Event, NULL);
}
gStatusCodeDisableCount--;
if (gStatusCodeDisableCount == 0) {
/* Convert boot-service pointers to runtime pointers */
gRuntimeServicesCopy->ConvertPointer (0, (VOID **)&gSystemTable);
gRuntimeServicesCopy->ConvertPointer (0, (VOID **)&gRuntimeServices);
if (gStatusCodeProtocolNotify != NULL) {
gRuntimeServicesCopy->ConvertPointer (
0, (VOID **)&gStatusCodeProtocolNotify
);
}
if (gStatusCodeProtocolNotify2 != NULL) {
gRuntimeServicesCopy->ConvertPointer (
0, (VOID **)&gStatusCodeProtocolNotify2
);
}
}
return EFI_SUCCESS;
}
/*=============================================================================
* EFI STATUS_CODE_PROTOCOL: Register at 0x139C
*============================================================================*/
EFI_STATUS
EFIAPI
RegisterStatusCodeListener (
IN EFI_STATUS_CODE_PROTOCOL_ReportStatusCode Callback,
IN UINT32 Tpl
)
{
UINTN Index;
STATUS_CODE_ROUTING_ENTRY *Entry;
UINT32 Count;
EFI_STATUS Status;
if (Callback == NULL) {
return EFI_INVALID_PARAMETER;
}
if (gStatusCodeDatabase == NULL) {
return EFI_NOT_STARTED;
}
Count = gStatusCodeDatabase->Count;
Entry = &gStatusCodeDatabase->Entries[0];
/* Search for existing or free slot */
for (Index = 0; Index < Count; Index++) {
if (Entry[Index].Callback == (UINT64)(UINTN)Callback) {
return EFI_ALREADY_STARTED;
}
if (Entry[Index].Callback == 0 && Entry == &Entry[Index]) {
Entry = &Entry[Index];
goto FOUND_SLOT;
}
}
if (Count >= gStatusCodeDatabase->MaxCount) {
return EFI_OUT_OF_RESOURCES;
}
Entry = &gStatusCodeDatabase->Entries[Count];
gStatusCodeDatabase->Count++;
FOUND_SLOT:
Entry->Callback = (UINT64)(UINTN)Callback;
Entry->Tpl = Tpl;
if (Tpl != 31 && !gReEntryBypass) {
/* Allocate 4KB deferred buffer */
VOID *Buffer;
Status = gBootServices->AllocatePool (
EfiBootServicesData,
4096,
&Buffer
);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
}
Entry->Buffer = (UINT64)(UINTN)Buffer;
Entry->BufferPtr = (UINT64)(UINTN)Buffer;
Entry->BufferSize = 4096;
/* Create timer event for periodic flush */
Status = gBootServices->CreateEvent (
EVT_TIMER | EVT_NOTIFY_SIGNAL,
Tpl,
DeferredEntryFlush,
Entry,
&Entry->Event
);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
}
}
return EFI_SUCCESS;
}
/*=============================================================================
* Unregister Status Code Listener at 0x1514
*============================================================================*/
EFI_STATUS
EFIAPI
UnregisterStatusCodeListener (
IN EFI_STATUS_CODE_PROTOCOL_ReportStatusCode Callback
)
{
UINTN Index;
if (Callback == NULL) {
return EFI_INVALID_PARAMETER;
}
if (gStatusCodeDatabase == NULL) {
return EFI_NOT_FOUND;
}
for (Index = 0; Index < gStatusCodeDatabase->Count; Index++) {
STATUS_CODE_ROUTING_ENTRY *Entry = &gStatusCodeDatabase->Entries[Index];
if (Entry->Callback == (UINT64)(UINTN)Callback) {
if (Entry->Tpl != 31) {
if (Entry->Buffer != 0) {
gBootServices->FreePool ((VOID *)(UINTN)Entry->Buffer);
gBootServices->CloseEvent ((EFI_EVENT)(UINTN)Entry->Event);
}
}
/* Zero out the entry */
gBootServices->SetMem (Entry, sizeof (*Entry), 0);
return EFI_SUCCESS;
}
}
return EFI_NOT_FOUND;
}
/*=============================================================================
* Deferred Entry Flush at 0x1338
*============================================================================*/
VOID
EFIAPI
DeferredEntryFlush (
IN EFI_EVENT Event,
IN VOID *Context
)
{
STATUS_CODE_ROUTING_ENTRY *Listener;
UINT32 *Entry;
Listener = (STATUS_CODE_ROUTING_ENTRY *)Context;
/* Walk the deferred buffer */
Entry = (UINT32 *)(UINTN)Listener->Buffer;
gBufferOverflowFlag = 1;
while ((UINTN)Entry < (UINTN)Listener->BufferPtr) {
/* Each buffer entry: Type(4), Value(4), Instance(4), CallerId(16),
* total 52 bytes header per entry. */
/* Call the callback directly */
((EFI_STATUS_CODE_PROTOCOL_ReportStatusCode)(UINTN)Listener->Callback)(
Entry[0], /* Type */
Entry[1], /* Value */
Entry[2], /* Instance */
(EFI_GUID *)&Entry[3], /* CallerId */
(EFI_STATUS_CODE_DATA *)&Entry[7] /* ExtendedData */
);
/* Advance past header: *a6[1] + *a6 + 52 */
Entry = (UINT32 *)((UINT8 *)Entry + 52);
}
Listener->BufferPtr = Listener->Buffer;
gBufferOverflowFlag = 0;
}
/*=============================================================================
* Dispatch to All Listeners at 0x15AC
*============================================================================*/
EFI_STATUS
DispatchToListeners (
IN EFI_STATUS_CODE_TYPE Type,
IN EFI_STATUS_CODE_VALUE Value,
IN UINT32 Instance,
IN EFI_GUID *CallerId OPTIONAL,
IN EFI_STATUS_CODE_DATA *Data OPTIONAL
)
{
STATUS_CODE_ROUTING_ENTRY *Entry;
UINT32 Index;
UINT32 Count;
UINTN CurrentTpl;
if (gStatusCodeDatabase == NULL) {
return EFI_NOT_FOUND;
}
/* Re-entrancy guard */
if (gDeferredLockFlag) {
if (gDeferredLockFlag != 1) {
return EFI_NOT_READY;
}
return EFI_NOT_READY;
}
gDeferredLockFlag = 1;
Count = gStatusCodeDatabase->Count;
Entry = &gStatusCodeDatabase->Entries[0];
for (Index = 0; Index < Count; Index++) {
if (Entry[Index].Callback == 0) {
continue;
}
if (!gReEntryBypass) {
CurrentTpl = gBootServices->RaiseTPL (TPL_HIGH_LEVEL);
gBootServices->RestoreTPL (CurrentTpl);
/* Check: if enough TPL, call directly; else buffer */
if (CurrentTpl >= Entry[Index].Tpl || Entry[Index].Tpl == 31) {
((EFI_STATUS_CODE_PROTOCOL_ReportStatusCode)(UINTN)Entry[Index].Callback)(
Type, Value, Instance, CallerId, Data
);
} else {
/* Buffer the entry for deferred delivery */
UINT32 *BufEntry;
BufEntry = (UINT32 *)(UINTN)Entry[Index].BufferPtr;
/* Calculate buffer entry size */
BufEntry[0] = Type; /* 4 bytes */
BufEntry[1] = Value; /* 4 bytes */
BufEntry[2] = Instance; /* 4 bytes */
if (CallerId) {
CopyMem (&BufEntry[3], CallerId, sizeof (EFI_GUID));
}
if (Data) {
/* Copy extended data header */;
}
Entry[Index].BufferPtr =
(UINT64)((UINTN)BufEntry + 52);
}
} else {
/* Bypass mode: call directly */
((EFI_STATUS_CODE_PROTOCOL_ReportStatusCode)(UINTN)Entry[Index].Callback)(
Type, Value, Instance, CallerId, Data
);
}
}
gDeferredLockFlag = 0;
return EFI_SUCCESS;
}
/*=============================================================================
* Report Status Code Dispatcher at 0x182C
*============================================================================*/
EFI_STATUS
EFIAPI
ReportStatusCodeDispatcher (
IN EFI_STATUS_CODE_TYPE Type,
IN EFI_STATUS_CODE_VALUE Value,
IN UINT32 Instance,
IN EFI_GUID *CallerId OPTIONAL,
IN EFI_STATUS_CODE_DATA *Data OPTIONAL
)
{
UINTN Index;
/* Step 1: Dispatch to all registered listeners */
DispatchToListeners (Type, Value, Instance, CallerId, Data);
/* Step 2: Format status string to display buffer */
gDisplayBuffer[0] = 0;
FormatStatusString (Type, Value, Data, gDisplayBuffer);
if (gDisplayBuffer[0] != 0) {
/* Step 3: Broadcast to output consumers */
OutputStringToConsumers (gDisplayBuffer);
}
/* Step 4: Iterate general notifiers */
for (Index = 0; gGeneralNotifiers[Index] != NULL; Index++) {
((VOID (*)(UINT64, UINT32, UINT32))gGeneralNotifiers[Index])(
0, (UINT32)Type, Value
);
}
/* Step 5: Check exception handler tables for matching values */
if ((UINT32)Type == 2) { /* EFI_STATUS_CODE_TYPE_ERROR? */
/* Actually: if Type == 2 (DEBUG), look up handler table */
EXCEPTION_HANDLER_ENTRY *Table;
Table = gReEntryBypass ?
gExceptionHandlers : gSerialExceptionHandlers;
while (Table->StatusCodeValue != 0) {
if (Table->StatusCodeValue == Value && Table->Handler != NULL) {
Table->Handler (0, Value);
return EFI_SUCCESS;
}
Table++;
}
}
return EFI_SUCCESS;
}
/*=============================================================================
* Format Status String at 0x24FC
*============================================================================*/
VOID
FormatStatusString (
IN EFI_STATUS_CODE_TYPE Type,
IN EFI_STATUS_CODE_VALUE Value,
IN EFI_STATUS_CODE_DATA *Data OPTIONAL,
OUT CHAR8 *Buffer
)
{
UINT16 Index;
CHAR8 *Str;
INTN StrLen;
Index = 0;
if (Data == NULL) {
goto DEFAULT_FORMAT;
}
/* Check CallerId against known data type GUIDs */
/* GUID at 0x8060: gEfiStatusCodeDataTypeDebugGuid */
/* DEBUG data: contains ASSERT file, line, expression */
if (CompareGuid (
(EFI_GUID *)&Data->Type,
&gEfiStatusCodeDataTypeDebugGuid
)) {
Str = (CHAR8 *)((UINTN)Data + 120); /* Offset to assert file name */
StrLen = AsciiStrLen (Str);
if (StrLen > 0) {
AsciiSPrint (
Buffer,
0x800,
"ASSERT in %a on %i: %a\n",
&Data->CallerId,
*((UINT32 *)((UINT8 *)Data + 0x28)),
Str + StrLen + 1
);
return;
}
}
/* GUID at 0x80B0: gEfiStatusCodeDataTypeCodeGuid */
/* Progress code data */
if (CompareGuid (
(EFI_GUID *)&Data->Type,
&gEfiStatusCodeDataTypeCodeGuid
)) {
/* Check data type field */
if (*((UINT32 *)((UINT8 *)Data + 20)) != 0) {
/* ASCII string data */
if (*((UINT32 *)((UINT8 *)Data + 20)) == 1) {
CHAR8 *RawStr;
RawStr = (CHAR8 *)((UINTN)Data + 24);
if (*RawStr != 0) {
AsciiSPrint (
Buffer,
0x800,
"%a",
RawStr
);
return;
}
}
}
}
/* GUID at 0x8070: gEfiStatusCodeDataTypeErrorGuid */
if (CompareGuid (
(EFI_GUID *)&Data->Type,
&gEfiStatusCodeDataTypeErrorGuid
)) {
/* Convert Unicode to ASCII, then format */
UINT16 *WStr;
WStr = (UINT16 *)((UINTN)Data + 24);
/* Check if Unicode vs ASCII */
if (*((UINT32 *)((UINT8 *)Data + 20)) == 0) {
/* ASCII string */
AsciiSPrint (
Buffer,
0x800,
"%a",
(CHAR8 *)WStr
);
return;
} else {
/* Unicode: convert each byte */
Index = 0;
while (*WStr != 0 && Index < 0x7FF) {
if (*((UINT32 *)((UINT8 *)Data + 20)) != 0) {
/* Wide char */
Buffer[Index++] = (CHAR8)*WStr;
WStr += 2;
} else {
Buffer[Index++] = *(CHAR8 *)WStr;
WStr += 1;
}
}
Buffer[Index] = 0;
return;
}
}
DEFAULT_FORMAT:
/* Default format: only for Type 2 (DEBUG) */
if ((UINT32)Type != 2) {
return;
}
/* CPU exception format */
if ((Value & 0xFFFF0000) == 0x30D0000 ||
(Value & 0xFFFF0000) == 0x3130000) {
if ((Value & 0xFFFF) < 20) {
AsciiSPrint (
Buffer,
0x800,
"ERROR: CPU exception %X(%a)\n",
Value & 0xFFFF,
gCpuExceptionNames[Value & 0xFFFF]
);
} else {
AsciiSPrint (
Buffer,
0x800,
"ERROR: CPU exception %X(%a)\n",
Value & 0xFFFF,
"Unknown"
);
}
} else {
UINTN MapIndex;
/* Try to look up error string in the mapping table */
for (MapIndex = 0; MapIndex < 25; MapIndex++) {
if (gStatusCodeStringMap[MapIndex].TypeValue == Value) {
AsciiSPrint (
Buffer,
0x800,
"ERROR: %a\n",
gStatusCodeStringMap[MapIndex].String
);
return;
}
}
/* Fallback: class/subclass/operation format */
AsciiSPrint (
Buffer,
0x800,
"ERROR: Class:%X; Subclass:%X; Operation: %X\n",
Value & 0xFF000000,
Value & 0xFF0000,
(UINT16)Value
);
}
}
/*=============================================================================
* Output String to Consumers at 0x2850
*============================================================================*/
VOID
OutputStringToConsumers (
IN CHAR8 *String
)
{
UINTN Index;
for (Index = 0; gConsoleStringCallbacks[Index] != NULL; Index++) {
((VOID (*)(UINT64, CHAR8 *))gConsoleStringCallbacks[Index])(
0, String
);
}
}
/*=============================================================================
* Build Callback Tables at 0x1B18
*============================================================================*/
VOID
EFIAPI
BuildCallbackTables (
VOID
)
{
UINTN Index;
gReEntryBypass = 1;
/* Copy string output handlers -> console string callbacks */
Index = 0;
if (gStringOutputHandlers[0] != NULL) {
do {
gConsoleStringCallbacks[Index] = gStringOutputHandlers[Index];
Index++;
} while (gStringOutputHandlers[Index] != NULL);
}
gConsoleStringCallbacks[Index] = NULL;
/* Copy serial output handlers -> runtime listeners */
Index = 0;
if (gSerialOutputHandlers[0] != NULL) {
do {
gRuntimeStatusCodeListeners[Index] = gSerialOutputHandlers[Index];
Index++;
} while (gSerialOutputHandlers[Index] != NULL);
}
gRuntimeStatusCodeListeners[Index] = NULL;
/* Copy progress code callbacks */
Index = 0;
if (/* gProgressCodeCallbacks[0] */ NULL != NULL) {
do {
/* not populated in this implementation */;
Index++;
} while (0);
}
/* ... */
}
/*=============================================================================
* Register Status Code Protocol Notify at 0x23C8
*============================================================================*/
EFI_STATUS
EFIAPI
RegisterStatusCodeProtocolNotify (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
VOID *Registration;
Status = gBootServices->RegisterProtocolNotify (
&gEfiStatusCodeProtocolGuid,
(EFI_EVENT)RegisterProgressHandlers,
&Registration
);
if (Status >= 0) {
gBootServices->CloseEvent (Registration);
}
return Status;
}
/*=============================================================================
* Register Progress Handlers at 0x2304
*============================================================================*/
VOID
EFIAPI
RegisterProgressHandlers (
IN EFI_HANDLE ImageHandle
)
{
if (gVgaClassProtocol == NULL) {
gBootServices->LocateProtocol (
&gAmiVgaClassProtocolGuid,
NULL,
&gVgaClassProtocol
);
gBootServices->LocateProtocol (
&gEfiSerialIoProtocolGuid,
NULL,
&gSerialPortProtocol
);
if (gBootServices->LocateProtocol (
&gEfiStatusCodeProtocolGuid,
NULL,
&gVgaClassProtocol /* re-use slot for protocol handle */
) >= 0) {
gBootServices->CloseEvent (ImageHandle);
} else {
gSerialPortProtocol = NULL;
}
gBootServices->SetTimer (
(EFI_EVENT)ImageHandle,
TimerPeriodic,
10000000 /* 1 second */
);
}
}
/*=============================================================================
* Register Console Listener at 0x2174
*============================================================================*/
EFI_STATUS
EFIAPI
RegisterConsoleListener (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
VOID *Registration;
VOID *NotifyHandle;
Status = gBootServices->RegisterProtocolNotify (
&gEfiGraphicsOutputProtocolGuid,
(EFI_EVENT)ProtocolLazyInit,
&Registration
);
if (Status >= 0) {
gBootServices->CloseEvent (Registration);
}
return Status;
}
/*=============================================================================
* Lazy Protocol Init Callback at 0x211C
*============================================================================*/
VOID
EFIAPI
ProtocolLazyInit (
IN EFI_HANDLE ImageHandle
)
{
if (gBmcProtocol == NULL) {
if (gBootServices->LocateProtocol (
&gAmiBmcProtocolGuid,
NULL,
&gBmcProtocol
) >= 0) {
gBootServices->CloseEvent (ImageHandle);
} else {
gBmcProtocol = NULL;
}
}
}
/*=============================================================================
* EntryPoint at 0x1DCC
*============================================================================*/
EFI_STATUS
EFIAPI
StatusCodeDxeEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_HANDLE NewHandle;
/*-----------------------------------------------------------------
* Save global system table pointers
*-----------------------------------------------------------------*/
if (gSystemTable == NULL) {
gSystemTable = SystemTable;
gBootServices = SystemTable->BootServices;
gRuntimeServices = SystemTable->RuntimeServices;
}
/*-----------------------------------------------------------------
* Initialize protocol state from boot HOBs / LocateProtocol
*-----------------------------------------------------------------*/
ProtocolStateInit ();
/*-----------------------------------------------------------------
* Initialize runtime protocol state
*-----------------------------------------------------------------*/
RuntimeProtocolStateInit ();
/*-----------------------------------------------------------------
* Increment registration refcounts (so ProtocolNotify can
* detect when registration is complete)
*-----------------------------------------------------------------*/
gStatusCodeEnableCount++;
gStatusCodeDisableCount++;
/*-----------------------------------------------------------------
* Register protocol notify for StatusCode protocol
*-----------------------------------------------------------------*/
gBootServices->RegisterProtocolNotify (
&gEfiStatusCodeProtocolGuid,
(EFI_EVENT)ProtocolNotifyHandler1,
&gStatusCodeNotifyKey
);
/*-----------------------------------------------------------------
* Register protocol notify for DxeSmmReadyToLock
*-----------------------------------------------------------------*/
gBootServices->RegisterProtocolNotify (
&gEfiStatusCodeDataGuid,
(EFI_EVENT)ProtocolNotifyHandler2,
&gBsNotifyKey
);
/*-----------------------------------------------------------------
* Dispatch boot init sub-functions:
* 0) UartInitialize
* 1) RegisterStatusCodeProtocolNotify
* 2) RegisterConsoleListener
*-----------------------------------------------------------------*/
{
UINTN Index;
for (Index = 0; gBootInitFunctions[Index] != NULL; Index++) {
gBootInitFunctions[Index] (ImageHandle, SystemTable);
}
}
/*-----------------------------------------------------------------
* Allocate status code database / listener pool
*-----------------------------------------------------------------*/
{
UINTN PoolSize;
PoolSize = sizeof (STATUS_CODE_DATABASE) +
15 * sizeof (STATUS_CODE_ROUTING_ENTRY);
Status = gBootServices->AllocatePool (
EfiBootServicesData,
PoolSize,
(VOID **)&gStatusCodeDatabase
);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
}
gBootServices->SetMem (
gStatusCodeDatabase,
PoolSize,
0
);
gStatusCodeDatabase->Count = 0;
gStatusCodeDatabase->MaxCount = 15;
}
/*-----------------------------------------------------------------
* Install EFI_STATUS_CODE_PROTOCOL via InstallProtocolInterface
* This makes "ReportStatusCodeDispatcher" available as the
* protocol's ReportStatusCode service.
*-----------------------------------------------------------------*/
{
NewHandle = NULL;
Status = gBootServices->InstallProtocolInterface (
&NewHandle,
&gEfiStatusCodeProtocolGuid,
EFI_NATIVE_INTERFACE,
(VOID *)ReportStatusCodeDispatcher
);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
}
/* Store function pointers for the protocol interface */
gStatusCodeProtocolNotify = (VOID *)RegisterStatusCodeListener;
gStatusCodeProtocolNotify2 = (VOID *)UnregisterStatusCodeListener;
}
return EFI_SUCCESS;
}
/*=============================================================================
* Module Entry Point at 0x1120
*============================================================================*/
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
/*
* Step 1: UEFI Boot Services Table Library init.
* Save gImageHandle, gST, gBS, gRT.
* Initialize HOB list, UART, LPC decode, timer wait.
*/
Status = UefiBootServicesTableLibConstructor (ImageHandle, SystemTable);
/*
* Step 2: Main initialization.
* Install EFI_STATUS_CODE_PROTOCOL, allocate listener pool,
* dispatch sub-functions.
*/
Status = StatusCodeDxeEntryPoint (ImageHandle, SystemTable);
if (EFI_ERROR (Status)) {
/*
* On failure, close the protocol notify events.
*/
gBootServices->CloseEvent ((EFI_EVENT)gStatusCodeNotifyKey);
gBootServices->CloseEvent ((EFI_EVENT)gBsNotifyKey);
}
return Status;
}
/*=============================================================================
* Unregister All Callbacks at 0x194C - on ExitBootServices
*============================================================================*/
STATIC
EFI_STATUS
UnregisterAllCallbacks (
VOID
)
{
UINTN Index;
/* Convert all boot-service callback pointers to RT */
for (Index = 0; gRuntimeStatusCodeListeners[Index] != NULL; Index++)
gRuntimeServices->ConvertPointer (0, (VOID **)&gRuntimeStatusCodeListeners[Index]);
for (Index = 0; gConsoleStringCallbacks[Index] != NULL; Index++)
gRuntimeServices->ConvertPointer (0, (VOID **)&gConsoleStringCallbacks[Index]);
for (Index = 0; gStatusCodeCallbacks[Index] != NULL; Index++)
gRuntimeServices->ConvertPointer (0, (VOID **)&gStatusCodeCallbacks[Index]);
for (Index = 0; gGeneralNotifiers[Index] != NULL; Index++)
gRuntimeServices->ConvertPointer (0, (VOID **)&gGeneralNotifiers[Index]);
for (Index = 0; gBootInitFunctions[Index] != NULL; Index++)
gRuntimeServices->ConvertPointer (0, (VOID **)&gBootInitFunctions[Index]);
/* Convert listener entries */
if (gStatusCodeDatabase != NULL) {
for (Index = 0; Index < gStatusCodeDatabase->Count; Index++) {
STATUS_CODE_ROUTING_ENTRY *Entry;
Entry = &gStatusCodeDatabase->Entries[Index];
if (Entry->Callback != 0) {
if (Entry->Tpl != 31 && Entry->Buffer != 0) {
gRuntimeServices->ConvertPointer (
0, (VOID **)&Entry->Buffer
);
}
}
}
gRuntimeServices->ConvertPointer (
0, (VOID **)&gStatusCodeDatabase
);
}
return EFI_SUCCESS;
}
/*=============================================================================
* Progress Update Handler at 0x1C80
*============================================================================*/
VOID
EFIAPI
ProgressUpdate (
IN UINTN Value,
IN UINT8 *Data OPTIONAL,
IN UINT16 *String OPTIONAL
)
{
UINTN Index;
if (Data != NULL) {
/* Format progress string */
AsciiSPrint (
gDisplayBuffer,
sizeof (gDisplayBuffer),
"%a",
Data
);
gLastReportedValue = (UINT32)Value;
/* Report as progress status code */
ReportStatusCodeDispatcher (
EFI_STATUS_CODE_TYPE_PROGRESS,
0x03060000 | (Value & 0xFFFF),
0,
NULL,
NULL
);
/* Notify OEM callbacks */
for (Index = 0; /* gOemCodeCallbacks[Index] */ NULL != NULL; Index++) {
((VOID (*)(CHAR8 *, UINTN))(void *)NULL)(gDisplayBuffer, Value);
}
/* Output to all consumers */
OutputStringToConsumers (gDisplayBuffer);
}
}
/*=============================================================================
* ASSERT Update Handler at 0x1D20
*============================================================================*/
VOID
EFIAPI
AssertUpdate (
IN UINTN FileName,
IN UINTN LineNumber,
IN UINTN Expression
)
{
UINTN Index;
/* Format the ASSERT string */
AsciiSPrint (
gDisplayBuffer,
sizeof (gDisplayBuffer),
"ASSERT %a(%d): %a\n Press any key to continue. \n",
FileName,
LineNumber,
Expression
);
gLastReportedValue = 0x80000000;
/* Report as error status code */
ReportStatusCodeDispatcher (
EFI_STATUS_CODE_TYPE_ERROR,
0x03060000 | 0x07,
0,
NULL,
NULL
);
/* Notify debug callbacks */
for (Index = 0; /* gDebugCallbacks[Index] */ NULL != NULL; Index++) {
((VOID (*)(CHAR8 *, UINTN, UINTN))(void *)NULL)(
gDisplayBuffer, FileName, LineNumber
);
}
/* Output to consumers */
OutputStringToConsumers (gDisplayBuffer);
}
/*=============================================================================
* ASSERT DEBUG Support at 0x2AF0/0x2A70
*============================================================================*/
VOID
EFIAPI
DebugAssert (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *Description
)
{
/* Called from ASSERT_EFI_ERROR macro (25 callers in this module) */
VOID (*DebugPrint)(UINT64, CONST CHAR8 *, ...);
/* Get debug print function from platform */
DebugPrint = (VOID (*)(UINT64, CONST CHAR8 *, ...))(UINTN)0;
if (DebugPrint != NULL) {
DebugPrint (
0x80000000LL,
"\nASSERT_EFI_ERROR (Status = %r)\n",
Description
);
}
}
/*=============================================================================
* Serial Output String at 0x5168
*============================================================================*/
CHAR8
EFIAPI
SerialOutputString (
IN UINT8 Row,
IN UINT8 Col,
IN CHAR8 Attribute,
IN CONST CHAR8 *String
)
{
UINTN Length;
CHAR8 *VgaPos;
UINTN Index;
CHAR8 LastChar;
Length = AsciiStrLen (String);
VgaPos = (CHAR8 *)(2 * (Col + 80 * Row) + VGA_BASE);
/* Disable cursor */
IoWrite8 (VGA_CRTC_ADDR, VGA_CRTC_CURSOR_START);
IoWrite8 (VGA_CRTC_DATA, 0x20);
LastChar = ' ';
for (Index = 0; Index < Length; Index++) {
LastChar = String[Index];
/* Write to VGA text buffer */
VgaPos[0] = LastChar;
VgaPos[1] = Attribute;
/* Write to serial UART */
IoWrite8 (UART_BASE_COM1 + UART_THR, LastChar);
/* Dummy read to flush */
IoRead8 (UART_BASE_COM1 + UART_RBR);
VgaPos += 2;
}
return LastChar;
}
/*=============================================================================
* BMC Progress Display at 0x4C14
*============================================================================*/
CHAR8
EFIAPI
BmcProgressDisplay (
IN UINT8 ProgressCode
)
{
CHAR8 Buffer[88];
/* Format string: "Status Code: %X" */
AsciiSPrint (Buffer, sizeof (Buffer), "Status Code: %X", ProgressCode);
/* Output to VGA/serial at specific row */
return SerialOutputString (0x18, 0x3E, 7, Buffer);
}
/*=============================================================================
* AsciiSPrint at 0x2C3C - from BasePrintLib
*
* Full vsnprintf implementation, ~3573 bytes code.
* Supports %s, %S, %a, %c, %d, %i, %x, %X, %p, %r, %g, %G
* width/padding, zero-padding, long prefix.
*
* The "%r" handler renders EFI_STATUS values as human-readable strings
* (e.g., "Success", "Invalid Parameter", "Not Found").
*
* Referenced files:
* e:\hs\MdePkg\Library\BasePrintLib\PrintLibInternal.c
* e:\hs\MdePkg\Library\BaseLib\String.c
* e:\hs\MdePkg\Library\BaseLib\Unaligned.c
*============================================================================*/
/*=============================================================================
* End of StatusCodeDxe.c
*============================================================================*/