Skip to content

Commit

Permalink
Prevent the UEFI watchdog timer from resetting the platform during MD…
Browse files Browse the repository at this point in the history
…5 processing

* UEFI firmwares implement a watchdog timer, set to 5 minutes, that considers a bootloader
  is stalled if it hasn't reported to the watchdog during that time, as described in:
  https://uefi.org/specs/UEFI/2.9_A/07_Services_Boot_Services.html#efi-boot-services-setwatchdogtimer
* Because our processing can take much longer than 5 minutes, make sure we manifest ourselves
  to the watchdog for every 128 MB of data we process.
* Also update the MD5 buffer size to 64 MB and add a missing File->Close() call in parser.c
  • Loading branch information
pbatard committed Mar 1, 2024
1 parent 6a6589b commit fcd9f64
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 7 deletions.
4 changes: 4 additions & 0 deletions src/boot.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ STATIC EFI_STATUS ExitProcess(
// not be found, in which case continue boot right away.
if (Status != EFI_NOT_FOUND) {
SetText(TEXT_YELLOW);
// Give the user 1 hour to answer the question
gBS->SetWatchdogTimer(3600, 0x11D5, 0, NULL);
PrintCentered(L"Continue with boot? [y/N]", Console.Rows - 2);
gST->ConIn->Reset(gST->ConIn, FALSE);
while (gST->ConIn->ReadKeyStroke(gST->ConIn, &Key) == EFI_NOT_READY);
Expand All @@ -187,6 +189,8 @@ STATIC EFI_STATUS ExitProcess(
}
RunCountDown = FALSE;
}
// Reset the watchdog to the default 5 minutes timeout and system code
gBS->SetWatchdogTimer(300, 0, 0, NULL);
Status = gBS->LoadImage(FALSE, MainImageHandle, DevicePath, NULL, 0, &ImageHandle);
SafeFree(DevicePath);
if (Status == EFI_SUCCESS) {
Expand Down
8 changes: 5 additions & 3 deletions src/boot.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ extern UINTN AlertYPos;
#define MD5_BLOCKSIZE 64

/* Buffer size used for MD5 hashing */
#define MD5_BUFFERSIZE 4096
#define MD5_BUFFERSIZE 65536

/* How many buffers we process between checks for user cancel */
#define MD5_CHECK_CANCEL 64
/* Number of MD5 buffers to process for each MB of data */
#define MD5_PROCESSED_1MB ((1024 * 1024) / MD5_BUFFERSIZE)

/* Size of the hexascii representation of a hash */
#define HASH_HEXASCII_SIZE (MD5_HASHSIZE * 2)
Expand Down Expand Up @@ -266,6 +266,8 @@ NO_RETURN STATIC __inline VOID ShutDown()
/* Freeze the system with current screen output, then shut it down after one hour */
NO_RETURN STATIC __inline VOID Halt()
{
// Disable the watchdog timer, since we don't want an early reset
gBS->SetWatchdogTimer(0, 0, 0, NULL);
Sleep((UINTN)3600 * 1000 * 1000);
gRT->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, NULL);
while (1);
Expand Down
13 changes: 10 additions & 3 deletions src/hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -281,10 +281,11 @@ EFI_STATUS HashFile(
)
{
STATIC UINTN BlocksProcessed = 0;
STATIC UINTN LastWatchDogReset = 0;
EFI_STATUS Status = EFI_INVALID_PARAMETER;
EFI_FILE_HANDLE File = NULL;
EFI_FILE_INFO* Info = NULL;
HASH_CONTEXT Context = { {0} };
HASH_CONTEXT Context = { 0 };
UINTN Size, ReadSize;
UINT64 ReadBytes;
UINT8 Buffer[MD5_BUFFERSIZE];
Expand Down Expand Up @@ -328,9 +329,15 @@ EFI_STATUS HashFile(
if (Progress != NULL && Progress->Type == PROGRESS_TYPE_BYTE)
Progress->Current += ReadSize;
Md5Write(&Context, Buffer, ReadSize);
// Check for user cancel and report progress every few blocks
if (BlocksProcessed++ % MD5_CHECK_CANCEL == 0) {
// Check for user cancel and report progress for each MB of data
if (BlocksProcessed++ % MD5_PROCESSED_1MB == 0) {
UpdateProgress(Progress);
// We need to set the watchdog timer regularly or else the UEFI firmware
// considers the bootloader stalled and resets the system. Do it for every
// 128 MB processed, as, with a default watchdog period of 5 mins, even
// very slow systems should be fine...
if (LastWatchDogReset++ % 128 == 0)
gBS->SetWatchdogTimer(300, 0x11D5, 0, NULL);
if (gST->BootServices->CheckEvent(gST->ConIn->WaitForKey) != EFI_NOT_READY) {
Status = EFI_ABORTED;
goto out;
Expand Down
4 changes: 3 additions & 1 deletion src/parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ EFI_STATUS Parse(
)
{
EFI_STATUS Status;
EFI_FILE_HANDLE File;
EFI_FILE_HANDLE File = NULL;
EFI_FILE_INFO* Info = NULL;
UINT8* HashFile = NULL;
HASH_ENTRY* HashList = NULL;
Expand Down Expand Up @@ -268,6 +268,8 @@ EFI_STATUS Parse(

out:
SafeFree(Info);
if (File != NULL)
File->Close(File);
if (EFI_ERROR(Status)) {
SafeFree(HashFile);
SafeFree(HashList);
Expand Down

0 comments on commit fcd9f64

Please sign in to comment.