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:
commit
9ce964d55b
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user