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