]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/boot/efi/util.c
Merge pull request #21990 from keszybz/indentation-and-comments
[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 BS->Stall(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 strcmpa(v, (CHAR8 *)"t") == 0 ||
80 strcmpa(v, (CHAR8 *)"on") == 0) {
81 *b = TRUE;
82 return EFI_SUCCESS;
83 }
84
85 if (strcmpa(v, (CHAR8 *)"0") == 0 ||
86 strcmpa(v, (CHAR8 *)"no") == 0 ||
87 strcmpa(v, (CHAR8 *)"n") == 0 ||
88 strcmpa(v, (CHAR8 *)"false") == 0 ||
89 strcmpa(v, (CHAR8 *)"f") == 0 ||
90 strcmpa(v, (CHAR8 *)"off") == 0) {
91 *b = FALSE;
92 return EFI_SUCCESS;
93 }
94
95 return EFI_INVALID_PARAMETER;
96 }
97
98 EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, const CHAR16 *name, const void *buf, UINTN size, UINT32 flags) {
99 assert(vendor);
100 assert(name);
101 assert(buf || size == 0);
102
103 flags |= EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;
104 return RT->SetVariable((CHAR16 *) name, (EFI_GUID *) vendor, flags, size, (void *) buf);
105 }
106
107 EFI_STATUS efivar_set(const EFI_GUID *vendor, const CHAR16 *name, const CHAR16 *value, UINT32 flags) {
108 assert(vendor);
109 assert(name);
110
111 return efivar_set_raw(vendor, name, value, value ? StrSize(value) : 0, flags);
112 }
113
114 EFI_STATUS efivar_set_uint_string(const EFI_GUID *vendor, const CHAR16 *name, UINTN i, UINT32 flags) {
115 CHAR16 str[32];
116
117 assert(vendor);
118 assert(name);
119
120 SPrint(str, ELEMENTSOF(str), L"%u", i);
121 return efivar_set(vendor, name, str, flags);
122 }
123
124 EFI_STATUS efivar_set_uint32_le(const EFI_GUID *vendor, const CHAR16 *name, UINT32 value, UINT32 flags) {
125 UINT8 buf[4];
126
127 assert(vendor);
128 assert(name);
129
130 buf[0] = (UINT8)(value >> 0U & 0xFF);
131 buf[1] = (UINT8)(value >> 8U & 0xFF);
132 buf[2] = (UINT8)(value >> 16U & 0xFF);
133 buf[3] = (UINT8)(value >> 24U & 0xFF);
134
135 return efivar_set_raw(vendor, name, buf, sizeof(buf), flags);
136 }
137
138 EFI_STATUS efivar_set_uint64_le(const EFI_GUID *vendor, const CHAR16 *name, UINT64 value, UINT32 flags) {
139 UINT8 buf[8];
140
141 assert(vendor);
142 assert(name);
143
144 buf[0] = (UINT8)(value >> 0U & 0xFF);
145 buf[1] = (UINT8)(value >> 8U & 0xFF);
146 buf[2] = (UINT8)(value >> 16U & 0xFF);
147 buf[3] = (UINT8)(value >> 24U & 0xFF);
148 buf[4] = (UINT8)(value >> 32U & 0xFF);
149 buf[5] = (UINT8)(value >> 40U & 0xFF);
150 buf[6] = (UINT8)(value >> 48U & 0xFF);
151 buf[7] = (UINT8)(value >> 56U & 0xFF);
152
153 return efivar_set_raw(vendor, name, buf, sizeof(buf), flags);
154 }
155
156 EFI_STATUS efivar_get(const EFI_GUID *vendor, const CHAR16 *name, CHAR16 **value) {
157 _cleanup_freepool_ CHAR16 *buf = NULL;
158 EFI_STATUS err;
159 CHAR16 *val;
160 UINTN size;
161
162 assert(vendor);
163 assert(name);
164
165 err = efivar_get_raw(vendor, name, (CHAR8**)&buf, &size);
166 if (EFI_ERROR(err))
167 return err;
168
169 /* Make sure there are no incomplete characters in the buffer */
170 if ((size % sizeof(CHAR16)) != 0)
171 return EFI_INVALID_PARAMETER;
172
173 if (!value)
174 return EFI_SUCCESS;
175
176 /* Return buffer directly if it happens to be NUL terminated already */
177 if (size >= sizeof(CHAR16) && buf[size / sizeof(CHAR16) - 1] == 0) {
178 *value = TAKE_PTR(buf);
179 return EFI_SUCCESS;
180 }
181
182 /* Make sure a terminating NUL is available at the end */
183 val = xallocate_pool(size + sizeof(CHAR16));
184
185 CopyMem(val, buf, size);
186 val[size / sizeof(CHAR16) - 1] = 0; /* NUL terminate */
187
188 *value = val;
189 return EFI_SUCCESS;
190 }
191
192 EFI_STATUS efivar_get_uint_string(const EFI_GUID *vendor, const CHAR16 *name, UINTN *i) {
193 _cleanup_freepool_ CHAR16 *val = NULL;
194 EFI_STATUS err;
195
196 assert(vendor);
197 assert(name);
198 assert(i);
199
200 err = efivar_get(vendor, name, &val);
201 if (!EFI_ERROR(err))
202 *i = Atoi(val);
203
204 return err;
205 }
206
207 EFI_STATUS efivar_get_uint32_le(const EFI_GUID *vendor, const CHAR16 *name, UINT32 *ret) {
208 _cleanup_freepool_ CHAR8 *buf = NULL;
209 UINTN size;
210 EFI_STATUS err;
211
212 assert(vendor);
213 assert(name);
214
215 err = efivar_get_raw(vendor, name, &buf, &size);
216 if (!EFI_ERROR(err) && ret) {
217 if (size != sizeof(UINT32))
218 return EFI_BUFFER_TOO_SMALL;
219
220 *ret = (UINT32) buf[0] << 0U | (UINT32) buf[1] << 8U | (UINT32) buf[2] << 16U |
221 (UINT32) buf[3] << 24U;
222 }
223
224 return err;
225 }
226
227 EFI_STATUS efivar_get_uint64_le(const EFI_GUID *vendor, const CHAR16 *name, UINT64 *ret) {
228 _cleanup_freepool_ CHAR8 *buf = NULL;
229 UINTN size;
230 EFI_STATUS err;
231
232 assert(vendor);
233 assert(name);
234
235 err = efivar_get_raw(vendor, name, &buf, &size);
236 if (!EFI_ERROR(err) && ret) {
237 if (size != sizeof(UINT64))
238 return EFI_BUFFER_TOO_SMALL;
239
240 *ret = (UINT64) buf[0] << 0U | (UINT64) buf[1] << 8U | (UINT64) buf[2] << 16U |
241 (UINT64) buf[3] << 24U | (UINT64) buf[4] << 32U | (UINT64) buf[5] << 40U |
242 (UINT64) buf[6] << 48U | (UINT64) buf[7] << 56U;
243 }
244
245 return err;
246 }
247
248 EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const CHAR16 *name, CHAR8 **buffer, UINTN *size) {
249 _cleanup_freepool_ CHAR8 *buf = NULL;
250 UINTN l;
251 EFI_STATUS err;
252
253 assert(vendor);
254 assert(name);
255
256 l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE;
257 buf = xallocate_pool(l);
258
259 err = RT->GetVariable((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 *xstra_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 = xnew(CHAR16, len + 1);
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 *xstra_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 = xnew(CHAR16, len + 2);
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 if (!s)
428 return NULL;
429
430 do {
431 if (*s == c)
432 return (CHAR8*) s;
433 } while (*s++);
434
435 return NULL;
436 }
437
438 EFI_STATUS file_read(EFI_FILE_HANDLE dir, const CHAR16 *name, UINTN off, UINTN size, CHAR8 **ret, UINTN *ret_size) {
439 _cleanup_(FileHandleClosep) EFI_FILE_HANDLE handle = NULL;
440 _cleanup_freepool_ CHAR8 *buf = NULL;
441 EFI_STATUS err;
442
443 assert(name);
444 assert(ret);
445
446 err = dir->Open(dir, &handle, (CHAR16*) name, EFI_FILE_MODE_READ, 0ULL);
447 if (EFI_ERROR(err))
448 return err;
449
450 if (size == 0) {
451 _cleanup_freepool_ EFI_FILE_INFO *info = NULL;
452
453 err = get_file_info_harder(handle, &info, NULL);
454 if (EFI_ERROR(err))
455 return err;
456
457 size = info->FileSize+1;
458 }
459
460 if (off > 0) {
461 err = handle->SetPosition(handle, off);
462 if (EFI_ERROR(err))
463 return err;
464 }
465
466 buf = xallocate_pool(size + 1);
467 err = handle->Read(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 INT32 attr = ST->ConOut->Mode->Attribute;
486 ST->ConOut->SetAttribute(ST->ConOut, EFI_LIGHTRED|EFI_BACKGROUND_BLACK);
487
488 if (ST->ConOut->Mode->CursorColumn > 0)
489 Print(L"\n");
490
491 va_start(args, fmt);
492 VPrint(fmt, args);
493 va_end(args);
494
495 Print(L"\n");
496
497 ST->ConOut->SetAttribute(ST->ConOut, attr);
498
499 /* Give the user a chance to see the message. */
500 BS->Stall(3 * 1000 * 1000);
501 }
502
503 EFI_STATUS log_oom(void) {
504 log_error_stall(L"Out of memory.");
505 return EFI_OUT_OF_RESOURCES;
506 }
507
508 void print_at(UINTN x, UINTN y, UINTN attr, const CHAR16 *str) {
509 assert(str);
510 ST->ConOut->SetCursorPosition(ST->ConOut, x, y);
511 ST->ConOut->SetAttribute(ST->ConOut, attr);
512 ST->ConOut->OutputString(ST->ConOut, (CHAR16*)str);
513 }
514
515 void clear_screen(UINTN attr) {
516 ST->ConOut->SetAttribute(ST->ConOut, attr);
517 ST->ConOut->ClearScreen(ST->ConOut);
518 }
519
520 void sort_pointer_array(
521 void **array,
522 UINTN n_members,
523 compare_pointer_func_t compare) {
524
525 assert(array || n_members == 0);
526 assert(compare);
527
528 if (n_members <= 1)
529 return;
530
531 for (UINTN i = 1; i < n_members; i++) {
532 BOOLEAN more = FALSE;
533
534 for (UINTN k = 0; k < n_members - i; k++) {
535 void *entry;
536
537 if (compare(array[k], array[k+1]) <= 0)
538 continue;
539
540 entry = array[k];
541 array[k] = array[k+1];
542 array[k+1] = entry;
543 more = TRUE;
544 }
545 if (!more)
546 break;
547 }
548 }
549
550 EFI_STATUS get_file_info_harder(
551 EFI_FILE_HANDLE handle,
552 EFI_FILE_INFO **ret,
553 UINTN *ret_size) {
554
555 UINTN size = offsetof(EFI_FILE_INFO, FileName) + 256;
556 _cleanup_freepool_ EFI_FILE_INFO *fi = NULL;
557 EFI_STATUS err;
558
559 assert(handle);
560 assert(ret);
561
562 /* A lot like LibFileInfo() but with useful error propagation */
563
564 fi = xallocate_pool(size);
565 err = handle->GetInfo(handle, &GenericFileInfo, &size, fi);
566 if (err == EFI_BUFFER_TOO_SMALL) {
567 FreePool(fi);
568 fi = xallocate_pool(size); /* GetInfo tells us the required size, let's use that now */
569 err = handle->GetInfo(handle, &GenericFileInfo, &size, fi);
570 }
571
572 if (EFI_ERROR(err))
573 return err;
574
575 *ret = TAKE_PTR(fi);
576
577 if (ret_size)
578 *ret_size = size;
579
580 return EFI_SUCCESS;
581 }
582
583 EFI_STATUS readdir_harder(
584 EFI_FILE_HANDLE handle,
585 EFI_FILE_INFO **buffer,
586 UINTN *buffer_size) {
587
588 EFI_STATUS err;
589 UINTN sz;
590
591 assert(handle);
592 assert(buffer);
593 assert(buffer_size);
594
595 /* buffer/buffer_size are both in and output parameters. Should be zero-initialized initially, and
596 * the specified buffer needs to be freed by caller, after final use. */
597
598 if (!*buffer) {
599 sz = offsetof(EFI_FILE_INFO, FileName) /* + 256 */;
600 *buffer = xallocate_pool(sz);
601 *buffer_size = sz;
602 } else
603 sz = *buffer_size;
604
605 err = handle->Read(handle, &sz, *buffer);
606 if (err == EFI_BUFFER_TOO_SMALL) {
607 FreePool(*buffer);
608 *buffer = xallocate_pool(sz);
609 *buffer_size = sz;
610 err = handle->Read(handle, &sz, *buffer);
611 }
612 if (EFI_ERROR(err))
613 return err;
614
615 if (sz == 0) {
616 /* End of directory */
617 FreePool(*buffer);
618 *buffer = NULL;
619 *buffer_size = 0;
620 }
621
622 return EFI_SUCCESS;
623 }
624
625 UINTN strnlena(const CHAR8 *p, UINTN maxlen) {
626 UINTN c;
627
628 if (!p)
629 return 0;
630
631 for (c = 0; c < maxlen; c++)
632 if (p[c] == 0)
633 break;
634
635 return c;
636 }
637
638 INTN strncasecmpa(const CHAR8 *a, const CHAR8 *b, UINTN maxlen) {
639 if (!a || !b)
640 return CMP(a, b);
641
642 while (maxlen > 0) {
643 CHAR8 ca = *a, cb = *b;
644 if (ca >= 'A' && ca <= 'Z')
645 ca += 'a' - 'A';
646 if (cb >= 'A' && cb <= 'Z')
647 cb += 'a' - 'A';
648 if (!ca || ca != cb)
649 return ca - cb;
650
651 a++;
652 b++;
653 maxlen--;
654 }
655
656 return 0;
657 }
658
659 CHAR8 *xstrndup8(const CHAR8 *p, UINTN sz) {
660 CHAR8 *n;
661
662 /* Following efilib's naming scheme this function would be called strndupa(), but we already have a
663 * function named like this in userspace, and it does something different there, hence to minimize
664 * confusion, let's pick a different name here */
665
666 assert(p || sz == 0);
667
668 sz = strnlena(p, sz);
669
670 n = xallocate_pool(sz + 1);
671
672 if (sz > 0)
673 CopyMem(n, p, sz);
674 n[sz] = 0;
675
676 return n;
677 }
678
679 BOOLEAN is_ascii(const CHAR16 *f) {
680 if (!f)
681 return FALSE;
682
683 for (; *f != 0; f++)
684 if (*f > 127)
685 return FALSE;
686
687 return TRUE;
688 }
689
690 CHAR16 **strv_free(CHAR16 **v) {
691 if (!v)
692 return NULL;
693
694 for (CHAR16 **i = v; *i; i++)
695 FreePool(*i);
696
697 FreePool(v);
698 return NULL;
699 }
700
701 EFI_STATUS open_directory(
702 EFI_FILE_HANDLE root,
703 const CHAR16 *path,
704 EFI_FILE_HANDLE *ret) {
705
706 _cleanup_(FileHandleClosep) EFI_FILE_HANDLE dir = NULL;
707 _cleanup_freepool_ EFI_FILE_INFO *file_info = NULL;
708 EFI_STATUS err;
709
710 assert(root);
711
712 /* Opens a file, and then verifies it is actually a directory */
713
714 err = root->Open(root, &dir, (CHAR16*) path, EFI_FILE_MODE_READ, 0ULL);
715 if (EFI_ERROR(err))
716 return err;
717
718 err = get_file_info_harder(dir, &file_info, NULL);
719 if (EFI_ERROR(err))
720 return err;
721 if (!FLAGS_SET(file_info->Attribute, EFI_FILE_DIRECTORY))
722 return EFI_LOAD_ERROR;
723
724 *ret = TAKE_PTR(dir);
725 return EFI_SUCCESS;
726 }
727
728 UINT64 get_os_indications_supported(void) {
729 UINT64 osind;
730 EFI_STATUS err;
731
732 /* Returns the supported OS indications. If we can't acquire it, returns a zeroed out mask, i.e. no
733 * supported features. */
734
735 err = efivar_get_uint64_le(EFI_GLOBAL_GUID, L"OsIndicationsSupported", &osind);
736 if (EFI_ERROR(err))
737 return 0;
738
739 return osind;
740 }