Skip to content

Commit e8f3010

Browse files
Ard Biesheuvelctmarinas
authored andcommitted
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>
1 parent 2079184 commit e8f3010

File tree

6 files changed

+139
-20
lines changed

6 files changed

+139
-20
lines changed

arch/arm64/kernel/Makefile

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ arm64-obj-y := debug-monitors.o entry.o irq.o fpsimd.o \
2020
cpufeature.o alternative.o cacheinfo.o \
2121
smp.o smp_spin_table.o topology.o
2222

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+
2331
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \
2432
sys_compat.o entry32.o \
2533
../../arm/kernel/opcodes.o
@@ -32,15 +40,15 @@ arm64-obj-$(CONFIG_CPU_PM) += sleep.o suspend.o
3240
arm64-obj-$(CONFIG_CPU_IDLE) += cpuidle.o
3341
arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o
3442
arm64-obj-$(CONFIG_KGDB) += kgdb.o
35-
arm64-obj-$(CONFIG_EFI) += efi.o efi-stub.o efi-entry.o
43+
arm64-obj-$(CONFIG_EFI) += efi.o $(stub-obj)
3644
arm64-obj-$(CONFIG_PCI) += pci.o
3745
arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o
3846
arm64-obj-$(CONFIG_ACPI) += acpi.o
3947

4048
obj-y += $(arm64-obj-y) vdso/
4149
obj-m += $(arm64-obj-m)
4250
head-y := head.o
43-
extra-y := $(head-y) vmlinux.lds
51+
extra-y += $(head-y) vmlinux.lds
4452

4553
# vDSO - this must be built first to generate the symbol offsets
4654
$(call objectify,$(arm64-obj-y)): $(obj)/vdso/vdso-offsets.h

arch/arm64/kernel/efi-entry.S

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
* we want to be. The kernel image wants to be placed at TEXT_OFFSET
3030
* from start of RAM.
3131
*/
32-
ENTRY(efi_stub_entry)
32+
ENTRY(entry)
3333
/*
3434
* Create a stack frame to save FP/LR with extra space
3535
* for image_addr variable passed to efi_entry().
@@ -86,8 +86,8 @@ ENTRY(efi_stub_entry)
8686
* entries for the VA range of the current image, so no maintenance is
8787
* necessary.
8888
*/
89-
adr x0, efi_stub_entry
90-
adr x1, efi_stub_entry_end
89+
adr x0, entry
90+
adr x1, entry_end
9191
sub x1, x1, x0
9292
bl __flush_dcache_area
9393

@@ -120,5 +120,5 @@ efi_load_fail:
120120
ldp x29, x30, [sp], #32
121121
ret
122122

123-
efi_stub_entry_end:
124-
ENDPROC(efi_stub_entry)
123+
entry_end:
124+
ENDPROC(entry)

arch/arm64/kernel/head.S

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ efi_head:
120120
#endif
121121

122122
#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
125125
.align 3
126126
pe_header:
127127
.ascii "PE"
@@ -144,8 +144,8 @@ optional_header:
144144
.long _end - stext // SizeOfCode
145145
.long 0 // SizeOfInitializedData
146146
.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
149149

150150
extra_header_fields:
151151
.quad 0 // ImageBase
@@ -162,7 +162,7 @@ extra_header_fields:
162162
.long _end - efi_head // SizeOfImage
163163

164164
// Everything before the kernel image is considered part of the header
165-
.long stext_offset // SizeOfHeaders
165+
.long __efistub_stext_offset // SizeOfHeaders
166166
.long 0 // CheckSum
167167
.short 0xa // Subsystem (EFI application)
168168
.short 0 // DllCharacteristics
@@ -207,9 +207,9 @@ section_table:
207207
.byte 0
208208
.byte 0 // end of 0 padding of section name
209209
.long _end - stext // VirtualSize
210-
.long stext_offset // VirtualAddress
210+
.long __efistub_stext_offset // VirtualAddress
211211
.long _edata - stext // SizeOfRawData
212-
.long stext_offset // PointerToRawData
212+
.long __efistub_stext_offset // PointerToRawData
213213

