From e0a4302c13d8e702ca7823cd8a438b4871ca780e Mon Sep 17 00:00:00 2001
From: Sascha Wildner <saw@online.de>
Date: Sat, 26 Nov 2016 10:37:23 +0100
Subject: [PATCH] boot/efi: Add three new commands, efi-show, efi-set and
 efi-unset.

For accessing EFI variables. These replace the nvram command.

Taken-from: FreeBSD
---
 lib/libstand/Makefile           |   8 +
 lib/libstand/uuid_from_string.c | 132 +++++++++++++++
 lib/libstand/uuid_to_string.c   | 111 +++++++++++++
 sys/boot/efi/loader/main.c      | 346 ++++++++++++++++++++++++++++++++++++----
 4 files changed, 564 insertions(+), 33 deletions(-)
 create mode 100644 lib/libstand/uuid_from_string.c
 create mode 100644 lib/libstand/uuid_to_string.c

diff --git a/lib/libstand/Makefile b/lib/libstand/Makefile
index 9ff11a5..a2e2c7e 100644
--- a/lib/libstand/Makefile
+++ b/lib/libstand/Makefile
@@ -18,6 +18,7 @@ NO_STRICT_ALIASING=
 
 LIBSTAND_SRC?=	${.CURDIR}
 LIBSTAND_ARCH?=	${MACHINE_ARCH}
+LIBC_SRC=	${LIBSTAND_SRC}/../libc
 
 # Mostly OK, some of the libc imports are a bit noisy
 CFLAGS+=	-ffreestanding
@@ -94,6 +95,13 @@ ${file}: ${BZ2DIR}/${file} ${file}.diff
 SRCS+=	closeall.c dev.c ioctl.c nullfs.c stat.c \
 	fstat.c close.c lseek.c open.c read.c write.c readdir.c
 
+# uuid routines
+SRCS+=	uuid_from_string.c uuid_to_string.c
+
+# uuid routines from libc
+.PATH: ${LIBC_SRC}/uuid
+SRCS+=	uuid_create_nil.c
+
 # network routines
 SRCS+=	arp.c ether.c inet_ntoa.c in_cksum.c net.c udp.c netif.c rpc.c
 
