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/slab.h>
16 #include <linux/highmem.h>
17 #include <linux/bootmem.h>
18 #include <linux/init.h>
19 #include <linux/crash_dump.h>
20 #include <linux/list.h>
21 #include <asm/uaccess.h>
24 /* List representing chunks of contiguous memory areas and their offsets in
27 static LIST_HEAD(vmcore_list
);
29 /* Stores the pointer to the buffer containing kernel elf core headers. */
30 static char *elfcorebuf
;
31 static size_t elfcorebuf_sz
;
33 /* Total size of vmcore file. */
34 static u64 vmcore_size
;
36 static struct proc_dir_entry
*proc_vmcore
= NULL
;
38 /* Reads a page from the oldmem device from given offset. */
39 static ssize_t
read_from_oldmem(char *buf
, size_t count
,
40 u64
*ppos
, int userbuf
)
42 unsigned long pfn
, offset
;
44 ssize_t read
= 0, tmp
;
49 offset
= (unsigned long)(*ppos
% PAGE_SIZE
);
50 pfn
= (unsigned long)(*ppos
/ PAGE_SIZE
);
53 if (count
> (PAGE_SIZE
- offset
))
54 nr_bytes
= PAGE_SIZE
- offset
;
58 tmp
= copy_oldmem_page(pfn
, buf
, nr_bytes
, offset
, userbuf
);
72 /* Maps vmcore file offset to respective physical address in memroy. */
73 static u64
map_offset_to_paddr(loff_t offset
, struct list_head
*vc_list
,
74 struct vmcore
**m_ptr
)
79 list_for_each_entry(m
, vc_list
, list
) {
82 end
= m
->offset
+ m
->size
- 1;
83 if (offset
>= start
&& offset
<= end
) {
84 paddr
= m
->paddr
+ offset
- start
;
93 /* Read from the ELF header and then the crash dump. On error, negative value is
94 * returned otherwise number of bytes read are returned.
96 static ssize_t
read_vmcore(struct file
*file
, char __user
*buffer
,
97 size_t buflen
, loff_t
*fpos
)
102 struct vmcore
*curr_m
= NULL
;
104 if (buflen
== 0 || *fpos
>= vmcore_size
)
107 /* trim buflen to not go beyond EOF */
108 if (buflen
> vmcore_size
- *fpos
)
109 buflen
= vmcore_size
- *fpos
;
111 /* Read ELF core header */
112 if (*fpos
< elfcorebuf_sz
) {
113 tsz
= elfcorebuf_sz
- *fpos
;
116 if (copy_to_user(buffer
, elfcorebuf
+ *fpos
, tsz
))
123 /* leave now if filled buffer already */
128 start
= map_offset_to_paddr(*fpos
, &vmcore_list
, &curr_m
);
131 if ((tsz
= (PAGE_SIZE
- (start
& ~PAGE_MASK
))) > buflen
)
134 /* Calculate left bytes in current memory segment. */
135 nr_bytes
= (curr_m
->size
- (start
- curr_m
->paddr
));
140 tmp
= read_from_oldmem(buffer
, tsz
, &start
, 1);
147 if (start
>= (curr_m
->paddr
+ curr_m
->size
)) {
148 if (curr_m
->list
.next
== &vmcore_list
)
150 curr_m
= list_entry(curr_m
->list
.next
,
151 struct vmcore
, list
);
152 start
= curr_m
->paddr
;
154 if ((tsz
= (PAGE_SIZE
- (start
& ~PAGE_MASK
))) > buflen
)
156 /* Calculate left bytes in current memory segment. */
157 nr_bytes
= (curr_m
->size
- (start
- curr_m
->paddr
));
164 static const struct file_operations proc_vmcore_operations
= {
168 static struct vmcore
* __init
get_new_element(void)
170 return kzalloc(sizeof(struct vmcore
), GFP_KERNEL
);
173 static u64 __init
get_vmcore_size_elf64(char *elfptr
)
177 Elf64_Ehdr
*ehdr_ptr
;
178 Elf64_Phdr
*phdr_ptr
;
180 ehdr_ptr
= (Elf64_Ehdr
*)elfptr
;
181 phdr_ptr
= (Elf64_Phdr
*)(elfptr
+ sizeof(Elf64_Ehdr
));
182 size
= sizeof(Elf64_Ehdr
) + ((ehdr_ptr
->e_phnum
) * sizeof(Elf64_Phdr
));
183 for (i
= 0; i
< ehdr_ptr
->e_phnum
; i
++) {
184 size
+= phdr_ptr
->p_memsz
;
190 static u64 __init
get_vmcore_size_elf32(char *elfptr
)
194 Elf32_Ehdr
*ehdr_ptr
;
195 Elf32_Phdr
*phdr_ptr
;
197 ehdr_ptr
= (Elf32_Ehdr
*)elfptr
;
198 phdr_ptr
= (Elf32_Phdr
*)(elfptr
+ sizeof(Elf32_Ehdr
));
199 size
= sizeof(Elf32_Ehdr
) + ((ehdr_ptr
->e_phnum
) * sizeof(Elf32_Phdr
));
200 for (i
= 0; i
< ehdr_ptr
->e_phnum
; i
++) {
201 size
+= phdr_ptr
->p_memsz
;
207 /* Merges all the PT_NOTE headers into one. */
208 static int __init
merge_note_headers_elf64(char *elfptr
, size_t *elfsz
,
209 struct list_head
*vc_list
)
211 int i
, nr_ptnote
=0, rc
=0;
213 Elf64_Ehdr
*ehdr_ptr
;
214 Elf64_Phdr phdr
, *phdr_ptr
;
215 Elf64_Nhdr
*nhdr_ptr
;
216 u64 phdr_sz
= 0, note_off
;
218 ehdr_ptr
= (Elf64_Ehdr
*)elfptr
;
219 phdr_ptr
= (Elf64_Phdr
*)(elfptr
+ sizeof(Elf64_Ehdr
));
220 for (i
= 0; i
< ehdr_ptr
->e_phnum
; i
++, phdr_ptr
++) {
224 u64 offset
, max_sz
, sz
, real_sz
= 0;
225 if (phdr_ptr
->p_type
!= PT_NOTE
)
228 max_sz
= phdr_ptr
->p_memsz
;
229 offset
= phdr_ptr
->p_offset
;
230 notes_section
= kmalloc(max_sz
, GFP_KERNEL
);
233 rc
= read_from_oldmem(notes_section
, max_sz
, &offset
, 0);
235 kfree(notes_section
);
238 nhdr_ptr
= notes_section
;
239 for (j
= 0; j
< max_sz
; j
+= sz
) {
240 if (nhdr_ptr
->n_namesz
== 0)
242 sz
= sizeof(Elf64_Nhdr
) +
243 ((nhdr_ptr
->n_namesz
+ 3) & ~3) +
244 ((nhdr_ptr
->n_descsz
+ 3) & ~3);
246 nhdr_ptr
= (Elf64_Nhdr
*)((char*)nhdr_ptr
+ sz
);
249 /* Add this contiguous chunk of notes section to vmcore list.*/
250 new = get_new_element();
252 kfree(notes_section
);
255 new->paddr
= phdr_ptr
->p_offset
;
257 list_add_tail(&new->list
, vc_list
);
259 kfree(notes_section
);
262 /* Prepare merged PT_NOTE program header. */
263 phdr
.p_type
= PT_NOTE
;
265 note_off
= sizeof(Elf64_Ehdr
) +
266 (ehdr_ptr
->e_phnum
- nr_ptnote
+1) * sizeof(Elf64_Phdr
);
267 phdr
.p_offset
= note_off
;
268 phdr
.p_vaddr
= phdr
.p_paddr
= 0;
269 phdr
.p_filesz
= phdr
.p_memsz
= phdr_sz
;
272 /* Add merged PT_NOTE program header*/
273 tmp
= elfptr
+ sizeof(Elf64_Ehdr
);
274 memcpy(tmp
, &phdr
, sizeof(phdr
));
277 /* Remove unwanted PT_NOTE program headers. */
278 i
= (nr_ptnote
- 1) * sizeof(Elf64_Phdr
);
280 memmove(tmp
, tmp
+i
, ((*elfsz
)-sizeof(Elf64_Ehdr
)-sizeof(Elf64_Phdr
)));
282 /* Modify e_phnum to reflect merged headers. */
283 ehdr_ptr
->e_phnum
= ehdr_ptr
->e_phnum
- nr_ptnote
+ 1;
288 /* Merges all the PT_NOTE headers into one. */
289 static int __init
merge_note_headers_elf32(char *elfptr
, size_t *elfsz
,
290 struct list_head
*vc_list
)
292 int i
, nr_ptnote
=0, rc
=0;
294 Elf32_Ehdr
*ehdr_ptr
;
295 Elf32_Phdr phdr
, *phdr_ptr
;
296 Elf32_Nhdr
*nhdr_ptr
;
297 u64 phdr_sz
= 0, note_off
;
299 ehdr_ptr
= (Elf32_Ehdr
*)elfptr
;
300 phdr_ptr
= (Elf32_Phdr
*)(elfptr
+ sizeof(Elf32_Ehdr
));
301 for (i
= 0; i
< ehdr_ptr
->e_phnum
; i
++, phdr_ptr
++) {
305 u64 offset
, max_sz
, sz
, real_sz
= 0;
306 if (phdr_ptr
->p_type
!= PT_NOTE
)
309 max_sz
= phdr_ptr
->p_memsz
;
310 offset
= phdr_ptr
->p_offset
;
311 notes_section
= kmalloc(max_sz
, GFP_KERNEL
);
314 rc
= read_from_oldmem(notes_section
, max_sz
, &offset
, 0);
316 kfree(notes_section
);
319 nhdr_ptr
= notes_section
;
320 for (j
= 0; j
< max_sz
; j
+= sz
) {
321 if (nhdr_ptr
->n_namesz
== 0)
323 sz
= sizeof(Elf32_Nhdr
) +
324 ((nhdr_ptr
->n_namesz
+ 3) & ~3) +
325 ((nhdr_ptr
->n_descsz
+ 3) & ~3);
327 nhdr_ptr
= (Elf32_Nhdr
*)((char*)nhdr_ptr
+ sz
);
330 /* Add this contiguous chunk of notes section to vmcore list.*/
331 new = get_new_element();
333 kfree(notes_section
);
336 new->paddr
= phdr_ptr
->p_offset
;
338 list_add_tail(&new->list
, vc_list
);
340 kfree(notes_section
);
343 /* Prepare merged PT_NOTE program header. */
344 phdr
.p_type
= PT_NOTE
;
346 note_off
= sizeof(Elf32_Ehdr
) +
347 (ehdr_ptr
->e_phnum
- nr_ptnote
+1) * sizeof(Elf32_Phdr
);
348 phdr
.p_offset
= note_off
;
349 phdr
.p_vaddr
= phdr
.p_paddr
= 0;
350 phdr
.p_filesz
= phdr
.p_memsz
= phdr_sz
;
353 /* Add merged PT_NOTE program header*/
354 tmp
= elfptr
+ sizeof(Elf32_Ehdr
);
355 memcpy(tmp
, &phdr
, sizeof(phdr
));
358 /* Remove unwanted PT_NOTE program headers. */
359 i
= (nr_ptnote
- 1) * sizeof(Elf32_Phdr
);
361 memmove(tmp
, tmp
+i
, ((*elfsz
)-sizeof(Elf32_Ehdr
)-sizeof(Elf32_Phdr
)));
363 /* Modify e_phnum to reflect merged headers. */
364 ehdr_ptr
->e_phnum
= ehdr_ptr
->e_phnum
- nr_ptnote
+ 1;
369 /* Add memory chunks represented by program headers to vmcore list. Also update
370 * the new offset fields of exported program headers. */
371 static int __init
process_ptload_program_headers_elf64(char *elfptr
,
373 struct list_head
*vc_list
)
376 Elf64_Ehdr
*ehdr_ptr
;
377 Elf64_Phdr
*phdr_ptr
;
381 ehdr_ptr
= (Elf64_Ehdr
*)elfptr
;
382 phdr_ptr
= (Elf64_Phdr
*)(elfptr
+ sizeof(Elf64_Ehdr
)); /* PT_NOTE hdr */
384 /* First program header is PT_NOTE header. */
385 vmcore_off
= sizeof(Elf64_Ehdr
) +
386 (ehdr_ptr
->e_phnum
) * sizeof(Elf64_Phdr
) +
387 phdr_ptr
->p_memsz
; /* Note sections */
389 for (i
= 0; i
< ehdr_ptr
->e_phnum
; i
++, phdr_ptr
++) {
390 if (phdr_ptr
->p_type
!= PT_LOAD
)
393 /* Add this contiguous chunk of memory to vmcore list.*/
394 new = get_new_element();
397 new->paddr
= phdr_ptr
->p_offset
;
398 new->size
= phdr_ptr
->p_memsz
;
399 list_add_tail(&new->list
, vc_list
);
401 /* Update the program header offset. */
402 phdr_ptr
->p_offset
= vmcore_off
;
403 vmcore_off
= vmcore_off
+ phdr_ptr
->p_memsz
;
408 static int __init
process_ptload_program_headers_elf32(char *elfptr
,
410 struct list_head
*vc_list
)
413 Elf32_Ehdr
*ehdr_ptr
;
414 Elf32_Phdr
*phdr_ptr
;
418 ehdr_ptr
= (Elf32_Ehdr
*)elfptr
;
419 phdr_ptr
= (Elf32_Phdr
*)(elfptr
+ sizeof(Elf32_Ehdr
)); /* PT_NOTE hdr */
421 /* First program header is PT_NOTE header. */
422 vmcore_off
= sizeof(Elf32_Ehdr
) +
423 (ehdr_ptr
->e_phnum
) * sizeof(Elf32_Phdr
) +
424 phdr_ptr
->p_memsz
; /* Note sections */
426 for (i
= 0; i
< ehdr_ptr
->e_phnum
; i
++, phdr_ptr
++) {
427 if (phdr_ptr
->p_type
!= PT_LOAD
)
430 /* Add this contiguous chunk of memory to vmcore list.*/
431 new = get_new_element();
434 new->paddr
= phdr_ptr
->p_offset
;
435 new->size
= phdr_ptr
->p_memsz
;
436 list_add_tail(&new->list
, vc_list
);
438 /* Update the program header offset */
439 phdr_ptr
->p_offset
= vmcore_off
;
440 vmcore_off
= vmcore_off
+ phdr_ptr
->p_memsz
;
445 /* Sets offset fields of vmcore elements. */
446 static void __init
set_vmcore_list_offsets_elf64(char *elfptr
,
447 struct list_head
*vc_list
)
450 Elf64_Ehdr
*ehdr_ptr
;
453 ehdr_ptr
= (Elf64_Ehdr
*)elfptr
;
455 /* Skip Elf header and program headers. */
456 vmcore_off
= sizeof(Elf64_Ehdr
) +
457 (ehdr_ptr
->e_phnum
) * sizeof(Elf64_Phdr
);
459 list_for_each_entry(m
, vc_list
, list
) {
460 m
->offset
= vmcore_off
;
461 vmcore_off
+= m
->size
;
465 /* Sets offset fields of vmcore elements. */
466 static void __init
set_vmcore_list_offsets_elf32(char *elfptr
,
467 struct list_head
*vc_list
)
470 Elf32_Ehdr
*ehdr_ptr
;
473 ehdr_ptr
= (Elf32_Ehdr
*)elfptr
;
475 /* Skip Elf header and program headers. */
476 vmcore_off
= sizeof(Elf32_Ehdr
) +
477 (ehdr_ptr
->e_phnum
) * sizeof(Elf32_Phdr
);
479 list_for_each_entry(m
, vc_list
, list
) {
480 m
->offset
= vmcore_off
;
481 vmcore_off
+= m
->size
;
485 static int __init
parse_crash_elf64_headers(void)
491 addr
= elfcorehdr_addr
;
493 /* Read Elf header */
494 rc
= read_from_oldmem((char*)&ehdr
, sizeof(Elf64_Ehdr
), &addr
, 0);
498 /* Do some basic Verification. */
499 if (memcmp(ehdr
.e_ident
, ELFMAG
, SELFMAG
) != 0 ||
500 (ehdr
.e_type
!= ET_CORE
) ||
501 !vmcore_elf_check_arch(&ehdr
) ||
502 ehdr
.e_ident
[EI_CLASS
] != ELFCLASS64
||
503 ehdr
.e_ident
[EI_VERSION
] != EV_CURRENT
||
504 ehdr
.e_version
!= EV_CURRENT
||
505 ehdr
.e_ehsize
!= sizeof(Elf64_Ehdr
) ||
506 ehdr
.e_phentsize
!= sizeof(Elf64_Phdr
) ||
508 printk(KERN_WARNING
"Warning: Core image elf header is not"
513 /* Read in all elf headers. */
514 elfcorebuf_sz
= sizeof(Elf64_Ehdr
) + ehdr
.e_phnum
* sizeof(Elf64_Phdr
);
515 elfcorebuf
= kmalloc(elfcorebuf_sz
, GFP_KERNEL
);
518 addr
= elfcorehdr_addr
;
519 rc
= read_from_oldmem(elfcorebuf
, elfcorebuf_sz
, &addr
, 0);
525 /* Merge all PT_NOTE headers into one. */
526 rc
= merge_note_headers_elf64(elfcorebuf
, &elfcorebuf_sz
, &vmcore_list
);
531 rc
= process_ptload_program_headers_elf64(elfcorebuf
, elfcorebuf_sz
,
537 set_vmcore_list_offsets_elf64(elfcorebuf
, &vmcore_list
);
541 static int __init
parse_crash_elf32_headers(void)
547 addr
= elfcorehdr_addr
;
549 /* Read Elf header */
550 rc
= read_from_oldmem((char*)&ehdr
, sizeof(Elf32_Ehdr
), &addr
, 0);
554 /* Do some basic Verification. */
555 if (memcmp(ehdr
.e_ident
, ELFMAG
, SELFMAG
) != 0 ||
556 (ehdr
.e_type
!= ET_CORE
) ||
557 !elf_check_arch(&ehdr
) ||
558 ehdr
.e_ident
[EI_CLASS
] != ELFCLASS32
||
559 ehdr
.e_ident
[EI_VERSION
] != EV_CURRENT
||
560 ehdr
.e_version
!= EV_CURRENT
||
561 ehdr
.e_ehsize
!= sizeof(Elf32_Ehdr
) ||
562 ehdr
.e_phentsize
!= sizeof(Elf32_Phdr
) ||
564 printk(KERN_WARNING
"Warning: Core image elf header is not"
569 /* Read in all elf headers. */
570 elfcorebuf_sz
= sizeof(Elf32_Ehdr
) + ehdr
.e_phnum
* sizeof(Elf32_Phdr
);
571 elfcorebuf
= kmalloc(elfcorebuf_sz
, GFP_KERNEL
);
574 addr
= elfcorehdr_addr
;
575 rc
= read_from_oldmem(elfcorebuf
, elfcorebuf_sz
, &addr
, 0);
581 /* Merge all PT_NOTE headers into one. */
582 rc
= merge_note_headers_elf32(elfcorebuf
, &elfcorebuf_sz
, &vmcore_list
);
587 rc
= process_ptload_program_headers_elf32(elfcorebuf
, elfcorebuf_sz
,
593 set_vmcore_list_offsets_elf32(elfcorebuf
, &vmcore_list
);
597 static int __init
parse_crash_elf_headers(void)
599 unsigned char e_ident
[EI_NIDENT
];
603 addr
= elfcorehdr_addr
;
604 rc
= read_from_oldmem(e_ident
, EI_NIDENT
, &addr
, 0);
607 if (memcmp(e_ident
, ELFMAG
, SELFMAG
) != 0) {
608 printk(KERN_WARNING
"Warning: Core image elf header"
613 if (e_ident
[EI_CLASS
] == ELFCLASS64
) {
614 rc
= parse_crash_elf64_headers();
618 /* Determine vmcore size. */
619 vmcore_size
= get_vmcore_size_elf64(elfcorebuf
);
620 } else if (e_ident
[EI_CLASS
] == ELFCLASS32
) {
621 rc
= parse_crash_elf32_headers();
625 /* Determine vmcore size. */
626 vmcore_size
= get_vmcore_size_elf32(elfcorebuf
);
628 printk(KERN_WARNING
"Warning: Core image elf header is not"
635 /* Init function for vmcore module. */
636 static int __init
vmcore_init(void)
640 /* If elfcorehdr= has been passed in cmdline, then capture the dump.*/
641 if (!(is_vmcore_usable()))
643 rc
= parse_crash_elf_headers();
645 printk(KERN_WARNING
"Kdump: vmcore not initialized\n");
649 proc_vmcore
= proc_create("vmcore", S_IRUSR
, NULL
, &proc_vmcore_operations
);
651 proc_vmcore
->size
= vmcore_size
;
654 module_init(vmcore_init
)