chiark / gitweb /
efi: efi_get_boot_options() should already sort the entries, the random order in...
[elogind.git] / src / shared / efivars.c
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
27 #include "util.h"
28 #include "utf8.h"
29 #include "efivars.h"
30
31 bool is_efi_boot(void) {
32         return access("/sys/firmware/efi", F_OK) >= 0;
33 }
34
35 int efi_get_variable(
36                 sd_id128_t vendor,
37                 const char *name,
38                 uint32_t *attribute,
39                 void **value,
40                 size_t *size) {
41
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)
71                 return -errno;
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;
94         *size = (size_t) st.st_size - 4;
95
96         if (attribute)
97                 *attribute = a;
98
99         return 0;
100 }
101
102 int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
103         _cleanup_free_ void *s = NULL;
104         size_t ss;
105         int r;
106         char *x;
107
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;
118 }
119
120 static size_t utf16_size(const uint16_t *s) {
121         size_t l = 0;
122
123         while (s[l] > 0)
124                 l++;
125
126         return (l+1) * sizeof(uint16_t);
127 }
128
129 static 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
149 int efi_get_boot_option(
150                 uint16_t id,
151                 char **title,
152                 sd_id128_t *part_uuid,
153                 char **path) {
154
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
180         char boot_id[9];
181         _cleanup_free_ uint8_t *buf = NULL;
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;
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;
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) {
209                 uint8_t *dbuf;
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
253         if (title)
254                 *title = s;
255         if (part_uuid)
256                 *part_uuid = p_uuid;
257         if (path)
258                 *path = p;
259
260         return 0;
261 err:
262         free(s);
263         free(p);
264         return err;
265 }
266
267 int efi_get_boot_order(uint16_t **order) {
268         void *buf;
269         size_t l;
270         int r;
271
272         r = efi_get_variable(EFI_VENDOR_GLOBAL, "BootOrder", NULL, &buf, &l);
273         if (r < 0)
274                 return r;
275
276         if (l <= 0) {
277                 free(buf);
278                 return -ENOENT;
279         }
280
281         if ((l % sizeof(uint16_t) > 0) ||
282             (l / sizeof(uint16_t) > INT_MAX)) {
283                 free(buf);
284                 return -EINVAL;
285         }
286
287         *order = buf;
288         return (int) (l / sizeof(uint16_t));
289 }
290
291 static 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
306 static 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
317 int efi_get_boot_options(uint16_t **options) {
318         _cleanup_closedir_ DIR *dir = NULL;
319         struct dirent *de;
320         uint16_t *list = NULL;
321         int count = 0, r;
322
323         assert(options);
324
325         dir = opendir("/sys/firmware/efi/efivars/");
326         if (!dir)
327                 return -errno;
328
329         FOREACH_DIRENT(de, dir, r = -errno; goto fail) {
330                 int id;
331                 uint16_t *t;
332
333                 if (strncmp(de->d_name, "Boot", 4) != 0)
334                         continue;
335
336                 if (strlen(de->d_name) != 45)
337                         continue;
338
339                 if (strcmp(de->d_name + 8, "-8be4df61-93ca-11d2-aa0d-00e098032b8c") != 0)
340                         continue;
341
342                 id = boot_id_hex(de->d_name + 4);
343                 if (id < 0)
344                         continue;
345
346                 t = realloc(list, (count + 1) * sizeof(uint16_t));
347                 if (!t) {
348                         r = -ENOMEM;
349                         goto fail;
350                 }
351
352                 list = t;
353                 list[count ++] = id;
354         }
355
356         qsort(list, count, sizeof(uint16_t), cmp_uint16);
357
358         *options = list;
359         return count;
360
361 fail:
362         free(list);
363         return r;
364 }
365
366 static int read_usec(sd_id128_t vendor, const char *name, usec_t *u) {
367         _cleanup_free_ char *j = NULL;
368         int r;
369         uint64_t x;
370
371         assert(name);
372         assert(u);
373
374         r = efi_get_variable_string(EFI_VENDOR_LOADER, name, &j);
375         if (r < 0)
376                 return r;
377
378         r = safe_atou64(j, &x);
379         if (r < 0)
380                 return r;
381
382         *u = x;
383         return 0;
384 }
385
386 static int get_boot_usec(usec_t *firmware, usec_t *loader) {
387         uint64_t x, y;
388         int r;
389
390         assert(firmware);
391         assert(loader);
392
393         r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeInitUSec", &x);
394         if (r < 0)
395                 return r;
396
397         r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeExecUSec", &y);
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
413 int 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 }
448
449 int efi_get_loader_device_part_uuid(sd_id128_t *u) {
450         _cleanup_free_ char *p = NULL;
451         int r, parsed[16];
452         unsigned i;
453
454         assert(u);
455
456         r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderDevicePartUUID", &p);
457         if (r < 0)
458                 return r;
459
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 }