]>
Commit | Line | Data |
---|---|---|
ad644e7c RC |
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 | ||
778e6af8 | 190 | EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes, |
ad644e7c RC |
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 | { | |
778e6af8 | 258 | EFI_ENTRY("%p \"%ls\" %pUl", variable_name_size, variable_name, vendor); |
ad644e7c RC |
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 | ||
778e6af8 | 273 | EFI_ENTRY("\"%ls\" %pUl %x %lu %p", variable_name, vendor, attributes, |
ad644e7c RC |
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 | } |