Updated `README.md` with instructions for building/using the kernel module.
[xeon-phi-kernel-module] / host / vmcore.c
CommitLineData
800f879a
AT
1/*
2 * Copyright 2010-2017 Intel Corporation.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
12 *
13 * Disclaimer: The codes contained in these modules may be specific to
14 * the Intel Software Development Platform codenamed Knights Ferry,
15 * and the Intel product codenamed Knights Corner, and are not backward
16 * compatible with other Intel products. Additionally, Intel will NOT
17 * support the codes or instruction set in future products.
18 *
19 * Intel offers no warranty of any kind regarding the code. This code is
20 * licensed on an "AS IS" basis and Intel is not obligated to provide
21 * any support, assistance, installation, training, or other services
22 * of any kind. Intel is also not obligated to provide any updates,
23 * enhancements or extensions. Intel specifically disclaims any warranty
24 * of merchantability, non-infringement, fitness for any particular
25 * purpose, and any other warranty.
26 *
27 * Further, Intel disclaims all liability of any kind, including but
28 * not limited to liability for infringement of any proprietary rights,
29 * relating to the use of the code, even if Intel is notified of the
30 * possibility of such liability. Except as expressly stated in an Intel
31 * license agreement provided with this code and agreed upon with Intel,
32 * no license, express or implied, by estoppel or otherwise, to any
33 * intellectual property rights is granted herein.
34 */
35
36/*
37 * fs/proc/vmcore.c Interface for accessing the crash
38 * dump from the system's previous life.
39 * Heavily borrowed from fs/proc/kcore.c
40 * Created by: Hariprasad Nellitheertha (hari@in.ibm.com)
41 * Copyright (C) IBM Corporation, 2004. All rights reserved
42 *
43 */
44
45#include <linux/version.h>
46#include <linux/mm.h>
47#include <linux/proc_fs.h>
48#include <linux/user.h>
49#include <linux/elf.h>
50#include <linux/elfcore.h>
51#include <linux/slab.h>
52#include <linux/highmem.h>
53#include <linux/bootmem.h>
54#include <linux/init.h>
55#include <linux/crash_dump.h>
56#include <linux/list.h>
57#include <asm/uaccess.h>
58#include <asm/io.h>
59#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0))
60#include <linux/kcore.h>
61#endif
62#include "mic_common.h"
63
64extern struct proc_dir_entry *vmcore_dir;
65
66/* Stores the physical address of elf header of crash image. */
67unsigned long long elfcorehdr_addr = 0x50e9000;
68
69/**
70 * mic_copy_oldmem_page - copy one page from "oldmem"
71 * @pfn: page frame number to be copied
72 * @buf: target memory address for the copy; this can be in kernel address
73 * space or user address space (see @userbuf)
74 * @csize: number of bytes to copy
75 * @offset: offset in bytes into the page (based on pfn) to begin the copy
76 * @userbuf: if set, @buf is in user address space, use copy_to_user(),
77 * otherwise @buf is in kernel address space, use memcpy().
78 *
79 * Copy a page from "oldmem". For this page, there is no pte mapped
80 * in the current kernel. We stitch up a pte, similar to kmap_atomic.
81 */
82ssize_t mic_copy_oldmem_page(mic_ctx_t *mic_ctx,
83 unsigned long pfn, char *buf,
84 size_t csize, unsigned long offset, int userbuf)
85{
86 void *vaddr, *tmp;
87 int err;
88 struct dma_channel *dma_chan;
89 dma_addr_t mic_dst_phys_addr;
90
91 vaddr = mic_ctx->aper.va + (pfn << PAGE_SHIFT);
92
93 if (!csize)
94 return 0;
95 if (csize == PAGE_SIZE && !offset) {
96 if (!(tmp = (void*)__get_free_pages(GFP_KERNEL, get_order(PAGE_SIZE)))) {
97 printk(KERN_ERR "%s: tmp buffer allocation failed\n", __func__);
98 return -ENOMEM;
99 }
100 mic_dst_phys_addr = mic_ctx_map_single(mic_ctx, tmp, csize);
101 if (mic_map_error(mic_dst_phys_addr)) {
102 printk(KERN_ERR "%s: mic_ctx_map_single failed\n", __func__);
103 free_pages((unsigned long)tmp, get_order(PAGE_SIZE));
104 return -ENOMEM;
105 }
106
107 if ((allocate_dma_channel(mic_ctx->dma_handle, &dma_chan))) {
108 printk(KERN_ERR "%s: allocate_dma_channel failed\n", __func__);
109 mic_ctx_unmap_single(mic_ctx, mic_dst_phys_addr, csize);
110 free_pages((unsigned long)tmp, get_order(PAGE_SIZE));
111 return -EBUSY;
112 }
113
114 err = do_dma(dma_chan,
115 0,
116 pfn << PAGE_SHIFT,
117 mic_dst_phys_addr,
118 csize,
119 NULL);
120 if (err) {
121 printk(KERN_ERR "DMA do_dma err %s %d err %d src 0x%lx "
122 "dst 0x%llx csize 0x%lx\n",
123 __func__, __LINE__, err, pfn << PAGE_SHIFT,
124 mic_dst_phys_addr, csize);
125 free_dma_channel(dma_chan);
126 mic_ctx_unmap_single(mic_ctx, mic_dst_phys_addr, csize);
127 free_pages((unsigned long)tmp, get_order(PAGE_SIZE));
128 return err;
129 }
130 free_dma_channel(dma_chan);
131 err = drain_dma_poll(dma_chan);
132 if (err) {
133 printk(KERN_ERR "DMA poll err %s %d err %d src 0x%lx i"
134 "dst 0x%llx csize 0x%lx\n",
135 __func__, __LINE__, err, pfn << PAGE_SHIFT,
136 mic_dst_phys_addr, csize);
137 mic_ctx_unmap_single(mic_ctx, mic_dst_phys_addr, csize);
138 free_pages((unsigned long)tmp, get_order(PAGE_SIZE));
139 return err;
140 }
141 if (userbuf) {
142 if (copy_to_user(buf, tmp, csize)) {
143 mic_ctx_unmap_single(mic_ctx, mic_dst_phys_addr, csize);
144 free_pages((unsigned long)tmp, get_order(PAGE_SIZE));
145 return -EFAULT;
146 }
147 } else {
148 memcpy(buf, tmp, csize);
149 }
150 smp_mb();
151 mic_ctx_unmap_single(mic_ctx, mic_dst_phys_addr, csize);
152 free_pages((unsigned long)tmp, get_order(PAGE_SIZE));
153 } else {
154 if (userbuf) {
155 if (copy_to_user(buf, vaddr + offset, csize))
156 return -EFAULT;
157 } else
158 memcpy_fromio(buf, vaddr + offset, csize);
159 }
160 return csize;
161}
162
163/* Reads a page from the oldmem device from given offset. */
164static ssize_t read_from_oldmem(mic_ctx_t *mic_ctx,
165 char *buf, size_t count,
166 u64 *ppos, int userbuf)
167{
168 unsigned long pfn, offset;
169 size_t nr_bytes;
170 ssize_t read = 0, tmp;
171
172 if (!count)
173 return 0;
174
175 offset = (unsigned long)(*ppos % PAGE_SIZE);
176 pfn = (unsigned long)(*ppos / PAGE_SIZE);
177
178 do {
179 if (count > (PAGE_SIZE - offset))
180 nr_bytes = PAGE_SIZE - offset;
181 else
182 nr_bytes = count;
183
184 tmp = mic_copy_oldmem_page(mic_ctx, pfn, buf, nr_bytes, offset, userbuf);
185 if (tmp < 0)
186 return tmp;
187 *ppos += nr_bytes;
188 count -= nr_bytes;
189 buf += nr_bytes;
190 read += nr_bytes;
191 ++pfn;
192 offset = 0;
193 } while (count);
194
195 return read;
196}
197
198/* Maps vmcore file offset to respective physical address in memroy. */
199static u64 map_offset_to_paddr(loff_t offset, struct list_head *vc_list,
200 struct vmcore **m_ptr)
201{
202 struct vmcore *m;
203 u64 paddr;
204
205 list_for_each_entry(m, vc_list, list) {
206 u64 start, end;
207 start = m->offset;
208 end = m->offset + m->size - 1;
209 if (offset >= start && offset <= end) {
210 paddr = m->paddr + offset - start;
211 *m_ptr = m;
212 return paddr;
213 }
214 }
215 *m_ptr = NULL;
216 return 0;
217}
218
219/* Read from the ELF header and then the crash dump. On error, negative value is
220 * returned otherwise number of bytes read are returned.
221 */
222static ssize_t read_vmcore(struct file *file, char __user *buffer,
223 size_t buflen, loff_t *fpos)
224{
225 ssize_t acc = 0, tmp;
226 size_t tsz;
227 u64 start, nr_bytes;
228 struct vmcore *curr_m = NULL;
229 struct inode *inode = file->f_path.dentry->d_inode;
230#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0))
231 mic_ctx_t *mic_ctx = PDE_DATA(inode);
232#else
233 struct proc_dir_entry *entry = PDE(inode);
234 mic_ctx_t *mic_ctx = entry->data;
235#endif
236
237 if (buflen == 0 || *fpos >= mic_ctx->vmcore_size)
238 return 0;
239
240 /* trim buflen to not go beyond EOF */
241 if (buflen > mic_ctx->vmcore_size - *fpos)
242 buflen = mic_ctx->vmcore_size - *fpos;
243
244 /* Read ELF core header */
245 if (*fpos < mic_ctx->elfcorebuf_sz) {
246 tsz = mic_ctx->elfcorebuf_sz - *fpos;
247 if (buflen < tsz)
248 tsz = buflen;
249 if (copy_to_user(buffer, mic_ctx->elfcorebuf + *fpos, tsz))
250 return -EFAULT;
251 buflen -= tsz;
252 *fpos += tsz;
253 buffer += tsz;
254 acc += tsz;
255
256 /* leave now if filled buffer already */
257 if (buflen == 0)
258 return acc;
259 }
260
261 start = map_offset_to_paddr(*fpos, &mic_ctx->vmcore_list, &curr_m);
262 if (!curr_m)
263 return -EINVAL;
264 if ((tsz = (PAGE_SIZE - (start & ~PAGE_MASK))) > buflen)
265 tsz = buflen;
266
267 /* Calculate left bytes in current memory segment. */
268 nr_bytes = (curr_m->size - (start - curr_m->paddr));
269 if (tsz > nr_bytes)
270 tsz = nr_bytes;
271
272 while (buflen) {
273 tmp = read_from_oldmem(mic_ctx,buffer, tsz, &start, 1);
274 if (tmp < 0)
275 return tmp;
276 buflen -= tsz;
277 *fpos += tsz;
278 buffer += tsz;
279 acc += tsz;
280 if (start >= (curr_m->paddr + curr_m->size)) {
281 if (curr_m->list.next == &mic_ctx->vmcore_list)
282 return acc; /*EOF*/
283 curr_m = list_entry(curr_m->list.next,
284 struct vmcore, list);
285 start = curr_m->paddr;
286 }
287 if ((tsz = (PAGE_SIZE - (start & ~PAGE_MASK))) > buflen)
288 tsz = buflen;
289 /* Calculate left bytes in current memory segment. */
290 nr_bytes = (curr_m->size - (start - curr_m->paddr));
291 if (tsz > nr_bytes)
292 tsz = nr_bytes;
293 }
294 return acc;
295}
296
297static const struct file_operations proc_vmcore_operations = {
298 .read = read_vmcore,
299};
300
301static struct vmcore* get_new_element(void)
302{
303 return kzalloc(sizeof(struct vmcore), GFP_KERNEL);
304}
305
306static u64 get_vmcore_size_elf64(char *elfptr)
307{
308 int i;
309 u64 size;
310 Elf64_Ehdr *ehdr_ptr;
311 Elf64_Phdr *phdr_ptr;
312
313 ehdr_ptr = (Elf64_Ehdr *)elfptr;
314 phdr_ptr = (Elf64_Phdr*)(elfptr + sizeof(Elf64_Ehdr));
315 size = sizeof(Elf64_Ehdr) + ((ehdr_ptr->e_phnum) * sizeof(Elf64_Phdr));
316 for (i = 0; i < ehdr_ptr->e_phnum; i++) {
317 size += phdr_ptr->p_memsz;
318 phdr_ptr++;
319 }
320 return size;
321}
322
323static u64 get_vmcore_size_elf32(char *elfptr)
324{
325 int i;
326 u64 size;
327 Elf32_Ehdr *ehdr_ptr;
328 Elf32_Phdr *phdr_ptr;
329
330 ehdr_ptr = (Elf32_Ehdr *)elfptr;
331 phdr_ptr = (Elf32_Phdr*)(elfptr + sizeof(Elf32_Ehdr));
332 size = sizeof(Elf32_Ehdr) + ((ehdr_ptr->e_phnum) * sizeof(Elf32_Phdr));
333 for (i = 0; i < ehdr_ptr->e_phnum; i++) {
334 size += phdr_ptr->p_memsz;
335 phdr_ptr++;
336 }
337 return size;
338}
339
340/* Merges all the PT_NOTE headers into one. */
341static int merge_note_headers_elf64(mic_ctx_t *mic_ctx,
342 char *elfptr, size_t *elfsz,
343 struct list_head *vc_list)
344{
345 int i, nr_ptnote=0, rc=0;
346 char *tmp;
347 Elf64_Ehdr *ehdr_ptr;
348 Elf64_Phdr phdr, *phdr_ptr;
349 Elf64_Nhdr *nhdr_ptr;
350 u64 phdr_sz = 0, note_off;
351
352 ehdr_ptr = (Elf64_Ehdr *)elfptr;
353 phdr_ptr = (Elf64_Phdr*)(elfptr + sizeof(Elf64_Ehdr));
354 for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) {
355 int j;
356 void *notes_section;
357 struct vmcore *new;
358 u64 offset, max_sz, sz, real_sz = 0;
359 if (phdr_ptr->p_type != PT_NOTE)
360 continue;
361 nr_ptnote++;
362 max_sz = phdr_ptr->p_memsz;
363 offset = phdr_ptr->p_offset;
364 notes_section = kmalloc(max_sz, GFP_KERNEL);
365 if (!notes_section)
366 return -ENOMEM;
367 rc = read_from_oldmem(mic_ctx, notes_section, max_sz, &offset, 0);
368 if (rc < 0) {
369 kfree(notes_section);
370 return rc;
371 }
372 nhdr_ptr = notes_section;
373 for (j = 0; j < max_sz; j += sz) {
374 if (nhdr_ptr->n_namesz == 0)
375 break;
376 sz = sizeof(Elf64_Nhdr) +
377 ((nhdr_ptr->n_namesz + 3) & ~3) +
378 ((nhdr_ptr->n_descsz + 3) & ~3);
379 real_sz += sz;
380 nhdr_ptr = (Elf64_Nhdr*)((char*)nhdr_ptr + sz);
381 }
382
383 /* Add this contiguous chunk of notes section to vmcore list.*/
384 new = get_new_element();
385 if (!new) {
386 kfree(notes_section);
387 return -ENOMEM;
388 }
389 new->paddr = phdr_ptr->p_offset;
390 new->size = real_sz;
391 list_add_tail(&new->list, vc_list);
392 phdr_sz += real_sz;
393 kfree(notes_section);
394 }
395
396 /* Prepare merged PT_NOTE program header. */
397 phdr.p_type = PT_NOTE;
398 phdr.p_flags = 0;
399 note_off = sizeof(Elf64_Ehdr) +
400 (ehdr_ptr->e_phnum - nr_ptnote +1) * sizeof(Elf64_Phdr);
401 phdr.p_offset = note_off;
402 phdr.p_vaddr = phdr.p_paddr = 0;
403 phdr.p_filesz = phdr.p_memsz = phdr_sz;
404 phdr.p_align = 0;
405
406 /* Add merged PT_NOTE program header*/
407 tmp = elfptr + sizeof(Elf64_Ehdr);
408 memcpy(tmp, &phdr, sizeof(phdr));
409 tmp += sizeof(phdr);
410
411 /* Remove unwanted PT_NOTE program headers. */
412 i = (nr_ptnote - 1) * sizeof(Elf64_Phdr);
413 *elfsz = *elfsz - i;
414 memmove(tmp, tmp+i, ((*elfsz)-sizeof(Elf64_Ehdr)-sizeof(Elf64_Phdr)));
415
416 /* Modify e_phnum to reflect merged headers. */
417 ehdr_ptr->e_phnum = ehdr_ptr->e_phnum - nr_ptnote + 1;
418
419 return 0;
420}
421
422/* Merges all the PT_NOTE headers into one. */
423static int merge_note_headers_elf32(mic_ctx_t *mic_ctx,
424 char *elfptr, size_t *elfsz,
425 struct list_head *vc_list)
426{
427 int i, nr_ptnote=0, rc=0;
428 char *tmp;
429 Elf32_Ehdr *ehdr_ptr;
430 Elf32_Phdr phdr, *phdr_ptr;
431 Elf32_Nhdr *nhdr_ptr;
432 u64 phdr_sz = 0, note_off;
433
434 ehdr_ptr = (Elf32_Ehdr *)elfptr;
435 phdr_ptr = (Elf32_Phdr*)(elfptr + sizeof(Elf32_Ehdr));
436 for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) {
437 int j;
438 void *notes_section;
439 struct vmcore *new;
440 u64 offset, max_sz, sz, real_sz = 0;
441 if (phdr_ptr->p_type != PT_NOTE)
442 continue;
443 nr_ptnote++;
444 max_sz = phdr_ptr->p_memsz;
445 offset = phdr_ptr->p_offset;
446 notes_section = kmalloc(max_sz, GFP_KERNEL);
447 if (!notes_section)
448 return -ENOMEM;
449 rc = read_from_oldmem(mic_ctx, notes_section, max_sz, &offset, 0);
450 if (rc < 0) {
451 kfree(notes_section);
452 return rc;
453 }
454 nhdr_ptr = notes_section;
455 for (j = 0; j < max_sz; j += sz) {
456 if (nhdr_ptr->n_namesz == 0)
457 break;
458 sz = sizeof(Elf32_Nhdr) +
459 ((nhdr_ptr->n_namesz + 3) & ~3) +
460 ((nhdr_ptr->n_descsz + 3) & ~3);
461 real_sz += sz;
462 nhdr_ptr = (Elf32_Nhdr*)((char*)nhdr_ptr + sz);
463 }
464
465 /* Add this contiguous chunk of notes section to vmcore list.*/
466 new = get_new_element();
467 if (!new) {
468 kfree(notes_section);
469 return -ENOMEM;
470 }
471 new->paddr = phdr_ptr->p_offset;
472 new->size = real_sz;
473 list_add_tail(&new->list, vc_list);
474 phdr_sz += real_sz;
475 kfree(notes_section);
476 }
477
478 /* Prepare merged PT_NOTE program header. */
479 phdr.p_type = PT_NOTE;
480 phdr.p_flags = 0;
481 note_off = sizeof(Elf32_Ehdr) +
482 (ehdr_ptr->e_phnum - nr_ptnote +1) * sizeof(Elf32_Phdr);
483 phdr.p_offset = note_off;
484 phdr.p_vaddr = phdr.p_paddr = 0;
485 phdr.p_filesz = phdr.p_memsz = phdr_sz;
486 phdr.p_align = 0;
487
488 /* Add merged PT_NOTE program header*/
489 tmp = elfptr + sizeof(Elf32_Ehdr);
490 memcpy(tmp, &phdr, sizeof(phdr));
491 tmp += sizeof(phdr);
492
493 /* Remove unwanted PT_NOTE program headers. */
494 i = (nr_ptnote - 1) * sizeof(Elf32_Phdr);
495 *elfsz = *elfsz - i;
496 memmove(tmp, tmp+i, ((*elfsz)-sizeof(Elf32_Ehdr)-sizeof(Elf32_Phdr)));
497
498 /* Modify e_phnum to reflect merged headers. */
499 ehdr_ptr->e_phnum = ehdr_ptr->e_phnum - nr_ptnote + 1;
500
501 return 0;
502}
503
504/* Add memory chunks represented by program headers to vmcore list. Also update
505 * the new offset fields of exported program headers. */
506static int process_ptload_program_headers_elf64(char *elfptr,
507 size_t elfsz,
508 struct list_head *vc_list)
509{
510 int i;
511 Elf64_Ehdr *ehdr_ptr;
512 Elf64_Phdr *phdr_ptr;
513 loff_t vmcore_off;
514 struct vmcore *new;
515
516 ehdr_ptr = (Elf64_Ehdr *)elfptr;
517 phdr_ptr = (Elf64_Phdr*)(elfptr + sizeof(Elf64_Ehdr)); /* PT_NOTE hdr */
518
519 /* First program header is PT_NOTE header. */
520 vmcore_off = sizeof(Elf64_Ehdr) +
521 (ehdr_ptr->e_phnum) * sizeof(Elf64_Phdr) +
522 phdr_ptr->p_memsz; /* Note sections */
523
524 for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) {
525 if (phdr_ptr->p_type != PT_LOAD)
526 continue;
527
528 /* Add this contiguous chunk of memory to vmcore list.*/
529 new = get_new_element();
530 if (!new)
531 return -ENOMEM;
532 new->paddr = phdr_ptr->p_offset;
533 new->size = phdr_ptr->p_memsz;
534 list_add_tail(&new->list, vc_list);
535
536 /* Update the program header offset. */
537 phdr_ptr->p_offset = vmcore_off;
538 vmcore_off = vmcore_off + phdr_ptr->p_memsz;
539 }
540 return 0;
541}
542
543static int process_ptload_program_headers_elf32(char *elfptr,
544 size_t elfsz,
545 struct list_head *vc_list)
546{
547 int i;
548 Elf32_Ehdr *ehdr_ptr;
549 Elf32_Phdr *phdr_ptr;
550 loff_t vmcore_off;
551 struct vmcore *new;
552
553 ehdr_ptr = (Elf32_Ehdr *)elfptr;
554 phdr_ptr = (Elf32_Phdr*)(elfptr + sizeof(Elf32_Ehdr)); /* PT_NOTE hdr */
555
556 /* First program header is PT_NOTE header. */
557 vmcore_off = sizeof(Elf32_Ehdr) +
558 (ehdr_ptr->e_phnum) * sizeof(Elf32_Phdr) +
559 phdr_ptr->p_memsz; /* Note sections */
560
561 for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) {
562 if (phdr_ptr->p_type != PT_LOAD)
563 continue;
564
565 /* Add this contiguous chunk of memory to vmcore list.*/
566 new = get_new_element();
567 if (!new)
568 return -ENOMEM;
569 new->paddr = phdr_ptr->p_offset;
570 new->size = phdr_ptr->p_memsz;
571 list_add_tail(&new->list, vc_list);
572
573 /* Update the program header offset */
574 phdr_ptr->p_offset = vmcore_off;
575 vmcore_off = vmcore_off + phdr_ptr->p_memsz;
576 }
577 return 0;
578}
579
580/* Sets offset fields of vmcore elements. */
581static void set_vmcore_list_offsets_elf64(char *elfptr,
582 struct list_head *vc_list)
583{
584 loff_t vmcore_off;
585 Elf64_Ehdr *ehdr_ptr;
586 struct vmcore *m;
587
588 ehdr_ptr = (Elf64_Ehdr *)elfptr;
589
590 /* Skip Elf header and program headers. */
591 vmcore_off = sizeof(Elf64_Ehdr) +
592 (ehdr_ptr->e_phnum) * sizeof(Elf64_Phdr);
593
594 list_for_each_entry(m, vc_list, list) {
595 m->offset = vmcore_off;
596 vmcore_off += m->size;
597 }
598}
599
600/* Sets offset fields of vmcore elements. */
601static void set_vmcore_list_offsets_elf32(char *elfptr,
602 struct list_head *vc_list)
603{
604 loff_t vmcore_off;
605 Elf32_Ehdr *ehdr_ptr;
606 struct vmcore *m;
607
608 ehdr_ptr = (Elf32_Ehdr *)elfptr;
609
610 /* Skip Elf header and program headers. */
611 vmcore_off = sizeof(Elf32_Ehdr) +
612 (ehdr_ptr->e_phnum) * sizeof(Elf32_Phdr);
613
614 list_for_each_entry(m, vc_list, list) {
615 m->offset = vmcore_off;
616 vmcore_off += m->size;
617 }
618}
619
620static int parse_crash_elf64_headers(mic_ctx_t *mic_ctx)
621{
622 int rc=0;
623 Elf64_Ehdr ehdr;
624 u64 addr;
625
626 addr = elfcorehdr_addr;
627
628 /* Read Elf header */
629 rc = read_from_oldmem(mic_ctx, (char*)&ehdr, sizeof(Elf64_Ehdr), &addr, 0);
630 if (rc < 0)
631 return rc;
632
633 /* Do some basic Verification. */
634 if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0 ||
635 (ehdr.e_type != ET_CORE) ||
636#ifdef CONFIG_CRASH_DUMP
637#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,36))
638 !vmcore_elf64_check_arch(&ehdr) ||
639#else
640 !vmcore_elf_check_arch(&ehdr) ||
641#endif
642#else
643 !elf_check_arch(&ehdr) ||
644#endif
645 ehdr.e_ident[EI_CLASS] != ELFCLASS64 ||
646 ehdr.e_ident[EI_VERSION] != EV_CURRENT ||
647 ehdr.e_version != EV_CURRENT ||
648 ehdr.e_ehsize != sizeof(Elf64_Ehdr) ||
649 ehdr.e_phentsize != sizeof(Elf64_Phdr) ||
650 ehdr.e_phnum == 0) {
651 printk(KERN_WARNING "Warning: Core image elf header is not"
652 "sane\n");
653 return -EINVAL;
654 }
655
656 WARN_ON(mic_ctx->elfcorebuf);
657 /* Read in all elf headers. */
658 mic_ctx->elfcorebuf_sz = sizeof(Elf64_Ehdr) + ehdr.e_phnum * sizeof(Elf64_Phdr);
659 mic_ctx->elfcorebuf = kmalloc(mic_ctx->elfcorebuf_sz, GFP_KERNEL);
660 if (!mic_ctx->elfcorebuf)
661 return -ENOMEM;
662 addr = elfcorehdr_addr;
663 rc = read_from_oldmem(mic_ctx, mic_ctx->elfcorebuf, mic_ctx->elfcorebuf_sz, &addr, 0);
664 if (rc < 0) {
665 kfree(mic_ctx->elfcorebuf);
666 mic_ctx->elfcorebuf = NULL;
667 return rc;
668 }
669
670 /* Merge all PT_NOTE headers into one. */
671 rc = merge_note_headers_elf64(mic_ctx, mic_ctx->elfcorebuf, &mic_ctx->elfcorebuf_sz, &mic_ctx->vmcore_list);
672 if (rc) {
673 kfree(mic_ctx->elfcorebuf);
674 mic_ctx->elfcorebuf = NULL;
675 return rc;
676 }
677 rc = process_ptload_program_headers_elf64(mic_ctx->elfcorebuf, mic_ctx->elfcorebuf_sz,
678 &mic_ctx->vmcore_list);
679 if (rc) {
680 kfree(mic_ctx->elfcorebuf);
681 mic_ctx->elfcorebuf = NULL;
682 return rc;
683 }
684 set_vmcore_list_offsets_elf64(mic_ctx->elfcorebuf, &mic_ctx->vmcore_list);
685 return 0;
686}
687
688static int parse_crash_elf32_headers(mic_ctx_t *mic_ctx)
689{
690 int rc=0;
691 Elf32_Ehdr ehdr;
692 u64 addr;
693
694 addr = elfcorehdr_addr;
695
696 /* Read Elf header */
697 rc = read_from_oldmem(mic_ctx, (char*)&ehdr, sizeof(Elf32_Ehdr), &addr, 0);
698 if (rc < 0)
699 return rc;
700
701 /* Do some basic Verification. */
702 if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0 ||
703 (ehdr.e_type != ET_CORE) ||
704 !elf_check_arch(&ehdr) ||
705 ehdr.e_ident[EI_CLASS] != ELFCLASS32||
706 ehdr.e_ident[EI_VERSION] != EV_CURRENT ||
707 ehdr.e_version != EV_CURRENT ||
708 ehdr.e_ehsize != sizeof(Elf32_Ehdr) ||
709 ehdr.e_phentsize != sizeof(Elf32_Phdr) ||
710 ehdr.e_phnum == 0) {
711 printk(KERN_WARNING "Warning: Core image elf header is not"
712 "sane\n");
713 return -EINVAL;
714 }
715
716 WARN_ON(mic_ctx->elfcorebuf);
717 /* Read in all elf headers. */
718 mic_ctx->elfcorebuf_sz = sizeof(Elf32_Ehdr) + ehdr.e_phnum * sizeof(Elf32_Phdr);
719 mic_ctx->elfcorebuf = kmalloc(mic_ctx->elfcorebuf_sz, GFP_KERNEL);
720 if (!mic_ctx->elfcorebuf)
721 return -ENOMEM;
722 addr = elfcorehdr_addr;
723 rc = read_from_oldmem(mic_ctx, mic_ctx->elfcorebuf, mic_ctx->elfcorebuf_sz, &addr, 0);
724 if (rc < 0) {
725 kfree(mic_ctx->elfcorebuf);
726 mic_ctx->elfcorebuf = NULL;
727 return rc;
728 }
729
730 /* Merge all PT_NOTE headers into one. */
731 rc = merge_note_headers_elf32(mic_ctx, mic_ctx->elfcorebuf, &mic_ctx->elfcorebuf_sz, &mic_ctx->vmcore_list);
732 if (rc) {
733 kfree(mic_ctx->elfcorebuf);
734 mic_ctx->elfcorebuf = NULL;
735 return rc;
736 }
737 rc = process_ptload_program_headers_elf32(mic_ctx->elfcorebuf, mic_ctx->elfcorebuf_sz,
738 &mic_ctx->vmcore_list);
739 if (rc) {
740 kfree(mic_ctx->elfcorebuf);
741 mic_ctx->elfcorebuf = NULL;
742 return rc;
743 }
744 set_vmcore_list_offsets_elf32(mic_ctx->elfcorebuf, &mic_ctx->vmcore_list);
745 return 0;
746}
747
748static int parse_crash_elf_headers(mic_ctx_t *mic_ctx)
749{
750 unsigned char e_ident[EI_NIDENT];
751 u64 addr;
752 int rc=0;
753
754 addr = elfcorehdr_addr;
755 rc = read_from_oldmem(mic_ctx, e_ident, EI_NIDENT, &addr, 0);
756 if (rc < 0)
757 return rc;
758 if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) {
759 printk(KERN_WARNING "Warning: Core image elf header"
760 " not found\n");
761 return -EINVAL;
762 }
763
764 if (e_ident[EI_CLASS] == ELFCLASS64) {
765 rc = parse_crash_elf64_headers(mic_ctx);
766 if (rc)
767 return rc;
768
769 /* Determine vmcore size. */
770 mic_ctx->vmcore_size = get_vmcore_size_elf64(mic_ctx->elfcorebuf);
771 } else if (e_ident[EI_CLASS] == ELFCLASS32) {
772 rc = parse_crash_elf32_headers(mic_ctx);
773 if (rc)
774 return rc;
775
776 /* Determine vmcore size. */
777 mic_ctx->vmcore_size = get_vmcore_size_elf32(mic_ctx->elfcorebuf);
778 } else {
779 printk(KERN_WARNING "Warning: Core image elf header is not"
780 " sane\n");
781 return -EINVAL;
782 }
783 return 0;
784}
785
786/* Init function for vmcore module. */
787int vmcore_create(mic_ctx_t *mic_ctx)
788{
789 int rc = 0;
790 char name[64];
791 if (!vmcore_dir) {
792 rc = -ENOMEM;
793 return rc;
794 }
795 INIT_LIST_HEAD(&mic_ctx->vmcore_list);
796 rc = parse_crash_elf_headers(mic_ctx);
797 if (rc) {
798 printk(KERN_WARNING "Kdump: vmcore not initialized\n");
799 if (mic_ctx->vmcore_dir) {
800 remove_proc_entry(name, vmcore_dir);
801 mic_ctx->vmcore_dir = NULL;
802 }
803 return rc;
804 }
805 snprintf(name, 64, "mic%d", mic_ctx->bi_id);
806 if (!mic_ctx->vmcore_dir) {
807 mic_ctx->vmcore_dir = proc_create_data(name, S_IRUSR,
808 vmcore_dir, &proc_vmcore_operations, mic_ctx);
809 if (!mic_ctx->vmcore_dir) {
810 printk(KERN_WARNING "Kdump: proc creation for %s failed\n", name);
811 rc = -ENOMEM;
812 return rc;
813 }
814 }
815#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0))
816#else
817 if (mic_ctx->vmcore_dir)
818 mic_ctx->vmcore_dir->size = mic_ctx->vmcore_size;
819#endif
820 return 0;
821}