soc/intel: Remove blank lines before '}' and after '{'
[coreboot2.git] / src / lib / ux_locales.c
blob0a285d9b6776ae289317e0e03afacbf0af9aaa59
1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <cbfs.h>
4 #include <console/console.h>
5 #include <security/vboot/misc.h>
6 #include <stddef.h>
7 #include <string.h>
8 #include <ux_locales.h>
9 #include <vb2_api.h>
11 #define LANG_ID_MAX 100
12 #define LANG_ID_LEN 3
14 #define PRERAM_LOCALES_VERSION_BYTE 0x01
15 #define PRERAM_LOCALES_NAME "preram_locales"
17 /* We need different delimiters to deal with the case where 'string_name' is the same as
18 'localized_string'. */
19 #define DELIM_STR 0x00
20 #define DELIM_NAME 0x01
23 * Devices which support early vga have the capability to show localized text in
24 * Code Page 437 encoding. (see src/drivers/pc80/vga/vga_font_8x16.c)
26 * preram_locales located in CBFS is an uncompressed file located in either RO
27 * or RW CBFS. It contains the localization information in the following format:
29 * [PRERAM_LOCALES_VERSION_BYTE]
30 * [string_name_1] [\x00]
31 * [language_id_1] [\x00] [localized_string_1] [\x00]
32 * [language_id_2] [\x00] [localized_string_2] [\x00] ...
33 * [\x01]
34 * [string_name_2] [\x00] ...
36 * This file contains tools to locate the file and search for localized strings
37 * with specific language ID.
40 /* Cached state for map (locales_get_map) and unmap (ux_locales_unmap). */
41 struct preram_locales_state {
42 void *data;
43 size_t size;
44 bool initialized;
47 static struct preram_locales_state cached_state;
49 void ux_locales_unmap(void)
51 if (cached_state.initialized) {
52 if (cached_state.data)
53 cbfs_unmap(cached_state.data);
54 cached_state.initialized = false;
55 cached_state.size = 0;
56 cached_state.data = NULL;
60 /* Get the map address of preram_locales. */
61 static void *locales_get_map(size_t *size_out, bool unmap)
63 if (cached_state.initialized) {
64 *size_out = cached_state.size;
65 return cached_state.data;
67 cached_state.initialized = true;
68 cached_state.data = cbfs_ro_map(PRERAM_LOCALES_NAME,
69 &cached_state.size);
70 *size_out = cached_state.size;
71 return cached_state.data;
74 /* Move to the next string in the data. Strings are separated by delim. */
75 static size_t move_next(const char *data, size_t offset, size_t size, char delim)
77 while (offset < size && data[offset] != delim)
78 offset++;
79 /* If we found delim, move to the start of the next string. */
80 if (offset < size)
81 offset++;
82 return offset;
85 /* Find the next occurrence of the specific string. Strings are separated by delim. */
86 static size_t search_for(const char *data, size_t offset, size_t size,
87 const char *str, char delim)
89 while (offset < size) {
90 if (!strncmp(data + offset, str, size - offset))
91 return offset;
92 offset = move_next(data, offset, size, delim);
94 return size;
97 /* Find the next occurrence of the string_name, which should always follow a DELIM_NAME. */
98 static inline size_t search_for_name(const char *data, size_t offset, size_t size,
99 const char *name)
101 return search_for(data, offset, size, name, DELIM_NAME);
104 /* Find the next occurrence of the integer ID, where ID is less than 100. */
105 static size_t search_for_id(const char *data, size_t offset, size_t size,
106 int id)
108 if (id >= LANG_ID_MAX)
109 return offset;
110 char int_to_str[LANG_ID_LEN] = {};
111 snprintf(int_to_str, LANG_ID_LEN, "%d", id);
112 return search_for(data, offset, size, int_to_str, DELIM_STR);
115 const char *ux_locales_get_text(const char *name)
117 const char *data;
118 size_t size, offset, name_offset, next_name_offset, next;
119 uint32_t lang_id = 0; /* default language English (0) */
120 unsigned char version;
122 data = locales_get_map(&size, false);
123 if (!data || size == 0) {
124 printk(BIOS_ERR, "%s: %s not found.\n", __func__,
125 PRERAM_LOCALES_NAME);
126 return NULL;
129 if (CONFIG(VBOOT)) {
130 /* Get the language ID from vboot API. */
131 lang_id = vb2api_get_locale_id(vboot_get_context());
132 /* Validity check: Language ID should smaller than LANG_ID_MAX. */
133 if (lang_id >= LANG_ID_MAX) {
134 printk(BIOS_WARNING, "%s: ID %d too big; fallback to 0.\n",
135 __func__, lang_id);
136 lang_id = 0;
140 printk(BIOS_INFO, "%s: Search for %s with language ID: %u\n",
141 __func__, name, lang_id);
143 /* Check if the version byte is the expected version. */
144 version = (unsigned char)data[0];
145 if (version != PRERAM_LOCALES_VERSION_BYTE) {
146 printk(BIOS_ERR, "%s: The version %u is not the expected one %u\n",
147 __func__, version, PRERAM_LOCALES_VERSION_BYTE);
148 return NULL;
151 /* Search for name. Skip the version byte. */
152 offset = search_for_name(data, 1, size, name);
153 if (offset >= size) {
154 printk(BIOS_ERR, "%s: Name %s not found.\n", __func__, name);
155 return NULL;
157 name_offset = offset;
159 /* Search for language ID. We should not search beyond the range of the current
160 string_name. */
161 next_name_offset = move_next(data, offset, size, DELIM_NAME);
162 assert(next_name_offset <= size);
163 offset = search_for_id(data, name_offset, next_name_offset, lang_id);
164 /* Language ID not supported; fallback to English if the current language is not
165 English (0). */
166 if (offset >= next_name_offset) {
167 /* Since we only support a limited charset, it is very normal that a language
168 is not supported and we fallback here silently. */
169 if (lang_id != 0)
170 offset = search_for_id(data, name_offset, next_name_offset, 0);
171 if (offset >= next_name_offset) {
172 printk(BIOS_ERR, "%s: Neither %d nor 0 found.\n", __func__, lang_id);
173 return NULL;
177 /* Move to the corresponding localized_string. */
178 offset = move_next(data, offset, next_name_offset, DELIM_STR);
179 if (offset >= next_name_offset)
180 return NULL;
182 /* Validity check that the returned string must be NULL terminated. */
183 next = move_next(data, offset, next_name_offset, DELIM_STR) - 1;
184 if (next >= next_name_offset || data[next] != '\0') {
185 printk(BIOS_ERR, "%s: %s is not NULL terminated.\n",
186 __func__, PRERAM_LOCALES_NAME);
187 return NULL;
190 return data + offset;