Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/s390/kernel/machine_kexec_file.c
26424 views
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 <[email protected]>
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(kernel + kernel_len - marker_len, MODULE_SIG_STRING,
44
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(kernel, kernel_len,
70
kernel + kernel_len, sig_len,
71
VERIFY_USE_SECONDARY_KEYRING,
72
VERIFYING_MODULE_SIGNATURE,
73
NULL, NULL);
74
if (ret == -ENOKEY && IS_ENABLED(CONFIG_INTEGRITY_PLATFORM_KEYRING))
75
ret = verify_pkcs7_signature(kernel, kernel_len,
76
kernel + kernel_len, sig_len,
77
VERIFY_USE_PLATFORM_KEYRING,
78
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, "kernel_entry", &entry,
99
sizeof(entry), false);
100
if (ret)
101
return ret;
102
103
ret = kexec_purgatory_get_set_symbol(image, "kernel_type", &type,
104
sizeof(type), 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, "crash_start",
113
&crashk_res.start,
114
sizeof(crashk_res.start),
115
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, "crash_size",
121
&crash_size,
122
sizeof(crash_size),
123
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, &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(&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(&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(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);
288
if (ret)
289
goto out;
290
}
291
292
ret = kexec_file_add_purgatory(image, &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);
304
out:
305
ipl_report_free(data.report);
306
return ERR_PTR(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(image->arch.ipl_buf);
386
image->arch.ipl_buf = NULL;
387
388
return kexec_image_post_load_cleanup_default(image);
389
}
390
391