coreboot-kgpe-d16/src/lib/ux_locales.c
Subrata Banik 7f7ebb7b3d lib: Update locales for non-VBOOT platforms
This patch sets the default locales to English for platforms that
do not have support for VBOOT configuration. This ensures that the
system will use English locales if the platform does not provide
its own locale settings.

TEST=Built and booted the google/rex platform successfully.

Change-Id: I7554c8bfd58411f460deeb22cf7218059ca8ba9f
Signed-off-by: Subrata Banik <subratabanik@google.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/79054
Reviewed-by: Hsuan-ting Chen <roccochen@google.com>
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Jérémy Compostella <jeremy.compostella@intel.com>
Reviewed-by: Julius Werner <jwerner@chromium.org>
2023-11-16 05:19:20 +00:00

191 lines
5.7 KiB
C

/* 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_VERSION_BYTE 0x01
#define PRERAM_LOCALES_NAME "preram_locales"
/* We need different delimiters to deal with the case where 'string_name' is the same as
'localized_string'. */
#define DELIM_STR 0x00
#define DELIM_NAME 0x01
/*
* 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:
*
* [PRERAM_LOCALES_VERSION_BYTE]
* [string_name_1] [\x00]
* [language_id_1] [\x00] [localized_string_1] [\x00]
* [language_id_2] [\x00] [localized_string_2] [\x00] ...
* [\x01]
* [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 delim. */
static size_t move_next(const char *data, size_t offset, size_t size, char delim)
{
while (offset < size && data[offset] != delim)
offset++;
/* If we found delim, move to the start of the next string. */
if (offset < size)
offset++;
return offset;
}
/* Find the next occurrence of the specific string. Strings are separated by delim. */
static size_t search_for(const char *data, size_t offset, size_t size,
const char *str, char delim)
{
while (offset < size) {
if (!strncmp(data + offset, str, size - offset))
return offset;
offset = move_next(data, offset, size, delim);
}
return size;
}
/* Find the next occurrence of the string_name, which should always follow a DELIM_NAME. */
static inline size_t search_for_name(const char *data, size_t offset, size_t size,
const char *name)
{
return search_for(data, offset, size, name, DELIM_NAME);
}
/* 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, DELIM_STR);
}
const char *ux_locales_get_text(const char *name)
{
const char *data;
size_t size, offset, name_offset, next_name_offset, next;
uint32_t lang_id = 0; /* default language English (0) */
unsigned char version;
data = locales_get_map(&size, false);
if (!data || size == 0) {
printk(BIOS_ERR, "%s: %s not found.\n", __func__,
PRERAM_LOCALES_NAME);
return NULL;
}
if (CONFIG(VBOOT)) {
/* 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_WARNING, "%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);
/* Check if the version byte is the expected version. */
version = (unsigned char)data[0];
if (version != PRERAM_LOCALES_VERSION_BYTE) {
printk(BIOS_ERR, "%s: The version %u is not the expected one %u\n",
__func__, version, PRERAM_LOCALES_VERSION_BYTE);
return NULL;
}
/* Search for name. Skip the version byte. */
offset = search_for_name(data, 1, size, name);
if (offset >= size) {
printk(BIOS_ERR, "%s: Name %s not found.\n", __func__, name);
return NULL;
}
name_offset = offset;
/* Search for language ID. We should not search beyond the range of the current
string_name. */
next_name_offset = move_next(data, offset, size, DELIM_NAME);
assert(next_name_offset <= size);
offset = search_for_id(data, name_offset, next_name_offset, lang_id);
/* Language ID not supported; fallback to English if the current language is not
English (0). */
if (offset >= next_name_offset) {
/* 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, next_name_offset, 0);
if (offset >= next_name_offset) {
printk(BIOS_ERR, "%s: Neither %d nor 0 found.\n", __func__, lang_id);
return NULL;
}
}
/* Move to the corresponding localized_string. */
offset = move_next(data, offset, next_name_offset, DELIM_STR);
if (offset >= next_name_offset)
return NULL;
/* Validity check that the returned string must be NULL terminated. */
next = move_next(data, offset, next_name_offset, DELIM_STR) - 1;
if (next >= next_name_offset || data[next] != '\0') {
printk(BIOS_ERR, "%s: %s is not NULL terminated.\n",
__func__, PRERAM_LOCALES_NAME);
return NULL;
}
return data + offset;
}