#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: showvar.c,v 1.5 2025/03/02 01:07:11 riastradh Exp $");
#endif
#include <sys/efiio.h>
#include <sys/ioctl.h>
#include <sys/uuid.h>
#include <assert.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <limits.h>
#include <regex.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <util.h>
#include "efiio.h"
#include "defs.h"
#include "bootvar.h"
#include "certs.h"
#include "devpath.h"
#include "getvars.h"
#include "showvar.h"
#include "utils.h"
#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI __BIT(0)
#define EFI_OS_INDICATIONS_TIMESTAMP_REVOCATION __BIT(1)
#define EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED __BIT(2)
#define EFI_OS_INDICATIONS_FMP_CAPSULE_SUPPORTED __BIT(3)
#define EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED __BIT(4)
#define EFI_OS_INDICATIONS_START_OS_RECOVERY __BIT(5)
#define EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY __BIT(6)
#define EFI_OS_INDICATIONS_JSON_CONFIG_DATA_REFRESH __BIT(7)
#define OS_INDICATIONS_BITS \
"\177\020" \
"b\x0""BootFWui\0" \
"b\x1""TimeStamp\0" \
"b\x2""FileCap\0" \
"b\x3""FMPCap\0" \
"b\x4""CapResVar\0" \
"b\x5""StartOsRecovery\0" \
"b\x6""StartPltformRec\0" \
"b\x7""JSONConfig\0" \
#define EFI_BOOT_OPTION_SUPPORT_KEY __BIT(0)
#define EFI_BOOT_OPTION_SUPPORT_APP __BIT(1)
#define EFI_BOOT_OPTION_SUPPORT_SYSPREP __BIT(4)
#define EFI_BOOT_OPTION_SUPPORT_COUNT __BITS(8,9)
#define BOOT_OPTION_SUPPORT_BITS \
"\177\020" \
"b\x0""Key\0" \
"b\x1""App\0" \
"b\x4""SysPrep\0" \
"f\x8\x2""Count\0"
static int
show_filelist_data(efi_var_t *v, bool dbg)
{
char *dmsg, *path;
path = devpath_parse(v->ev.data, v->ev.datasize, dbg ? &dmsg : NULL);
printf("%s: %s\n", v->name, path);
free(path);
if (dbg) {
printf("%s", dmsg);
free(dmsg);
}
return 0;
}
static int
show_asciiz_data(efi_var_t *v, bool dbg __unused)
{
char *cp, *ep;
printf("%s: ", v->name);
cp = v->ev.data;
ep = cp + v->ev.datasize;
for (; cp < ep; cp += strlen(cp) + 1) {
printf("%s\n", cp);
}
if (cp != ep)
warnx("short asciiz data\n");
return 0;
}
static int
show_array8_data(efi_var_t *v, bool dbg __unused)
{
size_t cnt, i;
uint8_t *array = v->ev.data;
printf("%s: ", v->name);
cnt = v->ev.datasize / sizeof(array[0]);
i = 0;
for (;;) {
printf("%02x", array[i]);
if (++i == cnt) {
printf("\n");
break;
}
printf(",");
}
return 0;
}
static int
show_array16_data(efi_var_t *v, bool dbg __unused)
{
size_t cnt, i;
uint16_t *array = v->ev.data;
printf("%s: ", v->name);
cnt = v->ev.datasize / sizeof(array[0]);
i = 0;
for (;;) {
printf("%04X", array[i]);
if (++i == cnt) {
printf("\n");
break;
}
printf(",");
}
return 0;
}
static int
show_uuid_array_data(efi_var_t *v, bool dbg)
{
size_t cnt, i;
uuid_t *array = v->ev.data;
const char *name;
printf("%s: ", v->name);
cnt = v->ev.datasize / sizeof(array[0]);
for (i = 0; i < cnt; i++) {
name = get_cert_name(&array[i]);
printf("%s%c", name, i + 1 < cnt ? ' ' : '\n');
}
if (dbg) {
for (i = 0; i < cnt; i++) {
printf(" ");
uuid_printf(&array[i]);
name = get_cert_name(&array[i]);
printf(" %s\n", name ? name : "unknown");
}
}
return 0;
}
static int
show_key_data(efi_var_t *v, bool dbg)
{
typedef union {
struct {
uint32_t Revision : 8;
uint32_t ShiftPressed : 1;
uint32_t ControlPressed : 1;
uint32_t AltPressed : 1;
uint32_t LogoPressed : 1;
uint32_t MenuPressed : 1;
uint32_t SysReqPressed : 1;
uint32_t Reserved : 16;
uint32_t InputKeyCount : 2;
#define BOOT_KEY_OPTION_BITS \
"\177\020" \
"f\x00\x08""Rev\0" \
"b\x08""SHFT\0" \
"b\x09""CTRL\0" \
"b\x0a""ALT\0" \
"b\x0b""LOGO\0" \
"b\x0c""MENU\0" \
"b\x0d""SysReq\0" \
\
"f\x1e\x02""KeyCnt\0"
} Options;
uint32_t PackedValue;
} __packed EFI_BOOT_KEY_DATA;
typedef struct {
uint16_t ScanCode;
uint16_t UnicodeChar;
} __packed EFI_INPUT_KEY;
typedef struct _EFI_KEY_OPTION {
EFI_BOOT_KEY_DATA KeyData;
uint32_t BootOptionCrc;
uint16_t BootOption;
EFI_INPUT_KEY Keys[3];
uint16_t DescLocation;
uint8_t UndocData[];
} __packed EFI_KEY_OPTION;
union {
EFI_KEY_OPTION *ko;
uint8_t *bp;
} u = { .bp = v->ev.data, };
char buf[256], c, *cp, *desc;
uint i;
printf("%s:", v->name);
desc = NULL;
if (offsetof(EFI_KEY_OPTION, UndocData) < v->ev.datasize &&
u.ko->DescLocation + sizeof(uint16_t) < v->ev.datasize) {
size_t sz = v->ev.datasize - u.ko->DescLocation;
desc = ucs2_to_utf8((uint16_t *)&u.bp[u.ko->DescLocation],
sz, NULL, NULL);
printf(" %s", desc);
}
snprintb(buf, sizeof(buf), BOOT_KEY_OPTION_BITS,
u.ko->KeyData.PackedValue);
c = '\0';
if ((cp = strchr(buf, ',')) || (cp = strchr(buf, '<'))) {
c = *cp;
*cp = '<';
}
else
cp = buf;
printf("\tBoot%04X \t%s", u.ko->BootOption, cp);
if (c != '\0')
*cp = c;
for (i = 0; i < u.ko->KeyData.Options.InputKeyCount; i++)
printf(" {%04x, %04x}", u.ko->Keys[i].ScanCode,
u.ko->Keys[i].UnicodeChar);
printf("\n");
if (dbg) {
printf(" KeyData: %s\n", buf);
printf(" BootOptionCrc: 0x%08x\n", u.ko->BootOptionCrc);
printf(" BootOption: Boot%04X\n", u.ko->BootOption);
for (i = 0; i < u.ko->KeyData.Options.InputKeyCount; i++) {
printf(" Keys[%u].ScanCode: 0x%04x\n", i,
u.ko->Keys[i].ScanCode);
printf(" Keys[%u].UnicodeChar: 0x%04x\n", i,
u.ko->Keys[i].UnicodeChar);
}
if (desc)
printf(" Desc: %s\n", desc);
}
free(desc);
return 0;
}
static char *
format_optional_data(char *od, size_t sz)
{
char *bp;
size_t i;
bp = emalloc(sz + 1);
for (i = 0; i < sz; i++) {
char c = od[i];
bp[i] = isprint((unsigned char)c) ? c : '.';
}
bp[i] = '\0';
return bp;
}
static int
show_boot_data(efi_var_t *v, uint debug, uint max_namelen)
{
struct {
char *name;
uint32_t Attributes;
char *Description;
devpath_t *devpath;
char *OptionalData;
size_t OptionalDataSize;
} info;
union {
char *cp;
boot_var_t *bb;
} u = { .cp = v->ev.data, };
char *args, *dmsg, *path;
size_t sz;
bool dbg = debug & DEBUG_STRUCT_BIT;
bool verbose = debug & (DEBUG_MASK | DEBUG_VERBOSE_BIT);
memset(&info, 0, sizeof(info));
info.name = v->name;
info.Attributes = u.bb->Attributes;
sz = (v->ev.datasize - sizeof(*u.bb)) / sizeof(uint16_t);
sz = (ucs2nlen(u.bb->Description, sz) + 1) * sizeof(uint16_t);
info.Description = ucs2_to_utf8(u.bb->Description, sz, NULL, NULL);
info.devpath = (devpath_t *)((uint8_t *)u.bb->Description + sz);
info.OptionalData = (char *)info.devpath + u.bb->FilePathListLength;
char *ep = u.cp + v->ev.datasize;
assert(info.OptionalData <= u.cp + v->ev.datasize);
if (info.OptionalData <= u.cp + v->ev.datasize) {
info.OptionalDataSize = (size_t)(ep - info.OptionalData);
}
else {
printf("ARG!!! "
"FilePahList[] extends past end of data by %zd bytes\n",
info.OptionalData - ep);
info.OptionalDataSize = 0;
}
printf("%s%c %-*s", v->name,
IS_ACTIVE(info.Attributes) ? '*' : ' ',
max_namelen, info.Description);
dmsg = NULL;
if (verbose) {
path = devpath_parse(info.devpath, u.bb->FilePathListLength,
dbg ? &dmsg : NULL);
args = format_optional_data(info.OptionalData,
info.OptionalDataSize);
printf("\t%s%s", path, args);
free(args);
free(path);
}
printf("\n");
if (dbg) {
char attr_str[256];
snprintb(attr_str, sizeof(attr_str),
LOAD_OPTION_BITS, info.Attributes);
printf(" Attr: %s\n", attr_str);
printf(" Description: %s\n", info.Description);
assert(dmsg != NULL);
printf("%s", dmsg);
if (info.OptionalDataSize > 0) {
show_data((void *)info.OptionalData,
info.OptionalDataSize, " ExtraData: ");
}
free(dmsg);
}
free(info.Description);
return 0;
}
static int
show_OsIndications_data(efi_var_t *e, bool dbg __unused)
{
uint64_t OsIndications;
char buf[256];
assert(e->ev.datasize == 8);
OsIndications = *(uint64_t *)e->ev.data;
snprintb(buf, sizeof(buf), OS_INDICATIONS_BITS, OsIndications);
printf("%s:\t%s\n", e->name, buf);
return 0;
}
static int
show_BootOptionSupport_data(efi_var_t *e, bool dbg __unused)
{
uint32_t boot_option_support;
char buf[256];
assert(e->ev.datasize == 4);
boot_option_support = *(uint32_t *)e->ev.data;
snprintb(buf, sizeof(buf), BOOT_OPTION_SUPPORT_BITS,
boot_option_support);
printf("%s:\t%s\n", e->name, buf);
return 0;
}
static int
show_Timeout_data(efi_var_t *e, bool dbg __unused)
{
uint16_t *timeout = e->ev.data;
if (e->ev.datasize != 2)
printf("bad timeout datasize: %zu\n", e->ev.datasize);
else
printf("Timeout: %u seconds\n", *timeout);
return 0;
}
PUBLIC int
show_generic_data(efi_var_t *e, uint var_width)
{
char uuid_str[UUID_STR_LEN];
char attr_str[256];
uuid_snprintf(uuid_str, sizeof(uuid_str), &e->ev.vendor);
snprintb(attr_str, sizeof(attr_str), EFI_VAR_ATTR_BITS, e->ev.attrib);
printf("%-*s %s %5zu %s\n", var_width, e->name, uuid_str,
e->ev.datasize, attr_str);
return 0;
}
struct vartbl {
const char *name;
int (*fn)(efi_var_t *, bool);
};
static int
varcmpsortfn(const void *a, const void *b)
{
const struct vartbl *p = a;
const struct vartbl *q = b;
return strcmp(p->name, q->name);
}
static int
varcmpsrchfn(const void *a, const void *b)
{
const struct vartbl *q = b;
return strcmp(a, q->name);
}
PUBLIC int
show_variable(efi_var_t *v, uint debug, uint max_namelen)
{
#define REGEXP_BOOTXXXX "^((Key)|(Boot)|(lBoot)|(Driver)|(SysPrep)|(OsRecovery))[0-9,A-F]{4}$"
static regex_t preg = { .re_magic = 0, };
static struct vartbl *tp, tbl[] = {
{ "AuditMode", show_array8_data, },
{ "BootCurrent", show_array16_data, },
{ "BootNext", show_array16_data, },
{ "BootOptionSupport", show_BootOptionSupport_data, },
{ "BootOrder", show_array16_data, },
{ "BootOrderDefault", show_array16_data, },
{ "db", show_cert_data, },
{ "dbDefault", show_cert_data, },
{ "dbr", show_cert_data, },
{ "dbrDefault", show_cert_data, },
{ "dbt", show_cert_data, },
{ "dbtDefault", show_cert_data, },
{ "dbx", show_cert_data, },
{ "dbxDefault", show_cert_data, },
{ "devdbDefault", show_cert_data, },
{ "ConIn", show_filelist_data, },
{ "ConInDev", show_filelist_data, },
{ "ConOut", show_filelist_data, },
{ "ConOutDev", show_filelist_data, },
{ "DriverOrder", show_array16_data, },
{ "ErrOut", show_filelist_data, },
{ "ErrOutDev", show_filelist_data, },
{ "KEK", show_cert_data, },
{ "KEKDefault", show_cert_data, },
{ "OsIndications", show_OsIndications_data, },
{ "OsIndicationsSupported", show_OsIndications_data, },
{ "PK", show_cert_data, },
{ "PKDefault", show_cert_data, },
{ "PlatformLang", show_asciiz_data, },
{ "PlatformLangCodes", show_asciiz_data, },
{ "ProtectedBootOptions", show_array16_data, },
{ "SecureBoot", show_array8_data, },
{ "SetupMode", show_array8_data, },
{ "SignatureSupport", show_uuid_array_data, },
{ "SysPrepOrder", show_array16_data, },
{ "Timeout", show_Timeout_data, },
{ "VendorKeys", show_array8_data, },
};
bool dbg = debug & DEBUG_STRUCT_BIT;
int rv;
if (preg.re_magic == 0) {
const char *regexp = REGEXP_BOOTXXXX;
if (regcomp(&preg, regexp, REG_EXTENDED) != 0)
err(EXIT_FAILURE, "regcomp: %s", regexp);
qsort(tbl, __arraycount(tbl), sizeof(*tbl), varcmpsortfn);
}
if (debug & DEBUG_EFI_IOC_BIT) {
rv = show_generic_data(v, max_namelen);
if (debug & DEBUG_VERBOSE_BIT)
return rv;
}
if (regexec(&preg, v->name, 0, NULL, 0) == 0) {
if (v->name[0] == 'K')
rv = show_key_data(v, dbg);
else
rv = show_boot_data(v, debug, max_namelen);
}
else {
tp = bsearch(v->name, tbl, __arraycount(tbl), sizeof(*tbl),
varcmpsrchfn);
if (tp != NULL)
rv = tp->fn(v, dbg);
else if(!(debug & DEBUG_EFI_IOC_BIT))
rv = show_generic_data(v, max_namelen);
}
if (debug & DEBUG_DATA_BIT)
show_data(v->ev.data, v->ev.datasize, " ");
return rv;
}