]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/boot/efi/util.c
sd-boot: Assert all the things!
[thirdparty/systemd.git] / src / boot / efi / util.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <efi.h>
4 #include <efilib.h>
5
6 #include "util.h"
7
8 #ifdef __x86_64__
9 UINT64 ticks_read(VOID) {
10 UINT64 a, d;
11 __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d));
12 return (d << 32) | a;
13 }
14 #elif defined(__i386__)
15 UINT64 ticks_read(VOID) {
16 UINT64 val;
17 __asm__ volatile ("rdtsc" : "=A" (val));
18 return val;
19 }
20 #elif defined(__aarch64__)
21 UINT64 ticks_read(VOID) {
22 UINT64 val;
23 __asm__ volatile ("mrs %0, cntpct_el0" : "=r" (val));
24 return val;
25 }
26 #else
27 UINT64 ticks_read(VOID) {
28 UINT64 val = 1;
29 return val;
30 }
31 #endif
32
33 #if defined(__aarch64__)
34 UINT64 ticks_freq(VOID) {
35 UINT64 freq;
36 __asm__ volatile ("mrs %0, cntfrq_el0": "=r" (freq));
37 return freq;
38 }
39 #else
40 /* count TSC ticks during a millisecond delay */
41 UINT64 ticks_freq(VOID) {
42 UINT64 ticks_start, ticks_end;
43
44 ticks_start = ticks_read();
45 uefi_call_wrapper(BS->Stall, 1, 1000);
46 ticks_end = ticks_read();
47
48 return (ticks_end - ticks_start) * 1000UL;
49 }
50 #endif
51
52 UINT64 time_usec(VOID) {
53 UINT64 ticks;
54 static UINT64 freq;
55
56 ticks = ticks_read();
57 if (ticks == 0)
58 return 0;
59
60 if (freq == 0) {
61 freq = ticks_freq();
62 if (freq == 0)
63 return 0;
64 }
65
66 return 1000UL * 1000UL * ticks / freq;
67 }
68
69 EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b) {
70 assert(b);
71
72 if (!v)
73 return EFI_INVALID_PARAMETER;
74
75 if (strcmpa(v, (CHAR8 *)"1") == 0 ||
76 strcmpa(v, (CHAR8 *)"yes") == 0 ||
77 strcmpa(v, (CHAR8 *)"y") == 0 ||
78 strcmpa(v, (CHAR8 *)"true") == 0) {
79 *b = TRUE;
80 return EFI_SUCCESS;
81 }
82
83 if (strcmpa(v, (CHAR8 *)"0") == 0 ||
84 strcmpa(v, (CHAR8 *)"no") == 0 ||
85 strcmpa(v, (CHAR8 *)"n") == 0 ||
86 strcmpa(v, (CHAR8 *)"false") == 0) {
87 *b = FALSE;
88 return EFI_SUCCESS;
89 }
90
91 return EFI_INVALID_PARAMETER;
92 }
93
94 EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, const CHAR16 *name, const VOID *buf, UINTN size, UINT32 flags) {
95 assert(vendor);
96 assert(name);
97 assert(buf || size == 0);
98
99 flags |= EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;
100 return uefi_call_wrapper(RT->SetVariable, 5, (CHAR16*) name, (EFI_GUID *)vendor, flags, size, (VOID*) buf);
101 }
102
103 EFI_STATUS efivar_set(const EFI_GUID *vendor, const CHAR16 *name, const CHAR16 *value, UINT32 flags) {
104 assert(vendor);
105 assert(name);
106
107 return efivar_set_raw(vendor, name, value, value ? (StrLen(value) + 1) * sizeof(CHAR16) : 0, flags);
108 }
109
110 EFI_STATUS efivar_set_uint_string(const EFI_GUID *vendor, const CHAR16 *name, UINTN i, UINT32 flags) {
111 CHAR16 str[32];
112
113 assert(vendor);
114 assert(name);
115
116 SPrint(str, ELEMENTSOF(str), L"%u", i);
117 return efivar_set(vendor, name, str, flags);
118 }
119
120 EFI_STATUS efivar_set_uint32_le(const EFI_GUID *vendor, const CHAR16 *name, UINT32 value, UINT32 flags) {
121 UINT8 buf[4];
122
123 assert(vendor);
124 assert(name);
125
126 buf[0] = (UINT8)(value >> 0U & 0xFF);
127 buf[1] = (UINT8)(value >> 8U & 0xFF);
128 buf[2] = (UINT8)(value >> 16U & 0xFF);
129 buf[3] = (UINT8)(value >> 24U & 0xFF);
130
131 return efivar_set_raw(vendor, name, buf, sizeof(buf), flags);
132 }
133
134 EFI_STATUS efivar_set_uint64_le(const EFI_GUID *vendor, const CHAR16 *name, UINT64 value, UINT32 flags) {
135 UINT8 buf[8];
136
137 assert(vendor);
138 assert(name);
139
140 buf[0] = (UINT8)(value >> 0U & 0xFF);
141 buf[1] = (UINT8)(value >> 8U & 0xFF);
142 buf[2] = (UINT8)(value >> 16U & 0xFF);
143 buf[3] = (UINT8)(value >> 24U & 0xFF);
144 buf[4] = (UINT8)(value >> 32U & 0xFF);
145 buf[5] = (UINT8)(value >> 40U & 0xFF);
146 buf[6] = (UINT8)(value >> 48U & 0xFF);
147 buf[7] = (UINT8)(value >> 56U & 0xFF);
148
149 return efivar_set_raw(vendor, name, buf, sizeof(buf), flags);
150 }
151
152 EFI_STATUS efivar_get(const EFI_GUID *vendor, const CHAR16 *name, CHAR16 **value) {
153 _cleanup_freepool_ CHAR16 *buf = NULL;
154 EFI_STATUS err;
155 CHAR16 *val;
156 UINTN size;
157
158 assert(vendor);
159 assert(name);
160
161 err = efivar_get_raw(vendor, name, (CHAR8**)&buf, &size);
162 if (EFI_ERROR(err))
163 return err;
164
165 /* Make sure there are no incomplete characters in the buffer */
166 if ((size % sizeof(CHAR16)) != 0)
167 return EFI_INVALID_PARAMETER;
168
169 if (!value)
170 return EFI_SUCCESS;
171
172 /* Return buffer directly if it happens to be NUL terminated already */
173 if (size >= sizeof(CHAR16) && buf[size/sizeof(CHAR16)] == 0) {
174 *value = TAKE_PTR(buf);
175 return EFI_SUCCESS;
176 }
177
178 /* Make sure a terminating NUL is available at the end */
179 val = AllocatePool(size + sizeof(CHAR16));
180 if (!val)
181 return EFI_OUT_OF_RESOURCES;
182
183 CopyMem(val, buf, size);
184 val[size / sizeof(CHAR16)] = 0; /* NUL terminate */
185
186 *value = val;
187 return EFI_SUCCESS;
188 }
189
190 EFI_STATUS efivar_get_uint_string(const EFI_GUID *vendor, const CHAR16 *name, UINTN *i) {
191 _cleanup_freepool_ CHAR16 *val = NULL;
192 EFI_STATUS err;
193
194 assert(vendor);
195 assert(name);
196 assert(i);
197
198 err = efivar_get(vendor, name, &val);
199 if (!EFI_ERROR(err))
200 *i = Atoi(val);
201
202 return err;
203 }
204
205 EFI_STATUS efivar_get_uint32_le(const EFI_GUID *vendor, const CHAR16 *name, UINT32 *ret) {
206 _cleanup_freepool_ CHAR8 *buf = NULL;
207 UINTN size;
208 EFI_STATUS err;
209
210 assert(vendor);
211 assert(name);
212
213 err = efivar_get_raw(vendor, name, &buf, &size);
214 if (!EFI_ERROR(err) && ret) {
215 if (size != sizeof(UINT32))
216 return EFI_BUFFER_TOO_SMALL;
217
218 *ret = (UINT32) buf[0] << 0U | (UINT32) buf[1] << 8U | (UINT32) buf[2] << 16U |
219 (UINT32) buf[3] << 24U;
220 }
221
222 return err;
223 }
224
225 EFI_STATUS efivar_get_uint64_le(const EFI_GUID *vendor, const CHAR16 *name, UINT64 *ret) {
226 _cleanup_freepool_ CHAR8 *buf = NULL;
227 UINTN size;
228 EFI_STATUS err;
229
230 assert(vendor);
231 assert(name);
232
233 err = efivar_get_raw(vendor, name, &buf, &size);
234 if (!EFI_ERROR(err) && ret) {
235 if (size != sizeof(UINT64))
236 return EFI_BUFFER_TOO_SMALL;
237
238 *ret = (UINT64) buf[0] << 0U | (UINT64) buf[1] << 8U | (UINT64) buf[2] << 16U |
239 (UINT64) buf[3] << 24U | (UINT64) buf[4] << 32U | (UINT64) buf[5] << 40U |
240 (UINT64) buf[6] << 48U | (UINT64) buf[7] << 56U;
241 }
242
243 return err;
244 }
245
246 EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const CHAR16 *name, CHAR8 **buffer, UINTN *size) {
247 _cleanup_freepool_ CHAR8 *buf = NULL;
248 UINTN l;
249 EFI_STATUS err;
250
251 assert(vendor);
252 assert(name);
253
254 l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE;
255 buf = AllocatePool(l);
256 if (!buf)
257 return EFI_OUT_OF_RESOURCES;
258
259 err = uefi_call_wrapper(RT->GetVariable, 5, (CHAR16*) name, (EFI_GUID *)vendor, NULL, &l, buf);
260 if (!EFI_ERROR(err)) {
261
262 if (buffer)
263 *buffer = TAKE_PTR(buf);
264
265 if (size)
266 *size = l;
267 }
268
269 return err;
270 }
271
272 EFI_STATUS efivar_get_boolean_u8(const EFI_GUID *vendor, const CHAR16 *name, BOOLEAN *ret) {
273 _cleanup_freepool_ CHAR8 *b = NULL;
274 UINTN size;
275 EFI_STATUS err;
276
277 assert(vendor);
278 assert(name);
279 assert(ret);
280
281 err = efivar_get_raw(vendor, name, &b, &size);
282 if (!EFI_ERROR(err))
283 *ret = *b > 0;
284
285 return err;
286 }
287
288 VOID efivar_set_time_usec(const EFI_GUID *vendor, const CHAR16 *name, UINT64 usec) {
289 CHAR16 str[32];
290
291 assert(vendor);
292 assert(name);
293
294 if (usec == 0)
295 usec = time_usec();
296 if (usec == 0)
297 return;
298
299 SPrint(str, ELEMENTSOF(str), L"%ld", usec);
300 efivar_set(vendor, name, str, 0);
301 }
302
303 static INTN utf8_to_16(const CHAR8 *stra, CHAR16 *c) {
304 CHAR16 unichar;
305 UINTN len;
306
307 assert(stra);
308 assert(c);
309
310 if (!(stra[0] & 0x80))
311 len = 1;
312 else if ((stra[0] & 0xe0) == 0xc0)
313 len = 2;
314 else if ((stra[0] & 0xf0) == 0xe0)
315 len = 3;
316 else if ((stra[0] & 0xf8) == 0xf0)
317 len = 4;
318 else if ((stra[0] & 0xfc) == 0xf8)
319 len = 5;
320 else if ((stra[0] & 0xfe) == 0xfc)
321 len = 6;
322 else
323 return -1;
324
325 switch (len) {
326 case 1:
327 unichar = stra[0];
328 break;
329 case 2:
330 unichar = stra[0] & 0x1f;
331 break;
332 case 3:
333 unichar = stra[0] & 0x0f;
334 break;
335 case 4:
336 unichar = stra[0] & 0x07;
337 break;
338 case 5:
339 unichar = stra[0] & 0x03;
340 break;
341 case 6:
342 unichar = stra[0] & 0x01;
343 break;
344 }
345
346 for (UINTN i = 1; i < len; i++) {
347 if ((stra[i] & 0xc0) != 0x80)
348 return -1;
349 unichar <<= 6;
350 unichar |= stra[i] & 0x3f;
351 }
352
353 *c = unichar;
354 return len;
355 }
356
357 CHAR16 *stra_to_str(const CHAR8 *stra) {
358 UINTN strlen;
359 UINTN len;
360 UINTN i;
361 CHAR16 *str;
362
363 assert(stra);
364
365 len = strlena(stra);
366 str = AllocatePool((len + 1) * sizeof(CHAR16));
367
368 strlen = 0;
369 i = 0;
370 while (i < len) {
371 INTN utf8len;
372
373 utf8len = utf8_to_16(stra + i, str + strlen);
374 if (utf8len <= 0) {
375 /* invalid utf8 sequence, skip the garbage */
376 i++;
377 continue;
378 }
379
380 strlen++;
381 i += utf8len;
382 }
383 str[strlen] = '\0';
384 return str;
385 }
386
387 CHAR16 *stra_to_path(const CHAR8 *stra) {
388 CHAR16 *str;
389 UINTN strlen;
390 UINTN len;
391 UINTN i;
392
393 assert(stra);
394
395 len = strlena(stra);
396 str = AllocatePool((len + 2) * sizeof(CHAR16));
397
398 str[0] = '\\';
399 strlen = 1;
400 i = 0;
401 while (i < len) {
402 INTN utf8len;
403
404 utf8len = utf8_to_16(stra + i, str + strlen);
405 if (utf8len <= 0) {
406 /* invalid utf8 sequence, skip the garbage */
407 i++;
408 continue;
409 }
410
411 if (str[strlen] == '/')
412 str[strlen] = '\\';
413 if (str[strlen] == '\\' && str[strlen-1] == '\\') {
414 /* skip double slashes */
415 i += utf8len;
416 continue;
417 }
418
419 strlen++;
420 i += utf8len;
421 }
422 str[strlen] = '\0';
423 return str;
424 }
425
426 CHAR8 *strchra(const CHAR8 *s, CHAR8 c) {
427 assert(s);
428 do {
429 if (*s == c)
430 return (CHAR8*) s;
431 } while (*s++);
432 return NULL;
433 }
434
435 EFI_STATUS file_read(EFI_FILE_HANDLE dir, const CHAR16 *name, UINTN off, UINTN size, CHAR8 **ret, UINTN *ret_size) {
436 _cleanup_(FileHandleClosep) EFI_FILE_HANDLE handle = NULL;
437 _cleanup_freepool_ CHAR8 *buf = NULL;
438 EFI_STATUS err;
439
440 assert(name);
441 assert(ret);
442
443 err = uefi_call_wrapper(dir->Open, 5, dir, &handle, (CHAR16*) name, EFI_FILE_MODE_READ, 0ULL);
444 if (EFI_ERROR(err))
445 return err;
446
447 if (size == 0) {
448 _cleanup_freepool_ EFI_FILE_INFO *info = NULL;
449
450 info = LibFileInfo(handle);
451 if (!info)
452 return EFI_OUT_OF_RESOURCES;
453
454 size = info->FileSize+1;
455 }
456
457 if (off > 0) {
458 err = uefi_call_wrapper(handle->SetPosition, 2, handle, off);
459 if (EFI_ERROR(err))
460 return err;
461 }
462
463 buf = AllocatePool(size + 1);
464 if (!buf)
465 return EFI_OUT_OF_RESOURCES;
466
467 err = uefi_call_wrapper(handle->Read, 3, handle, &size, buf);
468 if (EFI_ERROR(err))
469 return err;
470
471 buf[size] = '\0';
472
473 *ret = TAKE_PTR(buf);
474 if (ret_size)
475 *ret_size = size;
476
477 return err;
478 }
479
480 VOID log_error_stall(const CHAR16 *fmt, ...) {
481 va_list args;
482
483 assert(fmt);
484
485 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTRED|EFI_BACKGROUND_BLACK);
486
487 Print(L"\n");
488 va_start(args, fmt);
489 VPrint(fmt, args);
490 va_end(args);
491 Print(L"\n");
492
493 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
494 }
495
496 EFI_STATUS log_oom(void) {
497 log_error_stall(L"Out of memory.");
498 return EFI_OUT_OF_RESOURCES;
499 }