Merge tag 'efi-zboot-direct-for-v6.2' into efi/next

This commit is contained in:
Ard Biesheuvel 2022-11-18 09:13:57 +01:00
commit 977122898e
36 changed files with 987 additions and 927 deletions

View File

@ -7789,7 +7789,6 @@ F: Documentation/admin-guide/efi-stub.rst
F: arch/*/include/asm/efi.h
F: arch/*/kernel/efi.c
F: arch/arm/boot/compressed/efi-header.S
F: arch/arm64/kernel/efi-entry.S
F: arch/x86/platform/efi/
F: drivers/firmware/efi/
F: include/linux/efi*.h

View File

@ -43,9 +43,6 @@ void efi_virtmap_unload(void);
/* arch specific definitions used by the stub code */
struct screen_info *alloc_screen_info(void);
void free_screen_info(struct screen_info *si);
/*
* A reasonable upper bound for the uncompressed kernel size is 32 MBytes,
* so we will reserve that amount of memory. We have no easy way to tell what

View File

@ -75,38 +75,13 @@ int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md)
return 0;
}
static unsigned long __initdata screen_info_table = EFI_INVALID_TABLE_ADDR;
static unsigned long __initdata cpu_state_table = EFI_INVALID_TABLE_ADDR;
const efi_config_table_type_t efi_arch_tables[] __initconst = {
{LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, &screen_info_table},
{LINUX_EFI_ARM_CPU_STATE_TABLE_GUID, &cpu_state_table},
{}
};
static void __init load_screen_info_table(void)
{
struct screen_info *si;
if (screen_info_table != EFI_INVALID_TABLE_ADDR) {
si = early_memremap_ro(screen_info_table, sizeof(*si));
if (!si) {
pr_err("Could not map screen_info config table\n");
return;
}
screen_info = *si;
early_memunmap(si, sizeof(*si));
/* dummycon on ARM needs non-zero values for columns/lines */
screen_info.orig_video_cols = 80;
screen_info.orig_video_lines = 25;
if (memblock_is_map_memory(screen_info.lfb_base))
memblock_mark_nomap(screen_info.lfb_base,
screen_info.lfb_size);
}
}
static void __init load_cpu_state_table(void)
{
if (cpu_state_table != EFI_INVALID_TABLE_ADDR) {
@ -145,7 +120,11 @@ void __init arm_efi_init(void)
{
efi_init();
load_screen_info_table();
if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI) {
/* dummycon on ARM needs non-zero values for columns/lines */
screen_info.orig_video_cols = 80;
screen_info.orig_video_lines = 25;
}
/* ARM does not permit early mappings to persist across paging_init() */
efi_memmap_unmap();

View File

@ -84,10 +84,19 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long image_addr)
return (image_addr & ~(SZ_1G - 1UL)) + (1UL << (VA_BITS_MIN - 1));
}
#define alloc_screen_info(x...) &screen_info
static inline void free_screen_info(struct screen_info *si)
static inline unsigned long efi_get_kimg_min_align(void)
{
extern bool efi_nokaslr;
/*
* Although relocatable kernels can fix up the misalignment with
* respect to MIN_KIMG_ALIGN, the resulting virtual text addresses are
* subtly out of sync with those recorded in the vmlinux when kaslr is
* disabled but the image required relocation anyway. Therefore retain
* 2M alignment if KASLR was explicitly disabled, even if it was not
* going to be activated to begin with.
*/
return efi_nokaslr ? MIN_KIMG_ALIGN : EFI_KIMG_ALIGN;
}
#define EFI_ALLOC_ALIGN SZ_64K

View File

@ -36,12 +36,6 @@ obj-y := debug-monitors.o entry.o irq.o fpsimd.o \
syscall.o proton-pack.o idreg-override.o idle.o \
patching.o
targets += efi-entry.o
OBJCOPYFLAGS := --prefix-symbols=__efistub_
$(obj)/%.stub.o: $(obj)/%.o FORCE
$(call if_changed,objcopy)
obj-$(CONFIG_COMPAT) += sys32.o signal32.o \
sys_compat.o
obj-$(CONFIG_COMPAT) += sigreturn32.o
@ -57,8 +51,7 @@ obj-$(CONFIG_CPU_PM) += sleep.o suspend.o
obj-$(CONFIG_CPU_IDLE) += cpuidle.o
obj-$(CONFIG_JUMP_LABEL) += jump_label.o
obj-$(CONFIG_KGDB) += kgdb.o
obj-$(CONFIG_EFI) += efi.o efi-entry.stub.o \
efi-rt-wrapper.o
obj-$(CONFIG_EFI) += efi.o efi-rt-wrapper.o
obj-$(CONFIG_PCI) += pci.o
obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o
obj-$(CONFIG_ACPI) += acpi.o

View File

@ -1,69 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* EFI entry point.
*
* Copyright (C) 2013, 2014 Red Hat, Inc.
* Author: Mark Salter <msalter@redhat.com>
*/
#include <linux/linkage.h>
#include <linux/init.h>
#include <asm/assembler.h>
__INIT
SYM_CODE_START(efi_enter_kernel)
/*
* efi_pe_entry() will have copied the kernel image if necessary and we
* end up here with device tree address in x1 and the kernel entry
* point stored in x0. Save those values in registers which are
* callee preserved.
*/
ldr w2, =primary_entry_offset
add x19, x0, x2 // relocated Image entrypoint
mov x20, x1 // DTB address
/*
* Clean the copied Image to the PoC, and ensure it is not shadowed by
* stale icache entries from before relocation.
*/
ldr w1, =kernel_size
add x1, x0, x1
bl dcache_clean_poc
ic ialluis
/*
* Clean the remainder of this routine to the PoC
* so that we can safely disable the MMU and caches.
*/
adr x0, 0f
adr x1, 3f
bl dcache_clean_poc
0:
/* Turn off Dcache and MMU */
mrs x0, CurrentEL
cmp x0, #CurrentEL_EL2
b.ne 1f
mrs x0, sctlr_el2
bic x0, x0, #1 << 0 // clear SCTLR.M
bic x0, x0, #1 << 2 // clear SCTLR.C
pre_disable_mmu_workaround
msr sctlr_el2, x0
isb
b 2f
1:
mrs x0, sctlr_el1
bic x0, x0, #1 << 0 // clear SCTLR.M
bic x0, x0, #1 << 2 // clear SCTLR.C
pre_disable_mmu_workaround
msr sctlr_el1, x0
isb
2:
/* Jump to kernel entry point */
mov x0, x20
mov x1, xzr
mov x2, xzr
mov x3, xzr
br x19
3:
SYM_CODE_END(efi_enter_kernel)

View File

@ -10,7 +10,6 @@
#error This file should only be included in vmlinux.lds.S
#endif
PROVIDE(__efistub_kernel_size = _edata - _text);
PROVIDE(__efistub_primary_entry_offset = primary_entry - _text);
/*
@ -22,13 +21,6 @@ PROVIDE(__efistub_primary_entry_offset = primary_entry - _text);
* linked at. The routines below are all implemented in assembler in a
* position independent manner
*/
PROVIDE(__efistub_memcmp = __pi_memcmp);
PROVIDE(__efistub_memchr = __pi_memchr);
PROVIDE(__efistub_strlen = __pi_strlen);
PROVIDE(__efistub_strnlen = __pi_strnlen);
PROVIDE(__efistub_strcmp = __pi_strcmp);
PROVIDE(__efistub_strncmp = __pi_strncmp);
PROVIDE(__efistub_strrchr = __pi_strrchr);
PROVIDE(__efistub_dcache_clean_poc = __pi_dcache_clean_poc);
PROVIDE(__efistub__text = _text);

View File

@ -19,18 +19,18 @@ void efifb_setup_from_dmi(struct screen_info *si, const char *opt);
#define EFI_ALLOC_ALIGN SZ_64K
#define EFI_RT_VIRTUAL_OFFSET CSR_DMW0_BASE
static inline struct screen_info *alloc_screen_info(void)
{
return &screen_info;
}
static inline void free_screen_info(struct screen_info *si)
{
}
static inline unsigned long efi_get_max_initrd_addr(unsigned long image_addr)
{
return ULONG_MAX;
}
static inline unsigned long efi_get_kimg_min_align(void)
{
return SZ_2M;
}
#define EFI_KIMG_PREFERRED_ADDRESS PHYSADDR(VMLINUX_LOAD_ADDRESS)
unsigned long kernel_entry_address(void);
#endif /* _ASM_LOONGARCH_EFI_H */

View File

@ -52,6 +52,27 @@ void __init efi_runtime_init(void)
set_bit(EFI_RUNTIME_SERVICES, &efi.flags);
}
unsigned long __initdata screen_info_table = EFI_INVALID_TABLE_ADDR;
static void __init init_screen_info(void)
{
struct screen_info *si;
if (screen_info_table == EFI_INVALID_TABLE_ADDR)
return;
si = early_memremap(screen_info_table, sizeof(*si));
if (!si) {
pr_err("Could not map screen_info config table\n");
return;
}
screen_info = *si;
memset(si, 0, sizeof(*si));
early_memunmap(si, sizeof(*si));
memblock_reserve(screen_info.lfb_base, screen_info.lfb_size);
}
void __init efi_init(void)
{
int size;
@ -80,8 +101,7 @@ void __init efi_init(void)
set_bit(EFI_CONFIG_TABLES, &efi.flags);
if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI)
memblock_reserve(screen_info.lfb_base, screen_info.lfb_size);
init_screen_info();
if (boot_memmap == EFI_INVALID_TABLE_ADDR)
return;

View File

@ -7,15 +7,7 @@
#ifdef CONFIG_EFI_STUB
__efistub_memcmp = memcmp;
__efistub_memchr = memchr;
__efistub_strcat = strcat;
__efistub_strcmp = strcmp;
__efistub_strlen = strlen;
__efistub_strncat = strncat;
__efistub_strnstr = strnstr;
__efistub_strnlen = strnlen;
__efistub_strrchr = strrchr;
__efistub_kernel_entry = kernel_entry;
__efistub_kernel_asize = kernel_asize;
__efistub_kernel_fsize = kernel_fsize;

