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