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