s390/vmcore: use vmcore for zfcpdump
[deliverable/linux.git] / arch / s390 / kernel / crash_dump.c
CommitLineData
60a0c68d
MH
1/*
2 * S390 kdump implementation
3 *
4 * Copyright IBM Corp. 2011
5 * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
6 */
7
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>
60a0c68d
MH
14#include <linux/bootmem.h>
15#include <linux/elf.h>
4857d4bb 16#include <asm/os_info.h>
6b563d8c
HC
17#include <asm/elf.h>
18#include <asm/ipl.h>
6f79d332 19#include <asm/sclp.h>
60a0c68d
MH
20
21#define PTR_ADD(x, y) (((char *) (x)) + ((unsigned long) (y)))
22#define PTR_SUB(x, y) (((char *) (x)) - ((unsigned long) (y)))
23#define PTR_DIFF(x, y) ((unsigned long)(((char *) (x)) - ((unsigned long) (y))))
24
191a2fa0
MH
25
26/*
27 * Return physical address for virtual address
28 */
29static inline void *load_real_addr(void *addr)
30{
31 unsigned long real_addr;
32
33 asm volatile(
34 " lra %0,0(%1)\n"
35 " jz 0f\n"
36 " la %0,0\n"
37 "0:"
38 : "=a" (real_addr) : "a" (addr) : "cc");
39 return (void *)real_addr;
40}
41
42/*
43 * Copy up to one page to vmalloc or real memory
44 */
45static ssize_t copy_page_real(void *buf, void *src, size_t csize)
46{
47 size_t size;
48
49 if (is_vmalloc_addr(buf)) {
50 BUG_ON(csize >= PAGE_SIZE);
51 /* If buf is not page aligned, copy first part */
52 size = min(roundup(__pa(buf), PAGE_SIZE) - __pa(buf), csize);
53 if (size) {
54 if (memcpy_real(load_real_addr(buf), src, size))
55 return -EFAULT;
56 buf += size;
57 src += size;
58 }
59 /* Copy second part */
60 size = csize - size;
61 return (size) ? memcpy_real(load_real_addr(buf), src, size) : 0;
62 } else {
63 return memcpy_real(buf, src, csize);
64 }
65}
66
97b0f6f9
MH
67/*
68 * Pointer to ELF header in new kernel
69 */
70static void *elfcorehdr_newmem;
71
60a0c68d 72/*
6f79d332
MH
73 * Copy one page from zfcpdump "oldmem"
74 *
75 * For pages below ZFCPDUMP_HSA_SIZE memory from the HSA is copied. Otherwise
76 * real memory copy is used.
77 */
78static ssize_t copy_oldmem_page_zfcpdump(char *buf, size_t csize,
79 unsigned long src, int userbuf)
80{
81 int rc;
82
83 if (src < ZFCPDUMP_HSA_SIZE) {
84 rc = memcpy_hsa(buf, src, csize, userbuf);
85 } else {
86 if (userbuf)
87 rc = copy_to_user_real((void __force __user *) buf,
88 (void *) src, csize);
89 else
90 rc = memcpy_real(buf, (void *) src, csize);
91 }
92 return rc ? rc : csize;
93}
94
95/*
96 * Copy one page from kdump "oldmem"
60a0c68d
MH
97 *
98 * For the kdump reserved memory this functions performs a swap operation:
99 * - [OLDMEM_BASE - OLDMEM_BASE + OLDMEM_SIZE] is mapped to [0 - OLDMEM_SIZE].
100 * - [0 - OLDMEM_SIZE] is mapped to [OLDMEM_BASE - OLDMEM_BASE + OLDMEM_SIZE]
101 */
6f79d332
MH
102static ssize_t copy_oldmem_page_kdump(char *buf, size_t csize,
103 unsigned long src, int userbuf)
104
60a0c68d 105{
191a2fa0 106 int rc;
60a0c68d 107
60a0c68d
MH
108 if (src < OLDMEM_SIZE)
109 src += OLDMEM_BASE;
110 else if (src > OLDMEM_BASE &&
111 src < OLDMEM_BASE + OLDMEM_SIZE)
112 src -= OLDMEM_BASE;
113 if (userbuf)
191a2fa0
MH
114 rc = copy_to_user_real((void __force __user *) buf,
115 (void *) src, csize);
60a0c68d 116 else
191a2fa0 117 rc = copy_page_real(buf, (void *) src, csize);
6f79d332
MH
118 return (rc == 0) ? rc : csize;
119}
120
121/*
122 * Copy one page from "oldmem"
123 */
124ssize_t copy_oldmem_page(unsigned long pfn, char *buf, size_t csize,
125 unsigned long offset, int userbuf)
126{
127 unsigned long src;
128
129 if (!csize)
130 return 0;
131 src = (pfn << PAGE_SHIFT) + offset;
132 if (OLDMEM_BASE)
133 return copy_oldmem_page_kdump(buf, csize, src, userbuf);
134 else
135 return copy_oldmem_page_zfcpdump(buf, csize, src, userbuf);
60a0c68d
MH
136}
137
23df79da 138/*
6f79d332 139 * Remap "oldmem" for kdump
23df79da
JW
140 *
141 * For the kdump reserved memory this functions performs a swap operation:
142 * [0 - OLDMEM_SIZE] is mapped to [OLDMEM_BASE - OLDMEM_BASE + OLDMEM_SIZE]
143 */
6f79d332
MH
144static int remap_oldmem_pfn_range_kdump(struct vm_area_struct *vma,
145 unsigned long from, unsigned long pfn,
146 unsigned long size, pgprot_t prot)
23df79da
JW
147{
148 unsigned long size_old;
149 int rc;
150
151 if (pfn < OLDMEM_SIZE >> PAGE_SHIFT) {
152 size_old = min(size, OLDMEM_SIZE - (pfn << PAGE_SHIFT));
153 rc = remap_pfn_range(vma, from,
154 pfn + (OLDMEM_BASE >> PAGE_SHIFT),
155 size_old, prot);
156 if (rc || size == size_old)
157 return rc;
158 size -= size_old;
159 from += size_old;
160 pfn += size_old >> PAGE_SHIFT;
161 }
162 return remap_pfn_range(vma, from, pfn, size, prot);
163}
164
6f79d332
MH
165/*
166 * Remap "oldmem" for zfcpdump
167 *
168 * We only map available memory above ZFCPDUMP_HSA_SIZE. Memory below
169 * ZFCPDUMP_HSA_SIZE is read on demand using the copy_oldmem_page() function.
170 */
171static int remap_oldmem_pfn_range_zfcpdump(struct vm_area_struct *vma,
172 unsigned long from,
173 unsigned long pfn,
174 unsigned long size, pgprot_t prot)
175{
176 unsigned long size_hsa;
177
178 if (pfn < ZFCPDUMP_HSA_SIZE >> PAGE_SHIFT) {
179 size_hsa = min(size, ZFCPDUMP_HSA_SIZE - (pfn << PAGE_SHIFT));
180 if (size == size_hsa)
181 return 0;
182 size -= size_hsa;
183 from += size_hsa;
184 pfn += size_hsa >> PAGE_SHIFT;
185 }
186 return remap_pfn_range(vma, from, pfn, size, prot);
187}
188
189/*
190 * Remap "oldmem" for kdump or zfcpdump
191 */
192int remap_oldmem_pfn_range(struct vm_area_struct *vma, unsigned long from,
193 unsigned long pfn, unsigned long size, pgprot_t prot)
194{
195 if (OLDMEM_BASE)
196 return remap_oldmem_pfn_range_kdump(vma, from, pfn, size, prot);
197 else
198 return remap_oldmem_pfn_range_zfcpdump(vma, from, pfn, size,
199 prot);
200}
201
60a0c68d
MH
202/*
203 * Copy memory from old kernel
204 */
4857d4bb 205int copy_from_oldmem(void *dest, void *src, size_t count)
60a0c68d
MH
206{
207 unsigned long copied = 0;
208 int rc;
209
6f79d332
MH
210 if (OLDMEM_BASE) {
211 if ((unsigned long) src < OLDMEM_SIZE) {
212 copied = min(count, OLDMEM_SIZE - (unsigned long) src);
213 rc = memcpy_real(dest, src + OLDMEM_BASE, copied);
214 if (rc)
215 return rc;
216 }
217 } else {
218 if ((unsigned long) src < ZFCPDUMP_HSA_SIZE) {
219 copied = min(count,
220 ZFCPDUMP_HSA_SIZE - (unsigned long) src);
221 rc = memcpy_hsa(dest, (unsigned long) src, copied, 0);
222 if (rc)
223 return rc;
224 }
60a0c68d
MH
225 }
226 return memcpy_real(dest + copied, src + copied, count - copied);
227}
228
229/*
230 * Alloc memory and panic in case of ENOMEM
231 */
232static void *kzalloc_panic(int len)
233{
234 void *rc;
235
236 rc = kzalloc(len, GFP_KERNEL);
237 if (!rc)
238 panic("s390 kdump kzalloc (%d) failed", len);
239 return rc;
240}
241
242/*
243 * Get memory layout and create hole for oldmem
244 */
245static struct mem_chunk *get_memory_layout(void)
246{
247 struct mem_chunk *chunk_array;
248
249 chunk_array = kzalloc_panic(MEMORY_CHUNKS * sizeof(struct mem_chunk));
df1bd59c 250 detect_memory_layout(chunk_array, 0);
996b4a7d 251 create_mem_hole(chunk_array, OLDMEM_BASE, OLDMEM_SIZE);
60a0c68d
MH
252 return chunk_array;
253}
254
255/*
256 * Initialize ELF note
257 */
258static void *nt_init(void *buf, Elf64_Word type, void *desc, int d_len,
259 const char *name)
260{
261 Elf64_Nhdr *note;
262 u64 len;
263
264 note = (Elf64_Nhdr *)buf;
265 note->n_namesz = strlen(name) + 1;
266 note->n_descsz = d_len;
267 note->n_type = type;
268 len = sizeof(Elf64_Nhdr);
269
270 memcpy(buf + len, name, note->n_namesz);
271 len = roundup(len + note->n_namesz, 4);
272
273 memcpy(buf + len, desc, note->n_descsz);
274 len = roundup(len + note->n_descsz, 4);
275
276 return PTR_ADD(buf, len);
277}
278
279/*
280 * Initialize prstatus note
281 */
282static void *nt_prstatus(void *ptr, struct save_area *sa)
283{
284 struct elf_prstatus nt_prstatus;
285 static int cpu_nr = 1;
286
287 memset(&nt_prstatus, 0, sizeof(nt_prstatus));
288 memcpy(&nt_prstatus.pr_reg.gprs, sa->gp_regs, sizeof(sa->gp_regs));
289 memcpy(&nt_prstatus.pr_reg.psw, sa->psw, sizeof(sa->psw));
290 memcpy(&nt_prstatus.pr_reg.acrs, sa->acc_regs, sizeof(sa->acc_regs));
291 nt_prstatus.pr_pid = cpu_nr;
292 cpu_nr++;
293
294 return nt_init(ptr, NT_PRSTATUS, &nt_prstatus, sizeof(nt_prstatus),
295 "CORE");
296}
297
298/*
299 * Initialize fpregset (floating point) note
300 */
301static void *nt_fpregset(void *ptr, struct save_area *sa)
302{
303 elf_fpregset_t nt_fpregset;
304
305 memset(&nt_fpregset, 0, sizeof(nt_fpregset));
306 memcpy(&nt_fpregset.fpc, &sa->fp_ctrl_reg, sizeof(sa->fp_ctrl_reg));
307 memcpy(&nt_fpregset.fprs, &sa->fp_regs, sizeof(sa->fp_regs));
308
309 return nt_init(ptr, NT_PRFPREG, &nt_fpregset, sizeof(nt_fpregset),
310 "CORE");
311}
312
313/*
314 * Initialize timer note
315 */
316static void *nt_s390_timer(void *ptr, struct save_area *sa)
317{
318 return nt_init(ptr, NT_S390_TIMER, &sa->timer, sizeof(sa->timer),
319 KEXEC_CORE_NOTE_NAME);
320}
321
322/*
323 * Initialize TOD clock comparator note
324 */
325static void *nt_s390_tod_cmp(void *ptr, struct save_area *sa)
326{
327 return nt_init(ptr, NT_S390_TODCMP, &sa->clk_cmp,
328 sizeof(sa->clk_cmp), KEXEC_CORE_NOTE_NAME);
329}
330
331/*
332 * Initialize TOD programmable register note
333 */
334static void *nt_s390_tod_preg(void *ptr, struct save_area *sa)
335{
336 return nt_init(ptr, NT_S390_TODPREG, &sa->tod_reg,
337 sizeof(sa->tod_reg), KEXEC_CORE_NOTE_NAME);
338}
339
340/*
341 * Initialize control register note
342 */
343static void *nt_s390_ctrs(void *ptr, struct save_area *sa)
344{
345 return nt_init(ptr, NT_S390_CTRS, &sa->ctrl_regs,
346 sizeof(sa->ctrl_regs), KEXEC_CORE_NOTE_NAME);
347}
348
349/*
350 * Initialize prefix register note
351 */
352static void *nt_s390_prefix(void *ptr, struct save_area *sa)
353{
354 return nt_init(ptr, NT_S390_PREFIX, &sa->pref_reg,
355 sizeof(sa->pref_reg), KEXEC_CORE_NOTE_NAME);
356}
357
358/*
359 * Fill ELF notes for one CPU with save area registers
360 */
361void *fill_cpu_elf_notes(void *ptr, struct save_area *sa)
362{
363 ptr = nt_prstatus(ptr, sa);
364 ptr = nt_fpregset(ptr, sa);
365 ptr = nt_s390_timer(ptr, sa);
366 ptr = nt_s390_tod_cmp(ptr, sa);
367 ptr = nt_s390_tod_preg(ptr, sa);
368 ptr = nt_s390_ctrs(ptr, sa);
369 ptr = nt_s390_prefix(ptr, sa);
370 return ptr;
371}
372
373/*
374 * Initialize prpsinfo note (new kernel)
375 */
376static void *nt_prpsinfo(void *ptr)
377{
378 struct elf_prpsinfo prpsinfo;
379
380 memset(&prpsinfo, 0, sizeof(prpsinfo));
381 prpsinfo.pr_sname = 'R';
382 strcpy(prpsinfo.pr_fname, "vmlinux");
383 return nt_init(ptr, NT_PRPSINFO, &prpsinfo, sizeof(prpsinfo),
384 KEXEC_CORE_NOTE_NAME);
385}
386
387/*
4857d4bb 388 * Get vmcoreinfo using lowcore->vmcore_info (new kernel)
60a0c68d 389 */
4857d4bb 390static void *get_vmcoreinfo_old(unsigned long *size)
60a0c68d
MH
391{
392 char nt_name[11], *vmcoreinfo;
393 Elf64_Nhdr note;
394 void *addr;
395
396 if (copy_from_oldmem(&addr, &S390_lowcore.vmcore_info, sizeof(addr)))
4857d4bb 397 return NULL;
60a0c68d
MH
398 memset(nt_name, 0, sizeof(nt_name));
399 if (copy_from_oldmem(&note, addr, sizeof(note)))
4857d4bb 400 return NULL;
60a0c68d 401 if (copy_from_oldmem(nt_name, addr + sizeof(note), sizeof(nt_name) - 1))
4857d4bb 402 return NULL;
60a0c68d 403 if (strcmp(nt_name, "VMCOREINFO") != 0)
4857d4bb
MH
404 return NULL;
405 vmcoreinfo = kzalloc_panic(note.n_descsz);
60a0c68d 406 if (copy_from_oldmem(vmcoreinfo, addr + 24, note.n_descsz))
4857d4bb
MH
407 return NULL;
408 *size = note.n_descsz;
409 return vmcoreinfo;
410}
411
412/*
413 * Initialize vmcoreinfo note (new kernel)
414 */
415static void *nt_vmcoreinfo(void *ptr)
416{
417 unsigned long size;
418 void *vmcoreinfo;
419
420 vmcoreinfo = os_info_old_entry(OS_INFO_VMCOREINFO, &size);
421 if (!vmcoreinfo)
422 vmcoreinfo = get_vmcoreinfo_old(&size);
423 if (!vmcoreinfo)
60a0c68d 424 return ptr;
4857d4bb 425 return nt_init(ptr, 0, vmcoreinfo, size, "VMCOREINFO");
60a0c68d
MH
426}
427
428/*
429 * Initialize ELF header (new kernel)
430 */
431static void *ehdr_init(Elf64_Ehdr *ehdr, int mem_chunk_cnt)
432{
433 memset(ehdr, 0, sizeof(*ehdr));
434 memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
435 ehdr->e_ident[EI_CLASS] = ELFCLASS64;
436 ehdr->e_ident[EI_DATA] = ELFDATA2MSB;
437 ehdr->e_ident[EI_VERSION] = EV_CURRENT;
438 memset(ehdr->e_ident + EI_PAD, 0, EI_NIDENT - EI_PAD);
439 ehdr->e_type = ET_CORE;
440 ehdr->e_machine = EM_S390;
441 ehdr->e_version = EV_CURRENT;
442 ehdr->e_phoff = sizeof(Elf64_Ehdr);
443 ehdr->e_ehsize = sizeof(Elf64_Ehdr);
444 ehdr->e_phentsize = sizeof(Elf64_Phdr);
445 ehdr->e_phnum = mem_chunk_cnt + 1;
446 return ehdr + 1;
447}
448
449/*
450 * Return CPU count for ELF header (new kernel)
451 */
452static int get_cpu_cnt(void)
453{
454 int i, cpus = 0;
455
456 for (i = 0; zfcpdump_save_areas[i]; i++) {
457 if (zfcpdump_save_areas[i]->pref_reg == 0)
458 continue;
459 cpus++;
460 }
461 return cpus;
462}
463
464/*
465 * Return memory chunk count for ELF header (new kernel)
466 */
467static int get_mem_chunk_cnt(void)
468{
469 struct mem_chunk *chunk_array, *mem_chunk;
470 int i, cnt = 0;
471
472 chunk_array = get_memory_layout();
473 for (i = 0; i < MEMORY_CHUNKS; i++) {
474 mem_chunk = &chunk_array[i];
475 if (chunk_array[i].type != CHUNK_READ_WRITE &&
476 chunk_array[i].type != CHUNK_READ_ONLY)
477 continue;
478 if (mem_chunk->size == 0)
479 continue;
480 cnt++;
481 }
482 kfree(chunk_array);
483 return cnt;
484}
485
60a0c68d
MH
486/*
487 * Initialize ELF loads (new kernel)
488 */
489static int loads_init(Elf64_Phdr *phdr, u64 loads_offset)
490{
491 struct mem_chunk *chunk_array, *mem_chunk;
492 int i;
493
494 chunk_array = get_memory_layout();
495 for (i = 0; i < MEMORY_CHUNKS; i++) {
496 mem_chunk = &chunk_array[i];
497 if (mem_chunk->size == 0)
996b4a7d 498 continue;
60a0c68d
MH
499 if (chunk_array[i].type != CHUNK_READ_WRITE &&
500 chunk_array[i].type != CHUNK_READ_ONLY)
501 continue;
502 else
503 phdr->p_filesz = mem_chunk->size;
504 phdr->p_type = PT_LOAD;
505 phdr->p_offset = mem_chunk->addr;
506 phdr->p_vaddr = mem_chunk->addr;
507 phdr->p_paddr = mem_chunk->addr;
508 phdr->p_memsz = mem_chunk->size;
509 phdr->p_flags = PF_R | PF_W | PF_X;
510 phdr->p_align = PAGE_SIZE;
511 phdr++;
512 }
513 kfree(chunk_array);
514 return i;
515}
516
517/*
518 * Initialize notes (new kernel)
519 */
520static void *notes_init(Elf64_Phdr *phdr, void *ptr, u64 notes_offset)
521{
522 struct save_area *sa;
523 void *ptr_start = ptr;
524 int i;
525
526 ptr = nt_prpsinfo(ptr);
527
528 for (i = 0; zfcpdump_save_areas[i]; i++) {
529 sa = zfcpdump_save_areas[i];
530 if (sa->pref_reg == 0)
531 continue;
532 ptr = fill_cpu_elf_notes(ptr, sa);
533 }
534 ptr = nt_vmcoreinfo(ptr);
535 memset(phdr, 0, sizeof(*phdr));
536 phdr->p_type = PT_NOTE;
97b0f6f9 537 phdr->p_offset = notes_offset;
60a0c68d
MH
538 phdr->p_filesz = (unsigned long) PTR_SUB(ptr, ptr_start);
539 phdr->p_memsz = phdr->p_filesz;
540 return ptr;
541}
542
543/*
544 * Create ELF core header (new kernel)
545 */
97b0f6f9 546int elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size)
60a0c68d
MH
547{
548 Elf64_Phdr *phdr_notes, *phdr_loads;
549 int mem_chunk_cnt;
550 void *ptr, *hdr;
551 u32 alloc_size;
552 u64 hdr_off;
553
6f79d332
MH
554 /* If we are not in kdump or zfcpdump mode return */
555 if (!OLDMEM_BASE && ipl_info.type != IPL_TYPE_FCP_DUMP)
97b0f6f9
MH
556 return 0;
557 /* If elfcorehdr= has been passed via cmdline, we use that one */
558 if (elfcorehdr_addr != ELFCORE_ADDR_MAX)
559 return 0;
60a0c68d
MH
560 mem_chunk_cnt = get_mem_chunk_cnt();
561
562 alloc_size = 0x1000 + get_cpu_cnt() * 0x300 +
563 mem_chunk_cnt * sizeof(Elf64_Phdr);
564 hdr = kzalloc_panic(alloc_size);
565 /* Init elf header */
566 ptr = ehdr_init(hdr, mem_chunk_cnt);
567 /* Init program headers */
568 phdr_notes = ptr;
569 ptr = PTR_ADD(ptr, sizeof(Elf64_Phdr));
570 phdr_loads = ptr;
571 ptr = PTR_ADD(ptr, sizeof(Elf64_Phdr) * mem_chunk_cnt);
572 /* Init notes */
573 hdr_off = PTR_DIFF(ptr, hdr);
574 ptr = notes_init(phdr_notes, ptr, ((unsigned long) hdr) + hdr_off);
575 /* Init loads */
576 hdr_off = PTR_DIFF(ptr, hdr);
97b0f6f9
MH
577 loads_init(phdr_loads, hdr_off);
578 *addr = (unsigned long long) hdr;
579 elfcorehdr_newmem = hdr;
580 *size = (unsigned long long) hdr_off;
581 BUG_ON(elfcorehdr_size > alloc_size);
582 return 0;
60a0c68d
MH
583}
584
585/*
97b0f6f9 586 * Free ELF core header (new kernel)
60a0c68d 587 */
97b0f6f9 588void elfcorehdr_free(unsigned long long addr)
60a0c68d 589{
97b0f6f9
MH
590 if (!elfcorehdr_newmem)
591 return;
592 kfree((void *)(unsigned long)addr);
593}
594
595/*
596 * Read from ELF header
597 */
598ssize_t elfcorehdr_read(char *buf, size_t count, u64 *ppos)
599{
600 void *src = (void *)(unsigned long)*ppos;
601
602 src = elfcorehdr_newmem ? src : src - OLDMEM_BASE;
603 memcpy(buf, src, count);
604 *ppos += count;
605 return count;
60a0c68d
MH
606}
607
97b0f6f9
MH
608/*
609 * Read from ELF notes data
610 */
611ssize_t elfcorehdr_read_notes(char *buf, size_t count, u64 *ppos)
612{
613 void *src = (void *)(unsigned long)*ppos;
614 int rc;
615
616 if (elfcorehdr_newmem) {
617 memcpy(buf, src, count);
618 } else {
619 rc = copy_from_oldmem(buf, src, count);
620 if (rc)
621 return rc;
622 }
623 *ppos += count;
624 return count;
625}
This page took 0.14149 seconds and 5 git commands to generate.