env.c revision 346472
1300081Simp/*
2300081Simp * Copyright (c) 2015 Netflix, Inc. All Rights Reserved.
3300081Simp *
4300081Simp * Redistribution and use in source and binary forms, with or without
5300081Simp * modification, are permitted provided that the following conditions
6300081Simp * are met:
7300081Simp * 1. Redistributions of source code must retain the above copyright
8300081Simp *    notice, this list of conditions and the following disclaimer.
9300081Simp * 2. Redistributions in binary form must reproduce the above copyright
10300081Simp *    notice, this list of conditions and the following disclaimer in the
11300081Simp *    documentation and/or other materials provided with the distribution.
12300081Simp *
13300081Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14300081Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15300081Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16300081Simp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17300081Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18300081Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19300081Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20300081Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21300081Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22300081Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23300081Simp * SUCH DAMAGE.
24300081Simp */
25300081Simp
26300081Simp#include <sys/cdefs.h>
27300081Simp__FBSDID("$FreeBSD: stable/11/stand/efi/libefi/env.c 346472 2019-04-21 03:16:09Z kevans $");
28300081Simp
29329099Skevans#include <sys/param.h>
30329011Skevans#include <stand.h>
31329011Skevans#include <string.h>
32300081Simp#include <efi.h>
33300081Simp#include <efilib.h>
34329011Skevans#include <uuid.h>
35329099Skevans#include <stdbool.h>
36329011Skevans#include "bootstrap.h"
37300081Simp
38329099Skevansvoid
39329099Skevansefi_init_environment(void)
40329099Skevans{
41329099Skevans	char var[128];
42329099Skevans
43329099Skevans	snprintf(var, sizeof(var), "%d.%02d", ST->Hdr.Revision >> 16,
44329099Skevans	    ST->Hdr.Revision & 0xffff);
45329099Skevans	env_setenv("efi-version", EV_VOLATILE, var, env_noset, env_nounset);
46329099Skevans}
47329099Skevans
48329099SkevansCOMMAND_SET(efishow, "efi-show", "print some or all EFI variables", command_efi_show);
49329099Skevans
50329099Skevansstatic int
51329099Skevansefi_print_var(CHAR16 *varnamearg, EFI_GUID *matchguid, int lflag)
52329099Skevans{
53329099Skevans	UINTN		datasz, i;
54329099Skevans	EFI_STATUS	status;
55329099Skevans	UINT32		attr;
56329099Skevans	CHAR16		*data;
57329099Skevans	char		*str;
58329099Skevans	uint32_t	uuid_status;
59329099Skevans	int		is_ascii;
60329099Skevans
61329099Skevans	datasz = 0;
62329099Skevans	status = RS->GetVariable(varnamearg, matchguid, &attr,
63329099Skevans	    &datasz, NULL);
64329099Skevans	if (status != EFI_BUFFER_TOO_SMALL) {
65329099Skevans		printf("Can't get the variable: error %#lx\n",
66329099Skevans		    EFI_ERROR_CODE(status));
67329099Skevans		return (CMD_ERROR);
68329099Skevans	}
69329099Skevans	data = malloc(datasz);
70329099Skevans	status = RS->GetVariable(varnamearg, matchguid, &attr,
71329099Skevans	    &datasz, data);
72329099Skevans	if (status != EFI_SUCCESS) {
73329099Skevans		printf("Can't get the variable: error %#lx\n",
74329099Skevans		    EFI_ERROR_CODE(status));
75329099Skevans		return (CMD_ERROR);
76329099Skevans	}
77329099Skevans	uuid_to_string((uuid_t *)matchguid, &str, &uuid_status);
78329099Skevans	if (lflag) {
79329099Skevans		printf("%s 0x%x %S", str, attr, varnamearg);
80329099Skevans	} else {
81329099Skevans		printf("%s 0x%x %S=", str, attr, varnamearg);
82329099Skevans		is_ascii = 1;
83329099Skevans		free(str);
84329099Skevans		str = (char *)data;
85329099Skevans		for (i = 0; i < datasz - 1; i++) {
86329099Skevans			/* Quick hack to see if this ascii-ish string printable range plus tab, cr and lf */
87329099Skevans			if ((str[i] < 32 || str[i] > 126) && str[i] != 9 && str[i] != 10 && str[i] != 13) {
88329099Skevans				is_ascii = 0;
89329099Skevans				break;
90329099Skevans			}
91329099Skevans		}
92329099Skevans		if (str[datasz - 1] != '\0')
93329099Skevans			is_ascii = 0;
94329099Skevans		if (is_ascii)
95329099Skevans			printf("%s", str);
96329099Skevans		else {
97329099Skevans			for (i = 0; i < datasz / 2; i++) {
98329099Skevans				if (isalnum(data[i]) || isspace(data[i]))
99329099Skevans					printf("%c", data[i]);
100329099Skevans				else
101329099Skevans					printf("\\x%02x", data[i]);
102329099Skevans			}
103329099Skevans		}
104329099Skevans	}
105329099Skevans	free(data);
106329099Skevans	if (pager_output("\n"))
107329099Skevans		return (CMD_WARN);
108329099Skevans	return (CMD_OK);
109329099Skevans}
110329099Skevans
111329099Skevansstatic int
112329099Skevanscommand_efi_show(int argc, char *argv[])
113329099Skevans{
114329099Skevans	/*
115329099Skevans	 * efi-show [-a]
116329099Skevans	 *	print all the env
117346472Skevans	 * efi-show -g UUID
118329099Skevans	 *	print all the env vars tagged with UUID
119329099Skevans	 * efi-show -v var
120329099Skevans	 *	search all the env vars and print the ones matching var
121346472Skevans	 * efi-show -g UUID -v var
122346472Skevans	 * efi-show UUID var
123329099Skevans	 *	print all the env vars that match UUID and var
124329099Skevans	 */
125329099Skevans	/* NB: We assume EFI_GUID is the same as uuid_t */
126329099Skevans	int		aflag = 0, gflag = 0, lflag = 0, vflag = 0;
127329099Skevans	int		ch, rv;
128329099Skevans	unsigned	i;
129329099Skevans	EFI_STATUS	status;
130329099Skevans	EFI_GUID	varguid = { 0,0,0,{0,0,0,0,0,0,0,0} };
131329099Skevans	EFI_GUID	matchguid = { 0,0,0,{0,0,0,0,0,0,0,0} };
132329099Skevans	uint32_t	uuid_status;
133329099Skevans	CHAR16		*varname;
134329099Skevans	CHAR16		*newnm;
135329099Skevans	CHAR16		varnamearg[128];
136329099Skevans	UINTN		varalloc;
137329099Skevans	UINTN		varsz;
138329099Skevans
139329099Skevans	while ((ch = getopt(argc, argv, "ag:lv:")) != -1) {
140329099Skevans		switch (ch) {
141329099Skevans		case 'a':
142329099Skevans			aflag = 1;
143329099Skevans			break;
144329099Skevans		case 'g':
145329099Skevans			gflag = 1;
146329099Skevans			uuid_from_string(optarg, (uuid_t *)&matchguid,
147329099Skevans			    &uuid_status);
148329099Skevans			if (uuid_status != uuid_s_ok) {
149329099Skevans				printf("uid %s could not be parsed\n", optarg);
150329099Skevans				return (CMD_ERROR);
151329099Skevans			}
152329099Skevans			break;
153329099Skevans		case 'l':
154329099Skevans			lflag = 1;
155329099Skevans			break;
156329099Skevans		case 'v':
157329099Skevans			vflag = 1;
158329099Skevans			if (strlen(optarg) >= nitems(varnamearg)) {
159329099Skevans				printf("Variable %s is longer than %zd characters\n",
160329099Skevans				    optarg, nitems(varnamearg));
161329099Skevans				return (CMD_ERROR);
162329099Skevans			}
163329099Skevans			for (i = 0; i < strlen(optarg); i++)
164329099Skevans				varnamearg[i] = optarg[i];
165329099Skevans			varnamearg[i] = 0;
166329099Skevans			break;
167329099Skevans		default:
168329099Skevans			printf("Invalid argument %c\n", ch);
169329099Skevans			return (CMD_ERROR);
170329099Skevans		}
171329099Skevans	}
172329099Skevans
173329099Skevans	if (aflag && (gflag || vflag)) {
174329099Skevans		printf("-a isn't compatible with -v or -u\n");
175329099Skevans		return (CMD_ERROR);
176329099Skevans	}
177329099Skevans
178329099Skevans	if (aflag && optind < argc) {
179329099Skevans		printf("-a doesn't take any args\n");
180329099Skevans		return (CMD_ERROR);
181329099Skevans	}
182329099Skevans
183329099Skevans	if (optind == argc)
184329099Skevans		aflag = 1;
185329099Skevans
186329099Skevans	argc -= optind;
187329099Skevans	argv += optind;
188329099Skevans
189329099Skevans	pager_open();
190329099Skevans	if (vflag && gflag) {
191329099Skevans		rv = efi_print_var(varnamearg, &matchguid, lflag);
192329099Skevans		pager_close();
193329099Skevans		return (rv);
194329099Skevans	}
195329099Skevans
196329099Skevans	if (argc == 2) {
197329099Skevans		optarg = argv[0];
198329099Skevans		if (strlen(optarg) >= nitems(varnamearg)) {
199329099Skevans			printf("Variable %s is longer than %zd characters\n",
200329099Skevans			    optarg, nitems(varnamearg));
201329099Skevans			pager_close();
202329099Skevans			return (CMD_ERROR);
203329099Skevans		}
204329099Skevans		for (i = 0; i < strlen(optarg); i++)
205329099Skevans			varnamearg[i] = optarg[i];
206329099Skevans		varnamearg[i] = 0;
207329099Skevans		optarg = argv[1];
208329099Skevans		uuid_from_string(optarg, (uuid_t *)&matchguid,
209329099Skevans		    &uuid_status);
210329099Skevans		if (uuid_status != uuid_s_ok) {
211329099Skevans			printf("uid %s could not be parsed\n", optarg);
212329099Skevans			pager_close();
213329099Skevans			return (CMD_ERROR);
214329099Skevans		}
215329099Skevans		rv = efi_print_var(varnamearg, &matchguid, lflag);
216329099Skevans		pager_close();
217329099Skevans		return (rv);
218329099Skevans	}
219329099Skevans
220329099Skevans	if (argc > 0) {
221329099Skevans		printf("Too many args %d\n", argc);
222329099Skevans		pager_close();
223329099Skevans		return (CMD_ERROR);
224329099Skevans	}
225329099Skevans
226329099Skevans	/*
227329099Skevans	 * Initiate the search -- note the standard takes pain
228329099Skevans	 * to specify the initial call must be a poiner to a NULL
229329099Skevans	 * character.
230329099Skevans	 */
231329099Skevans	varalloc = 1024;
232329099Skevans	varname = malloc(varalloc);
233329099Skevans	if (varname == NULL) {
234329099Skevans		printf("Can't allocate memory to get variables\n");
235329099Skevans		pager_close();
236329099Skevans		return (CMD_ERROR);
237329099Skevans	}
238329099Skevans	varname[0] = 0;
239329099Skevans	while (1) {
240329099Skevans		varsz = varalloc;
241329099Skevans		status = RS->GetNextVariableName(&varsz, varname, &varguid);
242329099Skevans		if (status == EFI_BUFFER_TOO_SMALL) {
243329099Skevans			varalloc = varsz;
244329099Skevans			newnm = realloc(varname, varalloc);
245329099Skevans			if (newnm == NULL) {
246329099Skevans				printf("Can't allocate memory to get variables\n");
247329099Skevans				free(varname);
248329099Skevans				pager_close();
249329099Skevans				return (CMD_ERROR);
250329099Skevans			}
251329099Skevans			varname = newnm;
252329099Skevans			continue; /* Try again with bigger buffer */
253329099Skevans		}
254329099Skevans		if (status != EFI_SUCCESS)
255329099Skevans			break;
256329099Skevans		if (aflag) {
257329099Skevans			if (efi_print_var(varname, &varguid, lflag) != CMD_OK)
258329099Skevans				break;
259329099Skevans			continue;
260329099Skevans		}
261329099Skevans		if (vflag) {
262329099Skevans			if (wcscmp(varnamearg, varname) == 0) {
263329099Skevans				if (efi_print_var(varname, &varguid, lflag) != CMD_OK)
264329099Skevans					break;
265329099Skevans				continue;
266329099Skevans			}
267329099Skevans		}
268329099Skevans		if (gflag) {
269329099Skevans			if (memcmp(&varguid, &matchguid, sizeof(varguid)) == 0) {
270329099Skevans				if (efi_print_var(varname, &varguid, lflag) != CMD_OK)
271329099Skevans					break;
272329099Skevans				continue;
273329099Skevans			}
274329099Skevans		}
275329099Skevans	}
276329099Skevans	free(varname);
277329099Skevans	pager_close();
278329099Skevans
279329099Skevans	return (CMD_OK);
280329099Skevans}
281329099Skevans
282329099SkevansCOMMAND_SET(efiset, "efi-set", "set EFI variables", command_efi_set);
283329099Skevans
284329099Skevansstatic int
285329099Skevanscommand_efi_set(int argc, char *argv[])
286329099Skevans{
287329099Skevans	char *uuid, *var, *val;
288329099Skevans	CHAR16 wvar[128];
289329099Skevans	EFI_GUID guid;
290329099Skevans	uint32_t status;
291329099Skevans	EFI_STATUS err;
292329099Skevans
293329099Skevans	if (argc != 4) {
294329099Skevans		printf("efi-set uuid var new-value\n");
295329099Skevans		return (CMD_ERROR);
296329099Skevans	}
297329099Skevans	uuid = argv[1];
298329099Skevans	var = argv[2];
299329099Skevans	val = argv[3];
300329099Skevans	uuid_from_string(uuid, (uuid_t *)&guid, &status);
301329099Skevans	if (status != uuid_s_ok) {
302329099Skevans		printf("Invalid uuid %s %d\n", uuid, status);
303329099Skevans		return (CMD_ERROR);
304329099Skevans	}
305329099Skevans	cpy8to16(var, wvar, sizeof(wvar));
306329099Skevans	err = RS->SetVariable(wvar, &guid,
307329099Skevans	    EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS,
308329099Skevans	    strlen(val) + 1, val);
309329099Skevans	if (EFI_ERROR(err)) {
310329099Skevans		printf("Failed to set variable: error %lu\n", EFI_ERROR_CODE(err));
311329099Skevans		return (CMD_ERROR);
312329099Skevans	}
313329099Skevans	return (CMD_OK);
314329099Skevans}
315329099Skevans
316329099SkevansCOMMAND_SET(efiunset, "efi-unset", "delete / unset EFI variables", command_efi_unset);
317329099Skevans
318329099Skevansstatic int
319329099Skevanscommand_efi_unset(int argc, char *argv[])
320329099Skevans{
321329099Skevans	char *uuid, *var;
322329099Skevans	CHAR16 wvar[128];
323329099Skevans	EFI_GUID guid;
324329099Skevans	uint32_t status;
325329099Skevans	EFI_STATUS err;
326329099Skevans
327329099Skevans	if (argc != 3) {
328329099Skevans		printf("efi-unset uuid var\n");
329329099Skevans		return (CMD_ERROR);
330329099Skevans	}
331329099Skevans	uuid = argv[1];
332329099Skevans	var = argv[2];
333329099Skevans	uuid_from_string(uuid, (uuid_t *)&guid, &status);
334329099Skevans	if (status != uuid_s_ok) {
335329099Skevans		printf("Invalid uuid %s\n", uuid);
336329099Skevans		return (CMD_ERROR);
337329099Skevans	}
338329099Skevans	cpy8to16(var, wvar, sizeof(wvar));
339329099Skevans	err = RS->SetVariable(wvar, &guid, 0, 0, NULL);
340329099Skevans	if (EFI_ERROR(err)) {
341329099Skevans		printf("Failed to unset variable: error %lu\n", EFI_ERROR_CODE(err));
342329099Skevans		return (CMD_ERROR);
343329099Skevans	}
344329099Skevans	return (CMD_OK);
345329099Skevans}
346