Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

arm64/efi: isolate EFI stub from the kernel proper

Since arm64 does not use a builtin decompressor, the EFI stub is built
into the kernel proper. So far, this has been working fine, but actually,
since the stub is in fact a PE/COFF relocatable binary that is executed
at an unknown offset in the 1:1 mapping provided by the UEFI firmware, we
should not be seamlessly sharing code with the kernel proper, which is a
position dependent executable linked at a high virtual offset.

So instead, separate the contents of libstub and its dependencies, by
putting them into their own namespace by prefixing all of its symbols
with __efistub. This way, we have tight control over what parts of the
kernel proper are referenced by the stub.

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Matt Fleming <matt.fleming@intel.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>

authored by

Ard Biesheuvel and committed by
Catalin Marinas
e8f3010f 20791846

+139 -20
+10 -2
arch/arm64/kernel/Makefile
··· 20 20 cpufeature.o alternative.o cacheinfo.o \ 21 21 smp.o smp_spin_table.o topology.o 22 22 23 + stub-obj := efi-stub.o efi-entry.o 24 + extra-y := $(stub-obj) 25 + stub-obj := $(patsubst %.o,%.stub.o,$(stub-obj)) 26 + 27 + OBJCOPYFLAGS := --prefix-symbols=__efistub_ 28 + $(obj)/%.stub.o: $(obj)/%.o FORCE 29 + $(call if_changed,objcopy) 30 + 23 31 arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ 24 32 sys_compat.o entry32.o \ 25 33 ../../arm/kernel/opcodes.o ··· 40 32 arm64-obj-$(CONFIG_CPU_IDLE) += cpuidle.o 41 33 arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o 42 34 arm64-obj-$(CONFIG_KGDB) += kgdb.o 43 - arm64-obj-$(CONFIG_EFI) += efi.o efi-stub.o efi-entry.o 35 + arm64-obj-$(CONFIG_EFI) += efi.o $(stub-obj) 44 36 arm64-obj-$(CONFIG_PCI) += pci.o 45 37 arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o 46 38 arm64-obj-$(CONFIG_ACPI) += acpi.o ··· 48 40 obj-y += $(arm64-obj-y) vdso/ 49 41 obj-m += $(arm64-obj-m) 50 42 head-y := head.o 51 - extra-y := $(head-y) vmlinux.lds 43 + extra-y += $(head-y) vmlinux.lds 52 44 53 45 # vDSO - this must be built first to generate the symbol offsets 54 46 $(call objectify,$(arm64-obj-y)): $(obj)/vdso/vdso-offsets.h
+5 -5
arch/arm64/kernel/efi-entry.S
··· 29 29 * we want to be. The kernel image wants to be placed at TEXT_OFFSET 30 30 * from start of RAM. 31 31 */ 32 - ENTRY(efi_stub_entry) 32 + ENTRY(entry) 33 33 /* 34 34 * Create a stack frame to save FP/LR with extra space 35 35 * for image_addr variable passed to efi_entry(). ··· 86 86 * entries for the VA range of the current image, so no maintenance is 87 87 * necessary. 88 88 */ 89 - adr x0, efi_stub_entry 90 - adr x1, efi_stub_entry_end 89 + adr x0, entry 90 + adr x1, entry_end 91 91 sub x1, x1, x0 92 92 bl __flush_dcache_area 93 93 ··· 120 120 ldp x29, x30, [sp], #32 121 121 ret 122 122 123 - efi_stub_entry_end: 124 - ENDPROC(efi_stub_entry) 123 + entry_end: 124 + ENDPROC(entry)
+7 -7
arch/arm64/kernel/head.S
··· 120 120 #endif 121 121 122 122 #ifdef CONFIG_EFI 123 - .globl stext_offset 124 - .set stext_offset, stext - efi_head 123 + .globl __efistub_stext_offset 124 + .set __efistub_stext_offset, stext - efi_head 125 125 .align 3 126 126 pe_header: 127 127 .ascii "PE" ··· 144 144 .long _end - stext // SizeOfCode 145 145 .long 0 // SizeOfInitializedData 146 146 .long 0 // SizeOfUninitializedData 147 - .long efi_stub_entry - efi_head // AddressOfEntryPoint 148 - .long stext_offset // BaseOfCode 147 + .long __efistub_entry - efi_head // AddressOfEntryPoint 148 + .long __efistub_stext_offset // BaseOfCode 149 149 150 150 extra_header_fields: 151 151 .quad 0 // ImageBase ··· 162 162 .long _end - efi_head // SizeOfImage 163 163 164 164 // Everything before the kernel image is considered part of the header 165 - .long stext_offset // SizeOfHeaders 165 + .long __efistub_stext_offset // SizeOfHeaders 166 166 .long 0 // CheckSum 167 167 .short 0xa // Subsystem (EFI application) 168 168 .short 0 // DllCharacteristics ··· 207 207 .byte 0 208 208 .byte 0 // end of 0 padding of section name 209 209 .long _end - stext // VirtualSize 210 - .long stext_offset // VirtualAddress 210 + .long __efistub_stext_offset // VirtualAddress 211 211 .long _edata - stext // SizeOfRawData 212 - .long stext_offset // PointerToRawData 212 + .long __efistub_stext_offset // PointerToRawData 213 213 214 214 .long 0 // PointerToRelocations (0 for executables) 215 215 .long 0 // PointerToLineNumbers (0 for executables)
+27
arch/arm64/kernel/image.h
··· 59 59 _kernel_offset_le = DATA_LE64(TEXT_OFFSET); \ 60 60 _kernel_flags_le = DATA_LE64(__HEAD_FLAGS); 61 61 62 + #ifdef CONFIG_EFI 63 + 64 + /* 65 + * The EFI stub has its own symbol namespace prefixed by __efistub_, to 66 + * isolate it from the kernel proper. The following symbols are legally 67 + * accessed by the stub, so provide some aliases to make them accessible. 68 + * Only include data symbols here, or text symbols of functions that are 69 + * guaranteed to be safe when executed at another offset than they were 70 + * linked at. The routines below are all implemented in assembler in a 71 + * position independent manner 72 + */ 73 + __efistub_memcmp = __pi_memcmp; 74 + __efistub_memchr = __pi_memchr; 75 + __efistub_memcpy = __pi_memcpy; 76 + __efistub_memmove = __pi_memmove; 77 + __efistub_memset = __pi_memset; 78 + __efistub_strlen = __pi_strlen; 79 + __efistub_strcmp = __pi_strcmp; 80 + __efistub_strncmp = __pi_strncmp; 81 + __efistub___flush_dcache_area = __pi___flush_dcache_area; 82 + 83 + __efistub__text = _text; 84 + __efistub__end = _end; 85 + __efistub__edata = _edata; 86 + 87 + #endif 88 + 62 89 #endif /* __ASM_IMAGE_H */
+33 -6
drivers/firmware/efi/libstub/Makefile
··· 14 14 cflags-$(CONFIG_ARM) := $(subst -pg,,$(KBUILD_CFLAGS)) \ 15 15 -fno-builtin -fpic -mno-single-pic-base 16 16 17 + cflags-$(CONFIG_EFI_ARMSTUB) += -I$(srctree)/scripts/dtc/libfdt 18 + 17 19 KBUILD_CFLAGS := $(cflags-y) \ 18 20 $(call cc-option,-ffreestanding) \ 19 21 $(call cc-option,-fno-stack-protector) ··· 24 22 KASAN_SANITIZE := n 25 23 26 24 lib-y := efi-stub-helper.o 27 - lib-$(CONFIG_EFI_ARMSTUB) += arm-stub.o fdt.o 25 + 26 + # include the stub's generic dependencies from lib/ when building for ARM/arm64 27 + arm-deps := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c sort.c 28 + 29 + $(obj)/lib-%.o: $(srctree)/lib/%.c FORCE 30 + $(call if_changed_rule,cc_o_c) 31 + 32 + lib-$(CONFIG_EFI_ARMSTUB) += arm-stub.o fdt.o string.o \ 33 + $(patsubst %.c,lib-%.o,$(arm-deps)) 28 34 29 35 # 30 36 # arm64 puts the stub in the kernel proper, which will unnecessarily retain all ··· 40 30 # So let's apply the __init annotations at the section level, by prefixing 41 31 # the section names directly. This will ensure that even all the inline string 42 32 # literals are covered. 33 + # The fact that the stub and the kernel proper are essentially the same binary 34 + # also means that we need to be extra careful to make sure that the stub does 35 + # not rely on any absolute symbol references, considering that the virtual 36 + # kernel mapping that the linker uses is not active yet when the stub is 37 + # executing. So build all C dependencies of the EFI stub into libstub, and do 38 + # a verification pass to see if any absolute relocations exist in any of the 39 + # object files. 43 40 # 44 - extra-$(CONFIG_ARM64) := $(lib-y) 45 - lib-$(CONFIG_ARM64) := $(patsubst %.o,%.init.o,$(lib-y)) 41 + extra-$(CONFIG_EFI_ARMSTUB) := $(lib-y) 42 + lib-$(CONFIG_EFI_ARMSTUB) := $(patsubst %.o,%.stub.o,$(lib-y)) 46 43 47 - OBJCOPYFLAGS := --prefix-alloc-sections=.init 48 - $(obj)/%.init.o: $(obj)/%.o FORCE 49 - $(call if_changed,objcopy) 44 + STUBCOPY_FLAGS-y := -R .debug* -R *ksymtab* 45 + STUBCOPY_FLAGS-$(CONFIG_ARM64) += --prefix-alloc-sections=.init \ 46 + --prefix-symbols=__efistub_ 47 + STUBCOPY_RELOC-$(CONFIG_ARM64) := R_AARCH64_ABS 48 + 49 + $(obj)/%.stub.o: $(obj)/%.o FORCE 50 + $(call if_changed,stubcopy) 51 + 52 + quiet_cmd_stubcopy = STUBCPY $@ 53 + cmd_stubcopy = if $(OBJCOPY) $(STUBCOPY_FLAGS-y) $< $@; then \ 54 + $(OBJDUMP) -r $@ | grep $(STUBCOPY_RELOC-y) \ 55 + && (echo >&2 "$@: absolute symbol references not allowed in the EFI stub"; \ 56 + rm -f $@; /bin/false); else /bin/false; fi
+57
drivers/firmware/efi/libstub/string.c
··· 1 + /* 2 + * Taken from: 3 + * linux/lib/string.c 4 + * 5 + * Copyright (C) 1991, 1992 Linus Torvalds 6 + */ 7 + 8 + #include <linux/types.h> 9 + #include <linux/string.h> 10 + 11 + #ifndef __HAVE_ARCH_STRSTR 12 + /** 13 + * strstr - Find the first substring in a %NUL terminated string 14 + * @s1: The string to be searched 15 + * @s2: The string to search for 16 + */ 17 + char *strstr(const char *s1, const char *s2) 18 + { 19 + size_t l1, l2; 20 + 21 + l2 = strlen(s2); 22 + if (!l2) 23 + return (char *)s1; 24 + l1 = strlen(s1); 25 + while (l1 >= l2) { 26 + l1--; 27 + if (!memcmp(s1, s2, l2)) 28 + return (char *)s1; 29 + s1++; 30 + } 31 + return NULL; 32 + } 33 + #endif 34 + 35 + #ifndef __HAVE_ARCH_STRNCMP 36 + /** 37 + * strncmp - Compare two length-limited strings 38 + * @cs: One string 39 + * @ct: Another string 40 + * @count: The maximum number of bytes to compare 41 + */ 42 + int strncmp(const char *cs, const char *ct, size_t count) 43 + { 44 + unsigned char c1, c2; 45 + 46 + while (count) { 47 + c1 = *cs++; 48 + c2 = *ct++; 49 + if (c1 != c2) 50 + return c1 < c2 ? -1 : 1; 51 + if (!c1) 52 + break; 53 + count--; 54 + } 55 + return 0; 56 + } 57 + #endif