]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/efi-loader.c
Merge pull request #23893 from yuwata/core-mount-re-read-mountinfo
[thirdparty/systemd.git] / src / shared / efi-loader.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "alloc-util.h"
4 #include "efi-loader.h"
5 #include "parse-util.h"
6 #include "path-util.h"
7 #include "stat-util.h"
8 #include "strv.h"
9 #include "utf8.h"
10
11 #if ENABLE_EFI
12
13 static int read_usec(const char *variable, usec_t *ret) {
14 _cleanup_free_ char *j = NULL;
15 uint64_t x = 0;
16 int r;
17
18 assert(variable);
19 assert(ret);
20
21 r = efi_get_variable_string(variable, &j);
22 if (r < 0)
23 return r;
24
25 r = safe_atou64(j, &x);
26 if (r < 0)
27 return r;
28
29 *ret = x;
30 return 0;
31 }
32
33 int efi_loader_get_boot_usec(usec_t *ret_firmware, usec_t *ret_loader) {
34 uint64_t x, y;
35 int r;
36
37 assert(ret_firmware);
38 assert(ret_loader);
39
40 if (!is_efi_boot())
41 return -EOPNOTSUPP;
42
43 r = read_usec(EFI_LOADER_VARIABLE(LoaderTimeInitUSec), &x);
44 if (r < 0)
45 return log_debug_errno(r, "Failed to read LoaderTimeInitUSec: %m");
46
47 r = read_usec(EFI_LOADER_VARIABLE(LoaderTimeExecUSec), &y);
48 if (r < 0)
49 return log_debug_errno(r, "Failed to read LoaderTimeExecUSec: %m");
50
51 if (y == 0 || y < x || y - x > USEC_PER_HOUR)
52 return log_debug_errno(SYNTHETIC_ERRNO(EIO),
53 "Bad LoaderTimeInitUSec=%"PRIu64", LoaderTimeExecUSec=%" PRIu64"; refusing.",
54 x, y);
55
56 *ret_firmware = x;
57 *ret_loader = y;
58 return 0;
59 }
60
61 int efi_loader_get_device_part_uuid(sd_id128_t *ret) {
62 _cleanup_free_ char *p = NULL;
63 int r, parsed[16];
64
65 if (!is_efi_boot())
66 return -EOPNOTSUPP;
67
68 r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderDevicePartUUID), &p);
69 if (r < 0)
70 return r;
71
72 if (sscanf(p, SD_ID128_UUID_FORMAT_STR,
73 &parsed[0], &parsed[1], &parsed[2], &parsed[3],
74 &parsed[4], &parsed[5], &parsed[6], &parsed[7],
75 &parsed[8], &parsed[9], &parsed[10], &parsed[11],
76 &parsed[12], &parsed[13], &parsed[14], &parsed[15]) != 16)
77 return -EIO;
78
79 if (ret)
80 for (unsigned i = 0; i < ELEMENTSOF(parsed); i++)
81 ret->bytes[i] = parsed[i];
82
83 return 0;
84 }
85
86 int efi_loader_get_entries(char ***ret) {
87 _cleanup_free_ char16_t *entries = NULL;
88 _cleanup_strv_free_ char **l = NULL;
89 size_t size;
90 int r;
91
92 assert(ret);
93
94 if (!is_efi_boot())
95 return -EOPNOTSUPP;
96
97 r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntries), NULL, (void**) &entries, &size);
98 if (r < 0)
99 return r;
100
101 /* The variable contains a series of individually NUL terminated UTF-16 strings. */
102
103 for (size_t i = 0, start = 0;; i++) {
104 _cleanup_free_ char *decoded = NULL;
105 bool end;
106
107 /* Is this the end of the variable's data? */
108 end = i * sizeof(char16_t) >= size;
109
110 /* Are we in the middle of a string? (i.e. not at the end of the variable, nor at a NUL terminator?) If
111 * so, let's go to the next entry. */
112 if (!end && entries[i] != 0)
113 continue;
114
115 /* We reached the end of a string, let's decode it into UTF-8 */
116 decoded = utf16_to_utf8(entries + start, (i - start) * sizeof(char16_t));
117 if (!decoded)
118 return -ENOMEM;
119
120 if (efi_loader_entry_name_valid(decoded)) {
121 r = strv_consume(&l, TAKE_PTR(decoded));
122 if (r < 0)
123 return r;
124 } else
125 log_debug("Ignoring invalid loader entry '%s'.", decoded);
126
127 /* We reached the end of the variable */
128 if (end)
129 break;
130
131 /* Continue after the NUL byte */
132 start = i + 1;
133 }
134
135 *ret = TAKE_PTR(l);
136 return 0;
137 }
138
139 int efi_loader_get_features(uint64_t *ret) {
140 _cleanup_free_ void *v = NULL;
141 size_t s;
142 int r;
143
144 assert(ret);
145
146 if (!is_efi_boot()) {
147 *ret = 0;
148 return 0;
149 }
150
151 r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderFeatures), NULL, &v, &s);
152 if (r == -ENOENT) {
153 _cleanup_free_ char *info = NULL;
154
155 /* The new (v240+) LoaderFeatures variable is not supported, let's see if it's systemd-boot at all */
156 r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderInfo), &info);
157 if (r < 0) {
158 if (r != -ENOENT)
159 return r;
160
161 /* Variable not set, definitely means not systemd-boot */
162
163 } else if (first_word(info, "systemd-boot")) {
164
165 /* An older systemd-boot version. Let's hardcode the feature set, since it was pretty
166 * static in all its versions. */
167
168 *ret = EFI_LOADER_FEATURE_CONFIG_TIMEOUT |
169 EFI_LOADER_FEATURE_ENTRY_DEFAULT |
170 EFI_LOADER_FEATURE_ENTRY_ONESHOT;
171
172 return 0;
173 }
174
175 /* No features supported */
176 *ret = 0;
177 return 0;
178 }
179 if (r < 0)
180 return r;
181
182 if (s != sizeof(uint64_t))
183 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
184 "LoaderFeatures EFI variable doesn't have the right size.");
185
186 memcpy(ret, v, sizeof(uint64_t));
187 return 0;
188 }
189
190 int efi_stub_get_features(uint64_t *ret) {
191 _cleanup_free_ void *v = NULL;
192 size_t s;
193 int r;
194
195 assert(ret);
196
197 if (!is_efi_boot()) {
198 *ret = 0;
199 return 0;
200 }
201
202 r = efi_get_variable(EFI_LOADER_VARIABLE(StubFeatures), NULL, &v, &s);
203 if (r == -ENOENT) {
204 _cleanup_free_ char *info = NULL;
205
206 /* The new (v252+) StubFeatures variable is not supported, let's see if it's systemd-stub at all */
207 r = efi_get_variable_string(EFI_LOADER_VARIABLE(StubInfo), &info);
208 if (r < 0) {
209 if (r != -ENOENT)
210 return r;
211
212 /* Variable not set, definitely means not systemd-stub */
213
214 } else if (first_word(info, "systemd-stub")) {
215
216 /* An older systemd-stub version. Let's hardcode the feature set, since it was pretty
217 * static in all its versions. */
218
219 *ret = EFI_STUB_FEATURE_REPORT_BOOT_PARTITION;
220 return 0;
221 }
222
223 /* No features supported */
224 *ret = 0;
225 return 0;
226 }
227 if (r < 0)
228 return r;
229
230 if (s != sizeof(uint64_t))
231 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
232 "StubFeatures EFI variable doesn't have the right size.");
233
234 memcpy(ret, v, sizeof(uint64_t));
235 return 0;
236 }
237
238 int efi_loader_get_config_timeout_one_shot(usec_t *ret) {
239 _cleanup_free_ char *v = NULL;
240 static struct stat cache_stat = {};
241 struct stat new_stat;
242 static usec_t cache;
243 uint64_t sec;
244 int r;
245
246 assert(ret);
247
248 /* stat() the EFI variable, to see if the mtime changed. If it did, we need to cache again. */
249 if (stat(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot)), &new_stat) < 0)
250 return -errno;
251
252 if (stat_inode_unmodified(&new_stat, &cache_stat)) {
253 *ret = cache;
254 return 0;
255 }
256
257 r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot), &v);
258 if (r < 0)
259 return r;
260
261 r = safe_atou64(v, &sec);
262 if (r < 0)
263 return r;
264 if (sec > USEC_INFINITY / USEC_PER_SEC)
265 return -ERANGE;
266
267 cache_stat = new_stat;
268 *ret = cache = sec * USEC_PER_SEC; /* return in µs */
269 return 0;
270 }
271
272 int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat) {
273 _cleanup_free_ char *v = NULL;
274 struct stat new_stat;
275 int r;
276
277 assert(cache);
278 assert(cache_stat);
279
280 /* stat() the EFI variable, to see if the mtime changed. If it did we need to cache again. */
281 if (stat(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderEntryOneShot)), &new_stat) < 0)
282 return -errno;
283
284 if (stat_inode_unmodified(&new_stat, cache_stat))
285 return 0;
286
287 r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderEntryOneShot), &v);
288 if (r < 0)
289 return r;
290
291 if (!efi_loader_entry_name_valid(v))
292 return -EINVAL;
293
294 *cache_stat = new_stat;
295 free_and_replace(*cache, v);
296
297 return 0;
298 }
299
300 #endif
301
302 bool efi_loader_entry_name_valid(const char *s) {
303 if (!filename_is_valid(s)) /* Make sure entry names fit in filenames */
304 return false;
305
306 return in_charset(s, ALPHANUMERICAL "+-_.");
307 }