1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "device-path-util.h"
4 #include "memory-util-fundamental.h"
5 #include "proto/device-path.h"
6 #include "proto/simple-text-io.h"
11 EFI_STATUS
efivar_set_raw(const EFI_GUID
*vendor
, const char16_t
*name
, const void *buf
, size_t size
, uint32_t flags
) {
14 assert(buf
|| size
== 0);
16 flags
|= EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
;
17 return RT
->SetVariable((char16_t
*) name
, (EFI_GUID
*) vendor
, flags
, size
, (void *) buf
);
20 EFI_STATUS
efivar_set(const EFI_GUID
*vendor
, const char16_t
*name
, const char16_t
*value
, uint32_t flags
) {
24 return efivar_set_raw(vendor
, name
, value
, value
? strsize16(value
) : 0, flags
);
27 EFI_STATUS
efivar_set_uint_string(const EFI_GUID
*vendor
, const char16_t
*name
, size_t i
, uint32_t flags
) {
31 _cleanup_free_ char16_t
*str
= xasprintf("%zu", i
);
32 return efivar_set(vendor
, name
, str
, flags
);
35 EFI_STATUS
efivar_set_uint32_le(const EFI_GUID
*vendor
, const char16_t
*name
, uint32_t value
, uint32_t flags
) {
41 buf
[0] = (uint8_t)(value
>> 0U & 0xFF);
42 buf
[1] = (uint8_t)(value
>> 8U & 0xFF);
43 buf
[2] = (uint8_t)(value
>> 16U & 0xFF);
44 buf
[3] = (uint8_t)(value
>> 24U & 0xFF);
46 return efivar_set_raw(vendor
, name
, buf
, sizeof(buf
), flags
);
49 EFI_STATUS
efivar_set_uint64_le(const EFI_GUID
*vendor
, const char16_t
*name
, uint64_t value
, uint32_t flags
) {
55 buf
[0] = (uint8_t)(value
>> 0U & 0xFF);
56 buf
[1] = (uint8_t)(value
>> 8U & 0xFF);
57 buf
[2] = (uint8_t)(value
>> 16U & 0xFF);
58 buf
[3] = (uint8_t)(value
>> 24U & 0xFF);
59 buf
[4] = (uint8_t)(value
>> 32U & 0xFF);
60 buf
[5] = (uint8_t)(value
>> 40U & 0xFF);
61 buf
[6] = (uint8_t)(value
>> 48U & 0xFF);
62 buf
[7] = (uint8_t)(value
>> 56U & 0xFF);
64 return efivar_set_raw(vendor
, name
, buf
, sizeof(buf
), flags
);
67 EFI_STATUS
efivar_unset(const EFI_GUID
*vendor
, const char16_t
*name
, uint32_t flags
) {
73 /* We could be wiping a non-volatile variable here and the spec makes no guarantees that won't incur
74 * in an extra write (and thus wear out). So check and clear only if needed. */
75 err
= efivar_get_raw(vendor
, name
, NULL
, NULL
);
76 if (err
== EFI_SUCCESS
)
77 return efivar_set_raw(vendor
, name
, NULL
, 0, flags
);
82 EFI_STATUS
efivar_get(const EFI_GUID
*vendor
, const char16_t
*name
, char16_t
**ret
) {
83 _cleanup_free_ char16_t
*buf
= NULL
;
91 err
= efivar_get_raw(vendor
, name
, (char **) &buf
, &size
);
92 if (err
!= EFI_SUCCESS
)
95 /* Make sure there are no incomplete characters in the buffer */
96 if ((size
% sizeof(char16_t
)) != 0)
97 return EFI_INVALID_PARAMETER
;
102 /* Return buffer directly if it happens to be NUL terminated already */
103 if (size
>= sizeof(char16_t
) && buf
[size
/ sizeof(char16_t
) - 1] == 0) {
104 *ret
= TAKE_PTR(buf
);
108 /* Make sure a terminating NUL is available at the end */
109 val
= xmalloc(size
+ sizeof(char16_t
));
111 memcpy(val
, buf
, size
);
112 val
[size
/ sizeof(char16_t
) - 1] = 0; /* NUL terminate */
118 EFI_STATUS
efivar_get_uint_string(const EFI_GUID
*vendor
, const char16_t
*name
, size_t *ret
) {
119 _cleanup_free_ char16_t
*val
= NULL
;
126 err
= efivar_get(vendor
, name
, &val
);
127 if (err
!= EFI_SUCCESS
)
130 if (!parse_number16(val
, &u
, NULL
) || u
> SIZE_MAX
)
131 return EFI_INVALID_PARAMETER
;
138 EFI_STATUS
efivar_get_uint32_le(const EFI_GUID
*vendor
, const char16_t
*name
, uint32_t *ret
) {
139 _cleanup_free_
char *buf
= NULL
;
146 err
= efivar_get_raw(vendor
, name
, &buf
, &size
);
147 if (err
!= EFI_SUCCESS
)
150 if (size
!= sizeof(uint32_t))
151 return EFI_BUFFER_TOO_SMALL
;
154 *ret
= (uint32_t) buf
[0] << 0U | (uint32_t) buf
[1] << 8U | (uint32_t) buf
[2] << 16U |
155 (uint32_t) buf
[3] << 24U;
160 EFI_STATUS
efivar_get_uint64_le(const EFI_GUID
*vendor
, const char16_t
*name
, uint64_t *ret
) {
161 _cleanup_free_
char *buf
= NULL
;
168 err
= efivar_get_raw(vendor
, name
, &buf
, &size
);
169 if (err
!= EFI_SUCCESS
)
172 if (size
!= sizeof(uint64_t))
173 return EFI_BUFFER_TOO_SMALL
;
176 *ret
= (uint64_t) buf
[0] << 0U | (uint64_t) buf
[1] << 8U | (uint64_t) buf
[2] << 16U |
177 (uint64_t) buf
[3] << 24U | (uint64_t) buf
[4] << 32U | (uint64_t) buf
[5] << 40U |
178 (uint64_t) buf
[6] << 48U | (uint64_t) buf
[7] << 56U;
183 EFI_STATUS
efivar_get_raw(const EFI_GUID
*vendor
, const char16_t
*name
, char **ret
, size_t *ret_size
) {
190 err
= RT
->GetVariable((char16_t
*) name
, (EFI_GUID
*) vendor
, NULL
, &size
, NULL
);
191 if (err
!= EFI_BUFFER_TOO_SMALL
)
194 _cleanup_free_
void *buf
= xmalloc(size
);
195 err
= RT
->GetVariable((char16_t
*) name
, (EFI_GUID
*) vendor
, NULL
, &size
, buf
);
196 if (err
!= EFI_SUCCESS
)
200 *ret
= TAKE_PTR(buf
);
207 EFI_STATUS
efivar_get_boolean_u8(const EFI_GUID
*vendor
, const char16_t
*name
, bool *ret
) {
208 _cleanup_free_
char *b
= NULL
;
215 err
= efivar_get_raw(vendor
, name
, &b
, &size
);
216 if (err
!= EFI_SUCCESS
)
225 void efivar_set_time_usec(const EFI_GUID
*vendor
, const char16_t
*name
, uint64_t usec
) {
234 _cleanup_free_ char16_t
*str
= xasprintf("%" PRIu64
, usec
);
235 efivar_set(vendor
, name
, str
, 0);
238 void convert_efi_path(char16_t
*path
) {
241 for (size_t i
= 0, fixed
= 0;; i
++) {
242 /* Fix device path node separator. */
243 path
[fixed
] = (path
[i
] == '/') ? '\\' : path
[i
];
245 /* Double '\' is not allowed in EFI file paths. */
246 if (fixed
> 0 && path
[fixed
- 1] == '\\' && path
[fixed
] == '\\')
256 char16_t
*xstr8_to_path(const char *str8
) {
258 char16_t
*path
= xstr8_to_16(str8
);
259 convert_efi_path(path
);
263 static bool shall_be_whitespace(char16_t c
) {
264 return c
<= 0x20U
|| c
== 0x7FU
; /* All control characters + space */
267 char16_t
* mangle_stub_cmdline(char16_t
*cmdline
) {
275 /* Skip initial whitespace */
276 while (shall_be_whitespace(*p
))
279 /* Turn inner control characters into proper spaces */
280 for (e
= p
; *p
!= 0; p
++) {
281 if (shall_be_whitespace(*p
)) {
287 e
= q
; /* remember last non-whitespace char */
290 /* Chop off trailing whitespace */
295 EFI_STATUS
chunked_read(EFI_FILE
*file
, size_t *size
, void *buf
) {
302 /* This is a drop-in replacement for EFI_FILE->Read() with the same API behavior.
303 * Some broken firmwares cannot handle large file reads and will instead return
304 * an error. As a workaround, read such files in small chunks.
305 * Note that we cannot just try reading the whole file first on such firmware as
306 * that will permanently break the handle even if it is re-opened.
308 * https://github.com/systemd/systemd/issues/25911 */
313 size_t read
= 0, remaining
= *size
;
314 while (remaining
> 0) {
315 size_t chunk
= MIN(1024U * 1024U, remaining
);
317 err
= file
->Read(file
, &chunk
, (uint8_t *) buf
+ read
);
318 if (err
!= EFI_SUCCESS
)
321 /* Caller requested more bytes than are in file. */
324 assert(chunk
<= remaining
);
333 EFI_STATUS
file_read(EFI_FILE
*dir
, const char16_t
*name
, size_t off
, size_t size
, char **ret
, size_t *ret_size
) {
334 _cleanup_(file_closep
) EFI_FILE
*handle
= NULL
;
335 _cleanup_free_
char *buf
= NULL
;
342 err
= dir
->Open(dir
, &handle
, (char16_t
*) name
, EFI_FILE_MODE_READ
, 0ULL);
343 if (err
!= EFI_SUCCESS
)
347 _cleanup_free_ EFI_FILE_INFO
*info
= NULL
;
349 err
= get_file_info(handle
, &info
, NULL
);
350 if (err
!= EFI_SUCCESS
)
353 size
= info
->FileSize
;
357 err
= handle
->SetPosition(handle
, off
);
358 if (err
!= EFI_SUCCESS
)
362 /* Allocate some extra bytes to guarantee the result is NUL-terminated for char and char16_t strings. */
363 size_t extra
= size
% sizeof(char16_t
) + sizeof(char16_t
);
365 buf
= xmalloc(size
+ extra
);
366 err
= chunked_read(handle
, &size
, buf
);
367 if (err
!= EFI_SUCCESS
)
370 /* Note that chunked_read() changes size to reflect the actual bytes read. */
371 memzero(buf
+ size
, extra
);
373 *ret
= TAKE_PTR(buf
);
380 void print_at(size_t x
, size_t y
, size_t attr
, const char16_t
*str
) {
382 ST
->ConOut
->SetCursorPosition(ST
->ConOut
, x
, y
);
383 ST
->ConOut
->SetAttribute(ST
->ConOut
, attr
);
384 ST
->ConOut
->OutputString(ST
->ConOut
, (char16_t
*) str
);
387 void clear_screen(size_t attr
) {
389 ST
->ConOut
->SetAttribute(ST
->ConOut
, attr
);
390 ST
->ConOut
->ClearScreen(ST
->ConOut
);
393 void sort_pointer_array(
396 compare_pointer_func_t compare
) {
398 assert(array
|| n_members
== 0);
404 for (size_t i
= 1; i
< n_members
; i
++) {
406 void *entry
= array
[i
];
408 for (k
= i
; k
> 0; k
--) {
409 if (compare(array
[k
- 1], entry
) <= 0)
412 array
[k
] = array
[k
- 1];
419 EFI_STATUS
get_file_info(EFI_FILE
*handle
, EFI_FILE_INFO
**ret
, size_t *ret_size
) {
420 size_t size
= EFI_FILE_INFO_MIN_SIZE
;
421 _cleanup_free_ EFI_FILE_INFO
*fi
= NULL
;
428 err
= handle
->GetInfo(handle
, MAKE_GUID_PTR(EFI_FILE_INFO
), &size
, fi
);
429 if (err
== EFI_BUFFER_TOO_SMALL
) {
431 fi
= xmalloc(size
); /* GetInfo tells us the required size, let's use that now */
432 err
= handle
->GetInfo(handle
, MAKE_GUID_PTR(EFI_FILE_INFO
), &size
, fi
);
435 if (err
!= EFI_SUCCESS
)
448 EFI_FILE_INFO
**buffer
,
449 size_t *buffer_size
) {
458 /* buffer/buffer_size are both in and output parameters. Should be zero-initialized initially, and
459 * the specified buffer needs to be freed by caller, after final use. */
462 sz
= EFI_FILE_INFO_MIN_SIZE
;
463 *buffer
= xmalloc(sz
);
468 err
= handle
->Read(handle
, &sz
, *buffer
);
469 if (err
== EFI_BUFFER_TOO_SMALL
) {
471 *buffer
= xmalloc(sz
);
473 err
= handle
->Read(handle
, &sz
, *buffer
);
475 if (err
!= EFI_SUCCESS
)
479 /* End of directory */
488 bool is_ascii(const char16_t
*f
) {
499 char16_t
**strv_free(char16_t
**v
) {
503 for (char16_t
**i
= v
; *i
; i
++)
510 EFI_STATUS
open_directory(
512 const char16_t
*path
,
515 _cleanup_(file_closep
) EFI_FILE
*dir
= NULL
;
516 _cleanup_free_ EFI_FILE_INFO
*file_info
= NULL
;
521 /* Opens a file, and then verifies it is actually a directory */
523 err
= root
->Open(root
, &dir
, (char16_t
*) path
, EFI_FILE_MODE_READ
, 0);
524 if (err
!= EFI_SUCCESS
)
527 err
= get_file_info(dir
, &file_info
, NULL
);
528 if (err
!= EFI_SUCCESS
)
530 if (!FLAGS_SET(file_info
->Attribute
, EFI_FILE_DIRECTORY
))
531 return EFI_LOAD_ERROR
;
533 *ret
= TAKE_PTR(dir
);
537 uint64_t get_os_indications_supported(void) {
541 /* Returns the supported OS indications. If we can't acquire it, returns a zeroed out mask, i.e. no
542 * supported features. */
544 err
= efivar_get_uint64_le(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE
), u
"OsIndicationsSupported", &osind
);
545 if (err
!= EFI_SUCCESS
)
551 __attribute__((noinline
)) void notify_debugger(const char *identity
, volatile bool wait
) {
553 printf("%s@%p %s\n", identity
, __executable_start
, GIT_VERSION
);
555 printf("Waiting for debugger to attach...\n");
557 /* This is a poor programmer's breakpoint to wait until a debugger
558 * has attached to us. Just "set variable wait = 0" or "return" to continue. */
560 /* Prefer asm based stalling so that gdb has a source location to present. */
561 # if defined(__i386__) || defined(__x86_64__)
562 asm volatile("pause");
563 # elif defined(__aarch64__)
571 #if defined(__i386__) || defined(__x86_64__)
572 static uint8_t inb(uint16_t port
) {
574 asm volatile("inb %1, %0" : "=a"(value
) : "Nd"(port
));
578 static void outb(uint16_t port
, uint8_t value
) {
579 asm volatile("outb %0, %1" : : "a"(value
), "Nd"(port
));
582 void beep(unsigned beep_count
) {
585 BEEP_DURATION_USEC
= 100 * 1000,
586 WAIT_DURATION_USEC
= 400 * 1000,
588 PIT_FREQUENCY
= 0x1234dd,
589 SPEAKER_CONTROL_PORT
= 0x61,
590 SPEAKER_ON_MASK
= 0x03,
591 TIMER_PORT_MAGIC
= 0xB6,
592 TIMER_CONTROL_PORT
= 0x43,
593 TIMER_CONTROL2_PORT
= 0x42,
597 uint32_t counter
= PIT_FREQUENCY
/ PITCH
;
598 outb(TIMER_CONTROL_PORT
, TIMER_PORT_MAGIC
);
599 outb(TIMER_CONTROL2_PORT
, counter
& 0xFF);
600 outb(TIMER_CONTROL2_PORT
, (counter
>> 8) & 0xFF);
602 uint8_t value
= inb(SPEAKER_CONTROL_PORT
);
604 while (beep_count
> 0) {
605 /* Turn speaker on. */
606 value
|= SPEAKER_ON_MASK
;
607 outb(SPEAKER_CONTROL_PORT
, value
);
609 BS
->Stall(BEEP_DURATION_USEC
);
611 /* Turn speaker off. */
612 value
&= ~SPEAKER_ON_MASK
;
613 outb(SPEAKER_CONTROL_PORT
, value
);
617 BS
->Stall(WAIT_DURATION_USEC
);
622 EFI_STATUS
open_volume(EFI_HANDLE device
, EFI_FILE
**ret_file
) {
625 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*volume
;
629 err
= BS
->HandleProtocol(device
, MAKE_GUID_PTR(EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
), (void **) &volume
);
630 if (err
!= EFI_SUCCESS
)
633 err
= volume
->OpenVolume(volume
, &file
);
634 if (err
!= EFI_SUCCESS
)
641 void *find_configuration_table(const EFI_GUID
*guid
) {
642 for (size_t i
= 0; i
< ST
->NumberOfTableEntries
; i
++)
643 if (efi_guid_equal(&ST
->ConfigurationTable
[i
].VendorGuid
, guid
))
644 return ST
->ConfigurationTable
[i
].VendorTable
;
649 static void remove_boot_count(char16_t
*path
) {
650 char16_t
*prefix_end
;
651 const char16_t
*tail
;
656 prefix_end
= strchr16(path
, '+');
660 tail
= prefix_end
+ 1;
662 if (!parse_number16(tail
, &ignored
, &tail
))
667 if (!parse_number16(tail
, &ignored
, &tail
))
671 if (!IN_SET(*tail
, '\0', '.'))
674 strcpy16(prefix_end
, tail
);
677 char16_t
*get_extra_dir(const EFI_DEVICE_PATH
*file_path
) {
681 /* A device path is allowed to have more than one file path node. If that is the case they are
682 * supposed to be concatenated. Unfortunately, the device path to text protocol simply converts the
683 * nodes individually and then combines those with the usual '/' for device path nodes. But this does
684 * not create a legal EFI file path that the file protocol can use. */
686 /* Make sure we really only got file paths. */
687 for (const EFI_DEVICE_PATH
*node
= file_path
; !device_path_is_end(node
);
688 node
= device_path_next_node(node
))
689 if (node
->Type
!= MEDIA_DEVICE_PATH
|| node
->SubType
!= MEDIA_FILEPATH_DP
)
692 _cleanup_free_ char16_t
*file_path_str
= NULL
;
693 if (device_path_to_str(file_path
, &file_path_str
) != EFI_SUCCESS
)
696 convert_efi_path(file_path_str
);
697 remove_boot_count(file_path_str
);
698 return xasprintf("%ls.extra.d", file_path_str
);