blob: 7e0e7f020ee12d428669d4027341703640a64c3a [file] [log] [blame]
Tom Rini70df9d62018-05-07 17:02:21 -04001// SPDX-License-Identifier: GPL-2.0+
Rob Clark15f3d742017-09-13 18:05:37 -04002/*
3 * EFI utils
4 *
5 * Copyright (c) 2017 Rob Clark
Rob Clark15f3d742017-09-13 18:05:37 -04006 */
7
8#include <malloc.h>
9#include <charset.h>
10#include <efi_loader.h>
11
12#define READ_ONLY BIT(31)
13
14/*
15 * Mapping between EFI variables and u-boot variables:
16 *
17 * efi_$guid_$varname = {attributes}(type)value
18 *
19 * For example:
20 *
21 * efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_OsIndicationsSupported=
22 * "{ro,boot,run}(blob)0000000000000000"
23 * efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_BootOrder=
24 * "(blob)00010000"
25 *
26 * The attributes are a comma separated list of these possible
27 * attributes:
28 *
29 * + ro - read-only
30 * + boot - boot-services access
31 * + run - runtime access
32 *
33 * NOTE: with current implementation, no variables are available after
34 * ExitBootServices, and all are persisted (if possible).
35 *
36 * If not specified, the attributes default to "{boot}".
37 *
38 * The required type is one of:
39 *
40 * + utf8 - raw utf8 string
41 * + blob - arbitrary length hex string
42 *
43 * Maybe a utf16 type would be useful to for a string value to be auto
44 * converted to utf16?
45 */
46
47#define MAX_VAR_NAME 31
48#define MAX_NATIVE_VAR_NAME \
49 (strlen("efi_xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx_") + \
50 (MAX_VAR_NAME * MAX_UTF8_PER_UTF16))
51
52static int hex(unsigned char ch)
53{
54 if (ch >= 'a' && ch <= 'f')
55 return ch-'a'+10;
56 if (ch >= '0' && ch <= '9')
57 return ch-'0';
58 if (ch >= 'A' && ch <= 'F')
59 return ch-'A'+10;
60 return -1;
61}
62
63static const char *hex2mem(u8 *mem, const char *hexstr, int count)
64{
65 memset(mem, 0, count/2);
66
67 do {
68 int nibble;
69
70 *mem = 0;
71
72 if (!count || !*hexstr)
73 break;
74
75 nibble = hex(*hexstr);
76 if (nibble < 0)
77 break;
78
79 *mem = nibble;
80 count--;
81 hexstr++;
82
83 if (!count || !*hexstr)
84 break;
85
86 nibble = hex(*hexstr);
87 if (nibble < 0)
88 break;
89
90 *mem = (*mem << 4) | nibble;
91 count--;
92 hexstr++;
93 mem++;
94
95 } while (1);
96
97 if (*hexstr)
98 return hexstr;
99
100 return NULL;
101}
102
103static char *mem2hex(char *hexstr, const u8 *mem, int count)
104{
105 static const char hexchars[] = "0123456789abcdef";
106
107 while (count-- > 0) {
108 u8 ch = *mem++;
109 *hexstr++ = hexchars[ch >> 4];
110 *hexstr++ = hexchars[ch & 0xf];
111 }
112
113 return hexstr;
114}
115
116static efi_status_t efi_to_native(char *native, s16 *variable_name,
117 efi_guid_t *vendor)
118{
119 size_t len;
120
121 len = utf16_strlen((u16 *)variable_name);
122 if (len >= MAX_VAR_NAME)
123 return EFI_DEVICE_ERROR;
124
125 native += sprintf(native, "efi_%pUl_", vendor);
126 native = (char *)utf16_to_utf8((u8 *)native, (u16 *)variable_name, len);
127 *native = '\0';
128
129 return EFI_SUCCESS;
130}
131
132static const char *prefix(const char *str, const char *prefix)
133{
134 size_t n = strlen(prefix);
135 if (!strncmp(prefix, str, n))
136 return str + n;
137 return NULL;
138}
139
140/* parse attributes part of variable value, if present: */
141static const char *parse_attr(const char *str, u32 *attrp)
142{
143 u32 attr = 0;
144 char sep = '{';
145
146 if (*str != '{') {
147 *attrp = EFI_VARIABLE_BOOTSERVICE_ACCESS;
148 return str;
149 }
150
151 while (*str == sep) {
152 const char *s;
153
154 str++;
155
156 if ((s = prefix(str, "ro"))) {
157 attr |= READ_ONLY;
158 } else if ((s = prefix(str, "boot"))) {
159 attr |= EFI_VARIABLE_BOOTSERVICE_ACCESS;
160 } else if ((s = prefix(str, "run"))) {
161 attr |= EFI_VARIABLE_RUNTIME_ACCESS;
162 } else {
163 printf("invalid attribute: %s\n", str);
164 break;
165 }
166
167 str = s;
168 sep = ',';
169 }
170
171 str++;
172
173 *attrp = attr;
174
175 return str;
176}
177
178/* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetVariable.28.29 */
179efi_status_t EFIAPI efi_get_variable(s16 *variable_name,
180 efi_guid_t *vendor, u32 *attributes,
181 unsigned long *data_size, void *data)
182{
183 char native_name[MAX_NATIVE_VAR_NAME + 1];
184 efi_status_t ret;
185 unsigned long in_size;
186 const char *val, *s;
187 u32 attr;
188
Rob Clark238f88c2017-09-13 18:05:41 -0400189 EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes,
Rob Clark15f3d742017-09-13 18:05:37 -0400190 data_size, data);
191
192 if (!variable_name || !vendor || !data_size)
193 return EFI_EXIT(EFI_INVALID_PARAMETER);
194
195 ret = efi_to_native(native_name, variable_name, vendor);
196 if (ret)
197 return EFI_EXIT(ret);
198
199 debug("%s: get '%s'\n", __func__, native_name);
200
201 val = env_get(native_name);
202 if (!val)
203 return EFI_EXIT(EFI_NOT_FOUND);
204
205 val = parse_attr(val, &attr);
206
207 in_size = *data_size;
208
209 if ((s = prefix(val, "(blob)"))) {
210 unsigned len = strlen(s);
211
212 /* two characters per byte: */
213 len = DIV_ROUND_UP(len, 2);
214 *data_size = len;
215
216 if (in_size < len)
217 return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
218
219 if (!data)
220 return EFI_EXIT(EFI_INVALID_PARAMETER);
221
222 if (hex2mem(data, s, len * 2))
223 return EFI_EXIT(EFI_DEVICE_ERROR);
224
225 debug("%s: got value: \"%s\"\n", __func__, s);
226 } else if ((s = prefix(val, "(utf8)"))) {
227 unsigned len = strlen(s) + 1;
228
229 *data_size = len;
230
231 if (in_size < len)
232 return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
233
234 if (!data)
235 return EFI_EXIT(EFI_INVALID_PARAMETER);
236
237 memcpy(data, s, len);
238 ((char *)data)[len] = '\0';
239
240 debug("%s: got value: \"%s\"\n", __func__, (char *)data);
241 } else {
242 debug("%s: invalid value: '%s'\n", __func__, val);
243 return EFI_EXIT(EFI_DEVICE_ERROR);
244 }
245
246 if (attributes)
247 *attributes = attr & EFI_VARIABLE_MASK;
248
249 return EFI_EXIT(EFI_SUCCESS);
250}
251
252/* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetNextVariableName.28.29 */
253efi_status_t EFIAPI efi_get_next_variable(
254 unsigned long *variable_name_size,
255 s16 *variable_name, efi_guid_t *vendor)
256{
Rob Clark238f88c2017-09-13 18:05:41 -0400257 EFI_ENTRY("%p \"%ls\" %pUl", variable_name_size, variable_name, vendor);
Rob Clark15f3d742017-09-13 18:05:37 -0400258
259 return EFI_EXIT(EFI_DEVICE_ERROR);
260}
261
262/* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#SetVariable.28.29 */
263efi_status_t EFIAPI efi_set_variable(s16 *variable_name,
264 efi_guid_t *vendor, u32 attributes,
265 unsigned long data_size, void *data)
266{
267 char native_name[MAX_NATIVE_VAR_NAME + 1];
268 efi_status_t ret = EFI_SUCCESS;
269 char *val, *s;
270 u32 attr;
271
Rob Clark238f88c2017-09-13 18:05:41 -0400272 EFI_ENTRY("\"%ls\" %pUl %x %lu %p", variable_name, vendor, attributes,
Rob Clark15f3d742017-09-13 18:05:37 -0400273 data_size, data);
274
275 if (!variable_name || !vendor)
276 return EFI_EXIT(EFI_INVALID_PARAMETER);
277
278 ret = efi_to_native(native_name, variable_name, vendor);
279 if (ret)
280 return EFI_EXIT(ret);
281
282#define ACCESS_ATTR (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)
283
284 if ((data_size == 0) || !(attributes & ACCESS_ATTR)) {
285 /* delete the variable: */
286 env_set(native_name, NULL);
287 return EFI_EXIT(EFI_SUCCESS);
288 }
289
290 val = env_get(native_name);
291 if (val) {
292 parse_attr(val, &attr);
293
294 if (attr & READ_ONLY)
295 return EFI_EXIT(EFI_WRITE_PROTECTED);
296 }
297
298 val = malloc(2 * data_size + strlen("{ro,run,boot}(blob)") + 1);
299 if (!val)
300 return EFI_EXIT(EFI_OUT_OF_RESOURCES);
301
302 s = val;
303
304 /* store attributes: */
305 attributes &= (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS);
306 s += sprintf(s, "{");
307 while (attributes) {
308 u32 attr = 1 << (ffs(attributes) - 1);
309
310 if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS)
311 s += sprintf(s, "boot");
312 else if (attr == EFI_VARIABLE_RUNTIME_ACCESS)
313 s += sprintf(s, "run");
314
315 attributes &= ~attr;
316 if (attributes)
317 s += sprintf(s, ",");
318 }
319 s += sprintf(s, "}");
320
321 /* store payload: */
322 s += sprintf(s, "(blob)");
323 s = mem2hex(s, data, data_size);
324 *s = '\0';
325
326 debug("%s: setting: %s=%s\n", __func__, native_name, val);
327
328 if (env_set(native_name, val))
329 ret = EFI_DEVICE_ERROR;
330
331 free(val);
332
333 return EFI_EXIT(ret);
334}