boot.c revision 2f8d336478536af789d654599f9523d02e0ca693
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/*
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* Copyright (C) 2012-2015 Kay Sievers <kay@vrfy.org>
* Copyright (C) 2012-2015 Harald Hoyer <harald@redhat.com>
*/
#include <efi.h>
#include <efilib.h>
#include "util.h"
#include "console.h"
#include "graphics.h"
#include "splash.h"
#include "pefile.h"
#include "linux.h"
#ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
#endif
/* magic string to find in the binary image */
enum loader_type {
};
typedef struct {
enum loader_type type;
} ConfigEntry;
typedef struct {
} Config;
{
if ((*cursor) > 0)
(*cursor)--;
else if ((*first) > 0)
(*first)--;
}
{
(*cursor)++;
(*first)++;
}
if (!line_in)
line_in = L"";
first = 0;
cursor = 0;
clear = 0;
while (!exit) {
UINTN i;
if (i >= x_max-1)
i = x_max-1;
clear--;
print[i++] = ' ';
}
print[i] = '\0';
continue;
switch (key) {
break;
/* beginning-of-line */
cursor = 0;
first = 0;
continue;
/* end-of-line */
}
continue;
/* forward-word */
continue;
/* backward-word */
}
continue;
case KEYPRESS(0, SCAN_RIGHT, 0):
/* forward-char */
continue;
continue;
/* backward-char */
continue;
/* kill-word */
clear = 0;
clear++;
clear++;
continue;
/* backward-kill-word */
clear = 0;
clear++;
clear++;
}
}
clear++;
}
continue;
case KEYPRESS(0, SCAN_DELETE, 0):
if (len == 0)
continue;
continue;
clear = 1;
len--;
continue;
/* kill-line */
continue;
case KEYPRESS(0, 0, CHAR_LINEFEED):
case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
}
break;
case KEYPRESS(0, 0, CHAR_BACKSPACE):
if (len == 0)
continue;
continue;
clear = 1;
len--;
if (cursor > 0)
cursor--;
continue;
/* show full line if it fits */
first = 0;
continue;
}
/* jump left to see what we delete */
if (first > 10) {
first -= 10;
cursor = 10;
} else {
first = 0;
}
continue;
continue;
len++;
cursor++;
first++;
continue;
}
}
return enter;
}
UINTN i;
if (key == 0)
return -1;
/* select entry by number key */
i = key - '0';
if (i > config->entry_count)
i = config->entry_count;
return i-1;
}
/* find matching key in config entries */
return i;
for (i = 0; i < start; i++)
return i;
return -1;
}
UINTN i;
CHAR16 *s;
CHAR8 *b;
UINTN x;
UINTN y;
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);
FreePool(b);
}
FreePool(b);
}
FreePool(b);
}
Print(L"\n");
if (config->timeout_sec_efivar >= 0)
if (config->entry_default_pattern)
Print(L"\n");
if (config->idx_default_efivar >= 0)
Print(L"\n");
Print(L"LoaderConfigTimeout: %d\n", i);
if (config->entry_oneshot)
Print(L"LoaderDeviceIdentifier: %s\n", s);
FreePool(s);
}
Print(L"LoaderDevicePartUUID: %s\n", s);
FreePool(s);
}
Print(L"LoaderEntryDefault: %s\n", s);
FreePool(s);
}
Print(L"\n--- press key ---\n\n");
for (i = 0; i < config->entry_count; i++) {
break;
if (len > 0) {
for (;;) {
static const EFI_GRAPHICS_OUTPUT_BLT_PIXEL colors[] = {
};
break;
/* 'b' rotates through background colors */
break;
color = 0;
}
}
}
if (entry->machine_id)
if (device_path) {
}
}
Print(L"internal call yes\n");
Print(L"\n--- press key ---\n\n");
}
}
static BOOLEAN menu_run(Config *config, ConfigEntry **chosen_entry, EFI_FILE *root_dir, CHAR16 *loaded_image_path) {
UINTN i;
/* draw a single character to make ClearScreen work on some firmware */
err = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &x_max, &y_max);
x_max = 80;
y_max = 25;
}
/* we check 10 times per second for a keystroke */
if (config->timeout_sec > 0)
else
timeout_remain = -1;
idx_highlight_prev = 0;
else
idx_first = 0;
/* length of the longest entry */
line_width = 5;
for (i = 0; i < config->entry_count; i++) {
if (line_width < entry_len)
}
/* offsets to center the entries on the screen */
else
y_start = 0;
/* menu entries title lines */
for (i = 0; i < config->entry_count; i++) {
UINTN j, k;
for (j = 0; j < x_start; j++)
lines[i][j] = ' ';
for (; j < x_max; j++)
lines[i][j] = ' ';
}
for (i = 0; i < x_max; i++)
clearline[i] = ' ';
clearline[i] = 0;
while (!exit) {
if (refresh) {
for (i = 0; i < config->entry_count; i++) {
continue;
if (i == idx_highlight)
else
uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + i - idx_first);
}
}
} else if (highlight) {
uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight_prev - idx_first);
uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight_prev - idx_first);
}
uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight - idx_first);
uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight - idx_first);
}
}
if (timeout_remain > 0) {
}
/* print status at last line of screen */
if (status) {
UINTN x;
/* center line */
else
x = 0;
}
/* timeout reached */
if (timeout_remain == 0) {
break;
}
/* sleep and update status */
if (timeout_remain > 0) {
continue;
}
/* timeout disabled, wait for next key */
continue;
}
timeout_remain = -1;
/* clear status after keystroke */
if (status) {
}
switch (key) {
case KEYPRESS(0, 0, 'k'):
if (idx_highlight > 0)
break;
case KEYPRESS(0, 0, 'j'):
break;
if (idx_highlight > 0) {
idx_highlight = 0;
}
break;
}
break;
case KEYPRESS(0, SCAN_PAGE_UP, 0):
if (idx_highlight > visible_max)
else
idx_highlight = 0;
break;
case KEYPRESS(0, SCAN_PAGE_DOWN, 0):
break;
case KEYPRESS(0, 0, CHAR_LINEFEED):
case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
break;
case KEYPRESS(0, 0, 'h'):
case KEYPRESS(0, 0, '?'):
break;
case KEYPRESS(0, 0, 'Q'):
break;
case KEYPRESS(0, 0, 'd'):
/* store the selected entry in a persistent EFI variable */
} else {
/* clear the default entry EFI variable */
}
break;
case KEYPRESS(0, 0, '-'):
case KEYPRESS(0, 0, 'T'):
if (config->timeout_sec_efivar > 0) {
if (config->timeout_sec_efivar > 0)
else
} else if (config->timeout_sec_efivar <= 0){
if (config->timeout_sec_config > 0)
else
}
break;
case KEYPRESS(0, 0, '+'):
case KEYPRESS(0, 0, 't'):
if (config->timeout_sec_efivar > 0)
else
break;
case KEYPRESS(0, 0, 'e'):
/* only the options of configured entries can be edited */
break;
break;
case KEYPRESS(0, 0, 'v'):
status = PoolPrint(L"systemd-boot " VERSION " (" EFI_MACHINE_TYPE_NAME "), UEFI Specification %d.%02d, Vendor %s %d.%02d",
break;
case KEYPRESS(0, 0, 'P'):
break;
break;
default:
/* jump with a hotkey directly to a matching entry */
if (idx < 0)
break;
idx_highlight = idx;
}
if (idx_highlight > idx_last) {
}
if (idx_highlight < idx_first) {
}
}
for (i = 0; i < config->entry_count; i++)
return run;
}
UINTN i;
if (config->entry_count == 0)
else
}
}
}
{
return (c >= '0') && (c <= '9');
}
{
if (c == '\0')
return 0;
if (is_digit(c))
return 0;
else if ((c >= 'a') && (c <= 'z'))
return c;
else
return c + 0x10000;
}
{
if (order)
return order;
s1++;
s2++;
}
while (*s1 == '0')
s1++;
while (*s2 == '0')
s2++;
first = 0;
if (first == 0)
s1++;
s2++;
}
return 1;
return -1;
if (first)
return first;
}
}
static CHAR8 *line_get_key_value(CHAR8 *content, CHAR8 *sep, UINTN *pos, CHAR8 **key_ret, CHAR8 **value_ret) {
skip:
if (*line == '\0')
return NULL;
linelen = 0;
linelen++;
/* move pos to next line */
(*pos)++;
/* empty line */
if (linelen == 0)
goto skip;
/* terminate line */
/* remove leading whitespace */
line++;
linelen--;
}
/* remove trailing whitespace */
linelen--;
if (*line == '#')
goto skip;
value++;
if (*value == '\0')
goto skip;
*value = '\0';
value++;
value++;
/* unquote */
value++;
}
return line;
}
CHAR16 *s;
s = stra_to_str(value);
FreePool(s);
continue;
}
continue;
}
}
}
static VOID config_entry_add_from_file(Config *config, EFI_HANDLE *device, CHAR16 *file, CHAR8 *content, CHAR16 *loaded_image_path) {
continue;
}
continue;
}
continue;
}
continue;
}
/* do not add an entry for ourselves */
break;
}
continue;
}
/* do not add an entry for an EFI image of architecture not matching with that of the image */
break;
}
continue;
}
if (initrd) {
CHAR16 *s;
initrd = s;
} else
continue;
}
CHAR16 *s;
} else {
}
continue;
}
continue;
}
}
return;
}
/* add initrd= to options */
CHAR16 *s;
} else {
}
}
if (entry->machine_id) {
/* append additional options from EFI variables for this machine-id */
if (var) {
CHAR16 *s;
} else
}
}
if (var) {
CHAR16 *s;
} else
}
}
}
/* remove ".conf" */
if (len > 5)
}
static VOID config_load(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path) {
UINTN i;
if (len > 0)
} else
err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &entries_dir, L"\\loader\\entries", EFI_FILE_MODE_READ, 0ULL);
for (;;) {
EFI_FILE_INFO *f;
break;
f = (EFI_FILE_INFO *) buf;
if (f->FileName[0] == '.')
continue;
if (f->Attribute & EFI_FILE_DIRECTORY)
continue;
if (len < 6)
continue;
continue;
if (len > 0)
}
}
/* sort entries after version number */
UINTN k;
for (k = 0; k < config->entry_count - i; k++) {
continue;
}
if (!more)
break;
}
}
UINTN i;
/*
* The EFI variable to specify a boot entry for the next, and only the
* next reboot. The variable is always cleared directly after it is read.
*/
for (i = 0; i < config->entry_count; i++) {
config->idx_default = i;
break;
}
}
if (found)
return;
}
/*
* The EFI variable to select the default boot entry overrides the
* configured pattern. The variable can be set and cleared by pressing
* the 'd' key in the loader selection menu, the entry is marked with
* an '*'.
*/
for (i = 0; i < config->entry_count; i++) {
config->idx_default = i;
config->idx_default_efivar = i;
break;
}
}
if (found)
return;
}
if (config->entry_count == 0)
return;
/*
* Match the pattern from the end of the list to the start, find last
* entry (largest number) matching the given pattern.
*/
if (config->entry_default_pattern) {
i = config->entry_count;
while (i--) {
continue;
config->idx_default = i;
return;
}
}
}
/* select the last suitable entry */
i = config->entry_count;
while (i--) {
continue;
config->idx_default = i;
return;
}
/* no entry found */
}
/* generate a unique title, avoiding non-distinguishable menu entries */
UINTN i, k;
/* set title */
for (i = 0; i < config->entry_count; i++) {
if (!title)
}
for (i = 0; i < config->entry_count; i++) {
for (k = 0; k < config->entry_count; k++) {
if (i == k)
continue;
continue;
}
}
if (unique)
return;
/* add version to non-unique titles */
for (i = 0; i < config->entry_count; i++) {
CHAR16 *s;
continue;
continue;
}
for (i = 0; i < config->entry_count; i++) {
for (k = 0; k < config->entry_count; k++) {
if (i == k)
continue;
continue;
}
}
if (unique)
return;
/* add machine-id to non-unique titles */
for (i = 0; i < config->entry_count; i++) {
CHAR16 *s;
CHAR16 *m;
continue;
continue;
m[8] = '\0';
FreePool(m);
}
for (i = 0; i < config->entry_count; i++) {
for (k = 0; k < config->entry_count; k++) {
if (i == k)
continue;
continue;
}
}
if (unique)
return;
/* add file name to non-unique titles */
for (i = 0; i < config->entry_count; i++) {
CHAR16 *s;
continue;
}
}
return TRUE;
}
return entry;
}
static BOOLEAN config_entry_add_loader_auto(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path,
/* do not add an entry for ourselves */
return FALSE;
/* check existence */
return FALSE;
if (!entry)
return FALSE;
/* do not boot right away into auto-detected entries */
/* export identifiers of automatically added entries */
if (config->entries_auto) {
CHAR16 *s;
config->entries_auto = s;
} else
return TRUE;
}
UINTN handle_count = 0;
UINTN i;
for (i = 0; i < handle_count; i++) {
if (!root)
continue;
L"\\System\\Library\\CoreServices\\boot.efi");
if (found)
break;
}
}
}
static VOID config_entry_add_linux( Config *config, EFI_LOADED_IMAGE *loaded_image, EFI_FILE *root_dir) {
err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &linux_dir, L"\\EFI\\Linux", EFI_FILE_MODE_READ, 0ULL);
for (;;) {
EFI_FILE_INFO *f;
(UINT8 *)".osrel",
};
break;
f = (EFI_FILE_INFO *) buf;
if (f->FileName[0] == '.')
continue;
if (f->Attribute & EFI_FILE_DIRECTORY)
continue;
if (len < 5)
continue;
continue;
/* look for an .osrel section in the .efi binary */
continue;
if (len <= 0)
continue;
/* read properties from the embedded os-release file */
continue;
}
continue;
}
continue;
}
}
config_entry_add_loader(config, loaded_image->DeviceHandle, LOADER_LINUX, conf, 'l', os_name, path);
}
}
}
}
static EFI_STATUS image_start(EFI_HANDLE parent_image, const Config *config, const ConfigEntry *entry) {
if (!path) {
Print(L"Error getting device path.");
return EFI_INVALID_PARAMETER;
}
goto out;
}
if (config->options_edit)
else
if (options) {
goto out_unload;
}
}
efivar_set_time_usec(L"LoaderTimeExecUSec", 0);
out:
return err;
}
CHAR8 *b;
FreePool(b);
return err;
return err;
}
UINTN i;
for (i = 0; i < config->entry_count; i++)
}
CHAR16 *s;
CHAR8 *b;
s = PoolPrint(L"%s %d.%02d", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
FreePool(s);
FreePool(s);
return err;
}
/* export the device path this image is started from */
if (device_path) {
continue;
continue;
continue;
break;
}
}
if (!root_dir) {
return EFI_LOAD_ERROR;
}
/* the filesystem path to this image, to prevent adding ourselves to the menu */
/* scan "\loader\entries\*.conf" files */
/* if we find some well-known loaders, add them to the end of the list */
L"auto-windows", 'w', L"Windows Boot Manager", L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
L"auto-efi-default", '\0', L"EFI Default Loader", L"\\EFI\\Boot\\boot" EFI_MACHINE_TYPE_NAME ".efi");
FreePool(b);
}
if (config.entry_count == 0) {
Print(L"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
goto out;
}
/* select entry by configured pattern or EFI LoaderDefaultEntry= variable*/
/* if no configured entry to select from was found, enable the menu */
config.idx_default = 0;
if (config.timeout_sec == 0)
}
/* select entry or show menu when key is pressed or timeout is set */
if (config.timeout_sec == 0) {
/* find matching key in config entries */
if (idx >= 0)
else
}
} else
for (;;) {
if (menu) {
efivar_set_time_usec(L"LoaderTimeMenuUSec", 0);
break;
/* run special entry like "reboot" */
continue;
}
if (len > 0)
}
/* export the selected boot entry to the system */
goto out;
}
config.timeout_sec = 0;
}
err = EFI_SUCCESS;
out:
return err;
}