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