diff options
author | Philipp Rudo <prudo@linux.vnet.ibm.com> | 2017-09-11 09:15:29 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2018-04-16 03:10:23 -0400 |
commit | 8be018827154666d1fe5904cb7a43b6706e01c87 (patch) | |
tree | 3fc01c556ba536ef0da3072abc3c3c0db94dda4e | |
parent | ee337f5469fd67f22d231e520ec4189ce0589d92 (diff) |
s390/kexec_file: Add ELF loader
Add an ELF loader for kexec_file. The main task here is to do proper sanity
checks on the ELF file. Basically all other functionality was already
implemented for the image loader.
Signed-off-by: Philipp Rudo <prudo@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r-- | arch/s390/include/asm/kexec.h | 1 | ||||
-rw-r--r-- | arch/s390/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/s390/kernel/kexec_elf.c | 147 | ||||
-rw-r--r-- | arch/s390/kernel/machine_kexec_file.c | 1 |
4 files changed, 150 insertions, 0 deletions
diff --git a/arch/s390/include/asm/kexec.h b/arch/s390/include/asm/kexec.h index 4c9da9974cf4..825dd0f7f221 100644 --- a/arch/s390/include/asm/kexec.h +++ b/arch/s390/include/asm/kexec.h | |||
@@ -67,5 +67,6 @@ int *kexec_file_update_kernel(struct kimage *iamge, | |||
67 | struct s390_load_data *data); | 67 | struct s390_load_data *data); |
68 | 68 | ||
69 | extern const struct kexec_file_ops s390_kexec_image_ops; | 69 | extern const struct kexec_file_ops s390_kexec_image_ops; |
70 | extern const struct kexec_file_ops s390_kexec_elf_ops; | ||
70 | 71 | ||
71 | #endif /*_S390_KEXEC_H */ | 72 | #endif /*_S390_KEXEC_H */ |
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index a84e9611c5c7..84ea6225efb4 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile | |||
@@ -83,6 +83,7 @@ obj-$(CONFIG_CRASH_DUMP) += crash_dump.o | |||
83 | obj-$(CONFIG_UPROBES) += uprobes.o | 83 | obj-$(CONFIG_UPROBES) += uprobes.o |
84 | 84 | ||
85 | obj-$(CONFIG_KEXEC_FILE) += machine_kexec_file.o kexec_image.o | 85 | obj-$(CONFIG_KEXEC_FILE) += machine_kexec_file.o kexec_image.o |
86 | obj-$(CONFIG_KEXEC_FILE) += kexec_elf.o | ||
86 | 87 | ||
87 | obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_cpum_cf.o perf_cpum_sf.o | 88 | obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_cpum_cf.o perf_cpum_sf.o |
88 | obj-$(CONFIG_PERF_EVENTS) += perf_cpum_cf_events.o perf_regs.o | 89 | obj-$(CONFIG_PERF_EVENTS) += perf_cpum_cf_events.o perf_regs.o |
diff --git a/arch/s390/kernel/kexec_elf.c b/arch/s390/kernel/kexec_elf.c new file mode 100644 index 000000000000..5a286b012043 --- /dev/null +++ b/arch/s390/kernel/kexec_elf.c | |||
@@ -0,0 +1,147 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * ELF loader for kexec_file_load system call. | ||
4 | * | ||
5 | * Copyright IBM Corp. 2018 | ||
6 | * | ||
7 | * Author(s): Philipp Rudo <prudo@linux.vnet.ibm.com> | ||
8 | */ | ||
9 | |||
10 | #include <linux/errno.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/kexec.h> | ||
13 | #include <asm/setup.h> | ||
14 | |||
15 | static int kexec_file_add_elf_kernel(struct kimage *image, | ||
16 | struct s390_load_data *data, | ||
17 | char *kernel, unsigned long kernel_len) | ||
18 | { | ||
19 | struct kexec_buf buf; | ||
20 | const Elf_Ehdr *ehdr; | ||
21 | const Elf_Phdr *phdr; | ||
22 | int i, ret; | ||
23 | |||
24 | ehdr = (Elf_Ehdr *)kernel; | ||
25 | buf.image = image; | ||
26 | |||
27 | phdr = (void *)ehdr + ehdr->e_phoff; | ||
28 | for (i = 0; i < ehdr->e_phnum; i++, phdr++) { | ||
29 | if (phdr->p_type != PT_LOAD) | ||
30 | continue; | ||
31 | |||
32 | buf.buffer = kernel + phdr->p_offset; | ||
33 | buf.bufsz = phdr->p_filesz; | ||
34 | |||
35 | buf.mem = ALIGN(phdr->p_paddr, phdr->p_align); | ||
36 | buf.memsz = phdr->p_memsz; | ||
37 | |||
38 | if (phdr->p_paddr == 0) { | ||
39 | data->kernel_buf = buf.buffer; | ||
40 | data->memsz += STARTUP_NORMAL_OFFSET; | ||
41 | |||
42 | buf.buffer += STARTUP_NORMAL_OFFSET; | ||
43 | buf.bufsz -= STARTUP_NORMAL_OFFSET; | ||
44 | |||
45 | buf.mem += STARTUP_NORMAL_OFFSET; | ||
46 | buf.memsz -= STARTUP_NORMAL_OFFSET; | ||
47 | } | ||
48 | |||
49 | if (image->type == KEXEC_TYPE_CRASH) | ||
50 | buf.mem += crashk_res.start; | ||
51 | |||
52 | ret = kexec_add_buffer(&buf); | ||
53 | if (ret) | ||
54 | return ret; | ||
55 | |||
56 | data->memsz += buf.memsz; | ||
57 | } | ||
58 | |||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | static void *s390_elf_load(struct kimage *image, | ||
63 | char *kernel, unsigned long kernel_len, | ||
64 | char *initrd, unsigned long initrd_len, | ||
65 | char *cmdline, unsigned long cmdline_len) | ||
66 | { | ||
67 | struct s390_load_data data = {0}; | ||
68 | const Elf_Ehdr *ehdr; | ||
69 | const Elf_Phdr *phdr; | ||
70 | size_t size; | ||
71 | int i, ret; | ||
72 | |||
73 | /* image->fobs->probe already checked for valid ELF magic number. */ | ||
74 | ehdr = (Elf_Ehdr *)kernel; | ||
75 | |||
76 | if (ehdr->e_type != ET_EXEC || | ||
77 | ehdr->e_ident[EI_CLASS] != ELFCLASS64 || | ||
78 | !elf_check_arch(ehdr)) | ||
79 | return ERR_PTR(-EINVAL); | ||
80 | |||
81 | if (!ehdr->e_phnum || ehdr->e_phentsize != sizeof(Elf_Phdr)) | ||
82 | return ERR_PTR(-EINVAL); | ||
83 | |||
84 | size = ehdr->e_ehsize + ehdr->e_phoff; | ||
85 | size += ehdr->e_phentsize * ehdr->e_phnum; | ||
86 | if (size > kernel_len) | ||
87 | return ERR_PTR(-EINVAL); | ||
88 | |||
89 | phdr = (void *)ehdr + ehdr->e_phoff; | ||
90 | size = ALIGN(size, phdr->p_align); | ||
91 | for (i = 0; i < ehdr->e_phnum; i++, phdr++) { | ||
92 | if (phdr->p_type == PT_INTERP) | ||
93 | return ERR_PTR(-EINVAL); | ||
94 | |||
95 | if (phdr->p_offset > kernel_len) | ||
96 | return ERR_PTR(-EINVAL); | ||
97 | |||
98 | size += ALIGN(phdr->p_filesz, phdr->p_align); | ||
99 | } | ||
100 | |||
101 | if (size > kernel_len) | ||
102 | return ERR_PTR(-EINVAL); | ||
103 | |||
104 | ret = kexec_file_add_elf_kernel(image, &data, kernel, kernel_len); | ||
105 | if (ret) | ||
106 | return ERR_PTR(ret); | ||
107 | |||
108 | if (!data.memsz) | ||
109 | return ERR_PTR(-EINVAL); | ||
110 | |||
111 | if (initrd) { | ||
112 | ret = kexec_file_add_initrd(image, &data, initrd, initrd_len); | ||
113 | if (ret) | ||
114 | return ERR_PTR(ret); | ||
115 | } | ||
116 | |||
117 | ret = kexec_file_add_purgatory(image, &data); | ||
118 | if (ret) | ||
119 | return ERR_PTR(ret); | ||
120 | |||
121 | return kexec_file_update_kernel(image, &data); | ||
122 | } | ||
123 | |||
124 | static int s390_elf_probe(const char *buf, unsigned long len) | ||
125 | { | ||
126 | const Elf_Ehdr *ehdr; | ||
127 | |||
128 | if (len < sizeof(Elf_Ehdr)) | ||
129 | return -ENOEXEC; | ||
130 | |||
131 | ehdr = (Elf_Ehdr *)buf; | ||
132 | |||
133 | /* Only check the ELF magic number here and do proper validity check | ||
134 | * in the loader. Any check here that fails would send the erroneous | ||
135 | * ELF file to the image loader that does not care what it gets. | ||
136 | * (Most likely) causing behavior not intended by the user. | ||
137 | */ | ||
138 | if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) | ||
139 | return -ENOEXEC; | ||
140 | |||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | const struct kexec_file_ops s390_kexec_elf_ops = { | ||
145 | .probe = s390_elf_probe, | ||
146 | .load = s390_elf_load, | ||
147 | }; | ||
diff --git a/arch/s390/kernel/machine_kexec_file.c b/arch/s390/kernel/machine_kexec_file.c index 75aea2c1d823..f413f57f8d20 100644 --- a/arch/s390/kernel/machine_kexec_file.c +++ b/arch/s390/kernel/machine_kexec_file.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <asm/setup.h> | 12 | #include <asm/setup.h> |
13 | 13 | ||
14 | const struct kexec_file_ops * const kexec_file_loaders[] = { | 14 | const struct kexec_file_ops * const kexec_file_loaders[] = { |
15 | &s390_kexec_elf_ops, | ||
15 | &s390_kexec_image_ops, | 16 | &s390_kexec_image_ops, |
16 | NULL, | 17 | NULL, |
17 | }; | 18 | }; |