]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/efivars.c
build-sys: make PolicyKit support compile-time optional (was runtime-optional already)
[thirdparty/systemd.git] / src / shared / efivars.c
CommitLineData
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
LP
26
27#include "util.h"
28#include "utf8.h"
29#include "efivars.h"
30
9cde64ff 31bool is_efi_boot(void) {
34e5a31e
LP
32 return access("/sys/firmware/efi", F_OK) >= 0;
33}
34
9cde64ff
LP
35int efi_get_variable(
36 sd_id128_t vendor,
37 const char *name,
38 uint32_t *attribute,
39 void **value,
40 size_t *size) {
41
2e3d0692
LP
42 _cleanup_close_ int fd = -1;
43 _cleanup_free_ char *p = NULL;
44 uint32_t a;
45 ssize_t n;
46 struct stat st;
47 void *r;
48
49 assert(name);
50 assert(value);
51 assert(size);
52
53 if (asprintf(&p,
54 "/sys/firmware/efi/efivars/%s-%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
55 name, SD_ID128_FORMAT_VAL(vendor)) < 0)
56 return -ENOMEM;
57
58 fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
59 if (fd < 0)
60 return -errno;
61
62 if (fstat(fd, &st) < 0)
63 return -errno;
64 if (st.st_size < 4)
65 return -EIO;
66 if (st.st_size > 4*1024*1024 + 4)
67 return -E2BIG;
68
69 n = read(fd, &a, sizeof(a));
70 if (n < 0)
9cde64ff 71 return -errno;
2e3d0692
LP
72 if (n != sizeof(a))
73 return -EIO;
74
75 r = malloc(st.st_size - 4 + 2);
76 if (!r)
77 return -ENOMEM;
78
79 n = read(fd, r, (size_t) st.st_size - 4);
80 if (n < 0) {
81 free(r);
82 return (int) -n;
83 }
84 if (n != (ssize_t) st.st_size - 4) {
85 free(r);
86 return -EIO;
87 }
88
89 /* Always NUL terminate (2 bytes, to protect UTF-16) */
90 ((char*) r)[st.st_size - 4] = 0;
91 ((char*) r)[st.st_size - 4 + 1] = 0;
92
93 *value = r;
ff47c895 94 *size = (size_t) st.st_size - 4;
2e3d0692
LP
95
96 if (attribute)
97 *attribute = a;
98
99 return 0;
100}
101
9cde64ff 102int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
7b4d7cc0
KS
103 _cleanup_free_ void *s = NULL;
104 size_t ss;
9cde64ff
LP
105 int r;
106 char *x;
7b4d7cc0 107
9cde64ff
LP
108 r = efi_get_variable(vendor, name, NULL, &s, &ss);
109 if (r < 0)
110 return r;
111
112 x = utf16_to_utf8(s, ss);
113 if (!x)
114 return -ENOMEM;
115
116 *p = x;
117 return 0;
7b4d7cc0
KS
118}
119
120static size_t utf16_size(const uint16_t *s) {
121 size_t l = 0;
122
123 while (s[l] > 0)
124 l++;
9cde64ff 125
7b4d7cc0
KS
126 return (l+1) * sizeof(uint16_t);
127}
128
129static void efi_guid_to_id128(const void *guid, sd_id128_t *id128) {
130 struct uuid {
131 uint32_t u1;
132 uint16_t u2;
133 uint16_t u3;
134 uint8_t u4[8];
135 } _packed_;
136 const struct uuid *uuid = guid;
137
138 id128->bytes[0] = (uuid->u1 >> 24) & 0xff;
139 id128->bytes[1] = (uuid->u1 >> 16) & 0xff;
140 id128->bytes[2] = (uuid->u1 >> 8) & 0xff;
141 id128->bytes[3] = (uuid->u1) & 0xff;
142 id128->bytes[4] = (uuid->u2 >> 8) & 0xff;
143 id128->bytes[5] = (uuid->u2) & 0xff;
144 id128->bytes[6] = (uuid->u3 >> 8) & 0xff;
145 id128->bytes[7] = (uuid->u3) & 0xff;
146 memcpy(&id128->bytes[8], uuid->u4, sizeof(uuid->u4));
147}
148
9cde64ff
LP
149int efi_get_boot_option(
150 uint16_t id,
151 char **title,
152 sd_id128_t *part_uuid,
153 char **path) {
154
7b4d7cc0
KS
155 struct boot_option {
156 uint32_t attr;
157 uint16_t path_len;
158 uint16_t title[];
159 } _packed_;
160
161 struct drive_path {
162 uint32_t part_nr;
163 uint64_t part_start;
164 uint64_t part_size;
165 char signature[16];
166 uint8_t mbr_type;
167 uint8_t signature_type;
168 } _packed_;
169
170 struct device_path {
171 uint8_t type;
172 uint8_t sub_type;
173 uint16_t length;
174 union {
175 uint16_t path[0];
176 struct drive_path drive;
177 };
178 } _packed_;
179
9cde64ff
LP
180 char boot_id[9];
181 _cleanup_free_ uint8_t *buf = NULL;
7b4d7cc0
KS
182 size_t l;
183 struct boot_option *header;
184 size_t title_size;
185 char *s = NULL;
186 char *p = NULL;
187 sd_id128_t p_uuid = SD_ID128_NULL;
7b4d7cc0
KS
188 int err;
189
190 snprintf(boot_id, sizeof(boot_id), "Boot%04X", id);
191 err = efi_get_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, (void **)&buf, &l);
192 if (err < 0)
193 return err;
7b4d7cc0
KS
194 if (l < sizeof(struct boot_option))
195 return -ENOENT;
196
197 header = (struct boot_option *)buf;
198 title_size = utf16_size(header->title);
199 if (title_size > l - offsetof(struct boot_option, title))
200 return -EINVAL;
201
202 s = utf16_to_utf8(header->title, title_size);
203 if (!s) {
204 err = -ENOMEM;
205 goto err;
206 }
207
208 if (header->path_len > 0) {
9cde64ff 209 uint8_t *dbuf;
7b4d7cc0
KS
210 size_t dnext;
211
212 dbuf = buf + offsetof(struct boot_option, title) + title_size;
213 dnext = 0;
214 while (dnext < header->path_len) {
215 struct device_path *dpath;
216
217 dpath = (struct device_path *)(dbuf + dnext);
218 if (dpath->length < 4)
219 break;
220
221 /* Type 0x7F – End of Hardware Device Path, Sub-Type 0xFF – End Entire Device Path */
222 if (dpath->type == 0x7f && dpath->sub_type == 0xff)
223 break;
224
225 dnext += dpath->length;
226
227 /* Type 0x04 – Media Device Path */
228 if (dpath->type != 0x04)
229 continue;
230
231 /* Sub-Type 1 – Hard Drive */
232 if (dpath->sub_type == 0x01) {
233 /* 0x02 – GUID Partition Table */
234 if (dpath->drive.mbr_type != 0x02)
235 continue;
236
237 /* 0x02 – GUID signature */
238 if (dpath->drive.signature_type != 0x02)
239 continue;
240
241 efi_guid_to_id128(dpath->drive.signature, &p_uuid);
242 continue;
243 }
244
245 /* Sub-Type 4 – File Path */
246 if (dpath->sub_type == 0x04) {
247 p = utf16_to_utf8(dpath->path, dpath->length-4);
248 continue;
249 }
250 }
251 }
252
9cde64ff
LP
253 if (title)
254 *title = s;
7b4d7cc0
KS
255 if (part_uuid)
256 *part_uuid = p_uuid;
257 if (path)
258 *path = p;
9cde64ff 259
7b4d7cc0
KS
260 return 0;
261err:
262 free(s);
263 free(p);
7b4d7cc0
KS
264 return err;
265}
266
9cde64ff 267int efi_get_boot_order(uint16_t **order) {
7b4d7cc0
KS
268 void *buf;
269 size_t l;
9cde64ff 270 int r;
7b4d7cc0 271
9cde64ff
LP
272 r = efi_get_variable(EFI_VENDOR_GLOBAL, "BootOrder", NULL, &buf, &l);
273 if (r < 0)
274 return r;
7b4d7cc0 275
9cde64ff 276 if (l <= 0) {
7b4d7cc0
KS
277 free(buf);
278 return -ENOENT;
279 }
280
9cde64ff
LP
281 if ((l % sizeof(uint16_t) > 0) ||
282 (l / sizeof(uint16_t) > INT_MAX)) {
7b4d7cc0
KS
283 free(buf);
284 return -EINVAL;
285 }
286
287 *order = buf;
9cde64ff
LP
288 return (int) (l / sizeof(uint16_t));
289}
290
4d34c495
KS
291static int boot_id_hex(const char s[4]) {
292 int i;
293 int id = 0;
294
295 for (i = 0; i < 4; i++)
296 if (s[i] >= '0' && s[i] <= '9')
297 id |= (s[i] - '0') << (3 - i) * 4;
298 else if (s[i] >= 'A' && s[i] <= 'F')
299 id |= (s[i] - 'A' + 10) << (3 - i) * 4;
300 else
301 return -1;
302
303 return id;
304}
305
9db11a99
LP
306static int cmp_uint16(const void *_a, const void *_b) {
307 const uint16_t *a = _a, *b = _b;
308
309 if (*a < *b)
310 return -1;
311 if (*a > *b)
312 return 1;
313
314 return 0;
315}
316
9cde64ff
LP
317int efi_get_boot_options(uint16_t **options) {
318 _cleanup_closedir_ DIR *dir = NULL;
319 struct dirent *de;
320 uint16_t *list = NULL;
9db11a99 321 int count = 0, r;
9cde64ff
LP
322
323 assert(options);
324
325 dir = opendir("/sys/firmware/efi/efivars/");
326 if (!dir)
327 return -errno;
328
9db11a99 329 FOREACH_DIRENT(de, dir, r = -errno; goto fail) {
4d34c495 330 int id;
9cde64ff
LP
331 uint16_t *t;
332
333 if (strncmp(de->d_name, "Boot", 4) != 0)
334 continue;
335
4d34c495 336 if (strlen(de->d_name) != 45)
9cde64ff
LP
337 continue;
338
339 if (strcmp(de->d_name + 8, "-8be4df61-93ca-11d2-aa0d-00e098032b8c") != 0)
340 continue;
341
4d34c495
KS
342 id = boot_id_hex(de->d_name + 4);
343 if (id < 0)
9cde64ff
LP
344 continue;
345
346 t = realloc(list, (count + 1) * sizeof(uint16_t));
347 if (!t) {
9db11a99
LP
348 r = -ENOMEM;
349 goto fail;
9cde64ff
LP
350 }
351
352 list = t;
4d34c495 353 list[count ++] = id;
9cde64ff
LP
354 }
355
9db11a99
LP
356 qsort(list, count, sizeof(uint16_t), cmp_uint16);
357
9cde64ff
LP
358 *options = list;
359 return count;
9db11a99
LP
360
361fail:
362 free(list);
363 return r;
7b4d7cc0
KS
364}
365
5dbe9f53 366static int read_usec(sd_id128_t vendor, const char *name, usec_t *u) {
2e3d0692 367 _cleanup_free_ char *j = NULL;
2e3d0692
LP
368 int r;
369 uint64_t x;
370
371 assert(name);
372 assert(u);
373
61cc634b 374 r = efi_get_variable_string(EFI_VENDOR_LOADER, name, &j);
2e3d0692
LP
375 if (r < 0)
376 return r;
377
2e3d0692
LP
378 r = safe_atou64(j, &x);
379 if (r < 0)
380 return r;
381
5dbe9f53 382 *u = x;
2e3d0692
LP
383 return 0;
384}
385
386static int get_boot_usec(usec_t *firmware, usec_t *loader) {
387 uint64_t x, y;
388 int r;
2e3d0692
LP
389
390 assert(firmware);
391 assert(loader);
392
e9cea16d 393 r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeInitUSec", &x);
2e3d0692
LP
394 if (r < 0)
395 return r;
396
e9cea16d 397 r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeExecUSec", &y);
2e3d0692
LP
398 if (r < 0)
399 return r;
400
401 if (y == 0 || y < x)
402 return -EIO;
403
404 if (y > USEC_PER_HOUR)
405 return -EIO;
406
407 *firmware = x;
408 *loader = y;
409
410 return 0;
411}
412
413int efi_get_boot_timestamps(const dual_timestamp *n, dual_timestamp *firmware, dual_timestamp *loader) {
414 usec_t x, y, a;
415 int r;
416 dual_timestamp _n;
417
418 assert(firmware);
419 assert(loader);
420
421 if (!n) {
422 dual_timestamp_get(&_n);
423 n = &_n;
424 }
425
426 r = get_boot_usec(&x, &y);
427 if (r < 0)
428 return r;
429
430 /* Let's convert this to timestamps where the firmware
431 * began/loader began working. To make this more confusing:
432 * since usec_t is unsigned and the kernel's monotonic clock
433 * begins at kernel initialization we'll actually initialize
434 * the monotonic timestamps here as negative of the actual
435 * value. */
436
437 firmware->monotonic = y;
438 loader->monotonic = y - x;
439
440 a = n->monotonic + firmware->monotonic;
441 firmware->realtime = n->realtime > a ? n->realtime - a : 0;
442
443 a = n->monotonic + loader->monotonic;
444 loader->realtime = n->realtime > a ? n->realtime - a : 0;
445
446 return 0;
447}
f4ce2b3e
LP
448
449int efi_get_loader_device_part_uuid(sd_id128_t *u) {
f4ce2b3e 450 _cleanup_free_ char *p = NULL;
f4ce2b3e
LP
451 int r, parsed[16];
452 unsigned i;
453
454 assert(u);
455
61cc634b 456 r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderDevicePartUUID", &p);
f4ce2b3e
LP
457 if (r < 0)
458 return r;
459
f4ce2b3e
LP
460 if (sscanf(p, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
461 &parsed[0], &parsed[1], &parsed[2], &parsed[3],
462 &parsed[4], &parsed[5], &parsed[6], &parsed[7],
463 &parsed[8], &parsed[9], &parsed[10], &parsed[11],
464 &parsed[12], &parsed[13], &parsed[14], &parsed[15]) != 16)
465 return -EIO;
466
467 for (i = 0; i < ELEMENTSOF(parsed); i++)
468 u->bytes[i] = parsed[i];
469
470 return 0;
471}