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