setvar.c revision 1.4 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