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