uint32_t compat_address;
err = pe_kernel_info(loaded_image->ImageBase, /* ret_entry_point= */ NULL, &compat_address,
- /* ret_size_in_memory= */ NULL);
+ /* ret_size_in_memory= */ NULL,
+ /* ret_section_alignment= */ NULL);
if (err != EFI_SUCCESS) {
if (err != EFI_UNSUPPORTED)
return log_error_status(err, "Error finding kernel compat entry address: %m");
const struct iovec *initrd) {
size_t kernel_size_in_memory = 0;
- uint32_t compat_entry_point, entry_point;
+ uint32_t compat_entry_point, entry_point, section_alignment;
EFI_STATUS err;
assert(parent_image);
assert(iovec_is_set(kernel));
assert(iovec_is_valid(initrd));
- err = pe_kernel_info(kernel->iov_base, &entry_point, &compat_entry_point, &kernel_size_in_memory);
+ err = pe_kernel_info(kernel->iov_base, &entry_point, &compat_entry_point, &kernel_size_in_memory, §ion_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
if (err != EFI_SUCCESS)
return log_error_status(err, "Cannot read sections: %m");
- /* Do we need to ensure under 4gb address on x86? */
- _cleanup_pages_ Pages loaded_kernel_pages = xmalloc_pages(
- AllocateAnyPages, EfiLoaderCode, EFI_SIZE_TO_PAGES(kernel_size_in_memory), 0);
+ /* Honor the PE SectionAlignment (SZ_64K on arm64): if _text is not aligned the kernel's EFI stub
+ * reallocates and copies the image, which can fail with EFI_OUT_OF_RESOURCES on memory-constrained
+ * firmware. When alignment <= EFI_PAGE_SIZE (e.g. x86_64) xmalloc_aligned_pages() reduces to a
+ * plain AllocatePages() with no extra over-allocation. pe_kernel_info() already sanitized a
+ * non-conforming SectionAlignment to plain page alignment. */
+ _cleanup_pages_ Pages loaded_kernel_pages = xmalloc_aligned_pages(
+ AllocateAnyPages,
+ EfiLoaderCode,
+ EFI_SIZE_TO_PAGES(kernel_size_in_memory),
+ section_alignment,
+ /* addr= */ 0);
uint8_t* loaded_kernel = PHYSICAL_ADDRESS_TO_POINTER(loaded_kernel_pages.addr);
FOREACH_ARRAY(h, headers, n_headers) {
return 0;
}
-EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_entry_point, uint32_t *ret_compat_entry_point, size_t *ret_size_in_memory) {
+EFI_STATUS pe_kernel_info(
+ const void *base,
+ uint32_t *ret_entry_point,
+ uint32_t *ret_compat_entry_point,
+ size_t *ret_size_in_memory,
+ uint32_t *ret_section_alignment) {
assert(base);
const DosFileHeader *dos = (const DosFileHeader *) base;
* of the SizeOfImage field in the PE header and return it */
size_t size_in_memory = pe->OptionalHeader.SizeOfImage;
+ /* Honoring SectionAlignment lets callers place the image so the kernel's EFI stub need not relocate
+ * it (SZ_64K on arm64). The PE spec requires a power of 2; for a non-conforming value fall back to
+ * plain page alignment (what we assumed before honoring this field) rather than propagate something
+ * that would break the allocator's over-alignment math. */
+ uint32_t section_alignment = pe->OptionalHeader.SectionAlignment;
+ if (!ISPOWEROF2(section_alignment)) {
+ log_warning("PE SectionAlignment %" PRIu32 " is not a power of 2, falling back to page alignment.", section_alignment);
+ section_alignment = EFI_PAGE_SIZE;
+ }
+
/* Support for LINUX_INITRD_MEDIA_GUID was added in kernel stub 1.0. */
if (pe->OptionalHeader.MajorImageVersion < 1)
return EFI_UNSUPPORTED;
*ret_compat_entry_point = 0;
if (ret_size_in_memory)
*ret_size_in_memory = size_in_memory;
+ if (ret_section_alignment)
+ *ret_section_alignment = section_alignment;
return EFI_SUCCESS;
}
*ret_compat_entry_point = compat_entry_point;
if (ret_size_in_memory)
*ret_size_in_memory = size_in_memory;
+ if (ret_section_alignment)
+ *ret_section_alignment = section_alignment;
return EFI_SUCCESS;
}
const char *const section_names[],
PeSectionVector sections[]);
-EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_entry_point, uint32_t *ret_compat_entry_point, size_t *ret_size_in_memory);
+EFI_STATUS pe_kernel_info(
+ const void *base,
+ uint32_t *ret_entry_point,
+ uint32_t *ret_compat_entry_point,
+ size_t *ret_size_in_memory,
+ uint32_t *ret_section_alignment);
EFI_STATUS pe_kernel_check_no_relocation(const void *base);