xref: /freebsd/stand/efi/libefi/env.c (revision 118063fb70c3f74f16678dc7ddbc8528c483a528)
1 /*
2  * Copyright (c) 2015 Netflix, Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28 
29 #include <sys/param.h>
30 #include <stand.h>
31 #include <string.h>
32 #include <efi.h>
33 #include <efilib.h>
34 #include <uuid.h>
35 #include <stdbool.h>
36 #include "bootstrap.h"
37 
38 /*
39  * Simple wrappers to the underlying UEFI functions.
40  * See http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES
41  * for details.
42  */
43 EFI_STATUS
44 efi_get_next_variable_name(UINTN *variable_name_size, CHAR16 *variable_name,
45     EFI_GUID *vendor_guid)
46 {
47 	return (RS->GetNextVariableName(variable_name_size, variable_name,
48 	    vendor_guid));
49 }
50 
51 EFI_STATUS
52 efi_get_variable(CHAR16 *variable_name, EFI_GUID *vendor_guid,
53     UINT32 *attributes, UINTN *data_size, void *data)
54 {
55 	return (RS->GetVariable(variable_name, vendor_guid, attributes,
56 	    data_size, data));
57 }
58 
59 EFI_STATUS
60 efi_set_variable(CHAR16 *variable_name, EFI_GUID *vendor_guid,
61     UINT32 attributes, UINTN data_size, void *data)
62 {
63 	return (RS->SetVariable(variable_name, vendor_guid, attributes,
64 	    data_size, data));
65 }
66 
67 void
68 efi_init_environment(void)
69 {
70 	char var[128];
71 
72 	snprintf(var, sizeof(var), "%d.%02d", ST->Hdr.Revision >> 16,
73 	    ST->Hdr.Revision & 0xffff);
74 	env_setenv("efi-version", EV_VOLATILE, var, env_noset, env_nounset);
75 }
76 
77 COMMAND_SET(efishow, "efi-show", "print some or all EFI variables", command_efi_show);
78 
79 static int
80 efi_print_var(CHAR16 *varnamearg, EFI_GUID *matchguid, int lflag)
81 {
82 	UINTN		datasz, i;
83 	EFI_STATUS	status;
84 	UINT32		attr;
85 	CHAR16		*data;
86 	char		*str;
87 	uint32_t	uuid_status;
88 	int		is_ascii;
89 
90 	datasz = 0;
91 	status = RS->GetVariable(varnamearg, matchguid, &attr,
92 	    &datasz, NULL);
93 	if (status != EFI_BUFFER_TOO_SMALL) {
94 		printf("Can't get the variable: error %#lx\n",
95 		    EFI_ERROR_CODE(status));
96 		return (CMD_ERROR);
97 	}
98 	data = malloc(datasz);
99 	status = RS->GetVariable(varnamearg, matchguid, &attr,
100 	    &datasz, data);
101 	if (status != EFI_SUCCESS) {
102 		printf("Can't get the variable: error %#lx\n",
103 		    EFI_ERROR_CODE(status));
104 		return (CMD_ERROR);
105 	}
106 	uuid_to_string((uuid_t *)matchguid, &str, &uuid_status);
107 	if (lflag) {
108 		printf("%s 0x%x %S", str, attr, varnamearg);
109 	} else {
110 		printf("%s 0x%x %S=", str, attr, varnamearg);
111 		is_ascii = 1;
112 		free(str);
113 		str = (char *)data;
114 		for (i = 0; i < datasz - 1; i++) {
115 			/* Quick hack to see if this ascii-ish string printable range plus tab, cr and lf */
116 			if ((str[i] < 32 || str[i] > 126) && str[i] != 9 && str[i] != 10 && str[i] != 13) {
117 				is_ascii = 0;
118 				break;
119 			}
120 		}
121 		if (str[datasz - 1] != '\0')
122 			is_ascii = 0;
123 		if (is_ascii)
124 			printf("%s", str);
125 		else {
126 			for (i = 0; i < datasz / 2; i++) {
127 				if (isalnum(data[i]) || isspace(data[i]))
128 					printf("%c", data[i]);
129 				else
130 					printf("\\x%02x", data[i]);
131 			}
132 		}
133 	}
134 	free(data);
135 	if (pager_output("\n"))
136 		return (CMD_WARN);
137 	return (CMD_OK);
138 }
139 
140 static int
141 command_efi_show(int argc, char *argv[])
142 {
143 	/*
144 	 * efi-show [-a]
145 	 *	print all the env
146 	 * efi-show -u UUID
147 	 *	print all the env vars tagged with UUID
148 	 * efi-show -v var
149 	 *	search all the env vars and print the ones matching var
150 	 * eif-show -u UUID -v var
151 	 * eif-show UUID var
152 	 *	print all the env vars that match UUID and var
153 	 */
154 	/* NB: We assume EFI_GUID is the same as uuid_t */
155 	int		aflag = 0, gflag = 0, lflag = 0, vflag = 0;
156 	int		ch, rv;
157 	unsigned	i;
158 	EFI_STATUS	status;
159 	EFI_GUID	varguid = { 0,0,0,{0,0,0,0,0,0,0,0} };
160 	EFI_GUID	matchguid = { 0,0,0,{0,0,0,0,0,0,0,0} };
161 	uint32_t	uuid_status;
162 	CHAR16		*varname;
163 	CHAR16		*newnm;
164 	CHAR16		varnamearg[128];
165 	UINTN		varalloc;
166 	UINTN		varsz;
167 
168 	while ((ch = getopt(argc, argv, "ag:lv:")) != -1) {
169 		switch (ch) {
170 		case 'a':
171 			aflag = 1;
172 			break;
173 		case 'g':
174 			gflag = 1;
175 			uuid_from_string(optarg, (uuid_t *)&matchguid,
176 			    &uuid_status);
177 			if (uuid_status != uuid_s_ok) {
178 				printf("uid %s could not be parsed\n", optarg);
179 				return (CMD_ERROR);
180 			}
181 			break;
182 		case 'l':
183 			lflag = 1;
184 			break;
185 		case 'v':
186 			vflag = 1;
187 			if (strlen(optarg) >= nitems(varnamearg)) {
188 				printf("Variable %s is longer than %zd characters\n",
189 				    optarg, nitems(varnamearg));
190 				return (CMD_ERROR);
191 			}
192 			for (i = 0; i < strlen(optarg); i++)
193 				varnamearg[i] = optarg[i];
194 			varnamearg[i] = 0;
195 			break;
196 		default:
197 			printf("Invalid argument %c\n", ch);
198 			return (CMD_ERROR);
199 		}
200 	}
201 
202 	if (aflag && (gflag || vflag)) {
203 		printf("-a isn't compatible with -v or -u\n");
204 		return (CMD_ERROR);
205 	}
206 
207 	if (aflag && optind < argc) {
208 		printf("-a doesn't take any args\n");
209 		return (CMD_ERROR);
210 	}
211 
212 	if (optind == argc)
213 		aflag = 1;
214 
215 	argc -= optind;
216 	argv += optind;
217 
218 	pager_open();
219 	if (vflag && gflag) {
220 		rv = efi_print_var(varnamearg, &matchguid, lflag);
221 		pager_close();
222 		return (rv);
223 	}
224 
225 	if (argc == 2) {
226 		optarg = argv[0];
227 		if (strlen(optarg) >= nitems(varnamearg)) {
228 			printf("Variable %s is longer than %zd characters\n",
229 			    optarg, nitems(varnamearg));
230 			pager_close();
231 			return (CMD_ERROR);
232 		}
233 		for (i = 0; i < strlen(optarg); i++)
234 			varnamearg[i] = optarg[i];
235 		varnamearg[i] = 0;
236 		optarg = argv[1];
237 		uuid_from_string(optarg, (uuid_t *)&matchguid,
238 		    &uuid_status);
239 		if (uuid_status != uuid_s_ok) {
240 			printf("uid %s could not be parsed\n", optarg);
241 			pager_close();
242 			return (CMD_ERROR);
243 		}
244 		rv = efi_print_var(varnamearg, &matchguid, lflag);
245 		pager_close();
246 		return (rv);
247 	}
248 
249 	if (argc > 0) {
250 		printf("Too many args %d\n", argc);
251 		pager_close();
252 		return (CMD_ERROR);
253 	}
254 
255 	/*
256 	 * Initiate the search -- note the standard takes pain
257 	 * to specify the initial call must be a poiner to a NULL
258 	 * character.
259 	 */
260 	varalloc = 1024;
261 	varname = malloc(varalloc);
262 	if (varname == NULL) {
263 		printf("Can't allocate memory to get variables\n");
264 		pager_close();
265 		return (CMD_ERROR);
266 	}
267 	varname[0] = 0;
268 	while (1) {
269 		varsz = varalloc;
270 		status = RS->GetNextVariableName(&varsz, varname, &varguid);
271 		if (status == EFI_BUFFER_TOO_SMALL) {
272 			varalloc = varsz;
273 			newnm = realloc(varname, varalloc);
274 			if (newnm == NULL) {
275 				printf("Can't allocate memory to get variables\n");
276 				free(varname);
277 				pager_close();
278 				return (CMD_ERROR);
279 			}
280 			varname = newnm;
281 			continue; /* Try again with bigger buffer */
282 		}
283 		if (status != EFI_SUCCESS)
284 			break;
285 		if (aflag) {
286 			if (efi_print_var(varname, &varguid, lflag) != CMD_OK)
287 				break;
288 			continue;
289 		}
290 		if (vflag) {
291 			if (wcscmp(varnamearg, varname) == 0) {
292 				if (efi_print_var(varname, &varguid, lflag) != CMD_OK)
293 					break;
294 				continue;
295 			}
296 		}
297 		if (gflag) {
298 			if (memcmp(&varguid, &matchguid, sizeof(varguid)) == 0) {
299 				if (efi_print_var(varname, &varguid, lflag) != CMD_OK)
300 					break;
301 				continue;
302 			}
303 		}
304 	}
305 	free(varname);
306 	pager_close();
307 
308 	return (CMD_OK);
309 }
310 
311 COMMAND_SET(efiset, "efi-set", "set EFI variables", command_efi_set);
312 
313 static int
314 command_efi_set(int argc, char *argv[])
315 {
316 	char *uuid, *var, *val;
317 	CHAR16 wvar[128];
318 	EFI_GUID guid;
319 	uint32_t status;
320 	EFI_STATUS err;
321 
322 	if (argc != 4) {
323 		printf("efi-set uuid var new-value\n");
324 		return (CMD_ERROR);
325 	}
326 	uuid = argv[1];
327 	var = argv[2];
328 	val = argv[3];
329 	uuid_from_string(uuid, (uuid_t *)&guid, &status);
330 	if (status != uuid_s_ok) {
331 		printf("Invalid uuid %s %d\n", uuid, status);
332 		return (CMD_ERROR);
333 	}
334 	cpy8to16(var, wvar, sizeof(wvar));
335 	err = RS->SetVariable(wvar, &guid,
336 	    EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS,
337 	    strlen(val) + 1, val);
338 	if (EFI_ERROR(err)) {
339 		printf("Failed to set variable: error %lu\n", EFI_ERROR_CODE(err));
340 		return (CMD_ERROR);
341 	}
342 	return (CMD_OK);
343 }
344 
345 COMMAND_SET(efiunset, "efi-unset", "delete / unset EFI variables", command_efi_unset);
346 
347 static int
348 command_efi_unset(int argc, char *argv[])
349 {
350 	char *uuid, *var;
351 	CHAR16 wvar[128];
352 	EFI_GUID guid;
353 	uint32_t status;
354 	EFI_STATUS err;
355 
356 	if (argc != 3) {
357 		printf("efi-unset uuid var\n");
358 		return (CMD_ERROR);
359 	}
360 	uuid = argv[1];
361 	var = argv[2];
362 	uuid_from_string(uuid, (uuid_t *)&guid, &status);
363 	if (status != uuid_s_ok) {
364 		printf("Invalid uuid %s\n", uuid);
365 		return (CMD_ERROR);
366 	}
367 	cpy8to16(var, wvar, sizeof(wvar));
368 	err = RS->SetVariable(wvar, &guid, 0, 0, NULL);
369 	if (EFI_ERROR(err)) {
370 		printf("Failed to unset variable: error %lu\n", EFI_ERROR_CODE(err));
371 		return (CMD_ERROR);
372 	}
373 	return (CMD_OK);
374 }
375