1 /* SPDX-License-Identifier: GPL-2.0-only */
4 #include <console/console.h>
5 #include <security/vboot/misc.h>
9 #include <ux_locales.h>
12 #define LANG_ID_MAX 100
15 #define PRERAM_LOCALES_VERSION_BYTE 0x01
16 #define PRERAM_LOCALES_NAME "preram_locales"
18 /* We need different delimiters to deal with the case where 'string_name' is the same as
19 'localized_string'. */
20 #define DELIM_STR 0x00
21 #define DELIM_NAME 0x01
24 * Devices which support early vga have the capability to show localized text in
25 * Code Page 437 encoding. (see src/drivers/pc80/vga/vga_font_8x16.c)
27 * preram_locales located in CBFS is an uncompressed file located in either RO
28 * or RW CBFS. It contains the localization information in the following format:
30 * [PRERAM_LOCALES_VERSION_BYTE]
31 * [string_name_1] [\x00]
32 * [language_id_1] [\x00] [localized_string_1] [\x00]
33 * [language_id_2] [\x00] [localized_string_2] [\x00] ...
35 * [string_name_2] [\x00] ...
37 * This file contains tools to locate the file and search for localized strings
38 * with specific language ID.
41 /* Cached state for map (locales_get_map) and unmap (ux_locales_unmap). */
42 struct preram_locales_state
{
48 static struct preram_locales_state cached_state
;
50 void ux_locales_unmap(void)
52 if (cached_state
.initialized
) {
53 if (cached_state
.data
)
54 cbfs_unmap(cached_state
.data
);
55 cached_state
.initialized
= false;
56 cached_state
.size
= 0;
57 cached_state
.data
= NULL
;
61 /* Get the map address of preram_locales. */
62 static void *locales_get_map(size_t *size_out
, bool unmap
)
64 if (cached_state
.initialized
) {
65 *size_out
= cached_state
.size
;
66 return cached_state
.data
;
68 cached_state
.initialized
= true;
69 cached_state
.data
= cbfs_ro_map(PRERAM_LOCALES_NAME
,
71 *size_out
= cached_state
.size
;
72 return cached_state
.data
;
75 /* Move to the next string in the data. Strings are separated by delim. */
76 static size_t move_next(const char *data
, size_t offset
, size_t size
, char delim
)
78 while (offset
< size
&& data
[offset
] != delim
)
80 /* If we found delim, move to the start of the next string. */
86 /* Find the next occurrence of the specific string. Strings are separated by delim. */
87 static size_t search_for(const char *data
, size_t offset
, size_t size
,
88 const char *str
, char delim
)
90 while (offset
< size
) {
91 if (!strncmp(data
+ offset
, str
, size
- offset
))
93 offset
= move_next(data
, offset
, size
, delim
);
98 /* Find the next occurrence of the string_name, which should always follow a DELIM_NAME. */
99 static inline size_t search_for_name(const char *data
, size_t offset
, size_t size
,
102 return search_for(data
, offset
, size
, name
, DELIM_NAME
);
105 /* Find the next occurrence of the integer ID, where ID is less than 100. */
106 static size_t search_for_id(const char *data
, size_t offset
, size_t size
,
109 if (id
>= LANG_ID_MAX
)
111 char int_to_str
[LANG_ID_LEN
] = {};
112 snprintf(int_to_str
, LANG_ID_LEN
, "%d", id
);
113 return search_for(data
, offset
, size
, int_to_str
, DELIM_STR
);
116 const char *ux_locales_get_text(const char *name
)
119 size_t size
, offset
, name_offset
, next_name_offset
, next
;
120 uint32_t lang_id
= 0; /* default language English (0) */
121 unsigned char version
;
123 data
= locales_get_map(&size
, false);
124 if (!data
|| size
== 0) {
125 printk(BIOS_ERR
, "%s: %s not found.\n", __func__
,
126 PRERAM_LOCALES_NAME
);
131 /* Get the language ID from vboot API. */
132 lang_id
= vb2api_get_locale_id(vboot_get_context());
133 /* Validity check: Language ID should smaller than LANG_ID_MAX. */
134 if (lang_id
>= LANG_ID_MAX
) {
135 printk(BIOS_WARNING
, "%s: ID %d too big; fallback to 0.\n",
141 printk(BIOS_INFO
, "%s: Search for %s with language ID: %u\n",
142 __func__
, name
, lang_id
);
144 /* Check if the version byte is the expected version. */
145 version
= (unsigned char)data
[0];
146 if (version
!= PRERAM_LOCALES_VERSION_BYTE
) {
147 printk(BIOS_ERR
, "%s: The version %u is not the expected one %u\n",
148 __func__
, version
, PRERAM_LOCALES_VERSION_BYTE
);
152 /* Search for name. Skip the version byte. */
153 offset
= search_for_name(data
, 1, size
, name
);
154 if (offset
>= size
) {
155 printk(BIOS_ERR
, "%s: Name %s not found.\n", __func__
, name
);
158 name_offset
= offset
;
160 /* Search for language ID. We should not search beyond the range of the current
162 next_name_offset
= move_next(data
, offset
, size
, DELIM_NAME
);
163 assert(next_name_offset
<= size
);
164 offset
= search_for_id(data
, name_offset
, next_name_offset
, lang_id
);
165 /* Language ID not supported; fallback to English if the current language is not
167 if (offset
>= next_name_offset
) {
168 /* Since we only support a limited charset, it is very normal that a language
169 is not supported and we fallback here silently. */
171 offset
= search_for_id(data
, name_offset
, next_name_offset
, 0);
172 if (offset
>= next_name_offset
) {
173 printk(BIOS_ERR
, "%s: Neither %d nor 0 found.\n", __func__
, lang_id
);
178 /* Move to the corresponding localized_string. */
179 offset
= move_next(data
, offset
, next_name_offset
, DELIM_STR
);
180 if (offset
>= next_name_offset
)
183 /* Validity check that the returned string must be NULL terminated. */
184 next
= move_next(data
, offset
, next_name_offset
, DELIM_STR
) - 1;
185 if (next
>= next_name_offset
|| data
[next
] != '\0') {
186 printk(BIOS_ERR
, "%s: %s is not NULL terminated.\n",
187 __func__
, PRERAM_LOCALES_NAME
);
191 return data
+ offset
;