Skip to content

Commit

Permalink
Add persistent console messages
Browse files Browse the repository at this point in the history
* By default, displaying a console alert box or dialog clears any previously displayed message.
* To avoid this, enable a recallable Print() function, that can restore onscreen output after
  a full screen dialog has been displayed.
  • Loading branch information
pbatard committed Aug 28, 2024
1 parent 8698574 commit e4fdef7
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 38 deletions.
53 changes: 53 additions & 0 deletions src/console.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@

#include <Uefi/UefiBaseType.h>

#define MAX_PRINT_LINES 50
#define MAX_LINE_SIZE 120

STATIC UINTN CurrentLine = 0;
STATIC CHAR16* PrintLine[MAX_PRINT_LINES] = { 0 };

STATIC __inline INTN CountLines(
IN CONST CHAR16 *StrArray[]
)
Expand Down Expand Up @@ -368,3 +374,50 @@ VOID ConsoleReset(VOID)
Console->SetMode(Console, 0);
Console->ClearScreen(Console);
}

/* Set of functions enabling printed data to persist after displaying a dialog */
UINTN EFIAPI RecallPrint(
IN CONST CHAR16 *FormatString,
...
)
{
// NB: VA_LIST requires the function call to be EFIAPI decorated
VA_LIST Marker;
UINTN Ret;

if (CurrentLine >= MAX_PRINT_LINES)
return 0;

PrintLine[CurrentLine] = AllocateZeroPool(MAX_LINE_SIZE * sizeof(CHAR16));
if (PrintLine[CurrentLine] == NULL)
return 0;

VA_START(Marker, FormatString);
Ret = UnicodeVSPrint(PrintLine[CurrentLine], MAX_LINE_SIZE * sizeof(CHAR16), FormatString, Marker);
VA_END(Marker);
// If we truncate a line with that ends with an LF, make sure we keep the LF
if (FormatString[StrLen(FormatString) - 1] == L'\n')
PrintLine[CurrentLine][MAX_LINE_SIZE - 2] = L'\n';
Print(L"%s", PrintLine[CurrentLine++]);
return Ret;
}

VOID RecallPrintRestore(VOID)
{
UINTN i;

ConsoleReset();
for (i = 0; i < CurrentLine; i++)
Print(L"%s", PrintLine[i]);
}

VOID RecallPrintFree(VOID)
{
UINTN i;

for (i = 0; i < MAX_PRINT_LINES; i++) {
FreePool(PrintLine[i]);
PrintLine[i] = NULL;
}
CurrentLine = 0;
}
12 changes: 12 additions & 0 deletions src/console.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@

#define NOSEL 0x7fffffff

/* Error reporting macro */
#define ReportErrorAndExit(...) do { RecallPrint(__VA_ARGS__); goto exit; } while(0)

EFI_INPUT_KEY ConsoleGetKeystroke(VOID);

