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