1
0
mirror of https://github.com/systemd/systemd.git synced 2024-10-27 01:55:22 +03:00

Merge pull request #33882 from bluca/efi_proto_fallback_memory

stub: allocate and zero enough space in legacy x86 handover protocol
This commit is contained in:
Daan De Meyer 2024-07-31 13:46:41 +02:00 committed by GitHub
commit 9ce964d55b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 30 additions and 11 deletions

View File

@ -2446,7 +2446,7 @@ static EFI_STATUS image_start(
if (err == EFI_UNSUPPORTED && entry->type == LOADER_LINUX) {
uint32_t compat_address;
err = pe_kernel_info(loaded_image->ImageBase, &compat_address);
err = pe_kernel_info(loaded_image->ImageBase, &compat_address, /* ret_size_in_memory= */ NULL);
if (err != EFI_SUCCESS) {
if (err != EFI_UNSUPPORTED)
return log_error_status(err, "Error finding kernel compat entry address: %m");

View File

@ -96,6 +96,7 @@ EFI_STATUS linux_exec(
const struct iovec *kernel,
const struct iovec *initrd) {
size_t kernel_size_in_memory = 0;
uint32_t compat_address;
EFI_STATUS err;
@ -103,7 +104,7 @@ EFI_STATUS linux_exec(
assert(iovec_is_set(kernel));
assert(iovec_is_valid(initrd));
err = pe_kernel_info(kernel->iov_base, &compat_address);
err = pe_kernel_info(kernel->iov_base, &compat_address, &kernel_size_in_memory);
#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
@ -112,7 +113,8 @@ EFI_STATUS linux_exec(
parent,
cmdline,
kernel,
initrd);
initrd,
kernel_size_in_memory);
#endif
if (err != EFI_SUCCESS)
return log_error_status(err, "Bad kernel image: %m");

View File

@ -13,4 +13,5 @@ EFI_STATUS linux_exec_efi_handover(
EFI_HANDLE parent,
const char16_t *cmdline,
const struct iovec *kernel,
const struct iovec *initrd);
const struct iovec *initrd,
size_t kernel_size_in_memory);

View File

@ -7,12 +7,13 @@
* this x86 specific linux_exec function passes the initrd by setting the
* corresponding fields in the setup_header struct.
*
* see https://docs.kernel.org/x86/boot.html
* see https://docs.kernel.org/arch/x86/boot.html
*/
#include "initrd.h"
#include "linux.h"
#include "macro-fundamental.h"
#include "memory-util-fundamental.h"
#include "util.h"
#define KERNEL_SECTOR_SIZE 512u
@ -124,7 +125,8 @@ EFI_STATUS linux_exec_efi_handover(
EFI_HANDLE parent,
const char16_t *cmdline,
const struct iovec *kernel,
const struct iovec *initrd) {
const struct iovec *initrd,
size_t kernel_size_in_memory) {
assert(parent);
assert(iovec_is_set(kernel));
@ -151,14 +153,23 @@ EFI_STATUS linux_exec_efi_handover(
FLAGS_SET(image_params->hdr.xloadflags, XLF_CAN_BE_LOADED_ABOVE_4G);
/* There is no way to pass the high bits of code32_start. Newer kernels seems to handle this
* just fine, but older kernels will fail even if they otherwise have above 4G boot support. */
* just fine, but older kernels will fail even if they otherwise have above 4G boot support.
* A PE image's memory footprint can be larger than its file size, due to unallocated virtual
* memory sections. While normally all PE headers should be taken into account, this case only
* involves x86 Linux bzImage kernel images, for which unallocated areas are only part of the last
* header, so parsing SizeOfImage and zeroeing the buffer past the image size is enough. */
_cleanup_pages_ Pages linux_relocated = {};
const void *linux_buffer;
if (POINTER_TO_PHYSICAL_ADDRESS(kernel->iov_base) + kernel->iov_len > UINT32_MAX) {
if (POINTER_TO_PHYSICAL_ADDRESS(kernel->iov_base) + kernel->iov_len > UINT32_MAX || kernel_size_in_memory > kernel->iov_len) {
linux_relocated = xmalloc_pages(
AllocateMaxAddress, EfiLoaderCode, EFI_SIZE_TO_PAGES(kernel->iov_len), UINT32_MAX);
AllocateMaxAddress,
EfiLoaderCode,
EFI_SIZE_TO_PAGES(kernel_size_in_memory > kernel->iov_len ? kernel_size_in_memory : kernel->iov_len),
UINT32_MAX);
linux_buffer = memcpy(
PHYSICAL_ADDRESS_TO_POINTER(linux_relocated.addr), kernel->iov_base, kernel->iov_len);
if (kernel_size_in_memory > kernel->iov_len)
memzero((uint8_t *) linux_buffer + kernel->iov_len, kernel_size_in_memory - kernel->iov_len);
} else
linux_buffer = kernel->iov_base;

View File

@ -278,7 +278,7 @@ static uint32_t get_compatibility_entry_address(const DosFileHeader *dos, const
return 0;
}
EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address) {
EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address, size_t *ret_size_in_memory) {
assert(base);
assert(ret_compat_address);
@ -290,6 +290,11 @@ EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address) {
if (!verify_pe(dos, pe, /* allow_compatibility= */ true))
return EFI_LOAD_ERROR;
/* When allocating we need to also consider the virtual/uninitialized data sections, so parse it out
* of the SizeOfImage field in the PE header and return it */
if (ret_size_in_memory)
*ret_size_in_memory = pe->OptionalHeader.SizeOfImage;
/* Support for LINUX_INITRD_MEDIA_GUID was added in kernel stub 1.0. */
if (pe->OptionalHeader.MajorImageVersion < 1)
return EFI_UNSUPPORTED;

View File

@ -26,4 +26,4 @@ EFI_STATUS pe_file_locate_sections(
const char *const section_names[],
PeSectionVector sections[]);
EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address);
EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address, size_t *ret_size_in_memory);