diff --git a/lib/libstand/uuid_from_string.c b/lib/libstand/uuid_from_string.c
new file mode 100644
index 0000000..42d35ba
--- /dev/null
+++ b/lib/libstand/uuid_from_string.c
@@ -0,0 +1,132 @@
+/*-
+ * Copyright (c) 2015 M. Warner Losh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: head/lib/libstand/uuid_from_string.c 300264 2016-05-20 05:33:26Z imp $
+ */
+
+/*
+ * Note: some comments taken from lib/libc/uuid/uuid_from_string.c
+ * Copyright (c) 2002 Marcel Moolenaar
+ * Copyright (c) 2002 Hiten Mahesh Pandya
+ */
+
+
+#include <stand.h>
+#include <uuid.h>
+
+static int
+hex2int(int ch)
+{
+	if (ch >= '0' && ch <= '9')
+		return ch - '0';
+	if (ch >= 'a' && ch <= 'f')
+		return 10 + ch - 'a';
+	if (ch >= 'A' && ch <= 'F')
+		return 10 + ch - 'A';
+	return 16;
+}
+
+static uint32_t
+fromhex(const char *s, int len, int *ok)
+{
+	uint32_t v;
+	int i, h;
+
+	if (!*ok)
+		return 0;
+	v = 0;
+	for (i = 0; i < len; i++) {
+		h = hex2int(s[i]);
+		if (h == 16) {
+			*ok = 0;
+			return v;
+		}
+		v = (v << 4) | h;
+	}
+	return v;
+}
+
+/*
+ * uuid_from_string() - convert a string representation of an UUID into
+ *			a binary representation.
+ * See also:
+ *	http://www.opengroup.org/onlinepubs/009629399/uuid_from_string.htm
+ *
+ * NOTE: The sequence field is in big-endian, while the time fields are in
+ *	 native byte order.
+ *
+ * 01234567-89ab-cdef-0123-456789abcdef
+ * 000000000011111111112222222222333333
+ * 012345678901234567890123456789012345
+ *         -    -    -    -
+ * hhhhhhhh-hhhh-hhhh-bbbb-bbbbbbbbbbbb
+ *
+ */
+void
+uuid_from_string(const char *s, uuid_t *u, uint32_t *status)
+{
+	int ok = 1;
+	int n;
+
+	if (s == NULL || *s == '\0') {
+		uuid_create_nil(u, status);
+		return;
+	}
+
+	if (status != NULL)
+		*status = uuid_s_invalid_string_uuid;
+	if (strlen(s) != 36)
+		return;
+	/* Only support new format, check for all the right dashes */
+	if (s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-')
+		return;
+	/* native byte order */
+	u->time_low                  = fromhex(s     , 8, &ok);
+	u->time_mid                  = fromhex(s +  9, 4, &ok);
+	u->time_hi_and_version       = fromhex(s + 14, 4, &ok);
+	/* Big endian, but presented as a whole number so decode as such */
+	u->clock_seq_hi_and_reserved = fromhex(s + 19, 2, &ok);
+	u->clock_seq_low             = fromhex(s + 21, 2, &ok);
+	u->node[0]                   = fromhex(s + 24, 2, &ok);
+	u->node[1]                   = fromhex(s + 26, 2, &ok);
+	u->node[2]                   = fromhex(s + 28, 2, &ok);
+	u->node[3]                   = fromhex(s + 30, 2, &ok);
+	u->node[4]                   = fromhex(s + 32, 2, &ok);
+	u->node[5]                   = fromhex(s + 34, 2, &ok);
+	if (!ok)
+		return;
+
+	/* We have a successful scan. Check semantics... */
+	n = u->clock_seq_hi_and_reserved;
+	if ((n & 0x80) != 0x00 &&			/* variant 0? */
+	    (n & 0xc0) != 0x80 &&			/* variant 1? */
+	    (n & 0xe0) != 0xc0) {			/* variant 2? */
+		if (status != NULL)
+			*status = uuid_s_bad_version;
+	} else {
+		if (status != NULL)
+			*status = uuid_s_ok;
+	}
+}
diff --git a/lib/libstand/uuid_to_string.c b/lib/libstand/uuid_to_string.c
new file mode 100644
index 0000000..7607d93
--- /dev/null
+++ b/lib/libstand/uuid_to_string.c
@@ -0,0 +1,111 @@
+/*-
+ * Copyright (c) 2015 M. Warner Losh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: head/lib/libstand/uuid_to_string.c 293468 2016-01-09 08:04:29Z ae $
+ */
+
+
+/*
+ * Note: some comments taken from lib/libc/uuid/uuid_to_string.c
+ * Copyright (c) 2002,2005 Marcel Moolenaar
+ * Copyright (c) 2002 Hiten Mahesh Pandya
+ */
+
+#include <stand.h>
+#include <uuid.h>
+
+/*
+ * Dump len characters into *buf from val as hex and update *buf
+ */
+static void
+tohex(char **buf, int len, uint32_t val)
+{
+	static const char *hexstr = "0123456789abcdef";
+	char *walker = *buf;
+	int i;
+
+	for (i = len - 1; i >= 0; i--) {
+		walker[i] = hexstr[val & 0xf];
+		val >>= 4;
+	}
+	*buf = walker + len;
+}
+
+/*
+ * uuid_to_string() - Convert a binary UUID into a string representation.
+ * See also:
+ *	http://www.opengroup.org/onlinepubs/009629399/uuid_to_string.htm
+ *
+ * NOTE: The references given above do not have a status code for when
+ *	 the string could not be allocated. The status code has been
+ *	 taken from the Hewlett-Packard implementation.
+ *
+ * NOTE: we don't support u == NULL for a nil UUID, sorry.
+ *
+ * NOTE: The sequence field is in big-endian, while the time fields are in
+ *	 native byte order.
+ *
+ *	 hhhhhhhh-hhhh-hhhh-bbbb-bbbbbbbbbbbb
+ *	 01234567-89ab-cdef-0123-456789abcdef
+ */
+void
+uuid_to_string(const uuid_t *u, char **s, uint32_t *status)
+{
+	uuid_t nil;
+	char *w;
+
+	if (status != NULL)
+		*status = uuid_s_ok;
+	if (s == NULL)	/* Regular version does this odd-ball behavior too */
+		return;
+	w = *s = malloc(37);
+	if (*s == NULL) {
+		if (status != NULL)
+			*status = uuid_s_no_memory;
+		return;
+	}
+	if (u == NULL) {
+		u = &nil;
+		uuid_create_nil(&nil, NULL);
+	}
+	/* native */
+	tohex(&w, 8, u->time_low);
+	*w++ = '-';
+	tohex(&w, 4, u->time_mid);
+	*w++ = '-';
+	tohex(&w, 4, u->time_hi_and_version);
+	*w++ = '-';
+	/* Big endian, so do a byte at a time */
+	tohex(&w, 2, u->clock_seq_hi_and_reserved);
+	tohex(&w, 2, u->clock_seq_low);
+	*w++ = '-';
+	tohex(&w, 2, u->node[0]);
+	tohex(&w, 2, u->node[1]);
+	tohex(&w, 2, u->node[2]);
+	tohex(&w, 2, u->node[3]);
+	tohex(&w, 2, u->node[4]);
+	tohex(&w, 2, u->node[5]);
+	*w++ = '\0';
+}
diff --git a/sys/boot/efi/loader/main.c b/sys/boot/efi/loader/main.c
index 09fe9a1..ce19f24 100644
--- a/sys/boot/efi/loader/main.c
+++ b/sys/boot/efi/loader/main.c
@@ -38,6 +38,8 @@ __FBSDID("$FreeBSD: head/sys/boot/efi/loader/main.c 295408 2016-02-08 19:34:17Z
 #include <efi.h>
 #include <efilib.h>
 
+#include <uuid.h>
+
 #include <bootstrap.h>
 #include <smbios.h>
 
@@ -78,6 +80,21 @@ print_str16(const CHAR16 *str)
 		printf("%c", (char)str[i]);
 }
 
+/*
+ * cpy8to16 copies a traditional C string into a CHAR16 string and
+ * 0 terminates it. len is the size of *dst in bytes.
+ */
+static void
+cpy8to16(const char *src, CHAR16 *dst, size_t len)
+{
+	len <<= 1;		/* Assume CHAR16 is 2 bytes */
+	while (len > 0 && *src) {
+		*dst++ = *src++;
+		len--;
+	}
+	*dst++ = (CHAR16)0;
+}
+
 static void
 cp16to8(const CHAR16 *src, char *dst, size_t len)
 {
@@ -380,6 +397,19 @@ main(int argc, CHAR16 *argv[])
 	return (EFI_SUCCESS);		/* keep compiler happy */
 }
 
+/* XXX move to lib stand ? */
+static int
+wcscmp(CHAR16 *a, CHAR16 *b)
+{
+
+	while (*a && *b && *a == *b) {
+		a++;
+		b++;
+	}
+	return *a - *b;
+}
+
+
 COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
 
 static int
@@ -586,50 +616,300 @@ command_mode(int argc, char *argv[])
 	return (CMD_OK);
 }
 
