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