lib: Support localized text of memory_training_desc in ux_locales.c
To support the localized text, we need to get the locale id by vboot APIs and read raw string content file: preram_locales located at either RO or RW. The preram_locales file follows the format: [string_name_1] [\x00] [locale_id_1] [\x00] [localized_string_1] [\x00] [locale_id_2] [\x00] [localized_string_2] ... [string_name_2] [\x00] ... This code will search for the correct localized string that its string name is `memory_training_desc` and its locale ID matches the ID vb2api returns. If no valid string found, we will try to display in English (locale ID 0). BUG=b:264666392 BRANCH=brya TEST=emerge-brya coreboot chromeos-bmpblk chromeos-bootimage Change-Id: I7e3c8d103c938a11b397c32c9228e44e31c3f01d Signed-off-by: Hsuan Ting Chen <roccochen@chromium.org> Reviewed-on: https://review.coreboot.org/c/coreboot/+/75330 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Julius Werner <jwerner@chromium.org>
This commit is contained in:
parent
ba5a2a189e
commit
3c2cdb6e57
|
@ -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_
|
|
@ -428,3 +428,5 @@ cbfs-files-$(NEED_CBFS_POINTER) += header_pointer
|
||||||
header_pointer-file := master_header_pointer.c:struct
|
header_pointer-file := master_header_pointer.c:struct
|
||||||
header_pointer-position := -4
|
header_pointer-position := -4
|
||||||
header_pointer-type := "cbfs header"
|
header_pointer-type := "cbfs header"
|
||||||
|
|
||||||
|
romstage-y += 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;
|
||||||
|
}
|
|
@ -3,9 +3,12 @@
|
||||||
#include <console/console.h>
|
#include <console/console.h>
|
||||||
#include <intelblocks/early_graphics.h>
|
#include <intelblocks/early_graphics.h>
|
||||||
#include <pc80/vga.h>
|
#include <pc80/vga.h>
|
||||||
|
#include <ux_locales.h>
|
||||||
|
|
||||||
#include "ux.h"
|
#include "ux.h"
|
||||||
|
|
||||||
|
#define UX_MEMORY_TRAINING_DESC "memory_training_desc"
|
||||||
|
|
||||||
bool ux_inform_user_of_update_operation(const char *name)
|
bool ux_inform_user_of_update_operation(const char *name)
|
||||||
{
|
{
|
||||||
if (!CONFIG(MAINBOARD_HAS_EARLY_LIBGFXINIT) ||
|
if (!CONFIG(MAINBOARD_HAS_EARLY_LIBGFXINIT) ||
|
||||||
|
@ -13,10 +16,15 @@ bool ux_inform_user_of_update_operation(const char *name)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
printk(BIOS_INFO, "Informing user on-display of %s.\n", name);
|
printk(BIOS_INFO, "Informing user on-display of %s.\n", name);
|
||||||
vga_write_text(VGA_TEXT_CENTER, VGA_TEXT_HORIZONTAL_MIDDLE,
|
|
||||||
(const unsigned char *)
|
const char *text = ux_locales_get_text(UX_MEMORY_TRAINING_DESC);
|
||||||
"Your device is finishing an update. "
|
/* 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"
|
"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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue