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