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
+13
View File
@@ -143,6 +143,8 @@ Variables will be listed below using the Linux efivarfs naming,
* `1 << 18` → The boot loader reports active TPM2 PCR banks in the
EFI variable `LoaderTpm2ActivePcrBanks-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f`.
* `1 << 19` → The boot loader supports the `LoaderEntryPreferred` variable when set.
* `1 << 20` → The boot loader reports the firmware-configured keyboard layout in the
EFI variable `LoaderKeyboardLayout-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f`.
* The EFI variable `LoaderSystemToken-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f`
contains binary random data,
@@ -171,6 +173,17 @@ Variables will be listed below using the Linux efivarfs naming,
the TCG EFI ProtocolSpecification for TPM 2.0 as `EFI_TCG2_BOOT_HASH_ALG_*`.
If no TPM2 support or no active banks were detected, will be set to `0`.
* The EFI variable `LoaderKeyboardLayout-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f`
contains the RFC 4646 (BCP 47) language tag of the currently-active keyboard
layout as reported by the UEFI HII database (e.g. `en-US`, `de-DE`).
It is formatted as a NUL-terminated UTF-16 string.
The boot loader sets this variable from the layout returned by
`EFI_HII_DATABASE_PROTOCOL.GetKeyboardLayout()`,
if that protocol is implemented by the firmware.
Userspace (notably `systemd-vconsole-setup`)
uses this as a lowest-priority fallback keyboard layout
when no explicit configuration is provided.
If `LoaderTimeInitUSec` and `LoaderTimeExecUSec` are set, `systemd-analyze`
will include them in its boot-time analysis. If `LoaderDevicePartUUID` is set,
systemd will mount the ESP that was used for the boot to `/boot`, but only if
+14
View File
@@ -95,6 +95,20 @@
</variablelist>
</refsect1>
<refsect1>
<title>Firmware-provided keyboard layout</title>
<para>If the boot loader reports the firmware-configured keyboard layout via the
<varname>LoaderKeyboardLayout</varname> EFI variable (see the
<ulink url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader Interface</ulink>),
<command>systemd-vconsole-setup</command> uses it as the lowest-priority fallback for the
keymap. The RFC 4646 / BCP 47 language tag reported by the firmware (e.g. <literal>de-DE</literal>) is
matched against the optional sixth column of <filename>/usr/share/systemd/kbd-model-map</filename>,
which lists the language tags each virtual-console keymap covers. Credentials,
<filename>/etc/vconsole.conf</filename>, and kernel command line options all override this
firmware-provided default.</para>
</refsect1>
<refsect1>
<title>See Also</title>
<para><simplelist type="inline">
+7
View File
@@ -56,6 +56,13 @@
might be checked for configuration of the virtual console as well,
however only as fallback.</para>
<para>If the boot loader reports the firmware-configured keyboard layout via the
<varname>LoaderKeyboardLayout</varname> EFI variable (see the
<ulink url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader Interface</ulink>),
it is used as the lowest-priority fallback for <varname>KEYMAP=</varname>.
Any setting from credentials, <filename>/etc/vconsole.conf</filename>, or the kernel
command line overrides it.</para>
<para><filename>/etc/vconsole.conf</filename> is usually created and updated
using
<citerefentry><refentrytitle>systemd-localed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+1
View File
@@ -3266,6 +3266,7 @@ static void export_loader_variables(
EFI_LOADER_FEATURE_TYPE1_UKI |
EFI_LOADER_FEATURE_TYPE1_UKI_URL |
EFI_LOADER_FEATURE_TPM2_ACTIVE_PCR_BANKS |
EFI_LOADER_FEATURE_KEYBOARD_LAYOUT |
0;
assert(loaded_image);
+10
View File
@@ -3,6 +3,7 @@
#include "device-path-util.h"
#include "efi-efivars.h"
#include "export-vars.h"
#include "hii.h"
#include "measure.h"
#include "part-discovery.h"
#include "url-discovery.h"
@@ -60,4 +61,13 @@ void export_common_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) {
s = xasprintf("0x%08x", active_pcr_banks);
efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderTpm2ActivePcrBanks", s, 0);
}
/* Report the firmware's currently-active HII keyboard layout (as an RFC 4646 language tag, e.g.
* "de-DE"), so the OS can pick a matching console keymap. Best-effort: many firmwares do not
* implement the HII database protocol or expose no keyboard layout. */
if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderKeyboardLayout", /* ret_data= */ NULL, /* ret_size= */ NULL) != EFI_SUCCESS) {
_cleanup_free_ char16_t *lang = hii_query_keyboard_layout_language();
if (lang)
efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderKeyboardLayout", lang, /* flags= */ 0);
}
}
+80
View File
@@ -0,0 +1,80 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "efi-log.h"
#include "hii.h"
#include "proto/hii-database.h"
#include "util.h"
char16_t *hii_query_keyboard_layout_language(void) {
EFI_HII_DATABASE_PROTOCOL *hii_db = NULL;
EFI_STATUS err;
err = BS->LocateProtocol(MAKE_GUID_PTR(EFI_HII_DATABASE_PROTOCOL), /* Registration= */ NULL, (void **) &hii_db);
if (err != EFI_SUCCESS) {
log_debug_status(err, "HII database protocol not available, ignoring: %m");
return NULL;
}
/* First call sizes the layout. We pass length=0 / buffer=NULL and expect EFI_BUFFER_TOO_SMALL. */
uint16_t length = 0;
err = hii_db->GetKeyboardLayout(hii_db, /* KeyGuid= */ NULL, &length, /* KeyboardLayout= */ NULL);
if (err != EFI_BUFFER_TOO_SMALL) {
log_debug_status(err, "Initial GetKeyboardLayout did not report required buffer size, ignoring: %m");
return NULL;
}
if (length < sizeof(EFI_HII_KEYBOARD_LAYOUT)) {
log_debug("Reported keyboard layout size %u is smaller than the header, ignoring.", length);
return NULL;
}
_cleanup_free_ EFI_HII_KEYBOARD_LAYOUT *layout = xmalloc(length);
err = hii_db->GetKeyboardLayout(hii_db, /* KeyGuid= */ NULL, &length, layout);
if (err != EFI_SUCCESS) {
log_debug_status(err, "Failed to retrieve current keyboard layout, ignoring: %m");
return NULL;
}
if (length < sizeof(EFI_HII_KEYBOARD_LAYOUT)) {
log_debug("Reported keyboard layout size %u shrank below the header, ignoring.", length);
return NULL;
}
if (layout->LayoutLength != length) {
log_debug("Keyboard layout reports inconsistent LayoutLength %u vs %u, ignoring.",
layout->LayoutLength, length);
return NULL;
}
uint32_t off = layout->LayoutDescriptorStringOffset;
if (off > length || length - off < sizeof(EFI_DESCRIPTION_STRING_BUNDLE)) {
log_debug("Keyboard layout descriptor string offset %u out of bounds (length %u), ignoring.",
off, length);
return NULL;
}
const EFI_DESCRIPTION_STRING_BUNDLE *bundle = (const EFI_DESCRIPTION_STRING_BUNDLE *) ((const uint8_t *) layout + off);
if (bundle->DescriptionCount == 0) {
log_debug("Keyboard layout has no description strings, ignoring.");
return NULL;
}
/* Walk Strings[] looking for the U+0020 that terminates the first language tag. */
size_t max_chars = (length - off - sizeof(EFI_DESCRIPTION_STRING_BUNDLE)) / sizeof(char16_t);
size_t n;
for (n = 0; n < max_chars; n++)
if (bundle->Strings[n] == u' ')
break;
if (n == max_chars) {
log_debug("Keyboard layout language tag is not terminated by a space, ignoring.");
return NULL;
}
if (n == 0) {
log_debug("Keyboard layout language tag is empty, ignoring.");
return NULL;
}
char16_t *s = xnew(char16_t, n + 1);
memcpy(s, bundle->Strings, n * sizeof(char16_t));
s[n] = u'\0';
return s;
}
+9
View File
@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "efi.h"
/* Queries the firmware's HII database for the currently-active keyboard layout and returns the RFC 4646
* language tag (e.g. u"de-DE") embedded in the layout description bundle. Returns NULL if the protocol
* is not provided, the table is malformed, or no language tag is present. */
char16_t *hii_query_keyboard_layout_language(void);
+1
View File
@@ -320,6 +320,7 @@ libefi_sources = files(
'efi-string.c',
'export-vars.c',
'graphics.c',
'hii.c',
'initrd.c',
'measure.c',
'part-discovery.c',
+126
View File
@@ -0,0 +1,126 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "efi.h"
#define EFI_HII_DATABASE_PROTOCOL_GUID \
GUID_DEF(0xef9fc172, 0xa1b2, 0x4693, 0xb3, 0x27, 0x6d, 0x32, 0xfc, 0x41, 0x60, 0x42)
typedef void *EFI_HII_HANDLE;
typedef struct {
EFI_GUID PackageListGuid;
uint32_t PackageLength;
} EFI_HII_PACKAGE_LIST_HEADER;
typedef struct _packed_ {
uint32_t LengthAndType; /* Length:24 | Type:8 (little-endian) */
} EFI_HII_PACKAGE_HEADER;
typedef size_t EFI_HII_DATABASE_NOTIFY_TYPE;
typedef EFI_STATUS (EFIAPI *EFI_HII_DATABASE_NOTIFY)(
uint8_t PackageType,
EFI_GUID *PackageGuid,
EFI_HII_PACKAGE_HEADER *Package,
EFI_HII_HANDLE Handle,
EFI_HII_DATABASE_NOTIFY_TYPE NotifyType);
typedef struct EFI_HII_DATABASE_PROTOCOL EFI_HII_DATABASE_PROTOCOL;
struct EFI_HII_DATABASE_PROTOCOL {
EFI_STATUS (EFIAPI *NewPackageList)(
EFI_HII_DATABASE_PROTOCOL *This,
EFI_HII_PACKAGE_LIST_HEADER *PackageList,
EFI_HANDLE DriverHandle,
EFI_HII_HANDLE *Handle);
EFI_STATUS (EFIAPI *RemovePackageList)(
EFI_HII_DATABASE_PROTOCOL *This,
EFI_HII_HANDLE Handle);
EFI_STATUS (EFIAPI *UpdatePackageList)(
EFI_HII_DATABASE_PROTOCOL *This,
EFI_HII_HANDLE Handle,
EFI_HII_PACKAGE_LIST_HEADER *PackageList);
EFI_STATUS (EFIAPI *ListPackageLists)(
EFI_HII_DATABASE_PROTOCOL *This,
uint8_t PackageType,
EFI_GUID *PackageGuid,
size_t *HandleBufferLength,
EFI_HII_HANDLE *Handle);
EFI_STATUS (EFIAPI *ExportPackageLists)(
EFI_HII_DATABASE_PROTOCOL *This,
EFI_HII_HANDLE Handle,
size_t *BufferSize,
EFI_HII_PACKAGE_LIST_HEADER *Buffer);
EFI_STATUS (EFIAPI *RegisterPackageNotify)(
EFI_HII_DATABASE_PROTOCOL *This,
uint8_t PackageType,
EFI_GUID *PackageGuid,
EFI_HII_DATABASE_NOTIFY PackageNotifyFn,
EFI_HII_DATABASE_NOTIFY_TYPE NotifyType,
EFI_HANDLE *NotifyHandle);
EFI_STATUS (EFIAPI *UnregisterPackageNotify)(
EFI_HII_DATABASE_PROTOCOL *This,
EFI_HANDLE NotificationHandle);
EFI_STATUS (EFIAPI *FindKeyboardLayouts)(
EFI_HII_DATABASE_PROTOCOL *This,
uint16_t *KeyGuidBufferLength,
EFI_GUID *KeyGuidBuffer);
EFI_STATUS (EFIAPI *GetKeyboardLayout)(
EFI_HII_DATABASE_PROTOCOL *This,
EFI_GUID *KeyGuid,
uint16_t *KeyboardLayoutLength,
void *KeyboardLayout);
EFI_STATUS (EFIAPI *SetKeyboardLayout)(
EFI_HII_DATABASE_PROTOCOL *This,
EFI_GUID *KeyGuid);
EFI_STATUS (EFIAPI *GetPackageListHandle)(
EFI_HII_DATABASE_PROTOCOL *This,
EFI_HII_HANDLE PackageListHandle,
EFI_HANDLE *DriverHandle);
};
/* EFI_HII_KEYBOARD_LAYOUT and EFI_KEY_DESCRIPTOR are packed: LayoutDescriptorStringOffset follows
* a 16-byte EFI_GUID at offset 2, so it is at offset 18 — *not* a natural 4-byte alignment. */
typedef struct _packed_ {
uint16_t LayoutLength;
EFI_GUID Guid;
uint32_t LayoutDescriptorStringOffset;
uint8_t DescriptorCount;
/* EFI_KEY_DESCRIPTOR Descriptors[DescriptorCount] follows here, then at
* LayoutDescriptorStringOffset (from the start of this struct) the description-string bundle. */
} EFI_HII_KEYBOARD_LAYOUT;
typedef struct _packed_ {
uint32_t Key;
char16_t Unicode;
char16_t ShiftedUnicode;
char16_t AltGrUnicode;
char16_t ShiftedAltGrUnicode;
uint16_t Modifier;
uint16_t AffectedAttribute;
} EFI_KEY_DESCRIPTOR;
/* The description-string bundle that LayoutDescriptorStringOffset points to. After DescriptionCount,
* each of the DescriptionCount entries is laid out as:
*
* CHAR16 Language[]; // RFC 4646 tag, terminated by the Space below (no NUL)
* CHAR16 Space; // U+0020
* CHAR16 DescriptionString[]; // NUL-terminated UTF-16 description
*
* Despite what the UEFI spec text says, Language is encoded as UTF-16 (CHAR16) in practice — see EDK2
* MdeModulePkg/Bus/Usb/UsbKbDxe/KeyBoard.h USB_KEYBOARD_LAYOUT_PACK_BIN. */
typedef struct _packed_ {
uint16_t DescriptionCount;
char16_t Strings[];
} EFI_DESCRIPTION_STRING_BUNDLE;
+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");
}
+1
View File
@@ -29,6 +29,7 @@
#define EFI_LOADER_FEATURE_TYPE1_UKI_URL (UINT64_C(1) << 17)
#define EFI_LOADER_FEATURE_TPM2_ACTIVE_PCR_BANKS (UINT64_C(1) << 18)
#define EFI_LOADER_FEATURE_ENTRY_PREFERRED (UINT64_C(1) << 19)
#define EFI_LOADER_FEATURE_KEYBOARD_LAYOUT (UINT64_C(1) << 20)
/* Features of the stub, i.e. systemd-stub */
#define EFI_STUB_FEATURE_REPORT_BOOT_PARTITION (UINT64_C(1) << 0)
+75 -72
View File
@@ -1,73 +1,76 @@
# Originally generated from system-config-keyboard's model list.
# consolelayout xlayout xmodel xvariant xoptions
sg ch pc105 de_nodeadkeys terminate:ctrl_alt_bksp
nl nl pc105 - terminate:ctrl_alt_bksp
mk-utf mk,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
trq tr pc105 - terminate:ctrl_alt_bksp
uk gb pc105 - terminate:ctrl_alt_bksp
is-latin1 is pc105 - terminate:ctrl_alt_bksp
de de pc105 - terminate:ctrl_alt_bksp
la-latin1 latam pc105 - terminate:ctrl_alt_bksp
us us pc105+inet - terminate:ctrl_alt_bksp
ko kr pc105 - terminate:ctrl_alt_bksp
ro-std ro pc105 std terminate:ctrl_alt_bksp
de-latin1 de pc105 - terminate:ctrl_alt_bksp
slovene si pc105 - terminate:ctrl_alt_bksp
hu hu pc105 - terminate:ctrl_alt_bksp
jp106 jp jp106 - terminate:ctrl_alt_bksp
croat hr pc105 - terminate:ctrl_alt_bksp
it2 it pc105 - terminate:ctrl_alt_bksp
hu101 hu pc105 qwerty terminate:ctrl_alt_bksp
sr-latin rs pc105 latin terminate:ctrl_alt_bksp
fi fi pc105 - terminate:ctrl_alt_bksp
fr_CH ch pc105 fr terminate:ctrl_alt_bksp
dk-latin1 dk pc105 - terminate:ctrl_alt_bksp
fr fr pc105 - terminate:ctrl_alt_bksp
it it pc105 - terminate:ctrl_alt_bksp
ua-utf ua,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
fr-latin1 fr pc105 - terminate:ctrl_alt_bksp
sg-latin1 ch pc105 de_nodeadkeys terminate:ctrl_alt_bksp
be-latin1 be pc105 - terminate:ctrl_alt_bksp
dk dk pc105 - terminate:ctrl_alt_bksp
fr-pc fr pc105 - terminate:ctrl_alt_bksp
bg_pho-utf8 bg,us pc105 ,phonetic terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
it-ibm it pc105 - terminate:ctrl_alt_bksp
cz-us-qwertz cz,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
cz-qwerty cz,us pc105 qwerty, terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
br-abnt2 br abnt2 - terminate:ctrl_alt_bksp
ro ro pc105 - terminate:ctrl_alt_bksp
us-acentos us pc105 intl terminate:ctrl_alt_bksp
pt-latin1 pt pc105 - terminate:ctrl_alt_bksp
ro-std-cedilla ro pc105 std_cedilla terminate:ctrl_alt_bksp
tj_alt-UTF8 tj pc105 - terminate:ctrl_alt_bksp
de-latin1-nodeadkeys de pc105 nodeadkeys terminate:ctrl_alt_bksp
no no pc105 - terminate:ctrl_alt_bksp
bg_bds-utf8 bg,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
dvorak us pc105 dvorak terminate:ctrl_alt_bksp
dvorak us pc105 dvorak-alt-intl terminate:ctrl_alt_bksp
ru ru,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
cz-lat2 cz pc105 qwerty terminate:ctrl_alt_bksp
pl2 pl pc105 - terminate:ctrl_alt_bksp
es es pc105 - terminate:ctrl_alt_bksp
ro-cedilla ro pc105 cedilla terminate:ctrl_alt_bksp
ie ie pc105 - terminate:ctrl_alt_bksp
et ee pc105 - terminate:ctrl_alt_bksp
sk-qwerty sk pc105 qwerty terminate:ctrl_alt_bksp
sk-qwertz sk pc105 - terminate:ctrl_alt_bksp
fr-latin9 fr pc105 latin9 terminate:ctrl_alt_bksp
fr_CH-latin1 ch pc105 fr terminate:ctrl_alt_bksp
cf ca pc105 - terminate:ctrl_alt_bksp
sv-latin1 se pc105 - terminate:ctrl_alt_bksp
sr-cy rs pc105 - terminate:ctrl_alt_bksp
gr gr,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
by by,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
il il pc105 - terminate:ctrl_alt_bksp
kazakh kz,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
lt.baltic lt pc105 - terminate:ctrl_alt_bksp
lt.l4 lt pc105 - terminate:ctrl_alt_bksp
lt lt pc105 - terminate:ctrl_alt_bksp
khmer kh,us pc105 - terminate:ctrl_alt_bksp
es-dvorak es microsoftpro dvorak terminate:ctrl_alt_bksp
lv lv pc105 apostrophe terminate:ctrl_alt_bksp
lv-tilde lv pc105 tilde terminate:ctrl_alt_bksp
ge ge,us pc105 - terminate:ctrl_alt_bksp
# The sixth column is an optional comma-separated list of RFC 4646 / BCP 47
# language tags the row matches; used to map a firmware-reported keyboard
# layout to a vconsole keymap. Use "-" or omit when no tags apply.
# consolelayout xlayout xmodel xvariant xoptions bcp47
sg ch pc105 de_nodeadkeys terminate:ctrl_alt_bksp de-CH
nl nl pc105 - terminate:ctrl_alt_bksp nl-NL,nl
mk-utf mk,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll mk-MK,mk
trq tr pc105 - terminate:ctrl_alt_bksp tr-TR,tr
uk gb pc105 - terminate:ctrl_alt_bksp en-GB
is-latin1 is pc105 - terminate:ctrl_alt_bksp is-IS,is
de de pc105 - terminate:ctrl_alt_bksp de-DE,de-AT,de
la-latin1 latam pc105 - terminate:ctrl_alt_bksp es-419,es-MX,es-AR,es-CO,es-CL,es-PE,es-VE
us us pc105+inet - terminate:ctrl_alt_bksp en-US,en
ko kr pc105 - terminate:ctrl_alt_bksp ko-KR,ko
ro-std ro pc105 std terminate:ctrl_alt_bksp -
de-latin1 de pc105 - terminate:ctrl_alt_bksp -
slovene si pc105 - terminate:ctrl_alt_bksp sl-SI,sl
hu hu pc105 - terminate:ctrl_alt_bksp hu-HU,hu
jp106 jp jp106 - terminate:ctrl_alt_bksp ja-JP,ja
croat hr pc105 - terminate:ctrl_alt_bksp hr-HR,hr
it2 it pc105 - terminate:ctrl_alt_bksp -
hu101 hu pc105 qwerty terminate:ctrl_alt_bksp -
sr-latin rs pc105 latin terminate:ctrl_alt_bksp sr-Latn-RS,sr-Latn
fi fi pc105 - terminate:ctrl_alt_bksp fi-FI,fi
fr_CH ch pc105 fr terminate:ctrl_alt_bksp fr-CH
dk-latin1 dk pc105 - terminate:ctrl_alt_bksp da-DK,da
fr fr pc105 - terminate:ctrl_alt_bksp fr-FR,fr
it it pc105 - terminate:ctrl_alt_bksp it-IT,it-CH,it
ua-utf ua,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll uk-UA,uk
fr-latin1 fr pc105 - terminate:ctrl_alt_bksp -
sg-latin1 ch pc105 de_nodeadkeys terminate:ctrl_alt_bksp -
be-latin1 be pc105 - terminate:ctrl_alt_bksp fr-BE,nl-BE
dk dk pc105 - terminate:ctrl_alt_bksp -
fr-pc fr pc105 - terminate:ctrl_alt_bksp -
bg_pho-utf8 bg,us pc105 ,phonetic terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll bg-BG,bg
it-ibm it pc105 - terminate:ctrl_alt_bksp -
cz-us-qwertz cz,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll cs-CZ,cs
cz-qwerty cz,us pc105 qwerty, terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll -
br-abnt2 br abnt2 - terminate:ctrl_alt_bksp pt-BR
ro ro pc105 - terminate:ctrl_alt_bksp ro-RO,ro
us-acentos us pc105 intl terminate:ctrl_alt_bksp -
pt-latin1 pt pc105 - terminate:ctrl_alt_bksp pt-PT,pt
ro-std-cedilla ro pc105 std_cedilla terminate:ctrl_alt_bksp -
tj_alt-UTF8 tj pc105 - terminate:ctrl_alt_bksp tg-TJ,tg
de-latin1-nodeadkeys de pc105 nodeadkeys terminate:ctrl_alt_bksp -
no no pc105 - terminate:ctrl_alt_bksp nb-NO,nn-NO,no
bg_bds-utf8 bg,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll -
dvorak us pc105 dvorak terminate:ctrl_alt_bksp -
dvorak us pc105 dvorak-alt-intl terminate:ctrl_alt_bksp -
ru ru,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll ru-RU,ru
cz-lat2 cz pc105 qwerty terminate:ctrl_alt_bksp -
pl2 pl pc105 - terminate:ctrl_alt_bksp pl-PL,pl
es es pc105 - terminate:ctrl_alt_bksp es-ES,es
ro-cedilla ro pc105 cedilla terminate:ctrl_alt_bksp -
ie ie pc105 - terminate:ctrl_alt_bksp en-IE,ga-IE,ga
et ee pc105 - terminate:ctrl_alt_bksp et-EE,et
sk-qwerty sk pc105 qwerty terminate:ctrl_alt_bksp -
sk-qwertz sk pc105 - terminate:ctrl_alt_bksp sk-SK,sk
fr-latin9 fr pc105 latin9 terminate:ctrl_alt_bksp -
fr_CH-latin1 ch pc105 fr terminate:ctrl_alt_bksp -
cf ca pc105 - terminate:ctrl_alt_bksp fr-CA
sv-latin1 se pc105 - terminate:ctrl_alt_bksp sv-SE,sv
sr-cy rs pc105 - terminate:ctrl_alt_bksp sr-Cyrl-RS,sr-Cyrl,sr-RS,sr
gr gr,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll el-GR,el
by by,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll be-BY,be
il il pc105 - terminate:ctrl_alt_bksp he-IL,he
kazakh kz,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll kk-KZ,kk
lt.baltic lt pc105 - terminate:ctrl_alt_bksp -
lt.l4 lt pc105 - terminate:ctrl_alt_bksp -
lt lt pc105 - terminate:ctrl_alt_bksp lt-LT,lt
khmer kh,us pc105 - terminate:ctrl_alt_bksp km-KH,km
es-dvorak es microsoftpro dvorak terminate:ctrl_alt_bksp -
lv lv pc105 apostrophe terminate:ctrl_alt_bksp lv-LV,lv
lv-tilde lv pc105 tilde terminate:ctrl_alt_bksp -
ge ge,us pc105 - terminate:ctrl_alt_bksp ka-GE,ka
+77
View File
@@ -578,6 +578,83 @@ int find_language_fallback(const char *lang, char **ret) {
}
}
int find_vconsole_keymap_for_bcp47(const char *tag, char **ret) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *fallback = NULL;
const char *map;
int r;
/* Look up a vconsole keymap by RFC 4646 / BCP 47 language tag (e.g. "de-DE") using the optional
* sixth column of /usr/share/systemd/kbd-model-map. That column lists comma-separated tags the
* row matches. An exact (case-insensitive) tag match returns immediately; if no exact match
* exists, the first row whose tag matches the input's primary subtag wins. Returns 1 on match,
* 0 otherwise. */
assert(tag);
assert(ret);
if (isempty(tag)) {
*ret = NULL;
return 0;
}
size_t primary_len = strcspn(tag, "-");
if (primary_len == 0) {
*ret = NULL;
return 0;
}
map = systemd_kbd_model_map();
f = fopen(map, "re");
if (!f)
return -errno;
for (unsigned n = 0;;) {
_cleanup_strv_free_ char **a = NULL, **tags = NULL;
r = read_next_mapping(map, 5, UINT_MAX, f, &n, &a);
if (r < 0)
return r;
if (r == 0)
break;
/* The BCP 47 tag list is the optional 6th column. "-" / empty means "no tags". */
if (strv_length(a) < 6 || isempty(a[5]) || streq(a[5], "-"))
continue;
r = strv_split_full(&tags, a[5], ",", /* flags= */ 0);
if (r < 0)
return r;
STRV_FOREACH(t, tags) {
if (strcaseeq(*t, tag)) {
log_debug("Found vconsole keymap '%s' for BCP 47 tag '%s' (exact match).",
a[0], tag);
r = strdup_to(ret, a[0]);
if (r < 0)
return r;
return 1;
}
if (!fallback && strlen(*t) == primary_len && !strchr(*t, '-') && strncaseeq(*t, tag, primary_len)) {
fallback = strdup(a[0]);
if (!fallback)
return -ENOMEM;
}
}
}
if (!fallback) {
*ret = NULL;
return 0;
}
log_debug("Found vconsole keymap '%s' for BCP 47 tag '%s' (primary subtag match).", fallback, tag);
*ret = TAKE_PTR(fallback);
return 1;
}
int vconsole_serialize(const VCContext *vc, const X11Context *xc, char ***env) {
int r;
+2
View File
@@ -39,4 +39,6 @@ typedef int (*X11VerifyCallback)(const X11Context *xc);
int vconsole_convert_to_x11(const VCContext *vc, X11VerifyCallback verify, X11Context *ret);
int x11_convert_to_vconsole(const X11Context *xc, VCContext *ret);
int find_vconsole_keymap_for_bcp47(const char *tag, char **ret);
int vconsole_serialize(const VCContext *vc, const X11Context *xc, char ***env);
+35 -2
View File
@@ -15,6 +15,7 @@
#include "alloc-util.h"
#include "creds-util.h"
#include "efivars.h"
#include "env-file.h"
#include "errno-util.h"
#include "fd-util.h"
@@ -30,6 +31,7 @@
#include "string-util.h"
#include "strv.h"
#include "terminal-util.h"
#include "vconsole-util.h"
typedef struct Context {
char *keymap;
@@ -72,6 +74,34 @@ static void context_merge_config(
context_merge(dst, src, src_compat, font_unimap);
}
static int context_read_efi(Context *c) {
_cleanup_(context_done) Context v = {};
_cleanup_free_ char *tag = NULL;
int r;
assert(c);
if (!is_efi_boot())
return 0;
r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("LoaderKeyboardLayout"), &tag);
if (r == -ENOENT)
return 0;
if (r < 0)
return log_debug_errno(r, "Failed to read LoaderKeyboardLayout EFI variable, ignoring: %m");
r = find_vconsole_keymap_for_bcp47(tag, &v.keymap);
if (r < 0)
return log_debug_errno(r, "Failed to look up vconsole keymap for firmware tag '%s', ignoring: %m", tag);
if (r == 0) {
log_debug("No vconsole keymap matches firmware-provided keyboard layout '%s', ignoring.", tag);
return 0;
}
context_merge_config(c, &v, /* src_compat= */ NULL);
return 0;
}
static int context_read_creds(Context *c) {
_cleanup_(context_done) Context v = {};
int r;
@@ -144,10 +174,13 @@ static int context_read_proc_cmdline(Context *c) {
static void context_load_config(Context *c) {
assert(c);
/* Load data from credentials (lowest priority) */
/* Pick up the firmware-provided keyboard layout if any (lowest priority) */
(void) context_read_efi(c);
/* Load data from credentials */
(void) context_read_creds(c);
/* Load data from configuration file (middle priority) */
/* Load data from configuration file */
(void) context_read_env(c);
/* Let the kernel command line override /etc/vconsole.conf (highest priority) */