Skip to content

Commit

Permalink
Use a scrolling section to display failed files
Browse files Browse the repository at this point in the history
* Also remove the margin so that we always use the full console width.
  • Loading branch information
pbatard committed Mar 6, 2024
1 parent d8d86d2 commit fba4af1
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 72 deletions.
26 changes: 17 additions & 9 deletions src/boot.c
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ STATIC EFI_STATUS ExitProcess(
Status = gBS->StartImage(ImageHandle, NULL, NULL);
}
if (EFI_ERROR(Status)) {
SetTextPosition(MARGIN_H, Console.Rows / 2 + 1);
SetTextPosition(0, Console.Rows / 2 + 1);
PrintError(L"Could not launch original bootloader");
}
}
Expand Down Expand Up @@ -304,7 +304,7 @@ EFI_STATUS EFIAPI efi_main(

Status = GetRootHandle(&DeviceHandle, &Root);
if (EFI_ERROR(Status)) {
PrintError(L"Could not open root directory\n");
PrintError(L"Could not open root directory");
goto out;
}

Expand Down Expand Up @@ -337,6 +337,13 @@ EFI_STATUS EFIAPI efi_main(
PrintCentered(L"[Press any key to cancel]", Console.Rows - 2);
DefText();

// Set up the scroll section where we display individual file validation errors
Status = InitScrollSection(Console.Rows / 2 + 1, Console.Rows / 2 - 4);
if (EFI_ERROR(Status)) {
PrintError(L"Could not set up scroll section");
goto out;
}

// Now go through each entry we parsed
for (Index = 0; Index < HashList.NumEntries; Index++) {
// Convert the expected hexascii hash to a binary value we can use
Expand Down Expand Up @@ -375,16 +382,17 @@ EFI_STATUS EFIAPI efi_main(
break;

// Report failures
if (EFI_ERROR(Status))
PrintFailedEntry(Status, Path, NumFailed++);
if (EFI_ERROR(Status)) {
NumFailed++;
PrintFailedEntry(Status, Path);
}
}

ExitScrollSection();

// Final report
UnicodeSPrint(Message, ARRAY_SIZE(Message), L"%d/%d file%s processed",
Index, HashList.NumEntries, (HashList.NumEntries == 1) ? L"" : L"s");
V_ASSERT(SafeStrLen(Message) < ARRAY_SIZE(Message) / 2);
UnicodeSPrint(&Message[SafeStrLen(Message)], ARRAY_SIZE(Message) - SafeStrLen(Message),
L" [%d failed]", NumFailed);
UnicodeSPrint(Message, ARRAY_SIZE(Message), L"%d/%d file%s processed [%d failed]",
Index, HashList.NumEntries, (HashList.NumEntries == 1) ? L"" : L"s", NumFailed);
PrintCentered(Message, Progress.YPos + 2);

out:
Expand Down
34 changes: 25 additions & 9 deletions src/boot.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,6 @@ extern UINTN AlertYPos;
#define COLS_MIN 50
#define ROWS_MIN 20

/* Horizontal margins used for the console output */
#define MARGIN_H 2

/* Size of an MD5 hash */
#define MD5_HASHSIZE 16

Expand Down Expand Up @@ -187,13 +184,13 @@ typedef struct {
/*
* Convenience macros to print informational, warning or error messages.
*/
#define PrintInfo(fmt, ...) do { SetTextPosition(MARGIN_H, AlertYPos++); \
#define PrintInfo(fmt, ...) do { SetTextPosition(0, AlertYPos++); \
SetText(TEXT_WHITE); Print(L"[INFO]"); DefText(); \
Print(L" " fmt L"\n", ##__VA_ARGS__); } while(0)
#define PrintWarning(fmt, ...) do { SetTextPosition(MARGIN_H, AlertYPos++); \
#define PrintWarning(fmt, ...) do { SetTextPosition(0, AlertYPos++); \
SetText(TEXT_YELLOW); Print(L"[WARN]"); DefText(); \
Print(L" " fmt L"\n", ##__VA_ARGS__); } while(0)
#define PrintError(fmt, ...) do { SetTextPosition(MARGIN_H, AlertYPos++); \
#define PrintError(fmt, ...) do { SetTextPosition(0, AlertYPos++); \
SetText(TEXT_RED); Print(L"[FAIL]"); DefText(); \
Print(L" " fmt L": [%d] %r\n", ##__VA_ARGS__, (Status&0x7FFFFFFF), Status); } while (0)
#define PrintTest(fmt, ...) do { if (IsTestMode) Print(L"[TEST] " fmt L"\n", ##__VA_ARGS__); } while(0)
Expand All @@ -202,7 +199,7 @@ typedef struct {
#define SetTextPosition(x, y) do { if (!IsTestMode) gST->ConOut->SetCursorPosition(gST->ConOut, x, y);} while (0)

/* Convenience assertion macro */
#define P_ASSERT(f, l, a) do { if(!(a)) { Print(L"*** ASSERT FAILED: %a(%d): %a ***\n", f, l, #a); \
#define P_ASSERT(f, l, a) do { if(!(a)) { Print(L"\n*** ASSERT FAILED: %a(%d): %a ***\n", f, l, #a); \
if (IsTestMode) ShutDown(); else Halt(); } } while(0)
#define V_ASSERT(a) P_ASSERT(__FILE__, __LINE__, a)

Expand Down Expand Up @@ -436,6 +433,26 @@ EFI_STATUS Utf8ToUcs2(
**/
VOID InitConsole(VOID);

/**
Initialize a scrolling section on the console.
@param[in] YPos The vertical start position of the scrolling section.
@param[in] NumberOfLines How many lines should the scrolling section have.
@retval EFI_SUCCESS The scroll section was successfully initialized.
@retval EFI_INVALID_PARAMETER The provided parameters are invalid.
@retval EFI_OUT_OF_RESOURCES A memory allocation error occurred.
**/
EFI_STATUS InitScrollSection(
CONST UINTN YPos,
CONST UINTN NumberOfLines
);

/**
Scroll section teardown.
**/
VOID ExitScrollSection(VOID);

/**
Print a centered message on the console.
Expand All @@ -457,8 +474,7 @@ VOID PrintCentered(
**/
VOID PrintFailedEntry(
IN CONST EFI_STATUS Status,
IN CHAR16* Path,
IN CONST UINTN NumFailed
IN CONST CHAR16* Path
);

/**
Expand Down
151 changes: 123 additions & 28 deletions src/console.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ UINTN AlertYPos = ROWS_MIN / 2 + 1;
/* String used to erase a single line on the console */
STATIC CHAR16 EmptyLine[STRING_MAX] = { 0 };

/* Structure used for scrolling messages */
STATIC struct {
CHAR16* Section;
UINTN Index;
UINTN Lines;
UINTN YPos;
UINTN MaxLines;
} Scroll = { 0 };

/**
Console initialisation.
**/
Expand All @@ -49,7 +58,7 @@ VOID InitConsole(VOID)
}
if (Console.Cols >= PATH_MAX)
Console.Cols = PATH_MAX - 1;
AlertYPos = Console.Rows / 2 + 1;
AlertYPos = 2;

// Populate a blank line we can use to erase a line
for (i = 0; i < Console.Cols; i++)
Expand Down Expand Up @@ -85,40 +94,128 @@ VOID PrintCentered(
Print(L"%s\n", Message);
}

/**
Initialize a scrolling section on the console.
@param[in] YPos The vertical start position of the scrolling section.
@param[in] NumberOfLines How many lines should the scrolling section have.
@retval EFI_SUCCESS The scroll section was successfully initialized.
@retval EFI_INVALID_PARAMETER The provided parameters are invalid.
@retval EFI_OUT_OF_RESOURCES A memory allocation error occurred.
**/
EFI_STATUS InitScrollSection(
CONST UINTN YPos,
CONST UINTN NumberOfLines
)
{
EFI_STATUS Status = EFI_SUCCESS;

V_ASSERT(Console.Rows > 8);
if (NumberOfLines < 2 || NumberOfLines + YPos >= Console.Rows)
return EFI_INVALID_PARAMETER;

// Set up the console real estate for scrolling messages
ZeroMem(&Scroll, sizeof(Scroll));
// Default position where the scroll section starts
Scroll.YPos = YPos;
// Maximum number of lines we display/scroll
Scroll.MaxLines = NumberOfLines;
Scroll.Section = AllocateZeroPool(Scroll.MaxLines * (Console.Cols + 1) * sizeof(CHAR16));
if (Scroll.Section == NULL)
Status = EFI_OUT_OF_RESOURCES;
return Status;
}

/**
Scroll section teardown.
**/
VOID ExitScrollSection(VOID)
{
SafeFree(Scroll.Section);
}

/**
Print a hash entry that has failed processing.
Do this over a specific section of the console we cycle over.
@param[in] Status The Status code from the failed operation on the entry.
@param[in] Path A pointer to the CHAR16 string with the Path of the entry.
@param[in] NumFailed The current number of failed entries.
**/
VOID PrintFailedEntry(
IN CONST EFI_STATUS Status,
IN CHAR16* Path,
IN CONST UINTN NumFailed
IN CONST CHAR16* Path
)
{
if (!EFI_ERROR(Status) || Path == NULL)
CHAR16 ErrorMsg[128], *Src, *Line;
UINTN Index;

if (!EFI_ERROR(Status) || Path == NULL || Scroll.Section == NULL ||
(Scroll.YPos + Scroll.MaxLines >= Console.Rows))
return;

// Truncate the path in case it's very long.
if (SafeStrLen(Path) > 80)
Path[80] = L'\0';
if (!IsTestMode) {
// Clear any existing text on this line
SetTextPosition(0, 1 + (NumFailed % (Console.Rows / 2 - 4)));
gST->ConOut->OutputString(gST->ConOut, EmptyLine);
}
SetTextPosition(MARGIN_H, 1 + (NumFailed % (Console.Rows / 2 - 4)));
SetText(TEXT_RED);
Print(L"[FAIL]");
DefText();
// Display a more explicit message (than "CRC Error") for files that fail MD5
if (Status == EFI_CRC_ERROR)
Print(L" File '%s': [27] MD5 Checksum Error\n", Path);
UnicodeSPrint(ErrorMsg, ARRAY_SIZE(ErrorMsg), L": [27] Checksum Error");
else
Print(L" File '%s': [%d] %r\n", Path, (Status & 0x7FFFFFFF), Status);
UnicodeSPrint(ErrorMsg, ARRAY_SIZE(ErrorMsg), L": [%d] %r", (Status & 0x7FFFFFFF), Status);
if (IsTestMode)
SafeStrCat(ErrorMsg, ARRAY_SIZE(ErrorMsg), L"\r\n");

// Fill a new line in our scroll section
V_ASSERT(Scroll.Index < Scroll.MaxLines);
Line = &Scroll.Section[Scroll.Index * (Console.Cols + 1)];

// Copy as much of the Path as we have space available before the error message
V_ASSERT(StrLen(ErrorMsg) < Console.Cols);
for (Index = 0; (Path[Index] != 0) && (Index < Console.Cols - StrLen(ErrorMsg)); Index++)
Line[Index] = Path[Index];

// Add a truncation mark to the path if it was too long (but longer than 16 characters)
if (Path[Index] != 0 && Index > 16) {
Line[Index - 3] = L'.';
Line[Index - 2] = L'.';
Line[Index - 1] = L'.';
}

// Append the error message (since we made sure that we had enough space)
Src = ErrorMsg;
while (*Src != 0)
Line[Index++] = *Src++;

// Fill the remainder of the line with spaces and terminate it
V_ASSERT(Index <= Console.Cols);
if (!IsTestMode) {
while (Index < Console.Cols)
Line[Index++] = L' ';
}
Line[Index] = 0;
// Be paranoid about string overflow
V_ASSERT(Line[Console.Cols] == 0);

if (Scroll.Lines < Scroll.MaxLines) {
// We haven't reached scroll capacity yet, so just output the new
// line after the last.
Scroll.Lines++;
SetTextPosition(0, Scroll.YPos + Scroll.Index);
gST->ConOut->OutputString(gST->ConOut, Line);
} else {
// We have reached scroll capacity, so we reprint all the lines at
// their new position.
SetTextPosition(0, Scroll.YPos);
V_ASSERT(Scroll.Index < Scroll.MaxLines);
// Start reprinting after the the line we just updated (i.e. from the
// line at Scroll.Index + 1) and make sure to apply Scroll.MaxLines
// as the modulo.
for (Index = Scroll.Index + 1; Index <= Scroll.Index + Scroll.MaxLines; Index++) {
Line = &Scroll.Section[(Index % Scroll.MaxLines) * (Console.Cols + 1)];
// Be paranoid about array overflow
V_ASSERT((UINTN)Line + (Console.Cols + 1) * sizeof(CHAR16) <=
(UINTN)Scroll.Section + Scroll.MaxLines * (Console.Cols + 1) * sizeof(CHAR16));
gST->ConOut->OutputString(gST->ConOut, Line);
}
}
Scroll.Index = (Scroll.Index + 1) % Scroll.MaxLines;
}

/**
Expand All @@ -138,14 +235,12 @@ VOID InitProgress(
Console.Cols >= STRING_MAX || Progress->Message == NULL)
return;

if (SafeStrLen(Progress->Message) > Console.Cols - MARGIN_H * 2 - 8)
return;

if (Progress->YPos > Console.Rows - 3)
Progress->YPos = Console.Rows - 3;

if ((SafeStrLen(Progress->Message) + 6) / 2 > Console.Cols / 2)
return;
MessagePos = Console.Cols / 2 - (SafeStrLen(Progress->Message) + 6) / 2;
V_ASSERT(MessagePos > MARGIN_H);

Progress->Current = 0;
Progress->LastCol = 0;
Expand All @@ -155,8 +250,8 @@ VOID InitProgress(
SetTextPosition(MessagePos, Progress->YPos);
Print(L"%s: 0.0%%", Progress->Message);

SetTextPosition(MARGIN_H, Progress->YPos + 1);
for (i = 0; i < Console.Cols - MARGIN_H * 2; i++)
SetTextPosition(0, Progress->YPos + 1);
for (i = 0; i < Console.Cols; i++)
Print(L"░");
}

Expand Down Expand Up @@ -189,10 +284,10 @@ VOID UpdateProgress(
}

// Update the progress bar
CurCol = (UINTN)((Progress->Current * (Console.Cols - MARGIN_H * 2)) / Progress->Maximum);
CurCol = (UINTN)((Progress->Current * Console.Cols) / Progress->Maximum);
if (!IsTestMode) {
for (; CurCol > Progress->LastCol && Progress->LastCol < Console.Cols; Progress->LastCol++) {
SetTextPosition(MARGIN_H + Progress->LastCol, Progress->YPos + 1);
SetTextPosition(Progress->LastCol, Progress->YPos + 1);
Print(L"%c", BLOCKELEMENT_FULL_BLOCK);
}
}
Expand All @@ -215,9 +310,9 @@ VOID CountDown(
UINTN MessagePos, CounterPos;
INTN i;

V_ASSERT(Console.Cols / 2 > SafeStrLen(Message) / 2 - 1);
MessagePos = Console.Cols / 2 - SafeStrLen(Message) / 2 - 1;
CounterPos = MessagePos + SafeStrLen(Message) + 2;
V_ASSERT(MessagePos > MARGIN_H);

if (IsTestMode)
return;
Expand Down
Loading

0 comments on commit fba4af1

Please sign in to comment.