Merge pull request #24434 from medhefgo/boot-fixes

boot: Small fixes
This commit is contained in:
Lennart Poettering
2022-09-08 11:17:12 +02:00
committed by GitHub
10 changed files with 142 additions and 111 deletions
+5 -1
View File
@@ -31,7 +31,11 @@ variables. All EFI variables use the vendor UUID
* The EFI variable `LoaderConfigTimeout` contains the boot menu timeout
currently in use. It may be modified both by the boot loader and by the
host. The value should be formatted as numeric, NUL-terminated, decimal
string, in UTF-16. The time is specified in µs.
string, in UTF-16. The time is specified in seconds. A value of `menu-force`
will disable the timeout and show the menu indefinitely. If set to `0` or
`menu-hidden` the default entry is booted immediately without showing a menu.
The boot loader should provide a way to interrupt this by for example
listening for key presses for a brief moment before booting.
* Similarly, the EFI variable `LoaderConfigTimeoutOneShot` contains a boot menu
timeout for a single following boot. It is set by the OS in order to request
+3 -1
View File
@@ -257,12 +257,13 @@
<varlistentry>
<term><keycap>Esc</keycap></term>
<term><keycombo><keycap>Ctrl</keycap><keycap>c</keycap></keycombo></term>
<listitem><para>Abort the edit and quit the editor</para></listitem>
</varlistentry>
<varlistentry>
<term><keycombo><keycap>Ctrl</keycap><keycap>k</keycap></keycombo></term>
<listitem><para>Clear the command line</para></listitem>
<listitem><para>Clear the command line forwards</para></listitem>
</varlistentry>
<varlistentry>
@@ -272,6 +273,7 @@
</varlistentry>
<varlistentry>
<term><keycombo><keycap>Ctrl</keycap><keycap>Del</keycap></keycombo></term>
<term><keycombo><keycap>Alt</keycap><keycap>d</keycap></keycombo></term>
<listitem><para>Delete word forwards</para></listitem>
</varlistentry>
+53 -21
View File
@@ -254,6 +254,7 @@ static bool line_edit(
cursor_left(&cursor, &first);
continue;
case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_DELETE, 0):
case KEYPRESS(EFI_ALT_PRESSED, 0, 'd'):
/* kill-word */
clear = 0;
@@ -317,8 +318,8 @@ static bool line_edit(
case KEYPRESS(0, 0, CHAR_LINEFEED):
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 */
case KEYPRESS(0, SCAN_F3, 0): /* EZpad Mini 4s firmware sends malformed events */
case KEYPRESS(0, SCAN_F3, CHAR_CARRIAGE_RETURN): /* Teclast X98+ II firmware sends malformed events */
if (!streq16(line, *line_in)) {
free(*line_in);
*line_in = TAKE_PTR(line);
@@ -868,8 +869,8 @@ static bool menu_run(
case KEYPRESS(0, 0, CHAR_LINEFEED):
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 */
case KEYPRESS(0, SCAN_F3, 0): /* EZpad Mini 4s firmware sends malformed events */
case KEYPRESS(0, SCAN_F3, CHAR_CARRIAGE_RETURN): /* Teclast X98+ II firmware sends malformed events */
case KEYPRESS(0, SCAN_RIGHT, 0):
exit = true;
break;
@@ -1036,11 +1037,20 @@ static bool menu_run(
}
if (timeout_efivar_saved != config->timeout_sec_efivar) {
if (config->timeout_sec_efivar == TIMEOUT_UNSET)
switch (config->timeout_sec_efivar) {
case TIMEOUT_UNSET:
efivar_set(LOADER_GUID, L"LoaderConfigTimeout", NULL, EFI_VARIABLE_NON_VOLATILE);
else
break;
case TIMEOUT_MENU_FORCE:
efivar_set(LOADER_GUID, u"LoaderConfigTimeout", u"menu-force", EFI_VARIABLE_NON_VOLATILE);
break;
case TIMEOUT_MENU_HIDDEN:
efivar_set(LOADER_GUID, u"LoaderConfigTimeout", u"menu-hidden", EFI_VARIABLE_NON_VOLATILE);
break;
default:
efivar_set_uint_string(LOADER_GUID, L"LoaderConfigTimeout",
config->timeout_sec_efivar, EFI_VARIABLE_NON_VOLATILE);
}
}
clear_screen(COLOR_NORMAL);
@@ -1531,6 +1541,34 @@ static void config_entry_add_type1(
TAKE_PTR(entry);
}
static EFI_STATUS efivar_get_timeout(const char16_t *var, uint32_t *ret_value) {
_cleanup_free_ char16_t *value = NULL;
EFI_STATUS err;
assert(var);
assert(ret_value);
err = efivar_get(LOADER_GUID, var, &value);
if (err != EFI_SUCCESS)
return err;
if (streq16(value, u"menu-force")) {
*ret_value = TIMEOUT_MENU_FORCE;
return EFI_SUCCESS;
}
if (streq16(value, u"menu-hidden")) {
*ret_value = TIMEOUT_MENU_HIDDEN;
return EFI_SUCCESS;
}
uint64_t timeout;
if (!parse_number16(value, &timeout, NULL))
return EFI_INVALID_PARAMETER;
*ret_value = MIN(timeout, TIMEOUT_TYPE_MAX);
return EFI_SUCCESS;
}
static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
_cleanup_free_ char *content = NULL;
UINTN value;
@@ -1556,20 +1594,20 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
if (err == EFI_SUCCESS)
config_defaults_load_from_file(config, content);
err = efivar_get_uint_string(LOADER_GUID, L"LoaderConfigTimeout", &value);
if (err == EFI_SUCCESS) {
config->timeout_sec_efivar = MIN(value, TIMEOUT_TYPE_MAX);
err = efivar_get_timeout(u"LoaderConfigTimeout", &config->timeout_sec_efivar);
if (err == EFI_SUCCESS)
config->timeout_sec = config->timeout_sec_efivar;
}
else if (err != EFI_NOT_FOUND)
log_error_stall(u"Error reading LoaderConfigTimeout EFI variable: %r", err);
err = efivar_get_uint_string(LOADER_GUID, L"LoaderConfigTimeoutOneShot", &value);
err = efivar_get_timeout(u"LoaderConfigTimeoutOneShot", &config->timeout_sec);
if (err == EFI_SUCCESS) {
/* Unset variable now, after all it's "one shot". */
(void) efivar_set(LOADER_GUID, L"LoaderConfigTimeoutOneShot", NULL, EFI_VARIABLE_NON_VOLATILE);
config->timeout_sec = MIN(value, TIMEOUT_TYPE_MAX);
config->force_menu = true; /* force the menu when this is set */
}
} else if (err != EFI_NOT_FOUND)
log_error_stall(u"Error reading LoaderConfigTimeoutOneShot EFI variable: %r", err);
err = efivar_get_uint_string(LOADER_GUID, L"LoaderConfigConsoleMode", &value);
if (err == EFI_SUCCESS)
@@ -2416,14 +2454,8 @@ static void config_write_entries_to_variable(Config *config) {
p = buffer = xmalloc(sz);
for (UINTN i = 0; i < config->entry_count; i++) {
UINTN l;
l = strsize16(config->entries[i]->id);
memcpy(p, config->entries[i]->id, l);
p += l;
}
for (UINTN i = 0; i < config->entry_count; i++)
p = mempcpy(p, config->entries[i]->id, strsize16(config->entries[i]->id));
assert(p == buffer + sz);
+7 -2
View File
@@ -132,14 +132,19 @@ EFI_STATUS console_key_read(uint64_t *key, uint64_t timeout_usec) {
if (FLAGS_SET(keydata.KeyState.KeyShiftState, EFI_SHIFT_STATE_VALID)) {
/* Do not distinguish between left and right keys (set both flags). */
if (keydata.KeyState.KeyShiftState & EFI_SHIFT_PRESSED)
shift |= EFI_SHIFT_PRESSED;
if (keydata.KeyState.KeyShiftState & EFI_CONTROL_PRESSED)
shift |= EFI_CONTROL_PRESSED;
if (keydata.KeyState.KeyShiftState & EFI_ALT_PRESSED)
shift |= EFI_ALT_PRESSED;
if (keydata.KeyState.KeyShiftState & EFI_LOGO_PRESSED)
shift |= EFI_LOGO_PRESSED;
/* Shift is not supposed to be reported for keys that can be represented as uppercase
* unicode chars (Shift+f is reported as F instead). Some firmware does it anyway, so
* filter those out. */
if ((keydata.KeyState.KeyShiftState & EFI_SHIFT_PRESSED) &&
keydata.Key.UnicodeChar == 0)
shift |= EFI_SHIFT_PRESSED;
}
/* 32 bit modifier keys + 16 bit scan code + 16 bit unicode */
+5 -10
View File
@@ -116,8 +116,7 @@ static EFI_STATUS pack_cpio_one(
*cpio_buffer = a;
a = (char *) *cpio_buffer + *cpio_buffer_size;
memcpy(a, "070701", 6); /* magic ID */
a += 6;
a = mempcpy(a, "070701", 6); /* magic ID */
a = write_cpio_word(a, (*inode_counter)++); /* inode */
a = write_cpio_word(a, access_mode | 0100000 /* = S_IFREG */); /* mode */
@@ -139,16 +138,14 @@ static EFI_STATUS pack_cpio_one(
a = write_cpio_word(a, target_dir_prefix_size + fname_size + 2); /* fname size */
a = write_cpio_word(a, 0); /* "crc" */
memcpy(a, target_dir_prefix, target_dir_prefix_size);
a += target_dir_prefix_size;
a = mempcpy(a, target_dir_prefix, target_dir_prefix_size);
*(a++) = '/';
a = mangle_filename(a, fname);
/* Pad to next multiple of 4 */
a = pad4(a, *cpio_buffer);
memcpy(a, contents, contents_size);
a += contents_size;
a = mempcpy(a, contents, contents_size);
/* Pad to next multiple of 4 */
a = pad4(a, *cpio_buffer);
@@ -198,8 +195,7 @@ static EFI_STATUS pack_cpio_dir(
*cpio_buffer = a = xrealloc(*cpio_buffer, *cpio_buffer_size, *cpio_buffer_size + l);
a = (char *) *cpio_buffer + *cpio_buffer_size;
memcpy(a, "070701", 6); /* magic ID */
a += 6;
a = mempcpy(a, "070701", 6); /* magic ID */
a = write_cpio_word(a, (*inode_counter)++); /* inode */
a = write_cpio_word(a, access_mode | 0040000 /* = S_IFDIR */); /* mode */
@@ -215,8 +211,7 @@ static EFI_STATUS pack_cpio_dir(
a = write_cpio_word(a, path_size + 1); /* fname size */
a = write_cpio_word(a, 0); /* "crc" */
memcpy(a, path, path_size + 1);
a += path_size + 1;
a = mempcpy(a, path, path_size + 1);
/* Pad to next multiple of 4 */
a = pad4(a, *cpio_buffer);
+6 -7
View File
@@ -25,15 +25,14 @@ EFI_STATUS disk_get_part_uuid(EFI_HANDLE *handle, char16_t uuid[static 37]) {
if (DevicePathSubType(dp) != MEDIA_HARDDRIVE_DP)
continue;
HARDDRIVE_DEVICE_PATH *hd = (HARDDRIVE_DEVICE_PATH *) dp;
if (hd->SignatureType != SIGNATURE_TYPE_GUID)
/* The HD device path may be misaligned. */
HARDDRIVE_DEVICE_PATH hd;
memcpy(&hd, dp, MIN(sizeof(hd), (size_t) DevicePathNodeLength(dp)));
if (hd.SignatureType != SIGNATURE_TYPE_GUID)
continue;
/* Use memcpy in case the device path node is misaligned. */
EFI_GUID sig;
memcpy(&sig, hd->Signature, sizeof(hd->Signature));
GuidToString(uuid, &sig);
GuidToString(uuid, (EFI_GUID *) &hd.Signature);
return EFI_SUCCESS;
}
+7
View File
@@ -112,6 +112,13 @@ bool parse_number16(const char16_t *s, uint64_t *ret_u, const char16_t **ret_tai
# define memcmp __builtin_memcmp
# define memcpy __builtin_memcpy
# define memset __builtin_memset
static inline void *mempcpy(void * restrict dest, const void * restrict src, size_t n) {
if (!dest || !src || n == 0)
return dest;
memcpy(dest, src, n);
return (uint8_t *) dest + n;
}
#endif
/* The actual implementations of builtins with efi_ prefix so we can unit test them. */
+7 -16
View File
@@ -69,8 +69,7 @@ static EFI_STATUS combine_initrd(
/* Order matters, the real initrd must come first, since it might include microcode updates
* which the kernel only looks for in the first cpio archive */
memcpy(p, PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size);
p += initrd_size;
p = mempcpy(p, PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size);
pad = ALIGN4(initrd_size) - initrd_size;
if (pad > 0) {
@@ -79,20 +78,12 @@ static EFI_STATUS combine_initrd(
}
}
if (credential_initrd) {
memcpy(p, credential_initrd, credential_initrd_size);
p += credential_initrd_size;
}
if (global_credential_initrd) {
memcpy(p, global_credential_initrd, global_credential_initrd_size);
p += global_credential_initrd_size;
}
if (sysext_initrd) {
memcpy(p, sysext_initrd, sysext_initrd_size);
p += sysext_initrd_size;
}
if (credential_initrd)
p = mempcpy(p, credential_initrd, credential_initrd_size);
if (global_credential_initrd)
p = mempcpy(p, global_credential_initrd, global_credential_initrd_size);
if (sysext_initrd)
p = mempcpy(p, sysext_initrd, sysext_initrd_size);
assert((uint8_t*) PHYSICAL_ADDRESS_TO_POINTER(base) + n == p);
+9 -12
View File
@@ -751,21 +751,18 @@ EFI_STATUS make_file_device_path(EFI_HANDLE device, const char16_t *file, EFI_DE
end_node = NextDevicePathNode(end_node);
size_t file_size = strsize16(file);
size_t dp_size = ((uint8_t *) end_node - (uint8_t *) dp) + END_DEVICE_PATH_LENGTH;
size_t dp_size = (uint8_t *) end_node - (uint8_t *) dp;
/* Make a copy that can also hold a file media device path. */
*ret_dp = xmalloc(dp_size + file_size + SIZE_OF_FILEPATH_DEVICE_PATH);
memcpy(*ret_dp, dp, dp_size);
*ret_dp = xmalloc(dp_size + file_size + SIZE_OF_FILEPATH_DEVICE_PATH + END_DEVICE_PATH_LENGTH);
dp = mempcpy(*ret_dp, dp, dp_size);
/* Point dp to the end node of the copied device path. */
dp = (EFI_DEVICE_PATH *) ((uint8_t *) *ret_dp + dp_size - END_DEVICE_PATH_LENGTH);
/* Replace end node with file media device path. */
FILEPATH_DEVICE_PATH *file_dp = (FILEPATH_DEVICE_PATH *) dp;
file_dp->Header.Type = MEDIA_DEVICE_PATH;
file_dp->Header.SubType = MEDIA_FILEPATH_DP;
memcpy(&file_dp->PathName, file, file_size);
SetDevicePathNodeLength(&file_dp->Header, SIZE_OF_FILEPATH_DEVICE_PATH + file_size);
/* Replace end node with file media device path. Use memcpy() in case dp is unaligned (if accessed as
* FILEPATH_DEVICE_PATH). */
dp->Type = MEDIA_DEVICE_PATH;
dp->SubType = MEDIA_FILEPATH_DP;
memcpy((uint8_t *) dp + offsetof(FILEPATH_DEVICE_PATH, PathName), file, file_size);
SetDevicePathNodeLength(dp, offsetof(FILEPATH_DEVICE_PATH, PathName) + file_size);
dp = NextDevicePathNode(dp);
SetDevicePathEndNode(dp);
+40 -41
View File
@@ -12,33 +12,29 @@ union GptHeaderBuffer {
uint8_t space[CONST_ALIGN_TO(sizeof(EFI_PARTITION_TABLE_HEADER), 512)];
};
static EFI_DEVICE_PATH *path_chop(EFI_DEVICE_PATH *path, EFI_DEVICE_PATH *node) {
static EFI_DEVICE_PATH *path_replace_hd(
const EFI_DEVICE_PATH *path,
const EFI_DEVICE_PATH *node,
const HARDDRIVE_DEVICE_PATH *new_node) {
/* Create a new device path as a copy of path, while chopping off the remainder starting at the given
* node. If new_node is provided, it is appended at the end of the new path. */
assert(path);
assert(node);
UINTN len = (uint8_t *) node - (uint8_t *) path;
EFI_DEVICE_PATH *chopped = xmalloc(len + END_DEVICE_PATH_LENGTH);
size_t len = (uint8_t *) node - (uint8_t *) path, new_node_len = 0;
if (new_node)
new_node_len = DevicePathNodeLength(&new_node->Header);
memcpy(chopped, path, len);
SetDevicePathEndNode((EFI_DEVICE_PATH *) ((uint8_t *) chopped + len));
EFI_DEVICE_PATH *ret = xmalloc(len + new_node_len + END_DEVICE_PATH_LENGTH);
EFI_DEVICE_PATH *end = mempcpy(ret, path, len);
return chopped;
}
if (new_node)
end = mempcpy(end, new_node, new_node_len);
static EFI_DEVICE_PATH *path_dup(const EFI_DEVICE_PATH *dp) {
assert(dp);
const EFI_DEVICE_PATH *node = dp;
size_t size = 0;
while (!IsDevicePathEnd(node)) {
size += DevicePathNodeLength(node);
node = NextDevicePathNode(node);
}
size += DevicePathNodeLength(node);
EFI_DEVICE_PATH *dup = xmalloc(size);
memcpy(dup, dp, size);
return dup;
SetDevicePathEndNode(end);
return ret;
}
static bool verify_gpt(union GptHeaderBuffer *gpt_header_buffer, EFI_LBA lba_expected) {
@@ -71,7 +67,7 @@ static bool verify_gpt(union GptHeaderBuffer *gpt_header_buffer, EFI_LBA lba_exp
if (h->MyLBA != lba_expected)
return false;
if (h->SizeOfPartitionEntry < sizeof(EFI_PARTITION_ENTRY))
if ((h->SizeOfPartitionEntry % sizeof(EFI_PARTITION_ENTRY)) != 0)
return false;
if (h->NumberOfPartitionEntries <= 0 || h->NumberOfPartitionEntries > 1024)
@@ -134,28 +130,33 @@ static EFI_STATUS try_gpt(
/* Now we can finally look for xbootloader partitions. */
for (UINTN i = 0; i < gpt.gpt_header.NumberOfPartitionEntries; i++) {
EFI_PARTITION_ENTRY *entry;
EFI_LBA start, end;
entry = (EFI_PARTITION_ENTRY*) ((uint8_t*) entries + gpt.gpt_header.SizeOfPartitionEntry * i);
EFI_PARTITION_ENTRY *entry =
(EFI_PARTITION_ENTRY *) ((uint8_t *) entries + gpt.gpt_header.SizeOfPartitionEntry * i);
if (memcmp(&entry->PartitionTypeGUID, XBOOTLDR_GUID, sizeof(entry->PartitionTypeGUID)) != 0)
continue;
/* Let's use memcpy(), in case the structs are not aligned (they really should be though) */
memcpy(&start, &entry->StartingLBA, sizeof(start));
memcpy(&end, &entry->EndingLBA, sizeof(end));
if (end < start) /* Bogus? */
if (entry->EndingLBA < entry->StartingLBA) /* Bogus? */
continue;
ret_hd->PartitionNumber = i + 1;
ret_hd->PartitionStart = start;
ret_hd->PartitionSize = end - start + 1;
ret_hd->MBRType = MBR_TYPE_EFI_PARTITION_TABLE_HEADER;
ret_hd->SignatureType = SIGNATURE_TYPE_GUID;
*ret_hd = (HARDDRIVE_DEVICE_PATH) {
.Header = {
.Type = MEDIA_DEVICE_PATH,
.SubType = MEDIA_HARDDRIVE_DP,
},
.PartitionNumber = i + 1,
.PartitionStart = entry->StartingLBA,
.PartitionSize = entry->EndingLBA - entry->StartingLBA + 1,
.MBRType = MBR_TYPE_EFI_PARTITION_TABLE_HEADER,
.SignatureType = SIGNATURE_TYPE_GUID,
};
memcpy(ret_hd->Signature, &entry->UniquePartitionGUID, sizeof(ret_hd->Signature));
/* HARDDRIVE_DEVICE_PATH has padding, which at least OVMF does not like. */
SetDevicePathNodeLength(
&ret_hd->Header,
offsetof(HARDDRIVE_DEVICE_PATH, SignatureType) + sizeof(ret_hd->SignatureType));
return EFI_SUCCESS;
}
@@ -192,7 +193,7 @@ static EFI_STATUS find_device(EFI_HANDLE *device, EFI_DEVICE_PATH **ret_device_p
/* Chop off the partition part, leaving us with the full path to the disk itself. */
_cleanup_free_ EFI_DEVICE_PATH *disk_path = NULL;
EFI_DEVICE_PATH *p = disk_path = path_chop(partition_path, part_node);
EFI_DEVICE_PATH *p = disk_path = path_replace_hd(partition_path, part_node, NULL);
EFI_HANDLE disk_handle;
EFI_BLOCK_IO_PROTOCOL *block_io;
@@ -214,7 +215,6 @@ static EFI_STATUS find_device(EFI_HANDLE *device, EFI_DEVICE_PATH **ret_device_p
/* Try several copies of the GPT header, in case one is corrupted */
EFI_LBA backup_lba = 0;
HARDDRIVE_DEVICE_PATH hd = *((HARDDRIVE_DEVICE_PATH *) part_node);
for (UINTN nr = 0; nr < 3; nr++) {
EFI_LBA lba;
@@ -230,6 +230,7 @@ static EFI_STATUS find_device(EFI_HANDLE *device, EFI_DEVICE_PATH **ret_device_p
else
continue;
HARDDRIVE_DEVICE_PATH hd;
err = try_gpt(
block_io, lba,
nr == 0 ? &backup_lba : NULL, /* Only get backup LBA location from first GPT header. */
@@ -243,9 +244,7 @@ static EFI_STATUS find_device(EFI_HANDLE *device, EFI_DEVICE_PATH **ret_device_p
}
/* Patch in the data we found */
EFI_DEVICE_PATH *xboot_path = path_dup(partition_path);
memcpy((uint8_t *) xboot_path + ((uint8_t *) part_node - (uint8_t *) partition_path), &hd, sizeof(hd));
*ret_device_path = xboot_path;
*ret_device_path = path_replace_hd(partition_path, part_node, &hd);
return EFI_SUCCESS;
}