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