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