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