diff --git a/usr.sbin/efivar/Makefile b/usr.sbin/efivar/Makefile index 821a8e41b8d3..89d453eeb962 100644 --- a/usr.sbin/efivar/Makefile +++ b/usr.sbin/efivar/Makefile @@ -1,11 +1,13 @@ # $FreeBSD$ PROG= efivar MAN= efivar.8 LIBADD= efivar +SRCS= efivar.c efiutil.c + EFIBOOT=${SRCTOP}/sys/boot/efi CFLAGS+= -I${EFIBOOT}/include .include diff --git a/usr.sbin/efivar/efiutil.c b/usr.sbin/efivar/efiutil.c new file mode 100644 index 000000000000..20efab54f1a3 --- /dev/null +++ b/usr.sbin/efivar/efiutil.c @@ -0,0 +1,179 @@ +/*- + * Copyright (c) 2017 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 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "efiutil.h" +#include "efichar.h" +#include + +/* + * Dump the data as ASCII data, which is a pretty + * printed form + */ +void +asciidump(uint8_t *data, size_t datalen) +{ + size_t i; + int len; + + len = 0; + 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"); +} + +void +utf8dump(uint8_t *data, size_t datalen) +{ + char *utf8 = NULL; + efi_char *ucs2; + + /* + * NUL terminate the string. Not all strings need it, but some + * do and an extra NUL won't change what's printed. + */ + ucs2 = malloc(datalen + sizeof(efi_char)); + memcpy(ucs2, data, datalen); + ucs2[datalen / sizeof(efi_char)] = 0; + ucs2_to_utf8(ucs2, &utf8); + printf("%s\n", utf8); + free(utf8); + free(ucs2); +} + +void +hexdump(uint8_t *data, size_t datalen) +{ + size_t i; + + 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"); +} + +void +bindump(uint8_t *data, size_t datalen) +{ + write(1, data, datalen); +} + +#define LOAD_OPTION_ACTIVE 1 + +void +efi_print_load_option(uint8_t *data, size_t datalen, int Aflag, int bflag, int uflag) +{ + uint8_t *ep = data + datalen; + uint8_t *walker = data; + uint32_t attr; + uint16_t fplen; + efi_char *descr; + efidp dp, edp; + char *str; + char buf[1024]; + int len; + void *opt; + int optlen; + + if (datalen < sizeof(attr) + sizeof(fplen) + sizeof(efi_char)) + return; + // First 4 bytes are attribute flags + attr = le32dec(walker); + walker += sizeof(attr); + // Next two bytes are length of the file paths + fplen = le16dec(walker); + walker += sizeof(fplen); + // Next we have a 0 terminated UCS2 string that we know to be aligned + descr = (efi_char *)(intptr_t)(void *)walker; + len = ucs2len(descr); // XXX need to sanity check that len < (datalen - (ep - walker) / 2) + walker += (len + 1) * sizeof(efi_char); + if (walker > ep) + return; + // Now we have fplen bytes worth of file path stuff + dp = (efidp)walker; + walker += fplen; + if (walker > ep) + return; + edp = (efidp)walker; + // Everything left is the binary option args + opt = walker; + optlen = ep - walker; + // We got to here, everything is good + printf("%c ", attr & LOAD_OPTION_ACTIVE ? '*' : ' '); + ucs2_to_utf8(descr, &str); + printf("%s", str); + free(str); + while (dp < edp) { + efidp_format_device_path(buf, sizeof(buf), dp, + (intptr_t)(void *)edp - (intptr_t)(void *)dp); + dp = (efidp)((char *)dp + efidp_size(dp)); + printf(" %s\n", buf); + } + if (optlen == 0) + return; + printf("Options: "); + if (Aflag) + asciidump(opt, optlen); + else if (bflag) + bindump(opt, optlen); + else if (uflag) + utf8dump(opt, optlen); + else + hexdump(opt, optlen); +} diff --git a/usr.sbin/efivar/efiutil.h b/usr.sbin/efivar/efiutil.h new file mode 100644 index 000000000000..143fd464f6ca --- /dev/null +++ b/usr.sbin/efivar/efiutil.h @@ -0,0 +1,38 @@ +/*- + * Copyright (c) 2017 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 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$ + */ + +/* + * differnt routines to dump data. + */ + +void asciidump(uint8_t *data, size_t datalen); +void bindump(uint8_t *data, size_t datalen); +void efi_print_load_option(uint8_t *, size_t, int, int, int); +void hexdump(uint8_t *data, size_t datalen); +void utf8dump(uint8_t *data, size_t datalen); + diff --git a/usr.sbin/efivar/efivar.c b/usr.sbin/efivar/efivar.c index 3c4ada22eba3..d156f78f1851 100644 --- a/usr.sbin/efivar/efivar.c +++ b/usr.sbin/efivar/efivar.c @@ -1,428 +1,362 @@ /*- * 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 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include +#include "efiutil.h" #include "efichar.h" /* 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' }, { "device", no_argument, NULL, 'd' }, { "device-path", no_argument, NULL, 'd' }, { "fromfile", required_argument, NULL, 'f' }, { "guid", no_argument, NULL, 'g' }, { "hex", no_argument, NULL, 'H' }, { "list-guids", no_argument, NULL, 'L' }, { "list", no_argument, NULL, 'l' }, + { "load-option", no_argument, NULL, 'O' }, { "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' }, { "utf8", no_argument, NULL, 'u' }, { "write", no_argument, NULL, 'w' }, { NULL, 0, NULL, 0 } }; static int aflag, Aflag, bflag, dflag, Dflag, gflag, Hflag, Nflag, - lflag, Lflag, Rflag, wflag, pflag, uflag; + lflag, Lflag, Rflag, wflag, pflag, uflag, load_opt_flag; static char *varname; static u_long attrib = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS; static void usage(void) { - errx(1, "efivar [-abdDHlLNpRtw] [-n name] [-f file] [--append] [--ascii]\n" + errx(1, "efivar [-abdDHlLNpRtuw] [-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]"); + "\t[--list-guids] [--list] [--load-option] [--name name] [--no-name]\n" + "\t[--print] [--print-decimal] [--raw-guid] [--utf8] [--write]\n" + "\tname[=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_name_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 -utf8dump(uint8_t *data, size_t datalen) -{ - char *utf8 = NULL; - efi_char *ucs2; - - /* - * NUL terminate the string. Not all strings need it, but some - * do and an extra NUL won't change what's printed. - */ - ucs2 = malloc(datalen + sizeof(efi_char)); - memcpy(ucs2, data, datalen); - ucs2[datalen / sizeof(efi_char)] = 0; - ucs2_to_utf8(ucs2, &utf8); - if (!Nflag) - printf("\n"); - printf("%s\n", utf8); - free(utf8); - free(ucs2); -} - -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 devpath_dump(uint8_t *data, size_t datalen) { char buffer[1024]; efidp_format_device_path(buffer, sizeof(buffer), (const_efidp)data, datalen); if (!Nflag) printf(": "); printf("%s\n", buffer); } static void pretty_guid(efi_guid_t *guid, char **gname) { char *pretty = NULL; if (gflag) efi_guid_to_name(guid, &pretty); if (pretty == NULL) efi_guid_to_str(guid, gname); else *gname = pretty; } static void print_var(efi_guid_t *guid, char *name) { uint32_t att; uint8_t *data; size_t datalen; char *gname; int rv; pretty_guid(guid, &gname); if (pflag) { rv = efi_get_variable(*guid, name, &data, &datalen, &att); if (rv < 0) err(1, "%s-%s", gname, name); if (!Nflag) - printf("%s-%s", gname, name); - if (Aflag) + printf("%s-%s\n", gname, name); + if (load_opt_flag) + efi_print_load_option(data, datalen, Aflag, bflag, uflag); + else if (Aflag) asciidump(data, datalen); else if (uflag) utf8dump(data, datalen); else if (bflag) bindump(data, datalen); else if (dflag) devpath_dump(data, datalen); else hexdump(data, datalen); } else { printf("%s-%s", gname, name); } 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 print_known_guid(void) { struct uuid_table *tbl; int i, n; n = efi_known_guid(&tbl); for (i = 0; i < n; i++) printf("%s %s\n", tbl[i].uuid_str, tbl[i].name); } static void parse_args(int argc, char **argv) { int ch, i; - while ((ch = getopt_long(argc, argv, "aAbdDf:gHlLNn:pRt:w", + while ((ch = getopt_long(argc, argv, "aAbdDf:gHlLNn:OpRt: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 'g': gflag++; break; case 'H': Hflag++; break; case 'l': lflag++; break; case 'L': Lflag++; break; case 'n': varname = optarg; break; case 'N': Nflag++; break; + case 'O': + load_opt_flag++; + break; case 'p': pflag++; break; case 'R': Rflag++; break; case 't': attrib = strtoul(optarg, NULL, 16); break; case 'u': uflag++; break; case 'w': wflag++; break; case 'f': 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 (Lflag) print_known_guid(); 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); }