1 1.4 riastrad /* $NetBSD: setvar.c,v 1.4 2025/03/02 01:07:11 riastradh Exp $ */ 2 1.1 christos 3 1.1 christos /* 4 1.1 christos * Redistribution and use in source and binary forms, with or without 5 1.1 christos * modification, are permitted provided that the following conditions 6 1.1 christos * are met: 7 1.1 christos * 1. Redistributions of source code must retain the above copyright 8 1.1 christos * notice, this list of conditions and the following disclaimer. 9 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright 10 1.1 christos * notice, this list of conditions and the following disclaimer in the 11 1.1 christos * documentation and/or other materials provided with the distribution. 12 1.1 christos * 13 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 14 1.1 christos * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 1.1 christos * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 1.1 christos * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 1.1 christos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 1.1 christos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 1.1 christos * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 1.1 christos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 1.1 christos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 1.1 christos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 1.1 christos * SUCH DAMAGE. 24 1.1 christos */ 25 1.1 christos 26 1.1 christos #include <sys/cdefs.h> 27 1.1 christos #ifndef lint 28 1.4 riastrad __RCSID("$NetBSD: setvar.c,v 1.4 2025/03/02 01:07:11 riastradh Exp $"); 29 1.1 christos #endif /* not lint */ 30 1.1 christos 31 1.1 christos #include <sys/efiio.h> 32 1.1 christos 33 1.1 christos #include <assert.h> 34 1.1 christos #include <err.h> 35 1.1 christos #include <stdbool.h> 36 1.1 christos #include <stdio.h> 37 1.1 christos #include <stdlib.h> 38 1.1 christos #include <string.h> 39 1.1 christos #include <uuid.h> 40 1.1 christos 41 1.1 christos #include "efiio.h" 42 1.1 christos #include "defs.h" 43 1.1 christos #include "bootvar.h" 44 1.1 christos #include "setvar.h" 45 1.1 christos #include "utils.h" 46 1.1 christos 47 1.1 christos #define BOOTNEXT "BootNext" 48 1.1 christos #define BOOTORDER "BootOrder" 49 1.1 christos #define TIMEOUT "Timeout" 50 1.1 christos 51 1.1 christos static size_t 52 1.1 christos parse_csus(const char *csus, uint16_t **array, int base) 53 1.1 christos { 54 1.1 christos uint16_t *data; 55 1.1 christos const char *p; 56 1.1 christos char *q; 57 1.1 christos size_t n; 58 1.1 christos 59 1.1 christos n = 1; 60 1.1 christos for (p = csus; *(p = strchrnul(p, ',')) != '\0'; p++) 61 1.1 christos n++; 62 1.1 christos 63 1.1 christos data = emalloc(n * sizeof(*data)); 64 1.1 christos 65 1.1 christos n = 0; 66 1.1 christos p = csus; 67 1.1 christos for (;;) { 68 1.1 christos data[n++] = strtous(p, &q, base); 69 1.1 christos if (*q != ',' && *q != '\0') 70 1.4 riastrad errx(EXIT_FAILURE, "invalid CSUS string:" 71 1.4 riastrad " '%s' (at %s)\n", 72 1.1 christos csus, p); 73 1.1 christos if (*q == '\0') 74 1.1 christos break; 75 1.1 christos p = q + 1; 76 1.1 christos } 77 1.1 christos 78 1.1 christos *array = data; 79 1.1 christos return n; 80 1.1 christos } 81 1.1 christos 82 1.1 christos PUBLIC int 83 1.3 riastrad prefix_bootorder(int fd, const char *target, const char *csus, 84 1.3 riastrad uint16_t bootnum) 85 1.1 christos { 86 1.1 christos struct efi_var_ioc ev; 87 1.1 christos char *targetorder; 88 1.1 christos uint16_t *data; 89 1.1 christos size_t datasize, n; 90 1.1 christos int rv; 91 1.1 christos 92 1.1 christos easprintf(&targetorder, "%sOrder", target); 93 1.1 christos ev = get_variable(fd, targetorder, 94 1.1 christos &EFI_GLOBAL_VARIABLE, 95 1.1 christos EFI_VARIABLE_NON_VOLATILE | 96 1.1 christos EFI_VARIABLE_BOOTSERVICE_ACCESS | 97 1.1 christos EFI_VARIABLE_RUNTIME_ACCESS); 98 1.1 christos 99 1.1 christos free(targetorder); 100 1.1 christos 101 1.1 christos if (csus != NULL) 102 1.1 christos n = parse_csus(csus, &data, 16); 103 1.1 christos else { 104 1.1 christos data = emalloc(sizeof(*data)); 105 1.1 christos *data = bootnum; 106 1.1 christos n = 1; 107 1.1 christos } 108 1.1 christos 109 1.1 christos datasize = ev.datasize + n * sizeof(*data); 110 1.1 christos data = erealloc(data, datasize); 111 1.1 christos memcpy(data + n, ev.data, ev.datasize); 112 1.1 christos 113 1.1 christos // free(ev.data); /* XXX: ??? */ 114 1.1 christos 115 1.1 christos ev.data = data; 116 1.1 christos ev.datasize = datasize; 117 1.2 riastrad 118 1.1 christos rv = set_variable(fd, &ev); 119 1.1 christos free(ev.data); 120 1.1 christos if (rv == -1) 121 1.1 christos err(EXIT_FAILURE, "prefix_bootorder: %s %s", target, csus); 122 1.1 christos return rv; 123 1.1 christos } 124 1.1 christos 125 1.1 christos PUBLIC int 126 1.3 riastrad remove_bootorder(int fd, const char *target, const char *csus, 127 1.3 riastrad uint16_t bootnum) 128 1.1 christos { 129 1.1 christos struct efi_var_ioc ev; 130 1.1 christos char *targetorder; 131 1.1 christos uint16_t *data, *dp, *rmlist; 132 1.1 christos size_t j, n, r; 133 1.1 christos int rv; 134 1.1 christos 135 1.1 christos easprintf(&targetorder, "%sOrder", target); 136 1.1 christos ev = get_variable(fd, targetorder, &EFI_GLOBAL_VARIABLE, 0); 137 1.1 christos free(targetorder); 138 1.1 christos 139 1.1 christos if (ev.datasize == 0) /* no such variable */ 140 1.1 christos return 0; 141 1.1 christos 142 1.1 christos if (csus != NULL) 143 1.1 christos r = parse_csus(csus, &rmlist, 16); 144 1.1 christos else { 145 1.1 christos rmlist = emalloc(sizeof(*rmlist)); 146 1.1 christos *rmlist = bootnum; 147 1.1 christos r = 1; 148 1.1 christos } 149 1.1 christos 150 1.1 christos data = emalloc(ev.datasize); 151 1.1 christos dp = ev.data; 152 1.1 christos n = ev.datasize / sizeof(*data); 153 1.1 christos j = 0; 154 1.1 christos for (size_t i = 0; i < n; i++) { 155 1.1 christos size_t k; 156 1.1 christos for (k = 0; k < r; k++) { 157 1.1 christos if (dp[i] == rmlist[k]) 158 1.1 christos break; 159 1.1 christos } 160 1.1 christos if (k == r) 161 1.1 christos data[j++] = dp[i]; 162 1.1 christos } 163 1.1 christos ev.data = data; 164 1.1 christos ev.datasize = j * sizeof(*data); 165 1.2 riastrad 166 1.1 christos rv = set_variable(fd, &ev); 167 1.1 christos free(ev.data); 168 1.1 christos if (rv == -1) 169 1.1 christos err(EXIT_FAILURE, "remove_bootorder: %s %u", target, bootnum); 170 1.1 christos return rv; 171 1.1 christos } 172 1.1 christos 173 1.1 christos PUBLIC int 174 1.1 christos set_bootorder(int fd, const char *target, const char *bootorder) 175 1.1 christos { 176 1.1 christos struct efi_var_ioc ev; 177 1.1 christos char *targetorder; 178 1.1 christos uint16_t *data; 179 1.1 christos size_t n; 180 1.1 christos int rv; 181 1.1 christos 182 1.1 christos easprintf(&targetorder, "%sOrder", target); 183 1.1 christos efi_var_init(&ev, targetorder, 184 1.1 christos &EFI_GLOBAL_VARIABLE, 185 1.1 christos EFI_VARIABLE_NON_VOLATILE | 186 1.1 christos EFI_VARIABLE_BOOTSERVICE_ACCESS | 187 1.1 christos EFI_VARIABLE_RUNTIME_ACCESS); 188 1.1 christos free(targetorder); 189 1.1 christos 190 1.1 christos n = parse_csus(bootorder, &data, 16); 191 1.1 christos ev.data = data; 192 1.1 christos ev.datasize = n * sizeof(*data); 193 1.1 christos 194 1.1 christos rv = set_variable(fd, &ev); 195 1.1 christos if (rv == -1) 196 1.1 christos warn("set_variable"); 197 1.1 christos 198 1.1 christos free(ev.name); 199 1.1 christos return rv; 200 1.1 christos } 201 1.1 christos 202 1.1 christos static int 203 1.1 christos delete_variable(int fd, const char *varname) 204 1.1 christos { 205 1.1 christos struct efi_var_ioc ev; 206 1.1 christos int rv; 207 1.1 christos 208 1.1 christos efi_var_init(&ev, varname, 209 1.1 christos &EFI_GLOBAL_VARIABLE, 210 1.1 christos EFI_VARIABLE_NON_VOLATILE | 211 1.1 christos EFI_VARIABLE_BOOTSERVICE_ACCESS | 212 1.1 christos EFI_VARIABLE_RUNTIME_ACCESS); 213 1.2 riastrad 214 1.1 christos rv = set_variable(fd, &ev); 215 1.1 christos free(ev.name); 216 1.1 christos return rv; 217 1.1 christos } 218 1.1 christos 219 1.1 christos PUBLIC int 220 1.1 christos del_bootorder(int fd, const char *target __unused) 221 1.1 christos { 222 1.1 christos int rv; 223 1.1 christos 224 1.1 christos rv = delete_variable(fd, BOOTORDER); 225 1.1 christos if (rv == -1) 226 1.1 christos warn("delete_variable"); 227 1.1 christos 228 1.1 christos return rv; 229 1.1 christos } 230 1.1 christos 231 1.1 christos PUBLIC int 232 1.1 christos del_bootorder_dups(int fd, const char *target) 233 1.1 christos { 234 1.1 christos struct efi_var_ioc ev; 235 1.1 christos char *targetorder; 236 1.1 christos uint16_t *data, *dp; 237 1.1 christos size_t i, j, k, n; 238 1.1 christos int rv; 239 1.1 christos 240 1.1 christos easprintf(&targetorder, "%sOrder", target); 241 1.1 christos ev = get_variable(fd, targetorder, &EFI_GLOBAL_VARIABLE, 0); 242 1.1 christos free(targetorder); 243 1.1 christos 244 1.1 christos if (ev.datasize == 0) 245 1.1 christos return 0; 246 1.1 christos 247 1.1 christos data = emalloc(ev.datasize); 248 1.1 christos dp = ev.data; 249 1.1 christos 250 1.1 christos n = ev.datasize / sizeof(*data); 251 1.1 christos j = 0; 252 1.1 christos for (i = 0; i < n; i++) { 253 1.1 christos for (k = 0; k < j; k++) { /* XXX: O(n^2) */ 254 1.1 christos if (data[k] == dp[i]) 255 1.1 christos break; 256 1.1 christos } 257 1.1 christos if (k == j) 258 1.1 christos data[j++] = dp[i]; 259 1.1 christos } 260 1.1 christos 261 1.1 christos ev.data = data; 262 1.1 christos ev.datasize = j * sizeof(*data); 263 1.1 christos 264 1.1 christos rv = set_variable(fd, &ev); 265 1.1 christos free(ev.data); 266 1.1 christos if (rv == -1) 267 1.1 christos err(EXIT_FAILURE, "del_bootorder_dups"); 268 1.1 christos return rv; 269 1.1 christos } 270 1.1 christos 271 1.1 christos PUBLIC int 272 1.1 christos set_bootnext(int fd, uint16_t bootnum) 273 1.1 christos { 274 1.1 christos struct efi_var_ioc ev; 275 1.1 christos int rv; 276 1.1 christos 277 1.1 christos efi_var_init(&ev, BOOTNEXT, 278 1.1 christos &EFI_GLOBAL_VARIABLE, 279 1.1 christos EFI_VARIABLE_NON_VOLATILE | 280 1.1 christos EFI_VARIABLE_BOOTSERVICE_ACCESS | 281 1.1 christos EFI_VARIABLE_RUNTIME_ACCESS); 282 1.1 christos 283 1.1 christos ev.data = &bootnum; 284 1.1 christos ev.datasize = sizeof(bootnum); 285 1.2 riastrad 286 1.1 christos printf("set BootNext = Boot%04X\n", bootnum); 287 1.1 christos 288 1.1 christos rv = set_variable(fd, &ev); 289 1.1 christos if (rv == -1) 290 1.1 christos warn("set_variable"); 291 1.1 christos 292 1.1 christos free(ev.name); 293 1.1 christos return rv; 294 1.1 christos } 295 1.1 christos 296 1.1 christos PUBLIC int 297 1.1 christos del_bootnext(int fd) 298 1.1 christos { 299 1.1 christos int rv; 300 1.1 christos 301 1.1 christos rv = delete_variable(fd, BOOTNEXT); 302 1.1 christos if (rv == -1) 303 1.1 christos warn("delete_variable"); 304 1.1 christos 305 1.1 christos return rv; 306 1.1 christos } 307 1.1 christos 308 1.1 christos PUBLIC int 309 1.1 christos set_timeout(int fd, uint16_t timeout) 310 1.1 christos { 311 1.1 christos struct efi_var_ioc ev; 312 1.1 christos int rv; 313 1.1 christos 314 1.1 christos efi_var_init(&ev, TIMEOUT, 315 1.1 christos &EFI_GLOBAL_VARIABLE, 316 1.1 christos EFI_VARIABLE_NON_VOLATILE | 317 1.1 christos EFI_VARIABLE_BOOTSERVICE_ACCESS | 318 1.1 christos EFI_VARIABLE_RUNTIME_ACCESS); 319 1.1 christos 320 1.1 christos ev.data = &timeout; 321 1.1 christos ev.datasize = sizeof(timeout); 322 1.2 riastrad 323 1.1 christos printf("set Timeout = %u seconds\n", timeout); 324 1.1 christos 325 1.1 christos rv = set_variable(fd, &ev); 326 1.1 christos if (rv == -1) 327 1.1 christos warn("set_variable"); 328 1.1 christos 329 1.1 christos free(ev.name); 330 1.1 christos return rv; 331 1.1 christos } 332 1.1 christos 333 1.1 christos PUBLIC int 334 1.1 christos del_timeout(int fd) 335 1.1 christos { 336 1.1 christos int rv; 337 1.1 christos 338 1.1 christos rv = delete_variable(fd, TIMEOUT); 339 1.1 christos if (rv == -1) 340 1.1 christos warn("del_variable"); 341 1.1 christos 342 1.1 christos return rv; 343 1.1 christos } 344 1.1 christos 345 1.1 christos PUBLIC int 346 1.1 christos set_active(int efi_fd, const char *target, uint16_t bootnum, bool active) 347 1.1 christos { 348 1.1 christos struct efi_var_ioc ev; 349 1.1 christos boot_var_t *bb; 350 1.1 christos char *name; 351 1.1 christos int rv; 352 1.2 riastrad 353 1.1 christos easprintf(&name, "%s%04X", target, bootnum); 354 1.1 christos ev = get_variable(efi_fd, name, &EFI_GLOBAL_VARIABLE, 0); 355 1.1 christos free(name); 356 1.1 christos 357 1.1 christos bb = ev.data; 358 1.1 christos if (active) 359 1.1 christos bb->Attributes |= LOAD_OPTION_ACTIVE; 360 1.1 christos else 361 1.1 christos bb->Attributes &= (uint32_t)(~LOAD_OPTION_ACTIVE); 362 1.2 riastrad 363 1.1 christos rv = set_variable(efi_fd, &ev); 364 1.1 christos if (rv == -1) 365 1.1 christos err(EXIT_FAILURE, "set_variable"); 366 1.1 christos 367 1.1 christos return rv; 368 1.1 christos } 369 1.1 christos 370 1.1 christos #if 0 371 1.1 christos PUBLIC int 372 1.1 christos del_variable(int fd, const char *varname) 373 1.1 christos { 374 1.1 christos struct efi_var_ioc ev; 375 1.1 christos int rv; 376 1.1 christos 377 1.1 christos efi_var_init(&ev, varname, 378 1.1 christos &EFI_GLOBAL_VARIABLE, 379 1.1 christos EFI_VARIABLE_NON_VOLATILE | 380 1.1 christos EFI_VARIABLE_BOOTSERVICE_ACCESS | 381 1.1 christos EFI_VARIABLE_RUNTIME_ACCESS); 382 1.2 riastrad 383 1.1 christos rv = set_variable(fd, &ev); 384 1.1 christos free(ev.name); 385 1.1 christos return rv; 386 1.1 christos } 387 1.1 christos #endif 388 1.1 christos 389 1.1 christos PUBLIC int 390 1.1 christos del_variable(int efi_fd, const char *target, uint16_t bootnum) 391 1.1 christos { 392 1.1 christos char *name; 393 1.1 christos int rv; 394 1.1 christos 395 1.1 christos easprintf(&name, "%s%04X", target, bootnum); 396 1.1 christos printf("deleting '%s'\n", name); 397 1.1 christos rv = delete_variable(efi_fd, name); 398 1.1 christos free(name); 399 1.1 christos 400 1.1 christos if (rv == -1) 401 1.1 christos err(EXIT_FAILURE, "del_variable"); 402 1.2 riastrad 403 1.1 christos rv = remove_bootorder(efi_fd, target, NULL, bootnum); 404 1.1 christos if (rv == -1) 405 1.1 christos err(EXIT_FAILURE, "remove_bootorder"); 406 1.2 riastrad 407 1.1 christos return rv; 408 1.1 christos } 409