/**
* DxeFrb.c -- FRB (Fault Resilient Boot) DXE Driver
* Lenovo HR650X UEFI BIOS
*
* Manages the hardware FRB2 watchdog timer and OS boot watchdog timer
* through the WCHG (Watchdog Chip) accessed via CMOS I/O ports 0x70/0x71.
*
* FRB2: Fault Resilient Boot Phase 2 timer. During boot phases where the
* system could hang (option ROM init, password prompt, setup), FRB2 is
* temporarily disabled. It is re-enabled after each critical phase completes.
*
* OS Boot WDT: A separate watchdog that supervises the OS boot process.
* It is disabled before entering the UEFI Shell.
*
* Debug output uses the WCHG debug protocol (accessed via GetDebugProtocol).
*/
#include "DxeFrb.h"
// ============================================================================
// Globals
// ============================================================================
EFI_HANDLE gImageHandle = NULL;
EFI_SYSTEM_TABLE *gST = NULL;
EFI_BOOT_SERVICES *gBS = NULL;
EFI_RUNTIME_SERVICES *gRT = NULL;
VOID *gWatchdogProtocol; // qword_2110 -- EFI_WATCHDOG_TIMER_ARCH_PROTOCOL
VOID *gHobList; // qword_1CC8 -- HOB list pointer
FRB2_DRIVER *gFrb2Driver; // qword_1CD8 -- Allocated driver instance
VOID *gDebugOutputProtocol; // qword_1CC0 -- WCHG debug protocol
BOOLEAN gFrb2Enabled = FALSE; // byte_1CE6
BOOLEAN gOsWdtEnabled = FALSE; // byte_1CEB
BOOLEAN gOsWdtArmed = FALSE; // byte_1CEE
UINT16 gOsWdtTimeout = 600; // n600 (600 * 100ms = 60s)
BOOLEAN gOpromDone = FALSE; // byte_1CD1
//
// WCHG timer state storage -- 3 timers * 6 bytes each (at 0x2120)
//
UINT8 gWchgTimerState[0x30];
//
// GUIDs used by this module
//
EFI_GUID gEfiWatchdogTimerArchProtocolGuid = EFI_WATCHDOG_TIMER_ARCH_PROTOCOL_GUID;
EFI_GUID gEfiEventReadyToBootGuid = EFI_EVENT_GROUP_READY_TO_BOOT;
// ============================================================================
// Internal Helpers
// ============================================================================
/**
* Acquire the WCHG debug output protocol.
* Allocates a small pool buffer then locates the protocol.
*
* The protocol provides:
* +0x00: DebugPrint (variadic printf-like debug output)
* +0x08: DebugAssert (file/line/desc assert handler)
*/
VOID *
GetDebugProtocol (
VOID
)
{
EFI_STATUS Status;
VOID *Protocol;
VOID *Buffer;
if (gDebugOutputProtocol != NULL) {
return gDebugOutputProtocol;
}
Status = gBS->AllocatePool (EfiBootServicesData, 0x10, &Buffer);
if (EFI_ERROR (Status)) {
return NULL;
}
Status = gBS->LocateProtocol (&gEfiDebugProtocolGuid, NULL, &Protocol);
if (EFI_ERROR (Status)) {
Protocol = NULL;
}
gDebugOutputProtocol = Protocol;
return Protocol;
}
/**
* Debug print via WCHG debug protocol.
* Checks debug level mask against a CMOS debug level register at
* CMOS index 0x4B (port 0x70/0x71). Levels: 1=ERROR, 2=WARN, 4=INFO.
*/
VOID
EFIAPI
DebugPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
VA_LIST Marker;
VOID *DebugProto;
UINT8 DebugLevel;
UINT8 SavedCMOSIndex;
UINT64 AllowedLevels;
DEBUG_PRINT *PrintFunc;
DebugProto = GetDebugProtocol ();
if (DebugProto == NULL) {
return;
}
//
// Read CMOS index 0x4B to get current debug level
//
SavedCMOSIndex = IoRead8 (0x70);
IoWrite8 (0x70, (SavedCMOSIndex & 0x80) | 0x4B);
DebugLevel = IoRead8 (0x71);
//
// Normalize debug level
//
if (DebugLevel > 3) {
if (DebugLevel == 0) {
DebugLevel = (*(UINT8 *)(UINTN)0xFDAF0490 & 2) | 1;
}
}
switch (DebugLevel) {
case 1: AllowedLevels = DEBUG_ERROR; break;
case 2: AllowedLevels = DEBUG_WARN; break;
case 4: AllowedLevels = DEBUG_INFO; break;
default:
// 0 or 3 => ERROR | WARN
AllowedLevels = DEBUG_ERROR | DEBUG_WARN;
break;
}
if ((AllowedLevels & ErrorLevel) == 0) {
return;
}
VA_START (Marker, Format);
PrintFunc = (DEBUG_PRINT *)((UINTN)DebugProto + 0);
PrintFunc (ErrorLevel, Format, Marker);
VA_END (Marker);
}
/**
* Debug assert via WCHG debug protocol.
*/
VOID
EFIAPI
DebugAssert (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *Description
)
{
VOID *DebugProto;
DEBUG_ASSERT *AssertFunc;
DebugProto = GetDebugProtocol ();
if (DebugProto == NULL) {
return;
}
AssertFunc = (DEBUG_ASSERT *)((UINTN)DebugProto + 8);
AssertFunc (FileName, LineNumber, Description);
}
/**
* Read 8 bytes from an unaligned pointer (BaseLib Unaligned.c).
*/
UINT64
ReadUnaligned64 (
IN CONST VOID *Buffer
)
{
if (Buffer == NULL) {
DEBUG_ASSERT (FALSE, "Buffer != NULL");
}
return *(UINT64 *)Buffer;
}
/**
* Compare two GUIDs as two UINT64 values.
*/
BOOLEAN
CompareGuid (
IN CONST EFI_GUID *Guid1,
IN CONST EFI_GUID *Guid2
)
{
return (ReadUnaligned64 (Guid1) == ReadUnaligned64 (Guid2)) &&
(ReadUnaligned64 ((UINT8 *)Guid1 + 8) == ReadUnaligned64 ((UINT8 *)Guid2 + 8));
}
/**
* Locate the HOB list from the SystemTable HOB pointer array.
*
* SystemTable + 0x68 (104) = number of HOB entries
* SystemTable + 0x70 (112) = array of {GUID_lo, GUID_hi, pointer} entries
*/
EFI_STATUS
GetHobList (
OUT VOID **HobList
)
{
UINTN Index;
UINTN HobCount;
UINTN *HobBase;
if (HobList == NULL) {
DEBUG_ASSERT (FALSE, "Table != NULL");
return EFI_INVALID_PARAMETER;
}
*HobList = NULL;
HobCount = *(UINTN *)((UINT8 *)gST + 0x68);
if (HobCount == 0) {
return EFI_NOT_FOUND;
}
HobBase = *(UINTN **)((UINT8 *)gST + 0x70);
for (Index = 0; Index < HobCount; Index++) {
if (CompareGuid (
(EFI_GUID *)(HobBase + Index * 3),
(EFI_GUID *)(HobBase + Index * 3 + 1)
)) {
*HobList = (VOID *)(HobBase[Index * 3 + 2]);
return EFI_SUCCESS;
}
}
return EFI_NOT_FOUND;
}
// ============================================================================
// WCHG Hardware Access Layer
// ============================================================================
/**
* WchgGetState -- Read FRB2/OSWDT hardware timer state from WCHG.
*
* TimerIndex: 1=FRB2, 2=reserved, 3=OS Boot WDT.
* Read FRB2 or OS watchdog state from the WCHG hardware.
*/
EFI_STATUS
EFIAPI
WchgGetState (
IN FRB2_DRIVER *This,
IN UINTN TimerIndex,
OUT WCHG_HW_STATE *State
)
{
UINT8 WchgData[6];
UINT8 ResultSize;
EFI_STATUS Status;
if (State == NULL) {
return EFI_INVALID_PARAMETER;
}
if (TimerIndex - 1 > 2) {
return EFI_UNSUPPORTED;
}
ResultSize = 8;
Status = ((WCHG_REGISTER_PROTOCOL *)gWatchdogProtocol)->RegisterSet (
gWatchdogProtocol,
6, // Register index (timer state)
0,
37, // Access type: read
0,
0,
WchgData,
&ResultSize
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Decode returned data
//
State->TimerRunning = (WchgData[0] & 0x40) != 0;
State->CountdownValue = 100000ULL * *(UINT16 *)&WchgData[3];
State->CpuBusNo = WchgData[1] & 7;
State->ErrorAction = (WchgData[1] >> 4) & 7;
switch (TimerIndex) {
case 1: State->TimeoutAction = (WchgData[2] >> 1) & 1; break;
case 2: State->TimeoutAction = (WchgData[2] >> 2) & 1; break;
case 3: State->TimeoutAction = (WchgData[2] >> 3) & 1; break;
}
return EFI_SUCCESS;
}
/**
* WchgSetTimerValue -- Set countdown value for a timer.
* Value is in 100-microsecond units.
* Program the countdown value for a WCHG timer.
*/
EFI_STATUS
EFIAPI
WchgSetTimerValue (
IN FRB2_DRIVER *This,
IN UINTN TimerIndex,
IN UINT64 *Value
)
{
if (TimerIndex - 1 > 2) {
return EFI_UNSUPPORTED;
}
if (Value == NULL || *Value == 0) {
return EFI_INVALID_PARAMETER;
}
gWchgTimerState[6 * TimerIndex] = (UINT8)((*Value / 100000) & 0xFF);
gWchgTimerState[6 * TimerIndex + 1] = (UINT8)((*Value / 100000) >> 8);
return EFI_SUCCESS;
}
/**
* WchgSetEnableFlags -- Set enable/disabled state for a timer.
* Program enable and armed flags for a WCHG timer.
*/
EFI_STATUS
EFIAPI
WchgSetEnableFlags (
IN FRB2_DRIVER *This,
IN UINTN TimerIndex,
IN UINT8 *Enable,
IN UINT8 ArmedState
)
{
if (TimerIndex - 1 > 2) {
return EFI_UNSUPPORTED;
}
gWchgTimerState[6 * TimerIndex + 2] = (Enable != NULL) ? *Enable : 0;
gWchgTimerState[6 * TimerIndex + 3] = ArmedState;
return EFI_SUCCESS;
}
/**
* WchgSetActionFlags -- Set action on timeout for a timer.
* Program the timeout action for a WCHG timer.
*/
EFI_STATUS
EFIAPI
WchgSetActionFlags (
IN FRB2_DRIVER *This,
IN UINTN TimerIndex,
IN UINT8 Action
)
{
if (TimerIndex - 1 > 2) {
return EFI_UNSUPPORTED;
}
gWchgTimerState[6 * TimerIndex + 4] = Action;
return EFI_SUCCESS;
}
/**
* WchgProgramAndArm -- Program WCHG hardware and arm the watchdog timer.
* If ResetSystem is TRUE, uses a reset-asserting action bitmask (0x3E).
* Program and arm the selected WCHG timer.
*/
EFI_STATUS
EFIAPI
WchgProgramAndArm (
IN FRB2_DRIVER *This,
IN UINTN TimerIndex,
IN VOID *Unused1,
IN VOID *Unused2,
IN BOOLEAN ResetSystem
)
{
UINT8 WchgData[6];
UINT8 ResultSize;
EFI_STATUS Status;
UINT16 Countdown;
if (TimerIndex - 1 > 2) {
return EFI_UNSUPPORTED;
}
//
// For FRB2 (timer 1), check if this is a cold boot via HOB
//
if (TimerIndex == 1) {
Status = GetHobList ((VOID **)&Countdown);
if (EFI_ERROR (Status)) {
return Status;
}
if (*(UINT16 *)Countdown != 1) {
return EFI_NOT_FOUND;
}
if (*(UINT32 *)((UINT8 *)Countdown + 12) == 32) {
return EFI_UNSUPPORTED; // S3 resume -- skip
}
}
//
// Prepare WCHG register data
//
Countdown = *(UINT16 *)&gWchgTimerState[6 * TimerIndex];
if (Countdown == 0) {
return EFI_SUCCESS; // Countdown of 0 means timer is disabled
}
WchgData[0] = 6;
WchgData[1] = (WchgData[1] & 0x38) | (TimerIndex & 7);
WchgData[2] = (WchgData[2] & 0x88) |
(gWchgTimerState[6 * TimerIndex + 4] & 7) |
(16 * (gWchgTimerState[6 * TimerIndex + 3] & 7));
WchgData[3] = gWchgTimerState[6 * TimerIndex + 2];
WchgData[4] = HIBYTE (Countdown);
WchgData[5] = LOBYTE (Countdown);
//
// Set action mask based on whether this is a full system reset
//
if (!ResetSystem) {
if (TimerIndex == 1) {
WchgData[5] |= 0x02; // FRB2 action
} else if (TimerIndex == 2) {
WchgData[5] |= 0x04;
} else if (TimerIndex == 3) {
WchgData[5] |= 0x08; // OS WDT action
}
} else {
WchgData[5] |= 0x3E; // Full system reset action
}
//
// Write register data
//
ResultSize = 6;
Status = ((WCHG_REGISTER_PROTOCOL *)gWatchdogProtocol)->RegisterSet (
gWatchdogProtocol,
6,
0,
36, // Access type: write
&WchgData[1],
6,
0,
&ResultSize
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Second write to commit/arm
//
ResultSize = 0;
Status = ((WCHG_REGISTER_PROTOCOL *)gWatchdogProtocol)->RegisterSet (
gWatchdogProtocol,
6,
0,
34, // Access type: commit/arm
NULL,
6,
0,
&ResultSize
);
return Status;
}
/**
* WchgReadBackVerify -- Read back WCHG state and verify.
* If the timer was previously disabled, returns EFI_ALREADY_STARTED.
* Read back and verify the selected WCHG timer state.
*/
EFI_STATUS
EFIAPI
WchgReadBackVerify (
IN FRB2_DRIVER *This,
IN UINTN TimerIndex,
IN VOID *Unused,
IN VOID *Unused2
)
{
UINT8 WchgData[6];
UINT8 ResultSize;
EFI_STATUS Status;
if (TimerIndex - 1 > 2) {
return EFI_UNSUPPORTED;
}
//
// Read hardware state
//
ResultSize = 8;
Status = ((WCHG_REGISTER_PROTOCOL *)gWatchdogProtocol)->RegisterSet (
gWatchdogProtocol,
6,
0,
37,
0,
0,
WchgData,
&ResultSize
);
if (EFI_ERROR (Status)) {
return Status;
}
if ((WchgData[0] & 0x40) != 0) {
//
// Timer is running -- verify index, sync state
//
if ((WchgData[0] & 7) != TimerIndex) {
return EFI_NOT_FOUND;
}
gWchgTimerState[6 * TimerIndex] = WchgData[1];
gWchgTimerState[6 * TimerIndex + 1] = WchgData[2];
gWchgTimerState[6 * TimerIndex + 4] = WchgData[1] & 7;
gWchgTimerState[6 * TimerIndex + 3] = (WchgData[1] >> 4) & 7;
//
// Re-arm the timer
//
WchgData[0] = 6;
WchgData[1] = TimerIndex & 7;
WchgData[2] = 0;
WchgData[3] = 0;
*(UINT16 *)&WchgData[3] = 0;
WchgData[3] = 0;
ResultSize = 6;
Status = ((WCHG_REGISTER_PROTOCOL *)gWatchdogProtocol)->RegisterSet (
gWatchdogProtocol,
6,
0,
36,
WchgData,
6,
0,
&ResultSize
);
return Status;
} else {
//
// Timer not running -- already disabled
//
DEBUG ((DEBUG_ERROR, "%a: EfiFrb2 already disabled\n", __FUNCTION__));
return EFI_ALREADY_STARTED;
}
}
// ============================================================================
// Boot Phase Notification Handlers
// ============================================================================
/**
* Frb2OpromNotify -- Called before/after option ROM dispatch.
* Toggles FRB2 off during OPROM and back on after.
* Temporarily disable FRB2 for option ROM dispatch, then re-arm it afterward.
*/
VOID
EFIAPI
Frb2OpromNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
if (!gOpromDone) {
Status = WchgReadBackVerify (gFrb2Driver, 1, NULL, NULL);
if (!EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "EfiFrb2 disabled Before OPRom Status = %r\n", Status));
gOpromDone = TRUE;
}
}
if (gOpromDone) {
Status = WchgProgramAndArm (gFrb2Driver, 1, NULL, NULL, FALSE);
if (!EFI_ERROR (Status)) {
gOpromDone = FALSE;
DEBUG ((DEBUG_INFO, "EfiFrb2 Enabled after OPRom Status = %r\n", Status));
}
}
}
/**
* Frb2PasswordNotify -- Disable FRB2 before password prompt.
* Disable FRB2 before the password prompt.
*/
VOID
EFIAPI
Frb2PasswordNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
Status = WchgReadBackVerify (gFrb2Driver, 1, NULL, NULL);
DEBUG ((DEBUG_INFO, "EfiFrb2 Disabled Before Giving Password= %r\n", Status));
}
/**
* Frb2PasswordDoneNotify -- Re-enable FRB2 after password.
* Re-enable FRB2 after the password prompt completes.
*/
VOID
EFIAPI
Frb2PasswordDoneNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
Status = WchgProgramAndArm (gFrb2Driver, 1, NULL, NULL, FALSE);
DEBUG ((DEBUG_INFO, "EfiFrb2 Enabled after Giving Password= %r\n", Status));
}
/**
* Frb2PromptTimeoutNotify -- Disable FRB2 before prompt timeout.
* Disable FRB2 before the prompt timeout window.
*/
VOID
EFIAPI
Frb2PromptTimeoutNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
Status = WchgReadBackVerify (gFrb2Driver, 1, NULL, NULL);
DEBUG ((DEBUG_INFO, "EfiFrb2 Disabled Before PromptTimeOut Start= %r\n", Status));
}
/**
* Frb2PromptTimeoutDoneNotify -- Re-enable FRB2 after prompt timeout.
* Re-enable FRB2 after the prompt timeout window.
*/
VOID
EFIAPI
Frb2PromptTimeoutDoneNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
Status = WchgProgramAndArm (gFrb2Driver, 1, NULL, NULL, FALSE);
DEBUG ((DEBUG_INFO, "EfiFrb2 Enabled after Prompt TimeOut Ends= %r\n", Status));
}
/**
* ShellEntryNotify -- Disable OS boot WDT when entering UEFI Shell.
* Disable the OS boot watchdog when entering the UEFI Shell.
*/
VOID
EFIAPI
ShellEntryNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
Status = WchgReadBackVerify (gFrb2Driver, 3, NULL, NULL);
DEBUG ((DEBUG_INFO, "OS WDT is disabled on entering shell. Status = %r\n", Status));
}
/**
* ReadyToBootCallback -- Fires at ReadyToBoot.
* Disables FRB2 and arms the OS boot WDT.
* Handle ReadyToBoot by disabling FRB2 and arming the OS boot watchdog.
*/
VOID
EFIAPI
ReadyToBootCallback (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
if (gFrb2Enabled) {
Status = WchgReadBackVerify (gFrb2Driver, 1, NULL, NULL);
DEBUG ((DEBUG_INFO, "EfiFrb2 disabled Status = %r\n", Status));
}
if (gOsWdtEnabled) {
UINT64 TimeoutUs;
TimeoutUs = 1000000ULL * gOsWdtTimeout;
WchgSetTimerValue (gFrb2Driver, 3, &TimeoutUs);
gWchgTimerState[21] = 0;
gWchgTimerState[22] = gOsWdtArmed;
Status = WchgProgramAndArm (gFrb2Driver, 3, NULL, NULL, FALSE);
DEBUG ((DEBUG_INFO, "EfiOsBootWdt Enabled Status = %r\n", Status));
}
}
/**
* BootPhaseNotify -- Disable FRB2 and OS WDT before setup.
* Disable FRB2 and OS watchdog before setup.
*/
VOID
EFIAPI
BootPhaseNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
if (gFrb2Enabled) {
Status = WchgReadBackVerify (gFrb2Driver, 1, NULL, NULL);
DEBUG ((DEBUG_INFO, "EfiFrb2 disabled Before setup Status = %r\n", Status));
if (gOsWdtEnabled) {
Status = WchgReadBackVerify (gFrb2Driver, 3, NULL, NULL);
DEBUG ((DEBUG_INFO, "EfiOsBootWdt disabled Before setup Status = %r\n", Status));
}
}
}
// ============================================================================
// Event Registration Helpers
// ============================================================================
/**
* RegisterBootEvent -- Create a TPL notify event for a boot phase GUID.
* Based on UefiLib's EfiCreateProtocolNotifyEvent pattern.
* Register a boot-phase notification event.
*/
EFI_STATUS
RegisterBootEvent (
IN EFI_GUID *EventGroupGuid,
IN UINTN NotifyTpl,
IN EFI_EVENT_NOTIFY NotifyFunction,
...
)
{
EFI_STATUS Status;
EFI_EVENT Event;
VA_LIST Context;
VOID *Registration;
if (EventGroupGuid == NULL) {
DEBUG_ASSERT (FALSE, "Name != NULL");
return EFI_INVALID_PARAMETER;
}
if (NotifyFunction == NULL) {
DEBUG_ASSERT (FALSE, "NotifyFunction != NULL");
return EFI_INVALID_PARAMETER;
}
Status = gBS->CreateEventEx (
EVT_NOTIFY_SIGNAL,
NotifyTpl,
NotifyFunction,
NULL,
EventGroupGuid,
&Event
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
DEBUG_ASSERT (FALSE, "!EFI_ERROR (Status)");
}
VA_START (Context, NotifyFunction);
Registration = VA_ARG (Context, VOID *);
VA_END (Context);
Status = gBS->RegisterProtocolNotify (EventGroupGuid, Event, Registration);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
DEBUG_ASSERT (FALSE, "!EFI_ERROR (Status)");
}
return Status;
}
/**
* RegisterReadyToBootEvent -- Register the ReadyToBoot event callback.
* Requires UEFI spec >= 2.0.
* Register the ReadyToBoot event callback.
*/
EFI_STATUS
RegisterReadyToBootEvent (
OUT EFI_EVENT *ReadyToBootEvent
)
{
if (ReadyToBootEvent == NULL) {
DEBUG_ASSERT (FALSE, "ReadyToBootEvent != NULL");
return EFI_INVALID_PARAMETER;
}
if (gST->Hdr.Revision >= EFI_SPECIFICATION_VERSION_2_0) {
return gBS->CreateEventEx (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
ReadyToBootCallback,
NULL,
&gEfiEventReadyToBootGuid,
ReadyToBootEvent
);
} else {
DEBUG ((DEBUG_ERROR, "EFI1.1 can't support ReadyToBootEvent!\n"));
DEBUG_ASSERT (FALSE, "((BOOLEAN)(0==1))");
return EFI_UNSUPPORTED;
}
}
// ============================================================================
// Driver Entry Point
// ============================================================================
/**
* InitializeHobList -- Get the HOB list pointer, used as early init.
* Cache the HOB list pointer for later use.
*
* This is called early in ModuleEntryPoint, before the main init.
* It caches the HOB list pointer so it is available for later use.
*/
EFI_STATUS
InitializeHobList (
VOID
)
{
EFI_STATUS Status;
if (gHobList != NULL) {
return EFI_SUCCESS;
}
Status = GetHobList (&gHobList);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
DEBUG_ASSERT (__FILE__, __LINE__, "!EFI_ERROR (Status)");
}
if (gHobList == NULL) {
DEBUG_ASSERT (__FILE__, __LINE__, "mHobList != NULL");
}
return (gHobList != NULL) ? EFI_SUCCESS : EFI_NOT_FOUND;
}
/**
* AllocateZeroPool -- Allocate and zero a pool buffer.
* Allocate a zeroed pool buffer.
*/
VOID *
AllocateZeroPool (
IN UINTN Size
)
{
VOID *Buffer;
EFI_STATUS Status;
Status = gBS->AllocatePool (EfiBootServicesData, Size, &Buffer);
if (EFI_ERROR (Status)) {
return NULL;
}
ZeroMem (Buffer, Size);
return Buffer;
}
/**
* FreePool -- Release a pool buffer.
* Free a pool buffer.
*/
VOID
FreePool (
IN VOID *Buffer
)
{
EFI_STATUS Status;
Status = gBS->FreePool (Buffer);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
DEBUG_ASSERT (__FILE__, __LINE__, "!EFI_ERROR (Status)");
}
}
/**
* DxeFrbEntryPoint -- Main driver entry point.
*
* 1. Locate EFI_WATCHDOG_TIMER_ARCH_PROTOCOL
* 2. Allocate and initialize FRB2 driver instance
* 3. Install protocol on image handle
* 4. Read "ServerSetup" variable for configuration
* 5. Register boot phase notification callbacks
*
* Main driver entry point.
*/
EFI_STATUS
EFIAPI
DxeFrbEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
FRB2_DRIVER *Driver;
VOID *ServerSetup;
UINTN VariableSize;
//
// 1. Locate EFI_WATCHDOG_TIMER_ARCH_PROTOCOL
//
Status = gBS->LocateProtocol (
&gEfiWatchdogTimerArchProtocolGuid,
NULL,
&gWatchdogProtocol
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// 2. Allocate and initialize the FRB2 driver instance
//
Driver = AllocateZeroPool (sizeof (FRB2_DRIVER));
if (Driver == NULL) {
return EFI_OUT_OF_RESOURCES;
}
gFrb2Driver = Driver;
//
// 3. Populate function table
//
Driver->GetState = WchgGetState;
Driver->SetTimerValue = WchgSetTimerValue;
Driver->SetEnableFlags = WchgSetEnableFlags;
Driver->SetActionFlags = WchgSetActionFlags;
Driver->ProgramAndArm = WchgProgramAndArm;
Driver->ReadBackVerify = WchgReadBackVerify;
//
// 4. Install the FRB2 driver protocol on the image handle
//
Status = gBS->InstallProtocolInterface (
&ImageHandle,
&gFrb2DriverBindingGuid,
EFI_NATIVE_INTERFACE,
Driver
);
if (EFI_ERROR (Status)) {
FreePool (Driver);
return Status;
}
//
// 5. Read configuration from "ServerSetup" UEFI variable
//
VariableSize = sizeof (ServerSetup);
Status = gRT->GetVariable (
L"ServerSetup",
&gEfiServerSetupGuid,
NULL,
&VariableSize,
&ServerSetup
);
if (EFI_ERROR (Status)) {
//
// Defaults when variable not found
//
gFrb2Enabled = TRUE;
gOsWdtTimeout = 600; // 60 seconds (600 * 100ms)
gOsWdtEnabled = FALSE;
gOsWdtArmed = TRUE;
}
//
// 6. Register event notification callbacks
//
if (gFrb2Enabled) {
RegisterBootEvent (&gEfiEventOpromGuid, 8, Frb2OpromNotify, NULL);
RegisterBootEvent (&gEfiEventPasswordGuid, 16, Frb2PasswordNotify, NULL);
RegisterBootEvent (&gEfiEventPasswordDoneGuid, 16, Frb2PasswordDoneNotify, NULL);
RegisterBootEvent (&gEfiEventPromptTimeoutGuid, 16, Frb2PromptTimeoutNotify, NULL);
RegisterBootEvent (&gEfiEventPromptTimeoutDoneGuid,16, Frb2PromptTimeoutDoneNotify, NULL);
}
if (gOsWdtEnabled) {
RegisterBootEvent (&gEfiEventOsWdtGuid1, 16, ShellEntryNotify, NULL);
RegisterBootEvent (&gEfiEventOsWdtGuid2, 16, ShellEntryNotify, NULL);
RegisterBootEvent (&gEfiEventOsWdtGuid3, 16, NullNotify, NULL);
}
if (gFrb2Enabled || gOsWdtEnabled) {
RegisterBootEvent (&gEfiEventBootPhaseGuid1, 8, BootPhaseNotify, NULL);
RegisterBootEvent (&gEfiEventBootPhaseGuid2, 8, ReadyToBootCallback, NULL);
RegisterBootEvent (&gEfiEventBootPhaseGuid3, 8, ReadyToBootCallback, NULL);
Status = RegisterReadyToBootEvent (&gReadyToBootEvent);
DEBUG ((DEBUG_INFO, "EfiFrb2 disabled Before boot Status = %r\n", Status));
}
return EFI_SUCCESS;
}