View File

@ -31,13 +31,20 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long image_addr)
return ULONG_MAX;
}
#define alloc_screen_info(x...) (&screen_info)
static inline void free_screen_info(struct screen_info *si)
static inline unsigned long efi_get_kimg_min_align(void)
{
/*
* RISC-V requires the kernel image to placed 2 MB aligned base for 64
* bit and 4MB for 32 bit.
*/
return IS_ENABLED(CONFIG_64BIT) ? SZ_2M : SZ_4M;
}
#define EFI_KIMG_PREFERRED_ADDRESS efi_get_kimg_min_align()
void efi_virtmap_load(void);
void efi_virtmap_unload(void);
unsigned long stext_offset(void);
#endif /* _ASM_EFI_H */

View File

@ -23,13 +23,7 @@
* linked at. The routines below are all implemented in assembler in a
* position independent manner
*/
__efistub_memcmp = memcmp;
__efistub_memchr = memchr;
__efistub_strlen = strlen;
__efistub_strnlen = strnlen;
__efistub_strcmp = strcmp;
__efistub_strncmp = strncmp;
__efistub_strrchr = strrchr;
__efistub__start = _start;
__efistub__start_kernel = _start_kernel;

View File

@ -22,6 +22,8 @@
#include <asm/efi.h>
unsigned long __initdata screen_info_table = EFI_INVALID_TABLE_ADDR;
static int __init is_memory(efi_memory_desc_t *md)
{
if (md->attribute & (EFI_MEMORY_WB|EFI_MEMORY_WT|EFI_MEMORY_WC))
@ -55,9 +57,22 @@ extern __weak const efi_config_table_type_t efi_arch_tables[];
static void __init init_screen_info(void)
{
if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI &&
memblock_is_map_memory(screen_info.lfb_base))
memblock_mark_nomap(screen_info.lfb_base, screen_info.lfb_size);
struct screen_info *si;
if (screen_info_table != EFI_INVALID_TABLE_ADDR) {
si = early_memremap(screen_info_table, sizeof(*si));
if (!si) {
pr_err("Could not map screen_info config table\n");
return;
}
screen_info = *si;
memset(si, 0, sizeof(*si));
early_memunmap(si, sizeof(*si));
if (memblock_is_map_memory(screen_info.lfb_base))
memblock_mark_nomap(screen_info.lfb_base,
screen_info.lfb_size);
}
}
static int __init uefi_init(u64 efi_system_table)

View File

