blob: 2ac27731fe57fe440246cdd8da10c6c28ac8e32f [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
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +020050static efi_status_t efi_to_native(char **native, const u16 *variable_name,
Heinrich Schuchardt0bda81b2018-12-30 20:53:51 +010051 const efi_guid_t *vendor)
Rob Clarkad644e72017-09-13 18:05:37 -040052{
53 size_t len;
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +020054 char *pos;
Rob Clarkad644e72017-09-13 18:05:37 -040055
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +020056 len = PREFIX_LEN + utf16_utf8_strlen(variable_name) + 1;
57 *native = malloc(len);
58 if (!*native)
59 return EFI_OUT_OF_RESOURCES;
Rob Clarkad644e72017-09-13 18:05:37 -040060
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +020061 pos = *native;
62 pos += sprintf(pos, "efi_%pUl_", vendor);
63 utf16_utf8_strcpy(&pos, variable_name);
Rob Clarkad644e72017-09-13 18:05:37 -040064
65 return EFI_SUCCESS;
66}
67
68static const char *prefix(const char *str, const char *prefix)
69{
70 size_t n = strlen(prefix);
71 if (!strncmp(prefix, str, n))
72 return str + n;
73 return NULL;
74}
75
76/* parse attributes part of variable value, if present: */
77static const char *parse_attr(const char *str, u32 *attrp)
78{
79 u32 attr = 0;
80 char sep = '{';
81
82 if (*str != '{') {
83 *attrp = EFI_VARIABLE_BOOTSERVICE_ACCESS;
84 return str;
85 }
86
87 while (*str == sep) {
88 const char *s;
89
90 str++;
91
92 if ((s = prefix(str, "ro"))) {
93 attr |= READ_ONLY;
94 } else if ((s = prefix(str, "boot"))) {
95 attr |= EFI_VARIABLE_BOOTSERVICE_ACCESS;
96 } else if ((s = prefix(str, "run"))) {
97 attr |= EFI_VARIABLE_RUNTIME_ACCESS;
98 } else {
99 printf("invalid attribute: %s\n", str);
100 break;
101 }
102
103 str = s;
104 sep = ',';
105 }
106
107 str++;
108
109 *attrp = attr;
110
111 return str;
112}
113
114/* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetVariable.28.29 */
Heinrich Schuchardt0bda81b2018-12-30 20:53:51 +0100115efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
116 const efi_guid_t *vendor, u32 *attributes,
117 efi_uintn_t *data_size, void *data)
Rob Clarkad644e72017-09-13 18:05:37 -0400118{
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +0200119 char *native_name;
Rob Clarkad644e72017-09-13 18:05:37 -0400120 efi_status_t ret;
121 unsigned long in_size;
122 const char *val, *s;
123 u32 attr;
124
Rob Clark778e6af2017-09-13 18:05:41 -0400125 EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes,
Rob Clarkad644e72017-09-13 18:05:37 -0400126 data_size, data);
127
128 if (!variable_name || !vendor || !data_size)
129 return EFI_EXIT(EFI_INVALID_PARAMETER);
130
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +0200131 ret = efi_to_native(&native_name, variable_name, vendor);
Rob Clarkad644e72017-09-13 18:05:37 -0400132 if (ret)
133 return EFI_EXIT(ret);
134
135 debug("%s: get '%s'\n", __func__, native_name);
136
137 val = env_get(native_name);
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +0200138 free(native_name);
Rob Clarkad644e72017-09-13 18:05:37 -0400139 if (!val)
140 return EFI_EXIT(EFI_NOT_FOUND);
141
142 val = parse_attr(val, &attr);
143
144 in_size = *data_size;
145
146 if ((s = prefix(val, "(blob)"))) {
Heinrich Schuchardt6e37fa22019-01-18 12:31:54 +0100147 size_t len = strlen(s);
Rob Clarkad644e72017-09-13 18:05:37 -0400148
Ivan Gorinovd73c8bc2018-05-11 13:18:25 -0700149 /* number of hexadecimal digits must be even */
150 if (len & 1)
151 return EFI_EXIT(EFI_DEVICE_ERROR);
152
Rob Clarkad644e72017-09-13 18:05:37 -0400153 /* two characters per byte: */
Ivan Gorinovd73c8bc2018-05-11 13:18:25 -0700154 len /= 2;
Rob Clarkad644e72017-09-13 18:05:37 -0400155 *data_size = len;
156
157 if (in_size < len)
158 return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
159
160 if (!data)
161 return EFI_EXIT(EFI_INVALID_PARAMETER);
162
Heinrich Schuchardt6e37fa22019-01-18 12:31:54 +0100163 if (hex2bin(data, s, len))
Rob Clarkad644e72017-09-13 18:05:37 -0400164 return EFI_EXIT(EFI_DEVICE_ERROR);
165
166 debug("%s: got value: \"%s\"\n", __func__, s);
167 } else if ((s = prefix(val, "(utf8)"))) {
168 unsigned len = strlen(s) + 1;
169
170 *data_size = len;
171
172 if (in_size < len)
173 return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
174
175 if (!data)
176 return EFI_EXIT(EFI_INVALID_PARAMETER);
177
178 memcpy(data, s, len);
179 ((char *)data)[len] = '\0';
180
181 debug("%s: got value: \"%s\"\n", __func__, (char *)data);
182 } else {
183 debug("%s: invalid value: '%s'\n", __func__, val);
184 return EFI_EXIT(EFI_DEVICE_ERROR);
185 }
186
187 if (attributes)
188 *attributes = attr & EFI_VARIABLE_MASK;
189
190 return EFI_EXIT(EFI_SUCCESS);
191}
192
193/* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetNextVariableName.28.29 */
Heinrich Schuchardt45c66f92018-05-17 07:57:05 +0200194efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size,
195 u16 *variable_name,
Heinrich Schuchardt0bda81b2018-12-30 20:53:51 +0100196 const efi_guid_t *vendor)
Rob Clarkad644e72017-09-13 18:05:37 -0400197{
Rob Clark778e6af2017-09-13 18:05:41 -0400198 EFI_ENTRY("%p \"%ls\" %pUl", variable_name_size, variable_name, vendor);
Rob Clarkad644e72017-09-13 18:05:37 -0400199
200 return EFI_EXIT(EFI_DEVICE_ERROR);
201}
202
203/* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#SetVariable.28.29 */
Heinrich Schuchardt0bda81b2018-12-30 20:53:51 +0100204efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
205 const efi_guid_t *vendor, u32 attributes,
Heinrich Schuchardt452257a2018-12-30 21:03:15 +0100206 efi_uintn_t data_size, const void *data)
Rob Clarkad644e72017-09-13 18:05:37 -0400207{
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +0200208 char *native_name = NULL, *val = NULL, *s;
Rob Clarkad644e72017-09-13 18:05:37 -0400209 efi_status_t ret = EFI_SUCCESS;
Rob Clarkad644e72017-09-13 18:05:37 -0400210 u32 attr;
211
Heinrich Schuchardt45c66f92018-05-17 07:57:05 +0200212 EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes,
Rob Clarkad644e72017-09-13 18:05:37 -0400213 data_size, data);
214
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +0200215 if (!variable_name || !vendor) {
216 ret = EFI_INVALID_PARAMETER;
217 goto out;
218 }
Rob Clarkad644e72017-09-13 18:05:37 -0400219
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +0200220 ret = efi_to_native(&native_name, variable_name, vendor);
Rob Clarkad644e72017-09-13 18:05:37 -0400221 if (ret)
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +0200222 goto out;
Rob Clarkad644e72017-09-13 18:05:37 -0400223
224#define ACCESS_ATTR (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)
225
226 if ((data_size == 0) || !(attributes & ACCESS_ATTR)) {
227 /* delete the variable: */
228 env_set(native_name, NULL);
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +0200229 ret = EFI_SUCCESS;
230 goto out;
Rob Clarkad644e72017-09-13 18:05:37 -0400231 }
232
233 val = env_get(native_name);
234 if (val) {
235 parse_attr(val, &attr);
236
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +0200237 if (attr & READ_ONLY) {
238 /* We should not free val */
239 val = NULL;
240 ret = EFI_WRITE_PROTECTED;
241 goto out;
242 }
Rob Clarkad644e72017-09-13 18:05:37 -0400243 }
244
245 val = malloc(2 * data_size + strlen("{ro,run,boot}(blob)") + 1);
Heinrich Schuchardtdadc2bd2018-10-02 05:30:05 +0200246 if (!val) {
247 ret = EFI_OUT_OF_RESOURCES;
248 goto out;
249 }
Rob Clarkad644e72017-09-13 18:05:37 -0400250
251 s = val;
252
253 /* store attributes: */
254 attributes &= (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS);
255 s += sprintf(s, "{");
256 while (attributes) {
257 u32 attr = 1 << (ffs(attributes) - 1);
258
259 if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS)
260 s += sprintf(s, "boot");
261 else if (attr == EFI_VARIABLE_RUNTIME_ACCESS)
262 s += sprintf(s, "run");
263
264 attributes &= ~attr;
265 if (attributes)
266 s += sprintf(s, ",");
267 }
268 s += sprintf(s, "}");
269
270 /* store payload: */
271 s += sprintf(s, "(blob)");
Heinrich Schuchardt8377ee32019-01-18 18:54:26 +0100272 s = bin2hex(s, data, data_size);
Rob Clarkad644e72017-09-13 18:05:37 -0400273 *s = '\0';
274
275 debug("%s: setting: %s=%s\n", __func__, native_name, val);
276
277 if (env_set(native_name, val))
278 ret = EFI_DEVICE_ERROR;
279
Heinrich Schuchardtdcdb64f2018-08-31 21:31:31 +0200280out:
281 free(native_name);
Rob Clarkad644e72017-09-13 18:05:37 -0400282 free(val);
283
284 return EFI_EXIT(ret);
285}