]> git.proxmox.com Git - systemd.git/blame - src/shared/efivars.c
Imported Upstream version 217
[systemd.git] / src / shared / efivars.c
CommitLineData
663996b3
MS
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>
23#include <string.h>
24#include <fcntl.h>
25#include <ctype.h>
26
14228c0d 27#include "acpi-fpdt.h"
663996b3
MS
28#include "util.h"
29#include "utf8.h"
30#include "efivars.h"
31
32#ifdef ENABLE_EFI
33
34bool is_efi_boot(void) {
35 return access("/sys/firmware/efi", F_OK) >= 0;
36}
37
38static int read_flag(const char *varname) {
39 int r;
60f067b4 40 _cleanup_free_ void *v = NULL;
663996b3
MS
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
60f067b4
JS
48 if (s != 1)
49 return -EINVAL;
663996b3
MS
50
51 b = *(uint8_t *)v;
52 r = b > 0;
663996b3
MS
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
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
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)
100 return -errno;
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);
111 return -errno;
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;
123 *size = (size_t) st.st_size - 4;
124
125 if (attribute)
126 *attribute = a;
127
128 return 0;
129}
130
131int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
132 _cleanup_free_ void *s = NULL;
60f067b4 133 size_t ss = 0;
663996b3
MS
134 int r;
135 char *x;
136
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;
147}
148
149static size_t utf16_size(const uint16_t *s) {
150 size_t l = 0;
151
152 while (s[l] > 0)
153 l++;
154
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
178int efi_get_boot_option(
179 uint16_t id,
180 char **title,
181 sd_id128_t *part_uuid,
182 char **path) {
183
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
209 char boot_id[9];
210 _cleanup_free_ uint8_t *buf = NULL;
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;
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;
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
231 if (title) {
232 s = utf16_to_utf8(header->title, title_size);
233 if (!s) {
234 err = -ENOMEM;
235 goto err;
236 }
237 }
238
239 if (header->path_len > 0) {
240 uint8_t *dbuf;
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
272 if (part_uuid)
273 efi_guid_to_id128(dpath->drive.signature, &p_uuid);
274 continue;
275 }
276
277 /* Sub-Type 4 – File Path */
278 if (dpath->sub_type == 0x04 && !p && path) {
279 p = utf16_to_utf8(dpath->path, dpath->length-4);
280 continue;
281 }
282 }
283 }
284
285 if (title)
286 *title = s;
287 if (part_uuid)
288 *part_uuid = p_uuid;
289 if (path)
290 *path = p;
291
292 return 0;
293err:
294 free(s);
295 free(p);
296 return err;
297}
298
299int efi_get_boot_order(uint16_t **order) {
300 void *buf;
301 size_t l;
302 int r;
303
304 r = efi_get_variable(EFI_VENDOR_GLOBAL, "BootOrder", NULL, &buf, &l);
305 if (r < 0)
306 return r;
307
308 if (l <= 0) {
309 free(buf);
310 return -ENOENT;
311 }
312
313 if ((l % sizeof(uint16_t) > 0) ||
314 (l / sizeof(uint16_t) > INT_MAX)) {
315 free(buf);
316 return -EINVAL;
317 }
318
319 *order = buf;
320 return (int) (l / sizeof(uint16_t));
321}
322
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
5eef597e 333 return -EINVAL;
663996b3
MS
334
335 return id;
336}
337
338static int cmp_uint16(const void *_a, const void *_b) {
339 const uint16_t *a = _a, *b = _b;
340
341 return (int)*a - (int)*b;
342}
343
344int efi_get_boot_options(uint16_t **options) {
345 _cleanup_closedir_ DIR *dir = NULL;
346 struct dirent *de;
347 uint16_t *list = NULL;
348 int count = 0, r;
349
350 assert(options);
351
352 dir = opendir("/sys/firmware/efi/efivars/");
353 if (!dir)
354 return -errno;
355
356 FOREACH_DIRENT(de, dir, r = -errno; goto fail) {
357 int id;
358 uint16_t *t;
359
360 if (strncmp(de->d_name, "Boot", 4) != 0)
361 continue;
362
363 if (strlen(de->d_name) != 45)
364 continue;
365
366 if (strcmp(de->d_name + 8, "-8be4df61-93ca-11d2-aa0d-00e098032b8c") != 0)
367 continue;
368
369 id = boot_id_hex(de->d_name + 4);
370 if (id < 0)
371 continue;
372
373 t = realloc(list, (count + 1) * sizeof(uint16_t));
374 if (!t) {
375 r = -ENOMEM;
376 goto fail;
377 }
378
379 list = t;
380 list[count ++] = id;
381 }
382
60f067b4 383 qsort_safe(list, count, sizeof(uint16_t), cmp_uint16);
663996b3
MS
384
385 *options = list;
386 return count;
387
388fail:
389 free(list);
390 return r;
391}
392
393static int read_usec(sd_id128_t vendor, const char *name, usec_t *u) {
394 _cleanup_free_ char *j = NULL;
395 int r;
60f067b4 396 uint64_t x = 0;
663996b3
MS
397
398 assert(name);
399 assert(u);
400
401 r = efi_get_variable_string(EFI_VENDOR_LOADER, name, &j);
402 if (r < 0)
403 return r;
404
405 r = safe_atou64(j, &x);
406 if (r < 0)
407 return r;
408
409 *u = x;
410 return 0;
411}
412
14228c0d 413int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader) {
663996b3
MS
414 uint64_t x, y;
415 int r;
416
417 assert(firmware);
418 assert(loader);
419
420 r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeInitUSec", &x);
421 if (r < 0)
422 return r;
423
424 r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeExecUSec", &y);
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
14228c0d 440int efi_loader_get_device_part_uuid(sd_id128_t *u) {
663996b3
MS
441 _cleanup_free_ char *p = NULL;
442 int r, parsed[16];
663996b3
MS
443
444 r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderDevicePartUUID", &p);
445 if (r < 0)
446 return r;
447
448 if (sscanf(p, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
449 &parsed[0], &parsed[1], &parsed[2], &parsed[3],
450 &parsed[4], &parsed[5], &parsed[6], &parsed[7],
451 &parsed[8], &parsed[9], &parsed[10], &parsed[11],
452 &parsed[12], &parsed[13], &parsed[14], &parsed[15]) != 16)
453 return -EIO;
454
60f067b4
JS
455 if (u) {
456 unsigned i;
457
458 for (i = 0; i < ELEMENTSOF(parsed); i++)
459 u->bytes[i] = parsed[i];
460 }
663996b3
MS
461
462 return 0;
463}
464
465#endif