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