chiark / gitweb /
efi: unify BootXXXX reading
[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 int efi_get_boot_options(uint16_t **options) {
307         _cleanup_closedir_ DIR *dir = NULL;
308         struct dirent *de;
309         uint16_t *list = NULL;
310         int count = 0;
311
312         assert(options);
313
314         dir = opendir("/sys/firmware/efi/efivars/");
315         if (!dir)
316                 return -errno;
317
318         while ((de = readdir(dir))) {
319                 int id;
320                 uint16_t *t;
321
322                 if (strncmp(de->d_name, "Boot", 4) != 0)
323                         continue;
324
325                 if (strlen(de->d_name) != 45)
326                         continue;
327
328                 if (strcmp(de->d_name + 8, "-8be4df61-93ca-11d2-aa0d-00e098032b8c") != 0)
329                         continue;
330
331                 id = boot_id_hex(de->d_name + 4);
332                 if (id < 0)
333                         continue;
334
335                 t = realloc(list, (count + 1) * sizeof(uint16_t));
336                 if (!t) {
337                         free(list);
338                         return -ENOMEM;
339                 }
340
341                 list = t;
342                 list[count ++] = id;
343
344         }
345
346         *options = list;
347         return count;
348 }
349
350 static int read_usec(sd_id128_t vendor, const char *name, usec_t *u) {
351         _cleanup_free_ void *i = NULL;
352         _cleanup_free_ char *j = NULL;
353         size_t is;
354         int r;
355         uint64_t x;
356
357         assert(name);
358         assert(u);
359
360         r = efi_get_variable(EFI_VENDOR_LOADER, name, NULL, &i, &is);
361         if (r < 0)
362                 return r;
363
364         j = utf16_to_utf8(i, is);
365         if (!j)
366                 return -ENOMEM;
367
368         r = safe_atou64(j, &x);
369         if (r < 0)
370                 return r;
371
372         *u = x;
373         return 0;
374 }
375
376 static int get_boot_usec(usec_t *firmware, usec_t *loader) {
377         uint64_t x, y;
378         int r;
379
380         assert(firmware);
381         assert(loader);
382
383         r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeInitUSec", &x);
384         if (r < 0)
385                 return r;
386
387         r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeExecUSec", &y);
388         if (r < 0)
389                 return r;
390
391         if (y == 0 || y < x)
392                 return -EIO;
393
394         if (y > USEC_PER_HOUR)
395                 return -EIO;
396
397         *firmware = x;
398         *loader = y;
399
400         return 0;
401 }
402
403 int efi_get_boot_timestamps(const dual_timestamp *n, dual_timestamp *firmware, dual_timestamp *loader) {
404         usec_t x, y, a;
405         int r;
406         dual_timestamp _n;
407
408         assert(firmware);
409         assert(loader);
410
411         if (!n) {
412                 dual_timestamp_get(&_n);
413                 n = &_n;
414         }
415
416         r = get_boot_usec(&x, &y);
417         if (r < 0)
418                 return r;
419
420         /* Let's convert this to timestamps where the firmware
421          * began/loader began working. To make this more confusing:
422          * since usec_t is unsigned and the kernel's monotonic clock
423          * begins at kernel initialization we'll actually initialize
424          * the monotonic timestamps here as negative of the actual
425          * value. */
426
427         firmware->monotonic = y;
428         loader->monotonic = y - x;
429
430         a = n->monotonic + firmware->monotonic;
431         firmware->realtime = n->realtime > a ? n->realtime - a : 0;
432
433         a = n->monotonic + loader->monotonic;
434         loader->realtime = n->realtime > a ? n->realtime - a : 0;
435
436         return 0;
437 }
438
439 int efi_get_loader_device_part_uuid(sd_id128_t *u) {
440         _cleanup_free_ void *s = NULL;
441         _cleanup_free_ char *p = NULL;
442         size_t ss;
443         int r, parsed[16];
444         unsigned i;
445
446         assert(u);
447
448         r = efi_get_variable(EFI_VENDOR_LOADER, "LoaderDevicePartUUID", NULL, &s, &ss);
449         if (r < 0)
450                 return r;
451
452         p = utf16_to_utf8(s, ss);
453         if (!p)
454                 return -ENOMEM;
455
456         if (sscanf(p, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
457                    &parsed[0], &parsed[1], &parsed[2], &parsed[3],
458                    &parsed[4], &parsed[5], &parsed[6], &parsed[7],
459                    &parsed[8], &parsed[9], &parsed[10], &parsed[11],
460                    &parsed[12], &parsed[13], &parsed[14], &parsed[15]) != 16)
461                 return -EIO;
462
463         for (i = 0; i < ELEMENTSOF(parsed); i++)
464                 u->bytes[i] = parsed[i];
465
466         return 0;
467 }