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).
74 lines
4.1 KiB
C
74 lines
4.1 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
|
|
#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"
|
|
#include "util.h"
|
|
|
|
void export_common_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) {
|
|
assert(loaded_image);
|
|
|
|
/* Export the device path this image is started from, if it's not set yet */
|
|
if (loaded_image->DeviceHandle) {
|
|
if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderDevicePartUUID", /* ret_data= */ NULL, /* ret_size= */ NULL) != EFI_SUCCESS) {
|
|
_cleanup_free_ char16_t *uuid = disk_get_part_uuid(loaded_image->DeviceHandle);
|
|
if (uuid)
|
|
efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderDevicePartUUID", uuid, 0);
|
|
}
|
|
|
|
if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderDeviceURL", /* ret_data= */ NULL, /* ret_size= */ NULL) != EFI_SUCCESS) {
|
|
_cleanup_free_ char16_t *url = disk_get_url(loaded_image->DeviceHandle);
|
|
if (url)
|
|
efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderDeviceURL", url, 0);
|
|
}
|
|
}
|
|
|
|
/* If LoaderImageIdentifier is not set, assume the image with this stub was loaded directly from the
|
|
* UEFI firmware without any boot loader, and hence set the LoaderImageIdentifier ourselves. Note
|
|
* that some boot chain loaders neither set LoaderImageIdentifier nor make FilePath available to us,
|
|
* in which case there's simple nothing to set for us. (The UEFI spec doesn't really say who's wrong
|
|
* here, i.e. whether FilePath may be NULL or not, hence handle this gracefully and check if FilePath
|
|
* is non-NULL explicitly.) */
|
|
if (loaded_image->FilePath &&
|
|
efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderImageIdentifier", NULL, NULL) != EFI_SUCCESS) {
|
|
_cleanup_free_ char16_t *s = NULL;
|
|
if (device_path_to_str(loaded_image->FilePath, &s) == EFI_SUCCESS)
|
|
efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderImageIdentifier", s, 0);
|
|
}
|
|
|
|
/* if LoaderFirmwareInfo is not set, let's set it */
|
|
if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareInfo", NULL, NULL) != EFI_SUCCESS) {
|
|
_cleanup_free_ char16_t *s = NULL;
|
|
s = xasprintf("%ls %u.%02u", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
|
|
efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareInfo", s, 0);
|
|
}
|
|
|
|
/* ditto for LoaderFirmwareType */
|
|
if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareType", NULL, NULL) != EFI_SUCCESS) {
|
|
_cleanup_free_ char16_t *s = NULL;
|
|
s = xasprintf("UEFI %u.%02u", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
|
|
efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareType", s, 0);
|
|
}
|
|
|
|
/* ditto for LoaderTpm2ActivePcrBanks */
|
|
if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderTpm2ActivePcrBanks", NULL, NULL) != EFI_SUCCESS) {
|
|
uint32_t active_pcr_banks = tpm_get_active_pcr_banks();
|
|
_cleanup_free_ char16_t *s = NULL;
|
|
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);
|
|
}
|
|
}
|