Merge pull request #8086 from hdante/sdboot-setmode-v2

This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2018-03-07 10:41:44 +01:00
commit 48af1eb6ee
6 changed files with 152 additions and 1 deletions

View file

@ -827,6 +827,7 @@ static int install_loader_config(const char *esp_path) {
}
fprintf(f, "#timeout 3\n");
fprintf(f, "#console-mode keep\n");
fprintf(f, "default %s-*\n", sd_id128_to_string(machine_id, machine_string));
r = fflush_sync_and_check(f);

View file

@ -71,6 +71,8 @@ typedef struct {
BOOLEAN editor;
BOOLEAN auto_entries;
BOOLEAN auto_firmware;
UINTN console_mode;
enum console_mode_change_type console_mode_change;
} Config;
static VOID cursor_left(UINTN *cursor, UINTN *first) {
@ -497,6 +499,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);
@ -505,7 +508,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)) {
@ -1030,6 +1044,26 @@ static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
continue;
config->auto_firmware = 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;
}
}
}

View file

@ -20,6 +20,9 @@
#include "console.h"
#include "util.h"
#define SYSTEM_FONT_WIDTH 8
#define SYSTEM_FONT_HEIGHT 19
#define EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID \
{ 0xdd9e7534, 0x7762, 0x4698, { 0x8c, 0x14, 0xf5, 0x85, 0x17, 0xa6, 0x25, 0xaa } }
@ -134,3 +137,105 @@ 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 UINT64 text_area_from_font_size(void) {
EFI_STATUS err;
UINT64 text_area;
UINTN rows, columns;
err = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &columns, &rows);
if (EFI_ERROR(err)) {
columns = 80;
rows = 25;
}
text_area = SYSTEM_FONT_WIDTH * SYSTEM_FONT_HEIGHT * (UINT64)rows * (UINT64)columns;
return text_area;
}
static EFI_STATUS mode_auto(UINTN *mode) {
const UINT32 HORIZONTAL_MAX_OK = 1920;
const UINT32 VERTICAL_MAX_OK = 1080;
const UINT64 VIEWPORT_RATIO = 10;
UINT64 screen_area, text_area;
EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
EFI_STATUS err;
BOOLEAN keep = FALSE;
err = LibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput);
if (!EFI_ERROR(err) && GraphicsOutput->Mode && GraphicsOutput->Mode->Info) {
Info = GraphicsOutput->Mode->Info;
/* Start verifying if we are in a resolution larger than Full HD
* (1920x1080). If we're not, assume we're in a good mode and do not
* try to change it. */
if (Info->HorizontalResolution <= HORIZONTAL_MAX_OK && Info->VerticalResolution <= VERTICAL_MAX_OK)
keep = TRUE;
/* For larger resolutions, calculate the ratio of the total screen
* area to the text viewport area. If it's less than 10 times bigger,
* then assume the text is readable and keep the text mode. */
else {
screen_area = (UINT64)Info->HorizontalResolution * (UINT64)Info->VerticalResolution;
text_area = text_area_from_font_size();
if (text_area != 0 && screen_area/text_area < VIEWPORT_RATIO)
keep = TRUE;
}
}
if (keep) {
/* Just clear the screen instead of changing the mode and return. */
*mode = ST->ConOut->Mode->Mode;
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
return EFI_SUCCESS;
}
/* If we reached here, then we have a high resolution screen and the text
* viewport is less than 10% the screen area, so the firmware developer
* screwed up. Try to switch to a better 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);
}

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

View file

@ -206,6 +206,8 @@ int boot_loader_read_conf(const char *path, BootConfig *config) {
r = free_and_strdup(&config->auto_entries, p);
else if (streq(buf, "auto-firmware"))
r = free_and_strdup(&config->auto_firmware, p);
else if (streq(buf, "console-mode"))
r = free_and_strdup(&config->console_mode, p);
else {
log_notice("%s:%u: Unknown line \"%s\"", path, line, field);
continue;

View file

@ -42,6 +42,7 @@ typedef struct BootConfig {
char *editor;
char *auto_entries;
char *auto_firmware;
char *console_mode;
char *entry_oneshot;
char *entry_default;