blob: b3895489552cb0d0574d7e53a368e4b6160795f1 [file] [log] [blame]
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +08001/* SPDX-License-Identifier: GPL-2.0-only */
2
3#include <cbfs.h>
4#include <console/console.h>
5#include <security/vboot/misc.h>
6#include <stddef.h>
Elyes Haouasbdd03c22024-05-27 11:20:07 +02007#include <stdio.h>
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +08008#include <string.h>
9#include <ux_locales.h>
10#include <vb2_api.h>
11
12#define LANG_ID_MAX 100
13#define LANG_ID_LEN 3
Hsuan Ting Chenf4e3f152023-07-06 15:58:42 +080014
15#define PRERAM_LOCALES_VERSION_BYTE 0x01
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +080016#define PRERAM_LOCALES_NAME "preram_locales"
17
Hsuan Ting Chenf4e3f152023-07-06 15:58:42 +080018/* 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
22
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +080023/*
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)
26 *
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:
29 *
Hsuan Ting Chenf4e3f152023-07-06 15:58:42 +080030 * [PRERAM_LOCALES_VERSION_BYTE]
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +080031 * [string_name_1] [\x00]
32 * [language_id_1] [\x00] [localized_string_1] [\x00]
33 * [language_id_2] [\x00] [localized_string_2] [\x00] ...
Hsuan Ting Chenf4e3f152023-07-06 15:58:42 +080034 * [\x01]
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +080035 * [string_name_2] [\x00] ...
36 *
37 * This file contains tools to locate the file and search for localized strings
38 * with specific language ID.
39 */
40
41/* Cached state for map (locales_get_map) and unmap (ux_locales_unmap). */
42struct preram_locales_state {
43 void *data;
44 size_t size;
45 bool initialized;
46};
47
48static struct preram_locales_state cached_state;
49
50void ux_locales_unmap(void)
51{
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;
58 }
59}
60
61/* Get the map address of preram_locales. */
62static void *locales_get_map(size_t *size_out, bool unmap)
63{
64 if (cached_state.initialized) {
65 *size_out = cached_state.size;
66 return cached_state.data;
67 }
68 cached_state.initialized = true;
69 cached_state.data = cbfs_ro_map(PRERAM_LOCALES_NAME,
70 &cached_state.size);
71 *size_out = cached_state.size;
72 return cached_state.data;
73}
74
Hsuan Ting Chenf4e3f152023-07-06 15:58:42 +080075/* Move to the next string in the data. Strings are separated by delim. */
76static size_t move_next(const char *data, size_t offset, size_t size, char delim)
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +080077{
Hsuan Ting Chenf4e3f152023-07-06 15:58:42 +080078 while (offset < size && data[offset] != delim)
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +080079 offset++;
Hsuan Ting Chenf4e3f152023-07-06 15:58:42 +080080 /* If we found delim, move to the start of the next string. */
81 if (offset < size)
82 offset++;
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +080083 return offset;
84}
85
Hsuan Ting Chenf4e3f152023-07-06 15:58:42 +080086/* Find the next occurrence of the specific string. Strings are separated by delim. */
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +080087static size_t search_for(const char *data, size_t offset, size_t size,
Hsuan Ting Chenf4e3f152023-07-06 15:58:42 +080088 const char *str, char delim)
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +080089{
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +080090 while (offset < size) {
91 if (!strncmp(data + offset, str, size - offset))
92 return offset;
Hsuan Ting Chenf4e3f152023-07-06 15:58:42 +080093 offset = move_next(data, offset, size, delim);
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +080094 }
95 return size;
96}
97
Hsuan Ting Chenf4e3f152023-07-06 15:58:42 +080098/* Find the next occurrence of the string_name, which should always follow a DELIM_NAME. */
99static inline size_t search_for_name(const char *data, size_t offset, size_t size,
100 const char *name)
101{
102 return search_for(data, offset, size, name, DELIM_NAME);
103}
104
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +0800105/* Find the next occurrence of the integer ID, where ID is less than 100. */
106static size_t search_for_id(const char *data, size_t offset, size_t size,
107 int id)
108{
109 if (id >= LANG_ID_MAX)
110 return offset;
111 char int_to_str[LANG_ID_LEN] = {};
112 snprintf(int_to_str, LANG_ID_LEN, "%d", id);
Hsuan Ting Chenf4e3f152023-07-06 15:58:42 +0800113 return search_for(data, offset, size, int_to_str, DELIM_STR);
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +0800114}
115
116const char *ux_locales_get_text(const char *name)
117{
118 const char *data;
Hsuan Ting Chenf4e3f152023-07-06 15:58:42 +0800119 size_t size, offset, name_offset, next_name_offset, next;
Subrata Banik7f7ebb72023-11-14 13:09:52 +0530120 uint32_t lang_id = 0; /* default language English (0) */
Hsuan Ting Chenf4e3f152023-07-06 15:58:42 +0800121 unsigned char version;
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +0800122
123 data = locales_get_map(&size, false);
Hsuan Ting Chenf4e3f152023-07-06 15:58:42 +0800124 if (!data || size == 0) {
Hsuan Ting Chen8f4b0152023-07-06 14:15:31 +0800125 printk(BIOS_ERR, "%s: %s not found.\n", __func__,
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +0800126 PRERAM_LOCALES_NAME);
127 return NULL;
128 }
129
Subrata Banik7f7ebb72023-11-14 13:09:52 +0530130 if (CONFIG(VBOOT)) {
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",
136 __func__, lang_id);
137 lang_id = 0;
138 }
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +0800139 }
140
141 printk(BIOS_INFO, "%s: Search for %s with language ID: %u\n",
142 __func__, name, lang_id);
143
Hsuan Ting Chenf4e3f152023-07-06 15:58:42 +0800144 /* 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);
149 return NULL;
150 }
151
152 /* Search for name. Skip the version byte. */
153 offset = search_for_name(data, 1, size, name);
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +0800154 if (offset >= size) {
Hsuan Ting Chen8f4b0152023-07-06 14:15:31 +0800155 printk(BIOS_ERR, "%s: Name %s not found.\n", __func__, name);
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +0800156 return NULL;
157 }
158 name_offset = offset;
159
Hsuan Ting Chenf4e3f152023-07-06 15:58:42 +0800160 /* Search for language ID. We should not search beyond the range of the current
161 string_name. */
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
166 English (0). */
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. */
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +0800170 if (lang_id != 0)
Hsuan Ting Chenf4e3f152023-07-06 15:58:42 +0800171 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);
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +0800174 return NULL;
175 }
176 }
177
Hsuan Ting Chenf4e3f152023-07-06 15:58:42 +0800178 /* Move to the corresponding localized_string. */
179 offset = move_next(data, offset, next_name_offset, DELIM_STR);
180 if (offset >= next_name_offset)
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +0800181 return NULL;
182
183 /* Validity check that the returned string must be NULL terminated. */
Hsuan Ting Chenf4e3f152023-07-06 15:58:42 +0800184 next = move_next(data, offset, next_name_offset, DELIM_STR) - 1;
185 if (next >= next_name_offset || data[next] != '\0') {
Hsuan Ting Chen8f4b0152023-07-06 14:15:31 +0800186 printk(BIOS_ERR, "%s: %s is not NULL terminated.\n",
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +0800187 __func__, PRERAM_LOCALES_NAME);
188 return NULL;
189 }
190
191 return data + offset;
192}