blob: eea7f68b857a3cf982f1b9f42c9fcaaf7807bac1 [file] [log] [blame]
Tom Rinif739fcd2018-05-07 17:02:21 -04001// SPDX-License-Identifier: GPL-2.0+
Rob Clarkad644e72017-09-13 18:05:37 -04002/*
3 * EFI utils
4 *
5 * Copyright (c) 2017 Rob Clark
Rob Clarkad644e72017-09-13 18:05:37 -04006 */
7
8#include <malloc.h>
9#include <charset.h>
10#include <efi_loader.h>
Heinrich Schuchardt6e37fa22019-01-18 12:31:54 +010011#include <hexdump.h>
Rob Clarkad644e72017-09-13 18:05:37 -040012
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
Heinrich Schuchardt506dc522018-09-23 04:08:09 +020048#define PREFIX_LEN (strlen("efi_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx_"))
Rob Clarkad644e72017-09-13 18:05:37 -040049
Rob Clarkad644e72017-09-13 18:05:37 -040050static char *mem2hex(char *hexstr, const u8 *mem, int count)
51{
52 static const char hexchars[] = "0123456789abcdef";
53
54 while (count-- > 0) {
55 u8 ch = *mem++;
56 *hexstr++ = hexchars[ch >> 4];
57 *hexstr++ = hexchars[ch & 0xf];
58 }
59
60 return hexstr;
61}
62
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +020063static efi_status_t efi_to_native(char **native, const u16 *variable_name,
Heinrich Schuchardt0bda81b2018-12-30 20:53:51 +010064 const efi_guid_t *vendor)
Rob Clarkad644e72017-09-13 18:05:37 -040065{
66 size_t len;
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +020067 char *pos;
Rob Clarkad644e72017-09-13 18:05:37 -040068
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +020069 len = PREFIX_LEN + utf16_utf8_strlen(variable_name) + 1;
70 *native = malloc(len);
71 if (!*native)
72 return EFI_OUT_OF_RESOURCES;
Rob Clarkad644e72017-09-13 18:05:37 -040073
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +020074 pos = *native;
75 pos += sprintf(pos, "efi_%pUl_", vendor);
76 utf16_utf8_strcpy(&pos, variable_name);
Rob Clarkad644e72017-09-13 18:05:37 -040077
78 return EFI_SUCCESS;
79}
80
81static const char *prefix(const char *str, const char *prefix)
82{
83 size_t n = strlen(prefix);
84 if (!strncmp(prefix, str, n))
85 return str + n;
86 return NULL;
87}
88
89/* parse attributes part of variable value, if present: */
90static const char *parse_attr(const char *str, u32 *attrp)
91{
92 u32 attr = 0;
93 char sep = '{';
94
95 if (*str != '{') {
96 *attrp = EFI_VARIABLE_BOOTSERVICE_ACCESS;
97 return str;
98 }
99
100 while (*str == sep) {
101 const char *s;
102
103 str++;
104
105 if ((s = prefix(str, "ro"))) {
106 attr |= READ_ONLY;
107 } else if ((s = prefix(str, "boot"))) {
108 attr |= EFI_VARIABLE_BOOTSERVICE_ACCESS;
109 } else if ((s = prefix(str, "run"))) {
110 attr |= EFI_VARIABLE_RUNTIME_ACCESS;
111 } else {
112 printf("invalid attribute: %s\n", str);
113 break;
114 }
115
116 str = s;
117 sep = ',';
118 }
119
120 str++;
121
122 *attrp = attr;
123
124 return str;
125}
126
127/* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetVariable.28.29 */
Heinrich Schuchardt0bda81b2018-12-30 20:53:51 +0100128efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
129 const efi_guid_t *vendor, u32 *attributes,
130 efi_uintn_t *data_size, void *data)
Rob Clarkad644e72017-09-13 18:05:37 -0400131{
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +0200132 char *native_name;
Rob Clarkad644e72017-09-13 18:05:37 -0400133 efi_status_t ret;
134 unsigned long in_size;
135 const char *val, *s;
136 u32 attr;
137
Rob Clark778e6af2017-09-13 18:05:41 -0400138 EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes,
Rob Clarkad644e72017-09-13 18:05:37 -0400139 data_size, data);
140
141 if (!variable_name || !vendor || !data_size)
142 return EFI_EXIT(EFI_INVALID_PARAMETER);
143
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +0200144 ret = efi_to_native(&native_name, variable_name, vendor);
Rob Clarkad644e72017-09-13 18:05:37 -0400145 if (ret)
146 return EFI_EXIT(ret);
147
148 debug("%s: get '%s'\n", __func__, native_name);
149
150 val = env_get(native_name);
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +0200151 free(native_name);
Rob Clarkad644e72017-09-13 18:05:37 -0400152 if (!val)
153 return EFI_EXIT(EFI_NOT_FOUND);
154
155 val = parse_attr(val, &attr);
156
157 in_size = *data_size;
158
159 if ((s = prefix(val, "(blob)"))) {
Heinrich Schuchardt6e37fa22019-01-18 12:31:54 +0100160 size_t len = strlen(s);
Rob Clarkad644e72017-09-13 18:05:37 -0400161
Ivan Gorinovd73c8bc2018-05-11 13:18:25 -0700162 /* number of hexadecimal digits must be even */
163 if (len & 1)
164 return EFI_EXIT(EFI_DEVICE_ERROR);
165
Rob Clarkad644e72017-09-13 18:05:37 -0400166 /* two characters per byte: */
Ivan Gorinovd73c8bc2018-05-11 13:18:25 -0700167 len /= 2;
Rob Clarkad644e72017-09-13 18:05:37 -0400168 *data_size = len;
169
170 if (in_size < len)
171 return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
172
173 if (!data)
174 return EFI_EXIT(EFI_INVALID_PARAMETER);
175
Heinrich Schuchardt6e37fa22019-01-18 12:31:54 +0100176 if (hex2bin(data, s, len))
Rob Clarkad644e72017-09-13 18:05:37 -0400177 return EFI_EXIT(EFI_DEVICE_ERROR);
178
179 debug("%s: got value: \"%s\"\n", __func__, s);
180 } else if ((s = prefix(val, "(utf8)"))) {
181 unsigned len = strlen(s) + 1;
182
183 *data_size = len;
184
185 if (in_size < len)
186 return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
187
188 if (!data)
189 return EFI_EXIT(EFI_INVALID_PARAMETER);
190
191 memcpy(data, s, len);
192 ((char *)data)[len] = '\0';
193
194 debug("%s: got value: \"%s\"\n", __func__, (char *)data);
195 } else {
196 debug("%s: invalid value: '%s'\n", __func__, val);
197 return EFI_EXIT(EFI_DEVICE_ERROR);
198 }
199
200 if (attributes)
201 *attributes = attr & EFI_VARIABLE_MASK;
202
203 return EFI_EXIT(EFI_SUCCESS);
204}
205
206/* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetNextVariableName.28.29 */
Heinrich Schuchardt45c66f92018-05-17 07:57:05 +0200207efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size,
208 u16 *variable_name,
Heinrich Schuchardt0bda81b2018-12-30 20:53:51 +0100209 const efi_guid_t *vendor)
Rob Clarkad644e72017-09-13 18:05:37 -0400210{
Rob Clark778e6af2017-09-13 18:05:41 -0400211 EFI_ENTRY("%p \"%ls\" %pUl", variable_name_size, variable_name, vendor);
Rob Clarkad644e72017-09-13 18:05:37 -0400212
213 return EFI_EXIT(EFI_DEVICE_ERROR);
214}
215
216/* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#SetVariable.28.29 */
Heinrich Schuchardt0bda81b2018-12-30 20:53:51 +0100217efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
218 const efi_guid_t *vendor, u32 attributes,
Heinrich Schuchardt452257a2018-12-30 21:03:15 +0100219 efi_uintn_t data_size, const void *data)
Rob Clarkad644e72017-09-13 18:05:37 -0400220{
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +0200221 char *native_name = NULL, *val = NULL, *s;
Rob Clarkad644e72017-09-13 18:05:37 -0400222 efi_status_t ret = EFI_SUCCESS;
Rob Clarkad644e72017-09-13 18:05:37 -0400223 u32 attr;
224
Heinrich Schuchardt45c66f92018-05-17 07:57:05 +0200225 EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes,
Rob Clarkad644e72017-09-13 18:05:37 -0400226 data_size, data);
227
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +0200228 if (!variable_name || !vendor) {
229 ret = EFI_INVALID_PARAMETER;
230 goto out;
231 }
Rob Clarkad644e72017-09-13 18:05:37 -0400232
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +0200233 ret = efi_to_native(&native_name, variable_name, vendor);
Rob Clarkad644e72017-09-13 18:05:37 -0400234 if (ret)
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +0200235 goto out;
Rob Clarkad644e72017-09-13 18:05:37 -0400236
237#define ACCESS_ATTR (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)
238
239 if ((data_size == 0) || !(attributes & ACCESS_ATTR)) {
240 /* delete the variable: */
241 env_set(native_name, NULL);
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +0200242 ret = EFI_SUCCESS;
243 goto out;
Rob Clarkad644e72017-09-13 18:05:37 -0400244 }
245
246 val = env_get(native_name);
247 if (val) {
248 parse_attr(val, &attr);
249
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +0200250 if (attr & READ_ONLY) {
251 /* We should not free val */
252 val = NULL;
253 ret = EFI_WRITE_PROTECTED;
254 goto out;
255 }
Rob Clarkad644e72017-09-13 18:05:37 -0400256 }
257
258 val = malloc(2 * data_size + strlen("{ro,run,boot}(blob)") + 1);
Heinrich Schuchardtdadc2bd2018-10-02 05:30:05 +0200259 if (!val) {
260 ret = EFI_OUT_OF_RESOURCES;
261 goto out;
262 }
Rob Clarkad644e72017-09-13 18:05:37 -0400263
264 s = val;
265
266 /* store attributes: */
267 attributes &= (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS);
268 s += sprintf(s, "{");
269 while (attributes) {
270 u32 attr = 1 << (ffs(attributes) - 1);
271
272 if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS)
273 s += sprintf(s, "boot");
274 else if (attr == EFI_VARIABLE_RUNTIME_ACCESS)
275 s += sprintf(s, "run");
276
277 attributes &= ~attr;
278 if (attributes)
279 s += sprintf(s, ",");
280 }
281 s += sprintf(s, "}");
282
283 /* store payload: */
284 s += sprintf(s, "(blob)");
285 s = mem2hex(s, data, data_size);
286 *s = '\0';
287
288 debug("%s: setting: %s=%s\n", __func__, native_name, val);
289
290 if (env_set(native_name, val))
291 ret = EFI_DEVICE_ERROR;
292
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +0200293out:
294 free(native_name);
Rob Clarkad644e72017-09-13 18:05:37 -0400295 free(val);
296
297 return EFI_EXIT(ret);
298}