Home | History | Annotate | Line # | Download | only in efi
showvar.c revision 1.1
      1 /* $NetBSD: showvar.c,v 1.1 2025/02/24 13:47:57 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.1 2025/02/24 13:47:57 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 /*###113 [lint] warning parameter 'dbg' unused in function 'show_asciiz_data' [231]%%%*/
    114 show_asciiz_data(efi_var_t *v, bool dbg)
    115 {
    116 	char *cp, *ep;
    117 
    118 	printf("%s: ", v->name);
    119 
    120 	cp = v->ev.data;
    121 	ep = cp + v->ev.datasize;
    122 	for (/*EMPTY*/; cp < ep; cp += strlen(cp) + 1) {
    123 		printf("%s\n", cp);
    124 	}
    125 	if (cp != ep)
    126 		warnx("short asciiz data\n");
    127 	return 0;
    128 }
    129 
    130 static int
    131 /*###130 [lint] warning parameter 'dbg' unused in function 'show_array8_data' [231]%%%*/
    132 show_array8_data(efi_var_t *v, bool dbg)
    133 {
    134 	size_t cnt, i;
    135 	uint8_t *array = v->ev.data;
    136 
    137 	printf("%s: ", v->name);
    138 
    139 	cnt = v->ev.datasize / sizeof(array[0]);
    140 	i = 0;
    141 	for (;;) {
    142 		printf("%02x", array[i]);
    143 		if (++i == cnt) {
    144 			printf("\n");
    145 			break;
    146 		}
    147 		printf(",");
    148 	}
    149 	return 0;
    150 }
    151 
    152 static int
    153 /*###151 [lint] warning parameter 'dbg' unused in function 'show_array16_data' [231]%%%*/
    154 show_array16_data(efi_var_t *v, bool dbg)
    155 {
    156 	size_t cnt, i;
    157 	uint16_t *array = v->ev.data;
    158 
    159 	printf("%s: ", v->name);
    160 
    161 	cnt = v->ev.datasize / sizeof(array[0]);
    162 	i = 0;
    163 	for (;;) {
    164 		printf("%04X", array[i]);
    165 		if (++i == cnt) {
    166 			printf("\n");
    167 			break;
    168 		}
    169 		printf(",");
    170 	}
    171 	return 0;
    172 }
    173 
    174 static int
    175 show_uuid_array_data(efi_var_t *v, bool dbg)
    176 {
    177 	size_t cnt, i;
    178 	uuid_t *array = v->ev.data;
    179 	const char *name;
    180 
    181 	printf("%s: ", v->name);
    182 
    183 	cnt = v->ev.datasize / sizeof(array[0]);
    184 
    185 	for (i = 0; i < cnt; i++) {
    186 		name = get_cert_name(&array[i]);
    187 		printf("%s%c", name, i + 1 < cnt ? ' ' : '\n');
    188 	}
    189 
    190 	if (dbg) {
    191 		for (i = 0; i < cnt; i++) {
    192 			printf("  ");
    193 			uuid_printf(&array[i]);
    194 			name = get_cert_name(&array[i]);
    195 			printf("  %s\n", name ? name : "unknown");
    196 		}
    197 	}
    198 	return 0;
    199 }
    200 
    201 /************************************************************************/
    202 
    203 static int
    204 show_key_data(efi_var_t *v, bool dbg)
    205 {
    206 	typedef union {
    207 		struct {
    208 			uint32_t	Revision	: 8;
    209 			uint32_t	ShiftPressed	: 1;
    210 			uint32_t	ControlPressed	: 1;
    211 			uint32_t	AltPressed	: 1;
    212 			uint32_t	LogoPressed	: 1;
    213 			uint32_t	MenuPressed	: 1;
    214 			uint32_t	SysReqPressed	: 1;
    215 			uint32_t	Reserved	: 16;
    216 			uint32_t	InputKeyCount	: 2;
    217 #define BOOT_KEY_OPTION_BITS \
    218 	"\177\020"		\
    219 	"f\x00\x08""Rev\0"	\
    220 	"b\x08""SHFT\0"		\
    221 	"b\x09""CTRL\0"		\
    222 	"b\x0a""ALT\0"		\
    223 	"b\x0b""LOGO\0"		\
    224 	"b\x0c""MENU\0"		\
    225 	"b\x0d""SysReq\0"	\
    226      /* "f\x0e\x10""Rsvd\0" */	\
    227 	"f\x1e\x02""KeyCnt\0"
    228 		} Options;
    229 		uint32_t		PackedValue;
    230 	} __packed EFI_BOOT_KEY_DATA;
    231 	typedef struct {
    232 		uint16_t		ScanCode;
    233 		uint16_t		UnicodeChar;
    234 	} __packed EFI_INPUT_KEY;
    235 	typedef struct _EFI_KEY_OPTION {
    236 		EFI_BOOT_KEY_DATA	KeyData;
    237 		uint32_t		BootOptionCrc;
    238 		uint16_t		BootOption;
    239 		EFI_INPUT_KEY		Keys[3];
    240 		uint16_t		DescLocation; /* XXX: a guess!  Not documented */
    241 		uint8_t			UndocData[];
    242 	} __packed EFI_KEY_OPTION;
    243 	union {
    244 		EFI_KEY_OPTION *ko;
    245 		uint8_t        *bp;
    246 	} u = { .bp = v->ev.data, };
    247 	char buf[256], c, *cp, *desc;
    248 
    249 	printf("%s:", v->name);
    250 
    251 	/*
    252 	 * Note: DescLocation is not documented in the UEFI spec, so
    253 	 * do some sanity checking before using it.
    254 	 */
    255 	desc = NULL;
    256 	if (offsetof(EFI_KEY_OPTION, UndocData) < v->ev.datasize &&
    257 	    u.ko->DescLocation + sizeof(uint16_t) < v->ev.datasize) {
    258 		size_t sz = v->ev.datasize - u.ko->DescLocation;
    259 		desc = ucs2_to_utf8((uint16_t *)&u.bp[u.ko->DescLocation],
    260 		    sz, NULL, NULL);
    261 		printf(" %s", desc);
    262 	}
    263 
    264 	/*
    265 	 * Parse the KeyData
    266 	 */
    267 	snprintb(buf, sizeof(buf), BOOT_KEY_OPTION_BITS,
    268 	    u.ko->KeyData.PackedValue);
    269 
    270 	/*
    271 	 * Skip over the raw value
    272 	 */
    273 	c = '\0';
    274 	if ((cp = strchr(buf, ',')) || (cp = strchr(buf, '<'))) {
    275 		c = *cp;
    276 		*cp = '<';
    277 	}
    278 	else
    279 		cp = buf;
    280 
    281 	printf("\tBoot%04X \t%s", u.ko->BootOption, cp);
    282 
    283 	if (c != '\0')
    284 		*cp = c;	/* restore the buffer */
    285 
    286 	for (uint i = 0; i < u.ko->KeyData.Options.InputKeyCount; i++)
    287 		printf(" {%04x, %04x}", u.ko->Keys[i].ScanCode,
    288 		    u.ko->Keys[i].UnicodeChar);
    289 	printf("\n");
    290 
    291 	if (dbg) {
    292 		printf("  KeyData: %s\n", buf);
    293 		printf("  BootOptionCrc: 0x%08x\n", u.ko->BootOptionCrc);
    294 		printf("  BootOption:    Boot%04X\n", u.ko->BootOption);
    295 		for (uint i = 0; i < u.ko->KeyData.Options.InputKeyCount; i++) {
    296 			printf("  Keys[%u].ScanCode:    0x%04x\n", i,
    297 			    u.ko->Keys[i].ScanCode);
    298 			printf("  Keys[%u].UnicodeChar: 0x%04x\n", i,
    299 			    u.ko->Keys[i].UnicodeChar);
    300 		}
    301 		if (desc)
    302 			printf("  Desc: %s\n", desc);
    303 	}
    304 	free(desc);
    305 	return 0;
    306 }
    307 
    308 /************************************************************************/
    309 
    310 static char *
    311 format_optional_data(char *od, size_t sz)
    312 {
    313 	char *bp;
    314 	size_t i;
    315 
    316 	bp = emalloc(sz + 1);
    317 
    318 	for (i = 0; i < sz; i++) {
    319 		char c = od[i];
    320 /*###317 [lint] warning argument to 'function from <ctype.h>' must be cast to 'unsigned char', not to 'int' [342]%%%*/
    321 		bp[i] = isprint((int)c) ? c : '.';
    322 	}
    323 	bp[i] = '\0';
    324 	return bp;
    325 }
    326 
    327 static int
    328 show_boot_data(efi_var_t *v, int debug, uint max_namelen)
    329 {
    330 	struct {
    331 		char *name;
    332 		uint32_t Attributes;
    333 		char *Description;
    334 		devpath_t *devpath;
    335 		char *OptionalData;
    336 		size_t OptionalDataSize;
    337 	} info;
    338 	union {
    339 		char *cp;
    340 		boot_var_t *bb;
    341 	} u = { .cp = v->ev.data, };
    342 	char *args, *dmsg, *path;
    343 	size_t sz;
    344 	bool dbg = debug & DEBUG_STRUCT_BIT;
    345 	bool verbose = debug & (DEBUG_MASK | DEBUG_VERBOSE_BIT);
    346 
    347 	memset(&info, 0, sizeof(info));
    348 	info.name = v->name;
    349 	info.Attributes = u.bb->Attributes;
    350 	sz = (v->ev.datasize - sizeof(*u.bb)) / sizeof(uint16_t);
    351 	sz = (ucs2nlen(u.bb->Description, sz) + 1) * sizeof(uint16_t);
    352 	info.Description = ucs2_to_utf8(u.bb->Description, sz, NULL, NULL);
    353 	info.devpath = (devpath_t *)((uint8_t *)u.bb->Description + sz);
    354 	info.OptionalData = (char *)info.devpath + u.bb->FilePathListLength;
    355 
    356 	char *ep = u.cp + v->ev.datasize;
    357 
    358 	assert(info.OptionalData <= u.cp + v->ev.datasize);
    359 
    360 	if (info.OptionalData <= u.cp + v->ev.datasize) {
    361 		info.OptionalDataSize = (size_t)(ep - info.OptionalData);
    362 	}
    363 	else {
    364 		printf("ARG!!! "
    365 		    "FilePahList[] extends past end of data by %zd bytes\n",
    366 		    info.OptionalData - ep);
    367 		info.OptionalDataSize = 0;
    368 	}
    369 	printf("%s%c %-*s", v->name,
    370 	    IS_ACTIVE(info.Attributes) ? '*' : ' ',
    371 	    max_namelen, info.Description);
    372 
    373 	dmsg = NULL;
    374 	if (verbose) {
    375 		path = devpath_parse(info.devpath, u.bb->FilePathListLength,
    376 		    dbg ? &dmsg : NULL);
    377 
    378 		args = format_optional_data(info.OptionalData,
    379 		    info.OptionalDataSize);
    380 
    381 		printf("\t%s%s", path, args);/* XXX: make this conditional on verbose? */
    382 		free(args);
    383 		free(path);
    384 	}
    385 
    386 	printf("\n");
    387 
    388 	if (dbg) {
    389 		char attr_str[256];
    390 
    391 		snprintb(attr_str, sizeof(attr_str),
    392 		    LOAD_OPTION_BITS, info.Attributes);
    393 		printf("  Attr: %s\n", attr_str);
    394 		printf("  Description: %s\n", info.Description);
    395 		assert(dmsg != NULL);
    396 		printf("%s", dmsg);
    397 		if (info.OptionalDataSize > 0) {
    398 			show_data((void *)info.OptionalData,
    399 			    info.OptionalDataSize, "  ExtraData: ");
    400 		}
    401 		free(dmsg);
    402 	}
    403 
    404 	free(info.Description);
    405 	return 0;
    406 }
    407 
    408 /************************************************************************/
    409 
    410 static int
    411 /*###407 [lint] warning parameter 'dbg' unused in function 'show_OsIndications_data' [231]%%%*/
    412 show_OsIndications_data(efi_var_t *e, bool dbg)
    413 {
    414 	uint64_t OsIndications;
    415 	char buf[256];
    416 
    417 	assert(e->ev.datasize == 8);
    418 	OsIndications = *(uint64_t *)e->ev.data;
    419 	snprintb(buf, sizeof(buf), OS_INDICATIONS_BITS, OsIndications);
    420 	printf("%s:\t%s\n", e->name, buf);
    421 	return 0;
    422 }
    423 
    424 static int
    425 /*###420 [lint] warning parameter 'dbg' unused in function 'show_BootOptionSupport_data' [231]%%%*/
    426 show_BootOptionSupport_data(efi_var_t *e, bool dbg)
    427 {
    428 	uint32_t boot_option_support;
    429 	char buf[256];
    430 
    431 	assert(e->ev.datasize == 4);
    432 	boot_option_support = *(uint32_t *)e->ev.data;
    433 	snprintb(buf, sizeof(buf), BOOT_OPTION_SUPPORT_BITS,
    434 	    boot_option_support);
    435 	printf("%s:\t%s\n", e->name, buf);
    436 	return 0;
    437 }
    438 
    439 static int
    440 /*###434 [lint] warning parameter 'dbg' unused in function 'show_Timeout_data' [231]%%%*/
    441 show_Timeout_data(efi_var_t *e, bool dbg)
    442 {
    443 	uint16_t *timeout = e->ev.data;
    444 
    445 	if (e->ev.datasize != 2)
    446 		printf("bad timeout datasize: %zu\n", e->ev.datasize);
    447 	else
    448 		printf("Timeout: %u seconds\n", *timeout);
    449 	return 0;
    450 }
    451 
    452 PUBLIC int
    453 show_generic_data(efi_var_t *e, uint var_width)
    454 {
    455 	char uuid_str[UUID_STR_LEN];
    456 	char attr_str[256];
    457 
    458 	uuid_snprintf(uuid_str, sizeof(uuid_str), &e->ev.vendor);
    459 	snprintb(attr_str, sizeof(attr_str), EFI_VAR_ATTR_BITS, e->ev.attrib);
    460 	printf("%-*s  %s %5zu %s\n", var_width, e->name, uuid_str,
    461 	    e->ev.datasize, attr_str);
    462 
    463 	return 0;
    464 }
    465 
    466 /************************************************************************/
    467 
    468 struct vartbl {
    469 	const char *name;
    470 	int (*fn)(efi_var_t *, bool);
    471 };
    472 
    473 static int
    474 varcmpsortfn(const void *a, const void *b)
    475 {
    476 	const struct vartbl *p = a;
    477 	const struct vartbl *q = b;
    478 
    479 	return strcmp(p->name, q->name);
    480 }
    481 
    482 static int
    483 varcmpsrchfn(const void *a, const void *b)
    484 {
    485 	const struct vartbl *q = b;
    486 
    487 	return strcmp(a, q->name);
    488 }
    489 
    490 PUBLIC int
    491 show_variable(efi_var_t *v, int debug, uint max_namelen)
    492 {
    493 #define REGEXP_BOOTXXXX	"^((Key)|(Boot)|(lBoot)|(Driver)|(SysPrep)|(OsRecovery))[0-9,A-F]{4}$"
    494 	static regex_t preg = { .re_magic = 0, };
    495 	static struct vartbl *tp, tbl[] = {
    496 		{ "AuditMode",			show_array8_data, },
    497 		{ "BootCurrent",		show_array16_data, },
    498 		{ "BootNext",			show_array16_data, },
    499 		{ "BootOptionSupport",		show_BootOptionSupport_data, },
    500 		{ "BootOrder",			show_array16_data, },
    501 		{ "BootOrderDefault",		show_array16_data, },
    502 		{ "db",				show_cert_data, },
    503 		{ "dbDefault",			show_cert_data, },
    504 		{ "dbr",			show_cert_data, },
    505 		{ "dbrDefault",			show_cert_data, },
    506 		{ "dbt",			show_cert_data, },
    507 		{ "dbtDefault",			show_cert_data, },
    508 		{ "dbx",			show_cert_data, },
    509 		{ "dbxDefault",			show_cert_data, },
    510 		{ "devdbDefault",		show_cert_data, },
    511 		{ "ConIn",			show_filelist_data, },
    512 		{ "ConInDev",			show_filelist_data, },
    513 		{ "ConOut",			show_filelist_data, },
    514 		{ "ConOutDev",			show_filelist_data, },
    515 		{ "DriverOrder",		show_array16_data, },
    516 		{ "ErrOut",			show_filelist_data, },
    517 		{ "ErrOutDev",			show_filelist_data, },
    518 		{ "KEK",			show_cert_data, },
    519 		{ "KEKDefault",			show_cert_data, },
    520 		{ "OsIndications",		show_OsIndications_data, },
    521 		{ "OsIndicationsSupported",	show_OsIndications_data, },
    522 		{ "PK",				show_cert_data, },
    523 		{ "PKDefault",			show_cert_data, },
    524 		{ "PlatformLang",		show_asciiz_data, },
    525 		{ "PlatformLangCodes",		show_asciiz_data, },
    526 		{ "ProtectedBootOptions",	show_array16_data, },
    527 		{ "SecureBoot",			show_array8_data, },
    528 		{ "SetupMode",			show_array8_data, },
    529 		{ "SignatureSupport",		show_uuid_array_data, },
    530 		{ "SysPrepOrder",		show_array16_data, },
    531 		{ "Timeout",			show_Timeout_data, },
    532 		{ "VendorKeys",			show_array8_data, },
    533 	};
    534 	bool dbg = debug & DEBUG_STRUCT_BIT;
    535 	int rv;
    536 
    537 	if (preg.re_magic == 0) {
    538 		const char *regexp = REGEXP_BOOTXXXX;
    539 		if (regcomp(&preg, regexp, REG_EXTENDED) != 0)
    540 			err(EXIT_FAILURE, "regcomp: %s", regexp);
    541 
    542 		qsort(tbl, __arraycount(tbl), sizeof(*tbl), varcmpsortfn);
    543 	}
    544 
    545 	if (debug & DEBUG_EFI_IOC_BIT) {
    546 		rv = show_generic_data(v, max_namelen);
    547 		if (debug & DEBUG_VERBOSE_BIT)
    548 			return rv;
    549 	}
    550 
    551 	if (regexec(&preg, v->name, 0, NULL, 0) == 0) { /* matched */
    552 		if (v->name[0] == 'K')
    553 			rv = show_key_data(v, dbg);
    554 		else
    555 			rv = show_boot_data(v, debug, max_namelen);
    556 	}
    557 	else {
    558 		tp = bsearch(v->name, tbl, __arraycount(tbl), sizeof(*tbl),
    559 		    varcmpsrchfn);
    560 
    561 		if (tp != NULL)
    562 			rv = tp->fn(v, dbg);
    563 		else if(!(debug & DEBUG_EFI_IOC_BIT))
    564 			rv = show_generic_data(v, max_namelen);
    565 	}
    566 	if (debug & DEBUG_DATA_BIT)
    567 		show_data(v->ev.data, v->ev.datasize, "  ");
    568 
    569 	return rv;
    570 }
    571