]>
Commit | Line | Data |
---|---|---|
f739fcd8 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
ad644e7c RC |
2 | /* |
3 | * EFI utils | |
4 | * | |
5 | * Copyright (c) 2017 Rob Clark | |
ad644e7c RC |
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 | ||
506dc52d | 47 | #define PREFIX_LEN (strlen("efi_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx_")) |
ad644e7c | 48 | |
d73c8bc0 | 49 | static int hex(int ch) |
ad644e7c RC |
50 | { |
51 | if (ch >= 'a' && ch <= 'f') | |
52 | return ch-'a'+10; | |
53 | if (ch >= '0' && ch <= '9') | |
54 | return ch-'0'; | |
55 | if (ch >= 'A' && ch <= 'F') | |
56 | return ch-'A'+10; | |
57 | return -1; | |
58 | } | |
59 | ||
d73c8bc0 | 60 | static int hex2mem(u8 *mem, const char *hexstr, int size) |
ad644e7c | 61 | { |
d73c8bc0 IG |
62 | int nibble; |
63 | int i; | |
ad644e7c | 64 | |
d73c8bc0 IG |
65 | for (i = 0; i < size; i++) { |
66 | if (*hexstr == '\0') | |
ad644e7c RC |
67 | break; |
68 | ||
69 | nibble = hex(*hexstr); | |
70 | if (nibble < 0) | |
d73c8bc0 | 71 | return -1; |
ad644e7c RC |
72 | |
73 | *mem = nibble; | |
ad644e7c RC |
74 | hexstr++; |
75 | ||
ad644e7c RC |
76 | nibble = hex(*hexstr); |
77 | if (nibble < 0) | |
d73c8bc0 | 78 | return -1; |
ad644e7c RC |
79 | |
80 | *mem = (*mem << 4) | nibble; | |
ad644e7c RC |
81 | hexstr++; |
82 | mem++; | |
d73c8bc0 | 83 | } |
ad644e7c | 84 | |
d73c8bc0 | 85 | return i; |
ad644e7c RC |
86 | } |
87 | ||
88 | static char *mem2hex(char *hexstr, const u8 *mem, int count) | |
89 | { | |
90 | static const char hexchars[] = "0123456789abcdef"; | |
91 | ||
92 | while (count-- > 0) { | |
93 | u8 ch = *mem++; | |
94 | *hexstr++ = hexchars[ch >> 4]; | |
95 | *hexstr++ = hexchars[ch & 0xf]; | |
96 | } | |
97 | ||
98 | return hexstr; | |
99 | } | |
100 | ||
dcdb64f7 | 101 | static efi_status_t efi_to_native(char **native, const u16 *variable_name, |
45c66f9c | 102 | efi_guid_t *vendor) |
ad644e7c RC |
103 | { |
104 | size_t len; | |
dcdb64f7 | 105 | char *pos; |
ad644e7c | 106 | |
dcdb64f7 HS |
107 | len = PREFIX_LEN + utf16_utf8_strlen(variable_name) + 1; |
108 | *native = malloc(len); | |
109 | if (!*native) | |
110 | return EFI_OUT_OF_RESOURCES; | |
ad644e7c | 111 | |
dcdb64f7 HS |
112 | pos = *native; |
113 | pos += sprintf(pos, "efi_%pUl_", vendor); | |
114 | utf16_utf8_strcpy(&pos, variable_name); | |
ad644e7c RC |
115 | |
116 | return EFI_SUCCESS; | |
117 | } | |
118 | ||
119 | static const char *prefix(const char *str, const char *prefix) | |
120 | { | |
121 | size_t n = strlen(prefix); | |
122 | if (!strncmp(prefix, str, n)) | |
123 | return str + n; | |
124 | return NULL; | |
125 | } | |
126 | ||
127 | /* parse attributes part of variable value, if present: */ | |
128 | static const char *parse_attr(const char *str, u32 *attrp) | |
129 | { | |
130 | u32 attr = 0; | |
131 | char sep = '{'; | |
132 | ||
133 | if (*str != '{') { | |
134 | *attrp = EFI_VARIABLE_BOOTSERVICE_ACCESS; | |
135 | return str; | |
136 | } | |
137 | ||
138 | while (*str == sep) { | |
139 | const char *s; | |
140 | ||
141 | str++; | |
142 | ||
143 | if ((s = prefix(str, "ro"))) { | |
144 | attr |= READ_ONLY; | |
145 | } else if ((s = prefix(str, "boot"))) { | |
146 | attr |= EFI_VARIABLE_BOOTSERVICE_ACCESS; | |
147 | } else if ((s = prefix(str, "run"))) { | |
148 | attr |= EFI_VARIABLE_RUNTIME_ACCESS; | |
149 | } else { | |
150 | printf("invalid attribute: %s\n", str); | |
151 | break; | |
152 | } | |
153 | ||
154 | str = s; | |
155 | sep = ','; | |
156 | } | |
157 | ||
158 | str++; | |
159 | ||
160 | *attrp = attr; | |
161 | ||
162 | return str; | |
163 | } | |
164 | ||
165 | /* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetVariable.28.29 */ | |
45c66f9c HS |
166 | efi_status_t EFIAPI efi_get_variable(u16 *variable_name, efi_guid_t *vendor, |
167 | u32 *attributes, efi_uintn_t *data_size, | |
168 | void *data) | |
ad644e7c | 169 | { |
dcdb64f7 | 170 | char *native_name; |
ad644e7c RC |
171 | efi_status_t ret; |
172 | unsigned long in_size; | |
173 | const char *val, *s; | |
174 | u32 attr; | |
175 | ||
778e6af8 | 176 | EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes, |
ad644e7c RC |
177 | data_size, data); |
178 | ||
179 | if (!variable_name || !vendor || !data_size) | |
180 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
181 | ||
dcdb64f7 | 182 | ret = efi_to_native(&native_name, variable_name, vendor); |
ad644e7c RC |
183 | if (ret) |
184 | return EFI_EXIT(ret); | |
185 | ||
186 | debug("%s: get '%s'\n", __func__, native_name); | |
187 | ||
188 | val = env_get(native_name); | |
dcdb64f7 | 189 | free(native_name); |
ad644e7c RC |
190 | if (!val) |
191 | return EFI_EXIT(EFI_NOT_FOUND); | |
192 | ||
193 | val = parse_attr(val, &attr); | |
194 | ||
195 | in_size = *data_size; | |
196 | ||
197 | if ((s = prefix(val, "(blob)"))) { | |
198 | unsigned len = strlen(s); | |
199 | ||
d73c8bc0 IG |
200 | /* number of hexadecimal digits must be even */ |
201 | if (len & 1) | |
202 | return EFI_EXIT(EFI_DEVICE_ERROR); | |
203 | ||
ad644e7c | 204 | /* two characters per byte: */ |
d73c8bc0 | 205 | len /= 2; |
ad644e7c RC |
206 | *data_size = len; |
207 | ||
208 | if (in_size < len) | |
209 | return EFI_EXIT(EFI_BUFFER_TOO_SMALL); | |
210 | ||
211 | if (!data) | |
212 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
213 | ||
d73c8bc0 | 214 | if (hex2mem(data, s, len) != len) |
ad644e7c RC |
215 | return EFI_EXIT(EFI_DEVICE_ERROR); |
216 | ||
217 | debug("%s: got value: \"%s\"\n", __func__, s); | |
218 | } else if ((s = prefix(val, "(utf8)"))) { | |
219 | unsigned len = strlen(s) + 1; | |
220 | ||
221 | *data_size = len; | |
222 | ||
223 | if (in_size < len) | |
224 | return EFI_EXIT(EFI_BUFFER_TOO_SMALL); | |
225 | ||
226 | if (!data) | |
227 | return EFI_EXIT(EFI_INVALID_PARAMETER); | |
228 | ||
229 | memcpy(data, s, len); | |
230 | ((char *)data)[len] = '\0'; | |
231 | ||
232 | debug("%s: got value: \"%s\"\n", __func__, (char *)data); | |
233 | } else { | |
234 | debug("%s: invalid value: '%s'\n", __func__, val); | |
235 | return EFI_EXIT(EFI_DEVICE_ERROR); | |
236 | } | |
237 | ||
238 | if (attributes) | |
239 | *attributes = attr & EFI_VARIABLE_MASK; | |
240 | ||
241 | return EFI_EXIT(EFI_SUCCESS); | |
242 | } | |
243 | ||
244 | /* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetNextVariableName.28.29 */ | |
45c66f9c HS |
245 | efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size, |
246 | u16 *variable_name, | |
247 | efi_guid_t *vendor) | |
ad644e7c | 248 | { |
778e6af8 | 249 | EFI_ENTRY("%p \"%ls\" %pUl", variable_name_size, variable_name, vendor); |
ad644e7c RC |
250 | |
251 | return EFI_EXIT(EFI_DEVICE_ERROR); | |
252 | } | |
253 | ||
254 | /* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#SetVariable.28.29 */ | |
45c66f9c HS |
255 | efi_status_t EFIAPI efi_set_variable(u16 *variable_name, efi_guid_t *vendor, |
256 | u32 attributes, efi_uintn_t data_size, | |
257 | void *data) | |
ad644e7c | 258 | { |
dcdb64f7 | 259 | char *native_name = NULL, *val = NULL, *s; |
ad644e7c | 260 | efi_status_t ret = EFI_SUCCESS; |
ad644e7c RC |
261 | u32 attr; |
262 | ||
45c66f9c | 263 | EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes, |
ad644e7c RC |
264 | data_size, data); |
265 | ||
dcdb64f7 HS |
266 | if (!variable_name || !vendor) { |
267 | ret = EFI_INVALID_PARAMETER; | |
268 | goto out; | |
269 | } | |
ad644e7c | 270 | |
dcdb64f7 | 271 | ret = efi_to_native(&native_name, variable_name, vendor); |
ad644e7c | 272 | if (ret) |
dcdb64f7 | 273 | goto out; |
ad644e7c RC |
274 | |
275 | #define ACCESS_ATTR (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS) | |
276 | ||
277 | if ((data_size == 0) || !(attributes & ACCESS_ATTR)) { | |
278 | /* delete the variable: */ | |
279 | env_set(native_name, NULL); | |
dcdb64f7 HS |
280 | ret = EFI_SUCCESS; |
281 | goto out; | |
ad644e7c RC |
282 | } |
283 | ||
284 | val = env_get(native_name); | |
285 | if (val) { | |
286 | parse_attr(val, &attr); | |
287 | ||
dcdb64f7 HS |
288 | if (attr & READ_ONLY) { |
289 | /* We should not free val */ | |
290 | val = NULL; | |
291 | ret = EFI_WRITE_PROTECTED; | |
292 | goto out; | |
293 | } | |
ad644e7c RC |
294 | } |
295 | ||
296 | val = malloc(2 * data_size + strlen("{ro,run,boot}(blob)") + 1); | |
297 | if (!val) | |
298 | return EFI_EXIT(EFI_OUT_OF_RESOURCES); | |
299 | ||
300 | s = val; | |
301 | ||
302 | /* store attributes: */ | |
303 | attributes &= (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS); | |
304 | s += sprintf(s, "{"); | |
305 | while (attributes) { | |
306 | u32 attr = 1 << (ffs(attributes) - 1); | |
307 | ||
308 | if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS) | |
309 | s += sprintf(s, "boot"); | |
310 | else if (attr == EFI_VARIABLE_RUNTIME_ACCESS) | |
311 | s += sprintf(s, "run"); | |
312 | ||
313 | attributes &= ~attr; | |
314 | if (attributes) | |
315 | s += sprintf(s, ","); | |
316 | } | |
317 | s += sprintf(s, "}"); | |
318 | ||
319 | /* store payload: */ | |
320 | s += sprintf(s, "(blob)"); | |
321 | s = mem2hex(s, data, data_size); | |
322 | *s = '\0'; | |
323 | ||
324 | debug("%s: setting: %s=%s\n", __func__, native_name, val); | |
325 | ||
326 | if (env_set(native_name, val)) | |
327 | ret = EFI_DEVICE_ERROR; | |
328 | ||
dcdb64f7 HS |
329 | out: |
330 | free(native_name); | |
ad644e7c RC |
331 | free(val); |
332 | ||
333 | return EFI_EXIT(ret); | |
334 | } |