@ -58,6 +58,8 @@ static unsigned long __initdata mem_reserve = EFI_INVALID_TABLE_ADDR;
static unsigned long __initdata rt_prop = EFI_INVALID_TABLE_ADDR;
static unsigned long __initdata initrd = EFI_INVALID_TABLE_ADDR;
extern unsigned long screen_info_table;
struct mm_struct efi_mm = {
.mm_mt = MTREE_INIT_EXT(mm_mt, MM_MT_FLAGS, efi_mm.mmap_lock),
.mm_users = ATOMIC_INIT(2),
@ -546,6 +548,9 @@ static const efi_config_table_type_t common_tables[] __initconst = {
#endif
#ifdef CONFIG_EFI_COCO_SECRET
{LINUX_EFI_COCO_SECRET_AREA_GUID, &efi.coco_secret, "CocoSecret" },
#endif
#ifdef CONFIG_EFI_GENERIC_STUB
{LINUX_EFI_SCREEN_INFO_TABLE_GUID, &screen_info_table },
#endif
{},
};

View File

@ -5,6 +5,10 @@
# things like ftrace and stack-protector are likely to cause trouble if left
# enabled, even if doing so doesn't break the build.
#
# non-x86 reuses KBUILD_CFLAGS, x86 does not
cflags-y := $(KBUILD_CFLAGS)
cflags-$(CONFIG_X86_32) := -march=i386
cflags-$(CONFIG_X86_64) := -mcmodel=small
cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ \
@ -18,20 +22,19 @@ cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ \
# arm64 uses the full KBUILD_CFLAGS so it's necessary to explicitly
# disable the stackleak plugin
cflags-$(CONFIG_ARM64) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \
-fpie $(DISABLE_STACKLEAK_PLUGIN) \
cflags-$(CONFIG_ARM64) += -fpie $(DISABLE_STACKLEAK_PLUGIN) \
$(call cc-option,-mbranch-protection=none)
cflags-$(CONFIG_ARM) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \
-fno-builtin -fpic \
cflags-$(CONFIG_ARM) += -DEFI_HAVE_STRLEN -DEFI_HAVE_STRNLEN \
-DEFI_HAVE_MEMCHR -DEFI_HAVE_STRRCHR \
-DEFI_HAVE_STRCMP -fno-builtin -fpic \
$(call cc-option,-mno-single-pic-base)
cflags-$(CONFIG_RISCV) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \
-fpic
cflags-$(CONFIG_LOONGARCH) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \
-fpie
cflags-$(CONFIG_RISCV) += -fpic
cflags-$(CONFIG_LOONGARCH) += -fpie
cflags-$(CONFIG_EFI_PARAMS_FROM_FDT) += -I$(srctree)/scripts/dtc/libfdt
KBUILD_CFLAGS := $(cflags-y) -Os -DDISABLE_BRANCH_PROFILING \
KBUILD_CFLAGS := $(subst $(CC_FLAGS_FTRACE),,$(cflags-y)) \
-Os -DDISABLE_BRANCH_PROFILING \
-include $(srctree)/include/linux/hidden.h \
-D__NO_FORTIFY \
-ffreestanding \
@ -67,7 +70,7 @@ KCOV_INSTRUMENT := n
lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \
file.o mem.o random.o randomalloc.o pci.o \
skip_spaces.o lib-cmdline.o lib-ctype.o \
alignedmem.o relocate.o vsprintf.o
alignedmem.o relocate.o printk.o vsprintf.o
# include the stub's libfdt dependencies from lib/ when needed
libfdt-deps := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c \
@ -79,13 +82,14 @@ lib-$(CONFIG_EFI_PARAMS_FROM_FDT) += fdt.o \
$(obj)/lib-%.o: $(srctree)/lib/%.c FORCE
$(call if_changed_rule,cc_o_c)
lib-$(CONFIG_EFI_GENERIC_STUB) += efi-stub.o string.o intrinsics.o systable.o
lib-$(CONFIG_EFI_GENERIC_STUB) += efi-stub.o string.o intrinsics.o systable.o \
screen_info.o efi-stub-entry.o
lib-$(CONFIG_ARM) += arm32-stub.o
lib-$(CONFIG_ARM64) += arm64-stub.o smbios.o
lib-$(CONFIG_ARM64) += arm64.o arm64-stub.o arm64-entry.o smbios.o
lib-$(CONFIG_X86) += x86-stub.o
lib-$(CONFIG_RISCV) += riscv-stub.o
lib-$(CONFIG_LOONGARCH) += loongarch-stub.o
lib-$(CONFIG_RISCV) += riscv.o riscv-stub.o
lib-$(CONFIG_LOONGARCH) += loongarch.o loongarch-stub.o
CFLAGS_arm32-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
@ -136,7 +140,7 @@ STUBCOPY_RELOC-$(CONFIG_ARM) := R_ARM_ABS
#
STUBCOPY_FLAGS-$(CONFIG_ARM64) += --prefix-alloc-sections=.init \
--prefix-symbols=__efistub_
STUBCOPY_RELOC-$(CONFIG_ARM64) := R_AARCH64_ABS
STUBCOPY_RELOC-$(CONFIG_ARM64) := R_AARCH64_ABS64
# For RISC-V, we don't need anything special other than arm64. Keep all the
# symbols in .init section and make sure that no absolute symbols references

View File

@ -10,18 +10,17 @@ comp-type-$(CONFIG_KERNEL_LZO) := lzo
comp-type-$(CONFIG_KERNEL_XZ) := xzkern
comp-type-$(CONFIG_KERNEL_ZSTD) := zstd22
# in GZIP, the appended le32 carrying the uncompressed size is part of the
# format, but in other cases, we just append it at the end for convenience,
# causing the original tools to complain when checking image integrity.
# So disregard it when calculating the payload size in the zimage header.
zboot-method-y := $(comp-type-y)_with_size
zboot-size-len-y := 4
zboot-method-$(CONFIG_KERNEL_GZIP) := gzip
zboot-size-len-$(CONFIG_KERNEL_GZIP) := 0
# Copy the SizeOfHeaders, SizeOfCode and SizeOfImage fields from the payload to
# the end of the compressed image. Note that this presupposes a PE header
# offset of 64 bytes, which is what arm64, RISC-V and LoongArch use.
quiet_cmd_compwithsize = $(quiet_cmd_$(comp-type-y))
cmd_compwithsize = $(cmd_$(comp-type-y)) && ( \
dd status=none if=$< bs=4 count=1 skip=37 ; \
dd status=none if=$< bs=4 count=1 skip=23 ; \
dd status=none if=$< bs=4 count=1 skip=36 ) >> $@
$(obj)/vmlinuz: $(obj)/$(EFI_ZBOOT_PAYLOAD) FORCE
$(call if_changed,$(zboot-method-y))
$(call if_changed,compwithsize)
OBJCOPYFLAGS_vmlinuz.o := -I binary -O $(EFI_ZBOOT_BFD_TARGET) \
--rename-section .data=.gzdata,load,alloc,readonly,contents
@ -30,7 +29,6 @@ $(obj)/vmlinuz.o: $(obj)/vmlinuz FORCE
AFLAGS_zboot-header.o += -DMACHINE_TYPE=IMAGE_FILE_MACHINE_$(EFI_ZBOOT_MACH_TYPE) \
-DZBOOT_EFI_PATH="\"$(realpath $(obj)/vmlinuz.efi.elf)\"" \
-DZBOOT_SIZE_LEN=$(zboot-size-len-y) \
-DCOMP_TYPE="\"$(comp-type-y)\""
$(obj)/zboot-header.o: $(srctree)/drivers/firmware/efi/libstub/zboot-header.S FORCE
@ -46,4 +44,4 @@ OBJCOPYFLAGS_vmlinuz.efi := -O binary
$(obj)/vmlinuz.efi: $(obj)/vmlinuz.efi.elf FORCE
$(call if_changed,objcopy)
targets += zboot-header.o vmlinuz vmlinuz.o vmlinuz.efi.elf vmlinuz.efi
targets += zboot-header.o vmlinuz.o vmlinuz.efi.elf vmlinuz.efi

View File

@ -76,43 +76,6 @@ void efi_handle_post_ebs_state(void)
&efi_entry_state->sctlr_after_ebs);
}
static efi_guid_t screen_info_guid = LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID;
struct screen_info *alloc_screen_info(void)
{
struct screen_info *si;
efi_status_t status;
/*
* Unlike on arm64, where we can directly fill out the screen_info
* structure from the stub, we need to allocate a buffer to hold
* its contents while we hand over to the kernel proper from the
* decompressor.
*/
status = efi_bs_call(allocate_pool, EFI_RUNTIME_SERVICES_DATA,
sizeof(*si), (void **)&si);
if (status != EFI_SUCCESS)
return NULL;
status = efi_bs_call(install_configuration_table,
&screen_info_guid, si);
if (status == EFI_SUCCESS)
return si;
efi_bs_call(free_pool, si);
return NULL;
}
void free_screen_info(struct screen_info *si)
{
if (!si)
return;
efi_bs_call(install_configuration_table, &screen_info_guid, NULL);
efi_bs_call(free_pool, si);
}
efi_status_t handle_kernel_image(unsigned long *image_addr,
unsigned long *image_size,
unsigned long *reserve_addr,

View File

@ -0,0 +1,67 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* EFI entry point.
*
* Copyright (C) 2013, 2014 Red Hat, Inc.
* Author: Mark Salter <msalter@redhat.com>
*/
#include <linux/linkage.h>
#include <asm/assembler.h>
/*
* The entrypoint of a arm64 bare metal image is at offset #0 of the
* image, so this is a reasonable default for primary_entry_offset.
* Only when the EFI stub is integrated into the core kernel, it is not
* guaranteed that the PE/COFF header has been copied to memory too, so
* in this case, primary_entry_offset should be overridden by the
* linker and point to primary_entry() directly.
*/
.weak primary_entry_offset
SYM_CODE_START(efi_enter_kernel)
/*
* efi_pe_entry() will have copied the kernel image if necessary and we
* end up here with device tree address in x1 and the kernel entry
* point stored in x0. Save those values in registers which are
* callee preserved.
*/
ldr w2, =primary_entry_offset
add x19, x0, x2 // relocated Image entrypoint
mov x0, x1 // DTB address
mov x1, xzr
mov x2, xzr
mov x3, xzr
/*
* Clean the remainder of this routine to the PoC
* so that we can safely disable the MMU and caches.
*/
adr x4, 1f
dc civac, x4
dsb sy
/* Turn off Dcache and MMU */
mrs x4, CurrentEL
cmp x4, #CurrentEL_EL2
mrs x4, sctlr_el1
b.ne 0f
mrs x4, sctlr_el2
0: bic x4, x4, #SCTLR_ELx_M
bic x4, x4, #SCTLR_ELx_C
b.eq 1f
b 2f
.balign 32
1: pre_disable_mmu_workaround
msr sctlr_el2, x4
isb
br x19 // jump to kernel entrypoint
2: pre_disable_mmu_workaround
msr sctlr_el1, x4
isb
br x19 // jump to kernel entrypoint
.org 1b + 32
SYM_CODE_END(efi_enter_kernel)

View File

@ -11,52 +11,9 @@
#include <asm/efi.h>
#include <asm/memory.h>
#include <asm/sections.h>
#include <asm/sysreg.h>
#include "efistub.h"
static bool system_needs_vamap(void)
{
const u8 *type1_family = efi_get_smbios_string(1, family);
/*
* Ampere Altra machines crash in SetTime() if SetVirtualAddressMap()
* has not been called prior.
*/
if (!type1_family || strcmp(type1_family, "Altra"))
return false;
efi_warn("Working around broken SetVirtualAddressMap()\n");
return true;
}
efi_status_t check_platform_features(void)
{
u64 tg;
/*
* If we have 48 bits of VA space for TTBR0 mappings, we can map the
* UEFI runtime regions 1:1 and so calling SetVirtualAddressMap() is
* unnecessary.
*/
if (VA_BITS_MIN >= 48 && !system_needs_vamap())
efi_novamap = true;
/* UEFI mandates support for 4 KB granularity, no need to check */
if (IS_ENABLED(CONFIG_ARM64_4K_PAGES))
return EFI_SUCCESS;
tg = (read_cpuid(ID_AA64MMFR0_EL1) >> ID_AA64MMFR0_EL1_TGRAN_SHIFT) & 0xf;
if (tg < ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MIN || tg > ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MAX) {
if (IS_ENABLED(CONFIG_ARM64_64K_PAGES))
efi_err("This 64 KB granular kernel is not supported by your CPU\n");
else
efi_err("This 16 KB granular kernel is not supported by your CPU\n");
return EFI_UNSUPPORTED;
}
return EFI_SUCCESS;
}
/*
* Distro versions of GRUB may ignore the BSS allocation entirely (i.e., fail
* to provide space, and fail to zero it). Check for this condition by double
@ -103,16 +60,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
efi_status_t status;
unsigned long kernel_size, kernel_memsize = 0;
u32 phys_seed = 0;
/*
* Although relocatable kernels can fix up the misalignment with
* respect to MIN_KIMG_ALIGN, the resulting virtual text addresses are
* subtly out of sync with those recorded in the vmlinux when kaslr is
* disabled but the image required relocation anyway. Therefore retain
* 2M alignment if KASLR was explicitly disabled, even if it was not
* going to be activated to begin with.
*/
u64 min_kimg_align = efi_nokaslr ? MIN_KIMG_ALIGN : EFI_KIMG_ALIGN;
u64 min_kimg_align = efi_get_kimg_min_align();
if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {
efi_guid_t li_fixed_proto = LINUX_EFI_LOADED_IMAGE_FIXED_GUID;
@ -171,7 +119,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
*/
*image_addr = (u64)_text;
*reserve_size = 0;
return EFI_SUCCESS;
goto clean_image_to_poc;
}
status = efi_allocate_pages_aligned(*reserve_size, reserve_addr,
@ -187,5 +135,13 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
*image_addr = *reserve_addr;
memcpy((void *)*image_addr, _text, kernel_size);
clean_image_to_poc:
/*
* Clean the copied Image to the PoC, and ensure it is not shadowed by
* stale icache entries from before relocation.
*/
dcache_clean_poc(*image_addr, *image_addr + kernel_size);
asm("ic ialluis");
return EFI_SUCCESS;
}

View File

@ -0,0 +1,76 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2013, 2014 Linaro Ltd; <roy.franz@linaro.org>
*
* This file implements the EFI boot stub for the arm64 kernel.
* Adapted from ARM version by Mark Salter <msalter@redhat.com>
*/
#include <linux/efi.h>
#include <asm/efi.h>
#include <asm/memory.h>
#include <asm/sysreg.h>
#include "efistub.h"
static bool system_needs_vamap(void)
{
const u8 *type1_family = efi_get_smbios_string(1, family);
/*
* Ampere Altra machines crash in SetTime() if SetVirtualAddressMap()
* has not been called prior.
*/
if (!type1_family || strcmp(type1_family, "Altra"))
return false;
efi_warn("Working around broken SetVirtualAddressMap()\n");
return true;
}
efi_status_t check_platform_features(void)
{
u64 tg;
/*
* If we have 48 bits of VA space for TTBR0 mappings, we can map the
* UEFI runtime regions 1:1 and so calling SetVirtualAddressMap() is
* unnecessary.
*/
if (VA_BITS_MIN >= 48 && !system_needs_vamap())
efi_novamap = true;
/* UEFI mandates support for 4 KB granularity, no need to check */
if (IS_ENABLED(CONFIG_ARM64_4K_PAGES))
return EFI_SUCCESS;
tg = (read_cpuid(ID_AA64MMFR0_EL1) >> ID_AA64MMFR0_EL1_TGRAN_SHIFT) & 0xf;
if (tg < ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MIN || tg > ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MAX) {
if (IS_ENABLED(CONFIG_ARM64_64K_PAGES))
efi_err("This 64 KB granular kernel is not supported by your CPU\n");
else
efi_err("This 16 KB granular kernel is not supported by your CPU\n");
return EFI_UNSUPPORTED;
}
return EFI_SUCCESS;
}
void efi_cache_sync_image(unsigned long image_base,
unsigned long alloc_size,
unsigned long code_size)
{
u32 ctr = read_cpuid_effective_cachetype();
u64 lsize = 4 << cpuid_feature_extract_unsigned_field(ctr,
CTR_EL0_DminLine_SHIFT);
do {
asm("dc civac, %0" :: "r"(image_base));
image_base += lsize;
alloc_size -= lsize;
} while (alloc_size >= lsize);
asm("ic ialluis");
dsb(ish);
isb();
}

View File

@ -0,0 +1,65 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/efi.h>
#include <asm/efi.h>
#include "efistub.h"
/*
* 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;
}
status = efi_stub_common(handle, image, image_addr, cmdline_ptr);
efi_free(image_size, image_addr);
efi_free(reserve_size, reserve_addr);
return status;
}

View File

@ -9,10 +9,8 @@
#include <linux/stdarg.h>
#include <linux/ctype.h>
#include <linux/efi.h>
#include <linux/kernel.h>
#include <linux/printk.h> /* For CONSOLE_LOGLEVEL_* */
#include <asm/efi.h>
#include <asm/setup.h>
@ -20,7 +18,6 @@
bool efi_nochunk;
bool efi_nokaslr = !IS_ENABLED(CONFIG_RANDOMIZE_BASE);
int efi_loglevel = CONSOLE_LOGLEVEL_DEFAULT;
bool efi_novamap;
static bool efi_noinitrd;
@ -32,146 +29,6 @@ bool __pure __efi_soft_reserve_enabled(void)
return !efi_nosoftreserve;
}
/**
* efi_char16_puts() - Write a UCS-2 encoded string to the console
* @str: UCS-2 encoded string
*/
void efi_char16_puts(efi_char16_t *str)
{
efi_call_proto(efi_table_attr(efi_system_table, con_out),
output_string, str);
}
static
u32 utf8_to_utf32(const u8 **s8)
{
u32 c32;
u8 c0, cx;
size_t clen, i;
c0 = cx = *(*s8)++;
/*
* The position of the most-significant 0 bit gives us the length of
* a multi-octet encoding.
*/
for (clen = 0; cx & 0x80; ++clen)
cx <<= 1;
/*
* If the 0 bit is in position 8, this is a valid single-octet
* encoding. If the 0 bit is in position 7 or positions 1-3, the
* encoding is invalid.
* In either case, we just return the first octet.
*/
if (clen < 2 || clen > 4)
return c0;
/* Get the bits from the first octet. */
c32 = cx >> clen--;
for (i = 0; i < clen; ++i) {
/* Trailing octets must have 10 in most significant bits. */
cx = (*s8)[i] ^ 0x80;
if (cx & 0xc0)
return c0;
c32 = (c32 << 6) | cx;
}
/*
* Check for validity:
* - The character must be in the Unicode range.
* - It must not be a surrogate.
* - It must be encoded using the correct number of octets.
*/
if (c32 > 0x10ffff ||
(c32 & 0xf800) == 0xd800 ||
clen != (c32 >= 0x80) + (c32 >= 0x800) + (c32 >= 0x10000))
return c0;
*s8 += clen;
return c32;
}
/**
* efi_puts() - Write a UTF-8 encoded string to the console
* @str: UTF-8 encoded string
*/
void efi_puts(const char *str)
{
efi_char16_t buf[128];
size_t pos = 0, lim = ARRAY_SIZE(buf);
const u8 *s8 = (const u8 *)str;
u32 c32;
while (*s8) {
if (*s8 == '\n')
buf[pos++] = L'\r';
c32 = utf8_to_utf32(&s8);
if (c32 < 0x10000) {
/* Characters in plane 0 use a single word. */
buf[pos++] = c32;
} else {
/*
* Characters in other planes encode into a surrogate
* pair.
*/
buf[pos++] = (0xd800 - (0x10000 >> 10)) + (c32 >> 10);
buf[pos++] = 0xdc00 + (c32 & 0x3ff);
}
if (*s8 == '\0' || pos >= lim - 2) {
buf[pos] = L'\0';
efi_char16_puts(buf);
pos = 0;
}
}
}
/**
* efi_printk() - Print a kernel message
* @fmt: format string
*
* The first letter of the format string is used to determine the logging level
* of the message. If the level is less then the current EFI logging level, the
* message is suppressed. The message will be truncated to 255 bytes.
*
* Return: number of printed characters
*/
int efi_printk(const char *fmt, ...)
{
char printf_buf[256];
va_list args;
int printed;
int loglevel = printk_get_level(fmt);
switch (loglevel) {
case '0' ... '9':
loglevel -= '0';
break;
default:
/*
* Use loglevel -1 for cases where we just want to print to
* the screen.
*/
loglevel = -1;
break;
}
if (loglevel >= efi_loglevel)
return 0;
if (loglevel >= 0)
efi_puts("EFI stub: ");
fmt = printk_skip_level(fmt);
va_start(args, fmt);
printed = vsnprintf(printf_buf, sizeof(printf_buf), fmt, args);
va_end(args);
efi_puts(printf_buf);
if (printed >= sizeof(printf_buf)) {
efi_puts("[Message truncated]\n");
return -1;
}
return printed;
}
/**
* efi_parse_options() - Parse EFI command line options
* @cmdline: kernel command line

View File

@ -35,15 +35,6 @@
* as well to minimize the code churn.
*/
#define EFI_RT_VIRTUAL_BASE SZ_512M
#define EFI_RT_VIRTUAL_SIZE SZ_512M
#ifdef CONFIG_ARM64
# define EFI_RT_VIRTUAL_LIMIT DEFAULT_MAP_WINDOW_64
#elif defined(CONFIG_RISCV) || defined(CONFIG_LOONGARCH)
# define EFI_RT_VIRTUAL_LIMIT TASK_SIZE_MIN
#else /* Only if TASK_SIZE is a constant */
# define EFI_RT_VIRTUAL_LIMIT TASK_SIZE
#endif
/*
* Some architectures map the EFI regions into the kernel's linear map using a
@ -56,6 +47,15 @@
static u64 virtmap_base = EFI_RT_VIRTUAL_BASE;
static bool flat_va_mapping = (EFI_RT_VIRTUAL_OFFSET != 0);
struct screen_info * __weak alloc_screen_info(void)
{
return &screen_info;
}
void __weak free_screen_info(struct screen_info *si)
{
}
static struct screen_info *setup_graphics(void)
{
efi_guid_t gop_proto = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
@ -115,62 +115,21 @@ static u32 get_supported_rt_services(void)
return supported;
}
/*
* EFI entry point for the arm/arm64 EFI stubs. This is the entrypoint
* that is described in the PE/COFF header. Most of the code is the same
* for both archictectures, with the arch-specific code provided in the
* handle_kernel_image() function.
*/
efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
efi_system_table_t *sys_table_arg)
efi_status_t efi_handle_cmdline(efi_loaded_image_t *image, char **cmdline_ptr)
{
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;
int cmdline_size = 0;
efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID;
unsigned long reserve_addr = 0;
unsigned long reserve_size = 0;
struct screen_info *si;
efi_properties_table_t *prop_tbl;
efi_system_table = sys_table_arg;
/* Check if we were booted by the EFI firmware */
if (efi_system_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) {
status = EFI_INVALID_PARAMETER;
goto fail;
}
status = check_platform_features();
if (status != EFI_SUCCESS)
goto fail;
/*
* 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");
goto fail;
}
efi_status_t status;
char *cmdline;
/*
* Get the command line from EFI, using the LOADED_IMAGE
* protocol. We are going to copy the command line into the
* device tree, so this can be allocated anywhere.
*/
cmdline_ptr = efi_convert_cmdline(image, &cmdline_size);
if (!cmdline_ptr) {
cmdline = efi_convert_cmdline(image, &cmdline_size);
if (!cmdline) {
efi_err("getting command line via LOADED_IMAGE_PROTOCOL\n");
status = EFI_OUT_OF_RESOURCES;
goto fail;
return EFI_OUT_OF_RESOURCES;
}
if (IS_ENABLED(CONFIG_CMDLINE_EXTEND) ||
@ -184,26 +143,35 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
}
if (!IS_ENABLED(CONFIG_CMDLINE_FORCE) && cmdline_size > 0) {
status = efi_parse_options(cmdline_ptr);
status = efi_parse_options(cmdline);
if (status != EFI_SUCCESS) {
efi_err("Failed to parse options\n");
goto fail_free_cmdline;
}
}
efi_info("Booting Linux Kernel...\n");
*cmdline_ptr = cmdline;
return EFI_SUCCESS;
fail_free_cmdline:
efi_bs_call(free_pool, cmdline_ptr);
return status;
}
efi_status_t efi_stub_common(efi_handle_t handle,
efi_loaded_image_t *image,
unsigned long image_addr,
char *cmdline_ptr)
{
struct screen_info *si;
efi_status_t status;
status = check_platform_features();
if (status != EFI_SUCCESS)
return status;
si = setup_graphics();
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");
goto fail_free_screeninfo;
}
efi_retrieve_tpm2_eventlog();
/* Ask the firmware to clear memory on unclean shutdown */
@ -214,53 +182,15 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
efi_random_get_seed();
/*
* If the NX PE data feature is enabled in the properties table, we
* should take care not to create a virtual mapping that changes the
* relative placement of runtime services code and data regions, as
* they may belong to the same PE/COFF executable image in memory.
* The easiest way to achieve that is to simply use a 1:1 mapping.
*/
prop_tbl = get_efi_config_table(EFI_PROPERTIES_TABLE_GUID);
flat_va_mapping |= prop_tbl &&
(prop_tbl->memory_protection_attribute &
EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA);
/* force efi_novamap if SetVirtualAddressMap() is unsupported */
efi_novamap |= !(get_supported_rt_services() &
EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP);
/* hibernation expects the runtime regions to stay in the same place */
if (!IS_ENABLED(CONFIG_HIBERNATION) && !efi_nokaslr && !flat_va_mapping) {
/*
* Randomize the base of the UEFI runtime services region.
* Preserve the 2 MB alignment of the region by taking a
* shift of 21 bit positions into account when scaling
* the headroom value using a 32-bit random value.
*/
static const u64 headroom = EFI_RT_VIRTUAL_LIMIT -
EFI_RT_VIRTUAL_BASE -
EFI_RT_VIRTUAL_SIZE;
u32 rnd;
status = efi_get_random_bytes(sizeof(rnd), (u8 *)&rnd);
if (status == EFI_SUCCESS) {
virtmap_base = EFI_RT_VIRTUAL_BASE +
(((headroom >> 21) * rnd) >> (32 - 21));
}
}
install_memreserve_table();
status = efi_boot_kernel(handle, image, image_addr, cmdline_ptr);
efi_free(image_size, image_addr);
efi_free(reserve_size, reserve_addr);
fail_free_screeninfo:
free_screen_info(si);
fail_free_cmdline:
efi_bs_call(free_pool, cmdline_ptr);
fail:
return status;
}