-
-COMMAND_SET(nvram, "nvram", "get or set NVRAM variables", command_nvram);
+COMMAND_SET(efishow, "efi-show", "print some or all EFI variables", command_efi_show);
 
 static int
-command_nvram(int argc, char *argv[])
+efi_print_var(CHAR16 *varnamearg, EFI_GUID *matchguid, int lflag)
 {
-	CHAR16 var[128];
-	CHAR16 *data;
-	EFI_STATUS status;
-	EFI_GUID varguid = { 0,0,0,{0,0,0,0,0,0,0,0} };
-	UINTN varsz, datasz, i;
-	SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
-
-	conout = ST->ConOut;
-
-	/* Initiate the search */
-	status = RS->GetNextVariableName(&varsz, NULL, NULL);
-
-	for (; status != EFI_NOT_FOUND; ) {
-		status = RS->GetNextVariableName(&varsz, var, &varguid);
-		//if (EFI_ERROR(status))
-			//break;
-
-		conout->OutputString(conout, var);
-		printf("=");
-		datasz = 0;
-		status = RS->GetVariable(var, &varguid, NULL, &datasz, NULL);
-		/* XXX: check status */
-		data = malloc(datasz);
-		status = RS->GetVariable(var, &varguid, NULL, &datasz, data);
-		if (EFI_ERROR(status))
-			printf("<error retrieving variable>");
+	UINTN		datasz, i;
+	EFI_STATUS	status;
+	UINT32		attr;
+	CHAR16		*data;
+	char		*str;
+	uint32_t	uuid_status;
+	int		is_ascii;
+
+	datasz = 0;
+	status = RS->GetVariable(varnamearg, matchguid, &attr,
+	    &datasz, NULL);
+	if (status != EFI_BUFFER_TOO_SMALL) {
+		printf("Can't get the variable: error %#lx\n", status);
+		return (CMD_ERROR);
+	}
+	data = malloc(datasz);
+	status = RS->GetVariable(varnamearg, matchguid, &attr,
+	    &datasz, data);
+	if (status != EFI_SUCCESS) {
+		printf("Can't get the variable: error %#lx\n", status);
+		return (CMD_ERROR);
+	}
+	uuid_to_string((uuid_t *)matchguid, &str, &uuid_status);
+	if (lflag) {
+		printf("%s 0x%x %S", str, attr, varnamearg);
+	} else {
+		printf("%s 0x%x %S=", str, attr, varnamearg);
+		is_ascii = 1;
+		free(str);
+		str = (char *)data;
+		for (i = 0; i < datasz - 1; i++) {
+			/* Quick hack to see if this ascii-ish string printable range plus tab, cr and lf */
+			if ((str[i] < 32 || str[i] > 126) && str[i] != 9 && str[i] != 10 && str[i] != 13) {
+				is_ascii = 0;
+				break;
+			}
+		}
+		if (str[datasz - 1] != '\0')
+			is_ascii = 0;
+		if (is_ascii)
+			printf("%s", str);
 		else {
-			for (i = 0; i < datasz; i++) {
+			for (i = 0; i < datasz / 2; i++) {
 				if (isalnum(data[i]) || isspace(data[i]))
 					printf("%c", data[i]);
 				else
 					printf("\\x%02x", data[i]);
 			}
 		}
-		/* XXX */
-		pager_output("\n");
-		free(data);
 	}
+	free(data);
+	pager_output("\n");
+	return (CMD_OK);
+}
+
+static int
+command_efi_show(int argc, char *argv[])
+{
+	/*
+	 * efi-show [-a]
+	 *	print all the env
+	 * efi-show -u UUID
+	 *	print all the env vars tagged with UUID
+	 * efi-show -v var
+	 *	search all the env vars and print the ones matching var
+	 * eif-show -u UUID -v var
+	 * eif-show UUID var
+	 *	print all the env vars that match UUID and var
+	 */
+	/* NB: We assume EFI_GUID is the same as uuid_t */
+	int		aflag = 0, gflag = 0, lflag = 0, vflag = 0;
+	int		ch, rv;
+	unsigned	i;
+	EFI_STATUS	status;
+	EFI_GUID	varguid = { 0,0,0,{0,0,0,0,0,0,0,0} };
+	EFI_GUID	matchguid = { 0,0,0,{0,0,0,0,0,0,0,0} };
+	uint32_t	uuid_status;
+	CHAR16		*varname;
+	CHAR16		*newnm;
+	CHAR16		varnamearg[128];
+	UINTN		varalloc;
+	UINTN		varsz;
+
+	while ((ch = getopt(argc, argv, "ag:lv:")) != -1) {
+		switch (ch) {
+		case 'a':
+			aflag = 1;
+			break;
+		case 'g':
+			gflag = 1;
+			uuid_from_string(optarg, (uuid_t *)&matchguid,
+			    &uuid_status);
+			if (uuid_status != uuid_s_ok) {
+				printf("uid %s could not be parsed\n", optarg);
+				return (CMD_ERROR);
+			}
+			break;
+		case 'l':
+			lflag = 1;
+			break;
+		case 'v':
+			vflag = 1;
+			if (strlen(optarg) >= nitems(varnamearg)) {
+				printf("Variable %s is longer than %zd characters\n",
+				    optarg, nitems(varnamearg));
+				return (CMD_ERROR);
+			}
+			for (i = 0; i < strlen(optarg); i++)
+				varnamearg[i] = optarg[i];
+			varnamearg[i] = 0;
+			break;
+		default:
+			printf("Invalid argument %c\n", ch);
+			return (CMD_ERROR);
+		}
+	}
+
+	if (aflag && (gflag || vflag)) {
+		printf("-a isn't compatible with -v or -u\n");
+		return (CMD_ERROR);
+	}
+
+	if (aflag && optind < argc) {
+		printf("-a doesn't take any args");
+		return (CMD_ERROR);
+	}
+
+	if (optind == argc)
+		aflag = 1;
+
+	argc -= optind;
+	argv += optind;
 
+	pager_open();
+	if (vflag && gflag) {
+		rv = efi_print_var(varnamearg, &matchguid, lflag);
+		pager_close();
+		return (rv);
+	}
+
+	if (argc == 2) {
+		optarg = argv[0];
+		if (strlen(optarg) >= nitems(varnamearg)) {
+			printf("Variable %s is longer than %zd characters\n",
+			    optarg, nitems(varnamearg));
+			pager_close();
+			return (CMD_ERROR);
+		}
+		for (i = 0; i < strlen(optarg); i++)
+			varnamearg[i] = optarg[i];
+		varnamearg[i] = 0;
+		optarg = argv[1];
+		uuid_from_string(optarg, (uuid_t *)&matchguid,
+		    &uuid_status);
+		if (uuid_status != uuid_s_ok) {
+			printf("uid %s could not be parsed\n", optarg);
+			pager_close();
+			return (CMD_ERROR);
+		}
+		rv = efi_print_var(varnamearg, &matchguid, lflag);
+		pager_close();
+		return (rv);
+	}
+
+	if (argc > 0) {
+		printf("Too many args %d\n", argc);
+		pager_close();
+		return (CMD_ERROR);
+	}
+
+	/*
+	 * Initiate the search -- note the standard takes pain
+	 * to specify the initial call must be a poiner to a NULL
+	 * character.
+	 */
+	varalloc = 1024;
+	varname = malloc(varalloc);
+	if (varname == NULL) {
+		printf("Can't allocate memory to get variables\n");
+		pager_close();
+		return (CMD_ERROR);
+	}
+	varname[0] = 0;
+	while (1) {
+		varsz = varalloc;
+		status = RS->GetNextVariableName(&varsz, varname, &varguid);
+		if (status == EFI_BUFFER_TOO_SMALL) {
+			varalloc = varsz;
+			newnm = malloc(varalloc);
+			if (newnm == NULL) {
+				printf("Can't allocate memory to get variables\n");
+				free(varname);
+				pager_close();
+				return (CMD_ERROR);
+			}
+			memcpy(newnm, varname, varsz);
+			free(varname);
+			varname = newnm;
+			continue; /* Try again with bigger buffer */
+		}
+		if (status != EFI_SUCCESS)
+			break;
+		if (aflag) {
+			if (efi_print_var(varname, &varguid, lflag) != CMD_OK)
+				break;
+			continue;
+		}
+		if (vflag) {
+			if (wcscmp(varnamearg, varname) == 0) {
+				if (efi_print_var(varname, &varguid, lflag) != CMD_OK)
+					break;
+				continue;
+			}
+		}
+		if (gflag) {
+			if (memcmp(&varguid, &matchguid, sizeof(varguid)) == 0) {
+				if (efi_print_var(varname, &varguid, lflag) != CMD_OK)
+					break;
+				continue;
+			}
+		}
+	}
+	free(varname);
+	pager_close();
+
+	return (CMD_OK);
+}
+
+COMMAND_SET(efiset, "efi-set", "set EFI variables", command_efi_set);
+
+static int
+command_efi_set(int argc, char *argv[])
+{
+	char *uuid, *var, *val;
+	CHAR16 wvar[128];
+	EFI_GUID guid;
+	uint32_t status;
+	EFI_STATUS err;
+
+	if (argc != 4) {
+		printf("efi-set uuid var new-value\n");
+		return (CMD_ERROR);
+	}
+	uuid = argv[1];
+	var = argv[2];
+	val = argv[3];
+	uuid_from_string(uuid, (uuid_t *)&guid, &status);
+	if (status != uuid_s_ok) {
+		printf("Invalid uuid %s %d\n", uuid, status);
+		return (CMD_ERROR);
+	}
+	cpy8to16(var, wvar, sizeof(wvar));
+	err = RS->SetVariable(wvar, &guid,
+	    EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS,
+	    strlen(val) + 1, val);
+	if (EFI_ERROR(err)) {
+		printf("Failed to set variable: error %lu\n", EFI_ERROR_CODE(err));
+		return (CMD_ERROR);
+	}
+	return (CMD_OK);
+}
+
+COMMAND_SET(efiunset, "efi-unset", "delete / unset EFI variables", command_efi_unset);
+
+static int
+command_efi_unset(int argc, char *argv[])
+{
+	char *uuid, *var;
+	CHAR16 wvar[128];
+	EFI_GUID guid;
+	uint32_t status;
+	EFI_STATUS err;
+
+	if (argc != 3) {
+		printf("efi-unset uuid var\n");
+		return (CMD_ERROR);
+	}
+	uuid = argv[1];
+	var = argv[2];
+	uuid_from_string(uuid, (uuid_t *)&guid, &status);
+	if (status != uuid_s_ok) {
+		printf("Invalid uuid %s\n", uuid);
+		return (CMD_ERROR);
+	}
+	cpy8to16(var, wvar, sizeof(wvar));
+	err = RS->SetVariable(wvar, &guid, 0, 0, NULL);
+	if (EFI_ERROR(err)) {
+		printf("Failed to unset variable: error %lu\n", EFI_ERROR_CODE(err));
+		return (CMD_ERROR);
+	}
 	return (CMD_OK);
 }
-- 
2.10.1

