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