]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
boot/efi: add console-mode boot option to change resolution
authorHenrique Dante de Almeida <hdante@gmail.com>
Sat, 16 Dec 2017 18:23:06 +0000 (16:23 -0200)
committerHenrique Dante de Almeida <hdante@gmail.com>
Sat, 3 Feb 2018 19:32:04 +0000 (17:32 -0200)
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 <number>
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.

src/boot/efi/boot.c
src/boot/efi/console.c
src/boot/efi/console.h

index 06331da2d41c2fc07c5ac06a759fe7152b6a758e..107e2451fca8f07af6c33b0d033cdfe5b7c07627 100644 (file)
@@ -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;
+                }
+
         }
 }
 
index 4487ed099faf8c7a5e6412ba164faf9e6e589695..241167b9f84071a262faa41cee1d114a7150a8f2 100644 (file)
@@ -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);
+}
index 5664a4e62f81b61909bf242c594a24382c86b999..6296d467c4cd465ae731dd5713c3d020b95e4e6b 100644 (file)
 #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