chiark / gitweb /
efi: various cleanups
[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
26 #include "util.h"
27 #include "utf8.h"
28 #include "efivars.h"
29
30 bool is_efi_boot(void) {
31         return access("/sys/firmware/efi", F_OK) >= 0;
32 }
33
34 int efi_get_variable(
35                 sd_id128_t vendor,
36                 const char *name,
37                 uint32_t *attribute,
38                 void **value,
39                 size_t *size) {
40
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)
70                 return -errno;
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;
93         *size = (size_t) st.st_size - 4;
94
95         if (attribute)
96                 *attribute = a;
97
98         return 0;
99 }
100
101 int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
102         _cleanup_free_ void *s = NULL;
103         size_t ss;
104         int r;
105         char *x;
106
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;
117 }
118
119 static size_t utf16_size(const uint16_t *s) {
120         size_t l = 0;
121
122         while (s[l] > 0)
123                 l++;
124
125         return (l+1) * sizeof(uint16_t);
126 }
127
128 static 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
148 int efi_get_boot_option(
149                 uint16_t id,
150                 char **title,
151                 sd_id128_t *part_uuid,
152                 char **path) {
153
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
179         char boot_id[9];
180         _cleanup_free_ uint8_t *buf = NULL;
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;
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;
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) {
208                 uint8_t *dbuf;
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
252         if (title)
253                 *title = s;
254         if (part_uuid)
255                 *part_uuid = p_uuid;
256         if (path)
257                 *path = p;
258
259         return 0;
260 err:
261         free(s);
262         free(p);
263         return err;
264 }
265
266 int efi_get_boot_order(uint16_t **order) {
267         void *buf;
268         size_t l;
269         int r;
270
271         r = efi_get_variable(EFI_VENDOR_GLOBAL, "BootOrder", NULL, &buf, &l);
272         if (r < 0)
273                 return r;
274
275         if (l <= 0) {
276                 free(buf);
277                 return -ENOENT;
278         }
279
280         if ((l % sizeof(uint16_t) > 0) ||
281             (l / sizeof(uint16_t) > INT_MAX)) {
282                 free(buf);
283                 return -EINVAL;
284         }
285
286         *order = buf;
287         return (int) (l / sizeof(uint16_t));
288 }
289
290 int 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;
338 }
339
340 static int read_usec(sd_id128_t vendor, const char *name, usec_t *u) {
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
362         *u = x;
363         return 0;
364 }
365
366 static int get_boot_usec(usec_t *firmware, usec_t *loader) {
367         uint64_t x, y;
368         int r;
369
370         assert(firmware);
371         assert(loader);
372
373         r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeInitUSec", &x);
374         if (r < 0)
375                 return r;
376
377         r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeExecUSec", &y);
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
393 int 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 }
428
429 int 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 }