Home | History | Annotate | Line # | Download | only in efi
setvar.c revision 1.1
      1 /* $NetBSD: setvar.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: setvar.c,v 1.1 2025/02/24 13:47:57 christos Exp $");
     29 #endif /* not lint */
     30 
     31 #include <sys/efiio.h>
     32 
     33 #include <assert.h>
     34 #include <err.h>
     35 #include <stdbool.h>
     36 #include <stdio.h>
     37 #include <stdlib.h>
     38 #include <string.h>
     39 #include <uuid.h>
     40 
     41 #include "efiio.h"
     42 #include "defs.h"
     43 #include "bootvar.h"
     44 #include "setvar.h"
     45 #include "utils.h"
     46 
     47 #define BOOTNEXT	"BootNext"
     48 #define BOOTORDER	"BootOrder"
     49 #define TIMEOUT		"Timeout"
     50 
     51 static size_t
     52 parse_csus(const char *csus, uint16_t **array, int base)
     53 {
     54 	uint16_t *data;
     55 	const char *p;
     56 	char *q;
     57 	size_t n;
     58 
     59 	n = 1;
     60 	for (p = csus; *(p = strchrnul(p, ',')) != '\0'; p++)
     61 		n++;
     62 
     63 	data = emalloc(n * sizeof(*data));
     64 
     65 	n = 0;
     66 	p = csus;
     67 	for (;;) {
     68 		data[n++] = strtous(p, &q, base);
     69 		if (*q != ',' && *q != '\0')
     70 			errx(EXIT_FAILURE, "invalid CSUS string: '%s' (at %s)\n",
     71 			    csus, p);
     72 		if (*q == '\0')
     73 			break;
     74 		p = q + 1;
     75 	}
     76 
     77 	*array = data;
     78 	return n;
     79 }
     80 
     81 PUBLIC int
     82 prefix_bootorder(int fd, const char *target, const char *csus, uint16_t bootnum)
     83 {
     84 	struct efi_var_ioc ev;
     85 	char *targetorder;
     86 	uint16_t *data;
     87 	size_t datasize, n;
     88 	int rv;
     89 
     90 	easprintf(&targetorder, "%sOrder", target);
     91 	ev = get_variable(fd, targetorder,
     92 	    &EFI_GLOBAL_VARIABLE,
     93 	    EFI_VARIABLE_NON_VOLATILE |
     94 	    EFI_VARIABLE_BOOTSERVICE_ACCESS |
     95 	    EFI_VARIABLE_RUNTIME_ACCESS);
     96 
     97 	free(targetorder);
     98 
     99 	if (csus != NULL)
    100 		n = parse_csus(csus, &data, 16);
    101 	else {
    102 		data = emalloc(sizeof(*data));
    103 		*data = bootnum;
    104 		n = 1;
    105 	}
    106 
    107 	datasize = ev.datasize + n * sizeof(*data);
    108 	data = erealloc(data, datasize);
    109 	memcpy(data + n, ev.data, ev.datasize);
    110 
    111 //	free(ev.data);	/* XXX: ??? */
    112 
    113 	ev.data = data;
    114 	ev.datasize = datasize;
    115 
    116 	rv = set_variable(fd, &ev);
    117 	free(ev.data);
    118 	if (rv == -1)
    119 		err(EXIT_FAILURE, "prefix_bootorder: %s %s", target, csus);
    120 	return rv;
    121 }
    122 
    123 PUBLIC int
    124 remove_bootorder(int fd, const char *target, const char *csus, uint16_t bootnum)
    125 {
    126 	struct efi_var_ioc ev;
    127 	char *targetorder;
    128 	uint16_t *data, *dp, *rmlist;
    129 	size_t j, n, r;
    130 	int rv;
    131 
    132 	easprintf(&targetorder, "%sOrder", target);
    133 	ev = get_variable(fd, targetorder, &EFI_GLOBAL_VARIABLE, 0);
    134 	free(targetorder);
    135 
    136 	if (ev.datasize == 0)	/* no such variable */
    137 		return 0;
    138 
    139 	if (csus != NULL)
    140 		r = parse_csus(csus, &rmlist, 16);
    141 	else {
    142 		rmlist = emalloc(sizeof(*rmlist));
    143 		*rmlist = bootnum;
    144 		r = 1;
    145 	}
    146 
    147 	data = emalloc(ev.datasize);
    148 	dp = ev.data;
    149 	n = ev.datasize / sizeof(*data);
    150 	j = 0;
    151 	for (size_t i = 0; i < n; i++) {
    152 		size_t k;
    153 		for (k = 0; k < r; k++) {
    154 			if (dp[i] == rmlist[k])
    155 				break;
    156 		}
    157 		if (k == r)
    158 			data[j++] = dp[i];
    159 	}
    160 	ev.data = data;
    161 	ev.datasize = j * sizeof(*data);
    162 
    163 	rv = set_variable(fd, &ev);
    164 	free(ev.data);
    165 	if (rv == -1)
    166 		err(EXIT_FAILURE, "remove_bootorder: %s %u", target, bootnum);
    167 	return rv;
    168 }
    169 
    170 PUBLIC int
    171 set_bootorder(int fd, const char *target, const char *bootorder)
    172 {
    173 	struct efi_var_ioc ev;
    174 	char *targetorder;
    175 	uint16_t *data;
    176 	size_t n;
    177 	int rv;
    178 
    179 	easprintf(&targetorder, "%sOrder", target);
    180 	efi_var_init(&ev, targetorder,
    181 	    &EFI_GLOBAL_VARIABLE,
    182 	    EFI_VARIABLE_NON_VOLATILE |
    183 	    EFI_VARIABLE_BOOTSERVICE_ACCESS |
    184 	    EFI_VARIABLE_RUNTIME_ACCESS);
    185 	free(targetorder);
    186 
    187 	n = parse_csus(bootorder, &data, 16);
    188 	ev.data = data;
    189 	ev.datasize = n * sizeof(*data);
    190 
    191 	rv = set_variable(fd, &ev);
    192 	if (rv == -1)
    193 		warn("set_variable");
    194 
    195 	free(ev.name);
    196 	return rv;
    197 }
    198 
    199 static int
    200 delete_variable(int fd, const char *varname)
    201 {
    202 	struct efi_var_ioc ev;
    203 	int rv;
    204 
    205 	efi_var_init(&ev, varname,
    206 	    &EFI_GLOBAL_VARIABLE,
    207 	    EFI_VARIABLE_NON_VOLATILE |
    208 	    EFI_VARIABLE_BOOTSERVICE_ACCESS |
    209 	    EFI_VARIABLE_RUNTIME_ACCESS);
    210 
    211 	rv = set_variable(fd, &ev);
    212 	free(ev.name);
    213 	return rv;
    214 }
    215 
    216 PUBLIC int
    217 del_bootorder(int fd, const char *target __unused)
    218 {
    219 	int rv;
    220 
    221 	rv = delete_variable(fd, BOOTORDER);
    222 	if (rv == -1)
    223 		warn("delete_variable");
    224 
    225 	return rv;
    226 }
    227 
    228 PUBLIC int
    229 del_bootorder_dups(int fd, const char *target)
    230 {
    231 	struct efi_var_ioc ev;
    232 	char *targetorder;
    233 	uint16_t *data, *dp;
    234 	size_t i, j, k, n;
    235 	int rv;
    236 
    237 	easprintf(&targetorder, "%sOrder", target);
    238 	ev = get_variable(fd, targetorder, &EFI_GLOBAL_VARIABLE, 0);
    239 	free(targetorder);
    240 
    241 	if (ev.datasize == 0)
    242 		return 0;
    243 
    244 	data = emalloc(ev.datasize);
    245 	dp = ev.data;
    246 
    247 	n = ev.datasize / sizeof(*data);
    248 	j = 0;
    249 	for (i = 0; i < n; i++) {
    250 		for (k = 0; k < j; k++) {  /* XXX: O(n^2) */
    251 			if (data[k] == dp[i])
    252 				break;
    253 		}
    254 		if (k == j)
    255 			data[j++] = dp[i];
    256 	}
    257 
    258 	ev.data = data;
    259 	ev.datasize = j * sizeof(*data);
    260 
    261 	rv = set_variable(fd, &ev);
    262 	free(ev.data);
    263 	if (rv == -1)
    264 		err(EXIT_FAILURE, "del_bootorder_dups");
    265 	return rv;
    266 }
    267 
    268 PUBLIC int
    269 set_bootnext(int fd, uint16_t bootnum)
    270 {
    271 	struct efi_var_ioc ev;
    272 	int rv;
    273 
    274 	efi_var_init(&ev, BOOTNEXT,
    275 	    &EFI_GLOBAL_VARIABLE,
    276 	    EFI_VARIABLE_NON_VOLATILE |
    277 	    EFI_VARIABLE_BOOTSERVICE_ACCESS |
    278 	    EFI_VARIABLE_RUNTIME_ACCESS);
    279 
    280 	ev.data = &bootnum;
    281 	ev.datasize = sizeof(bootnum);
    282 
    283 	printf("set BootNext = Boot%04X\n", bootnum);
    284 
    285 	rv = set_variable(fd, &ev);
    286 	if (rv == -1)
    287 		warn("set_variable");
    288 
    289 	free(ev.name);
    290 	return rv;
    291 }
    292 
    293 PUBLIC int
    294 del_bootnext(int fd)
    295 {
    296 	int rv;
    297 
    298 	rv = delete_variable(fd, BOOTNEXT);
    299 	if (rv == -1)
    300 		warn("delete_variable");
    301 
    302 	return rv;
    303 }
    304 
    305 PUBLIC int
    306 set_timeout(int fd, uint16_t timeout)
    307 {
    308 	struct efi_var_ioc ev;
    309 	int rv;
    310 
    311 
    312 	efi_var_init(&ev, TIMEOUT,
    313 	    &EFI_GLOBAL_VARIABLE,
    314 	    EFI_VARIABLE_NON_VOLATILE |
    315 	    EFI_VARIABLE_BOOTSERVICE_ACCESS |
    316 	    EFI_VARIABLE_RUNTIME_ACCESS);
    317 
    318 	ev.data = &timeout;
    319 	ev.datasize = sizeof(timeout);
    320 
    321 	printf("set Timeout = %u seconds\n", timeout);
    322 
    323 	rv = set_variable(fd, &ev);
    324 	if (rv == -1)
    325 		warn("set_variable");
    326 
    327 	free(ev.name);
    328 	return rv;
    329 }
    330 
    331 PUBLIC int
    332 del_timeout(int fd)
    333 {
    334 	int rv;
    335 
    336 	rv = delete_variable(fd, TIMEOUT);
    337 	if (rv == -1)
    338 		warn("del_variable");
    339 
    340 	return rv;
    341 }
    342 
    343 PUBLIC int
    344 set_active(int efi_fd, const char *target, uint16_t bootnum, bool active)
    345 {
    346 	struct efi_var_ioc ev;
    347 	boot_var_t *bb;
    348 	char *name;
    349 	int rv;
    350 
    351 	easprintf(&name, "%s%04X", target, bootnum);
    352 	ev = get_variable(efi_fd, name, &EFI_GLOBAL_VARIABLE, 0);
    353 	free(name);
    354 
    355 	bb = ev.data;
    356 	if (active)
    357 		bb->Attributes |= LOAD_OPTION_ACTIVE;
    358 	else
    359 		bb->Attributes &= (uint32_t)(~LOAD_OPTION_ACTIVE);
    360 
    361 	rv = set_variable(efi_fd, &ev);
    362 	if (rv == -1)
    363 		err(EXIT_FAILURE, "set_variable");
    364 
    365 	return rv;
    366 }
    367 
    368 #if 0
    369 PUBLIC int
    370 del_variable(int fd, const char *varname)
    371 {
    372 	struct efi_var_ioc ev;
    373 	int rv;
    374 
    375 	efi_var_init(&ev, varname,
    376 	    &EFI_GLOBAL_VARIABLE,
    377 	    EFI_VARIABLE_NON_VOLATILE |
    378 	    EFI_VARIABLE_BOOTSERVICE_ACCESS |
    379 	    EFI_VARIABLE_RUNTIME_ACCESS);
    380 
    381 	rv = set_variable(fd, &ev);
    382 	free(ev.name);
    383 	return rv;
    384 }
    385 #endif
    386 
    387 PUBLIC int
    388 del_variable(int efi_fd, const char *target, uint16_t bootnum)
    389 {
    390 	char *name;
    391 	int rv;
    392 
    393 	easprintf(&name, "%s%04X", target, bootnum);
    394 	printf("deleting '%s'\n", name);
    395 	rv = delete_variable(efi_fd, name);
    396 	free(name);
    397 
    398 	if (rv == -1)
    399 		err(EXIT_FAILURE, "del_variable");
    400 
    401 	rv = remove_bootorder(efi_fd, target, NULL, bootnum);
    402 	if (rv == -1)
    403 		err(EXIT_FAILURE, "remove_bootorder");
    404 
    405 	return rv;
    406 }
    407