]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/efivars.c
efi: fix returned length of efi_get_variable()
[thirdparty/systemd.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 - 4;
89
90 if (attribute)
91 *attribute = a;
92
93 return 0;
94 }
95
96 static int read_usec(sd_id128_t vendor, const char *name, usec_t *u) {
97 _cleanup_free_ void *i = NULL;
98 _cleanup_free_ char *j = NULL;
99 size_t is;
100 int r;
101 uint64_t x;
102
103 assert(name);
104 assert(u);
105
106 r = efi_get_variable(EFI_VENDOR_LOADER, name, NULL, &i, &is);
107 if (r < 0)
108 return r;
109
110 j = utf16_to_utf8(i, is);
111 if (!j)
112 return -ENOMEM;
113
114 r = safe_atou64(j, &x);
115 if (r < 0)
116 return r;
117
118 *u = x;
119 return 0;
120 }
121
122 static int get_boot_usec(usec_t *firmware, usec_t *loader) {
123 uint64_t x, y;
124 int r;
125
126 assert(firmware);
127 assert(loader);
128
129 r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeInitUSec", &x);
130 if (r < 0)
131 return r;
132
133 r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeExecUSec", &y);
134 if (r < 0)
135 return r;
136
137 if (y == 0 || y < x)
138 return -EIO;
139
140 if (y > USEC_PER_HOUR)
141 return -EIO;
142
143 *firmware = x;
144 *loader = y;
145
146 return 0;
147 }
148
149 int efi_get_boot_timestamps(const dual_timestamp *n, dual_timestamp *firmware, dual_timestamp *loader) {
150 usec_t x, y, a;
151 int r;
152 dual_timestamp _n;
153
154 assert(firmware);
155 assert(loader);
156
157 if (!n) {
158 dual_timestamp_get(&_n);
159 n = &_n;
160 }
161
162 r = get_boot_usec(&x, &y);
163 if (r < 0)
164 return r;
165
166 /* Let's convert this to timestamps where the firmware
167 * began/loader began working. To make this more confusing:
168 * since usec_t is unsigned and the kernel's monotonic clock
169 * begins at kernel initialization we'll actually initialize
170 * the monotonic timestamps here as negative of the actual
171 * value. */
172
173 firmware->monotonic = y;
174 loader->monotonic = y - x;
175
176 a = n->monotonic + firmware->monotonic;
177 firmware->realtime = n->realtime > a ? n->realtime - a : 0;
178
179 a = n->monotonic + loader->monotonic;
180 loader->realtime = n->realtime > a ? n->realtime - a : 0;
181
182 return 0;
183 }
184
185 int efi_get_loader_device_part_uuid(sd_id128_t *u) {
186 _cleanup_free_ void *s = NULL;
187 _cleanup_free_ char *p = NULL;
188 size_t ss;
189 int r, parsed[16];
190 unsigned i;
191
192 assert(u);
193
194 r = efi_get_variable(EFI_VENDOR_LOADER, "LoaderDevicePartUUID", NULL, &s, &ss);
195 if (r < 0)
196 return r;
197
198 p = utf16_to_utf8(s, ss);
199 if (!p)
200 return -ENOMEM;
201
202 if (sscanf(p, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
203 &parsed[0], &parsed[1], &parsed[2], &parsed[3],
204 &parsed[4], &parsed[5], &parsed[6], &parsed[7],
205 &parsed[8], &parsed[9], &parsed[10], &parsed[11],
206 &parsed[12], &parsed[13], &parsed[14], &parsed[15]) != 16)
207 return -EIO;
208
209 for (i = 0; i < ELEMENTSOF(parsed); i++)
210 u->bytes[i] = parsed[i];
211
212 return 0;
213 }