boot,vconsole: Propagate UEFI HII keyboard layout to the OS

UEFI firmware can report the currently-active keyboard layout via
EFI_HII_DATABASE_PROTOCOL.GetKeyboardLayout(). The layout descriptor
includes an RFC 4646 / BCP 47 language tag (e.g. "en-US"). Query this
from sd-boot/sd-stub and write it to a new LoaderKeyboardLayout EFI
variable, advertised through a new EFI_LOADER_FEATURE_KEYBOARD_LAYOUT
feature bit.

On the OS side, systemd-vconsole-setup reads the variable as a
lowest-priority fallback for the console keymap. To map the BCP 47
tag to a vconsole keymap we extend /usr/share/systemd/kbd-model-map
with an optional sixth column listing the comma-separated BCP 47 tags
each row covers; a new find_vconsole_keymap_for_bcp47() helper walks
the file, preferring an exact tag match and otherwise falling back to
the row whose tag matches the input's primary subtag. Credentials,
/etc/vconsole.conf, and vconsole.keymap= on the kernel command line
continue to take precedence.

bootctl status surfaces the new variable, printing the language tag
or "n/a (not reported by firmware)" when sd-boot advertises the
feature but the firmware HII database didn't expose a layout (common
on QEMU without a USB keyboard, since EDK2's PS/2 driver does not
register an HII keyboard layout).
This commit is contained in:
Daan De Meyer
2026-05-11 21:10:11 +02:00
committed by Daan De Meyer
parent 6f49c7e598
commit 7b9d76cba7
15 changed files with 494 additions and 111 deletions
+43 -37
View File
@@ -320,7 +320,7 @@ static int efi_get_variable_path_and_warn(const char *variable, char **ret) {
static void print_yes_no_line(bool first, bool good, const char *name) {
printf("%s%s %s\n",
first ? " Features: " : " ",
first ? " Features: " : " ",
COLOR_MARK_BOOL(good),
name);
}
@@ -387,25 +387,26 @@ int verb_status(int argc, char *argv[], uintptr_t _data, void *userdata) {
uint64_t flag;
const char *name;
} loader_flags[] = {
{ EFI_LOADER_FEATURE_BOOT_COUNTING, "Boot counting" },
{ EFI_LOADER_FEATURE_CONFIG_TIMEOUT, "Menu timeout control" },
{ EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT, "One-shot menu timeout control" },
{ EFI_LOADER_FEATURE_ENTRY_DEFAULT, "Default entry control" },
{ EFI_LOADER_FEATURE_ENTRY_ONESHOT, "One-shot entry control" },
{ EFI_LOADER_FEATURE_XBOOTLDR, "Support for XBOOTLDR partition" },
{ EFI_LOADER_FEATURE_RANDOM_SEED, "Support for passing random seed to OS" },
{ EFI_LOADER_FEATURE_LOAD_DRIVER, "Load drop-in drivers" },
{ EFI_LOADER_FEATURE_SORT_KEY, "Support Type #1 sort-key field" },
{ EFI_LOADER_FEATURE_SAVED_ENTRY, "Support @saved pseudo-entry" },
{ EFI_LOADER_FEATURE_DEVICETREE, "Support Type #1 devicetree field" },
{ EFI_LOADER_FEATURE_SECUREBOOT_ENROLL, "Enroll SecureBoot keys" },
{ EFI_LOADER_FEATURE_RETAIN_SHIM, "Retain SHIM protocols" },
{ EFI_LOADER_FEATURE_MENU_DISABLE, "Menu can be disabled" },
{ EFI_LOADER_FEATURE_MULTI_PROFILE_UKI, "Multi-Profile UKIs are supported" },
{ EFI_LOADER_FEATURE_REPORT_URL, "Loader reports network boot URL" },
{ EFI_LOADER_FEATURE_TYPE1_UKI, "Support Type #1 uki field" },
{ EFI_LOADER_FEATURE_TYPE1_UKI_URL, "Support Type #1 uki-url field" },
{ EFI_LOADER_FEATURE_TPM2_ACTIVE_PCR_BANKS, "Loader reports active TPM2 PCR banks" },
{ EFI_LOADER_FEATURE_BOOT_COUNTING, "Boot counting" },
{ EFI_LOADER_FEATURE_CONFIG_TIMEOUT, "Menu timeout control" },
{ EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT, "One-shot menu timeout control" },
{ EFI_LOADER_FEATURE_ENTRY_DEFAULT, "Default entry control" },
{ EFI_LOADER_FEATURE_ENTRY_ONESHOT, "One-shot entry control" },
{ EFI_LOADER_FEATURE_XBOOTLDR, "Support for XBOOTLDR partition" },
{ EFI_LOADER_FEATURE_RANDOM_SEED, "Support for passing random seed to OS" },
{ EFI_LOADER_FEATURE_LOAD_DRIVER, "Load drop-in drivers" },
{ EFI_LOADER_FEATURE_SORT_KEY, "Support Type #1 sort-key field" },
{ EFI_LOADER_FEATURE_SAVED_ENTRY, "Support @saved pseudo-entry" },
{ EFI_LOADER_FEATURE_DEVICETREE, "Support Type #1 devicetree field" },
{ EFI_LOADER_FEATURE_SECUREBOOT_ENROLL, "Enroll SecureBoot keys" },
{ EFI_LOADER_FEATURE_RETAIN_SHIM, "Retain SHIM protocols" },
{ EFI_LOADER_FEATURE_MENU_DISABLE, "Menu can be disabled" },
{ EFI_LOADER_FEATURE_MULTI_PROFILE_UKI, "Multi-Profile UKIs are supported" },
{ EFI_LOADER_FEATURE_REPORT_URL, "Loader reports network boot URL" },
{ EFI_LOADER_FEATURE_TYPE1_UKI, "Support Type #1 uki field" },
{ EFI_LOADER_FEATURE_TYPE1_UKI_URL, "Support Type #1 uki-url field" },
{ EFI_LOADER_FEATURE_TPM2_ACTIVE_PCR_BANKS, "Loader reports active TPM2 PCR banks" },
{ EFI_LOADER_FEATURE_KEYBOARD_LAYOUT, "Loader reports firmware keyboard layout" },
};
static const struct {
uint64_t flag;
@@ -426,7 +427,7 @@ int verb_status(int argc, char *argv[], uintptr_t _data, void *userdata) {
};
_cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL, *stub_path = NULL,
*current_entry = NULL, *oneshot_entry = NULL, *preferred_entry = NULL, *default_entry = NULL, *sysfail_entry = NULL,
*sysfail_reason = NULL;
*sysfail_reason = NULL, *keyboard_layout = NULL;
uint64_t loader_features = 0, stub_features = 0;
int have;
@@ -444,6 +445,7 @@ int verb_status(int argc, char *argv[], uintptr_t _data, void *userdata) {
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderEntryDefault"), &default_entry);
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderEntrySysFail"), &sysfail_entry);
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderSysFailReason"), &sysfail_reason);
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderKeyboardLayout"), &keyboard_layout);
SecureBootMode secure = efi_get_secure_boot_mode();
printf("%sSystem:%s\n", ansi_underline(), ansi_normal());
@@ -503,7 +505,7 @@ int verb_status(int argc, char *argv[], uintptr_t _data, void *userdata) {
if (loader) {
printf("%sCurrent Boot Loader:%s\n", ansi_underline(), ansi_normal());
printf(" Product: %s%s%s\n", ansi_highlight(), loader, ansi_normal());
printf(" Product: %s%s%s\n", ansi_highlight(), loader, ansi_normal());
for (size_t i = 0; i < ELEMENTSOF(loader_flags); i++)
print_yes_no_line(i == 0, FLAGS_SET(loader_features, loader_flags[i].flag), loader_flags[i].name);
@@ -521,38 +523,42 @@ int verb_status(int argc, char *argv[], uintptr_t _data, void *userdata) {
SD_ID128_FORMAT_VAL(loader_partition_uuid),
SD_ID128_FORMAT_VAL(esp_uuid));
printf(" Partition: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n",
printf(" Partition: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n",
SD_ID128_FORMAT_VAL(loader_partition_uuid));
} else if (loader_path)
printf(" Partition: n/a\n");
printf(" Partition: n/a\n");
if (loader_path)
printf(" Loader: %s%s%s/%s%s\n",
printf(" Loader: %s%s%s/%s%s\n",
glyph(GLYPH_TREE_RIGHT), ansi_grey(), arg_esp_path, ansi_normal(), loader_path);
if (loader_url)
printf(" Net Boot URL: %s\n", loader_url);
printf(" Net Boot URL: %s\n", loader_url);
if (FLAGS_SET(loader_features, EFI_LOADER_FEATURE_KEYBOARD_LAYOUT))
printf("Keyboard Layout: %s\n",
keyboard_layout ?: "n/a (not reported by firmware)");
if (sysfail_entry)
printf("SysFail Reason: %s\n", sysfail_reason);
printf(" SysFail Reason: %s\n", sysfail_reason);
if (current_entry)
printf(" Current Entry: %s\n", current_entry);
printf(" Current Entry: %s\n", current_entry);
if (preferred_entry)
printf(" Preferred Entry: %s\n", preferred_entry);
printf("Preferred Entry: %s\n", preferred_entry);
if (default_entry)
printf(" Default Entry: %s\n", default_entry);
printf(" Default Entry: %s\n", default_entry);
if (oneshot_entry && !streq_ptr(oneshot_entry, default_entry))
printf(" OneShot Entry: %s\n", oneshot_entry);
printf(" OneShot Entry: %s\n", oneshot_entry);
if (sysfail_entry)
printf(" SysFail Entry: %s\n", sysfail_entry);
printf(" SysFail Entry: %s\n", sysfail_entry);
printf("\n");
}
if (stub) {
printf("%sCurrent Stub:%s\n", ansi_underline(), ansi_normal());
printf(" Product: %s%s%s\n", ansi_highlight(), stub, ansi_normal());
printf(" Product: %s%s%s\n", ansi_highlight(), stub, ansi_normal());
for (size_t i = 0; i < ELEMENTSOF(stub_flags); i++)
print_yes_no_line(i == 0, FLAGS_SET(stub_features, stub_flags[i].flag), stub_flags[i].name);
@@ -573,16 +579,16 @@ int verb_status(int argc, char *argv[], uintptr_t _data, void *userdata) {
SD_ID128_FORMAT_VAL(esp_uuid),
SD_ID128_FORMAT_VAL(xbootldr_uuid));
printf(" Partition: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n",
printf(" Partition: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n",
SD_ID128_FORMAT_VAL(stub_partition_uuid));
} else if (stub_path)
printf(" Partition: n/a\n");
printf(" Partition: n/a\n");
if (stub_path)
printf(" Stub: %s%s\n", glyph(GLYPH_TREE_RIGHT), strna(stub_path));
printf(" Stub: %s%s\n", glyph(GLYPH_TREE_RIGHT), strna(stub_path));
if (stub_url)
printf(" Net Boot URL: %s\n", stub_url);
printf(" Net Boot URL: %s\n", stub_url);
printf("\n");
}