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 @@ -5,6 +5,8 @@ MAN= efivar.8 LIBADD= efivar +SRCS= efivar.c efiutil.c + EFIBOOT=${SRCTOP}/sys/boot/efi CFLAGS+= -I${EFIBOOT}/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 @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include "efiutil.h" #include "efichar.h" /* options descriptor */ @@ -54,6 +55,7 @@ static struct option longopts[] = { { "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' }, @@ -66,7 +68,7 @@ static struct option longopts[] = { 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; @@ -74,10 +76,11 @@ 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 @@ -148,80 +151,6 @@ write_variable(char *name, char *val) 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) { @@ -265,8 +194,10 @@ print_var(efi_guid_t *guid, char *name) 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); @@ -324,7 +255,7 @@ 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': @@ -360,6 +291,9 @@ parse_args(int argc, char **argv) case 'N': Nflag++; break; + case 'O': + load_opt_flag++; + break; case 'p': pflag++; break;