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 <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.
This commit is contained in:
Henrique Dante de Almeida
2018-02-03 17:32:04 -02:00
parent cf6e1e6333
commit 68d4b8ac9b
3 changed files with 88 additions and 1 deletions
+35 -1
View 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;
}
}
}
+45
View 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);
}
+8
View File
@@ -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