1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * s390 code 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 | #define pr_fmt(fmt) "kexec: " fmt |
11 | |
12 | #include <linux/elf.h> |
13 | #include <linux/errno.h> |
14 | #include <linux/kexec.h> |
15 | #include <linux/module_signature.h> |
16 | #include <linux/verification.h> |
17 | #include <linux/vmalloc.h> |
18 | #include <asm/boot_data.h> |
19 | #include <asm/ipl.h> |
20 | #include <asm/setup.h> |
21 | |
22 | const struct kexec_file_ops * const kexec_file_loaders[] = { |
23 | &s390_kexec_elf_ops, |
24 | &s390_kexec_image_ops, |
25 | NULL, |
26 | }; |
27 | |
28 | #ifdef CONFIG_KEXEC_SIG |
29 | int s390_verify_sig(const char *kernel, unsigned long kernel_len) |
30 | { |
31 | const unsigned long marker_len = sizeof(MODULE_SIG_STRING) - 1; |
32 | struct module_signature *ms; |
33 | unsigned long sig_len; |
34 | int ret; |
35 | |
36 | /* Skip signature verification when not secure IPLed. */ |
37 | if (!ipl_secure_flag) |
38 | return 0; |
39 | |
40 | if (marker_len > kernel_len) |
41 | return -EKEYREJECTED; |
42 | |
43 | if (memcmp(p: kernel + kernel_len - marker_len, MODULE_SIG_STRING, |
44 | size: marker_len)) |
45 | return -EKEYREJECTED; |
46 | kernel_len -= marker_len; |
47 | |
48 | ms = (void *)kernel + kernel_len - sizeof(*ms); |
49 | kernel_len -= sizeof(*ms); |
50 | |
51 | sig_len = be32_to_cpu(ms->sig_len); |
52 | if (sig_len >= kernel_len) |
53 | return -EKEYREJECTED; |
54 | kernel_len -= sig_len; |
55 | |
56 | if (ms->id_type != PKEY_ID_PKCS7) |
57 | return -EKEYREJECTED; |
58 | |
59 | if (ms->algo != 0 || |
60 | ms->hash != 0 || |
61 | ms->signer_len != 0 || |
62 | ms->key_id_len != 0 || |
63 | ms->__pad[0] != 0 || |
64 | ms->__pad[1] != 0 || |
65 | ms->__pad[2] != 0) { |
66 | return -EBADMSG; |
67 | } |
68 | |
69 | ret = verify_pkcs7_signature(data: kernel, len: kernel_len, |
70 | raw_pkcs7: kernel + kernel_len, pkcs7_len: sig_len, |
71 | VERIFY_USE_SECONDARY_KEYRING, |
72 | usage: VERIFYING_MODULE_SIGNATURE, |
73 | NULL, NULL); |
74 | if (ret == -ENOKEY && IS_ENABLED(CONFIG_INTEGRITY_PLATFORM_KEYRING)) |
75 | ret = verify_pkcs7_signature(data: kernel, len: kernel_len, |
76 | raw_pkcs7: kernel + kernel_len, pkcs7_len: sig_len, |
77 | VERIFY_USE_PLATFORM_KEYRING, |
78 | usage: VERIFYING_MODULE_SIGNATURE, |
79 | NULL, NULL); |
80 | return ret; |
81 | } |
82 | #endif /* CONFIG_KEXEC_SIG */ |
83 | |
84 | static int kexec_file_update_purgatory(struct kimage *image, |
85 | struct s390_load_data *data) |
86 | { |
87 | u64 entry, type; |
88 | int ret; |
89 | |
90 | if (image->type == KEXEC_TYPE_CRASH) { |
91 | entry = STARTUP_KDUMP_OFFSET; |
92 | type = KEXEC_TYPE_CRASH; |
93 | } else { |
94 | entry = STARTUP_NORMAL_OFFSET; |
95 | type = KEXEC_TYPE_DEFAULT; |
96 | } |
97 | |
98 | ret = kexec_purgatory_get_set_symbol(image, name: "kernel_entry" , buf: &entry, |
99 | size: sizeof(entry), get_value: false); |
100 | if (ret) |
101 | return ret; |
102 | |
103 | ret = kexec_purgatory_get_set_symbol(image, name: "kernel_type" , buf: &type, |
104 | size: sizeof(type), get_value: false); |
105 | if (ret) |
106 | return ret; |
107 | |
108 | #ifdef CONFIG_CRASH_DUMP |
109 | if (image->type == KEXEC_TYPE_CRASH) { |
110 | u64 crash_size; |
111 | |
112 | ret = kexec_purgatory_get_set_symbol(image, name: "crash_start" , |
113 | buf: &crashk_res.start, |
114 | size: sizeof(crashk_res.start), |
115 | get_value: false); |
116 | if (ret) |
117 | return ret; |
118 | |
119 | crash_size = crashk_res.end - crashk_res.start + 1; |
120 | ret = kexec_purgatory_get_set_symbol(image, name: "crash_size" , |
121 | buf: &crash_size, |
122 | size: sizeof(crash_size), |
123 | get_value: false); |
124 | } |
125 | #endif |
126 | return ret; |
127 | } |
128 | |
129 | static int kexec_file_add_purgatory(struct kimage *image, |
130 | struct s390_load_data *data) |
131 | { |
132 | struct kexec_buf buf; |
133 | int ret; |
134 | |
135 | buf.image = image; |
136 | |
137 | data->memsz = ALIGN(data->memsz, PAGE_SIZE); |
138 | buf.mem = data->memsz; |
139 | #ifdef CONFIG_CRASH_DUMP |
140 | if (image->type == KEXEC_TYPE_CRASH) |
141 | buf.mem += crashk_res.start; |
142 | #endif |
143 | |
144 | ret = kexec_load_purgatory(image, kbuf: &buf); |
145 | if (ret) |
146 | return ret; |
147 | data->memsz += buf.memsz; |
148 | |
149 | return kexec_file_update_purgatory(image, data); |
150 | } |
151 | |
152 | static int kexec_file_add_initrd(struct kimage *image, |
153 | struct s390_load_data *data) |
154 | { |
155 | struct kexec_buf buf; |
156 | int ret; |
157 | |
158 | buf.image = image; |
159 | |
160 | buf.buffer = image->initrd_buf; |
161 | buf.bufsz = image->initrd_buf_len; |
162 | |
163 | data->memsz = ALIGN(data->memsz, PAGE_SIZE); |
164 | buf.mem = data->memsz; |
165 | #ifdef CONFIG_CRASH_DUMP |
166 | if (image->type == KEXEC_TYPE_CRASH) |
167 | buf.mem += crashk_res.start; |
168 | #endif |
169 | buf.memsz = buf.bufsz; |
170 | |
171 | data->parm->initrd_start = data->memsz; |
172 | data->parm->initrd_size = buf.memsz; |
173 | data->memsz += buf.memsz; |
174 | |
175 | ret = kexec_add_buffer(kbuf: &buf); |
176 | if (ret) |
177 | return ret; |
178 | |
179 | return ipl_report_add_component(data->report, &buf, 0, 0); |
180 | } |
181 | |
182 | static int kexec_file_add_ipl_report(struct kimage *image, |
183 | struct s390_load_data *data) |
184 | { |
185 | __u32 *lc_ipl_parmblock_ptr; |
186 | unsigned int len, ncerts; |
187 | struct kexec_buf buf; |
188 | unsigned long addr; |
189 | void *ptr, *end; |
190 | int ret; |
191 | |
192 | buf.image = image; |
193 | |
194 | data->memsz = ALIGN(data->memsz, PAGE_SIZE); |
195 | buf.mem = data->memsz; |
196 | |
197 | ptr = __va(ipl_cert_list_addr); |
198 | end = ptr + ipl_cert_list_size; |
199 | ncerts = 0; |
200 | while (ptr < end) { |
201 | ncerts++; |
202 | len = *(unsigned int *)ptr; |
203 | ptr += sizeof(len); |
204 | ptr += len; |
205 | } |
206 | |
207 | addr = data->memsz + data->report->size; |
208 | addr += ncerts * sizeof(struct ipl_rb_certificate_entry); |
209 | ptr = __va(ipl_cert_list_addr); |
210 | while (ptr < end) { |
211 | len = *(unsigned int *)ptr; |
212 | ptr += sizeof(len); |
213 | ipl_report_add_certificate(data->report, ptr, addr, len); |
214 | addr += len; |
215 | ptr += len; |
216 | } |
217 | |
218 | ret = -ENOMEM; |
219 | buf.buffer = ipl_report_finish(data->report); |
220 | if (!buf.buffer) |
221 | goto out; |
222 | buf.bufsz = data->report->size; |
223 | buf.memsz = buf.bufsz; |
224 | image->arch.ipl_buf = buf.buffer; |
225 | |
226 | data->memsz += buf.memsz; |
227 | |
228 | lc_ipl_parmblock_ptr = |
229 | data->kernel_buf + offsetof(struct lowcore, ipl_parmblock_ptr); |
230 | *lc_ipl_parmblock_ptr = (__u32)buf.mem; |
231 | |
232 | #ifdef CONFIG_CRASH_DUMP |
233 | if (image->type == KEXEC_TYPE_CRASH) |
234 | buf.mem += crashk_res.start; |
235 | #endif |
236 | |
237 | ret = kexec_add_buffer(kbuf: &buf); |
238 | out: |
239 | return ret; |
240 | } |
241 | |
242 | void *kexec_file_add_components(struct kimage *image, |
243 | int (*add_kernel)(struct kimage *image, |
244 | struct s390_load_data *data)) |
245 | { |
246 | unsigned long max_command_line_size = LEGACY_COMMAND_LINE_SIZE; |
247 | struct s390_load_data data = {0}; |
248 | unsigned long minsize; |
249 | int ret; |
250 | |
251 | data.report = ipl_report_init(&ipl_block); |
252 | if (IS_ERR(ptr: data.report)) |
253 | return data.report; |
254 | |
255 | ret = add_kernel(image, &data); |
256 | if (ret) |
257 | goto out; |
258 | |
259 | ret = -EINVAL; |
260 | minsize = PARMAREA + offsetof(struct parmarea, command_line); |
261 | if (image->kernel_buf_len < minsize) |
262 | goto out; |
263 | |
264 | if (data.parm->max_command_line_size) |
265 | max_command_line_size = data.parm->max_command_line_size; |
266 | |
267 | if (minsize + max_command_line_size < minsize) |
268 | goto out; |
269 | |
270 | if (image->kernel_buf_len < minsize + max_command_line_size) |
271 | goto out; |
272 | |
273 | if (image->cmdline_buf_len >= max_command_line_size) |
274 | goto out; |
275 | |
276 | memcpy(data.parm->command_line, image->cmdline_buf, |
277 | image->cmdline_buf_len); |
278 | |
279 | #ifdef CONFIG_CRASH_DUMP |
280 | if (image->type == KEXEC_TYPE_CRASH) { |
281 | data.parm->oldmem_base = crashk_res.start; |
282 | data.parm->oldmem_size = crashk_res.end - crashk_res.start + 1; |
283 | } |
284 | #endif |
285 | |
286 | if (image->initrd_buf) { |
287 | ret = kexec_file_add_initrd(image, data: &data); |
288 | if (ret) |
289 | goto out; |
290 | } |
291 | |
292 | ret = kexec_file_add_purgatory(image, data: &data); |
293 | if (ret) |
294 | goto out; |
295 | |
296 | if (data.kernel_mem == 0) { |
297 | unsigned long restart_psw = 0x0008000080000000UL; |
298 | restart_psw += image->start; |
299 | memcpy(data.kernel_buf, &restart_psw, sizeof(restart_psw)); |
300 | image->start = 0; |
301 | } |
302 | |
303 | ret = kexec_file_add_ipl_report(image, data: &data); |
304 | out: |
305 | ipl_report_free(data.report); |
306 | return ERR_PTR(error: ret); |
307 | } |
308 | |
309 | int arch_kexec_apply_relocations_add(struct purgatory_info *pi, |
310 | Elf_Shdr *section, |
311 | const Elf_Shdr *relsec, |
312 | const Elf_Shdr *symtab) |
313 | { |
314 | const char *strtab, *name, *shstrtab; |
315 | const Elf_Shdr *sechdrs; |
316 | Elf_Rela *relas; |
317 | int i, r_type; |
318 | int ret; |
319 | |
320 | /* String & section header string table */ |
321 | sechdrs = (void *)pi->ehdr + pi->ehdr->e_shoff; |
322 | strtab = (char *)pi->ehdr + sechdrs[symtab->sh_link].sh_offset; |
323 | shstrtab = (char *)pi->ehdr + sechdrs[pi->ehdr->e_shstrndx].sh_offset; |
324 | |
325 | relas = (void *)pi->ehdr + relsec->sh_offset; |
326 | |
327 | for (i = 0; i < relsec->sh_size / sizeof(*relas); i++) { |
328 | const Elf_Sym *sym; /* symbol to relocate */ |
329 | unsigned long addr; /* final location after relocation */ |
330 | unsigned long val; /* relocated symbol value */ |
331 | void *loc; /* tmp location to modify */ |
332 | |
333 | sym = (void *)pi->ehdr + symtab->sh_offset; |
334 | sym += ELF64_R_SYM(relas[i].r_info); |
335 | |
336 | if (sym->st_name) |
337 | name = strtab + sym->st_name; |
338 | else |
339 | name = shstrtab + sechdrs[sym->st_shndx].sh_name; |
340 | |
341 | if (sym->st_shndx == SHN_UNDEF) { |
342 | pr_err("Undefined symbol: %s\n" , name); |
343 | return -ENOEXEC; |
344 | } |
345 | |
346 | if (sym->st_shndx == SHN_COMMON) { |
347 | pr_err("symbol '%s' in common section\n" , name); |
348 | return -ENOEXEC; |
349 | } |
350 | |
351 | if (sym->st_shndx >= pi->ehdr->e_shnum && |
352 | sym->st_shndx != SHN_ABS) { |
353 | pr_err("Invalid section %d for symbol %s\n" , |
354 | sym->st_shndx, name); |
355 | return -ENOEXEC; |
356 | } |
357 | |
358 | loc = pi->purgatory_buf; |
359 | loc += section->sh_offset; |
360 | loc += relas[i].r_offset; |
361 | |
362 | val = sym->st_value; |
363 | if (sym->st_shndx != SHN_ABS) |
364 | val += pi->sechdrs[sym->st_shndx].sh_addr; |
365 | val += relas[i].r_addend; |
366 | |
367 | addr = section->sh_addr + relas[i].r_offset; |
368 | |
369 | r_type = ELF64_R_TYPE(relas[i].r_info); |
370 | |
371 | if (r_type == R_390_PLT32DBL) |
372 | r_type = R_390_PC32DBL; |
373 | |
374 | ret = arch_kexec_do_relocs(r_type, loc, val, addr); |
375 | if (ret) { |
376 | pr_err("Unknown rela relocation: %d\n" , r_type); |
377 | return -ENOEXEC; |
378 | } |
379 | } |
380 | return 0; |
381 | } |
382 | |
383 | int arch_kimage_file_post_load_cleanup(struct kimage *image) |
384 | { |
385 | vfree(addr: image->arch.ipl_buf); |
386 | image->arch.ipl_buf = NULL; |
387 | |
388 | return kexec_image_post_load_cleanup_default(image); |
389 | } |
390 | |