mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-02-03 13:47:04 +03:00
Merge pull request #22550 from medhefgo/boot-mixed
boot: EFI mixed mode support
This commit is contained in:
commit
ebd4571e31
@ -341,6 +341,11 @@
|
||||
token when adding a second installation.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--all-architectures</option></term>
|
||||
<listitem><para>Install binaries for all supported EFI architectures (this implies <option>--no-variables</option>).</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="no-pager"/>
|
||||
<xi:include href="standard-options.xml" xpointer="json" />
|
||||
<xi:include href="standard-options.xml" xpointer="help"/>
|
||||
|
@ -29,10 +29,6 @@ static inline char* strstr_ptr(const char *haystack, const char *needle) {
|
||||
return strstr(haystack, needle);
|
||||
}
|
||||
|
||||
static inline const char* strempty(const char *s) {
|
||||
return s ?: "";
|
||||
}
|
||||
|
||||
static inline const char* strnull(const char *s) {
|
||||
return s ?: "(null)";
|
||||
}
|
||||
@ -181,13 +177,6 @@ int free_and_strndup(char **p, const char *s, size_t l);
|
||||
|
||||
bool string_is_safe(const char *p) _pure_;
|
||||
|
||||
static inline size_t strlen_ptr(const char *s) {
|
||||
if (!s)
|
||||
return 0;
|
||||
|
||||
return strlen(s);
|
||||
}
|
||||
|
||||
DISABLE_WARNING_STRINGOP_TRUNCATION;
|
||||
static inline void strncpy_exact(char *buf, const char *src, size_t buf_len) {
|
||||
strncpy(buf, src, buf_len);
|
||||
|
@ -140,12 +140,6 @@ static inline int strv_from_nulstr(char ***a, const char *nulstr) {
|
||||
|
||||
bool strv_overlap(char * const *a, char * const *b) _pure_;
|
||||
|
||||
#define _STRV_FOREACH(s, l, i) \
|
||||
for (typeof(*(l)) *s, *i = (l); (s = i) && *i; i++)
|
||||
|
||||
#define STRV_FOREACH(s, l) \
|
||||
_STRV_FOREACH(s, l, UNIQ_T(i, UNIQ))
|
||||
|
||||
#define _STRV_FOREACH_BACKWARDS(s, l, h, i) \
|
||||
for (typeof(*(l)) *s, *h = (l), *i = ({ \
|
||||
size_t _len = strv_length(h); \
|
||||
|
@ -74,6 +74,7 @@ static enum {
|
||||
} arg_entry_token_type = ARG_ENTRY_TOKEN_AUTO;
|
||||
static char *arg_entry_token = NULL;
|
||||
static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
|
||||
static bool arg_arch_all = false;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep);
|
||||
@ -413,6 +414,36 @@ finish:
|
||||
return r;
|
||||
}
|
||||
|
||||
static const char *get_efi_arch(void) {
|
||||
/* Detect EFI firmware architecture of the running system. On mixed mode systems, it could be 32bit
|
||||
* while the kernel is running in 64bit. */
|
||||
|
||||
#ifdef __x86_64__
|
||||
_cleanup_free_ char *platform_size = NULL;
|
||||
int r;
|
||||
|
||||
r = read_one_line_file("/sys/firmware/efi/fw_platform_size", &platform_size);
|
||||
if (r == -ENOENT)
|
||||
return EFI_MACHINE_TYPE_NAME;
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Error reading EFI firmware word size, assuming '%u': %m", __WORDSIZE);
|
||||
return EFI_MACHINE_TYPE_NAME;
|
||||
}
|
||||
|
||||
if (streq(platform_size, "64"))
|
||||
return EFI_MACHINE_TYPE_NAME;
|
||||
if (streq(platform_size, "32"))
|
||||
return "ia32";
|
||||
|
||||
log_warning(
|
||||
"Unknown EFI firmware word size '%s', using default word size '%u' instead.",
|
||||
platform_size,
|
||||
__WORDSIZE);
|
||||
#endif
|
||||
|
||||
return EFI_MACHINE_TYPE_NAME;
|
||||
}
|
||||
|
||||
static int enumerate_binaries(const char *esp_path, const char *path, const char *prefix) {
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
const char *p;
|
||||
@ -829,7 +860,7 @@ static int copy_one_file(const char *esp_path, const char *name, bool force) {
|
||||
return r;
|
||||
}
|
||||
|
||||
static int install_binaries(const char *esp_path, bool force) {
|
||||
static int install_binaries(const char *esp_path, const char *arch, bool force) {
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
int r = 0;
|
||||
|
||||
@ -837,10 +868,13 @@ static int install_binaries(const char *esp_path, bool force) {
|
||||
if (!d)
|
||||
return log_error_errno(errno, "Failed to open \""BOOTLIBDIR"\": %m");
|
||||
|
||||
const char *suffix = strjoina(arch, ".efi");
|
||||
const char *suffix_signed = strjoina(arch, ".efi.signed");
|
||||
|
||||
FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read \""BOOTLIBDIR"\": %m")) {
|
||||
int k;
|
||||
|
||||
if (!endswith_no_case(de->d_name, ".efi") && !endswith_no_case(de->d_name, ".efi.signed"))
|
||||
if (!endswith_no_case(de->d_name, suffix) && !endswith_no_case(de->d_name, suffix_signed))
|
||||
continue;
|
||||
|
||||
/* skip the .efi file, if there's a .signed version of it */
|
||||
@ -1327,6 +1361,8 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" Entry token to use for this installation\n"
|
||||
" --json=pretty|short|off\n"
|
||||
" Generate JSON output\n"
|
||||
" --all-architectures\n"
|
||||
" Install all supported EFI architectures\n"
|
||||
"\nSee the %2$s for details.\n",
|
||||
program_invocation_short_name,
|
||||
link,
|
||||
@ -1349,6 +1385,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_MAKE_ENTRY_DIRECTORY,
|
||||
ARG_ENTRY_TOKEN,
|
||||
ARG_JSON,
|
||||
ARG_ARCH_ALL,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@ -1368,6 +1405,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "make-machine-id-directory", required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY }, /* Compatibility alias */
|
||||
{ "entry-token", required_argument, NULL, ARG_ENTRY_TOKEN },
|
||||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{ "all-architectures", no_argument, NULL, ARG_ARCH_ALL },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -1470,7 +1508,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
r = parse_json_argument(optarg, &arg_json_format_flags);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
case ARG_ARCH_ALL:
|
||||
arg_arch_all = true;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
@ -1601,13 +1642,14 @@ static int verb_status(int argc, char *argv[], void *userdata) {
|
||||
|
||||
SecureBootMode secure = efi_get_secure_boot_mode();
|
||||
printf("System:\n");
|
||||
printf(" Firmware: %s%s (%s)%s\n", ansi_highlight(), strna(fw_type), strna(fw_info), ansi_normal());
|
||||
printf(" Secure Boot: %sd (%s)\n",
|
||||
printf(" Firmware: %s%s (%s)%s\n", ansi_highlight(), strna(fw_type), strna(fw_info), ansi_normal());
|
||||
printf(" Firmware Arch: %s\n", get_efi_arch());
|
||||
printf(" Secure Boot: %sd (%s)\n",
|
||||
enable_disable(IN_SET(secure, SECURE_BOOT_USER, SECURE_BOOT_DEPLOYED)),
|
||||
secure_boot_mode_to_string(secure));
|
||||
|
||||
s = tpm2_support();
|
||||
printf(" TPM2 Support: %s%s%s\n",
|
||||
printf(" TPM2 Support: %s%s%s\n",
|
||||
FLAGS_SET(s, TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER) ? ansi_highlight_green() :
|
||||
(s & (TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER)) != 0 ? ansi_highlight_red() : ansi_highlight_yellow(),
|
||||
FLAGS_SET(s, TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER) ? "yes" :
|
||||
@ -1617,14 +1659,14 @@ static int verb_status(int argc, char *argv[], void *userdata) {
|
||||
|
||||
k = efi_get_reboot_to_firmware();
|
||||
if (k > 0)
|
||||
printf(" Boot into FW: %sactive%s\n", ansi_highlight_yellow(), ansi_normal());
|
||||
printf(" Boot into FW: %sactive%s\n", ansi_highlight_yellow(), ansi_normal());
|
||||
else if (k == 0)
|
||||
printf(" Boot into FW: supported\n");
|
||||
printf(" Boot into FW: supported\n");
|
||||
else if (k == -EOPNOTSUPP)
|
||||
printf(" Boot into FW: not supported\n");
|
||||
printf(" Boot into FW: not supported\n");
|
||||
else {
|
||||
errno = -k;
|
||||
printf(" Boot into FW: %sfailed%s (%m)\n", ansi_highlight_red(), ansi_normal());
|
||||
printf(" Boot into FW: %sfailed%s (%m)\n", ansi_highlight_red(), ansi_normal());
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
@ -1926,6 +1968,8 @@ static int verb_install(int argc, char *argv[], void *userdata) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
const char *arch = arg_arch_all ? "" : get_efi_arch();
|
||||
|
||||
RUN_WITH_UMASK(0002) {
|
||||
if (install) {
|
||||
/* Don't create any of these directories when we are just updating. When we update
|
||||
@ -1940,7 +1984,7 @@ static int verb_install(int argc, char *argv[], void *userdata) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = install_binaries(arg_esp_path, install);
|
||||
r = install_binaries(arg_esp_path, arch, install);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -1969,13 +2013,16 @@ static int verb_install(int argc, char *argv[], void *userdata) {
|
||||
|
||||
(void) sync_everything();
|
||||
|
||||
if (arg_touch_variables)
|
||||
r = install_variables(arg_esp_path,
|
||||
part, pstart, psize, uuid,
|
||||
"/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi",
|
||||
install);
|
||||
if (!arg_touch_variables)
|
||||
return 0;
|
||||
|
||||
return r;
|
||||
if (arg_arch_all) {
|
||||
log_info("Not changing EFI variables with --all-architectures.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *path = strjoina("/EFI/systemd/systemd-boot", arch, ".efi");
|
||||
return install_variables(arg_esp_path, part, pstart, psize, uuid, path, install);
|
||||
}
|
||||
|
||||
static int verb_remove(int argc, char *argv[], void *userdata) {
|
||||
@ -2041,7 +2088,13 @@ static int verb_remove(int argc, char *argv[], void *userdata) {
|
||||
if (!arg_touch_variables)
|
||||
return r;
|
||||
|
||||
q = remove_variables(uuid, "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi", true);
|
||||
if (arg_arch_all) {
|
||||
log_info("Not changing EFI variables with --all-architectures.");
|
||||
return r;
|
||||
}
|
||||
|
||||
char *path = strjoina("/EFI/systemd/systemd-boot", get_efi_arch(), ".efi");
|
||||
q = remove_variables(uuid, path, true);
|
||||
if (q < 0 && r >= 0)
|
||||
r = q;
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "drivers.h"
|
||||
#include "efivars-fundamental.h"
|
||||
#include "graphics.h"
|
||||
#include "initrd.h"
|
||||
#include "linux.h"
|
||||
#include "measure.h"
|
||||
#include "pe.h"
|
||||
@ -61,6 +62,7 @@ typedef struct {
|
||||
CHAR16 *loader;
|
||||
CHAR16 *devicetree;
|
||||
CHAR16 *options;
|
||||
CHAR16 **initrd;
|
||||
CHAR16 key;
|
||||
EFI_STATUS (*call)(void);
|
||||
UINTN tries_done;
|
||||
@ -82,7 +84,6 @@ typedef struct {
|
||||
CHAR16 *entry_default_efivar;
|
||||
CHAR16 *entry_oneshot;
|
||||
CHAR16 *entry_saved;
|
||||
CHAR16 *options_edit;
|
||||
BOOLEAN editor;
|
||||
BOOLEAN auto_entries;
|
||||
BOOLEAN auto_firmware;
|
||||
@ -139,24 +140,20 @@ static void cursor_right(
|
||||
}
|
||||
|
||||
static BOOLEAN line_edit(
|
||||
const CHAR16 *line_in,
|
||||
CHAR16 **line_out,
|
||||
CHAR16 **line_in,
|
||||
UINTN x_max,
|
||||
UINTN y_pos) {
|
||||
|
||||
_cleanup_freepool_ CHAR16 *line = NULL, *print = NULL;
|
||||
UINTN size, len, first = 0, cursor = 0, clear = 0;
|
||||
|
||||
assert(line_out);
|
||||
assert(line_in);
|
||||
|
||||
if (!line_in)
|
||||
line_in = L"";
|
||||
|
||||
len = StrLen(line_in);
|
||||
len = strlen_ptr(*line_in);
|
||||
size = len + 1024;
|
||||
line = xnew(CHAR16, size);
|
||||
print = xnew(CHAR16, x_max + 1);
|
||||
StrCpy(line, line_in);
|
||||
StrCpy(line, strempty(*line_in));
|
||||
|
||||
for (;;) {
|
||||
EFI_STATUS err;
|
||||
@ -320,8 +317,10 @@ static BOOLEAN line_edit(
|
||||
case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
|
||||
case KEYPRESS(0, CHAR_CARRIAGE_RETURN, 0): /* EZpad Mini 4s firmware sends malformed events */
|
||||
case KEYPRESS(0, CHAR_CARRIAGE_RETURN, CHAR_CARRIAGE_RETURN): /* Teclast X98+ II firmware sends malformed events */
|
||||
if (StrCmp(line, line_in) != 0)
|
||||
*line_out = TAKE_PTR(line);
|
||||
if (!streq(line, *line_in)) {
|
||||
FreePool(*line_in);
|
||||
*line_in = TAKE_PTR(line);
|
||||
}
|
||||
return TRUE;
|
||||
|
||||
case KEYPRESS(0, 0, CHAR_BACKSPACE):
|
||||
@ -562,6 +561,8 @@ static void print_status(Config *config, CHAR16 *loaded_image_path) {
|
||||
if (entry->device)
|
||||
Print(L" device: %D\n", DevicePathFromHandle(entry->device));
|
||||
ps_string(L" loader: %s\n", entry->loader);
|
||||
STRV_FOREACH(initrd, entry->initrd)
|
||||
Print(L" initrd: %s\n", *initrd);
|
||||
ps_string(L" devicetree: %s\n", entry->devicetree);
|
||||
ps_string(L" options: %s\n", entry->options);
|
||||
ps_bool(L" internal call: %s\n", !!entry->call);
|
||||
@ -915,7 +916,7 @@ static BOOLEAN menu_run(
|
||||
* Since we cannot paint the last character of the edit line, we simply start
|
||||
* at x-offset 1 for symmetry. */
|
||||
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);
|
||||
exit = line_edit(&config->entries[idx_highlight]->options, x_max - 2, y_status);
|
||||
print_at(1, y_status, COLOR_NORMAL, clearline + 2);
|
||||
break;
|
||||
|
||||
@ -1058,6 +1059,7 @@ static void config_entry_free(ConfigEntry *entry) {
|
||||
FreePool(entry->loader);
|
||||
FreePool(entry->devicetree);
|
||||
FreePool(entry->options);
|
||||
strv_free(entry->initrd);
|
||||
FreePool(entry->path);
|
||||
FreePool(entry->current_name);
|
||||
FreePool(entry->next_name);
|
||||
@ -1367,7 +1369,7 @@ good:
|
||||
prefix = xstrdup(file);
|
||||
prefix[i] = 0;
|
||||
|
||||
entry->next_name = xpool_print(L"%s+%" PRIuN L"-%" PRIuN L"%s", prefix, next_left, next_done, suffix ?: L"");
|
||||
entry->next_name = xpool_print(L"%s+%" PRIuN L"-%" PRIuN L"%s", prefix, next_left, next_done, strempty(suffix));
|
||||
}
|
||||
|
||||
static void config_entry_bump_counters(ConfigEntry *entry, EFI_FILE *root_dir) {
|
||||
@ -1430,10 +1432,9 @@ static void config_entry_add_from_file(
|
||||
|
||||
_cleanup_(config_entry_freep) ConfigEntry *entry = NULL;
|
||||
CHAR8 *line;
|
||||
UINTN pos = 0;
|
||||
UINTN pos = 0, n_initrd = 0;
|
||||
CHAR8 *key, *value;
|
||||
EFI_STATUS err;
|
||||
_cleanup_freepool_ CHAR16 *initrd = NULL;
|
||||
|
||||
assert(config);
|
||||
assert(device);
|
||||
@ -1510,18 +1511,12 @@ static void config_entry_add_from_file(
|
||||
}
|
||||
|
||||
if (strcmpa((CHAR8 *)"initrd", key) == 0) {
|
||||
_cleanup_freepool_ CHAR16 *new = NULL;
|
||||
|
||||
new = xstra_to_path(value);
|
||||
if (initrd) {
|
||||
CHAR16 *s;
|
||||
|
||||
s = xpool_print(L"%s initrd=%s", initrd, new);
|
||||
FreePool(initrd);
|
||||
initrd = s;
|
||||
} else
|
||||
initrd = xpool_print(L"initrd=%s", new);
|
||||
|
||||
entry->initrd = xreallocate_pool(
|
||||
entry->initrd,
|
||||
n_initrd == 0 ? 0 : (n_initrd + 1) * sizeof(UINT16 *),
|
||||
(n_initrd + 2) * sizeof(UINT16 *));
|
||||
entry->initrd[n_initrd++] = xstra_to_path(value);
|
||||
entry->initrd[n_initrd] = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1551,18 +1546,6 @@ static void config_entry_add_from_file(
|
||||
if (EFI_ERROR(err))
|
||||
return;
|
||||
|
||||
/* add initrd= to options */
|
||||
if (entry->type == LOADER_LINUX && initrd) {
|
||||
if (entry->options) {
|
||||
CHAR16 *s;
|
||||
|
||||
s = xpool_print(L"%s %s", initrd, entry->options);
|
||||
FreePool(entry->options);
|
||||
entry->options = s;
|
||||
} else
|
||||
entry->options = TAKE_PTR(initrd);
|
||||
}
|
||||
|
||||
entry->device = device;
|
||||
entry->id = xstrdup(file);
|
||||
StrLwr(entry->id);
|
||||
@ -2316,18 +2299,88 @@ static void config_load_xbootldr(
|
||||
config_load_entries(config, new_device, root_dir, NULL);
|
||||
}
|
||||
|
||||
static EFI_STATUS initrd_prepare(
|
||||
EFI_FILE *root,
|
||||
const ConfigEntry *entry,
|
||||
CHAR16 **ret_options,
|
||||
void **ret_initrd,
|
||||
UINTN *ret_initrd_size) {
|
||||
|
||||
assert(root);
|
||||
assert(entry);
|
||||
assert(ret_options);
|
||||
assert(ret_initrd);
|
||||
assert(ret_initrd_size);
|
||||
|
||||
if (entry->type != LOADER_LINUX || !entry->initrd) {
|
||||
ret_options = NULL;
|
||||
ret_initrd = NULL;
|
||||
ret_initrd_size = 0;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/* Note that order of initrds matters. The kernel will only look for microcode updates in the very
|
||||
* first one it sees. */
|
||||
|
||||
/* Add initrd= to options for older kernels that do not support LINUX_INITRD_MEDIA. Should be dropped
|
||||
* if linux_x86.c is dropped. */
|
||||
_cleanup_freepool_ CHAR16 *options = NULL;
|
||||
|
||||
EFI_STATUS err;
|
||||
UINTN size = 0;
|
||||
_cleanup_freepool_ UINT8 *initrd = NULL;
|
||||
|
||||
STRV_FOREACH(i, entry->initrd) {
|
||||
_cleanup_freepool_ CHAR16 *o = options;
|
||||
if (o)
|
||||
options = xpool_print(L"%s initrd=%s", o, *i);
|
||||
else
|
||||
options = xpool_print(L"initrd=%s", *i);
|
||||
|
||||
_cleanup_(file_closep) EFI_FILE *handle = NULL;
|
||||
err = root->Open(root, &handle, *i, EFI_FILE_MODE_READ, 0);
|
||||
if (EFI_ERROR(err))
|
||||
return err;
|
||||
|
||||
_cleanup_freepool_ EFI_FILE_INFO *info = NULL;
|
||||
err = get_file_info_harder(handle, &info, NULL);
|
||||
if (EFI_ERROR(err))
|
||||
return err;
|
||||
|
||||
UINTN new_size, read_size = info->FileSize;
|
||||
if (__builtin_add_overflow(size, read_size, &new_size))
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
initrd = xreallocate_pool(initrd, size, new_size);
|
||||
|
||||
err = handle->Read(handle, &read_size, initrd + size);
|
||||
if (EFI_ERROR(err))
|
||||
return err;
|
||||
|
||||
/* Make sure the actual read size is what we expected. */
|
||||
assert(size + read_size == new_size);
|
||||
size = new_size;
|
||||
}
|
||||
|
||||
if (entry->options) {
|
||||
_cleanup_freepool_ CHAR16 *o = options;
|
||||
options = xpool_print(L"%s %s", o, entry->options);
|
||||
}
|
||||
|
||||
*ret_options = TAKE_PTR(options);
|
||||
*ret_initrd = TAKE_PTR(initrd);
|
||||
*ret_initrd_size = size;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static EFI_STATUS image_start(
|
||||
EFI_HANDLE parent_image,
|
||||
const Config *config,
|
||||
const ConfigEntry *entry) {
|
||||
|
||||
_cleanup_(devicetree_cleanup) struct devicetree_state dtstate = {};
|
||||
EFI_HANDLE image;
|
||||
_cleanup_(unload_imagep) EFI_HANDLE image = NULL;
|
||||
_cleanup_freepool_ EFI_DEVICE_PATH *path = NULL;
|
||||
CHAR16 *options;
|
||||
EFI_STATUS err;
|
||||
|
||||
assert(config);
|
||||
assert(entry);
|
||||
|
||||
/* If this loader entry has a special way to boot, try that first. */
|
||||
@ -2342,6 +2395,13 @@ static EFI_STATUS image_start(
|
||||
if (!path)
|
||||
return log_error_status_stall(EFI_INVALID_PARAMETER, L"Error getting device path.");
|
||||
|
||||
UINTN initrd_size = 0;
|
||||
_cleanup_freepool_ void *initrd = NULL;
|
||||
_cleanup_freepool_ CHAR16 *options_initrd = NULL;
|
||||
err = initrd_prepare(image_root, entry, &options_initrd, &initrd, &initrd_size);
|
||||
if (EFI_ERROR(err))
|
||||
return log_error_status_stall(err, L"Error preparing initrd: %r", err);
|
||||
|
||||
err = BS->LoadImage(FALSE, parent_image, path, NULL, 0, &image);
|
||||
if (EFI_ERROR(err))
|
||||
return log_error_status_stall(err, L"Error loading %s: %r", entry->loader, err);
|
||||
@ -2352,23 +2412,20 @@ static EFI_STATUS image_start(
|
||||
return log_error_status_stall(err, L"Error loading %s: %r", entry->devicetree, err);
|
||||
}
|
||||
|
||||
if (config->options_edit)
|
||||
options = config->options_edit;
|
||||
else if (entry->options)
|
||||
options = entry->options;
|
||||
else
|
||||
options = NULL;
|
||||
if (options) {
|
||||
EFI_LOADED_IMAGE *loaded_image;
|
||||
_cleanup_(cleanup_initrd) EFI_HANDLE initrd_handle = NULL;
|
||||
err = initrd_register(initrd, initrd_size, &initrd_handle);
|
||||
if (EFI_ERROR(err))
|
||||
return log_error_status_stall(err, L"Error registering initrd: %r", err);
|
||||
|
||||
err = BS->OpenProtocol(image, &LoadedImageProtocol, (void **)&loaded_image,
|
||||
parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
|
||||
if (EFI_ERROR(err)) {
|
||||
log_error_stall(L"Error getting LoadedImageProtocol handle: %r", err);
|
||||
goto out_unload;
|
||||
}
|
||||
EFI_LOADED_IMAGE *loaded_image;
|
||||
err = BS->HandleProtocol(image, &LoadedImageProtocol, (void **) &loaded_image);
|
||||
if (EFI_ERROR(err))
|
||||
return log_error_status_stall(err, L"Error getting LoadedImageProtocol handle: %r", err);
|
||||
|
||||
CHAR16 *options = options_initrd ?: entry->options;
|
||||
if (options) {
|
||||
loaded_image->LoadOptions = options;
|
||||
loaded_image->LoadOptionsSize = StrSize(loaded_image->LoadOptions);
|
||||
loaded_image->LoadOptionsSize = StrSize(options);
|
||||
|
||||
/* Try to log any options to the TPM, especially to catch manually edited options */
|
||||
(void) tpm_log_load_options(options);
|
||||
@ -2376,9 +2433,30 @@ static EFI_STATUS image_start(
|
||||
|
||||
efivar_set_time_usec(LOADER_GUID, L"LoaderTimeExecUSec", 0);
|
||||
err = BS->StartImage(image, NULL, NULL);
|
||||
out_unload:
|
||||
BS->UnloadImage(image);
|
||||
return err;
|
||||
graphics_mode(FALSE);
|
||||
if (err == EFI_SUCCESS)
|
||||
return EFI_SUCCESS;
|
||||
|
||||
/* Try calling the kernel compat entry point if one exists. */
|
||||
if (err == EFI_UNSUPPORTED && entry->type == LOADER_LINUX) {
|
||||
UINT32 kernel_entry_address;
|
||||
|
||||
err = pe_alignment_info(loaded_image->ImageBase, &kernel_entry_address, NULL, NULL);
|
||||
if (EFI_ERROR(err)) {
|
||||
if (err != EFI_UNSUPPORTED)
|
||||
return log_error_status_stall(err, L"Error finding kernel compat entry address: %r", err);
|
||||
} else {
|
||||
EFI_IMAGE_ENTRY_POINT kernel_entry =
|
||||
(EFI_IMAGE_ENTRY_POINT) ((UINT8 *) loaded_image->ImageBase + kernel_entry_address);
|
||||
|
||||
err = kernel_entry(image, ST);
|
||||
graphics_mode(FALSE);
|
||||
if (err == EFI_SUCCESS)
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return log_error_status_stall(err, L"Failed to execute %s (%s): %r", entry->title_show, entry->loader, err);
|
||||
}
|
||||
|
||||
static void config_free(Config *config) {
|
||||
@ -2387,7 +2465,6 @@ static void config_free(Config *config) {
|
||||
config_entry_free(config->entries[i]);
|
||||
FreePool(config->entries);
|
||||
FreePool(config->entry_default_config);
|
||||
FreePool(config->options_edit);
|
||||
FreePool(config->entry_oneshot);
|
||||
}
|
||||
|
||||
@ -2622,12 +2699,11 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
|
||||
/* Optionally, read a random seed off the ESP and pass it to the OS */
|
||||
(void) process_random_seed(root_dir, config.random_seed_mode);
|
||||
|
||||
err = image_start(image, &config, entry);
|
||||
if (EFI_ERROR(err)) {
|
||||
graphics_mode(FALSE);
|
||||
log_error_stall(L"Failed to execute %s (%s): %r", entry->title_show, entry->loader, err);
|
||||
err = image_start(image, entry);
|
||||
if (err != EFI_SUCCESS)
|
||||
/* Not using EFI_ERROR here because positive values are also errors like with any
|
||||
* other (userspace) program. */
|
||||
goto out;
|
||||
}
|
||||
|
||||
menu = TRUE;
|
||||
config.timeout_sec = 0;
|
||||
|
@ -6,17 +6,12 @@
|
||||
#include "drivers.h"
|
||||
#include "util.h"
|
||||
|
||||
static void efi_unload_image(EFI_HANDLE *h) {
|
||||
if (*h)
|
||||
(void) BS->UnloadImage(*h);
|
||||
}
|
||||
|
||||
static EFI_STATUS load_one_driver(
|
||||
EFI_HANDLE parent_image,
|
||||
EFI_LOADED_IMAGE *loaded_image,
|
||||
const CHAR16 *fname) {
|
||||
|
||||
_cleanup_(efi_unload_image) EFI_HANDLE image = NULL;
|
||||
_cleanup_(unload_imagep) EFI_HANDLE image = NULL;
|
||||
_cleanup_freepool_ EFI_DEVICE_PATH *path = NULL;
|
||||
_cleanup_freepool_ CHAR16 *spath = NULL;
|
||||
EFI_STATUS err;
|
||||
|
@ -9,3 +9,8 @@ EFI_STATUS initrd_register(
|
||||
EFI_HANDLE *ret_initrd_handle);
|
||||
|
||||
EFI_STATUS initrd_unregister(EFI_HANDLE initrd_handle);
|
||||
|
||||
static inline void cleanup_initrd(EFI_HANDLE *initrd_handle) {
|
||||
(void) initrd_unregister(*initrd_handle);
|
||||
*initrd_handle = NULL;
|
||||
}
|
||||
|
@ -87,11 +87,6 @@ static EFI_STATUS loaded_image_unregister(EFI_HANDLE loaded_image_handle) {
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static inline void cleanup_initrd(EFI_HANDLE *initrd_handle) {
|
||||
(void) initrd_unregister(*initrd_handle);
|
||||
*initrd_handle = NULL;
|
||||
}
|
||||
|
||||
static inline void cleanup_loaded_image(EFI_HANDLE *loaded_image_handle) {
|
||||
(void) loaded_image_unregister(*loaded_image_handle);
|
||||
*loaded_image_handle = NULL;
|
||||
|
@ -351,6 +351,7 @@ common_sources = files(
|
||||
'devicetree.c',
|
||||
'disk.c',
|
||||
'graphics.c',
|
||||
'initrd.c',
|
||||
'measure.c',
|
||||
'pe.c',
|
||||
'secure-boot.c',
|
||||
@ -369,7 +370,6 @@ systemd_boot_sources = files(
|
||||
|
||||
stub_sources = files(
|
||||
'cpio.c',
|
||||
'initrd.c',
|
||||
'splash.c',
|
||||
'stub.c',
|
||||
)
|
||||
|
@ -12,17 +12,22 @@
|
||||
#define MAX_SECTIONS 96
|
||||
|
||||
#if defined(__i386__)
|
||||
#define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_IA32
|
||||
# define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_IA32
|
||||
# define TARGET_MACHINE_TYPE_COMPATIBILITY EFI_IMAGE_MACHINE_X64
|
||||
#elif defined(__x86_64__)
|
||||
#define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_X64
|
||||
# define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_X64
|
||||
#elif defined(__aarch64__)
|
||||
#define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_AARCH64
|
||||
# define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_AARCH64
|
||||
#elif defined(__arm__)
|
||||
#define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_ARMTHUMB_MIXED
|
||||
# define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_ARMTHUMB_MIXED
|
||||
#elif defined(__riscv) && __riscv_xlen == 64
|
||||
#define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_RISCV64
|
||||
# define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_RISCV64
|
||||
#else
|
||||
#error Unknown EFI arch
|
||||
# error Unknown EFI arch
|
||||
#endif
|
||||
|
||||
#ifndef TARGET_MACHINE_TYPE_COMPATIBILITY
|
||||
# define TARGET_MACHINE_TYPE_COMPATIBILITY 0
|
||||
#endif
|
||||
|
||||
struct DosFileHeader {
|
||||
@ -117,10 +122,11 @@ static inline BOOLEAN verify_dos(const struct DosFileHeader *dos) {
|
||||
return CompareMem(dos->Magic, DOS_FILE_MAGIC, STRLEN(DOS_FILE_MAGIC)) == 0;
|
||||
}
|
||||
|
||||
static inline BOOLEAN verify_pe(const struct PeFileHeader *pe) {
|
||||
static inline BOOLEAN verify_pe(const struct PeFileHeader *pe, BOOLEAN allow_compatibility) {
|
||||
assert(pe);
|
||||
return CompareMem(pe->Magic, PE_FILE_MAGIC, STRLEN(PE_FILE_MAGIC)) == 0 &&
|
||||
pe->FileHeader.Machine == TARGET_MACHINE_TYPE &&
|
||||
(pe->FileHeader.Machine == TARGET_MACHINE_TYPE ||
|
||||
(allow_compatibility && pe->FileHeader.Machine == TARGET_MACHINE_TYPE_COMPATIBILITY)) &&
|
||||
pe->FileHeader.NumberOfSections > 0 &&
|
||||
pe->FileHeader.NumberOfSections <= MAX_SECTIONS &&
|
||||
IN_SET(pe->OptionalHeader.Magic, OPTHDR32_MAGIC, OPTHDR64_MAGIC);
|
||||
@ -160,6 +166,49 @@ static void locate_sections(
|
||||
}
|
||||
}
|
||||
|
||||
static UINT32 get_compatibility_entry_address(const struct DosFileHeader *dos, const struct PeFileHeader *pe) {
|
||||
UINTN addr = 0, size = 0;
|
||||
static const CHAR8 *sections[] = { (CHAR8 *) ".compat", NULL };
|
||||
|
||||
/* The kernel may provide alternative PE entry points for different PE architectures. This allows
|
||||
* booting a 64bit kernel on 32bit EFI that is otherwise running on a 64bit CPU. The locations of any
|
||||
* such compat entry points are located in a special PE section. */
|
||||
|
||||
locate_sections((const struct PeSectionHeader *) ((const UINT8 *) dos + section_table_offset(dos, pe)),
|
||||
pe->FileHeader.NumberOfSections,
|
||||
sections,
|
||||
&addr,
|
||||
NULL,
|
||||
&size);
|
||||
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
typedef struct {
|
||||
UINT8 type;
|
||||
UINT8 size;
|
||||
UINT16 machine_type;
|
||||
UINT32 entry_point;
|
||||
} _packed_ LinuxPeCompat1;
|
||||
|
||||
while (size >= sizeof(LinuxPeCompat1) && addr % __alignof__(LinuxPeCompat1) == 0) {
|
||||
LinuxPeCompat1 *compat = (LinuxPeCompat1 *) ((UINT8 *) dos + addr);
|
||||
|
||||
if (compat->type == 0 || compat->size == 0 || compat->size > size)
|
||||
break;
|
||||
|
||||
if (compat->type == 1 &&
|
||||
compat->size >= sizeof(LinuxPeCompat1) &&
|
||||
compat->machine_type == TARGET_MACHINE_TYPE)
|
||||
return compat->entry_point;
|
||||
|
||||
addr += compat->size;
|
||||
size -= compat->size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
EFI_STATUS pe_alignment_info(
|
||||
const void *base,
|
||||
UINT32 *ret_entry_point_address,
|
||||
@ -171,20 +220,30 @@ EFI_STATUS pe_alignment_info(
|
||||
|
||||
assert(base);
|
||||
assert(ret_entry_point_address);
|
||||
assert(ret_size_of_image);
|
||||
assert(ret_section_alignment);
|
||||
|
||||
dos = (const struct DosFileHeader *) base;
|
||||
if (!verify_dos(dos))
|
||||
return EFI_LOAD_ERROR;
|
||||
|
||||
pe = (const struct PeFileHeader*) ((const UINT8 *)base + dos->ExeHeader);
|
||||
if (!verify_pe(pe))
|
||||
if (!verify_pe(pe, /* allow_compatibility= */ TRUE))
|
||||
return EFI_LOAD_ERROR;
|
||||
|
||||
*ret_entry_point_address = pe->OptionalHeader.AddressOfEntryPoint;
|
||||
*ret_size_of_image = pe->OptionalHeader.SizeOfImage;
|
||||
*ret_section_alignment = pe->OptionalHeader.SectionAlignment;
|
||||
UINT32 entry_address = pe->OptionalHeader.AddressOfEntryPoint;
|
||||
|
||||
/* Look for a compat entry point. */
|
||||
if (pe->FileHeader.Machine != TARGET_MACHINE_TYPE) {
|
||||
entry_address = get_compatibility_entry_address(dos, pe);
|
||||
if (entry_address == 0)
|
||||
/* Image type not supported and no compat entry found. */
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
*ret_entry_point_address = entry_address;
|
||||
if (ret_size_of_image)
|
||||
*ret_size_of_image = pe->OptionalHeader.SizeOfImage;
|
||||
if (ret_section_alignment)
|
||||
*ret_section_alignment = pe->OptionalHeader.SectionAlignment;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
@ -207,7 +266,7 @@ EFI_STATUS pe_memory_locate_sections(
|
||||
return EFI_LOAD_ERROR;
|
||||
|
||||
pe = (const struct PeFileHeader*)&base[dos->ExeHeader];
|
||||
if (!verify_pe(pe))
|
||||
if (!verify_pe(pe, /* allow_compatibility= */ FALSE))
|
||||
return EFI_LOAD_ERROR;
|
||||
|
||||
offset = section_table_offset(dos, pe);
|
||||
@ -255,7 +314,7 @@ EFI_STATUS pe_file_locate_sections(
|
||||
err = handle->Read(handle, &len, &pe);
|
||||
if (EFI_ERROR(err))
|
||||
return err;
|
||||
if (len != sizeof(pe) || !verify_pe(&pe))
|
||||
if (len != sizeof(pe) || !verify_pe(&pe, /* allow_compatibility= */ FALSE))
|
||||
return EFI_LOAD_ERROR;
|
||||
|
||||
section_table_len = pe.FileHeader.NumberOfSections * sizeof(struct PeSectionHeader);
|
||||
|
@ -86,6 +86,11 @@ static inline void file_closep(EFI_FILE **handle) {
|
||||
(*handle)->Close(*handle);
|
||||
}
|
||||
|
||||
static inline void unload_imagep(EFI_HANDLE *image) {
|
||||
if (*image)
|
||||
(void) BS->UnloadImage(*image);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocated random UUID, intended to be shared across tools that implement
|
||||
* the (ESP)\loader\entries\<vendor>-<revision>.conf convention and the
|
||||
|
@ -50,6 +50,13 @@ static inline sd_bool strcaseeq_ptr(const sd_char *a, const sd_char *b) {
|
||||
return strcasecmp_ptr(a, b) == 0;
|
||||
}
|
||||
|
||||
static inline size_t strlen_ptr(const sd_char *s) {
|
||||
if (!s)
|
||||
return 0;
|
||||
|
||||
return strlen(s);
|
||||
}
|
||||
|
||||
sd_char *startswith(const sd_char *s, const sd_char *prefix) _pure_;
|
||||
#ifndef SD_BOOT
|
||||
sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) _pure_;
|
||||
@ -61,6 +68,10 @@ static inline sd_bool isempty(const sd_char *a) {
|
||||
return !a || a[0] == '\0';
|
||||
}
|
||||
|
||||
static inline const sd_char *strempty(const sd_char *s) {
|
||||
return s ?: STR_C("");
|
||||
}
|
||||
|
||||
static inline const sd_char *yes_no(sd_bool b) {
|
||||
return b ? STR_C("yes") : STR_C("no");
|
||||
}
|
||||
@ -82,3 +93,9 @@ static inline void *memory_startswith(const void *p, size_t sz, const sd_char *t
|
||||
|
||||
return (uint8_t*) p + n;
|
||||
}
|
||||
|
||||
#define _STRV_FOREACH(s, l, i) \
|
||||
for (typeof(*(l)) *s, *i = (l); (s = i) && *i; i++)
|
||||
|
||||
#define STRV_FOREACH(s, l) \
|
||||
_STRV_FOREACH(s, l, UNIQ_T(i, UNIQ))
|
||||
|
Loading…
x
Reference in New Issue
Block a user