diff options
-rw-r--r-- | src/include/ux_locales.h | 18 | ||||
-rw-r--r-- | src/lib/Makefile.inc | 2 | ||||
-rw-r--r-- | src/lib/ux_locales.c | 167 | ||||
-rw-r--r-- | src/soc/intel/alderlake/romstage/ux.c | 16 |
4 files changed, 199 insertions, 4 deletions
diff --git a/src/include/ux_locales.h b/src/include/ux_locales.h new file mode 100644 index 0000000000..1affd925d1 --- /dev/null +++ b/src/include/ux_locales.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _UX_LOCALES_H_ +#define _UX_LOCALES_H_ + +#include <types.h> + +/* Unmap the preram_locales if it has been mapped. No-op otherwise. */ +void ux_locales_unmap(void); + +/* + * Get the localized text for a given string name. + * This function will try to read the language ID from vboot API, and search the + * corresponding translation from CBFS preram_locales. + */ +const char *ux_locales_get_text(const char *name); + +#endif // _UX_LOCALES_H_ diff --git a/src/lib/Makefile.inc b/src/lib/Makefile.inc index 385c87eb7c..35b892a44b 100644 --- a/src/lib/Makefile.inc +++ b/src/lib/Makefile.inc @@ -428,3 +428,5 @@ cbfs-files-$(NEED_CBFS_POINTER) += header_pointer header_pointer-file := master_header_pointer.c:struct header_pointer-position := -4 header_pointer-type := "cbfs header" + +romstage-y += ux_locales.c diff --git a/src/lib/ux_locales.c b/src/lib/ux_locales.c new file mode 100644 index 0000000000..0ba66ad8cc --- /dev/null +++ b/src/lib/ux_locales.c @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <cbfs.h> +#include <console/console.h> +#include <security/vboot/misc.h> +#include <stddef.h> +#include <string.h> +#include <ux_locales.h> +#include <vb2_api.h> + +#define LANG_ID_MAX 100 +#define LANG_ID_LEN 3 +#define PRERAM_LOCALES_NAME "preram_locales" + +/* + * Devices which support early vga have the capability to show localized text in + * Code Page 437 encoding. (see src/drivers/pc80/vga/vga_font_8x16.c) + * + * preram_locales located in CBFS is an uncompressed file located in either RO + * or RW CBFS. It contains the localization information in the following format: + * + * [string_name_1] [\x00] + * [language_id_1] [\x00] [localized_string_1] [\x00] + * [language_id_2] [\x00] [localized_string_2] [\x00] ... + * [string_name_2] [\x00] ... + * + * This file contains tools to locate the file and search for localized strings + * with specific language ID. + */ + +/* Cached state for map (locales_get_map) and unmap (ux_locales_unmap). */ +struct preram_locales_state { + void *data; + size_t size; + bool initialized; +}; + +static struct preram_locales_state cached_state; + +void ux_locales_unmap(void) +{ + if (cached_state.initialized) { + if (cached_state.data) + cbfs_unmap(cached_state.data); + cached_state.initialized = false; + cached_state.size = 0; + cached_state.data = NULL; + } +} + +/* Get the map address of preram_locales. */ +static void *locales_get_map(size_t *size_out, bool unmap) +{ + if (cached_state.initialized) { + *size_out = cached_state.size; + return cached_state.data; + } + cached_state.initialized = true; + cached_state.data = cbfs_ro_map(PRERAM_LOCALES_NAME, + &cached_state.size); + *size_out = cached_state.size; + return cached_state.data; +} + +/* Move to the next string in the data. Strings are separated by 0x00. */ +static size_t move_next(const char *data, size_t offset, size_t size) +{ + if (offset >= size) + return size; + while (offset < size && data[offset] != '\0') + offset++; + offset++; + return offset; +} + +/* Find the next occurrence of the specific string. */ +static size_t search_for(const char *data, size_t offset, size_t size, + const char *str) +{ + if (offset >= size) + return size; + while (offset < size) { + if (!strncmp(data + offset, str, size - offset)) + return offset; + offset = move_next(data, offset, size); + } + return size; +} + +/* Find the next occurrence of the integer ID, where ID is less than 100. */ +static size_t search_for_id(const char *data, size_t offset, size_t size, + int id) +{ + if (id >= LANG_ID_MAX) + return offset; + char int_to_str[LANG_ID_LEN] = {}; + snprintf(int_to_str, LANG_ID_LEN, "%d", id); + return search_for(data, offset, size, int_to_str); +} + +const char *ux_locales_get_text(const char *name) +{ + const char *data; + size_t size, offset, name_offset, next; + uint32_t lang_id; + + data = locales_get_map(&size, false); + if (!data) { + printk(BIOS_INFO, "%s: %s not found.\n", __func__, + PRERAM_LOCALES_NAME); + return NULL; + } + + /* Get the language ID from vboot API. */ + lang_id = vb2api_get_locale_id(vboot_get_context()); + /* Validity check: Language ID should smaller than LANG_ID_MAX. */ + if (lang_id >= LANG_ID_MAX) { + printk(BIOS_INFO, "%s: ID %d too big; fallback to 0.\n", + __func__, lang_id); + lang_id = 0; + } + + printk(BIOS_INFO, "%s: Search for %s with language ID: %u\n", + __func__, name, lang_id); + + offset = 0; + /* Search for name. */ + offset = search_for(data, offset, size, name); + if (offset >= size) { + printk(BIOS_INFO, "%s: Name %s not found.\n", __func__, name); + return NULL; + } + name_offset = offset; + + /* Search for language ID. */ + offset = search_for_id(data, name_offset, size, lang_id); + /* Language ID not supported; fallback to English if the current + * language is not English (0). */ + if (offset >= size) { + /* + * Since we only support a limited charset, it is very normal + * that a language is not supported and we fallback here + * silently. + */ + if (lang_id != 0) + offset = search_for_id(data, name_offset, size, 0); + if (offset >= size) { + printk(BIOS_INFO, "%s: Neither %d nor 0 found.\n", + __func__, lang_id); + return NULL; + } + } + + offset = move_next(data, offset, size); + if (offset >= size) + return NULL; + + /* Validity check that the returned string must be NULL terminated. */ + next = move_next(data, offset, size) - 1; + if (next >= size || data[next] != '\0') { + printk(BIOS_INFO, "%s: %s is not NULL terminated.\n", + __func__, PRERAM_LOCALES_NAME); + return NULL; + } + + return data + offset; +} diff --git a/src/soc/intel/alderlake/romstage/ux.c b/src/soc/intel/alderlake/romstage/ux.c index 23fd0fef7e..af8c7832ac 100644 --- a/src/soc/intel/alderlake/romstage/ux.c +++ b/src/soc/intel/alderlake/romstage/ux.c @@ -3,9 +3,12 @@ #include <console/console.h> #include <intelblocks/early_graphics.h> #include <pc80/vga.h> +#include <ux_locales.h> #include "ux.h" +#define UX_MEMORY_TRAINING_DESC "memory_training_desc" + bool ux_inform_user_of_update_operation(const char *name) { if (!CONFIG(MAINBOARD_HAS_EARLY_LIBGFXINIT) || @@ -13,10 +16,15 @@ bool ux_inform_user_of_update_operation(const char *name) return false; printk(BIOS_INFO, "Informing user on-display of %s.\n", name); - vga_write_text(VGA_TEXT_CENTER, VGA_TEXT_HORIZONTAL_MIDDLE, - (const unsigned char *) - "Your device is finishing an update. " + + const char *text = ux_locales_get_text(UX_MEMORY_TRAINING_DESC); + /* No localized text found; fallback to built-in English. */ + if (!text) + text = "Your device is finishing an update. " "This may take 1-2 minutes.\n" - "Please do not turn off your device."); + "Please do not turn off your device."; + vga_write_text(VGA_TEXT_CENTER, VGA_TEXT_HORIZONTAL_MIDDLE, + (const unsigned char *)text); + ux_locales_unmap(); return true; } |