chiark / gitweb /
util: move is_efiboot() to efivars.c
[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 <fcntl.h>
24
25 #include "util.h"
26 #include "utf8.h"
27 #include "efivars.h"
28
29 #define EFI_VENDOR_LOADER SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f)
30
31 bool is_efiboot(void) {
32         return access("/sys/firmware/efi", F_OK) >= 0;
33 }
34
35 int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size) {
36         _cleanup_close_ int fd = -1;
37         _cleanup_free_ char *p = NULL;
38         uint32_t a;
39         ssize_t n;
40         struct stat st;
41         void *r;
42
43         assert(name);
44         assert(value);
45         assert(size);
46
47         if (asprintf(&p,
48                      "/sys/firmware/efi/efivars/%s-%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
49                      name, SD_ID128_FORMAT_VAL(vendor)) < 0)
50                 return -ENOMEM;
51
52         fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
53         if (fd < 0)
54                 return -errno;
55
56         if (fstat(fd, &st) < 0)
57                 return -errno;
58         if (st.st_size < 4)
59                 return -EIO;
60         if (st.st_size > 4*1024*1024 + 4)
61                 return -E2BIG;
62
63         n = read(fd, &a, sizeof(a));
64         if (n < 0)
65                 return (int) n;
66         if (n != sizeof(a))
67                 return -EIO;
68
69         r = malloc(st.st_size - 4 + 2);
70         if (!r)
71                 return -ENOMEM;
72
73         n = read(fd, r, (size_t) st.st_size - 4);
74         if (n < 0) {
75                 free(r);
76                 return (int) -n;
77         }
78         if (n != (ssize_t) st.st_size - 4) {
79                 free(r);
80                 return -EIO;
81         }
82
83         /* Always NUL terminate (2 bytes, to protect UTF-16) */
84         ((char*) r)[st.st_size - 4] = 0;
85         ((char*) r)[st.st_size - 4 + 1] = 0;
86
87         *value = r;
88         *size = (size_t) st.st_size;
89
90         if (attribute)
91                 *attribute = a;
92
93         return 0;
94 }
95
96 static int read_bogomips(unsigned long *u) {
97         _cleanup_fclose_ FILE *f = NULL;
98
99         f = fopen("/proc/cpuinfo", "re");
100         if (!f)
101                 return -errno;
102
103         while (!feof(f)) {
104                 char line[LINE_MAX];
105                 char *x;
106                 unsigned long a, b;
107
108                 if (!fgets(line, sizeof(line), f))
109                         return -EIO;
110
111                 char_array_0(line);
112                 truncate_nl(line);
113
114                 if (!startswith(line, "bogomips"))
115                         continue;
116
117                 x = line + 8;
118                 x += strspn(x, WHITESPACE);
119                 if (*x != ':')
120                         continue;
121                 x++;
122                 x += strspn(x, WHITESPACE);
123
124                 if (sscanf(x, "%lu.%lu", &a, &b) != 2)
125                         continue;
126
127                 *u = a * 1000000L + b * 10000L;
128                 return 0;
129         }
130
131         return -EIO;
132 }
133
134 static int read_ticks(sd_id128_t vendor, const char *name, unsigned long speed, usec_t *u) {
135         _cleanup_free_ void *i = NULL;
136         _cleanup_free_ char *j = NULL;
137         size_t is;
138         int r;
139         uint64_t x;
140
141         assert(name);
142         assert(u);
143
144         r = efi_get_variable(EFI_VENDOR_LOADER, name, NULL, &i, &is);
145         if (r < 0)
146                 return r;
147
148         j = utf16_to_utf8(i, is);
149         if (!j)
150                 return -ENOMEM;
151
152         r = safe_atou64(j, &x);
153         if (r < 0)
154                 return r;
155
156         *u = USEC_PER_SEC * x / speed;
157         return 0;
158 }
159
160 static int get_boot_usec(usec_t *firmware, usec_t *loader) {
161         uint64_t x, y;
162         int r;
163         unsigned long bogomips;
164
165         assert(firmware);
166         assert(loader);
167
168         /* Returns the usec after the CPU was turned on. The two
169          * timestamps are: the firmware finished, and the boot loader
170          * finished. */
171
172         /* We assume that the kernel's bogomips value is calibrated to
173          * twice the CPU frequency, and use this to convert the TSC
174          * ticks into usec. Of course, bogomips are only vaguely
175          * defined. If this breaks one day we can come up with
176          * something better. However, for now this saves us from doing
177          * a local calibration loop. */
178
179         r = read_bogomips(&bogomips);
180         if (r < 0)
181                 return r;
182
183         r = read_ticks(EFI_VENDOR_LOADER, "LoaderTicksInit", bogomips / 2, &x);
184         if (r < 0)
185                 return r;
186
187         r = read_ticks(EFI_VENDOR_LOADER, "LoaderTicksExec", bogomips / 2, &y);
188         if (r < 0)
189                 return r;
190
191         if (y == 0 || y < x)
192                 return -EIO;
193
194         if (y > USEC_PER_HOUR)
195                 return -EIO;
196
197         *firmware = x;
198         *loader = y;
199
200         return 0;
201 }
202
203 int efi_get_boot_timestamps(const dual_timestamp *n, dual_timestamp *firmware, dual_timestamp *loader) {
204         usec_t x, y, a;
205         int r;
206         dual_timestamp _n;
207
208         assert(firmware);
209         assert(loader);
210
211         if (!n) {
212                 dual_timestamp_get(&_n);
213                 n = &_n;
214         }
215
216         r = get_boot_usec(&x, &y);
217         if (r < 0)
218                 return r;
219
220         /* Let's convert this to timestamps where the firmware
221          * began/loader began working. To make this more confusing:
222          * since usec_t is unsigned and the kernel's monotonic clock
223          * begins at kernel initialization we'll actually initialize
224          * the monotonic timestamps here as negative of the actual
225          * value. */
226
227         firmware->monotonic = y;
228         loader->monotonic = y - x;
229
230         a = n->monotonic + firmware->monotonic;
231         firmware->realtime = n->realtime > a ? n->realtime - a : 0;
232
233         a = n->monotonic + loader->monotonic;
234         loader->realtime = n->realtime > a ? n->realtime - a : 0;
235
236         return 0;
237 }