| /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
| |
| #include <efi.h> |
| #include <efilib.h> |
| #include <inttypes.h> |
| |
| #include "ticks.h" |
| #include "util.h" |
| |
| EFI_STATUS parse_boolean(const char *v, bool *b) { |
| assert(b); |
| |
| if (!v) |
| return EFI_INVALID_PARAMETER; |
| |
| if (streq8(v, "1") || streq8(v, "yes") || streq8(v, "y") || streq8(v, "true") || streq8(v, "t") || |
| streq8(v, "on")) { |
| *b = true; |
| return EFI_SUCCESS; |
| } |
| |
| if (streq8(v, "0") || streq8(v, "no") || streq8(v, "n") || streq8(v, "false") || streq8(v, "f") || |
| streq8(v, "off")) { |
| *b = false; |
| return EFI_SUCCESS; |
| } |
| |
| return EFI_INVALID_PARAMETER; |
| } |
| |
| EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, const char16_t *name, const void *buf, size_t size, uint32_t flags) { |
| assert(vendor); |
| assert(name); |
| assert(buf || size == 0); |
| |
| flags |= EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS; |
| return RT->SetVariable((char16_t *) name, (EFI_GUID *) vendor, flags, size, (void *) buf); |
| } |
| |
| EFI_STATUS efivar_set(const EFI_GUID *vendor, const char16_t *name, const char16_t *value, uint32_t flags) { |
| assert(vendor); |
| assert(name); |
| |
| return efivar_set_raw(vendor, name, value, value ? strsize16(value) : 0, flags); |
| } |
| |
| EFI_STATUS efivar_set_uint_string(const EFI_GUID *vendor, const char16_t *name, size_t i, uint32_t flags) { |
| assert(vendor); |
| assert(name); |
| |
| _cleanup_free_ char16_t *str = xasprintf("%zu", i); |
| return efivar_set(vendor, name, str, flags); |
| } |
| |
| EFI_STATUS efivar_set_uint32_le(const EFI_GUID *vendor, const char16_t *name, uint32_t value, uint32_t flags) { |
| uint8_t buf[4]; |
| |
| assert(vendor); |
| assert(name); |
| |
| buf[0] = (uint8_t)(value >> 0U & 0xFF); |
| buf[1] = (uint8_t)(value >> 8U & 0xFF); |
| buf[2] = (uint8_t)(value >> 16U & 0xFF); |
| buf[3] = (uint8_t)(value >> 24U & 0xFF); |
| |
| return efivar_set_raw(vendor, name, buf, sizeof(buf), flags); |
| } |
| |
| EFI_STATUS efivar_set_uint64_le(const EFI_GUID *vendor, const char16_t *name, uint64_t value, uint32_t flags) { |
| uint8_t buf[8]; |
| |
| assert(vendor); |
| assert(name); |
| |
| buf[0] = (uint8_t)(value >> 0U & 0xFF); |
| buf[1] = (uint8_t)(value >> 8U & 0xFF); |
| buf[2] = (uint8_t)(value >> 16U & 0xFF); |
| buf[3] = (uint8_t)(value >> 24U & 0xFF); |
| buf[4] = (uint8_t)(value >> 32U & 0xFF); |
| buf[5] = (uint8_t)(value >> 40U & 0xFF); |
| buf[6] = (uint8_t)(value >> 48U & 0xFF); |
| buf[7] = (uint8_t)(value >> 56U & 0xFF); |
| |
| return efivar_set_raw(vendor, name, buf, sizeof(buf), flags); |
| } |
| |
| EFI_STATUS efivar_get(const EFI_GUID *vendor, const char16_t *name, char16_t **ret) { |
| _cleanup_free_ char16_t *buf = NULL; |
| EFI_STATUS err; |
| char16_t *val; |
| size_t size; |
| |
| assert(vendor); |
| assert(name); |
| |
| err = efivar_get_raw(vendor, name, (char **) &buf, &size); |
| if (err != EFI_SUCCESS) |
| return err; |
| |
| /* Make sure there are no incomplete characters in the buffer */ |
| if ((size % sizeof(char16_t)) != 0) |
| return EFI_INVALID_PARAMETER; |
| |
| if (!ret) |
| return EFI_SUCCESS; |
| |
| /* Return buffer directly if it happens to be NUL terminated already */ |
| if (size >= sizeof(char16_t) && buf[size / sizeof(char16_t) - 1] == 0) { |
| *ret = TAKE_PTR(buf); |
| return EFI_SUCCESS; |
| } |
| |
| /* Make sure a terminating NUL is available at the end */ |
| val = xmalloc(size + sizeof(char16_t)); |
| |
| memcpy(val, buf, size); |
| val[size / sizeof(char16_t) - 1] = 0; /* NUL terminate */ |
| |
| *ret = val; |
| return EFI_SUCCESS; |
| } |
| |
| EFI_STATUS efivar_get_uint_string(const EFI_GUID *vendor, const char16_t *name, size_t *ret) { |
| _cleanup_free_ char16_t *val = NULL; |
| EFI_STATUS err; |
| uint64_t u; |
| |
| assert(vendor); |
| assert(name); |
| |
| err = efivar_get(vendor, name, &val); |
| if (err != EFI_SUCCESS) |
| return err; |
| |
| if (!parse_number16(val, &u, NULL) || u > SIZE_MAX) |
| return EFI_INVALID_PARAMETER; |
| |
| if (ret) |
| *ret = u; |
| return EFI_SUCCESS; |
| } |
| |
| EFI_STATUS efivar_get_uint32_le(const EFI_GUID *vendor, const char16_t *name, uint32_t *ret) { |
| _cleanup_free_ char *buf = NULL; |
| size_t size; |
| EFI_STATUS err; |
| |
| assert(vendor); |
| assert(name); |
| |
| err = efivar_get_raw(vendor, name, &buf, &size); |
| if (err != EFI_SUCCESS) |
| return err; |
| |
| if (size != sizeof(uint32_t)) |
| return EFI_BUFFER_TOO_SMALL; |
| |
| if (ret) |
| *ret = (uint32_t) buf[0] << 0U | (uint32_t) buf[1] << 8U | (uint32_t) buf[2] << 16U | |
| (uint32_t) buf[3] << 24U; |
| |
| return EFI_SUCCESS; |
| } |
| |
| EFI_STATUS efivar_get_uint64_le(const EFI_GUID *vendor, const char16_t *name, uint64_t *ret) { |
| _cleanup_free_ char *buf = NULL; |
| size_t size; |
| EFI_STATUS err; |
| |
| assert(vendor); |
| assert(name); |
| |
| err = efivar_get_raw(vendor, name, &buf, &size); |
| if (err != EFI_SUCCESS) |
| return err; |
| |
| if (size != sizeof(uint64_t)) |
| return EFI_BUFFER_TOO_SMALL; |
| |
| if (ret) |
| *ret = (uint64_t) buf[0] << 0U | (uint64_t) buf[1] << 8U | (uint64_t) buf[2] << 16U | |
| (uint64_t) buf[3] << 24U | (uint64_t) buf[4] << 32U | (uint64_t) buf[5] << 40U | |
| (uint64_t) buf[6] << 48U | (uint64_t) buf[7] << 56U; |
| |
| return EFI_SUCCESS; |
| } |
| |
| EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const char16_t *name, char **ret, size_t *ret_size) { |
| _cleanup_free_ char *buf = NULL; |
| size_t l; |
| EFI_STATUS err; |
| |
| assert(vendor); |
| assert(name); |
| |
| l = sizeof(char16_t *) * EFI_MAXIMUM_VARIABLE_SIZE; |
| buf = xmalloc(l); |
| |
| err = RT->GetVariable((char16_t *) name, (EFI_GUID *) vendor, NULL, &l, buf); |
| if (err != EFI_SUCCESS) |
| return err; |
| |
| if (ret) |
| *ret = TAKE_PTR(buf); |
| if (ret_size) |
| *ret_size = l; |
| |
| return EFI_SUCCESS; |
| } |
| |
| EFI_STATUS efivar_get_boolean_u8(const EFI_GUID *vendor, const char16_t *name, bool *ret) { |
| _cleanup_free_ char *b = NULL; |
| size_t size; |
| EFI_STATUS err; |
| |
| assert(vendor); |
| assert(name); |
| |
| err = efivar_get_raw(vendor, name, &b, &size); |
| if (err != EFI_SUCCESS) |
| return err; |
| |
| if (ret) |
| *ret = *b > 0; |
| |
| return EFI_SUCCESS; |
| } |
| |
| void efivar_set_time_usec(const EFI_GUID *vendor, const char16_t *name, uint64_t usec) { |
| assert(vendor); |
| assert(name); |
| |
| if (usec == 0) |
| usec = time_usec(); |
| if (usec == 0) |
| return; |
| |
| _cleanup_free_ char16_t *str = xasprintf("%" PRIu64, usec); |
| efivar_set(vendor, name, str, 0); |
| } |
| |
| void convert_efi_path(char16_t *path) { |
| assert(path); |
| |
| for (size_t i = 0, fixed = 0;; i++) { |
| /* Fix device path node separator. */ |
| path[fixed] = (path[i] == '/') ? '\\' : path[i]; |
| |
| /* Double '\' is not allowed in EFI file paths. */ |
| if (fixed > 0 && path[fixed - 1] == '\\' && path[fixed] == '\\') |
| continue; |
| |
| if (path[i] == '\0') |
| break; |
| |
| fixed++; |
| } |
| } |
| |
| char16_t *xstr8_to_path(const char *str8) { |
| assert(str8); |
| char16_t *path = xstr8_to_16(str8); |
| convert_efi_path(path); |
| return path; |
| } |
| |
| void mangle_stub_cmdline(char16_t *cmdline) { |
| char16_t *p = cmdline; |
| |
| for (; *cmdline != '\0'; cmdline++) |
| /* Convert ASCII control characters to spaces. */ |
| if (*cmdline <= 0x1F) |
| *cmdline = ' '; |
| |
| /* chomp the trailing whitespaces */ |
| while (cmdline != p) { |
| --cmdline; |
| |
| if (*cmdline != ' ') |
| break; |
| |
| *cmdline = '\0'; |
| } |
| } |
| |
| EFI_STATUS file_read(EFI_FILE *dir, const char16_t *name, size_t off, size_t size, char **ret, size_t *ret_size) { |
| _cleanup_(file_closep) EFI_FILE *handle = NULL; |
| _cleanup_free_ char *buf = NULL; |
| EFI_STATUS err; |
| |
| assert(dir); |
| assert(name); |
| assert(ret); |
| |
| err = dir->Open(dir, &handle, (char16_t*) name, EFI_FILE_MODE_READ, 0ULL); |
| if (err != EFI_SUCCESS) |
| return err; |
| |
| if (size == 0) { |
| _cleanup_free_ EFI_FILE_INFO *info = NULL; |
| |
| err = get_file_info_harder(handle, &info, NULL); |
| if (err != EFI_SUCCESS) |
| return err; |
| |
| size = info->FileSize; |
| } |
| |
| if (off > 0) { |
| err = handle->SetPosition(handle, off); |
| if (err != EFI_SUCCESS) |
| return err; |
| } |
| |
| /* Allocate some extra bytes to guarantee the result is NUL-terminated for char and char16_t strings. */ |
| size_t extra = size % sizeof(char16_t) + sizeof(char16_t); |
| |
| buf = xmalloc(size + extra); |
| if (size > 0) { |
| err = handle->Read(handle, &size, buf); |
| if (err != EFI_SUCCESS) |
| return err; |
| } |
| |
| /* Note that handle->Read() changes size to reflect the actually bytes read. */ |
| memset(buf + size, 0, extra); |
| |
| *ret = TAKE_PTR(buf); |
| if (ret_size) |
| *ret_size = size; |
| |
| return err; |
| } |
| |
| void print_at(size_t x, size_t y, size_t attr, const char16_t *str) { |
| assert(str); |
| ST->ConOut->SetCursorPosition(ST->ConOut, x, y); |
| ST->ConOut->SetAttribute(ST->ConOut, attr); |
| ST->ConOut->OutputString(ST->ConOut, (char16_t *) str); |
| } |
| |
| void clear_screen(size_t attr) { |
| log_wait(); |
| ST->ConOut->SetAttribute(ST->ConOut, attr); |
| ST->ConOut->ClearScreen(ST->ConOut); |
| } |
| |
| void sort_pointer_array( |
| void **array, |
| size_t n_members, |
| compare_pointer_func_t compare) { |
| |
| assert(array || n_members == 0); |
| assert(compare); |
| |
| if (n_members <= 1) |
| return; |
| |
| for (size_t i = 1; i < n_members; i++) { |
| size_t k; |
| void *entry = array[i]; |
| |
| for (k = i; k > 0; k--) { |
| if (compare(array[k - 1], entry) <= 0) |
| break; |
| |
| array[k] = array[k - 1]; |
| } |
| |
| array[k] = entry; |
| } |
| } |
| |
| EFI_STATUS get_file_info_harder( |
| EFI_FILE *handle, |
| EFI_FILE_INFO **ret, |
| size_t *ret_size) { |
| |
| size_t size = offsetof(EFI_FILE_INFO, FileName) + 256; |
| _cleanup_free_ EFI_FILE_INFO *fi = NULL; |
| EFI_STATUS err; |
| |
| assert(handle); |
| assert(ret); |
| |
| /* A lot like LibFileInfo() but with useful error propagation */ |
| |
| fi = xmalloc(size); |
| err = handle->GetInfo(handle, MAKE_GUID_PTR(EFI_FILE_INFO), &size, fi); |
| if (err == EFI_BUFFER_TOO_SMALL) { |
| free(fi); |
| fi = xmalloc(size); /* GetInfo tells us the required size, let's use that now */ |
| err = handle->GetInfo(handle, MAKE_GUID_PTR(EFI_FILE_INFO), &size, fi); |
| } |
| |
| if (err != EFI_SUCCESS) |
| return err; |
| |
| *ret = TAKE_PTR(fi); |
| |
| if (ret_size) |
| *ret_size = size; |
| |
| return EFI_SUCCESS; |
| } |
| |
| EFI_STATUS readdir_harder( |
| EFI_FILE *handle, |
| EFI_FILE_INFO **buffer, |
| size_t *buffer_size) { |
| |
| EFI_STATUS err; |
| size_t sz; |
| |
| assert(handle); |
| assert(buffer); |
| assert(buffer_size); |
| |
| /* buffer/buffer_size are both in and output parameters. Should be zero-initialized initially, and |
| * the specified buffer needs to be freed by caller, after final use. */ |
| |
| if (!*buffer) { |
| /* Some broken firmware violates the EFI spec by still advancing the readdir |
| * position when returning EFI_BUFFER_TOO_SMALL, effectively skipping over any files when |
| * the buffer was too small. Therefore, start with a buffer that should handle FAT32 max |
| * file name length. |
| * As a side effect, most readdir_harder() calls will now be slightly faster. */ |
| sz = sizeof(EFI_FILE_INFO) + 256 * sizeof(char16_t); |
| *buffer = xmalloc(sz); |
| *buffer_size = sz; |
| } else |
| sz = *buffer_size; |
| |
| err = handle->Read(handle, &sz, *buffer); |
| if (err == EFI_BUFFER_TOO_SMALL) { |
| free(*buffer); |
| *buffer = xmalloc(sz); |
| *buffer_size = sz; |
| err = handle->Read(handle, &sz, *buffer); |
| } |
| if (err != EFI_SUCCESS) |
| return err; |
| |
| if (sz == 0) { |
| /* End of directory */ |
| free(*buffer); |
| *buffer = NULL; |
| *buffer_size = 0; |
| } |
| |
| return EFI_SUCCESS; |
| } |
| |
| bool is_ascii(const char16_t *f) { |
| if (!f) |
| return false; |
| |
| for (; *f != 0; f++) |
| if (*f > 127) |
| return false; |
| |
| return true; |
| } |
| |
| char16_t **strv_free(char16_t **v) { |
| if (!v) |
| return NULL; |
| |
| for (char16_t **i = v; *i; i++) |
| free(*i); |
| |
| free(v); |
| return NULL; |
| } |
| |
| EFI_STATUS open_directory( |
| EFI_FILE *root, |
| const char16_t *path, |
| EFI_FILE **ret) { |
| |
| _cleanup_(file_closep) EFI_FILE *dir = NULL; |
| _cleanup_free_ EFI_FILE_INFO *file_info = NULL; |
| EFI_STATUS err; |
| |
| assert(root); |
| |
| /* Opens a file, and then verifies it is actually a directory */ |
| |
| err = root->Open(root, &dir, (char16_t *) path, EFI_FILE_MODE_READ, 0); |
| if (err != EFI_SUCCESS) |
| return err; |
| |
| err = get_file_info_harder(dir, &file_info, NULL); |
| if (err != EFI_SUCCESS) |
| return err; |
| if (!FLAGS_SET(file_info->Attribute, EFI_FILE_DIRECTORY)) |
| return EFI_LOAD_ERROR; |
| |
| *ret = TAKE_PTR(dir); |
| return EFI_SUCCESS; |
| } |
| |
| uint64_t get_os_indications_supported(void) { |
| uint64_t osind; |
| EFI_STATUS err; |
| |
| /* Returns the supported OS indications. If we can't acquire it, returns a zeroed out mask, i.e. no |
| * supported features. */ |
| |
| err = efivar_get_uint64_le(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE), u"OsIndicationsSupported", &osind); |
| if (err != EFI_SUCCESS) |
| return 0; |
| |
| return osind; |
| } |
| |
| #ifdef EFI_DEBUG |
| extern uint8_t _text, _data; |
| __attribute__((noinline)) void notify_debugger(const char *identity, volatile bool wait) { |
| printf("%s@%p,%p\n", identity, &_text, &_data); |
| if (wait) |
| printf("Waiting for debugger to attach...\n"); |
| |
| /* This is a poor programmer's breakpoint to wait until a debugger |
| * has attached to us. Just "set variable wait = 0" or "return" to continue. */ |
| while (wait) |
| /* Prefer asm based stalling so that gdb has a source location to present. */ |
| #if defined(__i386__) || defined(__x86_64__) |
| asm volatile("pause"); |
| #elif defined(__aarch64__) |
| asm volatile("wfi"); |
| #else |
| BS->Stall(5000); |
| #endif |
| } |
| #endif |
| |
| #ifdef EFI_DEBUG |
| void hexdump(const char16_t *prefix, const void *data, size_t size) { |
| static const char hex[16] = "0123456789abcdef"; |
| _cleanup_free_ char16_t *buf = NULL; |
| const uint8_t *d = data; |
| |
| assert(prefix); |
| assert(data || size == 0); |
| |
| /* Debugging helper — please keep this around, even if not used */ |
| |
| buf = xnew(char16_t, size*2+1); |
| |
| for (size_t i = 0; i < size; i++) { |
| buf[i*2] = hex[d[i] >> 4]; |
| buf[i*2+1] = hex[d[i] & 0x0F]; |
| } |
| |
| buf[size*2] = 0; |
| |
| log_error("%ls[%zu]: %ls", prefix, size, buf); |
| } |
| #endif |
| |
| #if defined(__i386__) || defined(__x86_64__) |
| static inline uint8_t inb(uint16_t port) { |
| uint8_t value; |
| asm volatile("inb %1, %0" : "=a"(value) : "Nd"(port)); |
| return value; |
| } |
| |
| static inline void outb(uint16_t port, uint8_t value) { |
| asm volatile("outb %0, %1" : : "a"(value), "Nd"(port)); |
| } |
| |
| void beep(unsigned beep_count) { |
| enum { |
| PITCH = 500, |
| BEEP_DURATION_USEC = 100 * 1000, |
| WAIT_DURATION_USEC = 400 * 1000, |
| |
| PIT_FREQUENCY = 0x1234dd, |
| SPEAKER_CONTROL_PORT = 0x61, |
| SPEAKER_ON_MASK = 0x03, |
| TIMER_PORT_MAGIC = 0xB6, |
| TIMER_CONTROL_PORT = 0x43, |
| TIMER_CONTROL2_PORT = 0x42, |
| }; |
| |
| /* Set frequency. */ |
| uint32_t counter = PIT_FREQUENCY / PITCH; |
| outb(TIMER_CONTROL_PORT, TIMER_PORT_MAGIC); |
| outb(TIMER_CONTROL2_PORT, counter & 0xFF); |
| outb(TIMER_CONTROL2_PORT, (counter >> 8) & 0xFF); |
| |
| uint8_t value = inb(SPEAKER_CONTROL_PORT); |
| |
| while (beep_count > 0) { |
| /* Turn speaker on. */ |
| value |= SPEAKER_ON_MASK; |
| outb(SPEAKER_CONTROL_PORT, value); |
| |
| BS->Stall(BEEP_DURATION_USEC); |
| |
| /* Turn speaker off. */ |
| value &= ~SPEAKER_ON_MASK; |
| outb(SPEAKER_CONTROL_PORT, value); |
| |
| beep_count--; |
| if (beep_count > 0) |
| BS->Stall(WAIT_DURATION_USEC); |
| } |
| } |
| #endif |
| |
| EFI_STATUS open_volume(EFI_HANDLE device, EFI_FILE **ret_file) { |
| EFI_STATUS err; |
| EFI_FILE *file; |
| EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *volume; |
| |
| assert(ret_file); |
| |
| err = BS->HandleProtocol(device, MAKE_GUID_PTR(EFI_SIMPLE_FILE_SYSTEM_PROTOCOL), (void **) &volume); |
| if (err != EFI_SUCCESS) |
| return err; |
| |
| err = volume->OpenVolume(volume, &file); |
| if (err != EFI_SUCCESS) |
| return err; |
| |
| *ret_file = file; |
| return EFI_SUCCESS; |
| } |
| |
| EFI_STATUS make_file_device_path(EFI_HANDLE device, const char16_t *file, EFI_DEVICE_PATH **ret_dp) { |
| EFI_STATUS err; |
| EFI_DEVICE_PATH *dp; |
| |
| assert(file); |
| assert(ret_dp); |
| |
| err = BS->HandleProtocol(device, MAKE_GUID_PTR(EFI_DEVICE_PATH_PROTOCOL), (void **) &dp); |
| if (err != EFI_SUCCESS) |
| return err; |
| |
| EFI_DEVICE_PATH *end_node = dp; |
| while (!IsDevicePathEnd(end_node)) |
| end_node = NextDevicePathNode(end_node); |
| |
| size_t file_size = strsize16(file); |
| size_t dp_size = (uint8_t *) end_node - (uint8_t *) dp; |
| |
| /* Make a copy that can also hold a file media device path. */ |
| *ret_dp = xmalloc(dp_size + file_size + SIZE_OF_FILEPATH_DEVICE_PATH + END_DEVICE_PATH_LENGTH); |
| dp = mempcpy(*ret_dp, dp, dp_size); |
| |
| /* Replace end node with file media device path. Use memcpy() in case dp is unaligned (if accessed as |
| * FILEPATH_DEVICE_PATH). */ |
| dp->Type = MEDIA_DEVICE_PATH; |
| dp->SubType = MEDIA_FILEPATH_DP; |
| memcpy((uint8_t *) dp + offsetof(FILEPATH_DEVICE_PATH, PathName), file, file_size); |
| SetDevicePathNodeLength(dp, offsetof(FILEPATH_DEVICE_PATH, PathName) + file_size); |
| |
| dp = NextDevicePathNode(dp); |
| SetDevicePathEndNode(dp); |
| return EFI_SUCCESS; |
| } |
| |
| EFI_STATUS device_path_to_str(const EFI_DEVICE_PATH *dp, char16_t **ret) { |
| EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *dp_to_text; |
| EFI_STATUS err; |
| _cleanup_free_ char16_t *str = NULL; |
| |
| assert(dp); |
| assert(ret); |
| |
| err = BS->LocateProtocol(MAKE_GUID_PTR(EFI_DEVICE_PATH_TO_TEXT_PROTOCOL), NULL, (void **) &dp_to_text); |
| if (err != EFI_SUCCESS) { |
| /* If the device path to text protocol is not available we can still do a best-effort attempt |
| * to convert it ourselves if we are given filepath-only device path. */ |
| |
| size_t size = 0; |
| for (const EFI_DEVICE_PATH *node = dp; !IsDevicePathEnd(node); |
| node = NextDevicePathNode(node)) { |
| |
| if (DevicePathType(node) != MEDIA_DEVICE_PATH || |
| DevicePathSubType(node) != MEDIA_FILEPATH_DP) |
| return err; |
| |
| size_t path_size = DevicePathNodeLength(node); |
| if (path_size <= offsetof(FILEPATH_DEVICE_PATH, PathName) || path_size % sizeof(char16_t)) |
| return EFI_INVALID_PARAMETER; |
| path_size -= offsetof(FILEPATH_DEVICE_PATH, PathName); |
| |
| _cleanup_free_ char16_t *old = str; |
| str = xmalloc(size + path_size); |
| if (old) { |
| memcpy(str, old, size); |
| str[size / sizeof(char16_t) - 1] = '\\'; |
| } |
| |
| memcpy(str + (size / sizeof(char16_t)), |
| ((uint8_t *) node) + offsetof(FILEPATH_DEVICE_PATH, PathName), |
| path_size); |
| size += path_size; |
| } |
| |
| *ret = TAKE_PTR(str); |
| return EFI_SUCCESS; |
| } |
| |
| str = dp_to_text->ConvertDevicePathToText(dp, false, false); |
| if (!str) |
| return EFI_OUT_OF_RESOURCES; |
| |
| *ret = TAKE_PTR(str); |
| return EFI_SUCCESS; |
| } |
| |
| void *find_configuration_table(const EFI_GUID *guid) { |
| for (size_t i = 0; i < ST->NumberOfTableEntries; i++) |
| if (efi_guid_equal(&ST->ConfigurationTable[i].VendorGuid, guid)) |
| return ST->ConfigurationTable[i].VendorTable; |
| |
| return NULL; |
| } |
| |
| /* libgcc's __aeabi_ldiv0 intrinsic will call raise() on division by zero, so we |
| * need to provide one ourselves for now. */ |
| _used_ _noreturn_ int raise(int sig) { |
| assert_not_reached(); |
| } |