Files
linux/drivers/firmware/efi/libstub/efi-stub-entry.c
Ard Biesheuvel fc3608aaa5 efi/libstub: Use relocated version of kernel's struct screen_info
In some cases, we expose the kernel's struct screen_info to the EFI stub
directly, so it gets populated before even entering the kernel.  This
means the early console is available as soon as the early param parsing
happens, which is nice. It also means we need two different ways to pass
this information, as this trick only works if the EFI stub is baked into
the core kernel image, which is not always the case.

Huacai reports that the preparatory refactoring that was needed to
implement this alternative method for zboot resulted in a non-functional
efifb earlycon for other cases as well, due to the reordering of the
kernel image relocation with the population of the screen_info struct,
and the latter now takes place after copying the image to its new
location, which means we copy the old, uninitialized state.

So let's ensure that the same-image version of alloc_screen_info()
produces the correct screen_info pointer, by taking the displacement of
the loaded image into account.

Reported-by: Huacai Chen <chenhuacai@loongson.cn>
Tested-by: Huacai Chen <chenhuacai@loongson.cn>
Link: https://lore.kernel.org/linux-efi/20230310021749.921041-1-chenhuacai@loongson.cn/
Fixes: 42c8ea3dca ("efi: libstub: Factor out EFI stub entrypoint into separate file")
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
2023-03-23 12:27:18 +01:00

77 lines
2.0 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
#include <linux/efi.h>
#include <asm/efi.h>
#include "efistub.h"
static unsigned long screen_info_offset;
struct screen_info *alloc_screen_info(void)
{
if (IS_ENABLED(CONFIG_ARM))
return __alloc_screen_info();
return (void *)&screen_info + screen_info_offset;
}
/*
* EFI entry point for the generic EFI stub used by ARM, arm64, RISC-V and
* LoongArch. This is the entrypoint that is described in the PE/COFF header
* of the core kernel.
*/
efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
efi_system_table_t *systab)
{
efi_loaded_image_t *image;
efi_status_t status;
unsigned long image_addr;
unsigned long image_size = 0;
/* addr/point and size pairs for memory management*/
char *cmdline_ptr = NULL;
efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID;
unsigned long reserve_addr = 0;
unsigned long reserve_size = 0;
WRITE_ONCE(efi_system_table, systab);
/* Check if we were booted by the EFI firmware */
if (efi_system_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
return EFI_INVALID_PARAMETER;
/*
* Get a handle to the loaded image protocol. This is used to get
* information about the running image, such as size and the command
* line.
*/
status = efi_bs_call(handle_protocol, handle, &loaded_image_proto,
(void *)&image);
if (status != EFI_SUCCESS) {
efi_err("Failed to get loaded image protocol\n");
return status;
}
status = efi_handle_cmdline(image, &cmdline_ptr);
if (status != EFI_SUCCESS)
return status;
efi_info("Booting Linux Kernel...\n");
status = handle_kernel_image(&image_addr, &image_size,
&reserve_addr,
&reserve_size,
image, handle);
if (status != EFI_SUCCESS) {
efi_err("Failed to relocate kernel\n");
return status;
}
screen_info_offset = image_addr - (unsigned long)image->image_base;
status = efi_stub_common(handle, image, image_addr, cmdline_ptr);
efi_free(image_size, image_addr);
efi_free(reserve_size, reserve_addr);
return status;
}