View File

@ -958,6 +958,14 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
efi_loaded_image_t *image,
efi_handle_t image_handle);
/* shared entrypoint between the normal stub and the zboot stub */
efi_status_t efi_stub_common(efi_handle_t handle,
efi_loaded_image_t *image,
unsigned long image_addr,
char *cmdline_ptr);
efi_status_t efi_handle_cmdline(efi_loaded_image_t *image, char **cmdline_ptr);
asmlinkage void __noreturn efi_enter_kernel(unsigned long entrypoint,
unsigned long fdt_addr,
unsigned long fdt_size);
@ -975,6 +983,13 @@ efi_enable_reset_attack_mitigation(void) { }
void efi_retrieve_tpm2_eventlog(void);
struct screen_info *alloc_screen_info(void);
void free_screen_info(struct screen_info *si);
void efi_cache_sync_image(unsigned long image_base,
unsigned long alloc_size,
unsigned long code_size);
struct efi_smbios_record {
u8 type;
u8 length;

View File

@ -66,28 +66,10 @@ static efi_status_t efi_open_file(efi_file_protocol_t *volume,
static efi_status_t efi_open_volume(efi_loaded_image_t *image,
efi_file_protocol_t **fh)
{
struct efi_vendor_dev_path *dp = image->file_path;
efi_guid_t li_proto = LOADED_IMAGE_PROTOCOL_GUID;
efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID;
efi_simple_file_system_protocol_t *io;
efi_status_t status;
// If we are using EFI zboot, we should look for the file system
// protocol on the parent image's handle instead
if (IS_ENABLED(CONFIG_EFI_ZBOOT) &&
image->parent_handle != NULL &&
dp != NULL &&
dp->header.type == EFI_DEV_MEDIA &&
dp->header.sub_type == EFI_DEV_MEDIA_VENDOR &&
!efi_guidcmp(dp->vendorguid, LINUX_EFI_ZBOOT_MEDIA_GUID)) {
status = efi_bs_call(handle_protocol, image->parent_handle,
&li_proto, (void *)&image);
if (status != EFI_SUCCESS) {
efi_err("Failed to locate parent image handle\n");
return status;
}
}
status = efi_bs_call(handle_protocol, image->device_handle, &fs_proto,
(void **)&io);
if (status != EFI_SUCCESS) {

View File

@ -28,3 +28,21 @@ void *memset(void *dst, int c, size_t len)
efi_bs_call(set_mem, dst, len, c & U8_MAX);
return dst;
}
/**
* memcmp - Compare two areas of memory
* @cs: One area of memory
* @ct: Another area of memory
* @count: The size of the area.
*/
#undef memcmp
int memcmp(const void *cs, const void *ct, size_t count)
{
const unsigned char *su1, *su2;
int res = 0;
for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--)
if ((res = *su1 - *su2) != 0)
break;
return res;
}

View File

@ -9,18 +9,10 @@
#include <asm/addrspace.h>
#include "efistub.h"
typedef void __noreturn (*kernel_entry_t)(bool efi, unsigned long cmdline,
unsigned long systab);
extern int kernel_asize;
extern int kernel_fsize;
extern int kernel_offset;
extern kernel_entry_t kernel_entry;
efi_status_t check_platform_features(void)
{
return EFI_SUCCESS;
}
extern int kernel_entry;
efi_status_t handle_kernel_image(unsigned long *image_addr,
unsigned long *image_size,
@ -29,74 +21,33 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
efi_loaded_image_t *image,
efi_handle_t image_handle)
{
int nr_pages = round_up(kernel_asize, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
efi_physical_addr_t kernel_addr = EFI_KIMG_PREFERRED_ADDRESS;
efi_status_t status;
unsigned long kernel_addr = 0;
kernel_addr = (unsigned long)&kernel_offset - kernel_offset;
/*
* Allocate space for the kernel image at the preferred offset. This is
* the only location in memory from where we can execute the image, so
* no point in falling back to another allocation.
*/
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
EFI_LOADER_DATA, nr_pages, &kernel_addr);
if (status != EFI_SUCCESS)
return status;
status = efi_relocate_kernel(&kernel_addr, kernel_fsize, kernel_asize,
PHYSADDR(VMLINUX_LOAD_ADDRESS), SZ_2M, 0x0);
*image_addr = kernel_addr;
*image_addr = EFI_KIMG_PREFERRED_ADDRESS;
*image_size = kernel_asize;
memcpy((void *)EFI_KIMG_PREFERRED_ADDRESS,
(void *)&kernel_offset - kernel_offset,
kernel_fsize);
return status;
}
struct exit_boot_struct {
efi_memory_desc_t *runtime_map;
int runtime_entry_count;
};
static efi_status_t exit_boot_func(struct efi_boot_memmap *map, void *priv)
unsigned long kernel_entry_address(void)
{
struct exit_boot_struct *p = priv;
unsigned long base = (unsigned long)&kernel_offset - kernel_offset;
/*
* Update the memory map with virtual addresses. The function will also
* populate @runtime_map with copies of just the EFI_MEMORY_RUNTIME
* entries so that we can pass it straight to SetVirtualAddressMap()
*/
efi_get_virtmap(map->map, map->map_size, map->desc_size,
p->runtime_map, &p->runtime_entry_count);
return EFI_SUCCESS;
}
efi_status_t efi_boot_kernel(void *handle, efi_loaded_image_t *image,
unsigned long kernel_addr, char *cmdline_ptr)
{
kernel_entry_t real_kernel_entry;
struct exit_boot_struct priv;
unsigned long desc_size;
efi_status_t status;
u32 desc_ver;
status = efi_alloc_virtmap(&priv.runtime_map, &desc_size, &desc_ver);
if (status != EFI_SUCCESS) {
efi_err("Unable to retrieve UEFI memory map.\n");
return status;
}
efi_info("Exiting boot services\n");
efi_novamap = false;
status = efi_exit_boot_services(handle, &priv, exit_boot_func);
if (status != EFI_SUCCESS)
return status;
/* Install the new virtual address map */
efi_rt_call(set_virtual_address_map,
priv.runtime_entry_count * desc_size, desc_size,
desc_ver, priv.runtime_map);
/* Config Direct Mapping */
csr_write64(CSR_DMW0_INIT, LOONGARCH_CSR_DMWIN0);
csr_write64(CSR_DMW1_INIT, LOONGARCH_CSR_DMWIN1);
real_kernel_entry = (kernel_entry_t)
((unsigned long)&kernel_entry - kernel_addr + VMLINUX_LOAD_ADDRESS);
real_kernel_entry(true, (unsigned long)cmdline_ptr,
(unsigned long)efi_system_table);
return (unsigned long)&kernel_entry - base + VMLINUX_LOAD_ADDRESS;
}

View File

@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Author: Yun Liu <liuyun@loongson.cn>
* Huacai Chen <chenhuacai@loongson.cn>
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#include <asm/efi.h>
#include <asm/addrspace.h>
#include "efistub.h"
typedef void __noreturn (*kernel_entry_t)(bool efi, unsigned long cmdline,
unsigned long systab);
efi_status_t check_platform_features(void)
{
return EFI_SUCCESS;
}
struct exit_boot_struct {
efi_memory_desc_t *runtime_map;
int runtime_entry_count;
};
static efi_status_t exit_boot_func(struct efi_boot_memmap *map, void *priv)
{
struct exit_boot_struct *p = priv;
/*
* Update the memory map with virtual addresses. The function will also
* populate @runtime_map with copies of just the EFI_MEMORY_RUNTIME
* entries so that we can pass it straight to SetVirtualAddressMap()
*/
efi_get_virtmap(map->map, map->map_size, map->desc_size,
p->runtime_map, &p->runtime_entry_count);
return EFI_SUCCESS;
}
unsigned long __weak kernel_entry_address(void)
{
return *(unsigned long *)(PHYSADDR(VMLINUX_LOAD_ADDRESS) + 8);
}
efi_status_t efi_boot_kernel(void *handle, efi_loaded_image_t *image,
unsigned long kernel_addr, char *cmdline_ptr)
{
kernel_entry_t real_kernel_entry;
struct exit_boot_struct priv;
unsigned long desc_size;
efi_status_t status;
u32 desc_ver;
status = efi_alloc_virtmap(&priv.runtime_map, &desc_size, &desc_ver);
if (status != EFI_SUCCESS) {
efi_err("Unable to retrieve UEFI memory map.\n");
return status;
}
efi_info("Exiting boot services\n");
efi_novamap = false;
status = efi_exit_boot_services(handle, &priv, exit_boot_func);
if (status != EFI_SUCCESS)
return status;
/* Install the new virtual address map */
efi_rt_call(set_virtual_address_map,
priv.runtime_entry_count * desc_size, desc_size,
desc_ver, priv.runtime_map);
/* Config Direct Mapping */
csr_write64(CSR_DMW0_INIT, LOONGARCH_CSR_DMWIN0);
csr_write64(CSR_DMW1_INIT, LOONGARCH_CSR_DMWIN1);
real_kernel_entry = (void *)kernel_entry_address();
real_kernel_entry(true, (unsigned long)cmdline_ptr,
(unsigned long)efi_system_table);
}

View File

@ -0,0 +1,154 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/stdarg.h>
#include <linux/ctype.h>
#include <linux/efi.h>
#include <linux/kernel.h>
#include <linux/printk.h> /* For CONSOLE_LOGLEVEL_* */
#include <asm/efi.h>
#include <asm/setup.h>
#include "efistub.h"
int efi_loglevel = CONSOLE_LOGLEVEL_DEFAULT;
/**
* efi_char16_puts() - Write a UCS-2 encoded string to the console
* @str: UCS-2 encoded string
*/
void efi_char16_puts(efi_char16_t *str)
{
efi_call_proto(efi_table_attr(efi_system_table, con_out),
output_string, str);
}
static
u32 utf8_to_utf32(const u8 **s8)
{
u32 c32;
u8 c0, cx;
size_t clen, i;
c0 = cx = *(*s8)++;
/*
* The position of the most-significant 0 bit gives us the length of
* a multi-octet encoding.
*/
for (clen = 0; cx & 0x80; ++clen)
cx <<= 1;
/*
* If the 0 bit is in position 8, this is a valid single-octet
* encoding. If the 0 bit is in position 7 or positions 1-3, the
* encoding is invalid.
* In either case, we just return the first octet.
*/
if (clen < 2 || clen > 4)
return c0;
/* Get the bits from the first octet. */
c32 = cx >> clen--;
for (i = 0; i < clen; ++i) {
/* Trailing octets must have 10 in most significant bits. */
cx = (*s8)[i] ^ 0x80;
if (cx & 0xc0)
return c0;
c32 = (c32 << 6) | cx;
}
/*
* Check for validity:
* - The character must be in the Unicode range.
* - It must not be a surrogate.
* - It must be encoded using the correct number of octets.
*/
if (c32 > 0x10ffff ||
(c32 & 0xf800) == 0xd800 ||
clen != (c32 >= 0x80) + (c32 >= 0x800) + (c32 >= 0x10000))
return c0;
*s8 += clen;
return c32;
}
/**
* efi_puts() - Write a UTF-8 encoded string to the console
* @str: UTF-8 encoded string
*/
void efi_puts(const char *str)
{
efi_char16_t buf[128];
size_t pos = 0, lim = ARRAY_SIZE(buf);
const u8 *s8 = (const u8 *)str;
u32 c32;
while (*s8) {
if (*s8 == '\n')
buf[pos++] = L'\r';
c32 = utf8_to_utf32(&s8);
if (c32 < 0x10000) {
/* Characters in plane 0 use a single word. */
buf[pos++] = c32;
} else {
/*
* Characters in other planes encode into a surrogate
* pair.
*/
buf[pos++] = (0xd800 - (0x10000 >> 10)) + (c32 >> 10);
buf[pos++] = 0xdc00 + (c32 & 0x3ff);
}
if (*s8 == '\0' || pos >= lim - 2) {
buf[pos] = L'\0';
efi_char16_puts(buf);
pos = 0;
}
}
}
/**
* efi_printk() - Print a kernel message
* @fmt: format string
*
* The first letter of the format string is used to determine the logging level
* of the message. If the level is less then the current EFI logging level, the
* message is suppressed. The message will be truncated to 255 bytes.
*
* Return: number of printed characters
*/
int efi_printk(const char *fmt, ...)
{
char printf_buf[256];
va_list args;
int printed;
int loglevel = printk_get_level(fmt);
switch (loglevel) {
case '0' ... '9':
loglevel -= '0';
break;
default:
/*
* Use loglevel -1 for cases where we just want to print to
* the screen.
*/
loglevel = -1;
break;
}
if (loglevel >= efi_loglevel)
return 0;
if (loglevel >= 0)
efi_puts("EFI stub: ");
fmt = printk_skip_level(fmt);
va_start(args, fmt);
printed = vsnprintf(printf_buf, sizeof(printf_buf), fmt, args);
va_end(args);
efi_puts(printf_buf);
if (printed >= sizeof(printf_buf)) {
efi_puts("[Message truncated]\n");
return -1;
}
return printed;
}

View File

@ -4,7 +4,6 @@
*/
#include <linux/efi.h>
#include <linux/libfdt.h>
#include <asm/efi.h>
#include <asm/sections.h>
@ -12,92 +11,16 @@
#include "efistub.h"
/*
* RISC-V requires the kernel image to placed 2 MB aligned base for 64 bit and
* 4MB for 32 bit.
*/
#ifdef CONFIG_64BIT
#define MIN_KIMG_ALIGN SZ_2M
#else
#define MIN_KIMG_ALIGN SZ_4M
#endif
typedef void __noreturn (*jump_kernel_func)(unsigned long, unsigned long);
static unsigned long hartid;
static int get_boot_hartid_from_fdt(void)
unsigned long stext_offset(void)
{
const void *fdt;
int chosen_node, len;
const void *prop;
fdt = get_efi_config_table(DEVICE_TREE_GUID);
if (!fdt)
return -EINVAL;
chosen_node = fdt_path_offset(fdt, "/chosen");
if (chosen_node < 0)
return -EINVAL;
prop = fdt_getprop((void *)fdt, chosen_node, "boot-hartid", &len);
if (!prop)
return -EINVAL;
if (len == sizeof(u32))
hartid = (unsigned long) fdt32_to_cpu(*(fdt32_t *)prop);
else if (len == sizeof(u64))
hartid = (unsigned long) fdt64_to_cpu(__get_unaligned_t(fdt64_t, prop));
else
return -EINVAL;
return 0;
}
static efi_status_t get_boot_hartid_from_efi(void)
{
efi_guid_t boot_protocol_guid = RISCV_EFI_BOOT_PROTOCOL_GUID;
struct riscv_efi_boot_protocol *boot_protocol;
efi_status_t status;
status = efi_bs_call(locate_protocol, &boot_protocol_guid, NULL,
(void **)&boot_protocol);
if (status != EFI_SUCCESS)
return status;
return efi_call_proto(boot_protocol, get_boot_hartid, &hartid);
}
efi_status_t check_platform_features(void)
{
efi_status_t status;
int ret;
status = get_boot_hartid_from_efi();
if (status != EFI_SUCCESS) {
ret = get_boot_hartid_from_fdt();
if (ret) {
efi_err("Failed to get boot hartid!\n");
return EFI_UNSUPPORTED;
}
}
return EFI_SUCCESS;
}
void __noreturn efi_enter_kernel(unsigned long entrypoint, unsigned long fdt,
unsigned long fdt_size)
{
unsigned long stext_offset = _start_kernel - _start;
unsigned long kernel_entry = entrypoint + stext_offset;
jump_kernel_func jump_kernel = (jump_kernel_func)kernel_entry;
/*
* Jump to real kernel here with following constraints.
* 1. MMU should be disabled.
* 2. a0 should contain hartid
* 3. a1 should DT address
* When built as part of the kernel, the EFI stub cannot branch to the
* kernel proper via the image header, as the PE/COFF header is
* strictly not part of the in-memory presentation of the image, only
* of the file representation. So instead, we need to jump to the
* actual entrypoint in the .text region of the image.
*/
csr_write(CSR_SATP, 0);
jump_kernel(hartid, fdt);
return _start_kernel - _start;
}
efi_status_t handle_kernel_image(unsigned long *image_addr,
@ -125,9 +48,10 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
* lowest possible memory region as long as the address and size meets
* the alignment constraints.
*/
preferred_addr = MIN_KIMG_ALIGN;
preferred_addr = EFI_KIMG_PREFERRED_ADDRESS;
status = efi_relocate_kernel(image_addr, kernel_size, *image_size,
preferred_addr, MIN_KIMG_ALIGN, 0x0);
preferred_addr, efi_get_kimg_min_align(),
0x0);
if (status != EFI_SUCCESS) {
efi_err("Failed to relocate kernel\n");

View File

@ -0,0 +1,98 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020 Western Digital Corporation or its affiliates.
*/
#include <linux/efi.h>
#include <linux/libfdt.h>
#include <asm/efi.h>
#include <asm/unaligned.h>
#include "efistub.h"
typedef void __noreturn (*jump_kernel_func)(unsigned long, unsigned long);
static unsigned long hartid;
static int get_boot_hartid_from_fdt(void)
{
const void *fdt;
int chosen_node, len;
const void *prop;
fdt = get_efi_config_table(DEVICE_TREE_GUID);
if (!fdt)
return -EINVAL;
chosen_node = fdt_path_offset(fdt, "/chosen");
if (chosen_node < 0)
return -EINVAL;
prop = fdt_getprop((void *)fdt, chosen_node, "boot-hartid", &len);
if (!prop)
return -EINVAL;
if (len == sizeof(u32))
hartid = (unsigned long) fdt32_to_cpu(*(fdt32_t *)prop);
else if (len == sizeof(u64))
hartid = (unsigned long) fdt64_to_cpu(__get_unaligned_t(fdt64_t, prop));
else
return -EINVAL;
return 0;
}
static efi_status_t get_boot_hartid_from_efi(void)
{
efi_guid_t boot_protocol_guid = RISCV_EFI_BOOT_PROTOCOL_GUID;
struct riscv_efi_boot_protocol *boot_protocol;
efi_status_t status;
status = efi_bs_call(locate_protocol, &boot_protocol_guid, NULL,
(void **)&boot_protocol);
if (status != EFI_SUCCESS)
return status;
return efi_call_proto(boot_protocol, get_boot_hartid, &hartid);
}
efi_status_t check_platform_features(void)
{
efi_status_t status;
int ret;
status = get_boot_hartid_from_efi();
if (status != EFI_SUCCESS) {
ret = get_boot_hartid_from_fdt();
if (ret) {
efi_err("Failed to get boot hartid!\n");
return EFI_UNSUPPORTED;
}
}
return EFI_SUCCESS;
}
unsigned long __weak stext_offset(void)
{
/*
* This fallback definition is used by the EFI zboot stub, which loads
* the entire image so it can branch via the image header at offset #0.
*/
return 0;
}
void __noreturn efi_enter_kernel(unsigned long entrypoint, unsigned long fdt,
unsigned long fdt_size)
{
unsigned long kernel_entry = entrypoint + stext_offset();
jump_kernel_func jump_kernel = (jump_kernel_func)kernel_entry;
/*
* Jump to real kernel here with following constraints.
* 1. MMU should be disabled.
* 2. a0 should contain hartid
* 3. a1 should DT address
*/
csr_write(CSR_SATP, 0);
jump_kernel(hartid, fdt);
}

View File

@ -0,0 +1,56 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/efi.h>
#include <asm/efi.h>
#include "efistub.h"
/*
* There are two ways of populating the core kernel's struct screen_info via the stub:
* - using a configuration table, like below, which relies on the EFI init code
* to locate the table and copy the contents;
* - by linking directly to the core kernel's copy of the global symbol.
*
* The latter is preferred because it makes the EFIFB earlycon available very
* early, but it only works if the EFI stub is part of the core kernel image
* itself. The zboot decompressor can only use the configuration table
* approach.
*
* In order to support both methods from the same build of the EFI stub
* library, provide this dummy global definition of struct screen_info. If it
* is required to satisfy a link dependency, it means we need to override the
* __weak alloc and free methods with the ones below, and those will be pulled
* in as well.
*/
struct screen_info screen_info;
static efi_guid_t screen_info_guid = LINUX_EFI_SCREEN_INFO_TABLE_GUID;
struct screen_info *alloc_screen_info(void)
{
struct screen_info *si;
efi_status_t status;
status = efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY,
sizeof(*si), (void **)&si);
if (status != EFI_SUCCESS)
return NULL;
status = efi_bs_call(install_configuration_table,
&screen_info_guid, si);
if (status == EFI_SUCCESS)
return si;
efi_bs_call(free_pool, si);
return NULL;
}
void free_screen_info(struct screen_info *si)
{
if (!si)
return;
efi_bs_call(install_configuration_table, &screen_info_guid, NULL);
efi_bs_call(free_pool, si);
}

View File

@ -11,7 +11,37 @@
#include <linux/types.h>
#include <linux/string.h>
#ifndef __HAVE_ARCH_STRSTR
#ifndef EFI_HAVE_STRLEN
/**
* strlen - Find the length of a string
* @s: The string to be sized
*/
size_t strlen(const char *s)
{
const char *sc;
for (sc = s; *sc != '\0'; ++sc)
/* nothing */;
return sc - s;
}
#endif
#ifndef EFI_HAVE_STRNLEN
/**
* strnlen - Find the length of a length-limited string
* @s: The string to be sized
* @count: The maximum number of bytes to search
*/
size_t strnlen(const char *s, size_t count)
{
const char *sc;
for (sc = s; count-- && *sc != '\0'; ++sc)
/* nothing */;
return sc - s;
}
#endif
/**
* strstr - Find the first substring in a %NUL terminated string
* @s1: The string to be searched
@ -33,9 +63,29 @@ char *strstr(const char *s1, const char *s2)
}
return NULL;
}
#ifndef EFI_HAVE_STRCMP
/**
* strcmp - Compare two strings
* @cs: One string
* @ct: Another string
*/
int strcmp(const char *cs, const char *ct)
{
unsigned char c1, c2;
while (1) {
c1 = *cs++;
c2 = *ct++;
if (c1 != c2)
return c1 < c2 ? -1 : 1;
if (!c1)
break;
}
return 0;
}
#endif
#ifndef __HAVE_ARCH_STRNCMP
/**
* strncmp - Compare two length-limited strings
* @cs: One string
@ -57,7 +107,6 @@ int strncmp(const char *cs, const char *ct, size_t count)
}
return 0;
}
#endif
/* Works only for digits and letters, but small and fast */
#define TOLOWER(x) ((x) | 0x20)
@ -113,3 +162,43 @@ long simple_strtol(const char *cp, char **endp, unsigned int base)
return simple_strtoull(cp, endp, base);
}
#ifdef CONFIG_EFI_PARAMS_FROM_FDT
#ifndef EFI_HAVE_STRRCHR
/**
* strrchr - Find the last occurrence of a character in a string
* @s: The string to be searched
* @c: The character to search for
*/
char *strrchr(const char *s, int c)
{
const char *last = NULL;
do {
if (*s == (char)c)
last = s;
} while (*s++);
return (char *)last;
}
#endif
#ifndef EFI_HAVE_MEMCHR
/**
* memchr - Find a character in an area of memory.
* @s: The memory area
* @c: The byte to search for
* @n: The size of the area.
*
* returns the address of the first occurrence of @c, or %NULL
* if @c is not found
*/
void *memchr(const void *s, int c, size_t n)
{
const unsigned char *p = s;
while (n-- != 0) {
if ((unsigned char)c == *p++) {
return (void *)(p - 1);
}
}
return NULL;
}
#endif
#endif

View File

@ -17,7 +17,7 @@ __efistub_efi_zboot_header:
.long MZ_MAGIC
.ascii "zimg" // image type
.long __efistub__gzdata_start - .Ldoshdr // payload offset
.long __efistub__gzdata_size - ZBOOT_SIZE_LEN // payload size
.long __efistub__gzdata_size - 12 // payload size
.long 0, 0 // reserved
.asciz COMP_TYPE // compression type
.org .Ldoshdr + 0x3c

View File

@ -32,271 +32,116 @@ static unsigned long free_mem_ptr, free_mem_end_ptr;
extern char efi_zboot_header[];
extern char _gzdata_start[], _gzdata_end[];
static void log(efi_char16_t str[])
{
efi_call_proto(efi_table_attr(efi_system_table, con_out),
output_string, L"EFI decompressor: ");
efi_call_proto(efi_table_attr(efi_system_table, con_out),
output_string, str);
efi_call_proto(efi_table_attr(efi_system_table, con_out),
output_string, L"\n");
}
static void error(char *x)
{
log(L"error() called from decompressor library\n");
efi_err("EFI decompressor: %s\n", x);
}
// Local version to avoid pulling in memcmp()
static bool guids_eq(const efi_guid_t *a, const efi_guid_t *b)
static unsigned long alloc_preferred_address(unsigned long alloc_size)
{
const u32 *l = (u32 *)a;
const u32 *r = (u32 *)b;
#ifdef EFI_KIMG_PREFERRED_ADDRESS
efi_physical_addr_t efi_addr = EFI_KIMG_PREFERRED_ADDRESS;
return l[0] == r[0] && l[1] == r[1] && l[2] == r[2] && l[3] == r[3];
if (efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
alloc_size / EFI_PAGE_SIZE, &efi_addr) == EFI_SUCCESS)
return efi_addr;
#endif
return ULONG_MAX;
}
static efi_status_t __efiapi
load_file(efi_load_file_protocol_t *this, efi_device_path_protocol_t *rem,
bool boot_policy, unsigned long *bufsize, void *buffer)
void __weak efi_cache_sync_image(unsigned long image_base,
unsigned long alloc_size,
unsigned long code_size)
{
unsigned long compressed_size = _gzdata_end - _gzdata_start;
struct efi_vendor_dev_path *vendor_dp;
bool decompress = false;
unsigned long size;
int ret;
if (rem == NULL || bufsize == NULL)
return EFI_INVALID_PARAMETER;
if (boot_policy)
return EFI_UNSUPPORTED;
// Look for our vendor media device node in the remaining file path
if (rem->type == EFI_DEV_MEDIA &&
rem->sub_type == EFI_DEV_MEDIA_VENDOR) {
vendor_dp = container_of(rem, struct efi_vendor_dev_path, header);
if (!guids_eq(&vendor_dp->vendorguid, &LINUX_EFI_ZBOOT_MEDIA_GUID))
return EFI_NOT_FOUND;
decompress = true;
rem = (void *)(vendor_dp + 1);
}
if (rem->type != EFI_DEV_END_PATH ||
rem->sub_type != EFI_DEV_END_ENTIRE)
return EFI_NOT_FOUND;
// The uncompressed size of the payload is appended to the raw bit
// stream, and may therefore appear misaligned in memory
size = decompress ? get_unaligned_le32(_gzdata_end - 4)
: compressed_size;
if (buffer == NULL || *bufsize < size) {
*bufsize = size;
return EFI_BUFFER_TOO_SMALL;
}
if (decompress) {
ret = __decompress(_gzdata_start, compressed_size, NULL, NULL,
buffer, size, NULL, error);
if (ret < 0) {
log(L"Decompression failed");
return EFI_DEVICE_ERROR;
}
} else {
memcpy(buffer, _gzdata_start, compressed_size);
}
return EFI_SUCCESS;
}
// Return the length in bytes of the device path up to the first end node.
static int device_path_length(const efi_device_path_protocol_t *dp)
{
int len = 0;
while (dp->type != EFI_DEV_END_PATH) {
len += dp->length;
dp = (void *)((u8 *)dp + dp->length);
}
return len;
}
static void append_rel_offset_node(efi_device_path_protocol_t **dp,
unsigned long start, unsigned long end)
{
struct efi_rel_offset_dev_path *rodp = (void *)*dp;
rodp->header.type = EFI_DEV_MEDIA;
rodp->header.sub_type = EFI_DEV_MEDIA_REL_OFFSET;
rodp->header.length = sizeof(struct efi_rel_offset_dev_path);
rodp->reserved = 0;
rodp->starting_offset = start;
rodp->ending_offset = end;
*dp = (void *)(rodp + 1);
}
static void append_ven_media_node(efi_device_path_protocol_t **dp,
efi_guid_t *guid)
{
struct efi_vendor_dev_path *vmdp = (void *)*dp;
vmdp->header.type = EFI_DEV_MEDIA;
vmdp->header.sub_type = EFI_DEV_MEDIA_VENDOR;
vmdp->header.length = sizeof(struct efi_vendor_dev_path);
vmdp->vendorguid = *guid;
*dp = (void *)(vmdp + 1);
}
static void append_end_node(efi_device_path_protocol_t **dp)
{
(*dp)->type = EFI_DEV_END_PATH;
(*dp)->sub_type = EFI_DEV_END_ENTIRE;
(*dp)->length = sizeof(struct efi_generic_dev_path);
++*dp;
// Provided by the arch to perform the cache maintenance necessary for
// executable code loaded into memory to be safe for execution.
}
asmlinkage efi_status_t __efiapi
efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab)
{
struct efi_mem_mapped_dev_path mmdp = {
.header.type = EFI_DEV_HW,
.header.sub_type = EFI_DEV_MEM_MAPPED,
.header.length = sizeof(struct efi_mem_mapped_dev_path)
};
efi_device_path_protocol_t *parent_dp, *dpp, *lf2_dp, *li_dp;
efi_load_file2_protocol_t zboot_load_file2;
efi_loaded_image_t *parent, *child;
unsigned long exit_data_size;
efi_handle_t child_handle;
efi_handle_t zboot_handle;
efi_char16_t *exit_data;
unsigned long compressed_size = _gzdata_end - _gzdata_start;
unsigned long image_base, alloc_size, code_size;
efi_loaded_image_t *image;
efi_status_t status;
void *dp_alloc;
int dp_len;
char *cmdline_ptr;
int ret;
WRITE_ONCE(efi_system_table, systab);
free_mem_ptr = (unsigned long)&zboot_heap;
free_mem_end_ptr = free_mem_ptr + sizeof(zboot_heap);
exit_data = NULL;
exit_data_size = 0;
status = efi_bs_call(handle_protocol, handle,
&LOADED_IMAGE_PROTOCOL_GUID, (void **)&parent);
&LOADED_IMAGE_PROTOCOL_GUID, (void **)&image);
if (status != EFI_SUCCESS) {
log(L"Failed to locate parent's loaded image protocol");
error("Failed to locate parent's loaded image protocol");
return status;
}
status = efi_bs_call(handle_protocol, handle,
&LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID,
(void **)&parent_dp);
if (status != EFI_SUCCESS || parent_dp == NULL) {
// Create a MemoryMapped() device path node to describe
// the parent image if no device path was provided.
mmdp.memory_type = parent->image_code_type;
mmdp.starting_addr = (unsigned long)parent->image_base;
mmdp.ending_addr = (unsigned long)parent->image_base +
parent->image_size - 1;
parent_dp = &mmdp.header;
dp_len = sizeof(mmdp);
} else {
dp_len = device_path_length(parent_dp);
}
// Allocate some pool memory for device path protocol data
status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
2 * (dp_len + sizeof(struct efi_rel_offset_dev_path) +
sizeof(struct efi_generic_dev_path)) +
sizeof(struct efi_vendor_dev_path),
(void **)&dp_alloc);
if (status != EFI_SUCCESS) {
log(L"Failed to allocate device path pool memory");
status = efi_handle_cmdline(image, &cmdline_ptr);
if (status != EFI_SUCCESS)
return status;
efi_info("Decompressing Linux Kernel...\n");
// SizeOfImage from the compressee's PE/COFF header
alloc_size = round_up(get_unaligned_le32(_gzdata_end - 4),
EFI_ALLOC_ALIGN);
// SizeOfHeaders and SizeOfCode from the compressee's PE/COFF header
code_size = get_unaligned_le32(_gzdata_end - 8) +
get_unaligned_le32(_gzdata_end - 12);
// If the architecture has a preferred address for the image,
// try that first.
image_base = alloc_preferred_address(alloc_size);
if (image_base == ULONG_MAX) {
unsigned long min_kimg_align = efi_get_kimg_min_align();
u32 seed = U32_MAX;
if (!IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {
// Setting the random seed to 0x0 is the same as
// allocating as low as possible
seed = 0;
} else if (efi_nokaslr) {
efi_info("KASLR disabled on kernel command line\n");
} else {
status = efi_get_random_bytes(sizeof(seed), (u8 *)&seed);
if (status == EFI_NOT_FOUND) {
efi_info("EFI_RNG_PROTOCOL unavailable\n");
efi_nokaslr = true;
} else if (status != EFI_SUCCESS) {
efi_err("efi_get_random_bytes() failed (0x%lx)\n",
status);
efi_nokaslr = true;
}
}
status = efi_random_alloc(alloc_size, min_kimg_align, &image_base,
seed, EFI_LOADER_CODE);
if (status != EFI_SUCCESS) {
efi_err("Failed to allocate memory\n");
goto free_cmdline;
}
}
// Create a device path describing the compressed payload in this image
// <...parent_dp...>/Offset(<start>, <end>)
lf2_dp = memcpy(dp_alloc, parent_dp, dp_len);
dpp = (void *)((u8 *)lf2_dp + dp_len);
append_rel_offset_node(&dpp,
(unsigned long)(_gzdata_start - efi_zboot_header),
(unsigned long)(_gzdata_end - efi_zboot_header - 1));
append_end_node(&dpp);
// Create a device path describing the decompressed payload in this image
// <...parent_dp...>/Offset(<start>, <end>)/VenMedia(ZBOOT_MEDIA_GUID)
dp_len += sizeof(struct efi_rel_offset_dev_path);
li_dp = memcpy(dpp, lf2_dp, dp_len);
dpp = (void *)((u8 *)li_dp + dp_len);
append_ven_media_node(&dpp, &LINUX_EFI_ZBOOT_MEDIA_GUID);
append_end_node(&dpp);
zboot_handle = NULL;
zboot_load_file2.load_file = load_file;
status = efi_bs_call(install_multiple_protocol_interfaces,
&zboot_handle,
&EFI_DEVICE_PATH_PROTOCOL_GUID, lf2_dp,
&EFI_LOAD_FILE2_PROTOCOL_GUID, &zboot_load_file2,
NULL);
if (status != EFI_SUCCESS) {
log(L"Failed to install LoadFile2 protocol and device path");
goto free_dpalloc;
// Decompress the payload into the newly allocated buffer.
ret = __decompress(_gzdata_start, compressed_size, NULL, NULL,
(void *)image_base, alloc_size, NULL, error);
if (ret < 0) {
error("Decompression failed");
status = EFI_DEVICE_ERROR;
goto free_image;
}
status = efi_bs_call(load_image, false, handle, li_dp, NULL, 0,
&child_handle);
if (status != EFI_SUCCESS) {
log(L"Failed to load image");
goto uninstall_lf2;
}
efi_cache_sync_image(image_base, alloc_size, code_size);
status = efi_bs_call(handle_protocol, child_handle,
&LOADED_IMAGE_PROTOCOL_GUID, (void **)&child);
if (status != EFI_SUCCESS) {
log(L"Failed to locate child's loaded image protocol");
goto unload_image;
}
status = efi_stub_common(handle, image, image_base, cmdline_ptr);
// Copy the kernel command line
child->load_options = parent->load_options;
child->load_options_size = parent->load_options_size;
status = efi_bs_call(start_image, child_handle, &exit_data_size,
&exit_data);
if (status != EFI_SUCCESS) {
log(L"StartImage() returned with error");
if (exit_data_size > 0)
log(exit_data);
// If StartImage() returns EFI_SECURITY_VIOLATION, the image is
// not unloaded so we need to do it by hand.
if (status == EFI_SECURITY_VIOLATION)
unload_image:
efi_bs_call(unload_image, child_handle);
}
uninstall_lf2:
efi_bs_call(uninstall_multiple_protocol_interfaces,
zboot_handle,
&EFI_DEVICE_PATH_PROTOCOL_GUID, lf2_dp,
&EFI_LOAD_FILE2_PROTOCOL_GUID, &zboot_load_file2,
NULL);
free_dpalloc:
efi_bs_call(free_pool, dp_alloc);
efi_bs_call(exit, handle, status, exit_data_size, exit_data);
// Free ExitData in case Exit() returned with a failure code,
// but return the original status code.
log(L"Exit() returned with failure code");
if (exit_data != NULL)
efi_bs_call(free_pool, exit_data);
free_image:
efi_free(alloc_size, image_base);
free_cmdline:
efi_bs_call(free_pool, cmdline_ptr);
return status;
}

View File

@ -404,7 +404,7 @@ void efi_native_runtime_setup(void);
* structure that was populated by the stub based on the GOP protocol instance
* associated with ConOut
*/
#define LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID EFI_GUID(0xe03fc20a, 0x85dc, 0x406e, 0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95)
#define LINUX_EFI_SCREEN_INFO_TABLE_GUID EFI_GUID(0xe03fc20a, 0x85dc, 0x406e, 0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95)
#define LINUX_EFI_ARM_CPU_STATE_TABLE_GUID EFI_GUID(0xef79e4aa, 0x3c3d, 0x4989, 0xb9, 0x02, 0x07, 0xa9, 0x43, 0xe5, 0x50, 0xd2)
#define LINUX_EFI_LOADER_ENTRY_GUID EFI_GUID(0x4a67b082, 0x0a4c, 0x41cf, 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f)
#define LINUX_EFI_RANDOM_SEED_TABLE_GUID EFI_GUID(0x1ce1e5bc, 0x7ceb, 0x42f2, 0x81, 0xe5, 0x8a, 0xad, 0xf1, 0x80, 0xf5, 0x7b)
@ -412,7 +412,6 @@ void efi_native_runtime_setup(void);
#define LINUX_EFI_TPM_FINAL_LOG_GUID EFI_GUID(0x1e2ed096, 0x30e2, 0x4254, 0xbd, 0x89, 0x86, 0x3b, 0xbe, 0xf8, 0x23, 0x25)
#define LINUX_EFI_MEMRESERVE_TABLE_GUID EFI_GUID(0x888eb0c6, 0x8ede, 0x4ff5, 0xa8, 0xf0, 0x9a, 0xee, 0x5c, 0xb9, 0x77, 0xc2)
#define LINUX_EFI_INITRD_MEDIA_GUID EFI_GUID(0x5568e427, 0x68fc, 0x4f3d, 0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68)
#define LINUX_EFI_ZBOOT_MEDIA_GUID EFI_GUID(0xe565a30d, 0x47da, 0x4dbd, 0xb3, 0x54, 0x9b, 0xb5, 0xc8, 0x4f, 0x8b, 0xe2)
#define LINUX_EFI_MOK_VARIABLE_TABLE_GUID EFI_GUID(0xc451ed2b, 0x9694, 0x45d3, 0xba, 0xba, 0xed, 0x9f, 0x89, 0x88, 0xa3, 0x89)
#define LINUX_EFI_COCO_SECRET_AREA_GUID EFI_GUID(0xadf956ad, 0xe98c, 0x484c, 0xae, 0x11, 0xb5, 0x1c, 0x7d, 0x33, 0x64, 0x47)
#define LINUX_EFI_BOOT_MEMMAP_GUID EFI_GUID(0x800f683f, 0xd08b, 0x423a, 0xa2, 0x93, 0x96, 0x5c, 0x3c, 0x6f, 0xe2, 0xb4)