Home | History | Annotate | Line # | Download | only in efi
showvar.c revision 1.3
      1 /* $NetBSD: showvar.c,v 1.3 2025/02/27 17:26:56 christos Exp $ */
      2 
      3 /*
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
     14  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     16  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     19  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     23  * SUCH DAMAGE.
     24  */
     25 
     26 #include <sys/cdefs.h>
     27 #ifndef lint
     28 __RCSID("$NetBSD: showvar.c,v 1.3 2025/02/27 17:26:56 christos Exp $");
     29 #endif /* not lint */
     30 
     31 #include <sys/efiio.h>
     32 #include <sys/ioctl.h>
     33 #include <sys/uuid.h>
     34 
     35 #include <assert.h>
     36 #include <ctype.h>
     37 #include <err.h>
     38 #include <errno.h>
     39 #include <fcntl.h>
     40 #include <getopt.h>
     41 #include <limits.h>
     42 #include <regex.h>
     43 #include <stdarg.h>
     44 #include <stdbool.h>
     45 #include <stddef.h>
     46 #include <stdio.h>
     47 #include <stdlib.h>
     48 #include <string.h>
     49 #include <unistd.h>
     50 #include <util.h>
     51 
     52 #include "efiio.h"
     53 #include "defs.h"
     54 #include "bootvar.h"
     55 #include "certs.h"
     56 #include "devpath.h"
     57 #include "getvars.h"
     58 #include "showvar.h"
     59 #include "utils.h"
     60 
     61 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI		   __BIT(0)
     62 #define EFI_OS_INDICATIONS_TIMESTAMP_REVOCATION 	   __BIT(1)
     63 #define EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED __BIT(2)
     64 #define EFI_OS_INDICATIONS_FMP_CAPSULE_SUPPORTED 	   __BIT(3)
     65 #define EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED	   __BIT(4)
     66 #define EFI_OS_INDICATIONS_START_OS_RECOVERY		   __BIT(5)
     67 #define EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY	   __BIT(6)
     68 #define EFI_OS_INDICATIONS_JSON_CONFIG_DATA_REFRESH	   __BIT(7)
     69 #define OS_INDICATIONS_BITS		\
     70 	"\177\020"			\
     71 	"b\x0""BootFWui\0"		\
     72 	"b\x1""TimeStamp\0"		\
     73 	"b\x2""FileCap\0"		\
     74 	"b\x3""FMPCap\0"		\
     75 	"b\x4""CapResVar\0"		\
     76 	"b\x5""StartOsRecovery\0"	\
     77 	"b\x6""StartPltformRec\0"	\
     78 	"b\x7""JSONConfig\0"		\
     79 
     80 //********************************************************
     81 // Boot Option Attributes
     82 //********************************************************
     83 #define EFI_BOOT_OPTION_SUPPORT_KEY	__BIT(0)	// 0x00000001
     84 #define EFI_BOOT_OPTION_SUPPORT_APP	__BIT(1)	// 0x00000002
     85 #define EFI_BOOT_OPTION_SUPPORT_SYSPREP	__BIT(4)	// 0x00000010
     86 #define EFI_BOOT_OPTION_SUPPORT_COUNT	__BITS(8,9)	// 0x00000300
     87 // All values 0x00000200-0x00001F00 are reserved
     88 #define BOOT_OPTION_SUPPORT_BITS \
     89 	"\177\020"		\
     90 	"b\x0""Key\0"		\
     91 	"b\x1""App\0"		\
     92 	"b\x4""SysPrep\0"	\
     93 	"f\x8\x2""Count\0"
     94 
     95 /************************************************************************/
     96 
     97 static int
     98 show_filelist_data(efi_var_t *v, bool dbg)
     99 {
    100 	char *dmsg, *path;
    101 
    102 	path = devpath_parse(v->ev.data, v->ev.datasize, dbg ? &dmsg : NULL);
    103 	printf("%s: %s\n", v->name, path);
    104 	free(path);
    105 	if (dbg) {
    106 		printf("%s", dmsg);
    107 		free(dmsg);
    108 	}
    109 	return 0;
    110 }
    111 
    112 static int
    113 show_asciiz_data(efi_var_t *v, bool dbg __unused)
    114 {
    115 	char *cp, *ep;
    116 
    117 	printf("%s: ", v->name);
    118 
    119 	cp = v->ev.data;
    120 	ep = cp + v->ev.datasize;
    121 	for (/*EMPTY*/; cp < ep; cp += strlen(cp) + 1) {
    122 		printf("%s\n", cp);
    123 	}
    124 	if (cp != ep)
    125 		warnx("short asciiz data\n");
    126 	return 0;
    127 }
    128 
    129 static int
    130 show_array8_data(efi_var_t *v, bool dbg __unused)
    131 {
    132 	size_t cnt, i;
    133 	uint8_t *array = v->ev.data;
    134 
    135 	printf("%s: ", v->name);
    136 
    137 	cnt = v->ev.datasize / sizeof(array[0]);
    138 	i = 0;
    139 	for (;;) {
    140 		printf("%02x", array[i]);
    141 		if (++i == cnt) {
    142 			printf("\n");
    143 			break;
    144 		}
    145 		printf(",");
    146 	}
    147 	return 0;
    148 }
    149 
    150 static int
    151 show_array16_data(efi_var_t *v, bool dbg __unused)
    152 {
    153 	size_t cnt, i;
    154 	uint16_t *array = v->ev.data;
    155 
    156 	printf("%s: ", v->name);
    157 
    158 	cnt = v->ev.datasize / sizeof(array[0]);
    159 	i = 0;
    160 	for (;;) {
    161 		printf("%04X", array[i]);
    162 		if (++i == cnt) {
    163 			printf("\n");
    164 			break;
    165 		}
    166 		printf(",");
    167 	}
    168 	return 0;
    169 }
    170 
    171 static int
    172 show_uuid_array_data(efi_var_t *v, bool dbg)
    173 {
    174 	size_t cnt, i;
    175 	uuid_t *array = v->ev.data;
    176 	const char *name;
    177 
    178 	printf("%s: ", v->name);
    179 
    180 	cnt = v->ev.datasize / sizeof(array[0]);
    181 
    182 	for (i = 0; i < cnt; i++) {
    183 		name = get_cert_name(&array[i]);
    184 		printf("%s%c", name, i + 1 < cnt ? ' ' : '\n');
    185 	}
    186 
    187 	if (dbg) {
    188 		for (i = 0; i < cnt; i++) {
    189 			printf("  ");
    190 			uuid_printf(&array[i]);
    191 			name = get_cert_name(&array[i]);
    192 			printf("  %s\n", name ? name : "unknown");
    193 		}
    194 	}
    195 	return 0;
    196 }
    197 
    198 /************************************************************************/
    199 
    200 static int
    201 show_key_data(efi_var_t *v, bool dbg)
    202 {
    203 	typedef union {
    204 		struct {
    205 			uint32_t	Revision	: 8;
    206 			uint32_t	ShiftPressed	: 1;
    207 			uint32_t	ControlPressed	: 1;
    208 			uint32_t	AltPressed	: 1;
    209 			uint32_t	LogoPressed	: 1;
    210 			uint32_t	MenuPressed	: 1;
    211 			uint32_t	SysReqPressed	: 1;
    212 			uint32_t	Reserved	: 16;
    213 			uint32_t	InputKeyCount	: 2;
    214 #define BOOT_KEY_OPTION_BITS \
    215 	"\177\020"		\
    216 	"f\x00\x08""Rev\0"	\
    217 	"b\x08""SHFT\0"		\
    218 	"b\x09""CTRL\0"		\
    219 	"b\x0a""ALT\0"		\
    220 	"b\x0b""LOGO\0"		\
    221 	"b\x0c""MENU\0"		\
    222 	"b\x0d""SysReq\0"	\
    223      /* "f\x0e\x10""Rsvd\0" */	\
    224 	"f\x1e\x02""KeyCnt\0"
    225 		} Options;
    226 		uint32_t		PackedValue;
    227 	} __packed EFI_BOOT_KEY_DATA;
    228 	typedef struct {
    229 		uint16_t		ScanCode;
    230 		uint16_t		UnicodeChar;
    231 	} __packed EFI_INPUT_KEY;
    232 	typedef struct _EFI_KEY_OPTION {
    233 		EFI_BOOT_KEY_DATA	KeyData;
    234 		uint32_t		BootOptionCrc;
    235 		uint16_t		BootOption;
    236 		EFI_INPUT_KEY		Keys[3];
    237 		uint16_t		DescLocation; /* XXX: a guess!  Not documented */
    238 		uint8_t			UndocData[];
    239 	} __packed EFI_KEY_OPTION;
    240 	union {
    241 		EFI_KEY_OPTION *ko;
    242 		uint8_t        *bp;
    243 	} u = { .bp = v->ev.data, };
    244 	char buf[256], c, *cp, *desc;
    245 
    246 	printf("%s:", v->name);
    247 
    248 	/*
    249 	 * Note: DescLocation is not documented in the UEFI spec, so
    250 	 * do some sanity checking before using it.
    251 	 */
    252 	desc = NULL;
    253 	if (offsetof(EFI_KEY_OPTION, UndocData) < v->ev.datasize &&
    254 	    u.ko->DescLocation + sizeof(uint16_t) < v->ev.datasize) {
    255 		size_t sz = v->ev.datasize - u.ko->DescLocation;
    256 		desc = ucs2_to_utf8((uint16_t *)&u.bp[u.ko->DescLocation],
    257 		    sz, NULL, NULL);
    258 		printf(" %s", desc);
    259 	}
    260 
    261 	/*
    262 	 * Parse the KeyData
    263 	 */
    264 	snprintb(buf, sizeof(buf), BOOT_KEY_OPTION_BITS,
    265 	    u.ko->KeyData.PackedValue);
    266 
    267 	/*
    268 	 * Skip over the raw value
    269 	 */
    270 	c = '\0';
    271 	if ((cp = strchr(buf, ',')) || (cp = strchr(buf, '<'))) {
    272 		c = *cp;
    273 		*cp = '<';
    274 	}
    275 	else
    276 		cp = buf;
    277 
    278 	printf("\tBoot%04X \t%s", u.ko->BootOption, cp);
    279 
    280 	if (c != '\0')
    281 		*cp = c;	/* restore the buffer */
    282 
    283 	for (uint i = 0; i < u.ko->KeyData.Options.InputKeyCount; i++)
    284 		printf(" {%04x, %04x}", u.ko->Keys[i].ScanCode,
    285 		    u.ko->Keys[i].UnicodeChar);
    286 	printf("\n");
    287 
    288 	if (dbg) {
    289 		printf("  KeyData: %s\n", buf);
    290 		printf("  BootOptionCrc: 0x%08x\n", u.ko->BootOptionCrc);
    291 		printf("  BootOption:    Boot%04X\n", u.ko->BootOption);
    292 		for (uint i = 0; i < u.ko->KeyData.Options.InputKeyCount; i++) {
    293 			printf("  Keys[%u].ScanCode:    0x%04x\n", i,
    294 			    u.ko->Keys[i].ScanCode);
    295 			printf("  Keys[%u].UnicodeChar: 0x%04x\n", i,
    296 			    u.ko->Keys[i].UnicodeChar);
    297 		}
    298 		if (desc)
    299 			printf("  Desc: %s\n", desc);
    300 	}
    301 	free(desc);
    302 	return 0;
    303 }
    304 
    305 /************************************************************************/
    306 
    307 static char *
    308 format_optional_data(char *od, size_t sz)
    309 {
    310 	char *bp;
    311 	size_t i;
    312 
    313 	bp = emalloc(sz + 1);
    314 
    315 	for (i = 0; i < sz; i++) {
    316 		char c = od[i];
    317 		bp[i] = isprint((unsigned char)c) ? c : '.';
    318 	}
    319 	bp[i] = '\0';
    320 	return bp;
    321 }
    322 
    323 static int
    324 show_boot_data(efi_var_t *v, uint debug, uint max_namelen)
    325 {
    326 	struct {
    327 		char *name;
    328 		uint32_t Attributes;
    329 		char *Description;
    330 		devpath_t *devpath;
    331 		char *OptionalData;
    332 		size_t OptionalDataSize;
    333 	} info;
    334 	union {
    335 		char *cp;
    336 		boot_var_t *bb;
    337 	} u = { .cp = v->ev.data, };
    338 	char *args, *dmsg, *path;
    339 	size_t sz;
    340 	bool dbg = debug & DEBUG_STRUCT_BIT;
    341 	bool verbose = debug & (DEBUG_MASK | DEBUG_VERBOSE_BIT);
    342 
    343 	memset(&info, 0, sizeof(info));
    344 	info.name = v->name;
    345 	info.Attributes = u.bb->Attributes;
    346 	sz = (v->ev.datasize - sizeof(*u.bb)) / sizeof(uint16_t);
    347 	sz = (ucs2nlen(u.bb->Description, sz) + 1) * sizeof(uint16_t);
    348 	info.Description = ucs2_to_utf8(u.bb->Description, sz, NULL, NULL);
    349 	info.devpath = (devpath_t *)((uint8_t *)u.bb->Description + sz);
    350 	info.OptionalData = (char *)info.devpath + u.bb->FilePathListLength;
    351 
    352 	char *ep = u.cp + v->ev.datasize;
    353 
    354 	assert(info.OptionalData <= u.cp + v->ev.datasize);
    355 
    356 	if (info.OptionalData <= u.cp + v->ev.datasize) {
    357 		info.OptionalDataSize = (size_t)(ep - info.OptionalData);
    358 	}
    359 	else {
    360 		printf("ARG!!! "
    361 		    "FilePahList[] extends past end of data by %zd bytes\n",
    362 		    info.OptionalData - ep);
    363 		info.OptionalDataSize = 0;
    364 	}
    365 	printf("%s%c %-*s", v->name,
    366 	    IS_ACTIVE(info.Attributes) ? '*' : ' ',
    367 	    max_namelen, info.Description);
    368 
    369 	dmsg = NULL;
    370 	if (verbose) {
    371 		path = devpath_parse(info.devpath, u.bb->FilePathListLength,
    372 		    dbg ? &dmsg : NULL);
    373 
    374 		args = format_optional_data(info.OptionalData,
    375 		    info.OptionalDataSize);
    376 
    377 		printf("\t%s%s", path, args);/* XXX: make this conditional on verbose? */
    378 		free(args);
    379 		free(path);
    380 	}
    381 
    382 	printf("\n");
    383 
    384 	if (dbg) {
    385 		char attr_str[256];
    386 
    387 		snprintb(attr_str, sizeof(attr_str),
    388 		    LOAD_OPTION_BITS, info.Attributes);
    389 		printf("  Attr: %s\n", attr_str);
    390 		printf("  Description: %s\n", info.Description);
    391 		assert(dmsg != NULL);
    392 		printf("%s", dmsg);
    393 		if (info.OptionalDataSize > 0) {
    394 			show_data((void *)info.OptionalData,
    395 			    info.OptionalDataSize, "  ExtraData: ");
    396 		}
    397 		free(dmsg);
    398 	}
    399 
    400 	free(info.Description);
    401 	return 0;
    402 }
    403 
    404 /************************************************************************/
    405 
    406 static int
    407 show_OsIndications_data(efi_var_t *e, bool dbg __unused)
    408 {
    409 	uint64_t OsIndications;
    410 	char buf[256];
    411 
    412 	assert(e->ev.datasize == 8);
    413 	OsIndications = *(uint64_t *)e->ev.data;
    414 	snprintb(buf, sizeof(buf), OS_INDICATIONS_BITS, OsIndications);
    415 	printf("%s:\t%s\n", e->name, buf);
    416 	return 0;
    417 }
    418 
    419 static int
    420 show_BootOptionSupport_data(efi_var_t *e, bool dbg __unused)
    421 {
    422 	uint32_t boot_option_support;
    423 	char buf[256];
    424 
    425 	assert(e->ev.datasize == 4);
    426 	boot_option_support = *(uint32_t *)e->ev.data;
    427 	snprintb(buf, sizeof(buf), BOOT_OPTION_SUPPORT_BITS,
    428 	    boot_option_support);
    429 	printf("%s:\t%s\n", e->name, buf);
    430 	return 0;
    431 }
    432 
    433 static int
    434 show_Timeout_data(efi_var_t *e, bool dbg __unused)
    435 {
    436 	uint16_t *timeout = e->ev.data;
    437 
    438 	if (e->ev.datasize != 2)
    439 		printf("bad timeout datasize: %zu\n", e->ev.datasize);
    440 	else
    441 		printf("Timeout: %u seconds\n", *timeout);
    442 	return 0;
    443 }
    444 
    445 PUBLIC int
    446 show_generic_data(efi_var_t *e, uint var_width)
    447 {
    448 	char uuid_str[UUID_STR_LEN];
    449 	char attr_str[256];
    450 
    451 	uuid_snprintf(uuid_str, sizeof(uuid_str), &e->ev.vendor);
    452 	snprintb(attr_str, sizeof(attr_str), EFI_VAR_ATTR_BITS, e->ev.attrib);
    453 	printf("%-*s  %s %5zu %s\n", var_width, e->name, uuid_str,
    454 	    e->ev.datasize, attr_str);
    455 
    456 	return 0;
    457 }
    458 
    459 /************************************************************************/
    460 
    461 struct vartbl {
    462 	const char *name;
    463 	int (*fn)(efi_var_t *, bool);
    464 };
    465 
    466 static int
    467 varcmpsortfn(const void *a, const void *b)
    468 {
    469 	const struct vartbl *p = a;
    470 	const struct vartbl *q = b;
    471 
    472 	return strcmp(p->name, q->name);
    473 }
    474 
    475 static int
    476 varcmpsrchfn(const void *a, const void *b)
    477 {
    478 	const struct vartbl *q = b;
    479 
    480 	return strcmp(a, q->name);
    481 }
    482 
    483 PUBLIC int
    484 show_variable(efi_var_t *v, uint debug, uint max_namelen)
    485 {
    486 #define REGEXP_BOOTXXXX	"^((Key)|(Boot)|(lBoot)|(Driver)|(SysPrep)|(OsRecovery))[0-9,A-F]{4}$"
    487 	static regex_t preg = { .re_magic = 0, };
    488 	static struct vartbl *tp, tbl[] = {
    489 		{ "AuditMode",			show_array8_data, },
    490 		{ "BootCurrent",		show_array16_data, },
    491 		{ "BootNext",			show_array16_data, },
    492 		{ "BootOptionSupport",		show_BootOptionSupport_data, },
    493 		{ "BootOrder",			show_array16_data, },
    494 		{ "BootOrderDefault",		show_array16_data, },
    495 		{ "db",				show_cert_data, },
    496 		{ "dbDefault",			show_cert_data, },
    497 		{ "dbr",			show_cert_data, },
    498 		{ "dbrDefault",			show_cert_data, },
    499 		{ "dbt",			show_cert_data, },
    500 		{ "dbtDefault",			show_cert_data, },
    501 		{ "dbx",			show_cert_data, },
    502 		{ "dbxDefault",			show_cert_data, },
    503 		{ "devdbDefault",		show_cert_data, },
    504 		{ "ConIn",			show_filelist_data, },
    505 		{ "ConInDev",			show_filelist_data, },
    506 		{ "ConOut",			show_filelist_data, },
    507 		{ "ConOutDev",			show_filelist_data, },
    508 		{ "DriverOrder",		show_array16_data, },
    509 		{ "ErrOut",			show_filelist_data, },
    510 		{ "ErrOutDev",			show_filelist_data, },
    511 		{ "KEK",			show_cert_data, },
    512 		{ "KEKDefault",			show_cert_data, },
    513 		{ "OsIndications",		show_OsIndications_data, },
    514 		{ "OsIndicationsSupported",	show_OsIndications_data, },
    515 		{ "PK",				show_cert_data, },
    516 		{ "PKDefault",			show_cert_data, },
    517 		{ "PlatformLang",		show_asciiz_data, },
    518 		{ "PlatformLangCodes",		show_asciiz_data, },
    519 		{ "ProtectedBootOptions",	show_array16_data, },
    520 		{ "SecureBoot",			show_array8_data, },
    521 		{ "SetupMode",			show_array8_data, },
    522 		{ "SignatureSupport",		show_uuid_array_data, },
    523 		{ "SysPrepOrder",		show_array16_data, },
    524 		{ "Timeout",			show_Timeout_data, },
    525 		{ "VendorKeys",			show_array8_data, },
    526 	};
    527 	bool dbg = debug & DEBUG_STRUCT_BIT;
    528 	int rv;
    529 
    530 	if (preg.re_magic == 0) {
    531 		const char *regexp = REGEXP_BOOTXXXX;
    532 		if (regcomp(&preg, regexp, REG_EXTENDED) != 0)
    533 			err(EXIT_FAILURE, "regcomp: %s", regexp);
    534 
    535 		qsort(tbl, __arraycount(tbl), sizeof(*tbl), varcmpsortfn);
    536 	}
    537 
    538 	if (debug & DEBUG_EFI_IOC_BIT) {
    539 		rv = show_generic_data(v, max_namelen);
    540 		if (debug & DEBUG_VERBOSE_BIT)
    541 			return rv;
    542 	}
    543 
    544 	if (regexec(&preg, v->name, 0, NULL, 0) == 0) { /* matched */
    545 		if (v->name[0] == 'K')
    546 			rv = show_key_data(v, dbg);
    547 		else
    548 			rv = show_boot_data(v, debug, max_namelen);
    549 	}
    550 	else {
    551 		tp = bsearch(v->name, tbl, __arraycount(tbl), sizeof(*tbl),
    552 		    varcmpsrchfn);
    553 
    554 		if (tp != NULL)
    555 			rv = tp->fn(v, dbg);
    556 		else if(!(debug & DEBUG_EFI_IOC_BIT))
    557 			rv = show_generic_data(v, max_namelen);
    558 	}
    559 	if (debug & DEBUG_DATA_BIT)
    560 		show_data(v->ev.data, v->ev.datasize, "  ");
    561 
    562 	return rv;
    563 }
    564