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
read_usec(const char * variable,usec_t * ret)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
efi_loader_get_boot_usec(usec_t * ret_firmware,usec_t * ret_loader)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
efi_loader_get_device_part_uuid(sd_id128_t * ret)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
efi_loader_get_entries(char *** ret)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
efi_loader_get_features(uint64_t * ret)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
efi_loader_get_config_timeout_one_shot(usec_t * ret)190 int efi_loader_get_config_timeout_one_shot(usec_t *ret) {
191 _cleanup_free_ char *v = NULL;
192 static struct stat cache_stat = {};
193 struct stat new_stat;
194 static usec_t cache;
195 uint64_t sec;
196 int r;
197
198 assert(ret);
199
200 /* stat() the EFI variable, to see if the mtime changed. If it did, we need to cache again. */
201 if (stat(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot)), &new_stat) < 0)
202 return -errno;
203
204 if (stat_inode_unmodified(&new_stat, &cache_stat)) {
205 *ret = cache;
206 return 0;
207 }
208
209 r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot), &v);
210 if (r < 0)
211 return r;
212
213 r = safe_atou64(v, &sec);
214 if (r < 0)
215 return r;
216 if (sec > USEC_INFINITY / USEC_PER_SEC)
217 return -ERANGE;
218
219 cache_stat = new_stat;
220 *ret = cache = sec * USEC_PER_SEC; /* return in µs */
221 return 0;
222 }
223
efi_loader_update_entry_one_shot_cache(char ** cache,struct stat * cache_stat)224 int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat) {
225 _cleanup_free_ char *v = NULL;
226 struct stat new_stat;
227 int r;
228
229 assert(cache);
230 assert(cache_stat);
231
232 /* stat() the EFI variable, to see if the mtime changed. If it did we need to cache again. */
233 if (stat(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderEntryOneShot)), &new_stat) < 0)
234 return -errno;
235
236 if (stat_inode_unmodified(&new_stat, cache_stat))
237 return 0;
238
239 r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderEntryOneShot), &v);
240 if (r < 0)
241 return r;
242
243 if (!efi_loader_entry_name_valid(v))
244 return -EINVAL;
245
246 *cache_stat = new_stat;
247 free_and_replace(*cache, v);
248
249 return 0;
250 }
251
252 #endif
253
efi_loader_entry_name_valid(const char * s)254 bool efi_loader_entry_name_valid(const char *s) {
255 if (!filename_is_valid(s)) /* Make sure entry names fit in filenames */
256 return false;
257
258 return in_charset(s, ALPHANUMERICAL "+-_.");
259 }
260