diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 06331da2d4..107e2451fc 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -69,6 +69,8 @@ typedef struct { CHAR16 *entry_oneshot; CHAR16 *options_edit; BOOLEAN no_editor; + UINTN console_mode; + enum console_mode_change_type console_mode_change; } Config; static VOID cursor_left(UINTN *cursor, UINTN *first) { @@ -491,6 +493,7 @@ static BOOLEAN menu_run(Config *config, ConfigEntry **chosen_entry, CHAR16 *load BOOLEAN exit = FALSE; BOOLEAN run = TRUE; BOOLEAN wait = FALSE; + BOOLEAN cleared_screen = FALSE; graphics_mode(FALSE); uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE); @@ -499,7 +502,18 @@ static BOOLEAN menu_run(Config *config, ConfigEntry **chosen_entry, CHAR16 *load /* draw a single character to make ClearScreen work on some firmware */ uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L" "); - uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); + + if (config->console_mode_change != CONSOLE_MODE_KEEP) { + err = console_set_mode(&config->console_mode, config->console_mode_change); + if (!EFI_ERROR(err)) + cleared_screen = TRUE; + } + + if (!cleared_screen) + uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); + + if (config->console_mode_change != CONSOLE_MODE_KEEP && EFI_ERROR(err)) + Print(L"Error switching console mode to %ld: %r.\r", (UINT64)config->console_mode, err); err = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &x_max, &y_max); if (EFI_ERROR(err)) { @@ -1008,6 +1022,26 @@ static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) { continue; config->no_editor = !on; } + + if (strcmpa((CHAR8 *)"console-mode", key) == 0) { + CHAR16 *s; + + if (strcmpa((CHAR8 *)"auto", value) == 0) + config->console_mode_change = CONSOLE_MODE_AUTO; + else if (strcmpa((CHAR8 *)"max", value) == 0) + config->console_mode_change = CONSOLE_MODE_MAX; + else if (strcmpa((CHAR8 *)"keep", value) == 0) + config->console_mode_change = CONSOLE_MODE_KEEP; + else { + s = stra_to_str(value); + config->console_mode = Atoi(s); + config->console_mode_change = CONSOLE_MODE_SET; + FreePool(s); + } + + continue; + } + } } diff --git a/src/boot/efi/console.c b/src/boot/efi/console.c index 4487ed099f..241167b9f8 100644 --- a/src/boot/efi/console.c +++ b/src/boot/efi/console.c @@ -134,3 +134,48 @@ EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait) { *key = KEYPRESS(0, k.ScanCode, k.UnicodeChar); return 0; } + +static EFI_STATUS change_mode(UINTN mode) { + EFI_STATUS err; + + err = uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, mode); + + /* Special case mode 1: when using OVMF and qemu, setting it returns error + * and breaks console output. */ + if (EFI_ERROR(err) && mode == 1) + uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, (UINTN)0); + + return err; +} + +static EFI_STATUS mode_auto(UINTN *mode) { + /* Mode number 2 is first non standard mode, which is provided by + * the device manufacturer, so it should be a good mode. + * Note: MaxMode is the number of modes, not the last mode. */ + if (ST->ConOut->Mode->MaxMode > 2) + *mode = 2; + /* Try again with mode different than zero (assume user requests + * auto mode due to some problem with mode zero). */ + else if (ST->ConOut->Mode->MaxMode == 2) + *mode = 1; + /* Else force mode change to zero. */ + else + *mode = 0; + + return change_mode(*mode); +} + +EFI_STATUS console_set_mode(UINTN *mode, enum console_mode_change_type how) { + if (how == CONSOLE_MODE_AUTO) + return mode_auto(mode); + + if (how == CONSOLE_MODE_MAX) { + /* Note: MaxMode is the number of modes, not the last mode. */ + if (ST->ConOut->Mode->MaxMode > 0) + *mode = ST->ConOut->Mode->MaxMode-1; + else + *mode = 0; + } + + return change_mode(*mode); +} diff --git a/src/boot/efi/console.h b/src/boot/efi/console.h index 5664a4e62f..6296d467c4 100644 --- a/src/boot/efi/console.h +++ b/src/boot/efi/console.h @@ -29,5 +29,13 @@ #define KEYCHAR(k) ((k) & 0xffff) #define CHAR_CTRL(c) ((c) - 'a' + 1) +enum console_mode_change_type { + CONSOLE_MODE_KEEP = 0, + CONSOLE_MODE_SET, + CONSOLE_MODE_AUTO, + CONSOLE_MODE_MAX, +}; + EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait); +EFI_STATUS console_set_mode(UINTN *mode, enum console_mode_change_type how); #endif