diff options
author | Gerald Schaefer <gerald.schaefer@de.ibm.com> | 2019-02-03 15:35:45 -0500 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2019-04-29 04:47:10 -0400 |
commit | 805bc0bc238f7209fca5e39c152b0d3c12046ac9 (patch) | |
tree | 97edfcee4981bc71c2e6a9dc853353010ac4fa94 | |
parent | 833b441ec0f6f3c57cc2106ef628bb19d8fb0ee2 (diff) |
s390/kernel: build a relocatable kernel
This patch adds support for building a relocatable kernel with -fPIE.
The kernel will be relocated to 0 early in the boot process.
Signed-off-by: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Reviewed-by: Philipp Rudo <prudo@linux.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r-- | arch/s390/Kconfig | 13 | ||||
-rw-r--r-- | arch/s390/Makefile | 4 | ||||
-rw-r--r-- | arch/s390/boot/Makefile | 1 | ||||
-rw-r--r-- | arch/s390/boot/compressed/decompressor.h | 3 | ||||
-rw-r--r-- | arch/s390/boot/machine_kexec_reloc.c | 2 | ||||
-rw-r--r-- | arch/s390/boot/startup.c | 27 | ||||
-rw-r--r-- | arch/s390/include/asm/kexec.h | 2 | ||||
-rw-r--r-- | arch/s390/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/s390/kernel/machine_kexec_file.c | 44 | ||||
-rw-r--r-- | arch/s390/kernel/machine_kexec_reloc.c | 53 | ||||
-rw-r--r-- | arch/s390/kernel/vmlinux.lds.S | 15 |
11 files changed, 124 insertions, 42 deletions
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 21e851b0a989..4c99e4f5f366 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig | |||
@@ -624,6 +624,19 @@ config EXPOLINE_FULL | |||
624 | 624 | ||
625 | endchoice | 625 | endchoice |
626 | 626 | ||
627 | config RELOCATABLE | ||
628 | bool "Build a relocatable kernel" | ||
629 | select MODULE_REL_CRCS if MODVERSIONS | ||
630 | default y | ||
631 | help | ||
632 | This builds a kernel image that retains relocation information | ||
633 | so it can be loaded at an arbitrary address. | ||
634 | The kernel is linked as a position-independent executable (PIE) | ||
635 | and contains dynamic relocations which are processed early in the | ||
636 | bootup process. | ||
637 | The relocations make the kernel image about 15% larger (compressed | ||
638 | 10%), but are discarded at runtime. | ||
639 | |||
627 | endmenu | 640 | endmenu |
628 | 641 | ||
629 | menu "Memory setup" | 642 | menu "Memory setup" |
diff --git a/arch/s390/Makefile b/arch/s390/Makefile index 9c079a506325..54b8a12d64e8 100644 --- a/arch/s390/Makefile +++ b/arch/s390/Makefile | |||
@@ -16,6 +16,10 @@ KBUILD_AFLAGS_MODULE += -fPIC | |||
16 | KBUILD_CFLAGS_MODULE += -fPIC | 16 | KBUILD_CFLAGS_MODULE += -fPIC |
17 | KBUILD_AFLAGS += -m64 | 17 | KBUILD_AFLAGS += -m64 |
18 | KBUILD_CFLAGS += -m64 | 18 | KBUILD_CFLAGS += -m64 |
19 | ifeq ($(CONFIG_RELOCATABLE),y) | ||
20 | KBUILD_CFLAGS += -fPIE | ||
21 | LDFLAGS_vmlinux := -pie | ||
22 | endif | ||
19 | aflags_dwarf := -Wa,-gdwarf-2 | 23 | aflags_dwarf := -Wa,-gdwarf-2 |
20 | KBUILD_AFLAGS_DECOMPRESSOR := -m64 -D__ASSEMBLY__ | 24 | KBUILD_AFLAGS_DECOMPRESSOR := -m64 -D__ASSEMBLY__ |
21 | KBUILD_AFLAGS_DECOMPRESSOR += $(if $(CONFIG_DEBUG_INFO),$(aflags_dwarf)) | 25 | KBUILD_AFLAGS_DECOMPRESSOR += $(if $(CONFIG_DEBUG_INFO),$(aflags_dwarf)) |
diff --git a/arch/s390/boot/Makefile b/arch/s390/boot/Makefile index c1993c57300f..4df43e83363a 100644 --- a/arch/s390/boot/Makefile +++ b/arch/s390/boot/Makefile | |||
@@ -32,6 +32,7 @@ obj-y := head.o als.o startup.o mem_detect.o ipl_parm.o ipl_report.o | |||
32 | obj-y += string.o ebcdic.o sclp_early_core.o mem.o ipl_vmparm.o cmdline.o | 32 | obj-y += string.o ebcdic.o sclp_early_core.o mem.o ipl_vmparm.o cmdline.o |
33 | obj-y += ctype.o | 33 | obj-y += ctype.o |
34 | obj-$(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) += uv.o | 34 | obj-$(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) += uv.o |
35 | obj-$(CONFIG_RELOCATABLE) += machine_kexec_reloc.o | ||
35 | targets := bzImage startup.a section_cmp.boot.data section_cmp.boot.preserved.data $(obj-y) | 36 | targets := bzImage startup.a section_cmp.boot.data section_cmp.boot.preserved.data $(obj-y) |
36 | subdir- := compressed | 37 | subdir- := compressed |
37 | 38 | ||
diff --git a/arch/s390/boot/compressed/decompressor.h b/arch/s390/boot/compressed/decompressor.h index 424cf524aac1..c15eb7114d83 100644 --- a/arch/s390/boot/compressed/decompressor.h +++ b/arch/s390/boot/compressed/decompressor.h | |||
@@ -19,6 +19,9 @@ struct vmlinux_info { | |||
19 | unsigned long bootdata_size; | 19 | unsigned long bootdata_size; |
20 | unsigned long bootdata_preserved_off; | 20 | unsigned long bootdata_preserved_off; |
21 | unsigned long bootdata_preserved_size; | 21 | unsigned long bootdata_preserved_size; |
22 | unsigned long dynsym_start; | ||
23 | unsigned long rela_dyn_start; | ||
24 | unsigned long rela_dyn_end; | ||
22 | }; | 25 | }; |
23 | 26 | ||
24 | extern char _vmlinux_info[]; | 27 | extern char _vmlinux_info[]; |
diff --git a/arch/s390/boot/machine_kexec_reloc.c b/arch/s390/boot/machine_kexec_reloc.c new file mode 100644 index 000000000000..b7a5d0f72097 --- /dev/null +++ b/arch/s390/boot/machine_kexec_reloc.c | |||
@@ -0,0 +1,2 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | #include "../kernel/machine_kexec_reloc.c" | ||
diff --git a/arch/s390/boot/startup.c b/arch/s390/boot/startup.c index 90898976a941..b7d6a76cb5e9 100644 --- a/arch/s390/boot/startup.c +++ b/arch/s390/boot/startup.c | |||
@@ -1,6 +1,8 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/string.h> | 2 | #include <linux/string.h> |
3 | #include <linux/elf.h> | ||
3 | #include <asm/setup.h> | 4 | #include <asm/setup.h> |
5 | #include <asm/kexec.h> | ||
4 | #include <asm/sclp.h> | 6 | #include <asm/sclp.h> |
5 | #include <asm/uv.h> | 7 | #include <asm/uv.h> |
6 | #include "compressed/decompressor.h" | 8 | #include "compressed/decompressor.h" |
@@ -47,6 +49,29 @@ static void copy_bootdata(void) | |||
47 | memcpy((void *)vmlinux.bootdata_preserved_off, __boot_data_preserved_start, vmlinux.bootdata_preserved_size); | 49 | memcpy((void *)vmlinux.bootdata_preserved_off, __boot_data_preserved_start, vmlinux.bootdata_preserved_size); |
48 | } | 50 | } |
49 | 51 | ||
52 | static void handle_relocs(unsigned long offset) | ||
53 | { | ||
54 | Elf64_Rela *rela_start, *rela_end, *rela; | ||
55 | int r_type, r_sym, rc; | ||
56 | Elf64_Addr loc, val; | ||
57 | Elf64_Sym *dynsym; | ||
58 | |||
59 | rela_start = (Elf64_Rela *) vmlinux.rela_dyn_start; | ||
60 | rela_end = (Elf64_Rela *) vmlinux.rela_dyn_end; | ||
61 | dynsym = (Elf64_Sym *) vmlinux.dynsym_start; | ||
62 | for (rela = rela_start; rela < rela_end; rela++) { | ||
63 | loc = rela->r_offset + offset; | ||
64 | val = rela->r_addend + offset; | ||
65 | r_sym = ELF64_R_SYM(rela->r_info); | ||
66 | if (r_sym) | ||
67 | val += dynsym[r_sym].st_value; | ||
68 | r_type = ELF64_R_TYPE(rela->r_info); | ||
69 | rc = arch_kexec_do_relocs(r_type, (void *) loc, val, 0); | ||
70 | if (rc) | ||
71 | error("Unknown relocation type"); | ||
72 | } | ||
73 | } | ||
74 | |||
50 | void startup_kernel(void) | 75 | void startup_kernel(void) |
51 | { | 76 | { |
52 | unsigned long safe_addr; | 77 | unsigned long safe_addr; |
@@ -67,5 +92,7 @@ void startup_kernel(void) | |||
67 | memmove((void *)vmlinux.default_lma, img, vmlinux.image_size); | 92 | memmove((void *)vmlinux.default_lma, img, vmlinux.image_size); |
68 | } | 93 | } |
69 | copy_bootdata(); | 94 | copy_bootdata(); |
95 | if (IS_ENABLED(CONFIG_RELOCATABLE)) | ||
96 | handle_relocs(0); | ||
70 | vmlinux.entry(); | 97 | vmlinux.entry(); |
71 | } | 98 | } |
diff --git a/arch/s390/include/asm/kexec.h b/arch/s390/include/asm/kexec.h index 305d3465574f..ea398a05f643 100644 --- a/arch/s390/include/asm/kexec.h +++ b/arch/s390/include/asm/kexec.h | |||
@@ -71,6 +71,8 @@ int s390_verify_sig(const char *kernel, unsigned long kernel_len); | |||
71 | void *kexec_file_add_components(struct kimage *image, | 71 | void *kexec_file_add_components(struct kimage *image, |
72 | int (*add_kernel)(struct kimage *image, | 72 | int (*add_kernel)(struct kimage *image, |
73 | struct s390_load_data *data)); | 73 | struct s390_load_data *data)); |
74 | int arch_kexec_do_relocs(int r_type, void *loc, unsigned long val, | ||
75 | unsigned long addr); | ||
74 | 76 | ||
75 | extern const struct kexec_file_ops s390_kexec_image_ops; | 77 | extern const struct kexec_file_ops s390_kexec_image_ops; |
76 | extern const struct kexec_file_ops s390_kexec_elf_ops; | 78 | extern const struct kexec_file_ops s390_kexec_elf_ops; |
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index d28acd7ba81e..19425605a83d 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile | |||
@@ -51,7 +51,7 @@ obj-y += debug.o irq.o ipl.o dis.o diag.o vdso.o early_nobss.o | |||
51 | obj-y += sysinfo.o lgr.o os_info.o machine_kexec.o pgm_check.o | 51 | obj-y += sysinfo.o lgr.o os_info.o machine_kexec.o pgm_check.o |
52 | obj-y += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o sthyi.o | 52 | obj-y += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o sthyi.o |
53 | obj-y += entry.o reipl.o relocate_kernel.o kdebugfs.o alternative.o | 53 | obj-y += entry.o reipl.o relocate_kernel.o kdebugfs.o alternative.o |
54 | obj-y += nospec-branch.o ipl_vmparm.o | 54 | obj-y += nospec-branch.o ipl_vmparm.o machine_kexec_reloc.o |
55 | 55 | ||
56 | extra-y += head64.o vmlinux.lds | 56 | extra-y += head64.o vmlinux.lds |
57 | 57 | ||
diff --git a/arch/s390/kernel/machine_kexec_file.c b/arch/s390/kernel/machine_kexec_file.c index 48cab9600ed9..42c23a5c8229 100644 --- a/arch/s390/kernel/machine_kexec_file.c +++ b/arch/s390/kernel/machine_kexec_file.c | |||
@@ -290,7 +290,7 @@ int arch_kexec_apply_relocations_add(struct purgatory_info *pi, | |||
290 | const Elf_Shdr *symtab) | 290 | const Elf_Shdr *symtab) |
291 | { | 291 | { |
292 | Elf_Rela *relas; | 292 | Elf_Rela *relas; |
293 | int i; | 293 | int i, r_type; |
294 | 294 | ||
295 | relas = (void *)pi->ehdr + relsec->sh_offset; | 295 | relas = (void *)pi->ehdr + relsec->sh_offset; |
296 | 296 | ||
@@ -324,46 +324,8 @@ int arch_kexec_apply_relocations_add(struct purgatory_info *pi, | |||
324 | 324 | ||
325 | addr = section->sh_addr + relas[i].r_offset; | 325 | addr = section->sh_addr + relas[i].r_offset; |
326 | 326 | ||
327 | switch (ELF64_R_TYPE(relas[i].r_info)) { | 327 | r_type = ELF64_R_TYPE(relas[i].r_info); |
328 | case R_390_8: /* Direct 8 bit. */ | 328 | arch_kexec_do_relocs(r_type, loc, val, addr); |
329 | *(u8 *)loc = val; | ||
330 | break; | ||
331 | case R_390_12: /* Direct 12 bit. */ | ||
332 | *(u16 *)loc &= 0xf000; | ||
333 | *(u16 *)loc |= val & 0xfff; | ||
334 | break; | ||
335 | case R_390_16: /* Direct 16 bit. */ | ||
336 | *(u16 *)loc = val; | ||
337 | break; | ||
338 | case R_390_20: /* Direct 20 bit. */ | ||
339 | *(u32 *)loc &= 0xf00000ff; | ||
340 | *(u32 *)loc |= (val & 0xfff) << 16; /* DL */ | ||
341 | *(u32 *)loc |= (val & 0xff000) >> 4; /* DH */ | ||
342 | break; | ||
343 | case R_390_32: /* Direct 32 bit. */ | ||
344 | *(u32 *)loc = val; | ||
345 | break; | ||
346 | case R_390_64: /* Direct 64 bit. */ | ||
347 | *(u64 *)loc = val; | ||
348 | break; | ||
349 | case R_390_PC16: /* PC relative 16 bit. */ | ||
350 | *(u16 *)loc = (val - addr); | ||
351 | break; | ||
352 | case R_390_PC16DBL: /* PC relative 16 bit shifted by 1. */ | ||
353 | *(u16 *)loc = (val - addr) >> 1; | ||
354 | break; | ||
355 | case R_390_PC32DBL: /* PC relative 32 bit shifted by 1. */ | ||
356 | *(u32 *)loc = (val - addr) >> 1; | ||
357 | break; | ||
358 | case R_390_PC32: /* PC relative 32 bit. */ | ||
359 | *(u32 *)loc = (val - addr); | ||
360 | break; | ||
361 | case R_390_PC64: /* PC relative 64 bit. */ | ||
362 | *(u64 *)loc = (val - addr); | ||
363 | break; | ||
364 | default: | ||
365 | break; | ||
366 | } | ||
367 | } | 329 | } |
368 | return 0; | 330 | return 0; |
369 | } | 331 | } |
diff --git a/arch/s390/kernel/machine_kexec_reloc.c b/arch/s390/kernel/machine_kexec_reloc.c new file mode 100644 index 000000000000..1dded39239f8 --- /dev/null +++ b/arch/s390/kernel/machine_kexec_reloc.c | |||
@@ -0,0 +1,53 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | #include <linux/elf.h> | ||
3 | |||
4 | int arch_kexec_do_relocs(int r_type, void *loc, unsigned long val, | ||
5 | unsigned long addr) | ||
6 | { | ||
7 | switch (r_type) { | ||
8 | case R_390_NONE: | ||
9 | break; | ||
10 | case R_390_8: /* Direct 8 bit. */ | ||
11 | *(u8 *)loc = val; | ||
12 | break; | ||
13 | case R_390_12: /* Direct 12 bit. */ | ||
14 | *(u16 *)loc &= 0xf000; | ||
15 | *(u16 *)loc |= val & 0xfff; | ||
16 | break; | ||
17 | case R_390_16: /* Direct 16 bit. */ | ||
18 | *(u16 *)loc = val; | ||
19 | break; | ||
20 | case R_390_20: /* Direct 20 bit. */ | ||
21 | *(u32 *)loc &= 0xf00000ff; | ||
22 | *(u32 *)loc |= (val & 0xfff) << 16; /* DL */ | ||
23 | *(u32 *)loc |= (val & 0xff000) >> 4; /* DH */ | ||
24 | break; | ||
25 | case R_390_32: /* Direct 32 bit. */ | ||
26 | *(u32 *)loc = val; | ||
27 | break; | ||
28 | case R_390_64: /* Direct 64 bit. */ | ||
29 | *(u64 *)loc = val; | ||
30 | break; | ||
31 | case R_390_PC16: /* PC relative 16 bit. */ | ||
32 | *(u16 *)loc = (val - addr); | ||
33 | break; | ||
34 | case R_390_PC16DBL: /* PC relative 16 bit shifted by 1. */ | ||
35 | *(u16 *)loc = (val - addr) >> 1; | ||
36 | break; | ||
37 | case R_390_PC32DBL: /* PC relative 32 bit shifted by 1. */ | ||
38 | *(u32 *)loc = (val - addr) >> 1; | ||
39 | break; | ||
40 | case R_390_PC32: /* PC relative 32 bit. */ | ||
41 | *(u32 *)loc = (val - addr); | ||
42 | break; | ||
43 | case R_390_PC64: /* PC relative 64 bit. */ | ||
44 | *(u64 *)loc = (val - addr); | ||
45 | break; | ||
46 | case R_390_RELATIVE: | ||
47 | *(unsigned long *) loc = val; | ||
48 | break; | ||
49 | default: | ||
50 | return 1; | ||
51 | } | ||
52 | return 0; | ||
53 | } | ||
diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S index 6ef9c62bb01b..49d55327de0b 100644 --- a/arch/s390/kernel/vmlinux.lds.S +++ b/arch/s390/kernel/vmlinux.lds.S | |||
@@ -144,6 +144,18 @@ SECTIONS | |||
144 | INIT_DATA_SECTION(0x100) | 144 | INIT_DATA_SECTION(0x100) |
145 | 145 | ||
146 | PERCPU_SECTION(0x100) | 146 | PERCPU_SECTION(0x100) |
147 | |||
148 | .dynsym ALIGN(8) : { | ||
149 | __dynsym_start = .; | ||
150 | *(.dynsym) | ||
151 | __dynsym_end = .; | ||
152 | } | ||
153 | .rela.dyn ALIGN(8) : { | ||
154 | __rela_dyn_start = .; | ||
155 | *(.rela*) | ||
156 | __rela_dyn_end = .; | ||
157 | } | ||
158 | |||
147 | . = ALIGN(PAGE_SIZE); | 159 | . = ALIGN(PAGE_SIZE); |
148 | __init_end = .; /* freed after init ends here */ | 160 | __init_end = .; /* freed after init ends here */ |
149 | 161 | ||
@@ -165,6 +177,9 @@ SECTIONS | |||
165 | QUAD(__boot_data_preserved_start) /* bootdata_preserved_off */ | 177 | QUAD(__boot_data_preserved_start) /* bootdata_preserved_off */ |
166 | QUAD(__boot_data_preserved_end - | 178 | QUAD(__boot_data_preserved_end - |
167 | __boot_data_preserved_start) /* bootdata_preserved_size */ | 179 | __boot_data_preserved_start) /* bootdata_preserved_size */ |
180 | QUAD(__dynsym_start) /* dynsym_start */ | ||
181 | QUAD(__rela_dyn_start) /* rela_dyn_start */ | ||
182 | QUAD(__rela_dyn_end) /* rela_dyn_end */ | ||
168 | } :NONE | 183 | } :NONE |
169 | 184 | ||
170 | /* Debugging sections. */ | 185 | /* Debugging sections. */ |