1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
10 EFI_STATUS
parse_boolean(const char *v
, bool *b
) {
14 return EFI_INVALID_PARAMETER
;
16 if (streq8(v
, "1") || streq8(v
, "yes") || streq8(v
, "y") || streq8(v
, "true") || streq8(v
, "t") ||
22 if (streq8(v
, "0") || streq8(v
, "no") || streq8(v
, "n") || streq8(v
, "false") || streq8(v
, "f") ||
28 return EFI_INVALID_PARAMETER
;
31 EFI_STATUS
efivar_set_raw(const EFI_GUID
*vendor
, const char16_t
*name
, const void *buf
, size_t size
, uint32_t flags
) {
34 assert(buf
|| size
== 0);
36 flags
|= EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
;
37 return RT
->SetVariable((char16_t
*) name
, (EFI_GUID
*) vendor
, flags
, size
, (void *) buf
);
40 EFI_STATUS
efivar_set(const EFI_GUID
*vendor
, const char16_t
*name
, const char16_t
*value
, uint32_t flags
) {
44 return efivar_set_raw(vendor
, name
, value
, value
? strsize16(value
) : 0, flags
);
47 EFI_STATUS
efivar_set_uint_string(const EFI_GUID
*vendor
, const char16_t
*name
, size_t i
, uint32_t flags
) {
51 _cleanup_free_ char16_t
*str
= xasprintf("%zu", i
);
52 return efivar_set(vendor
, name
, str
, flags
);
55 EFI_STATUS
efivar_set_uint32_le(const EFI_GUID
*vendor
, const char16_t
*name
, uint32_t value
, uint32_t flags
) {
61 buf
[0] = (uint8_t)(value
>> 0U & 0xFF);
62 buf
[1] = (uint8_t)(value
>> 8U & 0xFF);
63 buf
[2] = (uint8_t)(value
>> 16U & 0xFF);
64 buf
[3] = (uint8_t)(value
>> 24U & 0xFF);
66 return efivar_set_raw(vendor
, name
, buf
, sizeof(buf
), flags
);
69 EFI_STATUS
efivar_set_uint64_le(const EFI_GUID
*vendor
, const char16_t
*name
, uint64_t value
, uint32_t flags
) {
75 buf
[0] = (uint8_t)(value
>> 0U & 0xFF);
76 buf
[1] = (uint8_t)(value
>> 8U & 0xFF);
77 buf
[2] = (uint8_t)(value
>> 16U & 0xFF);
78 buf
[3] = (uint8_t)(value
>> 24U & 0xFF);
79 buf
[4] = (uint8_t)(value
>> 32U & 0xFF);
80 buf
[5] = (uint8_t)(value
>> 40U & 0xFF);
81 buf
[6] = (uint8_t)(value
>> 48U & 0xFF);
82 buf
[7] = (uint8_t)(value
>> 56U & 0xFF);
84 return efivar_set_raw(vendor
, name
, buf
, sizeof(buf
), flags
);
87 EFI_STATUS
efivar_get(const EFI_GUID
*vendor
, const char16_t
*name
, char16_t
**ret
) {
88 _cleanup_free_ char16_t
*buf
= NULL
;
96 err
= efivar_get_raw(vendor
, name
, (char **) &buf
, &size
);
97 if (err
!= EFI_SUCCESS
)
100 /* Make sure there are no incomplete characters in the buffer */
101 if ((size
% sizeof(char16_t
)) != 0)
102 return EFI_INVALID_PARAMETER
;
107 /* Return buffer directly if it happens to be NUL terminated already */
108 if (size
>= sizeof(char16_t
) && buf
[size
/ sizeof(char16_t
) - 1] == 0) {
109 *ret
= TAKE_PTR(buf
);
113 /* Make sure a terminating NUL is available at the end */
114 val
= xmalloc(size
+ sizeof(char16_t
));
116 memcpy(val
, buf
, size
);
117 val
[size
/ sizeof(char16_t
) - 1] = 0; /* NUL terminate */
123 EFI_STATUS
efivar_get_uint_string(const EFI_GUID
*vendor
, const char16_t
*name
, size_t *ret
) {
124 _cleanup_free_ char16_t
*val
= NULL
;
131 err
= efivar_get(vendor
, name
, &val
);
132 if (err
!= EFI_SUCCESS
)
135 if (!parse_number16(val
, &u
, NULL
) || u
> SIZE_MAX
)
136 return EFI_INVALID_PARAMETER
;
143 EFI_STATUS
efivar_get_uint32_le(const EFI_GUID
*vendor
, const char16_t
*name
, uint32_t *ret
) {
144 _cleanup_free_
char *buf
= NULL
;
151 err
= efivar_get_raw(vendor
, name
, &buf
, &size
);
152 if (err
!= EFI_SUCCESS
)
155 if (size
!= sizeof(uint32_t))
156 return EFI_BUFFER_TOO_SMALL
;
159 *ret
= (uint32_t) buf
[0] << 0U | (uint32_t) buf
[1] << 8U | (uint32_t) buf
[2] << 16U |
160 (uint32_t) buf
[3] << 24U;
165 EFI_STATUS
efivar_get_uint64_le(const EFI_GUID
*vendor
, const char16_t
*name
, uint64_t *ret
) {
166 _cleanup_free_
char *buf
= NULL
;
173 err
= efivar_get_raw(vendor
, name
, &buf
, &size
);
174 if (err
!= EFI_SUCCESS
)
177 if (size
!= sizeof(uint64_t))
178 return EFI_BUFFER_TOO_SMALL
;
181 *ret
= (uint64_t) buf
[0] << 0U | (uint64_t) buf
[1] << 8U | (uint64_t) buf
[2] << 16U |
182 (uint64_t) buf
[3] << 24U | (uint64_t) buf
[4] << 32U | (uint64_t) buf
[5] << 40U |
183 (uint64_t) buf
[6] << 48U | (uint64_t) buf
[7] << 56U;
188 EFI_STATUS
efivar_get_raw(const EFI_GUID
*vendor
, const char16_t
*name
, char **ret
, size_t *ret_size
) {
189 _cleanup_free_
char *buf
= NULL
;
196 l
= sizeof(char16_t
*) * EFI_MAXIMUM_VARIABLE_SIZE
;
199 err
= RT
->GetVariable((char16_t
*) name
, (EFI_GUID
*) vendor
, NULL
, &l
, buf
);
200 if (err
!= EFI_SUCCESS
)
204 *ret
= TAKE_PTR(buf
);
211 EFI_STATUS
efivar_get_boolean_u8(const EFI_GUID
*vendor
, const char16_t
*name
, bool *ret
) {
212 _cleanup_free_
char *b
= NULL
;
219 err
= efivar_get_raw(vendor
, name
, &b
, &size
);
220 if (err
!= EFI_SUCCESS
)
229 void efivar_set_time_usec(const EFI_GUID
*vendor
, const char16_t
*name
, uint64_t usec
) {
238 _cleanup_free_ char16_t
*str
= xasprintf("%" PRIu64
, usec
);
239 efivar_set(vendor
, name
, str
, 0);
242 void convert_efi_path(char16_t
*path
) {
245 for (size_t i
= 0, fixed
= 0;; i
++) {
246 /* Fix device path node separator. */
247 path
[fixed
] = (path
[i
] == '/') ? '\\' : path
[i
];
249 /* Double '\' is not allowed in EFI file paths. */
250 if (fixed
> 0 && path
[fixed
- 1] == '\\' && path
[fixed
] == '\\')
260 char16_t
*xstr8_to_path(const char *str8
) {
262 char16_t
*path
= xstr8_to_16(str8
);
263 convert_efi_path(path
);
267 void mangle_stub_cmdline(char16_t
*cmdline
) {
268 char16_t
*p
= cmdline
;
270 for (; *cmdline
!= '\0'; cmdline
++)
271 /* Convert ASCII control characters to spaces. */
272 if (*cmdline
<= 0x1F)
275 /* chomp the trailing whitespaces */
276 while (cmdline
!= p
) {
286 EFI_STATUS
file_read(EFI_FILE
*dir
, const char16_t
*name
, size_t off
, size_t size
, char **ret
, size_t *ret_size
) {
287 _cleanup_(file_closep
) EFI_FILE
*handle
= NULL
;
288 _cleanup_free_
char *buf
= NULL
;
295 err
= dir
->Open(dir
, &handle
, (char16_t
*) name
, EFI_FILE_MODE_READ
, 0ULL);
296 if (err
!= EFI_SUCCESS
)
300 _cleanup_free_ EFI_FILE_INFO
*info
= NULL
;
302 err
= get_file_info_harder(handle
, &info
, NULL
);
303 if (err
!= EFI_SUCCESS
)
306 size
= info
->FileSize
;
310 err
= handle
->SetPosition(handle
, off
);
311 if (err
!= EFI_SUCCESS
)
315 /* Allocate some extra bytes to guarantee the result is NUL-terminated for char and char16_t strings. */
316 size_t extra
= size
% sizeof(char16_t
) + sizeof(char16_t
);
318 buf
= xmalloc(size
+ extra
);
320 err
= handle
->Read(handle
, &size
, buf
);
321 if (err
!= EFI_SUCCESS
)
325 /* Note that handle->Read() changes size to reflect the actually bytes read. */
326 memset(buf
+ size
, 0, extra
);
328 *ret
= TAKE_PTR(buf
);
335 void print_at(size_t x
, size_t y
, size_t attr
, const char16_t
*str
) {
337 ST
->ConOut
->SetCursorPosition(ST
->ConOut
, x
, y
);
338 ST
->ConOut
->SetAttribute(ST
->ConOut
, attr
);
339 ST
->ConOut
->OutputString(ST
->ConOut
, (char16_t
*) str
);
342 void clear_screen(size_t attr
) {
344 ST
->ConOut
->SetAttribute(ST
->ConOut
, attr
);
345 ST
->ConOut
->ClearScreen(ST
->ConOut
);
348 void sort_pointer_array(
351 compare_pointer_func_t compare
) {
353 assert(array
|| n_members
== 0);
359 for (size_t i
= 1; i
< n_members
; i
++) {
361 void *entry
= array
[i
];
363 for (k
= i
; k
> 0; k
--) {
364 if (compare(array
[k
- 1], entry
) <= 0)
367 array
[k
] = array
[k
- 1];
374 EFI_STATUS
get_file_info_harder(
379 size_t size
= offsetof(EFI_FILE_INFO
, FileName
) + 256;
380 _cleanup_free_ EFI_FILE_INFO
*fi
= NULL
;
386 /* A lot like LibFileInfo() but with useful error propagation */
389 err
= handle
->GetInfo(handle
, MAKE_GUID_PTR(EFI_FILE_INFO
), &size
, fi
);
390 if (err
== EFI_BUFFER_TOO_SMALL
) {
392 fi
= xmalloc(size
); /* GetInfo tells us the required size, let's use that now */
393 err
= handle
->GetInfo(handle
, MAKE_GUID_PTR(EFI_FILE_INFO
), &size
, fi
);
396 if (err
!= EFI_SUCCESS
)
407 EFI_STATUS
readdir_harder(
409 EFI_FILE_INFO
**buffer
,
410 size_t *buffer_size
) {
419 /* buffer/buffer_size are both in and output parameters. Should be zero-initialized initially, and
420 * the specified buffer needs to be freed by caller, after final use. */
423 /* Some broken firmware violates the EFI spec by still advancing the readdir
424 * position when returning EFI_BUFFER_TOO_SMALL, effectively skipping over any files when
425 * the buffer was too small. Therefore, start with a buffer that should handle FAT32 max
427 * As a side effect, most readdir_harder() calls will now be slightly faster. */
428 sz
= sizeof(EFI_FILE_INFO
) + 256 * sizeof(char16_t
);
429 *buffer
= xmalloc(sz
);
434 err
= handle
->Read(handle
, &sz
, *buffer
);
435 if (err
== EFI_BUFFER_TOO_SMALL
) {
437 *buffer
= xmalloc(sz
);
439 err
= handle
->Read(handle
, &sz
, *buffer
);
441 if (err
!= EFI_SUCCESS
)
445 /* End of directory */
454 bool is_ascii(const char16_t
*f
) {
465 char16_t
**strv_free(char16_t
**v
) {
469 for (char16_t
**i
= v
; *i
; i
++)
476 EFI_STATUS
open_directory(
478 const char16_t
*path
,
481 _cleanup_(file_closep
) EFI_FILE
*dir
= NULL
;
482 _cleanup_free_ EFI_FILE_INFO
*file_info
= NULL
;
487 /* Opens a file, and then verifies it is actually a directory */
489 err
= root
->Open(root
, &dir
, (char16_t
*) path
, EFI_FILE_MODE_READ
, 0);
490 if (err
!= EFI_SUCCESS
)
493 err
= get_file_info_harder(dir
, &file_info
, NULL
);
494 if (err
!= EFI_SUCCESS
)
496 if (!FLAGS_SET(file_info
->Attribute
, EFI_FILE_DIRECTORY
))
497 return EFI_LOAD_ERROR
;
499 *ret
= TAKE_PTR(dir
);
503 uint64_t get_os_indications_supported(void) {
507 /* Returns the supported OS indications. If we can't acquire it, returns a zeroed out mask, i.e. no
508 * supported features. */
510 err
= efivar_get_uint64_le(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE
), u
"OsIndicationsSupported", &osind
);
511 if (err
!= EFI_SUCCESS
)
518 extern uint8_t _text
, _data
;
519 __attribute__((noinline
)) void notify_debugger(const char *identity
, volatile bool wait
) {
520 printf("%s@%p,%p\n", identity
, &_text
, &_data
);
522 printf("Waiting for debugger to attach...\n");
524 /* This is a poor programmer's breakpoint to wait until a debugger
525 * has attached to us. Just "set variable wait = 0" or "return" to continue. */
527 /* Prefer asm based stalling so that gdb has a source location to present. */
528 #if defined(__i386__) || defined(__x86_64__)
529 asm volatile("pause");
530 #elif defined(__aarch64__)
539 void hexdump(const char16_t
*prefix
, const void *data
, size_t size
) {
540 static const char hex
[16] = "0123456789abcdef";
541 _cleanup_free_ char16_t
*buf
= NULL
;
542 const uint8_t *d
= data
;
545 assert(data
|| size
== 0);
547 /* Debugging helper — please keep this around, even if not used */
549 buf
= xnew(char16_t
, size
*2+1);
551 for (size_t i
= 0; i
< size
; i
++) {
552 buf
[i
*2] = hex
[d
[i
] >> 4];
553 buf
[i
*2+1] = hex
[d
[i
] & 0x0F];
558 log_error("%ls[%zu]: %ls", prefix
, size
, buf
);
562 #if defined(__i386__) || defined(__x86_64__)
563 static inline uint8_t inb(uint16_t port
) {
565 asm volatile("inb %1, %0" : "=a"(value
) : "Nd"(port
));
569 static inline void outb(uint16_t port
, uint8_t value
) {
570 asm volatile("outb %0, %1" : : "a"(value
), "Nd"(port
));
573 void beep(UINTN beep_count
) {
576 BEEP_DURATION_USEC
= 100 * 1000,
577 WAIT_DURATION_USEC
= 400 * 1000,
579 PIT_FREQUENCY
= 0x1234dd,
580 SPEAKER_CONTROL_PORT
= 0x61,
581 SPEAKER_ON_MASK
= 0x03,
582 TIMER_PORT_MAGIC
= 0xB6,
583 TIMER_CONTROL_PORT
= 0x43,
584 TIMER_CONTROL2_PORT
= 0x42,
588 uint32_t counter
= PIT_FREQUENCY
/ PITCH
;
589 outb(TIMER_CONTROL_PORT
, TIMER_PORT_MAGIC
);
590 outb(TIMER_CONTROL2_PORT
, counter
& 0xFF);
591 outb(TIMER_CONTROL2_PORT
, (counter
>> 8) & 0xFF);
593 uint8_t value
= inb(SPEAKER_CONTROL_PORT
);
595 while (beep_count
> 0) {
596 /* Turn speaker on. */
597 value
|= SPEAKER_ON_MASK
;
598 outb(SPEAKER_CONTROL_PORT
, value
);
600 BS
->Stall(BEEP_DURATION_USEC
);
602 /* Turn speaker off. */
603 value
&= ~SPEAKER_ON_MASK
;
604 outb(SPEAKER_CONTROL_PORT
, value
);
608 BS
->Stall(WAIT_DURATION_USEC
);
613 EFI_STATUS
open_volume(EFI_HANDLE device
, EFI_FILE
**ret_file
) {
616 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*volume
;
620 err
= BS
->HandleProtocol(device
, MAKE_GUID_PTR(EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
), (void **) &volume
);
621 if (err
!= EFI_SUCCESS
)
624 err
= volume
->OpenVolume(volume
, &file
);
625 if (err
!= EFI_SUCCESS
)
632 EFI_STATUS
make_file_device_path(EFI_HANDLE device
, const char16_t
*file
, EFI_DEVICE_PATH
**ret_dp
) {
639 err
= BS
->HandleProtocol(device
, MAKE_GUID_PTR(EFI_DEVICE_PATH_PROTOCOL
), (void **) &dp
);
640 if (err
!= EFI_SUCCESS
)
643 EFI_DEVICE_PATH
*end_node
= dp
;
644 while (!IsDevicePathEnd(end_node
))
645 end_node
= NextDevicePathNode(end_node
);
647 size_t file_size
= strsize16(file
);
648 size_t dp_size
= (uint8_t *) end_node
- (uint8_t *) dp
;
650 /* Make a copy that can also hold a file media device path. */
651 *ret_dp
= xmalloc(dp_size
+ file_size
+ SIZE_OF_FILEPATH_DEVICE_PATH
+ END_DEVICE_PATH_LENGTH
);
652 dp
= mempcpy(*ret_dp
, dp
, dp_size
);
654 /* Replace end node with file media device path. Use memcpy() in case dp is unaligned (if accessed as
655 * FILEPATH_DEVICE_PATH). */
656 dp
->Type
= MEDIA_DEVICE_PATH
;
657 dp
->SubType
= MEDIA_FILEPATH_DP
;
658 memcpy((uint8_t *) dp
+ offsetof(FILEPATH_DEVICE_PATH
, PathName
), file
, file_size
);
659 SetDevicePathNodeLength(dp
, offsetof(FILEPATH_DEVICE_PATH
, PathName
) + file_size
);
661 dp
= NextDevicePathNode(dp
);
662 SetDevicePathEndNode(dp
);
666 EFI_STATUS
device_path_to_str(const EFI_DEVICE_PATH
*dp
, char16_t
**ret
) {
667 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL
*dp_to_text
;
669 _cleanup_free_ char16_t
*str
= NULL
;
674 err
= BS
->LocateProtocol(MAKE_GUID_PTR(EFI_DEVICE_PATH_TO_TEXT_PROTOCOL
), NULL
, (void **) &dp_to_text
);
675 if (err
!= EFI_SUCCESS
) {
676 /* If the device path to text protocol is not available we can still do a best-effort attempt
677 * to convert it ourselves if we are given filepath-only device path. */
680 for (const EFI_DEVICE_PATH
*node
= dp
; !IsDevicePathEnd(node
);
681 node
= NextDevicePathNode(node
)) {
683 if (DevicePathType(node
) != MEDIA_DEVICE_PATH
||
684 DevicePathSubType(node
) != MEDIA_FILEPATH_DP
)
687 size_t path_size
= DevicePathNodeLength(node
);
688 if (path_size
<= offsetof(FILEPATH_DEVICE_PATH
, PathName
) || path_size
% sizeof(char16_t
))
689 return EFI_INVALID_PARAMETER
;
690 path_size
-= offsetof(FILEPATH_DEVICE_PATH
, PathName
);
692 _cleanup_free_ char16_t
*old
= str
;
693 str
= xmalloc(size
+ path_size
);
695 memcpy(str
, old
, size
);
696 str
[size
/ sizeof(char16_t
) - 1] = '\\';
699 memcpy(str
+ (size
/ sizeof(char16_t
)),
700 ((uint8_t *) node
) + offsetof(FILEPATH_DEVICE_PATH
, PathName
),
705 *ret
= TAKE_PTR(str
);
709 str
= dp_to_text
->ConvertDevicePathToText(dp
, false, false);
711 return EFI_OUT_OF_RESOURCES
;
713 *ret
= TAKE_PTR(str
);
717 void *find_configuration_table(const EFI_GUID
*guid
) {
718 for (size_t i
= 0; i
< ST
->NumberOfTableEntries
; i
++)
719 if (efi_guid_equal(&ST
->ConfigurationTable
[i
].VendorGuid
, guid
))
720 return ST
->ConfigurationTable
[i
].VendorTable
;