214214
.long 0 // PointerToRelocations (0 for executables)
215215
.long 0 // PointerToLineNumbers (0 for executables)

arch/arm64/kernel/image.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,31 @@
5959
_kernel_offset_le = DATA_LE64(TEXT_OFFSET); \
6060
_kernel_flags_le = DATA_LE64(__HEAD_FLAGS);
6161

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+
6289
#endif /* __ASM_IMAGE_H */

drivers/firmware/efi/libstub/Makefile

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ cflags-$(CONFIG_ARM64) := $(subst -pg,,$(KBUILD_CFLAGS))
1414
cflags-$(CONFIG_ARM) := $(subst -pg,,$(KBUILD_CFLAGS)) \
1515
-fno-builtin -fpic -mno-single-pic-base
1616

17+
cflags-$(CONFIG_EFI_ARMSTUB) += -I$(srctree)/scripts/dtc/libfdt
18+
1719
KBUILD_CFLAGS := $(cflags-y) \
1820
$(call cc-option,-ffreestanding) \
1921
$(call cc-option,-fno-stack-protector)
@@ -22,18 +24,43 @@ GCOV_PROFILE := n
2224
KASAN_SANITIZE := n
2325

2426
lib-y := efi-stub-helper.o
25-
lib-$(CONFIG_EFI_ARMSTUB) += arm-stub.o fdt.o
27+
28+
# include the stub's generic dependencies from lib/ when building for ARM/arm64
29+
arm-deps := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c sort.c
30+
31+
$(obj)/lib-%.o: $(srctree)/lib/%.c FORCE
32+
$(call if_changed_rule,cc_o_c)
33+
34+
lib-$(CONFIG_EFI_ARMSTUB) += arm-stub.o fdt.o string.o \
35+
$(patsubst %.c,lib-%.o,$(arm-deps))
2636

2737
#
2838
# arm64 puts the stub in the kernel proper, which will unnecessarily retain all
2939
# code indefinitely unless it is annotated as __init/__initdata/__initconst etc.
3040
# So let's apply the __init annotations at the section level, by prefixing
3141
# the section names directly. This will ensure that even all the inline string
3242
# literals are covered.
43+
# The fact that the stub and the kernel proper are essentially the same binary
44+
# also means that we need to be extra careful to make sure that the stub does
45+
# not rely on any absolute symbol references, considering that the virtual
46+
# kernel mapping that the linker uses is not active yet when the stub is
47+
# executing. So build all C dependencies of the EFI stub into libstub, and do
48+
# a verification pass to see if any absolute relocations exist in any of the
49+
# object files.
3350
#
34-
extra-$(CONFIG_ARM64) := $(lib-y)
35-
lib-$(CONFIG_ARM64) := $(patsubst %.o,%.init.o,$(lib-y))
51+
extra-$(CONFIG_EFI_ARMSTUB) := $(lib-y)
52+
lib-$(CONFIG_EFI_ARMSTUB) := $(patsubst %.o,%.stub.o,$(lib-y))
53+
54+
STUBCOPY_FLAGS-y := -R .debug* -R *ksymtab*
55+
STUBCOPY_FLAGS-$(CONFIG_ARM64) += --prefix-alloc-sections=.init \
56+
--prefix-symbols=__efistub_
57+
STUBCOPY_RELOC-$(CONFIG_ARM64) := R_AARCH64_ABS
58+
59+
$(obj)/%.stub.o: $(obj)/%.o FORCE
60+
$(call if_changed,stubcopy)
3661

37-
OBJCOPYFLAGS := --prefix-alloc-sections=.init
38-
$(obj)/%.init.o: $(obj)/%.o FORCE
39-
$(call if_changed,objcopy)
62+
quiet_cmd_stubcopy = STUBCPY $@
63+
cmd_stubcopy = if $(OBJCOPY) $(STUBCOPY_FLAGS-y) $< $@; then \
64+
$(OBJDUMP) -r $@ | grep $(STUBCOPY_RELOC-y) \
65+
&& (echo >&2 "$@: absolute symbol references not allowed in the EFI stub"; \
66+
rm -f $@; /bin/false); else /bin/false; fi

drivers/firmware/efi/libstub/string.c

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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

0 commit comments

Comments
 (0)