1 // SPDX-License-Identifier: GPL-2.0
3 * s390 code for kexec_file_load system call
5 * Copyright IBM Corp. 2018
7 * Author(s): Philipp Rudo <prudo@linux.vnet.ibm.com>
10 #include <linux/elf.h>
11 #include <linux/kexec.h>
12 #include <asm/setup.h>
14 const struct kexec_file_ops
* const kexec_file_loaders
[] = {
16 &s390_kexec_image_ops
,
20 int *kexec_file_update_kernel(struct kimage
*image
,
21 struct s390_load_data
*data
)
25 if (image
->cmdline_buf_len
>= ARCH_COMMAND_LINE_SIZE
)
26 return ERR_PTR(-EINVAL
);
28 if (image
->cmdline_buf_len
)
29 memcpy(data
->kernel_buf
+ COMMAND_LINE_OFFSET
,
30 image
->cmdline_buf
, image
->cmdline_buf_len
);
32 if (image
->type
== KEXEC_TYPE_CRASH
) {
33 loc
= (unsigned long *)(data
->kernel_buf
+ OLDMEM_BASE_OFFSET
);
34 *loc
= crashk_res
.start
;
36 loc
= (unsigned long *)(data
->kernel_buf
+ OLDMEM_SIZE_OFFSET
);
37 *loc
= crashk_res
.end
- crashk_res
.start
+ 1;
40 if (image
->initrd_buf
) {
41 loc
= (unsigned long *)(data
->kernel_buf
+ INITRD_START_OFFSET
);
42 *loc
= data
->initrd_load_addr
;
44 loc
= (unsigned long *)(data
->kernel_buf
+ INITRD_SIZE_OFFSET
);
45 *loc
= image
->initrd_buf_len
;
51 static int kexec_file_update_purgatory(struct kimage
*image
)
56 if (image
->type
== KEXEC_TYPE_CRASH
) {
57 entry
= STARTUP_KDUMP_OFFSET
;
58 type
= KEXEC_TYPE_CRASH
;
60 entry
= STARTUP_NORMAL_OFFSET
;
61 type
= KEXEC_TYPE_DEFAULT
;
64 ret
= kexec_purgatory_get_set_symbol(image
, "kernel_entry", &entry
,
65 sizeof(entry
), false);
69 ret
= kexec_purgatory_get_set_symbol(image
, "kernel_type", &type
,
74 if (image
->type
== KEXEC_TYPE_CRASH
) {
77 ret
= kexec_purgatory_get_set_symbol(image
, "crash_start",
79 sizeof(crashk_res
.start
),
84 crash_size
= crashk_res
.end
- crashk_res
.start
+ 1;
85 ret
= kexec_purgatory_get_set_symbol(image
, "crash_size",
93 int kexec_file_add_purgatory(struct kimage
*image
, struct s390_load_data
*data
)
100 data
->memsz
= ALIGN(data
->memsz
, PAGE_SIZE
);
101 buf
.mem
= data
->memsz
;
102 if (image
->type
== KEXEC_TYPE_CRASH
)
103 buf
.mem
+= crashk_res
.start
;
105 ret
= kexec_load_purgatory(image
, &buf
);
109 ret
= kexec_file_update_purgatory(image
);
113 int kexec_file_add_initrd(struct kimage
*image
, struct s390_load_data
*data
,
114 char *initrd
, unsigned long initrd_len
)
116 struct kexec_buf buf
;
122 buf
.bufsz
= initrd_len
;
124 data
->memsz
= ALIGN(data
->memsz
, PAGE_SIZE
);
125 buf
.mem
= data
->memsz
;
126 if (image
->type
== KEXEC_TYPE_CRASH
)
127 buf
.mem
+= crashk_res
.start
;
128 buf
.memsz
= buf
.bufsz
;
130 data
->initrd_load_addr
= buf
.mem
;
131 data
->memsz
+= buf
.memsz
;
133 ret
= kexec_add_buffer(&buf
);
137 int arch_kexec_apply_relocations_add(struct purgatory_info
*pi
,
139 const Elf_Shdr
*relsec
,
140 const Elf_Shdr
*symtab
)
145 relas
= (void *)pi
->ehdr
+ relsec
->sh_offset
;
147 for (i
= 0; i
< relsec
->sh_size
/ sizeof(*relas
); i
++) {
148 const Elf_Sym
*sym
; /* symbol to relocate */
149 unsigned long addr
; /* final location after relocation */
150 unsigned long val
; /* relocated symbol value */
151 void *loc
; /* tmp location to modify */
153 sym
= (void *)pi
->ehdr
+ symtab
->sh_offset
;
154 sym
+= ELF64_R_SYM(relas
[i
].r_info
);
156 if (sym
->st_shndx
== SHN_UNDEF
)
159 if (sym
->st_shndx
== SHN_COMMON
)
162 if (sym
->st_shndx
>= pi
->ehdr
->e_shnum
&&
163 sym
->st_shndx
!= SHN_ABS
)
166 loc
= pi
->purgatory_buf
;
167 loc
+= section
->sh_offset
;
168 loc
+= relas
[i
].r_offset
;
171 if (sym
->st_shndx
!= SHN_ABS
)
172 val
+= pi
->sechdrs
[sym
->st_shndx
].sh_addr
;
173 val
+= relas
[i
].r_addend
;
175 addr
= section
->sh_addr
+ relas
[i
].r_offset
;
177 switch (ELF64_R_TYPE(relas
[i
].r_info
)) {
178 case R_390_8
: /* Direct 8 bit. */
181 case R_390_12
: /* Direct 12 bit. */
182 *(u16
*)loc
&= 0xf000;
183 *(u16
*)loc
|= val
& 0xfff;
185 case R_390_16
: /* Direct 16 bit. */
188 case R_390_20
: /* Direct 20 bit. */
189 *(u32
*)loc
&= 0xf00000ff;
190 *(u32
*)loc
|= (val
& 0xfff) << 16; /* DL */
191 *(u32
*)loc
|= (val
& 0xff000) >> 4; /* DH */
193 case R_390_32
: /* Direct 32 bit. */
196 case R_390_64
: /* Direct 64 bit. */
199 case R_390_PC16
: /* PC relative 16 bit. */
200 *(u16
*)loc
= (val
- addr
);
202 case R_390_PC16DBL
: /* PC relative 16 bit shifted by 1. */
203 *(u16
*)loc
= (val
- addr
) >> 1;
205 case R_390_PC32DBL
: /* PC relative 32 bit shifted by 1. */
206 *(u32
*)loc
= (val
- addr
) >> 1;
208 case R_390_PC32
: /* PC relative 32 bit. */
209 *(u32
*)loc
= (val
- addr
);
211 case R_390_PC64
: /* PC relative 64 bit. */
212 *(u64
*)loc
= (val
- addr
);
221 int arch_kexec_kernel_image_probe(struct kimage
*image
, void *buf
,
222 unsigned long buf_len
)
224 /* A kernel must be at least large enough to contain head.S. During
225 * load memory in head.S will be accessed, e.g. to register the next
226 * command line. If the next kernel were smaller the current kernel
227 * will panic at load.
229 * 0x11000 = sizeof(head.S)
231 if (buf_len
< 0x11000)
234 return kexec_image_probe_default(image
, buf
, buf_len
);