]>
Commit | Line | Data |
---|---|---|
2e3d0692 LP |
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | ||
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright 2013 Lennart Poettering | |
7 | ||
8 | systemd is free software; you can redistribute it and/or modify it | |
9 | under the terms of the GNU Lesser General Public License as published by | |
10 | the Free Software Foundation; either version 2.1 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | systemd is distributed in the hope that it will be useful, but | |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | Lesser General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU Lesser General Public License | |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
20 | ***/ | |
21 | ||
22 | #include <unistd.h> | |
7b4d7cc0 | 23 | #include <string.h> |
2e3d0692 | 24 | #include <fcntl.h> |
726c6b6b | 25 | #include <ctype.h> |
2e3d0692 | 26 | |
c51d84dc | 27 | #include "acpi-fpdt.h" |
2e3d0692 LP |
28 | #include "util.h" |
29 | #include "utf8.h" | |
30 | #include "efivars.h" | |
31 | ||
b872e9a0 LP |
32 | #ifdef ENABLE_EFI |
33 | ||
0974a682 KS |
34 | #define LOAD_OPTION_ACTIVE 0x00000001 |
35 | #define MEDIA_DEVICE_PATH 0x04 | |
36 | #define MEDIA_HARDDRIVE_DP 0x01 | |
37 | #define MEDIA_FILEPATH_DP 0x04 | |
38 | #define SIGNATURE_TYPE_GUID 0x02 | |
39 | #define MBR_TYPE_EFI_PARTITION_TABLE_HEADER 0x02 | |
40 | #define END_DEVICE_PATH_TYPE 0x7f | |
41 | #define END_ENTIRE_DEVICE_PATH_SUBTYPE 0xff | |
42 | ||
43 | struct boot_option { | |
44 | uint32_t attr; | |
45 | uint16_t path_len; | |
46 | uint16_t title[]; | |
47 | } __attribute__((packed)); | |
48 | ||
49 | struct drive_path { | |
50 | uint32_t part_nr; | |
51 | uint64_t part_start; | |
52 | uint64_t part_size; | |
53 | char signature[16]; | |
54 | uint8_t mbr_type; | |
55 | uint8_t signature_type; | |
56 | } __attribute__((packed)); | |
57 | ||
58 | struct device_path { | |
59 | uint8_t type; | |
60 | uint8_t sub_type; | |
61 | uint16_t length; | |
62 | union { | |
63 | uint16_t path[0]; | |
64 | struct drive_path drive; | |
65 | }; | |
66 | } __attribute__((packed)); | |
67 | ||
9cde64ff | 68 | bool is_efi_boot(void) { |
34e5a31e LP |
69 | return access("/sys/firmware/efi", F_OK) >= 0; |
70 | } | |
71 | ||
bc6f2e7c KS |
72 | static int read_flag(const char *varname) { |
73 | int r; | |
b47d419c | 74 | _cleanup_free_ void *v = NULL; |
bc6f2e7c KS |
75 | size_t s; |
76 | uint8_t b; | |
77 | ||
78 | r = efi_get_variable(EFI_VENDOR_GLOBAL, varname, NULL, &v, &s); | |
79 | if (r < 0) | |
80 | return r; | |
81 | ||
b47d419c ZJS |
82 | if (s != 1) |
83 | return -EINVAL; | |
bc6f2e7c KS |
84 | |
85 | b = *(uint8_t *)v; | |
86 | r = b > 0; | |
bc6f2e7c KS |
87 | return r; |
88 | } | |
89 | ||
90 | int is_efi_secure_boot(void) { | |
91 | return read_flag("SecureBoot"); | |
92 | } | |
93 | ||
94 | int is_efi_secure_boot_setup_mode(void) { | |
95 | return read_flag("SetupMode"); | |
96 | } | |
97 | ||
9cde64ff LP |
98 | int efi_get_variable( |
99 | sd_id128_t vendor, | |
100 | const char *name, | |
101 | uint32_t *attribute, | |
102 | void **value, | |
103 | size_t *size) { | |
104 | ||
2e3d0692 LP |
105 | _cleanup_close_ int fd = -1; |
106 | _cleanup_free_ char *p = NULL; | |
107 | uint32_t a; | |
108 | ssize_t n; | |
109 | struct stat st; | |
110 | void *r; | |
111 | ||
112 | assert(name); | |
113 | assert(value); | |
114 | assert(size); | |
115 | ||
116 | if (asprintf(&p, | |
117 | "/sys/firmware/efi/efivars/%s-%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", | |
118 | name, SD_ID128_FORMAT_VAL(vendor)) < 0) | |
119 | return -ENOMEM; | |
120 | ||
121 | fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC); | |
122 | if (fd < 0) | |
123 | return -errno; | |
124 | ||
125 | if (fstat(fd, &st) < 0) | |
126 | return -errno; | |
127 | if (st.st_size < 4) | |
128 | return -EIO; | |
129 | if (st.st_size > 4*1024*1024 + 4) | |
130 | return -E2BIG; | |
131 | ||
132 | n = read(fd, &a, sizeof(a)); | |
133 | if (n < 0) | |
9cde64ff | 134 | return -errno; |
2e3d0692 LP |
135 | if (n != sizeof(a)) |
136 | return -EIO; | |
137 | ||
138 | r = malloc(st.st_size - 4 + 2); | |
139 | if (!r) | |
140 | return -ENOMEM; | |
141 | ||
142 | n = read(fd, r, (size_t) st.st_size - 4); | |
143 | if (n < 0) { | |
144 | free(r); | |
742af54a | 145 | return -errno; |
2e3d0692 LP |
146 | } |
147 | if (n != (ssize_t) st.st_size - 4) { | |
148 | free(r); | |
149 | return -EIO; | |
150 | } | |
151 | ||
152 | /* Always NUL terminate (2 bytes, to protect UTF-16) */ | |
153 | ((char*) r)[st.st_size - 4] = 0; | |
154 | ((char*) r)[st.st_size - 4 + 1] = 0; | |
155 | ||
156 | *value = r; | |
ff47c895 | 157 | *size = (size_t) st.st_size - 4; |
2e3d0692 LP |
158 | |
159 | if (attribute) | |
160 | *attribute = a; | |
161 | ||
162 | return 0; | |
163 | } | |
164 | ||
0974a682 KS |
165 | int efi_set_variable( |
166 | sd_id128_t vendor, | |
167 | const char *name, | |
168 | const void *value, | |
169 | size_t size) { | |
170 | ||
171 | struct var { | |
172 | uint32_t attr; | |
173 | char buf[]; | |
174 | } __attribute__((packed)) *buf = NULL; | |
175 | char *p = NULL; | |
176 | int fd = -1; | |
177 | int r; | |
178 | ||
179 | assert(name); | |
180 | ||
181 | if (asprintf(&p, | |
182 | "/sys/firmware/efi/efivars/%s-%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", | |
183 | name, SD_ID128_FORMAT_VAL(vendor)) < 0) | |
184 | return -ENOMEM; | |
185 | ||
186 | if (size == 0) { | |
187 | r = unlink(p); | |
188 | goto finish; | |
189 | } | |
190 | ||
191 | fd = open(p, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0644); | |
192 | if (fd < 0) { | |
193 | r = -errno; | |
194 | goto finish; | |
195 | } | |
196 | ||
197 | buf = malloc(sizeof(uint32_t) + size); | |
198 | if (!buf) { | |
199 | r = -errno; | |
200 | goto finish; | |
201 | } | |
202 | ||
203 | buf->attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS; | |
204 | memcpy(buf->buf, value, size); | |
205 | ||
206 | r = write(fd, buf, sizeof(uint32_t) + size); | |
207 | if (r < 0) { | |
208 | r = -errno; | |
209 | goto finish; | |
210 | } | |
211 | ||
212 | if ((size_t)r != sizeof(uint32_t) + size) { | |
213 | r = -EIO; | |
214 | goto finish; | |
215 | } | |
216 | ||
217 | finish: | |
218 | if (fd >= 0) | |
219 | close(fd); | |
220 | free(buf); | |
221 | free(p); | |
222 | return r; | |
223 | } | |
224 | ||
9cde64ff | 225 | int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) { |
7b4d7cc0 | 226 | _cleanup_free_ void *s = NULL; |
39883f62 | 227 | size_t ss = 0; |
9cde64ff LP |
228 | int r; |
229 | char *x; | |
7b4d7cc0 | 230 | |
9cde64ff LP |
231 | r = efi_get_variable(vendor, name, NULL, &s, &ss); |
232 | if (r < 0) | |
233 | return r; | |
234 | ||
235 | x = utf16_to_utf8(s, ss); | |
236 | if (!x) | |
237 | return -ENOMEM; | |
238 | ||
239 | *p = x; | |
240 | return 0; | |
7b4d7cc0 KS |
241 | } |
242 | ||
243 | static size_t utf16_size(const uint16_t *s) { | |
244 | size_t l = 0; | |
245 | ||
246 | while (s[l] > 0) | |
247 | l++; | |
9cde64ff | 248 | |
7b4d7cc0 KS |
249 | return (l+1) * sizeof(uint16_t); |
250 | } | |
251 | ||
252 | static void efi_guid_to_id128(const void *guid, sd_id128_t *id128) { | |
253 | struct uuid { | |
254 | uint32_t u1; | |
255 | uint16_t u2; | |
256 | uint16_t u3; | |
257 | uint8_t u4[8]; | |
258 | } _packed_; | |
259 | const struct uuid *uuid = guid; | |
260 | ||
261 | id128->bytes[0] = (uuid->u1 >> 24) & 0xff; | |
262 | id128->bytes[1] = (uuid->u1 >> 16) & 0xff; | |
263 | id128->bytes[2] = (uuid->u1 >> 8) & 0xff; | |
264 | id128->bytes[3] = (uuid->u1) & 0xff; | |
265 | id128->bytes[4] = (uuid->u2 >> 8) & 0xff; | |
266 | id128->bytes[5] = (uuid->u2) & 0xff; | |
267 | id128->bytes[6] = (uuid->u3 >> 8) & 0xff; | |
268 | id128->bytes[7] = (uuid->u3) & 0xff; | |
269 | memcpy(&id128->bytes[8], uuid->u4, sizeof(uuid->u4)); | |
270 | } | |
271 | ||
9cde64ff LP |
272 | int efi_get_boot_option( |
273 | uint16_t id, | |
274 | char **title, | |
275 | sd_id128_t *part_uuid, | |
0974a682 KS |
276 | char **path, |
277 | bool *active) { | |
7b4d7cc0 KS |
278 | struct boot_option { |
279 | uint32_t attr; | |
280 | uint16_t path_len; | |
281 | uint16_t title[]; | |
282 | } _packed_; | |
283 | ||
284 | struct drive_path { | |
285 | uint32_t part_nr; | |
286 | uint64_t part_start; | |
287 | uint64_t part_size; | |
288 | char signature[16]; | |
289 | uint8_t mbr_type; | |
290 | uint8_t signature_type; | |
291 | } _packed_; | |
292 | ||
293 | struct device_path { | |
294 | uint8_t type; | |
295 | uint8_t sub_type; | |
296 | uint16_t length; | |
297 | union { | |
298 | uint16_t path[0]; | |
299 | struct drive_path drive; | |
300 | }; | |
301 | } _packed_; | |
302 | ||
9cde64ff LP |
303 | char boot_id[9]; |
304 | _cleanup_free_ uint8_t *buf = NULL; | |
7b4d7cc0 KS |
305 | size_t l; |
306 | struct boot_option *header; | |
307 | size_t title_size; | |
308 | char *s = NULL; | |
309 | char *p = NULL; | |
310 | sd_id128_t p_uuid = SD_ID128_NULL; | |
7b4d7cc0 KS |
311 | int err; |
312 | ||
313 | snprintf(boot_id, sizeof(boot_id), "Boot%04X", id); | |
314 | err = efi_get_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, (void **)&buf, &l); | |
315 | if (err < 0) | |
316 | return err; | |
7b4d7cc0 KS |
317 | if (l < sizeof(struct boot_option)) |
318 | return -ENOENT; | |
319 | ||
320 | header = (struct boot_option *)buf; | |
321 | title_size = utf16_size(header->title); | |
322 | if (title_size > l - offsetof(struct boot_option, title)) | |
323 | return -EINVAL; | |
324 | ||
5483a186 ZJS |
325 | if (title) { |
326 | s = utf16_to_utf8(header->title, title_size); | |
327 | if (!s) { | |
328 | err = -ENOMEM; | |
329 | goto err; | |
330 | } | |
7b4d7cc0 KS |
331 | } |
332 | ||
333 | if (header->path_len > 0) { | |
9cde64ff | 334 | uint8_t *dbuf; |
7b4d7cc0 KS |
335 | size_t dnext; |
336 | ||
337 | dbuf = buf + offsetof(struct boot_option, title) + title_size; | |
338 | dnext = 0; | |
339 | while (dnext < header->path_len) { | |
340 | struct device_path *dpath; | |
341 | ||
342 | dpath = (struct device_path *)(dbuf + dnext); | |
343 | if (dpath->length < 4) | |
344 | break; | |
345 | ||
346 | /* Type 0x7F – End of Hardware Device Path, Sub-Type 0xFF – End Entire Device Path */ | |
0974a682 | 347 | if (dpath->type == END_DEVICE_PATH_TYPE && dpath->sub_type == END_ENTIRE_DEVICE_PATH_SUBTYPE) |
7b4d7cc0 KS |
348 | break; |
349 | ||
350 | dnext += dpath->length; | |
351 | ||
352 | /* Type 0x04 – Media Device Path */ | |
0974a682 | 353 | if (dpath->type != MEDIA_DEVICE_PATH) |
7b4d7cc0 KS |
354 | continue; |
355 | ||
356 | /* Sub-Type 1 – Hard Drive */ | |
0974a682 | 357 | if (dpath->sub_type == MEDIA_HARDDRIVE_DP) { |
7b4d7cc0 | 358 | /* 0x02 – GUID Partition Table */ |
0974a682 | 359 | if (dpath->drive.mbr_type != MBR_TYPE_EFI_PARTITION_TABLE_HEADER) |
7b4d7cc0 KS |
360 | continue; |
361 | ||
362 | /* 0x02 – GUID signature */ | |
0974a682 | 363 | if (dpath->drive.signature_type != SIGNATURE_TYPE_GUID) |
7b4d7cc0 KS |
364 | continue; |
365 | ||
5483a186 ZJS |
366 | if (part_uuid) |
367 | efi_guid_to_id128(dpath->drive.signature, &p_uuid); | |
7b4d7cc0 KS |
368 | continue; |
369 | } | |
370 | ||
371 | /* Sub-Type 4 – File Path */ | |
0974a682 | 372 | if (dpath->sub_type == MEDIA_FILEPATH_DP && !p && path) { |
7b4d7cc0 | 373 | p = utf16_to_utf8(dpath->path, dpath->length-4); |
0974a682 | 374 | efi_tilt_backslashes(p); |
7b4d7cc0 KS |
375 | continue; |
376 | } | |
377 | } | |
378 | } | |
379 | ||
9cde64ff LP |
380 | if (title) |
381 | *title = s; | |
7b4d7cc0 KS |
382 | if (part_uuid) |
383 | *part_uuid = p_uuid; | |
384 | if (path) | |
385 | *path = p; | |
0974a682 KS |
386 | if (active) |
387 | *active = !!header->attr & LOAD_OPTION_ACTIVE; | |
9cde64ff | 388 | |
7b4d7cc0 KS |
389 | return 0; |
390 | err: | |
391 | free(s); | |
392 | free(p); | |
7b4d7cc0 KS |
393 | return err; |
394 | } | |
395 | ||
0974a682 KS |
396 | static void to_utf16(uint16_t *dest, const char *src) { |
397 | int i; | |
398 | ||
399 | for (i = 0; src[i] != '\0'; i++) | |
400 | dest[i] = src[i]; | |
401 | dest[i] = '\0'; | |
402 | } | |
403 | ||
404 | struct guid { | |
405 | uint32_t u1; | |
406 | uint16_t u2; | |
407 | uint16_t u3; | |
408 | uint8_t u4[8]; | |
409 | } __attribute__((packed)); | |
410 | ||
411 | static void id128_to_efi_guid(sd_id128_t id, void *guid) { | |
412 | struct guid *uuid = guid; | |
413 | ||
414 | uuid->u1 = id.bytes[0] << 24 | id.bytes[1] << 16 | id.bytes[2] << 8 | id.bytes[3]; | |
415 | uuid->u2 = id.bytes[4] << 8 | id.bytes[5]; | |
416 | uuid->u3 = id.bytes[6] << 8 | id.bytes[7]; | |
417 | memcpy(uuid->u4, id.bytes+8, sizeof(uuid->u4)); | |
418 | } | |
419 | ||
420 | static uint16_t *tilt_slashes(uint16_t *s) { | |
421 | uint16_t *p; | |
422 | ||
423 | for (p = s; *p; p++) | |
424 | if (*p == '/') | |
425 | *p = '\\'; | |
426 | ||
427 | return s; | |
428 | } | |
429 | ||
430 | char *efi_tilt_backslashes(char *s) { | |
431 | char *p; | |
432 | ||
433 | for (p = s; *p; p++) | |
434 | if (*p == '\\') | |
435 | *p = '/'; | |
436 | ||
437 | return s; | |
438 | } | |
439 | ||
440 | int efi_add_boot_option(uint16_t id, const char *title, | |
441 | uint32_t part, uint64_t pstart, uint64_t psize, | |
442 | sd_id128_t part_uuid, const char *path) { | |
443 | char boot_id[9]; | |
444 | char *buf; | |
445 | size_t size; | |
446 | size_t title_len; | |
447 | size_t path_len; | |
448 | struct boot_option *option; | |
449 | struct device_path *devicep; | |
450 | int err; | |
451 | ||
452 | title_len = (strlen(title)+1) * 2; | |
453 | path_len = (strlen(path)+1) * 2; | |
454 | ||
455 | buf = calloc(sizeof(struct boot_option) + title_len + | |
456 | sizeof(struct drive_path) + | |
457 | sizeof(struct device_path) + path_len, 1); | |
458 | if (!buf) { | |
459 | err = -ENOMEM; | |
460 | goto finish; | |
461 | } | |
462 | ||
463 | /* header */ | |
464 | option = (struct boot_option *)buf; | |
465 | option->attr = LOAD_OPTION_ACTIVE; | |
466 | option->path_len = offsetof(struct device_path, drive) + sizeof(struct drive_path) + | |
467 | offsetof(struct device_path, path) + path_len + | |
468 | offsetof(struct device_path, path); | |
469 | to_utf16(option->title, title); | |
470 | size = offsetof(struct boot_option, title) + title_len; | |
471 | ||
472 | /* partition info */ | |
473 | devicep = (struct device_path *)(buf + size); | |
474 | devicep->type = MEDIA_DEVICE_PATH; | |
475 | devicep->sub_type = MEDIA_HARDDRIVE_DP; | |
476 | devicep->length = offsetof(struct device_path, drive) + sizeof(struct drive_path); | |
477 | devicep->drive.part_nr = part; | |
478 | devicep->drive.part_start = pstart; | |
479 | devicep->drive.part_size = psize; | |
480 | devicep->drive.signature_type = SIGNATURE_TYPE_GUID; | |
481 | devicep->drive.mbr_type = MBR_TYPE_EFI_PARTITION_TABLE_HEADER; | |
482 | id128_to_efi_guid(part_uuid, devicep->drive.signature); | |
483 | size += devicep->length; | |
484 | ||
485 | /* path to loader */ | |
486 | devicep = (struct device_path *)(buf + size); | |
487 | devicep->type = MEDIA_DEVICE_PATH; | |
488 | devicep->sub_type = MEDIA_FILEPATH_DP; | |
489 | devicep->length = offsetof(struct device_path, path) + path_len; | |
490 | to_utf16(devicep->path, path); | |
491 | tilt_slashes(devicep->path); | |
492 | size += devicep->length; | |
493 | ||
494 | /* end of path */ | |
495 | devicep = (struct device_path *)(buf + size); | |
496 | devicep->type = END_DEVICE_PATH_TYPE; | |
497 | devicep->sub_type = END_ENTIRE_DEVICE_PATH_SUBTYPE; | |
498 | devicep->length = offsetof(struct device_path, path); | |
499 | size += devicep->length; | |
500 | ||
501 | snprintf(boot_id, sizeof(boot_id), "Boot%04X", id); | |
502 | err = efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, buf, size); | |
503 | ||
504 | finish: | |
505 | free(buf); | |
506 | return err; | |
507 | } | |
508 | ||
509 | int efi_remove_boot_option(uint16_t id) { | |
510 | char boot_id[9]; | |
511 | ||
512 | snprintf(boot_id, sizeof(boot_id), "Boot%04X", id); | |
513 | return efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, 0); | |
514 | } | |
515 | ||
9cde64ff | 516 | int efi_get_boot_order(uint16_t **order) { |
7b4d7cc0 KS |
517 | void *buf; |
518 | size_t l; | |
9cde64ff | 519 | int r; |
7b4d7cc0 | 520 | |
9cde64ff LP |
521 | r = efi_get_variable(EFI_VENDOR_GLOBAL, "BootOrder", NULL, &buf, &l); |
522 | if (r < 0) | |
523 | return r; | |
7b4d7cc0 | 524 | |
9cde64ff | 525 | if (l <= 0) { |
7b4d7cc0 KS |
526 | free(buf); |
527 | return -ENOENT; | |
528 | } | |
529 | ||
9cde64ff LP |
530 | if ((l % sizeof(uint16_t) > 0) || |
531 | (l / sizeof(uint16_t) > INT_MAX)) { | |
7b4d7cc0 KS |
532 | free(buf); |
533 | return -EINVAL; | |
534 | } | |
535 | ||
536 | *order = buf; | |
9cde64ff LP |
537 | return (int) (l / sizeof(uint16_t)); |
538 | } | |
539 | ||
0974a682 KS |
540 | int efi_set_boot_order(uint16_t *order, size_t n) { |
541 | return efi_set_variable(EFI_VENDOR_GLOBAL, "BootOrder", order, n * sizeof(uint16_t)); | |
542 | } | |
543 | ||
4d34c495 KS |
544 | static int boot_id_hex(const char s[4]) { |
545 | int i; | |
546 | int id = 0; | |
547 | ||
548 | for (i = 0; i < 4; i++) | |
549 | if (s[i] >= '0' && s[i] <= '9') | |
550 | id |= (s[i] - '0') << (3 - i) * 4; | |
551 | else if (s[i] >= 'A' && s[i] <= 'F') | |
552 | id |= (s[i] - 'A' + 10) << (3 - i) * 4; | |
553 | else | |
7e8185ef | 554 | return -EINVAL; |
4d34c495 KS |
555 | |
556 | return id; | |
557 | } | |
558 | ||
9db11a99 LP |
559 | static int cmp_uint16(const void *_a, const void *_b) { |
560 | const uint16_t *a = _a, *b = _b; | |
561 | ||
02a6fc3e | 562 | return (int)*a - (int)*b; |
9db11a99 LP |
563 | } |
564 | ||
9cde64ff LP |
565 | int efi_get_boot_options(uint16_t **options) { |
566 | _cleanup_closedir_ DIR *dir = NULL; | |
567 | struct dirent *de; | |
568 | uint16_t *list = NULL; | |
9db11a99 | 569 | int count = 0, r; |
9cde64ff LP |
570 | |
571 | assert(options); | |
572 | ||
573 | dir = opendir("/sys/firmware/efi/efivars/"); | |
574 | if (!dir) | |
575 | return -errno; | |
576 | ||
9db11a99 | 577 | FOREACH_DIRENT(de, dir, r = -errno; goto fail) { |
4d34c495 | 578 | int id; |
9cde64ff LP |
579 | uint16_t *t; |
580 | ||
581 | if (strncmp(de->d_name, "Boot", 4) != 0) | |
582 | continue; | |
583 | ||
4d34c495 | 584 | if (strlen(de->d_name) != 45) |
9cde64ff LP |
585 | continue; |
586 | ||
587 | if (strcmp(de->d_name + 8, "-8be4df61-93ca-11d2-aa0d-00e098032b8c") != 0) | |
588 | continue; | |
589 | ||
4d34c495 KS |
590 | id = boot_id_hex(de->d_name + 4); |
591 | if (id < 0) | |
9cde64ff LP |
592 | continue; |
593 | ||
594 | t = realloc(list, (count + 1) * sizeof(uint16_t)); | |
595 | if (!t) { | |
9db11a99 LP |
596 | r = -ENOMEM; |
597 | goto fail; | |
9cde64ff LP |
598 | } |
599 | ||
600 | list = t; | |
4d34c495 | 601 | list[count ++] = id; |
9cde64ff LP |
602 | } |
603 | ||
7ff7394d | 604 | qsort_safe(list, count, sizeof(uint16_t), cmp_uint16); |
9db11a99 | 605 | |
9cde64ff LP |
606 | *options = list; |
607 | return count; | |
9db11a99 LP |
608 | |
609 | fail: | |
610 | free(list); | |
611 | return r; | |
7b4d7cc0 KS |
612 | } |
613 | ||
5dbe9f53 | 614 | static int read_usec(sd_id128_t vendor, const char *name, usec_t *u) { |
2e3d0692 | 615 | _cleanup_free_ char *j = NULL; |
2e3d0692 | 616 | int r; |
39883f62 | 617 | uint64_t x = 0; |
2e3d0692 LP |
618 | |
619 | assert(name); | |
620 | assert(u); | |
621 | ||
61cc634b | 622 | r = efi_get_variable_string(EFI_VENDOR_LOADER, name, &j); |
2e3d0692 LP |
623 | if (r < 0) |
624 | return r; | |
625 | ||
2e3d0692 LP |
626 | r = safe_atou64(j, &x); |
627 | if (r < 0) | |
628 | return r; | |
629 | ||
5dbe9f53 | 630 | *u = x; |
2e3d0692 LP |
631 | return 0; |
632 | } | |
633 | ||
c51d84dc | 634 | int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader) { |
2e3d0692 LP |
635 | uint64_t x, y; |
636 | int r; | |
2e3d0692 LP |
637 | |
638 | assert(firmware); | |
639 | assert(loader); | |
640 | ||
e9cea16d | 641 | r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeInitUSec", &x); |
2e3d0692 LP |
642 | if (r < 0) |
643 | return r; | |
644 | ||
e9cea16d | 645 | r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeExecUSec", &y); |
2e3d0692 LP |
646 | if (r < 0) |
647 | return r; | |
648 | ||
649 | if (y == 0 || y < x) | |
650 | return -EIO; | |
651 | ||
652 | if (y > USEC_PER_HOUR) | |
653 | return -EIO; | |
654 | ||
655 | *firmware = x; | |
656 | *loader = y; | |
657 | ||
658 | return 0; | |
659 | } | |
660 | ||
c51d84dc | 661 | int efi_loader_get_device_part_uuid(sd_id128_t *u) { |
f4ce2b3e | 662 | _cleanup_free_ char *p = NULL; |
f4ce2b3e | 663 | int r, parsed[16]; |
f4ce2b3e | 664 | |
61cc634b | 665 | r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderDevicePartUUID", &p); |
f4ce2b3e LP |
666 | if (r < 0) |
667 | return r; | |
668 | ||
f4ce2b3e LP |
669 | if (sscanf(p, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", |
670 | &parsed[0], &parsed[1], &parsed[2], &parsed[3], | |
671 | &parsed[4], &parsed[5], &parsed[6], &parsed[7], | |
672 | &parsed[8], &parsed[9], &parsed[10], &parsed[11], | |
673 | &parsed[12], &parsed[13], &parsed[14], &parsed[15]) != 16) | |
674 | return -EIO; | |
675 | ||
73b80ec2 LP |
676 | if (u) { |
677 | unsigned i; | |
678 | ||
679 | for (i = 0; i < ELEMENTSOF(parsed); i++) | |
680 | u->bytes[i] = parsed[i]; | |
681 | } | |
f4ce2b3e LP |
682 | |
683 | return 0; | |
684 | } | |
b872e9a0 LP |
685 | |
686 | #endif |