1
0
mirror of https://github.com/systemd/systemd.git synced 2024-10-27 10:25:37 +03:00

Merge pull request #20483 from medhefgo/boot

sd-boot: Add support for changing console mode at runtime
This commit is contained in:
Lennart Poettering 2021-09-16 00:03:41 +02:00 committed by GitHub
commit 28078aa5cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 275 additions and 178 deletions

View File

@ -151,6 +151,16 @@
<listitem><para>Decrease the timeout</para></listitem>
</varlistentry>
<varlistentry>
<term><keycap>r</keycap></term>
<listitem><para>Change screen resolution, skipping any unsupported modes.</para></listitem>
</varlistentry>
<varlistentry>
<term><keycap>R</keycap></term>
<listitem><para>Reset screen resolution to firmware or configuration file default.</para></listitem>
</varlistentry>
<varlistentry>
<term><keycap>p</keycap></term>
<listitem><para>Print status</para></listitem>

View File

@ -69,8 +69,8 @@ typedef struct {
BOOLEAN auto_entries;
BOOLEAN auto_firmware;
BOOLEAN force_menu;
UINTN console_mode;
enum console_mode_change_type console_mode_change;
INT64 console_mode;
INT64 console_mode_efivar;
RandomSeedMode random_seed_mode;
} Config;
@ -370,13 +370,13 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
UINTN timeout;
BOOLEAN modevar;
_cleanup_freepool_ CHAR16 *partstr = NULL, *defaultstr = NULL;
UINTN x, y;
UINTN x_max, y_max;
assert(config);
assert(loaded_image_path);
uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, COLOR_NORMAL);
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
clear_screen(COLOR_NORMAL);
console_query_mode(&x_max, &y_max);
Print(L"systemd-boot version: " GIT_VERSION "\n");
Print(L"architecture: " EFI_MACHINE_TYPE_NAME "\n");
@ -384,10 +384,8 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
Print(L"UEFI specification: %d.%02d\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
Print(L"firmware vendor: %s\n", ST->FirmwareVendor);
Print(L"firmware version: %d.%02d\n", ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
if (uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &x, &y) == EFI_SUCCESS)
Print(L"console size: %d x %d\n", x, y);
Print(L"console mode: %d/%ld\n", ST->ConOut->Mode->Mode, ST->ConOut->Mode->MaxMode - 1LL);
Print(L"console size: %d x %d\n", x_max, y_max);
Print(L"SecureBoot: %s\n", yes_no(secure_boot_enabled()));
if (efivar_get_boolean_u8(EFI_GLOBAL_GUID, L"SetupMode", &modevar) == EFI_SUCCESS)
@ -499,8 +497,6 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
Print(L"\n--- press key ---\n\n");
console_key_read(&key, 0);
}
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
}
static BOOLEAN menu_run(
@ -513,107 +509,115 @@ static BOOLEAN menu_run(
assert(loaded_image_path);
EFI_STATUS err;
UINTN visible_max;
UINTN visible_max = 0;
UINTN idx_highlight = config->idx_default;
UINTN idx_highlight_prev = 0;
UINTN idx_first;
UINTN idx_last;
BOOLEAN refresh = TRUE;
BOOLEAN highlight = FALSE;
UINTN line_width = 0;
UINTN entry_padding = 3;
CHAR16 **lines;
UINTN x_start;
UINTN y_start;
UINTN x_max;
UINTN y_max;
CHAR16 *status = NULL;
CHAR16 *clearline;
UINTN idx_first = 0, idx_last = 0;
BOOLEAN new_mode = TRUE, clear = TRUE;
BOOLEAN refresh = TRUE, highlight = FALSE;
UINTN x_start = 0, y_start = 0, y_status = 0;
UINTN x_max, y_max;
CHAR16 **lines = NULL, *status = NULL, *clearline = NULL;
UINTN timeout_remain = config->timeout_sec;
INT16 idx;
BOOLEAN exit = FALSE;
BOOLEAN run = TRUE;
BOOLEAN exit = FALSE, run = TRUE;
INT64 console_mode_initial = ST->ConOut->Mode->Mode, console_mode_efivar_saved = config->console_mode_efivar;
graphics_mode(FALSE);
uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE);
uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, COLOR_NORMAL);
/* draw a single character to make ClearScreen work on some firmware */
Print(L" ");
if (config->console_mode_change != CONSOLE_MODE_KEEP) {
err = console_set_mode(&config->console_mode, config->console_mode_change);
if (EFI_ERROR(err)) {
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
log_error_stall(L"Error switching console mode to %lu: %r", (UINT64)config->console_mode, err);
}
} else
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
err = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &x_max, &y_max);
err = console_set_mode(config->console_mode_efivar != CONSOLE_MODE_KEEP ?
config->console_mode_efivar : config->console_mode);
if (EFI_ERROR(err)) {
x_max = 80;
y_max = 25;
clear_screen(COLOR_NORMAL);
log_error_stall(L"Error switching console mode: %r", err);
}
visible_max = y_max - 2;
/* Drawing entries starts at idx_first until idx_last. We want to make
* sure that idx_highlight is centered, but not if we are close to the
* beginning/end of the entry list. Otherwise we would have a half-empty
* screen. */
if (config->entry_count <= visible_max || idx_highlight <= visible_max / 2)
idx_first = 0;
else if (idx_highlight >= config->entry_count - (visible_max / 2))
idx_first = config->entry_count - visible_max;
else
idx_first = idx_highlight - (visible_max / 2);
idx_last = idx_first + visible_max - 1;
/* length of the longest entry */
for (UINTN i = 0; i < config->entry_count; i++)
line_width = MAX(line_width, StrLen(config->entries[i]->title_show));
line_width = MIN(line_width + 2 * entry_padding, x_max);
/* offsets to center the entries on the screen */
x_start = (x_max - (line_width)) / 2;
if (config->entry_count < visible_max)
y_start = ((visible_max - config->entry_count) / 2) + 1;
else
y_start = 0;
/* menu entries title lines */
lines = AllocatePool(sizeof(CHAR16 *) * config->entry_count);
for (UINTN i = 0; i < config->entry_count; i++) {
UINTN j;
lines[i] = AllocatePool(((line_width + 1) * sizeof(CHAR16)));
UINTN padding = (line_width - MIN(StrLen(config->entries[i]->title_show), line_width)) / 2;
for (j = 0; j < padding; j++)
lines[i][j] = ' ';
for (UINTN k = 0; config->entries[i]->title_show[k] != '\0' && j < line_width; j++, k++)
lines[i][j] = config->entries[i]->title_show[k];
for (; j < line_width; j++)
lines[i][j] = ' ';
lines[i][line_width] = '\0';
}
clearline = AllocatePool((x_max+1) * sizeof(CHAR16));
for (UINTN i = 0; i < x_max; i++)
clearline[i] = ' ';
clearline[x_max] = 0;
while (!exit) {
UINT64 key;
if (refresh) {
if (new_mode) {
UINTN line_width = 0, entry_padding = 3;
console_query_mode(&x_max, &y_max);
/* account for padding+status */
visible_max = y_max - 2;
/* Drawing entries starts at idx_first until idx_last. We want to make
* sure that idx_highlight is centered, but not if we are close to the
* beginning/end of the entry list. Otherwise we would have a half-empty
* screen. */
if (config->entry_count <= visible_max || idx_highlight <= visible_max / 2)
idx_first = 0;
else if (idx_highlight >= config->entry_count - (visible_max / 2))
idx_first = config->entry_count - visible_max;
else
idx_first = idx_highlight - (visible_max / 2);
idx_last = idx_first + visible_max - 1;
/* length of the longest entry */
for (UINTN i = 0; i < config->entry_count; i++)
line_width = MAX(line_width, StrLen(config->entries[i]->title_show));
line_width = MIN(line_width + 2 * entry_padding, x_max);
/* offsets to center the entries on the screen */
x_start = (x_max - (line_width)) / 2;
if (config->entry_count < visible_max)
y_start = ((visible_max - config->entry_count) / 2) + 1;
else
y_start = 0;
/* Put status line after the entry list, but give it some breathing room. */
y_status = MIN(y_start + MIN(visible_max, config->entry_count) + 4, y_max - 1);
if (lines) {
for (UINTN i = 0; i < config->entry_count; i++)
FreePool(lines[i]);
FreePool(lines);
FreePool(clearline);
}
/* menu entries title lines */
lines = AllocatePool(sizeof(CHAR16 *) * config->entry_count);
for (UINTN i = 0; i < config->entry_count; i++) {
if (i < idx_first || i > idx_last)
continue;
UINTN j, padding;
lines[i] = AllocatePool(((line_width + 1) * sizeof(CHAR16)));
padding = (line_width - MIN(StrLen(config->entries[i]->title_show), line_width)) / 2;
for (j = 0; j < padding; j++)
lines[i][j] = ' ';
for (UINTN k = 0; config->entries[i]->title_show[k] != '\0' && j < line_width; j++, k++)
lines[i][j] = config->entries[i]->title_show[k];
for (; j < line_width; j++)
lines[i][j] = ' ';
lines[i][line_width] = '\0';
}
clearline = AllocatePool((x_max+1) * sizeof(CHAR16));
for (UINTN i = 0; i < x_max; i++)
clearline[i] = ' ';
clearline[x_max] = 0;
new_mode = FALSE;
clear = TRUE;
}
if (clear) {
clear_screen(COLOR_NORMAL);
clear = FALSE;
refresh = TRUE;
}
if (refresh) {
for (UINTN i = idx_first; i <= idx_last && i < config->entry_count; i++) {
print_at(x_start, y_start + i - idx_first,
(i == idx_highlight) ? COLOR_HIGHLIGHT : COLOR_ENTRY,
lines[i]);
@ -649,7 +653,7 @@ static BOOLEAN menu_run(
x = (x_max - len) / 2;
else
x = 0;
print_at(0, y_max - 1, COLOR_NORMAL, clearline + (x_max - x));
print_at(0, y_status, COLOR_NORMAL, clearline + (x_max - x));
uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, status);
uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1 + x + len);
}
@ -671,7 +675,7 @@ static BOOLEAN menu_run(
if (status) {
FreePool(status);
status = NULL;
print_at(0, y_max - 1, COLOR_NORMAL, clearline + 1);
print_at(0, y_status, COLOR_NORMAL, clearline + 1);
}
idx_highlight_prev = idx_highlight;
@ -733,7 +737,7 @@ static BOOLEAN menu_run(
case KEYPRESS(0, 0, 'H'):
case KEYPRESS(0, 0, '?'):
/* This must stay below 80 characters! Q/v/Ctrl+l deliberately not advertised. */
status = StrDuplicate(L"(d)efault (t/T)timeout (e)dit (p)rint (h)elp");
status = StrDuplicate(L"(d)efault (t/T)timeout (e)dit (r/R)resolution (p)rint (h)elp");
break;
case KEYPRESS(0, 0, 'Q'):
@ -813,9 +817,9 @@ static BOOLEAN menu_run(
* causing a scroll to happen that screws with our beautiful boot loader output.
* Since we cannot paint the last character of the edit line, we simply start
* at x-offset 1 for symmetry. */
print_at(1, y_max - 1, COLOR_EDIT, clearline + 2);
exit = line_edit(config->entries[idx_highlight]->options, &config->options_edit, x_max-2, y_max-1);
print_at(1, y_max - 1, COLOR_NORMAL, clearline + 2);
print_at(1, y_status, COLOR_EDIT, clearline + 2);
exit = line_edit(config->entries[idx_highlight]->options, &config->options_edit, x_max - 2, y_status);
print_at(1, y_status, COLOR_NORMAL, clearline + 2);
break;
case KEYPRESS(0, 0, 'v'):
@ -827,12 +831,35 @@ static BOOLEAN menu_run(
case KEYPRESS(0, 0, 'p'):
case KEYPRESS(0, 0, 'P'):
print_status(config, loaded_image_path);
refresh = TRUE;
clear = TRUE;
break;
case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'l'):
case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('l')):
refresh = TRUE;
clear = TRUE;
break;
case KEYPRESS(0, 0, 'r'):
err = console_set_mode(CONSOLE_MODE_NEXT);
if (EFI_ERROR(err))
status = PoolPrint(L"Error changing console mode: %r", err);
else {
config->console_mode_efivar = ST->ConOut->Mode->Mode;
status = PoolPrint(L"Console mode changed to %ld.", config->console_mode_efivar);
}
new_mode = TRUE;
break;
case KEYPRESS(0, 0, 'R'):
config->console_mode_efivar = CONSOLE_MODE_KEEP;
err = console_set_mode(config->console_mode == CONSOLE_MODE_KEEP ?
console_mode_initial : config->console_mode);
if (EFI_ERROR(err))
status = PoolPrint(L"Error resetting console mode: %r", err);
else
status = PoolPrint(L"Console mode reset to %s default.",
config->console_mode == CONSOLE_MODE_KEEP ? L"firmware" : L"configuration file");
new_mode = TRUE;
break;
default:
@ -860,13 +887,23 @@ static BOOLEAN menu_run(
*chosen_entry = config->entries[idx_highlight];
/* The user is likely to cycle through several modes before
* deciding to keep one. Therefore, we update the EFI var after
* we left the menu to reduce nvram writes. */
if (console_mode_efivar_saved != config->console_mode_efivar) {
if (config->console_mode_efivar == CONSOLE_MODE_KEEP)
efivar_set(LOADER_GUID, L"LoaderConfigConsoleMode", NULL, EFI_VARIABLE_NON_VOLATILE);
else
efivar_set_uint_string(LOADER_GUID, L"LoaderConfigConsoleMode",
config->console_mode_efivar, EFI_VARIABLE_NON_VOLATILE);
}
for (UINTN i = 0; i < config->entry_count; i++)
FreePool(lines[i]);
FreePool(lines);
FreePool(clearline);
uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, COLOR_NORMAL);
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
clear_screen(COLOR_NORMAL);
return run;
}
@ -1027,17 +1064,16 @@ static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
if (strcmpa((CHAR8 *)"console-mode", key) == 0) {
if (strcmpa((CHAR8 *)"auto", value) == 0)
config->console_mode_change = CONSOLE_MODE_AUTO;
config->console_mode = CONSOLE_MODE_AUTO;
else if (strcmpa((CHAR8 *)"max", value) == 0)
config->console_mode_change = CONSOLE_MODE_MAX;
config->console_mode = CONSOLE_MODE_FIRMWARE_MAX;
else if (strcmpa((CHAR8 *)"keep", value) == 0)
config->console_mode_change = CONSOLE_MODE_KEEP;
config->console_mode = CONSOLE_MODE_KEEP;
else {
_cleanup_freepool_ CHAR16 *s = NULL;
s = stra_to_str(value);
config->console_mode = Atoi(s);
config->console_mode_change = CONSOLE_MODE_SET;
config->console_mode = MIN(Atoi(s), (UINTN)CONSOLE_MODE_RANGE_MAX);
}
continue;
@ -1410,7 +1446,7 @@ static VOID config_entry_add_from_file(
static VOID config_load_defaults(Config *config, EFI_FILE *root_dir) {
_cleanup_freepool_ CHAR8 *content = NULL;
UINTN sec;
UINTN value;
EFI_STATUS err;
assert(root_dir);
@ -1421,27 +1457,33 @@ static VOID config_load_defaults(Config *config, EFI_FILE *root_dir) {
.auto_firmware = TRUE,
.random_seed_mode = RANDOM_SEED_WITH_SYSTEM_TOKEN,
.idx_default_efivar = -1,
.console_mode = CONSOLE_MODE_KEEP,
.console_mode_efivar = CONSOLE_MODE_KEEP,
};
err = file_read(root_dir, L"\\loader\\loader.conf", 0, 0, &content, NULL);
if (!EFI_ERROR(err))
config_defaults_load_from_file(config, content);
err = efivar_get_uint_string(LOADER_GUID, L"LoaderConfigTimeout", &sec);
err = efivar_get_uint_string(LOADER_GUID, L"LoaderConfigTimeout", &value);
if (!EFI_ERROR(err)) {
config->timeout_sec_efivar = sec > INTN_MAX ? INTN_MAX : sec;
config->timeout_sec = sec;
config->timeout_sec_efivar = value > INTN_MAX ? INTN_MAX : value;
config->timeout_sec = value;
} else
config->timeout_sec_efivar = -1;
err = efivar_get_uint_string(LOADER_GUID, L"LoaderConfigTimeoutOneShot", &sec);
err = efivar_get_uint_string(LOADER_GUID, L"LoaderConfigTimeoutOneShot", &value);
if (!EFI_ERROR(err)) {
/* Unset variable now, after all it's "one shot". */
(void) efivar_set(LOADER_GUID, L"LoaderConfigTimeoutOneShot", NULL, EFI_VARIABLE_NON_VOLATILE);
config->timeout_sec = sec;
config->timeout_sec = value;
config->force_menu = TRUE; /* force the menu when this is set */
}
err = efivar_get_uint_string(LOADER_GUID, L"LoaderConfigConsoleMode", &value);
if (!EFI_ERROR(err))
config->console_mode_efivar = value;
}
static VOID config_load_entries(

View File

@ -8,6 +8,9 @@
#define SYSTEM_FONT_WIDTH 8
#define SYSTEM_FONT_HEIGHT 19
#define HORIZONTAL_MAX_OK 1920
#define VERTICAL_MAX_OK 1080
#define VIEWPORT_RATIO 10
#define EFI_SIMPLE_TEXT_INPUT_EX_GUID \
&(const EFI_GUID) EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID
@ -115,51 +118,36 @@ EFI_STATUS console_key_read(UINT64 *key, UINT64 timeout_usec) {
return EFI_SUCCESS;
}
static EFI_STATUS change_mode(UINTN mode) {
static EFI_STATUS change_mode(INT64 mode) {
EFI_STATUS err;
INT32 old_mode;
/* SetMode expects a UINTN, so make sure these values are sane. */
mode = CLAMP(mode, CONSOLE_MODE_RANGE_MIN, CONSOLE_MODE_RANGE_MAX);
old_mode = MAX(CONSOLE_MODE_RANGE_MIN, ST->ConOut->Mode->Mode);
err = uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, mode);
if (!EFI_ERROR(err))
return EFI_SUCCESS;
/* 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);
/* Something went wrong. Output is probably borked, so try to revert to previous mode. */
if (!EFI_ERROR(uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, old_mode)))
return err;
/* Maybe the device is on fire? */
uefi_call_wrapper(ST->ConOut->Reset, 2, ST->ConOut, TRUE);
uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, CONSOLE_MODE_RANGE_MIN);
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;
static const EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
static INT64 get_auto_mode(void) {
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
EFI_STATUS err;
BOOLEAN keep = FALSE;
assert(mode);
err = LibLocateProtocol((EFI_GUID*) &GraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput);
err = LibLocateProtocol(&GraphicsOutputProtocol, (VOID **)&GraphicsOutput);
if (!EFI_ERROR(err) && GraphicsOutput->Mode && GraphicsOutput->Mode->Info) {
Info = GraphicsOutput->Mode->Info;
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info = GraphicsOutput->Mode->Info;
BOOLEAN keep = FALSE;
/* 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
@ -170,19 +158,19 @@ static EFI_STATUS mode_auto(UINTN *mode) {
* 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();
UINT64 text_area;
UINTN x_max, y_max;
UINT64 screen_area = (UINT64)Info->HorizontalResolution * (UINT64)Info->VerticalResolution;
console_query_mode(&x_max, &y_max);
text_area = SYSTEM_FONT_WIDTH * SYSTEM_FONT_HEIGHT * (UINT64)x_max * (UINT64)y_max;
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 (keep)
return ST->ConOut->Mode->Mode;
}
/* If we reached here, then we have a high resolution screen and the text
@ -191,32 +179,72 @@ static EFI_STATUS mode_auto(UINTN *mode) {
* 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;
if (ST->ConOut->Mode->MaxMode > CONSOLE_MODE_FIRMWARE_FIRST)
return CONSOLE_MODE_FIRMWARE_FIRST;
/* 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;
if (ST->ConOut->Mode->MaxMode > CONSOLE_MODE_80_50)
return CONSOLE_MODE_80_50;
return change_mode(*mode);
return CONSOLE_MODE_80_25;
}
EFI_STATUS console_set_mode(UINTN *mode, enum console_mode_change_type how) {
assert(mode);
EFI_STATUS console_set_mode(INT64 mode) {
switch (mode) {
case CONSOLE_MODE_KEEP:
/* If the firmware indicates the current mode is invalid, change it anyway. */
if (ST->ConOut->Mode->Mode < CONSOLE_MODE_RANGE_MIN)
return change_mode(CONSOLE_MODE_RANGE_MIN);
return EFI_SUCCESS;
if (how == CONSOLE_MODE_AUTO)
return mode_auto(mode);
case CONSOLE_MODE_NEXT:
if (ST->ConOut->Mode->MaxMode <= CONSOLE_MODE_RANGE_MIN)
return EFI_UNSUPPORTED;
if (how == CONSOLE_MODE_MAX) {
mode = MAX(CONSOLE_MODE_RANGE_MIN, ST->ConOut->Mode->Mode);
do {
mode = (mode + 1) % ST->ConOut->Mode->MaxMode;
if (!EFI_ERROR(change_mode(mode)))
break;
/* If this mode is broken/unsupported, try the next.
* If mode is 0, we wrapped around and should stop. */
} while (mode > CONSOLE_MODE_RANGE_MIN);
return EFI_SUCCESS;
case CONSOLE_MODE_AUTO:
return change_mode(get_auto_mode());
case CONSOLE_MODE_FIRMWARE_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(ST->ConOut->Mode->MaxMode - 1LL);
default:
return change_mode(mode);
}
}
EFI_STATUS console_query_mode(UINTN *x_max, UINTN *y_max) {
EFI_STATUS err;
assert(x_max);
assert(y_max);
err = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, x_max, y_max);
if (EFI_ERROR(err)) {
/* Fallback values mandated by UEFI spec. */
switch (ST->ConOut->Mode->Mode) {
case CONSOLE_MODE_80_50:
*x_max = 80;
*y_max = 50;
break;
case CONSOLE_MODE_80_25:
default:
*x_max = 80;
*y_max = 25;
}
}
return change_mode(*mode);
return err;
}

View File

@ -9,12 +9,23 @@
#define KEYCHAR(k) ((k) & 0xffff)
#define CHAR_CTRL(c) ((c) - 'a' + 1)
enum console_mode_change_type {
CONSOLE_MODE_KEEP = 0,
CONSOLE_MODE_SET,
enum {
/* Console mode is a INT32 in EFI. We use INT64 to make room for our special values. */
CONSOLE_MODE_RANGE_MIN = 0,
CONSOLE_MODE_RANGE_MAX = INT32_MAX, /* This is just the theoretical limit. */
CONSOLE_MODE_INVALID = -1, /* UEFI uses -1 if the device is not in a valid text mode. */
CONSOLE_MODE_80_25 = 0, /* 80x25 is required by UEFI spec. */
CONSOLE_MODE_80_50 = 1, /* 80x50 may be supported. */
CONSOLE_MODE_FIRMWARE_FIRST = 2, /* First custom mode, if supported. */
/* These are our own mode values that map to concrete values at runtime. */
CONSOLE_MODE_KEEP = CONSOLE_MODE_RANGE_MAX + 1LL,
CONSOLE_MODE_NEXT,
CONSOLE_MODE_AUTO,
CONSOLE_MODE_MAX,
CONSOLE_MODE_FIRMWARE_MAX, /* 'max' in config. */
};
EFI_STATUS console_key_read(UINT64 *key, UINT64 timeout_usec);
EFI_STATUS console_set_mode(UINTN *mode, enum console_mode_change_type how);
EFI_STATUS console_set_mode(INT64 mode);
EFI_STATUS console_query_mode(UINTN *x_max, UINTN *y_max);

View File

@ -522,3 +522,8 @@ VOID print_at(UINTN x, UINTN y, UINTN attr, const CHAR16 *str) {
uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, attr);
uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, (CHAR16*)str);
}
VOID clear_screen(UINTN attr) {
uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, attr);
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
}

View File

@ -93,3 +93,4 @@ static inline VOID *mempmem_safe(const VOID *haystack, UINTN haystack_len, const
}
VOID print_at(UINTN x, UINTN y, UINTN attr, const CHAR16 *str);
VOID clear_screen(UINTN attr);