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