2 * fs/proc/vmcore.c Interface for accessing the crash
3 * dump from the system's previous life.
4 * Heavily borrowed from fs/proc/kcore.c
5 * Created by: Hariprasad Nellitheertha (hari@in.ibm.com)
6 * Copyright (C) IBM Corporation, 2004. All rights reserved
11 #include <linux/proc_fs.h>
12 #include <linux/user.h>
13 #include <linux/elf.h>
14 #include <linux/elfcore.h>
15 #include <linux/export.h>
16 #include <linux/slab.h>
17 #include <linux/highmem.h>
18 #include <linux/printk.h>
19 #include <linux/bootmem.h>
20 #include <linux/init.h>
21 #include <linux/crash_dump.h>
22 #include <linux/list.h>
23 #include <asm/uaccess.h>
26 /* List representing chunks of contiguous memory areas and their offsets in
29 static LIST_HEAD(vmcore_list
);
31 /* Stores the pointer to the buffer containing kernel elf core headers. */
32 static char *elfcorebuf
;
33 static size_t elfcorebuf_sz
;
35 /* Total size of vmcore file. */
36 static u64 vmcore_size
;
38 static struct proc_dir_entry
*proc_vmcore
= NULL
;
41 * Returns > 0 for RAM pages, 0 for non-RAM pages, < 0 on error
42 * The called function has to take care of module refcounting.
44 static int (*oldmem_pfn_is_ram
)(unsigned long pfn
);
46 int register_oldmem_pfn_is_ram(int (*fn
)(unsigned long pfn
))
48 if (oldmem_pfn_is_ram
)
50 oldmem_pfn_is_ram
= fn
;
53 EXPORT_SYMBOL_GPL(register_oldmem_pfn_is_ram
);
55 void unregister_oldmem_pfn_is_ram(void)
57 oldmem_pfn_is_ram
= NULL
;
60 EXPORT_SYMBOL_GPL(unregister_oldmem_pfn_is_ram
);
62 static int pfn_is_ram(unsigned long pfn
)
64 int (*fn
)(unsigned long pfn
);
65 /* pfn is ram unless fn() checks pagetype */
69 * Ask hypervisor if the pfn is really ram.
70 * A ballooned page contains no data and reading from such a page
71 * will cause high load in the hypervisor.
73 fn
= oldmem_pfn_is_ram
;
80 /* Reads a page from the oldmem device from given offset. */
81 static ssize_t
read_from_oldmem(char *buf
, size_t count
,
82 u64
*ppos
, int userbuf
)
84 unsigned long pfn
, offset
;
86 ssize_t read
= 0, tmp
;
91 offset
= (unsigned long)(*ppos
% PAGE_SIZE
);
92 pfn
= (unsigned long)(*ppos
/ PAGE_SIZE
);
95 if (count
> (PAGE_SIZE
- offset
))
96 nr_bytes
= PAGE_SIZE
- offset
;
100 /* If pfn is not ram, return zeros for sparse dump files */
101 if (pfn_is_ram(pfn
) == 0)
102 memset(buf
, 0, nr_bytes
);
104 tmp
= copy_oldmem_page(pfn
, buf
, nr_bytes
,
120 /* Maps vmcore file offset to respective physical address in memroy. */
121 static u64
map_offset_to_paddr(loff_t offset
, struct list_head
*vc_list
,
122 struct vmcore
**m_ptr
)
127 list_for_each_entry(m
, vc_list
, list
) {
130 end
= m
->offset
+ m
->size
- 1;
131 if (offset
>= start
&& offset
<= end
) {
132 paddr
= m
->paddr
+ offset
- start
;
141 /* Read from the ELF header and then the crash dump. On error, negative value is
142 * returned otherwise number of bytes read are returned.
144 static ssize_t
read_vmcore(struct file
*file
, char __user
*buffer
,
145 size_t buflen
, loff_t
*fpos
)
147 ssize_t acc
= 0, tmp
;
150 struct vmcore
*curr_m
= NULL
;
152 if (buflen
== 0 || *fpos
>= vmcore_size
)
155 /* trim buflen to not go beyond EOF */
156 if (buflen
> vmcore_size
- *fpos
)
157 buflen
= vmcore_size
- *fpos
;
159 /* Read ELF core header */
160 if (*fpos
< elfcorebuf_sz
) {
161 tsz
= elfcorebuf_sz
- *fpos
;
164 if (copy_to_user(buffer
, elfcorebuf
+ *fpos
, tsz
))
171 /* leave now if filled buffer already */
176 start
= map_offset_to_paddr(*fpos
, &vmcore_list
, &curr_m
);
181 tsz
= min_t(size_t, buflen
, PAGE_SIZE
- (start
& ~PAGE_MASK
));
183 /* Calculate left bytes in current memory segment. */
184 nr_bytes
= (curr_m
->size
- (start
- curr_m
->paddr
));
188 tmp
= read_from_oldmem(buffer
, tsz
, &start
, 1);
195 if (start
>= (curr_m
->paddr
+ curr_m
->size
)) {
196 if (curr_m
->list
.next
== &vmcore_list
)
198 curr_m
= list_entry(curr_m
->list
.next
,
199 struct vmcore
, list
);
200 start
= curr_m
->paddr
;
206 static const struct file_operations proc_vmcore_operations
= {
208 .llseek
= default_llseek
,
211 static struct vmcore
* __init
get_new_element(void)
213 return kzalloc(sizeof(struct vmcore
), GFP_KERNEL
);
216 static u64 __init
get_vmcore_size_elf64(char *elfptr
)
220 Elf64_Ehdr
*ehdr_ptr
;
221 Elf64_Phdr
*phdr_ptr
;
223 ehdr_ptr
= (Elf64_Ehdr
*)elfptr
;
224 phdr_ptr
= (Elf64_Phdr
*)(elfptr
+ sizeof(Elf64_Ehdr
));
225 size
= sizeof(Elf64_Ehdr
) + ((ehdr_ptr
->e_phnum
) * sizeof(Elf64_Phdr
));
226 for (i
= 0; i
< ehdr_ptr
->e_phnum
; i
++) {
227 size
+= phdr_ptr
->p_memsz
;
233 static u64 __init
get_vmcore_size_elf32(char *elfptr
)
237 Elf32_Ehdr
*ehdr_ptr
;
238 Elf32_Phdr
*phdr_ptr
;
240 ehdr_ptr
= (Elf32_Ehdr
*)elfptr
;
241 phdr_ptr
= (Elf32_Phdr
*)(elfptr
+ sizeof(Elf32_Ehdr
));
242 size
= sizeof(Elf32_Ehdr
) + ((ehdr_ptr
->e_phnum
) * sizeof(Elf32_Phdr
));
243 for (i
= 0; i
< ehdr_ptr
->e_phnum
; i
++) {
244 size
+= phdr_ptr
->p_memsz
;
250 /* Merges all the PT_NOTE headers into one. */
251 static int __init
merge_note_headers_elf64(char *elfptr
, size_t *elfsz
,
252 struct list_head
*vc_list
)
254 int i
, nr_ptnote
=0, rc
=0;
256 Elf64_Ehdr
*ehdr_ptr
;
257 Elf64_Phdr phdr
, *phdr_ptr
;
258 Elf64_Nhdr
*nhdr_ptr
;
259 u64 phdr_sz
= 0, note_off
;
261 ehdr_ptr
= (Elf64_Ehdr
*)elfptr
;
262 phdr_ptr
= (Elf64_Phdr
*)(elfptr
+ sizeof(Elf64_Ehdr
));
263 for (i
= 0; i
< ehdr_ptr
->e_phnum
; i
++, phdr_ptr
++) {
267 u64 offset
, max_sz
, sz
, real_sz
= 0;
268 if (phdr_ptr
->p_type
!= PT_NOTE
)
271 max_sz
= phdr_ptr
->p_memsz
;
272 offset
= phdr_ptr
->p_offset
;
273 notes_section
= kmalloc(max_sz
, GFP_KERNEL
);
276 rc
= read_from_oldmem(notes_section
, max_sz
, &offset
, 0);
278 kfree(notes_section
);
281 nhdr_ptr
= notes_section
;
282 for (j
= 0; j
< max_sz
; j
+= sz
) {
283 if (nhdr_ptr
->n_namesz
== 0)
285 sz
= sizeof(Elf64_Nhdr
) +
286 ((nhdr_ptr
->n_namesz
+ 3) & ~3) +
287 ((nhdr_ptr
->n_descsz
+ 3) & ~3);
289 nhdr_ptr
= (Elf64_Nhdr
*)((char*)nhdr_ptr
+ sz
);
292 /* Add this contiguous chunk of notes section to vmcore list.*/
293 new = get_new_element();
295 kfree(notes_section
);
298 new->paddr
= phdr_ptr
->p_offset
;
300 list_add_tail(&new->list
, vc_list
);
302 kfree(notes_section
);
305 /* Prepare merged PT_NOTE program header. */
306 phdr
.p_type
= PT_NOTE
;
308 note_off
= sizeof(Elf64_Ehdr
) +
309 (ehdr_ptr
->e_phnum
- nr_ptnote
+1) * sizeof(Elf64_Phdr
);
310 phdr
.p_offset
= note_off
;
311 phdr
.p_vaddr
= phdr
.p_paddr
= 0;
312 phdr
.p_filesz
= phdr
.p_memsz
= phdr_sz
;
315 /* Add merged PT_NOTE program header*/
316 tmp
= elfptr
+ sizeof(Elf64_Ehdr
);
317 memcpy(tmp
, &phdr
, sizeof(phdr
));
320 /* Remove unwanted PT_NOTE program headers. */
321 i
= (nr_ptnote
- 1) * sizeof(Elf64_Phdr
);
323 memmove(tmp
, tmp
+i
, ((*elfsz
)-sizeof(Elf64_Ehdr
)-sizeof(Elf64_Phdr
)));
325 /* Modify e_phnum to reflect merged headers. */
326 ehdr_ptr
->e_phnum
= ehdr_ptr
->e_phnum
- nr_ptnote
+ 1;
331 /* Merges all the PT_NOTE headers into one. */
332 static int __init
merge_note_headers_elf32(char *elfptr
, size_t *elfsz
,
333 struct list_head
*vc_list
)
335 int i
, nr_ptnote
=0, rc
=0;
337 Elf32_Ehdr
*ehdr_ptr
;
338 Elf32_Phdr phdr
, *phdr_ptr
;
339 Elf32_Nhdr
*nhdr_ptr
;
340 u64 phdr_sz
= 0, note_off
;
342 ehdr_ptr
= (Elf32_Ehdr
*)elfptr
;
343 phdr_ptr
= (Elf32_Phdr
*)(elfptr
+ sizeof(Elf32_Ehdr
));
344 for (i
= 0; i
< ehdr_ptr
->e_phnum
; i
++, phdr_ptr
++) {
348 u64 offset
, max_sz
, sz
, real_sz
= 0;
349 if (phdr_ptr
->p_type
!= PT_NOTE
)
352 max_sz
= phdr_ptr
->p_memsz
;
353 offset
= phdr_ptr
->p_offset
;
354 notes_section
= kmalloc(max_sz
, GFP_KERNEL
);
357 rc
= read_from_oldmem(notes_section
, max_sz
, &offset
, 0);
359 kfree(notes_section
);
362 nhdr_ptr
= notes_section
;
363 for (j
= 0; j
< max_sz
; j
+= sz
) {
364 if (nhdr_ptr
->n_namesz
== 0)
366 sz
= sizeof(Elf32_Nhdr
) +
367 ((nhdr_ptr
->n_namesz
+ 3) & ~3) +
368 ((nhdr_ptr
->n_descsz
+ 3) & ~3);
370 nhdr_ptr
= (Elf32_Nhdr
*)((char*)nhdr_ptr
+ sz
);
373 /* Add this contiguous chunk of notes section to vmcore list.*/
374 new = get_new_element();
376 kfree(notes_section
);
379 new->paddr
= phdr_ptr
->p_offset
;
381 list_add_tail(&new->list
, vc_list
);
383 kfree(notes_section
);
386 /* Prepare merged PT_NOTE program header. */
387 phdr
.p_type
= PT_NOTE
;
389 note_off
= sizeof(Elf32_Ehdr
) +
390 (ehdr_ptr
->e_phnum
- nr_ptnote
+1) * sizeof(Elf32_Phdr
);
391 phdr
.p_offset
= note_off
;
392 phdr
.p_vaddr
= phdr
.p_paddr
= 0;
393 phdr
.p_filesz
= phdr
.p_memsz
= phdr_sz
;
396 /* Add merged PT_NOTE program header*/
397 tmp
= elfptr
+ sizeof(Elf32_Ehdr
);
398 memcpy(tmp
, &phdr
, sizeof(phdr
));
401 /* Remove unwanted PT_NOTE program headers. */
402 i
= (nr_ptnote
- 1) * sizeof(Elf32_Phdr
);
404 memmove(tmp
, tmp
+i
, ((*elfsz
)-sizeof(Elf32_Ehdr
)-sizeof(Elf32_Phdr
)));
406 /* Modify e_phnum to reflect merged headers. */
407 ehdr_ptr
->e_phnum
= ehdr_ptr
->e_phnum
- nr_ptnote
+ 1;
412 /* Add memory chunks represented by program headers to vmcore list. Also update
413 * the new offset fields of exported program headers. */
414 static int __init
process_ptload_program_headers_elf64(char *elfptr
,
416 struct list_head
*vc_list
)
419 Elf64_Ehdr
*ehdr_ptr
;
420 Elf64_Phdr
*phdr_ptr
;
424 ehdr_ptr
= (Elf64_Ehdr
*)elfptr
;
425 phdr_ptr
= (Elf64_Phdr
*)(elfptr
+ sizeof(Elf64_Ehdr
)); /* PT_NOTE hdr */
427 /* First program header is PT_NOTE header. */
428 vmcore_off
= sizeof(Elf64_Ehdr
) +
429 (ehdr_ptr
->e_phnum
) * sizeof(Elf64_Phdr
) +
430 phdr_ptr
->p_memsz
; /* Note sections */
432 for (i
= 0; i
< ehdr_ptr
->e_phnum
; i
++, phdr_ptr
++) {
433 if (phdr_ptr
->p_type
!= PT_LOAD
)
436 /* Add this contiguous chunk of memory to vmcore list.*/
437 new = get_new_element();
440 new->paddr
= phdr_ptr
->p_offset
;
441 new->size
= phdr_ptr
->p_memsz
;
442 list_add_tail(&new->list
, vc_list
);
444 /* Update the program header offset. */
445 phdr_ptr
->p_offset
= vmcore_off
;
446 vmcore_off
= vmcore_off
+ phdr_ptr
->p_memsz
;
451 static int __init
process_ptload_program_headers_elf32(char *elfptr
,
453 struct list_head
*vc_list
)
456 Elf32_Ehdr
*ehdr_ptr
;
457 Elf32_Phdr
*phdr_ptr
;
461 ehdr_ptr
= (Elf32_Ehdr
*)elfptr
;
462 phdr_ptr
= (Elf32_Phdr
*)(elfptr
+ sizeof(Elf32_Ehdr
)); /* PT_NOTE hdr */
464 /* First program header is PT_NOTE header. */
465 vmcore_off
= sizeof(Elf32_Ehdr
) +
466 (ehdr_ptr
->e_phnum
) * sizeof(Elf32_Phdr
) +
467 phdr_ptr
->p_memsz
; /* Note sections */
469 for (i
= 0; i
< ehdr_ptr
->e_phnum
; i
++, phdr_ptr
++) {
470 if (phdr_ptr
->p_type
!= PT_LOAD
)
473 /* Add this contiguous chunk of memory to vmcore list.*/
474 new = get_new_element();
477 new->paddr
= phdr_ptr
->p_offset
;
478 new->size
= phdr_ptr
->p_memsz
;
479 list_add_tail(&new->list
, vc_list
);
481 /* Update the program header offset */
482 phdr_ptr
->p_offset
= vmcore_off
;
483 vmcore_off
= vmcore_off
+ phdr_ptr
->p_memsz
;
488 /* Sets offset fields of vmcore elements. */
489 static void __init
set_vmcore_list_offsets_elf64(char *elfptr
,
490 struct list_head
*vc_list
)
493 Elf64_Ehdr
*ehdr_ptr
;
496 ehdr_ptr
= (Elf64_Ehdr
*)elfptr
;
498 /* Skip Elf header and program headers. */
499 vmcore_off
= sizeof(Elf64_Ehdr
) +
500 (ehdr_ptr
->e_phnum
) * sizeof(Elf64_Phdr
);
502 list_for_each_entry(m
, vc_list
, list
) {
503 m
->offset
= vmcore_off
;
504 vmcore_off
+= m
->size
;
508 /* Sets offset fields of vmcore elements. */
509 static void __init
set_vmcore_list_offsets_elf32(char *elfptr
,
510 struct list_head
*vc_list
)
513 Elf32_Ehdr
*ehdr_ptr
;
516 ehdr_ptr
= (Elf32_Ehdr
*)elfptr
;
518 /* Skip Elf header and program headers. */
519 vmcore_off
= sizeof(Elf32_Ehdr
) +
520 (ehdr_ptr
->e_phnum
) * sizeof(Elf32_Phdr
);
522 list_for_each_entry(m
, vc_list
, list
) {
523 m
->offset
= vmcore_off
;
524 vmcore_off
+= m
->size
;
528 static int __init
parse_crash_elf64_headers(void)
534 addr
= elfcorehdr_addr
;
536 /* Read Elf header */
537 rc
= read_from_oldmem((char*)&ehdr
, sizeof(Elf64_Ehdr
), &addr
, 0);
541 /* Do some basic Verification. */
542 if (memcmp(ehdr
.e_ident
, ELFMAG
, SELFMAG
) != 0 ||
543 (ehdr
.e_type
!= ET_CORE
) ||
544 !vmcore_elf64_check_arch(&ehdr
) ||
545 ehdr
.e_ident
[EI_CLASS
] != ELFCLASS64
||
546 ehdr
.e_ident
[EI_VERSION
] != EV_CURRENT
||
547 ehdr
.e_version
!= EV_CURRENT
||
548 ehdr
.e_ehsize
!= sizeof(Elf64_Ehdr
) ||
549 ehdr
.e_phentsize
!= sizeof(Elf64_Phdr
) ||
551 pr_warn("Warning: Core image elf header is not sane\n");
555 /* Read in all elf headers. */
556 elfcorebuf_sz
= sizeof(Elf64_Ehdr
) + ehdr
.e_phnum
* sizeof(Elf64_Phdr
);
557 elfcorebuf
= kmalloc(elfcorebuf_sz
, GFP_KERNEL
);
560 addr
= elfcorehdr_addr
;
561 rc
= read_from_oldmem(elfcorebuf
, elfcorebuf_sz
, &addr
, 0);
567 /* Merge all PT_NOTE headers into one. */
568 rc
= merge_note_headers_elf64(elfcorebuf
, &elfcorebuf_sz
, &vmcore_list
);
573 rc
= process_ptload_program_headers_elf64(elfcorebuf
, elfcorebuf_sz
,
579 set_vmcore_list_offsets_elf64(elfcorebuf
, &vmcore_list
);
583 static int __init
parse_crash_elf32_headers(void)
589 addr
= elfcorehdr_addr
;
591 /* Read Elf header */
592 rc
= read_from_oldmem((char*)&ehdr
, sizeof(Elf32_Ehdr
), &addr
, 0);
596 /* Do some basic Verification. */
597 if (memcmp(ehdr
.e_ident
, ELFMAG
, SELFMAG
) != 0 ||
598 (ehdr
.e_type
!= ET_CORE
) ||
599 !elf_check_arch(&ehdr
) ||
600 ehdr
.e_ident
[EI_CLASS
] != ELFCLASS32
||
601 ehdr
.e_ident
[EI_VERSION
] != EV_CURRENT
||
602 ehdr
.e_version
!= EV_CURRENT
||
603 ehdr
.e_ehsize
!= sizeof(Elf32_Ehdr
) ||
604 ehdr
.e_phentsize
!= sizeof(Elf32_Phdr
) ||
606 pr_warn("Warning: Core image elf header is not sane\n");
610 /* Read in all elf headers. */
611 elfcorebuf_sz
= sizeof(Elf32_Ehdr
) + ehdr
.e_phnum
* sizeof(Elf32_Phdr
);
612 elfcorebuf
= kmalloc(elfcorebuf_sz
, GFP_KERNEL
);
615 addr
= elfcorehdr_addr
;
616 rc
= read_from_oldmem(elfcorebuf
, elfcorebuf_sz
, &addr
, 0);
622 /* Merge all PT_NOTE headers into one. */
623 rc
= merge_note_headers_elf32(elfcorebuf
, &elfcorebuf_sz
, &vmcore_list
);
628 rc
= process_ptload_program_headers_elf32(elfcorebuf
, elfcorebuf_sz
,
634 set_vmcore_list_offsets_elf32(elfcorebuf
, &vmcore_list
);
638 static int __init
parse_crash_elf_headers(void)
640 unsigned char e_ident
[EI_NIDENT
];
644 addr
= elfcorehdr_addr
;
645 rc
= read_from_oldmem(e_ident
, EI_NIDENT
, &addr
, 0);
648 if (memcmp(e_ident
, ELFMAG
, SELFMAG
) != 0) {
649 pr_warn("Warning: Core image elf header not found\n");
653 if (e_ident
[EI_CLASS
] == ELFCLASS64
) {
654 rc
= parse_crash_elf64_headers();
658 /* Determine vmcore size. */
659 vmcore_size
= get_vmcore_size_elf64(elfcorebuf
);
660 } else if (e_ident
[EI_CLASS
] == ELFCLASS32
) {
661 rc
= parse_crash_elf32_headers();
665 /* Determine vmcore size. */
666 vmcore_size
= get_vmcore_size_elf32(elfcorebuf
);
668 pr_warn("Warning: Core image elf header is not sane\n");
674 /* Init function for vmcore module. */
675 static int __init
vmcore_init(void)
679 /* If elfcorehdr= has been passed in cmdline, then capture the dump.*/
680 if (!(is_vmcore_usable()))
682 rc
= parse_crash_elf_headers();
684 pr_warn("Kdump: vmcore not initialized\n");
688 proc_vmcore
= proc_create("vmcore", S_IRUSR
, NULL
, &proc_vmcore_operations
);
690 proc_vmcore
->size
= vmcore_size
;
693 module_init(vmcore_init
)
695 /* Cleanup function for vmcore module. */
696 void vmcore_cleanup(void)
698 struct list_head
*pos
, *next
;
701 remove_proc_entry(proc_vmcore
->name
, proc_vmcore
->parent
);
705 /* clear the vmcore list. */
706 list_for_each_safe(pos
, next
, &vmcore_list
) {
709 m
= list_entry(pos
, struct vmcore
, list
);
716 EXPORT_SYMBOL_GPL(vmcore_cleanup
);