From: Henrique Dante de Almeida Date: Sat, 16 Dec 2017 18:23:06 +0000 (-0200) Subject: boot/efi: add console-mode boot option to change resolution X-Git-Tag: v239~575^2~2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=68d4b8ac9b9673637fa198b735f6e64b78b35d3b;p=thirdparty%2Fsystemd.git boot/efi: add console-mode boot option to change resolution The new boot option allows changing the display console mode to some new display resolution. Usage: in systemd-boot configuration file loader/loader.conf add one of: console-mode console-mode auto console-mode max console-mode keep Where number can be: 1) 0: UEFI standard 80x25 mode 2) 1: 80x50 mode, if supported by device 3) 2 or greater: some non-standard device mode, if supported When using the auto mode, systemd-boot will try to find a reasonable mode automatically using some heuristic. Current implementation tries to switch console mode to mode 2 if it exists, mode 1 if it exists, else mode 0. Mode 2 is the first non standard mode provided by the firmware developer and it's assumed to be a reasonable mode. The max mode adds support for switching to the highest numbered console mode, whatever mode that would be in the system. The maximum mode being the last one implemented by the firmware provider should be a reasonable mode. The keep mode is the default and is only included for completeness. With this parameter, the user explicitly states and can be assured that systemd-boot will not change console mode. Note: patch includes a workaround for mode 1, that may not exist in EDK2 OVFM and results in a buggy text mode when requested. --- diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 06331da2d41..107e2451fca 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 4487ed099fa..241167b9f84 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 5664a4e62f8..6296d467c4c 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