#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: setvar.c,v 1.4 2025/03/02 01:07:11 riastradh Exp $");
#endif
#include <sys/efiio.h>
#include <assert.h>
#include <err.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uuid.h>
#include "efiio.h"
#include "defs.h"
#include "bootvar.h"
#include "setvar.h"
#include "utils.h"
#define BOOTNEXT "BootNext"
#define BOOTORDER "BootOrder"
#define TIMEOUT "Timeout"
static size_t
parse_csus(const char *csus, uint16_t **array, int base)
{
uint16_t *data;
const char *p;
char *q;
size_t n;
n = 1;
for (p = csus; *(p = strchrnul(p, ',')) != '\0'; p++)
n++;
data = emalloc(n * sizeof(*data));
n = 0;
p = csus;
for (;;) {
data[n++] = strtous(p, &q, base);
if (*q != ',' && *q != '\0')
errx(EXIT_FAILURE, "invalid CSUS string:"
" '%s' (at %s)\n",
csus, p);
if (*q == '\0')
break;
p = q + 1;
}
*array = data;
return n;
}
PUBLIC int
prefix_bootorder(int fd, const char *target, const char *csus,
uint16_t bootnum)
{
struct efi_var_ioc ev;
char *targetorder;
uint16_t *data;
size_t datasize, n;
int rv;
easprintf(&targetorder, "%sOrder", target);
ev = get_variable(fd, targetorder,
&EFI_GLOBAL_VARIABLE,
EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS);
free(targetorder);
if (csus != NULL)
n = parse_csus(csus, &data, 16);
else {
data = emalloc(sizeof(*data));
*data = bootnum;
n = 1;
}
datasize = ev.datasize + n * sizeof(*data);
data = erealloc(data, datasize);
memcpy(data + n, ev.data, ev.datasize);
ev.data = data;
ev.datasize = datasize;
rv = set_variable(fd, &ev);
free(ev.data);
if (rv == -1)
err(EXIT_FAILURE, "prefix_bootorder: %s %s", target, csus);
return rv;
}
PUBLIC int
remove_bootorder(int fd, const char *target, const char *csus,
uint16_t bootnum)
{
struct efi_var_ioc ev;
char *targetorder;
uint16_t *data, *dp, *rmlist;
size_t j, n, r;
int rv;
easprintf(&targetorder, "%sOrder", target);
ev = get_variable(fd, targetorder, &EFI_GLOBAL_VARIABLE, 0);
free(targetorder);
if (ev.datasize == 0)
return 0;
if (csus != NULL)
r = parse_csus(csus, &rmlist, 16);
else {
rmlist = emalloc(sizeof(*rmlist));
*rmlist = bootnum;
r = 1;
}
data = emalloc(ev.datasize);
dp = ev.data;
n = ev.datasize / sizeof(*data);
j = 0;
for (size_t i = 0; i < n; i++) {
size_t k;
for (k = 0; k < r; k++) {
if (dp[i] == rmlist[k])
break;
}
if (k == r)
data[j++] = dp[i];
}
ev.data = data;
ev.datasize = j * sizeof(*data);
rv = set_variable(fd, &ev);
free(ev.data);
if (rv == -1)
err(EXIT_FAILURE, "remove_bootorder: %s %u", target, bootnum);
return rv;
}
PUBLIC int
set_bootorder(int fd, const char *target, const char *bootorder)
{
struct efi_var_ioc ev;
char *targetorder;
uint16_t *data;
size_t n;
int rv;
easprintf(&targetorder, "%sOrder", target);
efi_var_init(&ev, targetorder,
&EFI_GLOBAL_VARIABLE,
EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS);
free(targetorder);
n = parse_csus(bootorder, &data, 16);
ev.data = data;
ev.datasize = n * sizeof(*data);
rv = set_variable(fd, &ev);
if (rv == -1)
warn("set_variable");
free(ev.name);
return rv;
}
static int
delete_variable(int fd, const char *varname)
{
struct efi_var_ioc ev;
int rv;
efi_var_init(&ev, varname,
&EFI_GLOBAL_VARIABLE,
EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS);
rv = set_variable(fd, &ev);
free(ev.name);
return rv;
}
PUBLIC int
del_bootorder(int fd, const char *target __unused)
{
int rv;
rv = delete_variable(fd, BOOTORDER);
if (rv == -1)
warn("delete_variable");
return rv;
}
PUBLIC int
del_bootorder_dups(int fd, const char *target)
{
struct efi_var_ioc ev;
char *targetorder;
uint16_t *data, *dp;
size_t i, j, k, n;
int rv;
easprintf(&targetorder, "%sOrder", target);
ev = get_variable(fd, targetorder, &EFI_GLOBAL_VARIABLE, 0);
free(targetorder);
if (ev.datasize == 0)
return 0;
data = emalloc(ev.datasize);
dp = ev.data;
n = ev.datasize / sizeof(*data);
j = 0;
for (i = 0; i < n; i++) {
for (k = 0; k < j; k++) {
if (data[k] == dp[i])
break;
}
if (k == j)
data[j++] = dp[i];
}
ev.data = data;
ev.datasize = j * sizeof(*data);
rv = set_variable(fd, &ev);
free(ev.data);
if (rv == -1)
err(EXIT_FAILURE, "del_bootorder_dups");
return rv;
}
PUBLIC int
set_bootnext(int fd, uint16_t bootnum)
{
struct efi_var_ioc ev;
int rv;
efi_var_init(&ev, BOOTNEXT,
&EFI_GLOBAL_VARIABLE,
EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS);
ev.data = &bootnum;
ev.datasize = sizeof(bootnum);
printf("set BootNext = Boot%04X\n", bootnum);
rv = set_variable(fd, &ev);
if (rv == -1)
warn("set_variable");
free(ev.name);
return rv;
}
PUBLIC int
del_bootnext(int fd)
{
int rv;
rv = delete_variable(fd, BOOTNEXT);
if (rv == -1)
warn("delete_variable");
return rv;
}
PUBLIC int
set_timeout(int fd, uint16_t timeout)
{
struct efi_var_ioc ev;
int rv;
efi_var_init(&ev, TIMEOUT,
&EFI_GLOBAL_VARIABLE,
EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS);
ev.data = &timeout;
ev.datasize = sizeof(timeout);
printf("set Timeout = %u seconds\n", timeout);
rv = set_variable(fd, &ev);
if (rv == -1)
warn("set_variable");
free(ev.name);
return rv;
}
PUBLIC int
del_timeout(int fd)
{
int rv;
rv = delete_variable(fd, TIMEOUT);
if (rv == -1)
warn("del_variable");
return rv;
}
PUBLIC int
set_active(int efi_fd, const char *target, uint16_t bootnum, bool active)
{
struct efi_var_ioc ev;
boot_var_t *bb;
char *name;
int rv;
easprintf(&name, "%s%04X", target, bootnum);
ev = get_variable(efi_fd, name, &EFI_GLOBAL_VARIABLE, 0);
free(name);
bb = ev.data;
if (active)
bb->Attributes |= LOAD_OPTION_ACTIVE;
else
bb->Attributes &= (uint32_t)(~LOAD_OPTION_ACTIVE);
rv = set_variable(efi_fd, &ev);
if (rv == -1)
err(EXIT_FAILURE, "set_variable");
return rv;
}
#if 0
PUBLIC int
del_variable(int fd, const char *varname)
{
struct efi_var_ioc ev;
int rv;
efi_var_init(&ev, varname,
&EFI_GLOBAL_VARIABLE,
EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS);
rv = set_variable(fd, &ev);
free(ev.name);
return rv;
}
#endif
PUBLIC int
del_variable(int efi_fd, const char *target, uint16_t bootnum)
{
char *name;
int rv;
easprintf(&name, "%s%04X", target, bootnum);
printf("deleting '%s'\n", name);
rv = delete_variable(efi_fd, name);
free(name);
if (rv == -1)
err(EXIT_FAILURE, "del_variable");
rv = remove_bootorder(efi_fd, target, NULL, bootnum);
if (rv == -1)
err(EXIT_FAILURE, "remove_bootorder");
return rv;
}