showvar.c revision 1.5 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