blob: 86126195b4a4dc8606a8f65a05cb880597bc4f9f [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>
7#include <string.h>
8#include <ux_locales.h>
9#include <vb2_api.h>
10
11#define LANG_ID_MAX 100
12#define LANG_ID_LEN 3
13#define PRERAM_LOCALES_NAME "preram_locales"
14
15/*
16 * Devices which support early vga have the capability to show localized text in
17 * Code Page 437 encoding. (see src/drivers/pc80/vga/vga_font_8x16.c)
18 *
19 * preram_locales located in CBFS is an uncompressed file located in either RO
20 * or RW CBFS. It contains the localization information in the following format:
21 *
22 * [string_name_1] [\x00]
23 * [language_id_1] [\x00] [localized_string_1] [\x00]
24 * [language_id_2] [\x00] [localized_string_2] [\x00] ...
25 * [string_name_2] [\x00] ...
26 *
27 * This file contains tools to locate the file and search for localized strings
28 * with specific language ID.
29 */
30
31/* Cached state for map (locales_get_map) and unmap (ux_locales_unmap). */
32struct preram_locales_state {
33 void *data;
34 size_t size;
35 bool initialized;
36};
37
38static struct preram_locales_state cached_state;
39
40void ux_locales_unmap(void)
41{
42 if (cached_state.initialized) {
43 if (cached_state.data)
44 cbfs_unmap(cached_state.data);
45 cached_state.initialized = false;
46 cached_state.size = 0;
47 cached_state.data = NULL;
48 }
49}
50
51/* Get the map address of preram_locales. */
52static void *locales_get_map(size_t *size_out, bool unmap)
53{
54 if (cached_state.initialized) {
55 *size_out = cached_state.size;
56 return cached_state.data;
57 }
58 cached_state.initialized = true;
59 cached_state.data = cbfs_ro_map(PRERAM_LOCALES_NAME,
60 &cached_state.size);
61 *size_out = cached_state.size;
62 return cached_state.data;
63}
64
65/* Move to the next string in the data. Strings are separated by 0x00. */
66static size_t move_next(const char *data, size_t offset, size_t size)
67{
68 if (offset >= size)
69 return size;
70 while (offset < size && data[offset] != '\0')
71 offset++;
72 offset++;
73 return offset;
74}
75
76/* Find the next occurrence of the specific string. */
77static size_t search_for(const char *data, size_t offset, size_t size,
78 const char *str)
79{
80 if (offset >= size)
81 return size;
82 while (offset < size) {
83 if (!strncmp(data + offset, str, size - offset))
84 return offset;
85 offset = move_next(data, offset, size);
86 }
87 return size;
88}
89
90/* Find the next occurrence of the integer ID, where ID is less than 100. */
91static size_t search_for_id(const char *data, size_t offset, size_t size,
92 int id)
93{
94 if (id >= LANG_ID_MAX)
95 return offset;
96 char int_to_str[LANG_ID_LEN] = {};
97 snprintf(int_to_str, LANG_ID_LEN, "%d", id);
98 return search_for(data, offset, size, int_to_str);
99}
100
101const char *ux_locales_get_text(const char *name)
102{
103 const char *data;
104 size_t size, offset, name_offset, next;
105 uint32_t lang_id;
106
107 data = locales_get_map(&size, false);
108 if (!data) {
Hsuan Ting Chen8f4b0152023-07-06 14:15:31 +0800109 printk(BIOS_ERR, "%s: %s not found.\n", __func__,
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +0800110 PRERAM_LOCALES_NAME);
111 return NULL;
112 }
113
114 /* Get the language ID from vboot API. */
115 lang_id = vb2api_get_locale_id(vboot_get_context());
116 /* Validity check: Language ID should smaller than LANG_ID_MAX. */
117 if (lang_id >= LANG_ID_MAX) {
Hsuan Ting Chen8f4b0152023-07-06 14:15:31 +0800118 printk(BIOS_WARNING, "%s: ID %d too big; fallback to 0.\n",
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +0800119 __func__, lang_id);
120 lang_id = 0;
121 }
122
123 printk(BIOS_INFO, "%s: Search for %s with language ID: %u\n",
124 __func__, name, lang_id);
125
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +0800126 /* Search for name. */
Hsuan Ting Chen8f4b0152023-07-06 14:15:31 +0800127 offset = search_for(data, 0, size, name);
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +0800128 if (offset >= size) {
Hsuan Ting Chen8f4b0152023-07-06 14:15:31 +0800129 printk(BIOS_ERR, "%s: Name %s not found.\n", __func__, name);
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +0800130 return NULL;
131 }
132 name_offset = offset;
133
134 /* Search for language ID. */
135 offset = search_for_id(data, name_offset, size, lang_id);
136 /* Language ID not supported; fallback to English if the current
137 * language is not English (0). */
138 if (offset >= size) {
139 /*
140 * Since we only support a limited charset, it is very normal
141 * that a language is not supported and we fallback here
142 * silently.
143 */
144 if (lang_id != 0)
145 offset = search_for_id(data, name_offset, size, 0);
146 if (offset >= size) {
Hsuan Ting Chen8f4b0152023-07-06 14:15:31 +0800147 printk(BIOS_ERR, "%s: Neither %d nor 0 found.\n",
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +0800148 __func__, lang_id);
149 return NULL;
150 }
151 }
152
Hsuan Ting Chen8f4b0152023-07-06 14:15:31 +0800153 offset = move_next(data, offset, size);
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +0800154 if (offset >= size)
155 return NULL;
156
157 /* Validity check that the returned string must be NULL terminated. */
158 next = move_next(data, offset, size) - 1;
159 if (next >= size || data[next] != '\0') {
Hsuan Ting Chen8f4b0152023-07-06 14:15:31 +0800160 printk(BIOS_ERR, "%s: %s is not NULL terminated.\n",
Hsuan Ting Chen3c2cdb62023-05-02 17:55:50 +0800161 __func__, PRERAM_LOCALES_NAME);
162 return NULL;
163 }
164
165 return data + offset;
166}