blob: d42c23730d3d1958b63060dc3782df27f066afe4 [file] [log] [blame]
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <efi.h>
#include <efilib.h>
#include <stddef.h>
#include "log.h"
#include "string-util-fundamental.h"
static inline void free(void *p) {
if (!p)
return;
/* Debugging an invalid free requires trace logging to find the call site or a debugger attached. For
* release builds it is not worth the bother to even warn when we cannot even print a call stack. */
#ifdef EFI_DEBUG
assert_se(BS->FreePool(p) == EFI_SUCCESS);
#else
(void) BS->FreePool(p);
#endif
}
static inline void freep(void *p) {
free(*(void **) p);
}
#define _cleanup_free_ _cleanup_(freep)
_malloc_ _alloc_(1) _returns_nonnull_ _warn_unused_result_
static inline void *xmalloc(size_t size) {
void *p;
assert_se(BS->AllocatePool(EfiBootServicesData, size, &p) == EFI_SUCCESS);
return p;
}
_malloc_ _alloc_(1, 2) _returns_nonnull_ _warn_unused_result_
static inline void *xmalloc_multiply(size_t size, size_t n) {
assert_se(!__builtin_mul_overflow(size, n, &size));
return xmalloc(size);
}
/* Use malloc attribute as this never returns p like userspace realloc. */
_malloc_ _alloc_(3) _returns_nonnull_ _warn_unused_result_
static inline void *xrealloc(void *p, size_t old_size, size_t new_size) {
void *t = xmalloc(new_size);
new_size = MIN(old_size, new_size);
if (new_size > 0)
memcpy(t, p, new_size);
free(p);
return t;
}
#define xnew(type, n) ((type *) xmalloc_multiply(sizeof(type), (n)))
typedef struct {
EFI_PHYSICAL_ADDRESS addr;
size_t n_pages;
} Pages;
static inline void cleanup_pages(Pages *p) {
if (p->n_pages == 0)
return;
#ifdef EFI_DEBUG
assert_se(BS->FreePages(p->addr, p->n_pages) == EFI_SUCCESS);
#else
(void) BS->FreePages(p->addr, p->n_pages);
#endif
}
#define _cleanup_pages_ _cleanup_(cleanup_pages)
static inline Pages xmalloc_pages(
EFI_ALLOCATE_TYPE type, EFI_MEMORY_TYPE memory_type, size_t n_pages, EFI_PHYSICAL_ADDRESS addr) {
assert_se(BS->AllocatePages(type, memory_type, n_pages, &addr) == EFI_SUCCESS);
return (Pages) {
.addr = addr,
.n_pages = n_pages,
};
}
EFI_STATUS parse_boolean(const char *v, bool *b);
EFI_STATUS efivar_set(const EFI_GUID *vendor, const char16_t *name, const char16_t *value, uint32_t flags);
EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, const char16_t *name, const void *buf, size_t size, uint32_t flags);
EFI_STATUS efivar_set_uint_string(const EFI_GUID *vendor, const char16_t *name, size_t i, uint32_t flags);
EFI_STATUS efivar_set_uint32_le(const EFI_GUID *vendor, const char16_t *NAME, uint32_t value, uint32_t flags);
EFI_STATUS efivar_set_uint64_le(const EFI_GUID *vendor, const char16_t *name, uint64_t value, uint32_t flags);
void efivar_set_time_usec(const EFI_GUID *vendor, const char16_t *name, uint64_t usec);
EFI_STATUS efivar_get(const EFI_GUID *vendor, const char16_t *name, char16_t **ret);
EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const char16_t *name, char **ret, size_t *ret_size);
EFI_STATUS efivar_get_uint_string(const EFI_GUID *vendor, const char16_t *name, size_t *ret);
EFI_STATUS efivar_get_uint32_le(const EFI_GUID *vendor, const char16_t *name, uint32_t *ret);
EFI_STATUS efivar_get_uint64_le(const EFI_GUID *vendor, const char16_t *name, uint64_t *ret);
EFI_STATUS efivar_get_boolean_u8(const EFI_GUID *vendor, const char16_t *name, bool *ret);
void convert_efi_path(char16_t *path);
char16_t *xstr8_to_path(const char *stra);
void mangle_stub_cmdline(char16_t *cmdline);
EFI_STATUS file_read(EFI_FILE *dir, const char16_t *name, size_t off, size_t size, char **content, size_t *content_size);
static inline void file_closep(EFI_FILE **handle) {
if (!*handle)
return;
(*handle)->Close(*handle);
}
static inline void unload_imagep(EFI_HANDLE *image) {
if (*image)
(void) BS->UnloadImage(*image);
}
/* Creates a EFI_GUID pointer suitable for EFI APIs. Use of const allows the compiler to merge multiple
* uses (although, currently compilers do that regardless). Most EFI APIs declare their EFI_GUID input
* as non-const, but almost all of them are in fact const. */
#define MAKE_GUID_PTR(name) ((EFI_GUID *) &(const EFI_GUID) name##_GUID)
/* These allow MAKE_GUID_PTR() to work without requiring an extra _GUID in the passed name. We want to
* keep the GUID definitions in line with the UEFI spec. */
#define EFI_GLOBAL_VARIABLE_GUID EFI_GLOBAL_VARIABLE
#define EFI_FILE_INFO_GUID EFI_FILE_INFO_ID
/*
* Allocated random UUID, intended to be shared across tools that implement
* the (ESP)\loader\entries\<vendor>-<revision>.conf convention and the
* associated EFI variables.
*/
#define LOADER_GUID \
{ 0x4a67b082, 0x0a4c, 0x41cf, { 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f } }
void print_at(size_t x, size_t y, size_t attr, const char16_t *str);
void clear_screen(size_t attr);
typedef int (*compare_pointer_func_t)(const void *a, const void *b);
void sort_pointer_array(void **array, size_t n_members, compare_pointer_func_t compare);
EFI_STATUS get_file_info_harder(EFI_FILE *handle, EFI_FILE_INFO **ret, size_t *ret_size);
EFI_STATUS readdir_harder(EFI_FILE *handle, EFI_FILE_INFO **buffer, size_t *buffer_size);
bool is_ascii(const char16_t *f);
char16_t **strv_free(char16_t **l);
static inline void strv_freep(char16_t ***p) {
strv_free(*p);
}
EFI_STATUS open_directory(EFI_FILE *root_dir, const char16_t *path, EFI_FILE **ret);
/* Conversion between EFI_PHYSICAL_ADDRESS and pointers is not obvious. The former is always 64bit, even on
* 32bit archs. And gcc complains if we cast a pointer to an integer of a different size. Hence let's do the
* conversion indirectly: first into uintptr_t and then extended to EFI_PHYSICAL_ADDRESS. */
static inline EFI_PHYSICAL_ADDRESS POINTER_TO_PHYSICAL_ADDRESS(const void *p) {
return (EFI_PHYSICAL_ADDRESS) (uintptr_t) p;
}
static inline void *PHYSICAL_ADDRESS_TO_POINTER(EFI_PHYSICAL_ADDRESS addr) {
/* On 32bit systems the address might not be convertible (as pointers are 32bit but
* EFI_PHYSICAL_ADDRESS 64bit) */
assert(addr <= UINTPTR_MAX);
return (void *) (uintptr_t) addr;
}
uint64_t get_os_indications_supported(void);
#ifdef EFI_DEBUG
/* Report the relocated position of text and data sections so that a debugger
* can attach to us. See debug-sd-boot.sh for how this can be done. */
void notify_debugger(const char *identity, bool wait);
void hexdump(const char16_t *prefix, const void *data, size_t size);
#else
# define notify_debugger(i, w)
#endif
#define DEFINE_EFI_MAIN_FUNCTION(func, identity, wait_for_debugger) \
EFI_SYSTEM_TABLE *ST; \
EFI_BOOT_SERVICES *BS; \
EFI_RUNTIME_SERVICES *RT; \
EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *system_table) { \
ST = system_table; \
BS = system_table->BootServices; \
RT = system_table->RuntimeServices; \
notify_debugger((identity), (wait_for_debugger)); \
EFI_STATUS err = func(image); \
log_wait(); \
return err; \
}
#if defined(__i386__) || defined(__x86_64__)
void beep(unsigned beep_count);
#else
static inline void beep(unsigned beep_count) {}
#endif
EFI_STATUS open_volume(EFI_HANDLE device, EFI_FILE **ret_file);
EFI_STATUS make_file_device_path(EFI_HANDLE device, const char16_t *file, EFI_DEVICE_PATH **ret_dp);
EFI_STATUS device_path_to_str(const EFI_DEVICE_PATH *dp, char16_t **ret);
static inline bool efi_guid_equal(const EFI_GUID *a, const EFI_GUID *b) {
return memcmp(a, b, sizeof(EFI_GUID)) == 0;
}
void *find_configuration_table(const EFI_GUID *guid);