Home | History | Annotate | Line # | Download | only in efi
      1 /* $NetBSD: showvar.c,v 1.5 2025/03/02 01:07:11 riastradh 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.5 2025/03/02 01:07:11 riastradh 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 	uint i;
    246 
    247 	printf("%s:", v->name);
    248 
    249 	/*
    250 	 * Note: DescLocation is not documented in the UEFI spec, so
    251 	 * do some sanity checking before using it.
    252 	 */
    253 	desc = NULL;
    254 	if (offsetof(EFI_KEY_OPTION, UndocData) < v->ev.datasize &&
    255 	    u.ko->DescLocation + sizeof(uint16_t) < v->ev.datasize) {
    256 		size_t sz = v->ev.datasize - u.ko->DescLocation;
    257 		desc = ucs2_to_utf8((uint16_t *)&u.bp[u.ko->DescLocation],
    258 		    sz, NULL, NULL);
    259 		printf(" %s", desc);
    260 	}
    261 
    262 	/*
    263 	 * Parse the KeyData
    264 	 */
    265 	snprintb(buf, sizeof(buf), BOOT_KEY_OPTION_BITS,
    266 	    u.ko->KeyData.PackedValue);
    267 
    268 	/*
    269 	 * Skip over the raw value
    270 	 */
    271 	c = '\0';
    272 	if ((cp = strchr(buf, ',')) || (cp = strchr(buf, '<'))) {
    273 		c = *cp;
    274 		*cp = '<';
    275 	}
    276 	else
    277 		cp = buf;
    278 
    279 	printf("\tBoot%04X \t%s", u.ko->BootOption, cp);
    280 
    281 	if (c != '\0')
    282 		*cp = c;	/* restore the buffer */
    283 
    284 	for (i = 0; i < u.ko->KeyData.Options.InputKeyCount; i++)
    285 		printf(" {%04x, %04x}", u.ko->Keys[i].ScanCode,
    286 		    u.ko->Keys[i].UnicodeChar);
    287 	printf("\n");
    288 
    289 	if (dbg) {
    290 		printf("  KeyData: %s\n", buf);
    291 		printf("  BootOptionCrc: 0x%08x\n", u.ko->BootOptionCrc);
    292 		printf("  BootOption:    Boot%04X\n", u.ko->BootOption);
    293 		for (i = 0; i < u.ko->KeyData.Options.InputKeyCount; i++) {
    294 			printf("  Keys[%u].ScanCode:    0x%04x\n", i,
    295 			    u.ko->Keys[i].ScanCode);
    296 			printf("  Keys[%u].UnicodeChar: 0x%04x\n", i,
    297 			    u.ko->Keys[i].UnicodeChar);
    298 		}
    299 		if (desc)
    300 			printf("  Desc: %s\n", desc);
    301 	}
    302 	free(desc);
    303 	return 0;
    304 }
    305 
    306 /************************************************************************/
    307 
    308 static char *
    309 format_optional_data(char *od, size_t sz)
    310 {
    311 	char *bp;
    312 	size_t i;
    313 
    314 	bp = emalloc(sz + 1);
    315 
    316 	for (i = 0; i < sz; i++) {
    317 		char c = od[i];
    318 		bp[i] = isprint((unsigned char)c) ? c : '.';
    319 	}
    320 	bp[i] = '\0';
    321 	return bp;
    322 }
    323 
    324 static int
    325 show_boot_data(efi_var_t *v, uint debug, uint max_namelen)
    326 {
    327 	struct {
    328 		char *name;
    329 		uint32_t Attributes;
    330 		char *Description;
    331 		devpath_t *devpath;
    332 		char *OptionalData;
    333 		size_t OptionalDataSize;
    334 	} info;
    335 	union {
    336 		char *cp;
    337 		boot_var_t *bb;
    338 	} u = { .cp = v->ev.data, };
    339 	char *args, *dmsg, *path;
    340 	size_t sz;
    341 	bool dbg = debug & DEBUG_STRUCT_BIT;
    342 	bool verbose = debug & (DEBUG_MASK | DEBUG_VERBOSE_BIT);
    343 
    344 	memset(&info, 0, sizeof(info));
    345 	info.name = v->name;
    346 	info.Attributes = u.bb->Attributes;
    347 	sz = (v->ev.datasize - sizeof(*u.bb)) / sizeof(uint16_t);
    348 	sz = (ucs2nlen(u.bb->Description, sz) + 1) * sizeof(uint16_t);
    349 	info.Description = ucs2_to_utf8(u.bb->Description, sz, NULL, NULL);
    350 	info.devpath = (devpath_t *)((uint8_t *)u.bb->Description + sz);
    351 	info.OptionalData = (char *)info.devpath + u.bb->FilePathListLength;
    352 
    353 	char *ep = u.cp + v->ev.datasize;
    354 
    355 	assert(info.OptionalData <= u.cp + v->ev.datasize);
    356 
    357 	if (info.OptionalData <= u.cp + v->ev.datasize) {
    358 		info.OptionalDataSize = (size_t)(ep - info.OptionalData);
    359 	}
    360 	else {
    361 		printf("ARG!!! "
    362 		    "FilePahList[] extends past end of data by %zd bytes\n",
    363 		    info.OptionalData - ep);
    364 		info.OptionalDataSize = 0;
    365 	}
    366 	printf("%s%c %-*s", v->name,
    367 	    IS_ACTIVE(info.Attributes) ? '*' : ' ',
    368 	    max_namelen, info.Description);
    369 
    370 	dmsg = NULL;
    371 	if (verbose) {
    372 		path = devpath_parse(info.devpath, u.bb->FilePathListLength,
    373 		    dbg ? &dmsg : NULL);
    374 
    375 		args = format_optional_data(info.OptionalData,
    376 		    info.OptionalDataSize);
    377 
    378 		printf("\t%s%s", path, args);/* XXX: make this conditional on verbose? */
    379 		free(args);
    380 		free(path);
    381 	}
    382 
    383 	printf("\n");
    384 
    385 	if (dbg) {
    386 		char attr_str[256];
    387 
    388 		snprintb(attr_str, sizeof(attr_str),
    389 		    LOAD_OPTION_BITS, info.Attributes);
    390 		printf("  Attr: %s\n", attr_str);
    391 		printf("  Description: %s\n", info.Description);
    392 		assert(dmsg != NULL);
    393 		printf("%s", dmsg);
    394 		if (info.OptionalDataSize > 0) {
    395 			show_data((void *)info.OptionalData,
    396 			    info.OptionalDataSize, "  ExtraData: ");
    397 		}
    398 		free(dmsg);
    399 	}
    400 
    401 	free(info.Description);
    402 	return 0;
    403 }
    404 
    405 /************************************************************************/
    406 
    407 static int
    408 show_OsIndications_data(efi_var_t *e, bool dbg __unused)
    409 {
    410 	uint64_t OsIndications;
    411 	char buf[256];
    412 
    413 	assert(e->ev.datasize == 8);
    414 	OsIndications = *(uint64_t *)e->ev.data;
    415 	snprintb(buf, sizeof(buf), OS_INDICATIONS_BITS, OsIndications);
    416 	printf("%s:\t%s\n", e->name, buf);
    417 	return 0;
    418 }
    419 
    420 static int
    421 show_BootOptionSupport_data(efi_var_t *e, bool dbg __unused)
    422 {
    423 	uint32_t boot_option_support;
    424 	char buf[256];
    425 
    426 	assert(e->ev.datasize == 4);
    427 	boot_option_support = *(uint32_t *)e->ev.data;
    428 	snprintb(buf, sizeof(buf), BOOT_OPTION_SUPPORT_BITS,
    429 	    boot_option_support);
    430 	printf("%s:\t%s\n", e->name, buf);
    431 	return 0;
    432 }
    433 
    434 static int
    435 show_Timeout_data(efi_var_t *e, bool dbg __unused)
    436 {
    437 	uint16_t *timeout = e->ev.data;
    438 
    439 	if (e->ev.datasize != 2)
    440 		printf("bad timeout datasize: %zu\n", e->ev.datasize);
    441 	else
    442 		printf("Timeout: %u seconds\n", *timeout);
    443 	return 0;
    444 }
    445 
    446 PUBLIC int
    447 show_generic_data(efi_var_t *e, uint var_width)
    448 {
    449 	char uuid_str[UUID_STR_LEN];
    450 	char attr_str[256];
    451 
    452 	uuid_snprintf(uuid_str, sizeof(uuid_str), &e->ev.vendor);
    453 	snprintb(attr_str, sizeof(attr_str), EFI_VAR_ATTR_BITS, e->ev.attrib);
    454 	printf("%-*s  %s %5zu %s\n", var_width, e->name, uuid_str,
    455 	    e->ev.datasize, attr_str);
    456 
    457 	return 0;
    458 }
    459 
    460 /************************************************************************/
    461 
    462 struct vartbl {
    463 	const char *name;
    464 	int (*fn)(efi_var_t *, bool);
    465 };
    466 
    467 static int
    468 varcmpsortfn(const void *a, const void *b)
    469 {
    470 	const struct vartbl *p = a;
    471 	const struct vartbl *q = b;
    472 
    473 	return strcmp(p->name, q->name);
    474 }
    475 
    476 static int
    477 varcmpsrchfn(const void *a, const void *b)
    478 {
    479 	const struct vartbl *q = b;
    480 
    481 	return strcmp(a, q->name);
    482 }
    483 
    484 PUBLIC int
    485 show_variable(efi_var_t *v, uint debug, uint max_namelen)
    486 {
    487 #define REGEXP_BOOTXXXX	"^((Key)|(Boot)|(lBoot)|(Driver)|(SysPrep)|(OsRecovery))[0-9,A-F]{4}$"
    488 	static regex_t preg = { .re_magic = 0, };
    489 	static struct vartbl *tp, tbl[] = {
    490 		{ "AuditMode",			show_array8_data, },
    491 		{ "BootCurrent",		show_array16_data, },
    492 		{ "BootNext",			show_array16_data, },
    493 		{ "BootOptionSupport",		show_BootOptionSupport_data, },
    494 		{ "BootOrder",			show_array16_data, },
    495 		{ "BootOrderDefault",		show_array16_data, },
    496 		{ "db",				show_cert_data, },
    497 		{ "dbDefault",			show_cert_data, },
    498 		{ "dbr",			show_cert_data, },
    499 		{ "dbrDefault",			show_cert_data, },
    500 		{ "dbt",			show_cert_data, },
    501 		{ "dbtDefault",			show_cert_data, },
    502 		{ "dbx",			show_cert_data, },
    503 		{ "dbxDefault",			show_cert_data, },
    504 		{ "devdbDefault",		show_cert_data, },
    505 		{ "ConIn",			show_filelist_data, },
    506 		{ "ConInDev",			show_filelist_data, },
    507 		{ "ConOut",			show_filelist_data, },
    508 		{ "ConOutDev",			show_filelist_data, },
    509 		{ "DriverOrder",		show_array16_data, },
    510 		{ "ErrOut",			show_filelist_data, },
    511 		{ "ErrOutDev",			show_filelist_data, },
    512 		{ "KEK",			show_cert_data, },
    513 		{ "KEKDefault",			show_cert_data, },
    514 		{ "OsIndications",		show_OsIndications_data, },
    515 		{ "OsIndicationsSupported",	show_OsIndications_data, },
    516 		{ "PK",				show_cert_data, },
    517 		{ "PKDefault",			show_cert_data, },
    518 		{ "PlatformLang",		show_asciiz_data, },
    519 		{ "PlatformLangCodes",		show_asciiz_data, },
    520 		{ "ProtectedBootOptions",	show_array16_data, },
    521 		{ "SecureBoot",			show_array8_data, },
    522 		{ "SetupMode",			show_array8_data, },
    523 		{ "SignatureSupport",		show_uuid_array_data, },
    524 		{ "SysPrepOrder",		show_array16_data, },
    525 		{ "Timeout",			show_Timeout_data, },
    526 		{ "VendorKeys",			show_array8_data, },
    527 	};
    528 	bool dbg = debug & DEBUG_STRUCT_BIT;
    529 	int rv;
    530 
    531 	if (preg.re_magic == 0) {
    532 		const char *regexp = REGEXP_BOOTXXXX;
    533 		if (regcomp(&preg, regexp, REG_EXTENDED) != 0)
    534 			err(EXIT_FAILURE, "regcomp: %s", regexp);
    535 
    536 		qsort(tbl, __arraycount(tbl), sizeof(*tbl), varcmpsortfn);
    537 	}
    538 
    539 	if (debug & DEBUG_EFI_IOC_BIT) {
    540 		rv = show_generic_data(v, max_namelen);
    541 		if (debug & DEBUG_VERBOSE_BIT)
    542 			return rv;
    543 	}
    544 
    545 	if (regexec(&preg, v->name, 0, NULL, 0) == 0) { /* matched */
    546 		if (v->name[0] == 'K')
    547 			rv = show_key_data(v, dbg);
    548 		else
    549 			rv = show_boot_data(v, debug, max_namelen);
    550 	}
    551 	else {
    552 		tp = bsearch(v->name, tbl, __arraycount(tbl), sizeof(*tbl),
    553 		    varcmpsrchfn);
    554 
    555 		if (tp != NULL)
    556 			rv = tp->fn(v, dbg);
    557 		else if(!(debug & DEBUG_EFI_IOC_BIT))
    558 			rv = show_generic_data(v, max_namelen);
    559 	}
    560 	if (debug & DEBUG_DATA_BIT)
    561 		show_data(v->ev.data, v->ev.datasize, "  ");
    562 
    563 	return rv;
    564 }
    565