2 * S390 kdump implementation
4 * Copyright IBM Corp. 2011
5 * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
8 #include <linux/crash_dump.h>
9 #include <asm/lowcore.h>
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/gfp.h>
13 #include <linux/slab.h>
14 #include <linux/crash_dump.h>
15 #include <linux/bootmem.h>
16 #include <linux/elf.h>
19 #define PTR_ADD(x, y) (((char *) (x)) + ((unsigned long) (y)))
20 #define PTR_SUB(x, y) (((char *) (x)) - ((unsigned long) (y)))
21 #define PTR_DIFF(x, y) ((unsigned long)(((char *) (x)) - ((unsigned long) (y))))
24 * Copy one page from "oldmem"
26 * For the kdump reserved memory this functions performs a swap operation:
27 * - [OLDMEM_BASE - OLDMEM_BASE + OLDMEM_SIZE] is mapped to [0 - OLDMEM_SIZE].
28 * - [0 - OLDMEM_SIZE] is mapped to [OLDMEM_BASE - OLDMEM_BASE + OLDMEM_SIZE]
30 ssize_t
copy_oldmem_page(unsigned long pfn
, char *buf
,
31 size_t csize
, unsigned long offset
, int userbuf
)
39 src
= (pfn
<< PAGE_SHIFT
) + offset
;
40 if (src
< OLDMEM_SIZE
)
42 else if (src
> OLDMEM_BASE
&&
43 src
< OLDMEM_BASE
+ OLDMEM_SIZE
)
46 rc
= copy_to_user_real((void __user
*) buf
, (void *) src
,
49 rc
= memcpy_real(buf
, (void *) src
, csize
);
50 return rc
< 0 ? rc
: csize
;
54 * Copy memory from old kernel
56 static int copy_from_oldmem(void *dest
, void *src
, size_t count
)
58 unsigned long copied
= 0;
61 if ((unsigned long) src
< OLDMEM_SIZE
) {
62 copied
= min(count
, OLDMEM_SIZE
- (unsigned long) src
);
63 rc
= memcpy_real(dest
, src
+ OLDMEM_BASE
, copied
);
67 return memcpy_real(dest
+ copied
, src
+ copied
, count
- copied
);
71 * Alloc memory and panic in case of ENOMEM
73 static void *kzalloc_panic(int len
)
77 rc
= kzalloc(len
, GFP_KERNEL
);
79 panic("s390 kdump kzalloc (%d) failed", len
);
84 * Get memory layout and create hole for oldmem
86 static struct mem_chunk
*get_memory_layout(void)
88 struct mem_chunk
*chunk_array
;
90 chunk_array
= kzalloc_panic(MEMORY_CHUNKS
* sizeof(struct mem_chunk
));
91 detect_memory_layout(chunk_array
);
92 create_mem_hole(chunk_array
, OLDMEM_BASE
, OLDMEM_SIZE
, CHUNK_CRASHK
);
99 static void *nt_init(void *buf
, Elf64_Word type
, void *desc
, int d_len
,
105 note
= (Elf64_Nhdr
*)buf
;
106 note
->n_namesz
= strlen(name
) + 1;
107 note
->n_descsz
= d_len
;
109 len
= sizeof(Elf64_Nhdr
);
111 memcpy(buf
+ len
, name
, note
->n_namesz
);
112 len
= roundup(len
+ note
->n_namesz
, 4);
114 memcpy(buf
+ len
, desc
, note
->n_descsz
);
115 len
= roundup(len
+ note
->n_descsz
, 4);
117 return PTR_ADD(buf
, len
);
121 * Initialize prstatus note
123 static void *nt_prstatus(void *ptr
, struct save_area
*sa
)
125 struct elf_prstatus nt_prstatus
;
126 static int cpu_nr
= 1;
128 memset(&nt_prstatus
, 0, sizeof(nt_prstatus
));
129 memcpy(&nt_prstatus
.pr_reg
.gprs
, sa
->gp_regs
, sizeof(sa
->gp_regs
));
130 memcpy(&nt_prstatus
.pr_reg
.psw
, sa
->psw
, sizeof(sa
->psw
));
131 memcpy(&nt_prstatus
.pr_reg
.acrs
, sa
->acc_regs
, sizeof(sa
->acc_regs
));
132 nt_prstatus
.pr_pid
= cpu_nr
;
135 return nt_init(ptr
, NT_PRSTATUS
, &nt_prstatus
, sizeof(nt_prstatus
),
140 * Initialize fpregset (floating point) note
142 static void *nt_fpregset(void *ptr
, struct save_area
*sa
)
144 elf_fpregset_t nt_fpregset
;
146 memset(&nt_fpregset
, 0, sizeof(nt_fpregset
));
147 memcpy(&nt_fpregset
.fpc
, &sa
->fp_ctrl_reg
, sizeof(sa
->fp_ctrl_reg
));
148 memcpy(&nt_fpregset
.fprs
, &sa
->fp_regs
, sizeof(sa
->fp_regs
));
150 return nt_init(ptr
, NT_PRFPREG
, &nt_fpregset
, sizeof(nt_fpregset
),
155 * Initialize timer note
157 static void *nt_s390_timer(void *ptr
, struct save_area
*sa
)
159 return nt_init(ptr
, NT_S390_TIMER
, &sa
->timer
, sizeof(sa
->timer
),
160 KEXEC_CORE_NOTE_NAME
);
164 * Initialize TOD clock comparator note
166 static void *nt_s390_tod_cmp(void *ptr
, struct save_area
*sa
)
168 return nt_init(ptr
, NT_S390_TODCMP
, &sa
->clk_cmp
,
169 sizeof(sa
->clk_cmp
), KEXEC_CORE_NOTE_NAME
);
173 * Initialize TOD programmable register note
175 static void *nt_s390_tod_preg(void *ptr
, struct save_area
*sa
)
177 return nt_init(ptr
, NT_S390_TODPREG
, &sa
->tod_reg
,
178 sizeof(sa
->tod_reg
), KEXEC_CORE_NOTE_NAME
);
182 * Initialize control register note
184 static void *nt_s390_ctrs(void *ptr
, struct save_area
*sa
)
186 return nt_init(ptr
, NT_S390_CTRS
, &sa
->ctrl_regs
,
187 sizeof(sa
->ctrl_regs
), KEXEC_CORE_NOTE_NAME
);
191 * Initialize prefix register note
193 static void *nt_s390_prefix(void *ptr
, struct save_area
*sa
)
195 return nt_init(ptr
, NT_S390_PREFIX
, &sa
->pref_reg
,
196 sizeof(sa
->pref_reg
), KEXEC_CORE_NOTE_NAME
);
200 * Fill ELF notes for one CPU with save area registers
202 void *fill_cpu_elf_notes(void *ptr
, struct save_area
*sa
)
204 ptr
= nt_prstatus(ptr
, sa
);
205 ptr
= nt_fpregset(ptr
, sa
);
206 ptr
= nt_s390_timer(ptr
, sa
);
207 ptr
= nt_s390_tod_cmp(ptr
, sa
);
208 ptr
= nt_s390_tod_preg(ptr
, sa
);
209 ptr
= nt_s390_ctrs(ptr
, sa
);
210 ptr
= nt_s390_prefix(ptr
, sa
);
215 * Initialize prpsinfo note (new kernel)
217 static void *nt_prpsinfo(void *ptr
)
219 struct elf_prpsinfo prpsinfo
;
221 memset(&prpsinfo
, 0, sizeof(prpsinfo
));
222 prpsinfo
.pr_sname
= 'R';
223 strcpy(prpsinfo
.pr_fname
, "vmlinux");
224 return nt_init(ptr
, NT_PRPSINFO
, &prpsinfo
, sizeof(prpsinfo
),
225 KEXEC_CORE_NOTE_NAME
);
229 * Initialize vmcoreinfo note (new kernel)
231 static void *nt_vmcoreinfo(void *ptr
)
233 char nt_name
[11], *vmcoreinfo
;
237 if (copy_from_oldmem(&addr
, &S390_lowcore
.vmcore_info
, sizeof(addr
)))
239 memset(nt_name
, 0, sizeof(nt_name
));
240 if (copy_from_oldmem(¬e
, addr
, sizeof(note
)))
242 if (copy_from_oldmem(nt_name
, addr
+ sizeof(note
), sizeof(nt_name
) - 1))
244 if (strcmp(nt_name
, "VMCOREINFO") != 0)
246 vmcoreinfo
= kzalloc_panic(note
.n_descsz
+ 1);
247 if (copy_from_oldmem(vmcoreinfo
, addr
+ 24, note
.n_descsz
))
249 vmcoreinfo
[note
.n_descsz
+ 1] = 0;
250 return nt_init(ptr
, 0, vmcoreinfo
, note
.n_descsz
, "VMCOREINFO");
254 * Initialize ELF header (new kernel)
256 static void *ehdr_init(Elf64_Ehdr
*ehdr
, int mem_chunk_cnt
)
258 memset(ehdr
, 0, sizeof(*ehdr
));
259 memcpy(ehdr
->e_ident
, ELFMAG
, SELFMAG
);
260 ehdr
->e_ident
[EI_CLASS
] = ELFCLASS64
;
261 ehdr
->e_ident
[EI_DATA
] = ELFDATA2MSB
;
262 ehdr
->e_ident
[EI_VERSION
] = EV_CURRENT
;
263 memset(ehdr
->e_ident
+ EI_PAD
, 0, EI_NIDENT
- EI_PAD
);
264 ehdr
->e_type
= ET_CORE
;
265 ehdr
->e_machine
= EM_S390
;
266 ehdr
->e_version
= EV_CURRENT
;
267 ehdr
->e_phoff
= sizeof(Elf64_Ehdr
);
268 ehdr
->e_ehsize
= sizeof(Elf64_Ehdr
);
269 ehdr
->e_phentsize
= sizeof(Elf64_Phdr
);
270 ehdr
->e_phnum
= mem_chunk_cnt
+ 1;
275 * Return CPU count for ELF header (new kernel)
277 static int get_cpu_cnt(void)
281 for (i
= 0; zfcpdump_save_areas
[i
]; i
++) {
282 if (zfcpdump_save_areas
[i
]->pref_reg
== 0)
290 * Return memory chunk count for ELF header (new kernel)
292 static int get_mem_chunk_cnt(void)
294 struct mem_chunk
*chunk_array
, *mem_chunk
;
297 chunk_array
= get_memory_layout();
298 for (i
= 0; i
< MEMORY_CHUNKS
; i
++) {
299 mem_chunk
= &chunk_array
[i
];
300 if (chunk_array
[i
].type
!= CHUNK_READ_WRITE
&&
301 chunk_array
[i
].type
!= CHUNK_READ_ONLY
)
303 if (mem_chunk
->size
== 0)
312 * Relocate pointer in order to allow vmcore code access the data
314 static inline unsigned long relocate(unsigned long addr
)
316 return OLDMEM_BASE
+ addr
;
320 * Initialize ELF loads (new kernel)
322 static int loads_init(Elf64_Phdr
*phdr
, u64 loads_offset
)
324 struct mem_chunk
*chunk_array
, *mem_chunk
;
327 chunk_array
= get_memory_layout();
328 for (i
= 0; i
< MEMORY_CHUNKS
; i
++) {
329 mem_chunk
= &chunk_array
[i
];
330 if (mem_chunk
->size
== 0)
332 if (chunk_array
[i
].type
!= CHUNK_READ_WRITE
&&
333 chunk_array
[i
].type
!= CHUNK_READ_ONLY
)
336 phdr
->p_filesz
= mem_chunk
->size
;
337 phdr
->p_type
= PT_LOAD
;
338 phdr
->p_offset
= mem_chunk
->addr
;
339 phdr
->p_vaddr
= mem_chunk
->addr
;
340 phdr
->p_paddr
= mem_chunk
->addr
;
341 phdr
->p_memsz
= mem_chunk
->size
;
342 phdr
->p_flags
= PF_R
| PF_W
| PF_X
;
343 phdr
->p_align
= PAGE_SIZE
;
351 * Initialize notes (new kernel)
353 static void *notes_init(Elf64_Phdr
*phdr
, void *ptr
, u64 notes_offset
)
355 struct save_area
*sa
;
356 void *ptr_start
= ptr
;
359 ptr
= nt_prpsinfo(ptr
);
361 for (i
= 0; zfcpdump_save_areas
[i
]; i
++) {
362 sa
= zfcpdump_save_areas
[i
];
363 if (sa
->pref_reg
== 0)
365 ptr
= fill_cpu_elf_notes(ptr
, sa
);
367 ptr
= nt_vmcoreinfo(ptr
);
368 memset(phdr
, 0, sizeof(*phdr
));
369 phdr
->p_type
= PT_NOTE
;
370 phdr
->p_offset
= relocate(notes_offset
);
371 phdr
->p_filesz
= (unsigned long) PTR_SUB(ptr
, ptr_start
);
372 phdr
->p_memsz
= phdr
->p_filesz
;
377 * Create ELF core header (new kernel)
379 static void s390_elf_corehdr_create(char **elfcorebuf
, size_t *elfcorebuf_sz
)
381 Elf64_Phdr
*phdr_notes
, *phdr_loads
;
387 mem_chunk_cnt
= get_mem_chunk_cnt();
389 alloc_size
= 0x1000 + get_cpu_cnt() * 0x300 +
390 mem_chunk_cnt
* sizeof(Elf64_Phdr
);
391 hdr
= kzalloc_panic(alloc_size
);
392 /* Init elf header */
393 ptr
= ehdr_init(hdr
, mem_chunk_cnt
);
394 /* Init program headers */
396 ptr
= PTR_ADD(ptr
, sizeof(Elf64_Phdr
));
398 ptr
= PTR_ADD(ptr
, sizeof(Elf64_Phdr
) * mem_chunk_cnt
);
400 hdr_off
= PTR_DIFF(ptr
, hdr
);
401 ptr
= notes_init(phdr_notes
, ptr
, ((unsigned long) hdr
) + hdr_off
);
403 hdr_off
= PTR_DIFF(ptr
, hdr
);
404 loads_init(phdr_loads
, ((unsigned long) hdr
) + hdr_off
);
405 *elfcorebuf_sz
= hdr_off
;
406 *elfcorebuf
= (void *) relocate((unsigned long) hdr
);
407 BUG_ON(*elfcorebuf_sz
> alloc_size
);
411 * Create kdump ELF core header in new kernel, if it has not been passed via
412 * the "elfcorehdr" kernel parameter
414 static int setup_kdump_elfcorehdr(void)
416 size_t elfcorebuf_sz
;
419 if (!OLDMEM_BASE
|| is_kdump_kernel())
421 s390_elf_corehdr_create(&elfcorebuf
, &elfcorebuf_sz
);
422 elfcorehdr_addr
= (unsigned long long) elfcorebuf
;
423 elfcorehdr_size
= elfcorebuf_sz
;
427 subsys_initcall(setup_kdump_elfcorehdr
);