summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/boot/efi/boot.c2
-rw-r--r--src/boot/efi/linux.c40
-rw-r--r--src/boot/efi/linux.h5
-rw-r--r--src/boot/efi/linux_x86.c209
-rw-r--r--src/boot/efi/meson.build3
-rw-r--r--src/boot/efi/pe.c6
-rw-r--r--src/boot/efi/pe.h2
-rw-r--r--src/boot/efi/stub.c26
-rw-r--r--src/boot/efi/util.h26
9 files changed, 179 insertions, 140 deletions
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index a7ef57cb99..fdbc55b884 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -2415,7 +2415,7 @@ static EFI_STATUS image_start(
if (err == EFI_UNSUPPORTED && entry->type == LOADER_LINUX) {
uint32_t kernel_entry_address;
- err = pe_alignment_info(loaded_image->ImageBase, &kernel_entry_address, NULL, NULL);
+ err = pe_kernel_info(loaded_image->ImageBase, &kernel_entry_address, NULL, NULL);
if (err != EFI_SUCCESS) {
if (err != EFI_UNSUPPORTED)
return log_error_status_stall(err, L"Error finding kernel compat entry address: %r", err);
diff --git a/src/boot/efi/linux.c b/src/boot/efi/linux.c
index 3cbffdbbeb..7771058c3a 100644
--- a/src/boot/efi/linux.c
+++ b/src/boot/efi/linux.c
@@ -92,18 +92,6 @@ static inline void cleanup_loaded_image(EFI_HANDLE *loaded_image_handle) {
*loaded_image_handle = NULL;
}
-/* struct to call cleanup_pages */
-struct pages {
- EFI_PHYSICAL_ADDRESS addr;
- UINTN num;
-};
-
-static inline void cleanup_pages(struct pages *p) {
- if (p->addr == 0)
- return;
- (void) BS->FreePages(p->addr, p->num);
-}
-
EFI_STATUS linux_exec(
EFI_HANDLE image,
const char *cmdline, UINTN cmdline_len,
@@ -114,7 +102,6 @@ EFI_STATUS linux_exec(
_cleanup_(cleanup_loaded_image) EFI_HANDLE loaded_image_handle = NULL;
uint32_t kernel_alignment, kernel_size_of_image, kernel_entry_address;
EFI_IMAGE_ENTRY_POINT kernel_entry;
- _cleanup_(cleanup_pages) struct pages kernel = {};
void *new_buffer;
EFI_STATUS err;
@@ -124,7 +111,20 @@ EFI_STATUS linux_exec(
assert(initrd_buffer || initrd_length == 0);
/* get the necessary fields from the PE header */
- err = pe_alignment_info(linux_buffer, &kernel_entry_address, &kernel_size_of_image, &kernel_alignment);
+ err = pe_kernel_info(linux_buffer, &kernel_entry_address, &kernel_size_of_image, &kernel_alignment);
+#if defined(__i386__) || defined(__x86_64__)
+ if (err == EFI_UNSUPPORTED)
+ /* Kernel is too old to support LINUX_INITRD_MEDIA_GUID, try the deprecated EFI handover
+ * protocol. */
+ return linux_exec_efi_handover(
+ image,
+ cmdline,
+ cmdline_len,
+ linux_buffer,
+ linux_length,
+ initrd_buffer,
+ initrd_length);
+#endif
if (err != EFI_SUCCESS)
return err;
/* sanity check */
@@ -140,11 +140,15 @@ EFI_STATUS linux_exec(
if they are not met. x86 and x86_64 kernel stubs don't do checks and fail if the BSS section is too small.
*/
/* allocate SizeOfImage + SectionAlignment because the new_buffer can move up to Alignment-1 bytes */
- kernel.num = EFI_SIZE_TO_PAGES(ALIGN_TO(kernel_size_of_image, kernel_alignment) + kernel_alignment);
- err = BS->AllocatePages(AllocateAnyPages, EfiLoaderCode, kernel.num, &kernel.addr);
- if (err != EFI_SUCCESS)
- return EFI_OUT_OF_RESOURCES;
+ _cleanup_pages_ Pages kernel = xmalloc_pages(
+ AllocateAnyPages,
+ EfiLoaderCode,
+ EFI_SIZE_TO_PAGES(ALIGN_TO(kernel_size_of_image, kernel_alignment) + kernel_alignment),
+ 0);
new_buffer = PHYSICAL_ADDRESS_TO_POINTER(ALIGN_TO(kernel.addr, kernel_alignment));
+ if (!new_buffer) /* Silence gcc 11.2.0, assert(new_buffer) doesn't work. */
+ return EFI_OUT_OF_RESOURCES;
+
memcpy(new_buffer, linux_buffer, linux_length);
/* zero out rest of memory (probably not needed, but BSS section should be 0) */
memset((uint8_t *)new_buffer + linux_length, 0, kernel_size_of_image - linux_length);
diff --git a/src/boot/efi/linux.h b/src/boot/efi/linux.h
index 0dfe744668..eab617e579 100644
--- a/src/boot/efi/linux.h
+++ b/src/boot/efi/linux.h
@@ -8,3 +8,8 @@ EFI_STATUS linux_exec(
const char *cmdline, UINTN cmdline_len,
const void *linux_buffer, UINTN linux_length,
const void *initrd_buffer, UINTN initrd_length);
+EFI_STATUS linux_exec_efi_handover(
+ EFI_HANDLE image,
+ const char *cmdline, UINTN cmdline_len,
+ const void *linux_buffer, UINTN linux_length,
+ const void *initrd_buffer, UINTN initrd_length);
diff --git a/src/boot/efi/linux_x86.c b/src/boot/efi/linux_x86.c
index d506070926..83bd7b2c31 100644
--- a/src/boot/efi/linux_x86.c
+++ b/src/boot/efi/linux_x86.c
@@ -18,9 +18,27 @@
#include "macro-fundamental.h"
#include "util.h"
-#define SETUP_MAGIC 0x53726448 /* "HdrS" */
+#define KERNEL_SECTOR_SIZE 512u
+#define BOOT_FLAG_MAGIC 0xAA55u
+#define SETUP_MAGIC 0x53726448u /* "HdrS" */
+#define SETUP_VERSION_2_11 0x20bu
+#define SETUP_VERSION_2_12 0x20cu
+#define SETUP_VERSION_2_15 0x20fu
+#define CMDLINE_PTR_MAX 0xA0000u
+
+enum {
+ XLF_KERNEL_64 = 1 << 0,
+ XLF_CAN_BE_LOADED_ABOVE_4G = 1 << 1,
+ XLF_EFI_HANDOVER_32 = 1 << 2,
+ XLF_EFI_HANDOVER_64 = 1 << 3,
+#ifdef __x86_64__
+ XLF_EFI_HANDOVER = XLF_EFI_HANDOVER_64,
+#else
+ XLF_EFI_HANDOVER = XLF_EFI_HANDOVER_32,
+#endif
+};
-struct setup_header {
+typedef struct {
uint8_t setup_sects;
uint16_t root_flags;
uint32_t syssize;
@@ -28,7 +46,8 @@ struct setup_header {
uint16_t vid_mode;
uint16_t root_dev;
uint16_t boot_flag;
- uint16_t jump;
+ uint8_t jump; /* We split the 2-byte jump field from the spec in two for convenience. */
+ uint8_t setup_size;
uint32_t header;
uint16_t version;
uint32_t realmode_swtch;
@@ -59,143 +78,127 @@ struct setup_header {
uint64_t pref_address;
uint32_t init_size;
uint32_t handover_offset;
-} _packed_;
-
-/* adapted from linux' bootparam.h */
-struct boot_params {
- uint8_t screen_info[64]; // was: struct screen_info
- uint8_t apm_bios_info[20]; // was: struct apm_bios_info
- uint8_t _pad2[4];
- uint64_t tboot_addr;
- uint8_t ist_info[16]; // was: struct ist_info
- uint8_t _pad3[16];
- uint8_t hd0_info[16];
- uint8_t hd1_info[16];
- uint8_t sys_desc_table[16]; // was: struct sys_desc_table
- uint8_t olpc_ofw_header[16]; // was: struct olpc_ofw_header
+} _packed_ SetupHeader;
+
+/* We really only care about a few fields, but we still have to provide a full page otherwise. */
+typedef struct {
+ uint8_t pad[192];
uint32_t ext_ramdisk_image;
uint32_t ext_ramdisk_size;
uint32_t ext_cmd_line_ptr;
- uint8_t _pad4[116];
- uint8_t edid_info[128]; // was: struct edid_info
- uint8_t efi_info[32]; // was: struct efi_info
- uint32_t alt_mem_k;
- uint32_t scratch;
- uint8_t e820_entries;
- uint8_t eddbuf_entries;
- uint8_t edd_mbr_sig_buf_entries;
- uint8_t kbd_status;
- uint8_t secure_boot;
- uint8_t _pad5[2];
- uint8_t sentinel;
- uint8_t _pad6[1];
- struct setup_header hdr;
- uint8_t _pad7[0x290-0x1f1-sizeof(struct setup_header)];
- uint32_t edd_mbr_sig_buffer[16]; // was: edd_mbr_sig_buffer[EDD_MBR_SIG_MAX]
- uint8_t e820_table[20*128]; // was: struct boot_e820_entry e820_table[E820_MAX_ENTRIES_ZEROPAGE]
- uint8_t _pad8[48];
- uint8_t eddbuf[6*82]; // was: struct edd_info eddbuf[EDDMAXNR]
- uint8_t _pad9[276];
-} _packed_;
+ uint8_t pad2[293];
+ SetupHeader hdr;
+ uint8_t pad3[3480];
+} _packed_ BootParams;
+assert_cc(offsetof(BootParams, ext_ramdisk_image) == 0x0C0);
+assert_cc(sizeof(BootParams) == 4096);
#ifdef __i386__
-#define __regparm0__ __attribute__((regparm(0)))
+# define __regparm0__ __attribute__((regparm(0)))
#else
-#define __regparm0__
+# define __regparm0__
#endif
-typedef void(*handover_f)(void *image, EFI_SYSTEM_TABLE *table, struct boot_params *params) __regparm0__;
-
-static void linux_efi_handover(EFI_HANDLE image, struct boot_params *params) {
- handover_f handover;
- UINTN start = (UINTN)params->hdr.code32_start;
+typedef void (*handover_f)(void *image, EFI_SYSTEM_TABLE *table, BootParams *params) __regparm0__
+ __attribute__((sysv_abi));
+static void linux_efi_handover(EFI_HANDLE image, uintptr_t kernel, BootParams *params) {
assert(params);
+ kernel += (params->hdr.setup_sects + 1) * KERNEL_SECTOR_SIZE; /* 32bit entry address. */
+
#ifdef __x86_64__
- asm volatile ("cli");
- start += 512;
+ kernel += KERNEL_SECTOR_SIZE; /* 64bit entry address. */
#endif
- handover = (handover_f)(start + params->hdr.handover_offset);
+
+ kernel += params->hdr.handover_offset; /* 32/64bit EFI handover address. */
+
+ /* Note in EFI mixed mode this now points to the correct 32bit handover entry point, allowing a 64bit
+ * kernel to be booted from a 32bit sd-stub. */
+
+ handover_f handover = (handover_f) kernel;
handover(image, ST, params);
}
-EFI_STATUS linux_exec(
+EFI_STATUS linux_exec_efi_handover(
EFI_HANDLE image,
const char *cmdline, UINTN cmdline_len,
const void *linux_buffer, UINTN linux_length,
const void *initrd_buffer, UINTN initrd_length) {
- const struct boot_params *image_params;
- struct boot_params *boot_params;
- EFI_HANDLE initrd_handle = NULL;
- EFI_PHYSICAL_ADDRESS addr;
- uint8_t setup_sectors;
- EFI_STATUS err;
-
assert(image);
assert(cmdline || cmdline_len == 0);
assert(linux_buffer);
assert(initrd_buffer || initrd_length == 0);
- if (linux_length < sizeof(struct boot_params))
+ if (linux_length < sizeof(BootParams))
return EFI_LOAD_ERROR;
- image_params = (const struct boot_params *) linux_buffer;
+ const BootParams *image_params = (const BootParams *) linux_buffer;
+ if (image_params->hdr.header != SETUP_MAGIC || image_params->hdr.boot_flag != BOOT_FLAG_MAGIC)
+ return log_error_status_stall(EFI_UNSUPPORTED, u"Unsupported kernel image.");
+ if (image_params->hdr.version < SETUP_VERSION_2_11)
+ return log_error_status_stall(EFI_UNSUPPORTED, u"Kernel too old.");
+ if (!image_params->hdr.relocatable_kernel)
+ return log_error_status_stall(EFI_UNSUPPORTED, u"Kernel is not relocatable.");
+
+ /* The xloadflags were added in version 2.12+ of the boot protocol but the handover support predates
+ * that, so we cannot safety-check this for 2.11. */
+ if (image_params->hdr.version >= SETUP_VERSION_2_12 &&
+ !FLAGS_SET(image_params->hdr.xloadflags, XLF_EFI_HANDOVER))
+ return log_error_status_stall(EFI_UNSUPPORTED, u"Kernel does not support EFI handover protocol.");
+
+ bool can_4g = image_params->hdr.version >= SETUP_VERSION_2_12 &&
+ FLAGS_SET(image_params->hdr.xloadflags, XLF_CAN_BE_LOADED_ABOVE_4G);
+
+ if (!can_4g && POINTER_TO_PHYSICAL_ADDRESS(linux_buffer) + linux_length > UINT32_MAX)
+ return log_error_status_stall(
+ EFI_UNSUPPORTED,
+ u"Unified kernel image was loaded above 4G, but kernel lacks support.");
+ if (!can_4g && POINTER_TO_PHYSICAL_ADDRESS(initrd_buffer) + initrd_length > UINT32_MAX)
+ return log_error_status_stall(
+ EFI_UNSUPPORTED, u"Initrd is above 4G, but kernel lacks support.");
+
+ _cleanup_pages_ Pages boot_params_page = xmalloc_pages(
+ can_4g ? AllocateAnyPages : AllocateMaxAddress,
+ EfiLoaderData,
+ EFI_SIZE_TO_PAGES(sizeof(BootParams)),
+ UINT32_MAX /* Below the 4G boundary */);
+ BootParams *boot_params = PHYSICAL_ADDRESS_TO_POINTER(boot_params_page.addr);
+ *boot_params = (BootParams){};
- if (image_params->hdr.boot_flag != 0xAA55 ||
- image_params->hdr.header != SETUP_MAGIC ||
- image_params->hdr.version < 0x20b ||
- !image_params->hdr.relocatable_kernel)
- return EFI_LOAD_ERROR;
+ /* Setup size is determined by offset 0x0202 + byte value at offset 0x0201, which is the same as
+ * offset of the header field and the target from the jump field (which we split for this reason). */
+ memcpy(&boot_params->hdr,
+ &image_params->hdr,
+ offsetof(SetupHeader, header) + image_params->hdr.setup_size);
- addr = UINT32_MAX; /* Below the 32bit boundary */
- err = BS->AllocatePages(
- AllocateMaxAddress,
- EfiLoaderData,
- EFI_SIZE_TO_PAGES(0x4000),
- &addr);
- if (err != EFI_SUCCESS)
- return err;
-
- boot_params = (struct boot_params *) PHYSICAL_ADDRESS_TO_POINTER(addr);
- memset(boot_params, 0, 0x4000);
- boot_params->hdr = image_params->hdr;
boot_params->hdr.type_of_loader = 0xff;
- setup_sectors = image_params->hdr.setup_sects > 0 ? image_params->hdr.setup_sects : 4;
- boot_params->hdr.code32_start = (uint32_t) POINTER_TO_PHYSICAL_ADDRESS(linux_buffer) + (setup_sectors + 1) * 512;
- if (cmdline) {
- addr = 0xA0000;
+ /* Spec says: For backwards compatibility, if the setup_sects field contains 0, the real value is 4. */
+ if (boot_params->hdr.setup_sects == 0)
+ boot_params->hdr.setup_sects = 4;
- err = BS->AllocatePages(
- AllocateMaxAddress,
+ _cleanup_pages_ Pages cmdline_pages = {};
+ if (cmdline) {
+ cmdline_pages = xmalloc_pages(
+ can_4g ? AllocateAnyPages : AllocateMaxAddress,
EfiLoaderData,
EFI_SIZE_TO_PAGES(cmdline_len + 1),
- &addr);
- if (err != EFI_SUCCESS)
- return err;
+ CMDLINE_PTR_MAX);
- memcpy(PHYSICAL_ADDRESS_TO_POINTER(addr), cmdline, cmdline_len);
- ((char *) PHYSICAL_ADDRESS_TO_POINTER(addr))[cmdline_len] = 0;
- boot_params->hdr.cmd_line_ptr = (uint32_t) addr;
+ memcpy(PHYSICAL_ADDRESS_TO_POINTER(cmdline_pages.addr), cmdline, cmdline_len);
+ ((char *) PHYSICAL_ADDRESS_TO_POINTER(cmdline_pages.addr))[cmdline_len] = 0;
+ boot_params->hdr.cmd_line_ptr = (uint32_t) cmdline_pages.addr;
+ boot_params->ext_cmd_line_ptr = cmdline_pages.addr >> 32;
+ assert(can_4g || cmdline_pages.addr <= CMDLINE_PTR_MAX);
}
- /* Providing the initrd via LINUX_INITRD_MEDIA_GUID is only supported by Linux 5.8+ (5.7+ on ARM64).
- Until supported kernels become more established, we continue to set ramdisk in the handover struct.
- This value is overridden by kernels that support LINUX_INITRD_MEDIA_GUID.
- If you need to know which protocol was used by the kernel, pass "efi=debug" to the kernel,
- this will print a line when InitrdMediaGuid was successfully used to load the initrd.
- */
- boot_params->hdr.ramdisk_image = (uint32_t) POINTER_TO_PHYSICAL_ADDRESS(initrd_buffer);
- boot_params->hdr.ramdisk_size = (uint32_t) initrd_length;
-
- /* register LINUX_INITRD_MEDIA_GUID */
- err = initrd_register(initrd_buffer, initrd_length, &initrd_handle);
- if (err != EFI_SUCCESS)
- return err;
- linux_efi_handover(image, boot_params);
- (void) initrd_unregister(initrd_handle);
- initrd_handle = NULL;
+ boot_params->hdr.ramdisk_image = (uintptr_t) initrd_buffer;
+ boot_params->ext_ramdisk_image = POINTER_TO_PHYSICAL_ADDRESS(initrd_buffer) >> 32;
+ boot_params->hdr.ramdisk_size = initrd_length;
+ boot_params->ext_ramdisk_size = ((uint64_t) initrd_length) >> 32;
+
+ linux_efi_handover(image, (uintptr_t) linux_buffer, boot_params);
return EFI_LOAD_ERROR;
}
diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build
index efe056c225..b149735246 100644
--- a/src/boot/efi/meson.build
+++ b/src/boot/efi/meson.build
@@ -392,14 +392,13 @@ systemd_boot_sources = files(
stub_sources = files(
'cpio.c',
+ 'linux.c',
'splash.c',
'stub.c',
)
if efi_arch[1] in ['ia32', 'x86_64']
stub_sources += files('linux_x86.c')
-else
- stub_sources += files('linux.c')
endif
tests += [
diff --git a/src/boot/efi/pe.c b/src/boot/efi/pe.c
index 8d7061d55b..4602827f19 100644
--- a/src/boot/efi/pe.c
+++ b/src/boot/efi/pe.c
@@ -209,7 +209,7 @@ static uint32_t get_compatibility_entry_address(const DosFileHeader *dos, const
return 0;
}
-EFI_STATUS pe_alignment_info(
+EFI_STATUS pe_kernel_info(
const void *base,
uint32_t *ret_entry_point_address,
uint32_t *ret_size_of_image,
@@ -229,6 +229,10 @@ EFI_STATUS pe_alignment_info(
if (!verify_pe(pe, /* allow_compatibility= */ true))
return EFI_LOAD_ERROR;
+ /* Support for LINUX_INITRD_MEDIA_GUID was added in kernel stub 1.0. */
+ if (pe->OptionalHeader.MajorImageVersion < 1)
+ return EFI_UNSUPPORTED;
+
uint32_t entry_address = pe->OptionalHeader.AddressOfEntryPoint;
/* Look for a compat entry point. */
diff --git a/src/boot/efi/pe.h b/src/boot/efi/pe.h
index ea4801ced5..ead2ba01cc 100644
--- a/src/boot/efi/pe.h
+++ b/src/boot/efi/pe.h
@@ -17,7 +17,7 @@ EFI_STATUS pe_file_locate_sections(
UINTN *offsets,
UINTN *sizes);
-EFI_STATUS pe_alignment_info(
+EFI_STATUS pe_kernel_info(
const void *base,
uint32_t *ret_entry_point_address,
uint32_t *ret_size_of_image,
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
index 494972fa38..2e8b4d99d2 100644
--- a/src/boot/efi/stub.c
+++ b/src/boot/efi/stub.c
@@ -21,14 +21,11 @@ _used_ _section_(".sdmagic") static const char magic[] = "#### LoaderInfo: syste
static EFI_STATUS combine_initrd(
EFI_PHYSICAL_ADDRESS initrd_base, UINTN initrd_size,
const void * const extra_initrds[], const size_t extra_initrd_sizes[], size_t n_extra_initrds,
- EFI_PHYSICAL_ADDRESS *ret_initrd_base, UINTN *ret_initrd_size) {
+ Pages *ret_initr_pages, UINTN *ret_initrd_size) {
- EFI_PHYSICAL_ADDRESS base = UINT32_MAX; /* allocate an area below the 32bit boundary for this */
- EFI_STATUS err;
- uint8_t *p;
UINTN n;
- assert(ret_initrd_base);
+ assert(ret_initr_pages);
assert(ret_initrd_size);
/* Combines four initrds into one, by simple concatenation in memory */
@@ -45,15 +42,12 @@ static EFI_STATUS combine_initrd(
n += extra_initrd_sizes[i];
}
- err = BS->AllocatePages(
+ _cleanup_pages_ Pages pages = xmalloc_pages(
AllocateMaxAddress,
EfiLoaderData,
EFI_SIZE_TO_PAGES(n),
- &base);
- if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to allocate space for combined initrd: %r", err);
-
- p = PHYSICAL_ADDRESS_TO_POINTER(base);
+ UINT32_MAX /* Below 4G boundary. */);
+ uint8_t *p = PHYSICAL_ADDRESS_TO_POINTER(pages.addr);
if (initrd_base != 0) {
UINTN pad;
@@ -75,10 +69,11 @@ static EFI_STATUS combine_initrd(
p = mempcpy(p, extra_initrds[i], extra_initrd_sizes[i]);
}
- assert((uint8_t*) PHYSICAL_ADDRESS_TO_POINTER(base) + n == p);
+ assert(PHYSICAL_ADDRESS_TO_POINTER(pages.addr + n) == p);
- *ret_initrd_base = base;
+ *ret_initr_pages = pages;
*ret_initrd_size = n;
+ pages.n_pages = 0;
return EFI_SUCCESS;
}
@@ -342,6 +337,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
dt_size = szs[UNIFIED_SECTION_DTB];
dt_base = dt_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_DTB] : 0;
+ _cleanup_pages_ Pages initrd_pages = {};
if (credential_initrd || global_credential_initrd || sysext_initrd) {
/* If we have generated initrds dynamically, let's combine them with the built-in initrd. */
err = combine_initrd(
@@ -361,10 +357,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
pcrpkey_initrd_size,
},
5,
- &initrd_base, &initrd_size);
+ &initrd_pages, &initrd_size);
if (err != EFI_SUCCESS)
return err;
+ initrd_base = initrd_pages.addr;
+
/* Given these might be large let's free them explicitly, quickly. */
credential_initrd = mfree(credential_initrd);
global_credential_initrd = mfree(global_credential_initrd);
diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h
index 684930d5d6..be79b205a4 100644
--- a/src/boot/efi/util.h
+++ b/src/boot/efi/util.h
@@ -70,6 +70,32 @@ static inline void *xrealloc(void *p, size_t old_size, size_t new_size) {
#define xpool_print(fmt, ...) ((char16_t *) ASSERT_SE_PTR(PoolPrint((fmt), ##__VA_ARGS__)))
#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);