INTN ConsoleCheckForKeystroke(
Expand Down Expand Up @@ -73,3 +76,12 @@ VOID ConsoleError(
);

VOID ConsoleReset(VOID);

UINTN EFIAPI RecallPrint(
IN CONST CHAR16 *FormatString,
...
);

VOID RecallPrintRestore(VOID);

VOID RecallPrintFree(VOID);
38 changes: 18 additions & 20 deletions src/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@
#include <Guid/FileSystemVolumeLabelInfo.h>
#include <Protocol/LoadedImage.h>

#define FileReportErrorAndExit(...) do { Print(__VA_ARGS__); goto exit; } while(0)

STATIC EFI_STATUS GeneratePath(
IN CONST CHAR16* Name,
IN CONST EFI_LOADED_IMAGE_PROTOCOL *LoadedImage,
Expand Down Expand Up @@ -71,7 +69,7 @@ STATIC EFI_STATUS GeneratePath(

if (*PathName == NULL) {
Status = EFI_OUT_OF_RESOURCES;
FileReportErrorAndExit(L"Failed to allocate path buffer\n");
ReportErrorAndExit(L"Failed to allocate path buffer\n");
}

StrCpyS(*PathName, PathLen, DevicePathString);
Expand Down Expand Up @@ -102,12 +100,12 @@ EFI_STATUS SimpleFileOpenByHandle(
Status = gBS->HandleProtocol(DeviceHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID**)&Drive);

if (EFI_ERROR(Status))
FileReportErrorAndExit(L"Failed to locate simple filesystem protocol: %r\n", Status);
ReportErrorAndExit(L"Failed to locate simple filesystem protocol: %r\n", Status);

Status = Drive->OpenVolume(Drive, &Root);

if (EFI_ERROR(Status))
FileReportErrorAndExit(L"Failed to open drive volume: %r\n", Status);
ReportErrorAndExit(L"Failed to open drive volume: %r\n", Status);

Status = Root->Open(Root, File, (CHAR16*)Name, Mode, 0);

Expand Down Expand Up @@ -136,7 +134,7 @@ EFI_STATUS SimpleFileOpen(
Status = GeneratePath(Name, LoadedImage, &LoadPath, &PathName);

if (EFI_ERROR(Status))
FileReportErrorAndExit(L"Failed to generate load path for %s: %r\n", Name, Status);
ReportErrorAndExit(L"Failed to generate load path for %s: %r\n", Name, Status);

Device = LoadedImage->DeviceHandle;

Expand Down Expand Up @@ -172,10 +170,10 @@ EFI_STATUS SimpleDirReadAllByHandle(
Size = sizeof(Buffer);
Status = File->GetInfo(File, &gEfiFileInfoGuid, &Size, Info);
if (EFI_ERROR(Status))
FileReportErrorAndExit(L"Failed to get file info: %r\n", Status);
ReportErrorAndExit(L"Failed to get file info: %r\n", Status);
if ((Info->Attribute & EFI_FILE_DIRECTORY) == 0) {
Status = EFI_INVALID_PARAMETER;
FileReportErrorAndExit(L"Not a directory: '%s'\n", Name);
ReportErrorAndExit(L"Not a directory: '%s'\n", Name);
}
Size = 0;
*Count = 0;
Expand Down Expand Up @@ -222,7 +220,7 @@ EFI_STATUS SimpleDirReadAll(

Status = SimpleFileOpen(Image, Name, &File, EFI_FILE_MODE_READ);
if (EFI_ERROR(Status))
FileReportErrorAndExit(L"Failed to open '%s': %r\n", Name, Status);
ReportErrorAndExit(L"Failed to open '%s': %r\n", Name, Status);

Status = SimpleDirReadAllByHandle(File, Name, Entries, Count);
exit:
Expand All @@ -244,21 +242,21 @@ EFI_STATUS SimpleFileReadAll(

Status = File->GetInfo(File, &gEfiFileInfoGuid, Size, Info);
if (EFI_ERROR(Status))
FileReportErrorAndExit(L"Failed to get file info: %r\n", Status);
ReportErrorAndExit(L"Failed to get file info: %r\n", Status);

*Size = Info->FileSize;

if (*Size > MAX_FILE_SIZE) {
Status = EFI_UNSUPPORTED;
FileReportErrorAndExit(L"File size %d is too large\n", *Size);
ReportErrorAndExit(L"File size %d is too large\n", *Size);
}

// Might use memory mapped, so align up to nearest page.
// Also + 1 so the data is always NUL terminated.
*Buffer = AllocateZeroPool(ALIGN_VALUE(*Size + 1, 4096));
if (*Buffer == NULL) {
Status = EFI_OUT_OF_RESOURCES;
FileReportErrorAndExit(L"Failed to allocate buffer of size %d\n", *Size);
ReportErrorAndExit(L"Failed to allocate buffer of size %d\n", *Size);
}
Status = File->Read(File, Size, *Buffer);

Expand Down Expand Up @@ -295,7 +293,7 @@ EFI_STATUS SimpleVolumeSelector(
Entries = AllocateZeroPool(sizeof(CHAR16 *) * (Count + 1));
if (Entries == NULL) {
Status = EFI_OUT_OF_RESOURCES;
FileReportErrorAndExit(L"Failed to allocate volume selector buffer\n");
ReportErrorAndExit(L"Failed to allocate volume selector buffer\n");
}

for (i = 0; i < Count; i++) {
Expand Down Expand Up @@ -328,7 +326,7 @@ EFI_STATUS SimpleVolumeSelector(
Name = ConvertDevicePathToText(DevicePathFromHandle(VolumeHandles[i]), FALSE, FALSE);
if (Name == NULL) {
Status = EFI_OUT_OF_RESOURCES;
FileReportErrorAndExit(L"Failed to convert device path\n");
ReportErrorAndExit(L"Failed to convert device path\n");
}
}

Expand Down Expand Up @@ -383,7 +381,7 @@ EFI_STATUS SimpleDirFilter(

if (NewFilter == NULL) {
Status = EFI_OUT_OF_RESOURCES;
FileReportErrorAndExit(L"Failed to allocate filter buffer\n");
ReportErrorAndExit(L"Failed to allocate filter buffer\n");
}

// Just in case EFI ever stops writeable strings
Expand Down Expand Up @@ -434,7 +432,7 @@ EFI_STATUS SimpleDirFilter(
*Result = AllocateZeroPool(2 * sizeof(VOID *));
if (*Result == NULL) {
Status = EFI_OUT_OF_RESOURCES;
FileReportErrorAndExit(L"Failed to allocate filter result buffer\n");
ReportErrorAndExit(L"Failed to allocate filter result buffer\n");
}

*Count = 0;
Expand Down Expand Up @@ -663,10 +661,10 @@ EFI_STATUS SimpleFileReadAllByPath(
Status = SimpleFileOpen(DeviceHandle == NULL ? Image : DeviceHandle,
PathStart, &File, EFI_FILE_MODE_READ);
if (EFI_ERROR(Status))
FileReportErrorAndExit(L"Failed to open '%s': %r\n", Path, Status);
ReportErrorAndExit(L"Failed to open '%s': %r\n", Path, Status);
Status = SimpleFileReadAll(File, Size, Buffer);
if (EFI_ERROR(Status))
FileReportErrorAndExit(L"Failed to read '%s': %r\n", Path, Status);
ReportErrorAndExit(L"Failed to read '%s': %r\n", Path, Status);

exit:
SimpleFileClose(File);
Expand All @@ -689,10 +687,10 @@ EFI_STATUS SimpleFileWriteAllByPath(
Status = SimpleFileOpen(DeviceHandle == NULL ? Image : DeviceHandle,
PathStart, &File, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE);
if (EFI_ERROR(Status))
FileReportErrorAndExit(L"Failed to create '%s': %r\n", Path, Status);
ReportErrorAndExit(L"Failed to create '%s': %r\n", Path, Status);
Status = SimpleFileWriteAll(File, Size, Buffer);
if (EFI_ERROR(Status))
FileReportErrorAndExit(L"Failed to write '%s': %r", Path, Status);
ReportErrorAndExit(L"Failed to write '%s': %r", Path, Status);
exit:
SimpleFileClose(File);
return Status;
Expand Down
27 changes: 16 additions & 11 deletions src/mosby.c
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ STATIC EFI_STATUS SetSecureBootVariable(

Status = gRT->GetTime(&Time, NULL);
if (EFI_ERROR(Status)) {
Print(L"Failed to get current time: %r\n", Status);
RecallPrint(L"Failed to get current time: %r\n", Status);
return Status;
}

Expand All @@ -246,15 +246,15 @@ STATIC EFI_STATUS SetSecureBootVariable(
Status = CreateTimeBasedPayload(&VarSize, &VarData, &Time);
if (EFI_ERROR(Status)) {
SafeFree(Esl);
Print(L"Failed to create time-based data payload: %r\n", Status);
RecallPrint(L"Failed to create time-based data payload: %r\n", Status);
return Status;
}

Status = gRT->SetVariable(KeyInfo[Type].VarName, KeyInfo[Type].VarGuid,
VarAttributes | (Append ? EFI_VARIABLE_APPEND_WRITE : 0), VarSize, VarData);
SafeFree(VarData);
if (EFI_ERROR(Status))
Print(L"Failed to set Secure Boot variable: %r\n", Status);
RecallPrint(L"Failed to set Secure Boot variable: %r\n", Status);

return EFI_SUCCESS;
}
Expand All @@ -276,7 +276,8 @@ EFI_STATUS EFIAPI efi_main(
CHAR16 **Argv = NULL, **ArgvCopy, Path[MAX_PATH];
INSTALLABLE_COLLECTION Installable = { 0 };

Print(L"Mosby %s\n", VERSION_STRING);
ConsoleReset();
RecallPrint(L"Mosby %s\n", VERSION_STRING);
gBaseImageHandle = BaseImageHandle;

/* 0. Parse arguments */
Expand Down Expand Up @@ -334,6 +335,7 @@ EFI_STATUS EFIAPI efi_main(
L"Only the first one will be used.",
NULL
});
RecallPrintRestore();
Installable.List[PK].NumEntries = 1;
};

Expand All @@ -350,6 +352,7 @@ EFI_STATUS EFIAPI efi_main(
L"If this is not what you want, please select 'Cancel' now.",
NULL
});
RecallPrintRestore();
if (Sel != 0)
goto exit;
}
Expand Down Expand Up @@ -396,7 +399,7 @@ EFI_STATUS EFIAPI efi_main(
L"DON'T INSTALL",
NULL
}, 1);
// TODO: Clear screen after selection
RecallPrintRestore();
SafeFree(Installable.List[Type].Path[Entry]);
if (Sel == 0) {
Installable.List[Type].Path[Entry] = StrDup(L"[SELECT]");
Expand All @@ -421,26 +424,27 @@ EFI_STATUS EFIAPI efi_main(
Title,
NULL
}, L"\\", L".cer|.crt|.esl|.bin", &Installable.List[Type].Path[Entry]);
RecallPrintRestore();
if (EFI_ERROR(Status))
continue;
if (!SimpleFileExistsByPath(gBaseImageHandle, Installable.List[Type].Path[Entry])) {
Print(L"No valid file selected for %a[%d] - Ignoring\n", KeyInfo[Type].Name, Entry);
RecallPrint(L"No valid file selected for %a[%d] - Ignoring\n", KeyInfo[Type].Name, Entry);
continue;
}
}

Installable.List[Type].Esl[Entry] = LoadToEsl(Installable.List[Type].Path[Entry]);

if (Installable.List[Type].Esl[Entry] == NULL) {
Print(L"Failed to load %a[%d] - Aborting\n", KeyInfo[Type].Name, Entry);
RecallPrint(L"Failed to load %a[%d] - Aborting\n", KeyInfo[Type].Name, Entry);
goto exit;
}
}
}

/* 6. Generate a keyless PK cert if none was specified */
if (Installable.List[PK].Esl[0] == NULL) {
Print(L"Generating PK certificate...\n");
RecallPrint(L"Generating PK certificate...\n");
SafeFree(Installable.List[PK].Path[0]);
Installable.List[PK].Path[0] = StrDup(L"AutoGenerated");
Cert = GenerateCredentials("Mosby Generated PK", NULL);
Expand All @@ -455,7 +459,7 @@ EFI_STATUS EFIAPI efi_main(
for (Entry = 0; Entry < Installable.List[DB].NumEntries &&
StrCmp(Installable.List[DB].Path[Entry], L"[GENERATE]") != 0; Entry++);
if (Entry < Installable.List[DB].NumEntries) {
Print(L"Generating Secure Boot signing credentials...\n");
RecallPrint(L"Generating Secure Boot signing credentials...\n");
SafeFree(Installable.List[DB].Path[Entry]);
Installable.List[DB].Path[Entry] = StrDup(L"AutoGenerated");
Cert = GenerateCredentials("Secure Boot signing", &Key);;
Expand All @@ -467,14 +471,14 @@ EFI_STATUS EFIAPI efi_main(
Status = SaveCredentials(Cert, Key, MOSBY_CRED_NAME);
if (EFI_ERROR(Status))
goto exit;
Print(L"Saved Secure Boot signing credentials as '%s'\n", MOSBY_CRED_NAME);
RecallPrint(L"Saved Secure Boot signing credentials as '%s'\n", MOSBY_CRED_NAME);
}

/* 8. Install the cert and DBX variables, making sure that we finish with the PK. */
for (Type = MAX_TYPES - 1; Type >= 0; Type--) {
for (Entry = 0; Entry < Installable.List[Type].NumEntries; Entry++) {
if (Installable.List[Type].Esl[Entry] != NULL) {
Print(L"Installing %a[%d] (from %s)\n", KeyInfo[Type].Name, Entry, Installable.List[Type].Path[Entry]);
RecallPrint(L"Installing %a[%d] (from %s)\n", KeyInfo[Type].Name, Entry, Installable.List[Type].Path[Entry]);
SetStatus = SetSecureBootVariable(Installable.List[Type].Esl[Entry], Type, (Entry != 0));
if (EFI_ERROR(SetStatus))
Status = SetStatus;
Expand All @@ -490,5 +494,6 @@ EFI_STATUS EFIAPI efi_main(
}
FreePool(Installable.ListData);
FreePool(Argv);
RecallPrintFree();
return Status;
}
3 changes: 0 additions & 3 deletions src/mosby.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,3 @@ typedef struct {

/* FreePool() replacement, that NULLs the freed pointer. */
#define SafeFree(p) do { FreePool(p); p = NULL; } while(0)

/* Error reporting macro */
#define ReportErrorAndExit(...) do { Print(__VA_ARGS__); goto exit; } while(0)
6 changes: 3 additions & 3 deletions src/pki.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ STATIC int OpenSSLErrorCallback(
VOID *UserData
)
{
Print(L"%s %a\n", (CHAR16*)UserData, AsciiString);
RecallPrint(L"%s %a\n", (CHAR16*)UserData, AsciiString);
return 0;
}

Expand All @@ -75,7 +75,7 @@ EFI_STATUS InitializePki(VOID)
if (Seed != NULL && Seed[0] != L'\0') {
RAND_seed(Seed, StrLen(Seed));
} else {
Print(L"Notice: Using hardcoded default random seed\n");
RecallPrint(L"Notice: Using hardcoded default random seed\n");
RAND_seed(DefaultSeed, sizeof(DefaultSeed));
}
FreePool(Seed);
Expand Down Expand Up @@ -190,7 +190,7 @@ VOID* GenerateCredentials(
ReportOpenSSLErrorAndExit(EFI_PROTOCOL_ERROR);
// Might as well verify the signature while we're at it
if (!X509_verify(Cert, Key))
Print(L"WARNING: Failed to verify autogenerated X509 credentials\n");
RecallPrint(L"WARNING: Failed to verify autogenerated X509 credentials\n");
Status = EFI_SUCCESS;

exit:
Expand Down
Loading

0 comments on commit e4fdef7

Please sign in to comment.