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