1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
9 UINT64
ticks_read(void) {
11 __asm__
volatile ("rdtsc" : "=a" (a
), "=d" (d
));
14 #elif defined(__i386__)
15 UINT64
ticks_read(void) {
17 __asm__
volatile ("rdtsc" : "=A" (val
));
20 #elif defined(__aarch64__)
21 UINT64
ticks_read(void) {
23 __asm__
volatile ("mrs %0, cntpct_el0" : "=r" (val
));
27 UINT64
ticks_read(void) {
33 #if defined(__aarch64__)
34 UINT64
ticks_freq(void) {
36 __asm__
volatile ("mrs %0, cntfrq_el0": "=r" (freq
));
40 /* count TSC ticks during a millisecond delay */
41 UINT64
ticks_freq(void) {
42 UINT64 ticks_start
, ticks_end
;
44 ticks_start
= ticks_read();
46 ticks_end
= ticks_read();
48 return (ticks_end
- ticks_start
) * 1000UL;
52 UINT64
time_usec(void) {
66 return 1000UL * 1000UL * ticks
/ freq
;
69 EFI_STATUS
parse_boolean(const CHAR8
*v
, BOOLEAN
*b
) {
73 return EFI_INVALID_PARAMETER
;
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) {
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) {
95 return EFI_INVALID_PARAMETER
;
98 EFI_STATUS
efivar_set_raw(const EFI_GUID
*vendor
, const CHAR16
*name
, const void *buf
, UINTN size
, UINT32 flags
) {
101 assert(buf
|| size
== 0);
103 flags
|= EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
;
104 return RT
->SetVariable((CHAR16
*) name
, (EFI_GUID
*) vendor
, flags
, size
, (void *) buf
);
107 EFI_STATUS
efivar_set(const EFI_GUID
*vendor
, const CHAR16
*name
, const CHAR16
*value
, UINT32 flags
) {
111 return efivar_set_raw(vendor
, name
, value
, value
? StrSize(value
) : 0, flags
);
114 EFI_STATUS
efivar_set_uint_string(const EFI_GUID
*vendor
, const CHAR16
*name
, UINTN i
, UINT32 flags
) {
120 SPrint(str
, ELEMENTSOF(str
), L
"%u", i
);
121 return efivar_set(vendor
, name
, str
, flags
);
124 EFI_STATUS
efivar_set_uint32_le(const EFI_GUID
*vendor
, const CHAR16
*name
, UINT32 value
, UINT32 flags
) {
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);
135 return efivar_set_raw(vendor
, name
, buf
, sizeof(buf
), flags
);
138 EFI_STATUS
efivar_set_uint64_le(const EFI_GUID
*vendor
, const CHAR16
*name
, UINT64 value
, UINT32 flags
) {
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);
153 return efivar_set_raw(vendor
, name
, buf
, sizeof(buf
), flags
);
156 EFI_STATUS
efivar_get(const EFI_GUID
*vendor
, const CHAR16
*name
, CHAR16
**value
) {
157 _cleanup_freepool_ CHAR16
*buf
= NULL
;
165 err
= efivar_get_raw(vendor
, name
, (CHAR8
**)&buf
, &size
);
169 /* Make sure there are no incomplete characters in the buffer */
170 if ((size
% sizeof(CHAR16
)) != 0)
171 return EFI_INVALID_PARAMETER
;
176 /* Return buffer directly if it happens to be NUL terminated already */
177 if (size
>= sizeof(CHAR16
) && buf
[size
/sizeof(CHAR16
)] == 0) {
178 *value
= TAKE_PTR(buf
);
182 /* Make sure a terminating NUL is available at the end */
183 val
= AllocatePool(size
+ sizeof(CHAR16
));
185 return EFI_OUT_OF_RESOURCES
;
187 CopyMem(val
, buf
, size
);
188 val
[size
/ sizeof(CHAR16
)] = 0; /* NUL terminate */
194 EFI_STATUS
efivar_get_uint_string(const EFI_GUID
*vendor
, const CHAR16
*name
, UINTN
*i
) {
195 _cleanup_freepool_ CHAR16
*val
= NULL
;
202 err
= efivar_get(vendor
, name
, &val
);
209 EFI_STATUS
efivar_get_uint32_le(const EFI_GUID
*vendor
, const CHAR16
*name
, UINT32
*ret
) {
210 _cleanup_freepool_ CHAR8
*buf
= NULL
;
217 err
= efivar_get_raw(vendor
, name
, &buf
, &size
);
218 if (!EFI_ERROR(err
) && ret
) {
219 if (size
!= sizeof(UINT32
))
220 return EFI_BUFFER_TOO_SMALL
;
222 *ret
= (UINT32
) buf
[0] << 0U | (UINT32
) buf
[1] << 8U | (UINT32
) buf
[2] << 16U |
223 (UINT32
) buf
[3] << 24U;
229 EFI_STATUS
efivar_get_uint64_le(const EFI_GUID
*vendor
, const CHAR16
*name
, UINT64
*ret
) {
230 _cleanup_freepool_ CHAR8
*buf
= NULL
;
237 err
= efivar_get_raw(vendor
, name
, &buf
, &size
);
238 if (!EFI_ERROR(err
) && ret
) {
239 if (size
!= sizeof(UINT64
))
240 return EFI_BUFFER_TOO_SMALL
;
242 *ret
= (UINT64
) buf
[0] << 0U | (UINT64
) buf
[1] << 8U | (UINT64
) buf
[2] << 16U |
243 (UINT64
) buf
[3] << 24U | (UINT64
) buf
[4] << 32U | (UINT64
) buf
[5] << 40U |
244 (UINT64
) buf
[6] << 48U | (UINT64
) buf
[7] << 56U;
250 EFI_STATUS
efivar_get_raw(const EFI_GUID
*vendor
, const CHAR16
*name
, CHAR8
**buffer
, UINTN
*size
) {
251 _cleanup_freepool_ CHAR8
*buf
= NULL
;
258 l
= sizeof(CHAR16
*) * EFI_MAXIMUM_VARIABLE_SIZE
;
259 buf
= AllocatePool(l
);
261 return EFI_OUT_OF_RESOURCES
;
263 err
= RT
->GetVariable((CHAR16
*) name
, (EFI_GUID
*) vendor
, NULL
, &l
, buf
);
264 if (!EFI_ERROR(err
)) {
267 *buffer
= TAKE_PTR(buf
);
276 EFI_STATUS
efivar_get_boolean_u8(const EFI_GUID
*vendor
, const CHAR16
*name
, BOOLEAN
*ret
) {
277 _cleanup_freepool_ CHAR8
*b
= NULL
;
285 err
= efivar_get_raw(vendor
, name
, &b
, &size
);
292 void efivar_set_time_usec(const EFI_GUID
*vendor
, const CHAR16
*name
, UINT64 usec
) {
303 SPrint(str
, ELEMENTSOF(str
), L
"%ld", usec
);
304 efivar_set(vendor
, name
, str
, 0);
307 static INTN
utf8_to_16(const CHAR8
*stra
, CHAR16
*c
) {
314 if (!(stra
[0] & 0x80))
316 else if ((stra
[0] & 0xe0) == 0xc0)
318 else if ((stra
[0] & 0xf0) == 0xe0)
320 else if ((stra
[0] & 0xf8) == 0xf0)
322 else if ((stra
[0] & 0xfc) == 0xf8)
324 else if ((stra
[0] & 0xfe) == 0xfc)
334 unichar
= stra
[0] & 0x1f;
337 unichar
= stra
[0] & 0x0f;
340 unichar
= stra
[0] & 0x07;
343 unichar
= stra
[0] & 0x03;
346 unichar
= stra
[0] & 0x01;
350 for (UINTN i
= 1; i
< len
; i
++) {
351 if ((stra
[i
] & 0xc0) != 0x80)
354 unichar
|= stra
[i
] & 0x3f;
361 CHAR16
*stra_to_str(const CHAR8
*stra
) {
370 str
= AllocatePool((len
+ 1) * sizeof(CHAR16
));
379 utf8len
= utf8_to_16(stra
+ i
, str
+ strlen
);
381 /* invalid utf8 sequence, skip the garbage */
393 CHAR16
*stra_to_path(const CHAR8
*stra
) {
402 str
= AllocatePool((len
+ 2) * sizeof(CHAR16
));
412 utf8len
= utf8_to_16(stra
+ i
, str
+ strlen
);
414 /* invalid utf8 sequence, skip the garbage */
419 if (str
[strlen
] == '/')
421 if (str
[strlen
] == '\\' && str
[strlen
-1] == '\\') {
422 /* skip double slashes */
434 CHAR8
*strchra(const CHAR8
*s
, CHAR8 c
) {
446 EFI_STATUS
file_read(EFI_FILE_HANDLE dir
, const CHAR16
*name
, UINTN off
, UINTN size
, CHAR8
**ret
, UINTN
*ret_size
) {
447 _cleanup_(FileHandleClosep
) EFI_FILE_HANDLE handle
= NULL
;
448 _cleanup_freepool_ CHAR8
*buf
= NULL
;
454 err
= dir
->Open(dir
, &handle
, (CHAR16
*) name
, EFI_FILE_MODE_READ
, 0ULL);
459 _cleanup_freepool_ EFI_FILE_INFO
*info
= NULL
;
461 err
= get_file_info_harder(handle
, &info
, NULL
);
465 size
= info
->FileSize
+1;
469 err
= handle
->SetPosition(handle
, off
);
474 buf
= AllocatePool(size
+ 1);
476 return EFI_OUT_OF_RESOURCES
;
478 err
= handle
->Read(handle
, &size
, buf
);
484 *ret
= TAKE_PTR(buf
);
491 void log_error_stall(const CHAR16
*fmt
, ...) {
496 ST
->ConOut
->SetAttribute(ST
->ConOut
, EFI_LIGHTRED
|EFI_BACKGROUND_BLACK
);
504 BS
->Stall(3 * 1000 * 1000);
507 EFI_STATUS
log_oom(void) {
508 log_error_stall(L
"Out of memory.");
509 return EFI_OUT_OF_RESOURCES
;
512 void *memmem_safe(const void *haystack
, UINTN haystack_len
, const void *needle
, UINTN needle_len
) {
513 assert(haystack
|| haystack_len
== 0);
514 assert(needle
|| needle_len
== 0);
517 return (void*)haystack
;
519 for (const CHAR8
*h
= haystack
, *n
= needle
; haystack_len
>= needle_len
; h
++, haystack_len
--)
520 if (*h
== *n
&& CompareMem(h
+ 1, n
+ 1, needle_len
- 1) == 0)
526 void print_at(UINTN x
, UINTN y
, UINTN attr
, const CHAR16
*str
) {
528 ST
->ConOut
->SetCursorPosition(ST
->ConOut
, x
, y
);
529 ST
->ConOut
->SetAttribute(ST
->ConOut
, attr
);
530 ST
->ConOut
->OutputString(ST
->ConOut
, (CHAR16
*)str
);
533 void clear_screen(UINTN attr
) {
534 ST
->ConOut
->SetAttribute(ST
->ConOut
, attr
);
535 ST
->ConOut
->ClearScreen(ST
->ConOut
);
538 void sort_pointer_array(
541 compare_pointer_func_t compare
) {
543 assert(array
|| n_members
== 0);
549 for (UINTN i
= 1; i
< n_members
; i
++) {
550 BOOLEAN more
= FALSE
;
552 for (UINTN k
= 0; k
< n_members
- i
; k
++) {
555 if (compare(array
[k
], array
[k
+1]) <= 0)
559 array
[k
] = array
[k
+1];
568 EFI_STATUS
get_file_info_harder(
569 EFI_FILE_HANDLE handle
,
573 static const EFI_GUID EfiFileInfoGuid
= EFI_FILE_INFO_ID
;
574 UINTN size
= OFFSETOF(EFI_FILE_INFO
, FileName
) + 256;
575 _cleanup_freepool_ EFI_FILE_INFO
*fi
= NULL
;
581 /* A lot like LibFileInfo() but with useful error propagation */
583 fi
= AllocatePool(size
);
585 return EFI_OUT_OF_RESOURCES
;
587 err
= handle
->GetInfo(handle
, (EFI_GUID
*) &EfiFileInfoGuid
, &size
, fi
);
588 if (err
== EFI_BUFFER_TOO_SMALL
) {
590 fi
= AllocatePool(size
); /* GetInfo tells us the required size, let's use that now */
592 return EFI_OUT_OF_RESOURCES
;
594 err
= handle
->GetInfo(handle
, (EFI_GUID
*) &EfiFileInfoGuid
, &size
, fi
);
608 EFI_STATUS
readdir_harder(
609 EFI_FILE_HANDLE handle
,
610 EFI_FILE_INFO
**buffer
,
611 UINTN
*buffer_size
) {
620 /* buffer/buffer_size are both in and output parameters. Should be zero-initialized initially, and
621 * the specified buffer needs to be freed by caller, after final use. */
624 sz
= OFFSETOF(EFI_FILE_INFO
, FileName
) /* + 256 */;
626 *buffer
= AllocatePool(sz
);
628 return EFI_OUT_OF_RESOURCES
;
634 err
= handle
->Read(handle
, &sz
, *buffer
);
635 if (err
== EFI_BUFFER_TOO_SMALL
) {
638 *buffer
= AllocatePool(sz
);
641 return EFI_OUT_OF_RESOURCES
;
646 err
= handle
->Read(handle
, &sz
, *buffer
);
652 /* End of directory */
661 UINTN
strnlena(const CHAR8
*p
, UINTN maxlen
) {
667 for (c
= 0; c
< maxlen
; c
++)
674 CHAR8
*strndup8(const CHAR8
*p
, UINTN sz
) {
677 /* Following efilib's naming scheme this function would be called strndupa(), but we already have a
678 * function named like this in userspace, and it does something different there, hence to minimize
679 * confusion, let's pick a different name here */
681 assert(p
|| sz
== 0);
683 sz
= strnlena(p
, sz
);
685 n
= AllocatePool(sz
+ 1);
696 BOOLEAN
is_ascii(const CHAR16
*f
) {
707 CHAR16
**strv_free(CHAR16
**v
) {
711 for (CHAR16
**i
= v
; *i
; i
++)
718 EFI_STATUS
open_directory(
719 EFI_FILE_HANDLE root
,
721 EFI_FILE_HANDLE
*ret
) {
723 _cleanup_(FileHandleClosep
) EFI_FILE_HANDLE dir
= NULL
;
724 _cleanup_freepool_ EFI_FILE_INFO
*file_info
= NULL
;
729 /* Opens a file, and then verifies it is actually a directory */
731 err
= root
->Open(root
, &dir
, (CHAR16
*) path
, EFI_FILE_MODE_READ
, 0ULL);
735 err
= get_file_info_harder(dir
, &file_info
, NULL
);
738 if (!(file_info
->Attribute
& EFI_FILE_DIRECTORY
))
739 return EFI_LOAD_ERROR
;
741 *ret
= TAKE_PTR(dir
);
745 UINT64
get_os_indications_supported(void) {
749 /* Returns the supported OS indications. If we can't acquire it, returns a zeroed out mask, i.e. no
750 * supported features. */
752 err
= efivar_get_uint64_le(EFI_GLOBAL_GUID
, L
"OsIndicationsSupported", &osind
);