From ce703b2b4eb0d98f60666c9bb06e12a07a0436df Mon Sep 17 00:00:00 2001 From: imp Date: Tue, 11 Oct 2016 22:31:45 +0000 Subject: [PATCH] Add efivar(1) to manipulate EFI variables. It uses a similar command line interface to the Linux program, as well as adding a number of useful features to make using it in shell scripts easier (since we don't have a filesystem to fall back on interacting with). Differential Revision: https://reviews.freebsd.org/D8128 Reviewed by: kib@, wblock@, Ganael Laplanche --- usr.sbin/Makefile | 1 + usr.sbin/efivar/Makefile | 8 + usr.sbin/efivar/efivar.8 | 164 ++++++++++++++++++ usr.sbin/efivar/efivar.c | 349 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 522 insertions(+) create mode 100644 usr.sbin/efivar/Makefile create mode 100644 usr.sbin/efivar/efivar.8 create mode 100644 usr.sbin/efivar/efivar.c diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index d9fc76a68294..1a1d0ced868c 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -123,6 +123,7 @@ SUBDIR.${MK_BSNMP}+= bsnmpd SUBDIR.${MK_CTM}+= ctm SUBDIR.${MK_DIALOG}+= tzsetup SUBDIR.${MK_DIALOG}+= bsdconfig +SUBDIR.${MK_EFI}+= efivar SUBDIR.${MK_FLOPPY}+= fdcontrol SUBDIR.${MK_FLOPPY}+= fdformat SUBDIR.${MK_FLOPPY}+= fdread diff --git a/usr.sbin/efivar/Makefile b/usr.sbin/efivar/Makefile new file mode 100644 index 000000000000..9a7ac90c820c --- /dev/null +++ b/usr.sbin/efivar/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +PROG= efivar +MAN= efivar.8 + +LIBADD= efivar + +.include diff --git a/usr.sbin/efivar/efivar.8 b/usr.sbin/efivar/efivar.8 new file mode 100644 index 000000000000..7f62639ee817 --- /dev/null +++ b/usr.sbin/efivar/efivar.8 @@ -0,0 +1,164 @@ +.\" Copyright (c) 2003 Netflix, Inc +.\" 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 REGENTS 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 REGENTS 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$ +.\" +.Dd September 29, 2016 +.Dt EFIVAR 8 +.Os +.Sh NAME +.Nm efivar +.Nd UEFI environemnt variable interaction +.Sh SYNOPSIS +.Nm +.Op Fl abdDHlLNpRtw +.Op Fl n Ar name +.Op Fl f Ar file +.Op Fl -append +.Op Fl -ascii +.Op Fl -attributes +.Op Fl -binary +.Op Fl -delete +.Op Fl -fromfile Ar file +.Op Fl -hex +.Op Fl -list-guids +.Op Fl -list +.Op Fl -name Ar name +.Op Fl -no-name +.Op Fl -print +.Op Fl -print-decimal +.Op Fl -raw-guid +.Op Fl -write +.Ar name Ns Op = Ns Ar value +.Sh DESCRIPTION +This program manages +.Dq Unified Extensible Firmware Interface +.Pq UEFI +environment variables. +UEFI variables have three part: A namespace, a name and a value. +The namespace is a GUID that's self assigned by the group defining the +variables. +The name is a Unicode name for the variable. +The value is binary data. +All Unicode data is presented to the user as UTF-8. +.Pp +The following options are available: +.Bl -tag -width 20m +.It Fl n Ar name Fl -name Ar name +Specify the name of the variable to operate on. +The +.Ar name +argument is the GUID of variable, followed by a dash, followed by the +UEFI variable name. +The GUID may be in numeric format, or may be one of the well known +symbolic name (see +.Fl -list-guids +for a complete list). +.It Fl f Ar file Fl -fromfile Ar file +When writing or appending to a variable, take the data for the +variable's value from +.Ar file +instead of from the command line. +This flag implies +.Fl -write +unless the +.Fl -append +flag is given. +This is not well understood and currently unimplemented. +.It Fl a Fl -append +Append the specified value to the UEFI variable rather than replacing +it.p +.It Fl t Ar attr Fl -attributes Ar attr +Specify, in user hostile hexidecimal, the attributes for this +variable. +See section 7.2 (GetVariable subsection, Related Definitions) of the +UEFI Specification for hex values to use. +.It Fl A Fl -ascii +Display the variable data as modified ascii: All printable characters +are printed, while unprintable characters are rendered as a two-digit +hexadecimal number preceeded by a % character. +.It Fl A Fl -binary +Display the variable data as binary data. +Usually will be used with the +.Fl N +or +.Fl -no-name +flag. +Useful in scripts. +.It Fl D Fl -delete +Delete the specified variable. +May not be used with either the +.Fl -write +or the +.Fl -append +flags. +No +.Ar value +may be specified. +.It Fl H Fl -hex +List variable data as a hex dump. +.It Fl L Fl -list-guids +Lists the well known GUIDs. +The names listed here may be used in place of the numeric GUID values. +These names will replace the numeric GUID values unless +.Fl -raw-guid +flag is specified. +.It Fl l Fl -list +List all the variables. +If the +.Fl -print +flag is also listed, their values will be displayed. +.It Fl N Fl -no-name +Do not display the variable name. +.It Fl p Fl -print +Print the value of the variable. +.It Fl d Fl -print-decimal +Treat the value of the variable as a number and print it as a +decimal. +This is currently unimplemented. +.It Fl R Fl -raw-guid +Do not substitute well known names for GUID numeric values in output. +.It Fl w Fl -write +Write (replace) the variable specified with the value specified. +.It Ar name +Display the +.Ar name +environment variable. +.It Ar name Ns = Ns Ar value +Set the specified +.Ar name +to +.Ar value . +This is not yet implemented. +.Sh COMPATIBILITY +The +.Nm +program is intended to be compatible (strict superset) with a progam +of the same name included in the Red Hat libefivar package. +.Sh SEE ALSO +Appendix A of the UEFI specification has the format for GUIDs. +All GUIDs +.Dq Globally Unique Identifiers +have the format described in RFC 4122. +.El diff --git a/usr.sbin/efivar/efivar.c b/usr.sbin/efivar/efivar.c new file mode 100644 index 000000000000..87b135b50989 --- /dev/null +++ b/usr.sbin/efivar/efivar.c @@ -0,0 +1,349 @@ +/*- + * Copyright (c) 2016 Netflix, Inc. + * 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 NETAPP, INC ``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 NETAPP, INC 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* options descriptor */ +static struct option longopts[] = { + { "append", no_argument, NULL, 'a' }, + { "ascii", no_argument, NULL, 'A' }, + { "attributes", required_argument, NULL, 't' }, + { "binary", no_argument, NULL, 'b' }, + { "delete", no_argument, NULL, 'D' }, + { "fromfile", required_argument, NULL, 'f' }, + { "hex", no_argument, NULL, 'H' }, + { "list-guids", no_argument, NULL, 'L' }, + { "list", no_argument, NULL, 'l' }, + { "name", required_argument, NULL, 'n' }, + { "no-name", no_argument, NULL, 'N' }, + { "print", no_argument, NULL, 'p' }, + { "print-decimal", no_argument, NULL, 'd' }, + { "raw-guid", no_argument, NULL, 'R' }, + { "write", no_argument, NULL, 'w' }, + { NULL, 0, NULL, 0 } +}; + + +static int aflag, Aflag, bflag, dflag, Dflag, Hflag, Nflag, + lflag, Lflag, Rflag, wflag, pflag; +static char *varname; +static u_long attrib = 0x7; + +static void +usage(void) +{ + + errx(1, "efivar [-abdDHlLNpRtw] [-n name] [-f file] [--append] [--ascii]\n" + "\t[--attributes] [--binary] [--delete] [--fromfile file] [--hex]\n" + "\t[--list-guids] [--list] [--name name] [--no-name] [--print]\n" + "\t[--print-decimal] [--raw-guid] [--write] name[=value]"); +} + +static void +breakdown_name(char *name, efi_guid_t *guid, char **vname) +{ + char *cp; + + cp = strrchr(name, '-'); + if (cp == NULL) + errx(1, "Invalid name: %s", name); + *vname = cp + 1; + *cp = '\0'; + if (efi_str_to_guid(name, guid) < 0) + errx(1, "Invalid guid %s", name); +} + +static uint8_t * +get_value(char *val, size_t *datalen) +{ + static char buffer[16*1024]; + + if (val != NULL) { + *datalen = strlen(val); + return ((uint8_t *)val); + } + /* Read from stdin */ + *datalen = sizeof(buffer); + *datalen = read(0, buffer, *datalen); + return ((uint8_t *)buffer); +} + +static void +append_variable(char *name, char *val) +{ + char *vname; + efi_guid_t guid; + size_t datalen; + uint8_t *data; + + breakdown_name(name, &guid, &vname); + data = get_value(val, &datalen); + if (efi_append_variable(guid, vname, data, datalen, attrib) < 0) + err(1, "efi_append_variable"); +} + +static void +delete_variable(char *name) +{ + char *vname; + efi_guid_t guid; + + breakdown_name(name, &guid, &vname); + if (efi_del_variable(guid, vname) < 0) + err(1, "efi_del_variable"); +} + +static void +write_variable(char *name, char *val) +{ + char *vname; + efi_guid_t guid; + size_t datalen; + uint8_t *data; + + breakdown_name(name, &guid, &vname); + data = get_value(val, &datalen); + if (efi_set_variable(guid, vname, data, datalen, attrib, 0) < 0) + err(1, "efi_set_variable"); +} + +static void +asciidump(uint8_t *data, size_t datalen) +{ + size_t i; + int len; + + len = 0; + if (!Nflag) + printf("\n"); + for (i = 0; i < datalen; i++) { + if (isprint(data[i])) { + len++; + if (len > 80) { + len = 0; + printf("\n"); + } + printf("%c", data[i]); + } else { + len +=3; + if (len > 80) { + len = 0; + printf("\n"); + } + printf("%%%02x", data[i]); + } + } + printf("\n"); +} + +static void +hexdump(uint8_t *data, size_t datalen) +{ + size_t i; + + if (!Nflag) + printf("\n"); + for (i = 0; i < datalen; i++) { + if (i % 16 == 0) { + if (i != 0) + printf("\n"); + printf("%04x: ", (int)i); + } + printf("%02x ", data[i]); + } + printf("\n"); +} + +static void +bindump(uint8_t *data, size_t datalen) +{ + write(1, data, datalen); +} + +static void +print_var(efi_guid_t *guid, char *name) +{ + uint32_t att; + uint8_t *data; + size_t datalen; + char *gname; + int rv; + + efi_guid_to_str(guid, &gname); + if (!Nflag) + printf("%s-%s", gname, name); + if (pflag) { + rv = efi_get_variable(*guid, name, &data, &datalen, &att); + + if (rv < 0) + printf("\n --- Error getting value --- %d", errno); + else { + if (Aflag) + asciidump(data, datalen); + else if (bflag) + bindump(data, datalen); + else + hexdump(data, datalen); + } + } + free(gname); + if (!Nflag) + printf("\n"); +} + +static void +print_variable(char *name) +{ + char *vname; + efi_guid_t guid; + + breakdown_name(name, &guid, &vname); + print_var(&guid, vname); +} + +static void +print_variables(void) +{ + int rv; + char *name = NULL; + efi_guid_t *guid = NULL; + + while ((rv = efi_get_next_variable_name(&guid, &name)) > 0) + print_var(guid, name); + + if (rv < 0) + err(1, "Error listing names"); +} + +static void +parse_args(int argc, char **argv) +{ + int ch, i; + + while ((ch = getopt_long(argc, argv, "aAbdDf:HlLNn:pRt:w", + longopts, NULL)) != -1) { + switch (ch) { + case 'a': + aflag++; + break; + case 'A': + Aflag++; + break; + case 'b': + bflag++; + break; + case 'd': + dflag++; + break; + case 'D': + Dflag++; + break; + case 'H': + Hflag++; + break; + case 'l': + lflag++; + break; + case 'L': + Lflag++; + break; + case 'n': + varname = optarg; + break; + case 'N': + Nflag++; + break; + case 'p': + pflag++; + break; + case 'R': + Rflag++; + break; + case 'w': + wflag++; + break; + case 'f': + case 't': + case 0: + errx(1, "unknown or unimplemented option\n"); + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc == 1) + varname = argv[0]; + + if (aflag + Dflag + wflag > 1) { + warnx("Can only use one of -a (--append), " + "-D (--delete) and -w (--write)"); + usage(); + } + + if (aflag + Dflag + wflag > 0 && varname == NULL) { + warnx("Must specify a variable for -a (--append), " + "-D (--delete) or -w (--write)"); + usage(); + } + + if (aflag) + append_variable(varname, NULL); + else if (Dflag) + delete_variable(varname); + else if (wflag) + write_variable(varname, NULL); + else if (varname) { + pflag++; + print_variable(varname); + } else if (argc > 0) { + pflag++; + for (i = 0; i < argc; i++) + print_variable(argv[i]); + } else + print_variables(); +} + +int +main(int argc, char **argv) +{ + + parse_args(argc, argv); +}