]> git.sur5r.net Git - u-boot/blob - lib/efi_loader/efi_variable.c
Merge tag 'signed-rpi-next' of git://github.com/agraf/u-boot
[u-boot] / lib / efi_loader / efi_variable.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  *  EFI utils
4  *
5  *  Copyright (c) 2017 Rob Clark
6  */
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
52 static 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
63 static 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
103 static 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
116 static 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
132 static 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: */
141 static 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 */
179 efi_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
189         EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes,
190                   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 */
253 efi_status_t EFIAPI efi_get_next_variable(
254                 unsigned long *variable_name_size,
255                 s16 *variable_name, efi_guid_t *vendor)
256 {
257         EFI_ENTRY("%p \"%ls\" %pUl", variable_name_size, variable_name, vendor);
258
259         return EFI_EXIT(EFI_DEVICE_ERROR);
260 }
261
262 /* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#SetVariable.28.29 */
263 efi_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
272         EFI_ENTRY("\"%ls\" %pUl %x %lu %p", variable_name, vendor, attributes,
273                   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 }