mirror of
https://github.com/torvalds/linux.git
synced 2024-11-15 08:31:55 +00:00
Merge tag 'efi-zboot-direct-for-v6.2' into efi/next
This commit is contained in:
commit
977122898e
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
{},
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
67
drivers/firmware/efi/libstub/arm64-entry.S
Normal file
67
drivers/firmware/efi/libstub/arm64-entry.S
Normal 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)
|
@ -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;
|
||||
}
|
||||
|
76
drivers/firmware/efi/libstub/arm64.c
Normal file
76
drivers/firmware/efi/libstub/arm64.c
Normal 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();
|
||||
}
|
65
drivers/firmware/efi/libstub/efi-stub-entry.c
Normal file
65
drivers/firmware/efi/libstub/efi-stub-entry.c
Normal 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;
|
||||
}
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
80
drivers/firmware/efi/libstub/loongarch.c
Normal file
80
drivers/firmware/efi/libstub/loongarch.c
Normal 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);
|
||||
}
|
154
drivers/firmware/efi/libstub/printk.c
Normal file
154
drivers/firmware/efi/libstub/printk.c
Normal 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;
|
||||
}
|
@ -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");
|
||||
|
98
drivers/firmware/efi/libstub/riscv.c
Normal file
98
drivers/firmware/efi/libstub/riscv.c
Normal 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);
|
||||
}
|
56
drivers/firmware/efi/libstub/screen_info.c
Normal file
56
drivers/firmware/efi/libstub/screen_info.c
Normal 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);
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user