Merge branch 'e1000-fixes' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik...
[linux-2.6] / fs / proc / vmcore.c
1 /*
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
7  *
8  */
9
10 #include <linux/mm.h>
11 #include <linux/proc_fs.h>
12 #include <linux/user.h>
13 #include <linux/a.out.h>
14 #include <linux/elf.h>
15 #include <linux/elfcore.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>
22 #include <asm/io.h>
23
24 /* List representing chunks of contiguous memory areas and their offsets in
25  * vmcore file.
26  */
27 static LIST_HEAD(vmcore_list);
28
29 /* Stores the pointer to the buffer containing kernel elf core headers. */
30 static char *elfcorebuf;
31 static size_t elfcorebuf_sz;
32
33 /* Total size of vmcore file. */
34 static u64 vmcore_size;
35
36 /* Stores the physical address of elf header of crash image. */
37 unsigned long long elfcorehdr_addr = ELFCORE_ADDR_MAX;
38
39 struct proc_dir_entry *proc_vmcore = NULL;
40
41 /* Reads a page from the oldmem device from given offset. */
42 static ssize_t read_from_oldmem(char *buf, size_t count,
43                                 u64 *ppos, int userbuf)
44 {
45         unsigned long pfn, offset;
46         size_t nr_bytes;
47         ssize_t read = 0, tmp;
48
49         if (!count)
50                 return 0;
51
52         offset = (unsigned long)(*ppos % PAGE_SIZE);
53         pfn = (unsigned long)(*ppos / PAGE_SIZE);
54         if (pfn > saved_max_pfn)
55                 return -EINVAL;
56
57         do {
58                 if (count > (PAGE_SIZE - offset))
59                         nr_bytes = PAGE_SIZE - offset;
60                 else
61                         nr_bytes = count;
62
63                 tmp = copy_oldmem_page(pfn, buf, nr_bytes, offset, userbuf);
64                 if (tmp < 0)
65                         return tmp;
66                 *ppos += nr_bytes;
67                 count -= nr_bytes;
68                 buf += nr_bytes;
69                 read += nr_bytes;
70                 ++pfn;
71                 offset = 0;
72         } while (count);
73
74         return read;
75 }
76
77 /* Maps vmcore file offset to respective physical address in memroy. */
78 static u64 map_offset_to_paddr(loff_t offset, struct list_head *vc_list,
79                                         struct vmcore **m_ptr)
80 {
81         struct vmcore *m;
82         u64 paddr;
83
84         list_for_each_entry(m, vc_list, list) {
85                 u64 start, end;
86                 start = m->offset;
87                 end = m->offset + m->size - 1;
88                 if (offset >= start && offset <= end) {
89                         paddr = m->paddr + offset - start;
90                         *m_ptr = m;
91                         return paddr;
92                 }
93         }
94         *m_ptr = NULL;
95         return 0;
96 }
97
98 /* Read from the ELF header and then the crash dump. On error, negative value is
99  * returned otherwise number of bytes read are returned.
100  */
101 static ssize_t read_vmcore(struct file *file, char __user *buffer,
102                                 size_t buflen, loff_t *fpos)
103 {
104         ssize_t acc = 0, tmp;
105         size_t tsz;
106         u64 start, nr_bytes;
107         struct vmcore *curr_m = NULL;
108
109         if (buflen == 0 || *fpos >= vmcore_size)
110                 return 0;
111
112         /* trim buflen to not go beyond EOF */
113         if (buflen > vmcore_size - *fpos)
114                 buflen = vmcore_size - *fpos;
115
116         /* Read ELF core header */
117         if (*fpos < elfcorebuf_sz) {
118                 tsz = elfcorebuf_sz - *fpos;
119                 if (buflen < tsz)
120                         tsz = buflen;
121                 if (copy_to_user(buffer, elfcorebuf + *fpos, tsz))
122                         return -EFAULT;
123                 buflen -= tsz;
124                 *fpos += tsz;
125                 buffer += tsz;
126                 acc += tsz;
127
128                 /* leave now if filled buffer already */
129                 if (buflen == 0)
130                         return acc;
131         }
132
133         start = map_offset_to_paddr(*fpos, &vmcore_list, &curr_m);
134         if (!curr_m)
135                 return -EINVAL;
136         if ((tsz = (PAGE_SIZE - (start & ~PAGE_MASK))) > buflen)
137                 tsz = buflen;
138
139         /* Calculate left bytes in current memory segment. */
140         nr_bytes = (curr_m->size - (start - curr_m->paddr));
141         if (tsz > nr_bytes)
142                 tsz = nr_bytes;
143
144         while (buflen) {
145                 tmp = read_from_oldmem(buffer, tsz, &start, 1);
146                 if (tmp < 0)
147                         return tmp;
148                 buflen -= tsz;
149                 *fpos += tsz;
150                 buffer += tsz;
151                 acc += tsz;
152                 if (start >= (curr_m->paddr + curr_m->size)) {
153                         if (curr_m->list.next == &vmcore_list)
154                                 return acc;     /*EOF*/
155                         curr_m = list_entry(curr_m->list.next,
156                                                 struct vmcore, list);
157                         start = curr_m->paddr;
158                 }
159                 if ((tsz = (PAGE_SIZE - (start & ~PAGE_MASK))) > buflen)
160                         tsz = buflen;
161                 /* Calculate left bytes in current memory segment. */
162                 nr_bytes = (curr_m->size - (start - curr_m->paddr));
163                 if (tsz > nr_bytes)
164                         tsz = nr_bytes;
165         }
166         return acc;
167 }
168
169 static int open_vmcore(struct inode *inode, struct file *filp)
170 {
171         return 0;
172 }
173
174 const struct file_operations proc_vmcore_operations = {
175         .read           = read_vmcore,
176         .open           = open_vmcore,
177 };
178
179 static struct vmcore* __init get_new_element(void)
180 {
181         struct vmcore *p;
182
183         p = kmalloc(sizeof(*p), GFP_KERNEL);
184         if (p)
185                 memset(p, 0, sizeof(*p));
186         return p;
187 }
188
189 static u64 __init get_vmcore_size_elf64(char *elfptr)
190 {
191         int i;
192         u64 size;
193         Elf64_Ehdr *ehdr_ptr;
194         Elf64_Phdr *phdr_ptr;
195
196         ehdr_ptr = (Elf64_Ehdr *)elfptr;
197         phdr_ptr = (Elf64_Phdr*)(elfptr + sizeof(Elf64_Ehdr));
198         size = sizeof(Elf64_Ehdr) + ((ehdr_ptr->e_phnum) * sizeof(Elf64_Phdr));
199         for (i = 0; i < ehdr_ptr->e_phnum; i++) {
200                 size += phdr_ptr->p_memsz;
201                 phdr_ptr++;
202         }
203         return size;
204 }
205
206 static u64 __init get_vmcore_size_elf32(char *elfptr)
207 {
208         int i;
209         u64 size;
210         Elf32_Ehdr *ehdr_ptr;
211         Elf32_Phdr *phdr_ptr;
212
213         ehdr_ptr = (Elf32_Ehdr *)elfptr;
214         phdr_ptr = (Elf32_Phdr*)(elfptr + sizeof(Elf32_Ehdr));
215         size = sizeof(Elf32_Ehdr) + ((ehdr_ptr->e_phnum) * sizeof(Elf32_Phdr));
216         for (i = 0; i < ehdr_ptr->e_phnum; i++) {
217                 size += phdr_ptr->p_memsz;
218                 phdr_ptr++;
219         }
220         return size;
221 }
222
223 /* Merges all the PT_NOTE headers into one. */
224 static int __init merge_note_headers_elf64(char *elfptr, size_t *elfsz,
225                                                 struct list_head *vc_list)
226 {
227         int i, nr_ptnote=0, rc=0;
228         char *tmp;
229         Elf64_Ehdr *ehdr_ptr;
230         Elf64_Phdr phdr, *phdr_ptr;
231         Elf64_Nhdr *nhdr_ptr;
232         u64 phdr_sz = 0, note_off;
233
234         ehdr_ptr = (Elf64_Ehdr *)elfptr;
235         phdr_ptr = (Elf64_Phdr*)(elfptr + sizeof(Elf64_Ehdr));
236         for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) {
237                 int j;
238                 void *notes_section;
239                 struct vmcore *new;
240                 u64 offset, max_sz, sz, real_sz = 0;
241                 if (phdr_ptr->p_type != PT_NOTE)
242                         continue;
243                 nr_ptnote++;
244                 max_sz = phdr_ptr->p_memsz;
245                 offset = phdr_ptr->p_offset;
246                 notes_section = kmalloc(max_sz, GFP_KERNEL);
247                 if (!notes_section)
248                         return -ENOMEM;
249                 rc = read_from_oldmem(notes_section, max_sz, &offset, 0);
250                 if (rc < 0) {
251                         kfree(notes_section);
252                         return rc;
253                 }
254                 nhdr_ptr = notes_section;
255                 for (j = 0; j < max_sz; j += sz) {
256                         if (nhdr_ptr->n_namesz == 0)
257                                 break;
258                         sz = sizeof(Elf64_Nhdr) +
259                                 ((nhdr_ptr->n_namesz + 3) & ~3) +
260                                 ((nhdr_ptr->n_descsz + 3) & ~3);
261                         real_sz += sz;
262                         nhdr_ptr = (Elf64_Nhdr*)((char*)nhdr_ptr + sz);
263                 }
264
265                 /* Add this contiguous chunk of notes section to vmcore list.*/
266                 new = get_new_element();
267                 if (!new) {
268                         kfree(notes_section);
269                         return -ENOMEM;
270                 }
271                 new->paddr = phdr_ptr->p_offset;
272                 new->size = real_sz;
273                 list_add_tail(&new->list, vc_list);
274                 phdr_sz += real_sz;
275                 kfree(notes_section);
276         }
277
278         /* Prepare merged PT_NOTE program header. */
279         phdr.p_type    = PT_NOTE;
280         phdr.p_flags   = 0;
281         note_off = sizeof(Elf64_Ehdr) +
282                         (ehdr_ptr->e_phnum - nr_ptnote +1) * sizeof(Elf64_Phdr);
283         phdr.p_offset  = note_off;
284         phdr.p_vaddr   = phdr.p_paddr = 0;
285         phdr.p_filesz  = phdr.p_memsz = phdr_sz;
286         phdr.p_align   = 0;
287
288         /* Add merged PT_NOTE program header*/
289         tmp = elfptr + sizeof(Elf64_Ehdr);
290         memcpy(tmp, &phdr, sizeof(phdr));
291         tmp += sizeof(phdr);
292
293         /* Remove unwanted PT_NOTE program headers. */
294         i = (nr_ptnote - 1) * sizeof(Elf64_Phdr);
295         *elfsz = *elfsz - i;
296         memmove(tmp, tmp+i, ((*elfsz)-sizeof(Elf64_Ehdr)-sizeof(Elf64_Phdr)));
297
298         /* Modify e_phnum to reflect merged headers. */
299         ehdr_ptr->e_phnum = ehdr_ptr->e_phnum - nr_ptnote + 1;
300
301         return 0;
302 }
303
304 /* Merges all the PT_NOTE headers into one. */
305 static int __init merge_note_headers_elf32(char *elfptr, size_t *elfsz,
306                                                 struct list_head *vc_list)
307 {
308         int i, nr_ptnote=0, rc=0;
309         char *tmp;
310         Elf32_Ehdr *ehdr_ptr;
311         Elf32_Phdr phdr, *phdr_ptr;
312         Elf32_Nhdr *nhdr_ptr;
313         u64 phdr_sz = 0, note_off;
314
315         ehdr_ptr = (Elf32_Ehdr *)elfptr;
316         phdr_ptr = (Elf32_Phdr*)(elfptr + sizeof(Elf32_Ehdr));
317         for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) {
318                 int j;
319                 void *notes_section;
320                 struct vmcore *new;
321                 u64 offset, max_sz, sz, real_sz = 0;
322                 if (phdr_ptr->p_type != PT_NOTE)
323                         continue;
324                 nr_ptnote++;
325                 max_sz = phdr_ptr->p_memsz;
326                 offset = phdr_ptr->p_offset;
327                 notes_section = kmalloc(max_sz, GFP_KERNEL);
328                 if (!notes_section)
329                         return -ENOMEM;
330                 rc = read_from_oldmem(notes_section, max_sz, &offset, 0);
331                 if (rc < 0) {
332                         kfree(notes_section);
333                         return rc;
334                 }
335                 nhdr_ptr = notes_section;
336                 for (j = 0; j < max_sz; j += sz) {
337                         if (nhdr_ptr->n_namesz == 0)
338                                 break;
339                         sz = sizeof(Elf32_Nhdr) +
340                                 ((nhdr_ptr->n_namesz + 3) & ~3) +
341                                 ((nhdr_ptr->n_descsz + 3) & ~3);
342                         real_sz += sz;
343                         nhdr_ptr = (Elf32_Nhdr*)((char*)nhdr_ptr + sz);
344                 }
345
346                 /* Add this contiguous chunk of notes section to vmcore list.*/
347                 new = get_new_element();
348                 if (!new) {
349                         kfree(notes_section);
350                         return -ENOMEM;
351                 }
352                 new->paddr = phdr_ptr->p_offset;
353                 new->size = real_sz;
354                 list_add_tail(&new->list, vc_list);
355                 phdr_sz += real_sz;
356                 kfree(notes_section);
357         }
358
359         /* Prepare merged PT_NOTE program header. */
360         phdr.p_type    = PT_NOTE;
361         phdr.p_flags   = 0;
362         note_off = sizeof(Elf32_Ehdr) +
363                         (ehdr_ptr->e_phnum - nr_ptnote +1) * sizeof(Elf32_Phdr);
364         phdr.p_offset  = note_off;
365         phdr.p_vaddr   = phdr.p_paddr = 0;
366         phdr.p_filesz  = phdr.p_memsz = phdr_sz;
367         phdr.p_align   = 0;
368
369         /* Add merged PT_NOTE program header*/
370         tmp = elfptr + sizeof(Elf32_Ehdr);
371         memcpy(tmp, &phdr, sizeof(phdr));
372         tmp += sizeof(phdr);
373
374         /* Remove unwanted PT_NOTE program headers. */
375         i = (nr_ptnote - 1) * sizeof(Elf32_Phdr);
376         *elfsz = *elfsz - i;
377         memmove(tmp, tmp+i, ((*elfsz)-sizeof(Elf32_Ehdr)-sizeof(Elf32_Phdr)));
378
379         /* Modify e_phnum to reflect merged headers. */
380         ehdr_ptr->e_phnum = ehdr_ptr->e_phnum - nr_ptnote + 1;
381
382         return 0;
383 }
384
385 /* Add memory chunks represented by program headers to vmcore list. Also update
386  * the new offset fields of exported program headers. */
387 static int __init process_ptload_program_headers_elf64(char *elfptr,
388                                                 size_t elfsz,
389                                                 struct list_head *vc_list)
390 {
391         int i;
392         Elf64_Ehdr *ehdr_ptr;
393         Elf64_Phdr *phdr_ptr;
394         loff_t vmcore_off;
395         struct vmcore *new;
396
397         ehdr_ptr = (Elf64_Ehdr *)elfptr;
398         phdr_ptr = (Elf64_Phdr*)(elfptr + sizeof(Elf64_Ehdr)); /* PT_NOTE hdr */
399
400         /* First program header is PT_NOTE header. */
401         vmcore_off = sizeof(Elf64_Ehdr) +
402                         (ehdr_ptr->e_phnum) * sizeof(Elf64_Phdr) +
403                         phdr_ptr->p_memsz; /* Note sections */
404
405         for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) {
406                 if (phdr_ptr->p_type != PT_LOAD)
407                         continue;
408
409                 /* Add this contiguous chunk of memory to vmcore list.*/
410                 new = get_new_element();
411                 if (!new)
412                         return -ENOMEM;
413                 new->paddr = phdr_ptr->p_offset;
414                 new->size = phdr_ptr->p_memsz;
415                 list_add_tail(&new->list, vc_list);
416
417                 /* Update the program header offset. */
418                 phdr_ptr->p_offset = vmcore_off;
419                 vmcore_off = vmcore_off + phdr_ptr->p_memsz;
420         }
421         return 0;
422 }
423
424 static int __init process_ptload_program_headers_elf32(char *elfptr,
425                                                 size_t elfsz,
426                                                 struct list_head *vc_list)
427 {
428         int i;
429         Elf32_Ehdr *ehdr_ptr;
430         Elf32_Phdr *phdr_ptr;
431         loff_t vmcore_off;
432         struct vmcore *new;
433
434         ehdr_ptr = (Elf32_Ehdr *)elfptr;
435         phdr_ptr = (Elf32_Phdr*)(elfptr + sizeof(Elf32_Ehdr)); /* PT_NOTE hdr */
436
437         /* First program header is PT_NOTE header. */
438         vmcore_off = sizeof(Elf32_Ehdr) +
439                         (ehdr_ptr->e_phnum) * sizeof(Elf32_Phdr) +
440                         phdr_ptr->p_memsz; /* Note sections */
441
442         for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) {
443                 if (phdr_ptr->p_type != PT_LOAD)
444                         continue;
445
446                 /* Add this contiguous chunk of memory to vmcore list.*/
447                 new = get_new_element();
448                 if (!new)
449                         return -ENOMEM;
450                 new->paddr = phdr_ptr->p_offset;
451                 new->size = phdr_ptr->p_memsz;
452                 list_add_tail(&new->list, vc_list);
453
454                 /* Update the program header offset */
455                 phdr_ptr->p_offset = vmcore_off;
456                 vmcore_off = vmcore_off + phdr_ptr->p_memsz;
457         }
458         return 0;
459 }
460
461 /* Sets offset fields of vmcore elements. */
462 static void __init set_vmcore_list_offsets_elf64(char *elfptr,
463                                                 struct list_head *vc_list)
464 {
465         loff_t vmcore_off;
466         Elf64_Ehdr *ehdr_ptr;
467         struct vmcore *m;
468
469         ehdr_ptr = (Elf64_Ehdr *)elfptr;
470
471         /* Skip Elf header and program headers. */
472         vmcore_off = sizeof(Elf64_Ehdr) +
473                         (ehdr_ptr->e_phnum) * sizeof(Elf64_Phdr);
474
475         list_for_each_entry(m, vc_list, list) {
476                 m->offset = vmcore_off;
477                 vmcore_off += m->size;
478         }
479 }
480
481 /* Sets offset fields of vmcore elements. */
482 static void __init set_vmcore_list_offsets_elf32(char *elfptr,
483                                                 struct list_head *vc_list)
484 {
485         loff_t vmcore_off;
486         Elf32_Ehdr *ehdr_ptr;
487         struct vmcore *m;
488
489         ehdr_ptr = (Elf32_Ehdr *)elfptr;
490
491         /* Skip Elf header and program headers. */
492         vmcore_off = sizeof(Elf32_Ehdr) +
493                         (ehdr_ptr->e_phnum) * sizeof(Elf32_Phdr);
494
495         list_for_each_entry(m, vc_list, list) {
496                 m->offset = vmcore_off;
497                 vmcore_off += m->size;
498         }
499 }
500
501 static int __init parse_crash_elf64_headers(void)
502 {
503         int rc=0;
504         Elf64_Ehdr ehdr;
505         u64 addr;
506
507         addr = elfcorehdr_addr;
508
509         /* Read Elf header */
510         rc = read_from_oldmem((char*)&ehdr, sizeof(Elf64_Ehdr), &addr, 0);
511         if (rc < 0)
512                 return rc;
513
514         /* Do some basic Verification. */
515         if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0 ||
516                 (ehdr.e_type != ET_CORE) ||
517                 !elf_check_arch(&ehdr) ||
518                 ehdr.e_ident[EI_CLASS] != ELFCLASS64 ||
519                 ehdr.e_ident[EI_VERSION] != EV_CURRENT ||
520                 ehdr.e_version != EV_CURRENT ||
521                 ehdr.e_ehsize != sizeof(Elf64_Ehdr) ||
522                 ehdr.e_phentsize != sizeof(Elf64_Phdr) ||
523                 ehdr.e_phnum == 0) {
524                 printk(KERN_WARNING "Warning: Core image elf header is not"
525                                         "sane\n");
526                 return -EINVAL;
527         }
528
529         /* Read in all elf headers. */
530         elfcorebuf_sz = sizeof(Elf64_Ehdr) + ehdr.e_phnum * sizeof(Elf64_Phdr);
531         elfcorebuf = kmalloc(elfcorebuf_sz, GFP_KERNEL);
532         if (!elfcorebuf)
533                 return -ENOMEM;
534         addr = elfcorehdr_addr;
535         rc = read_from_oldmem(elfcorebuf, elfcorebuf_sz, &addr, 0);
536         if (rc < 0) {
537                 kfree(elfcorebuf);
538                 return rc;
539         }
540
541         /* Merge all PT_NOTE headers into one. */
542         rc = merge_note_headers_elf64(elfcorebuf, &elfcorebuf_sz, &vmcore_list);
543         if (rc) {
544                 kfree(elfcorebuf);
545                 return rc;
546         }
547         rc = process_ptload_program_headers_elf64(elfcorebuf, elfcorebuf_sz,
548                                                         &vmcore_list);
549         if (rc) {
550                 kfree(elfcorebuf);
551                 return rc;
552         }
553         set_vmcore_list_offsets_elf64(elfcorebuf, &vmcore_list);
554         return 0;
555 }
556
557 static int __init parse_crash_elf32_headers(void)
558 {
559         int rc=0;
560         Elf32_Ehdr ehdr;
561         u64 addr;
562
563         addr = elfcorehdr_addr;
564
565         /* Read Elf header */
566         rc = read_from_oldmem((char*)&ehdr, sizeof(Elf32_Ehdr), &addr, 0);
567         if (rc < 0)
568                 return rc;
569
570         /* Do some basic Verification. */
571         if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0 ||
572                 (ehdr.e_type != ET_CORE) ||
573                 !elf_check_arch(&ehdr) ||
574                 ehdr.e_ident[EI_CLASS] != ELFCLASS32||
575                 ehdr.e_ident[EI_VERSION] != EV_CURRENT ||
576                 ehdr.e_version != EV_CURRENT ||
577                 ehdr.e_ehsize != sizeof(Elf32_Ehdr) ||
578                 ehdr.e_phentsize != sizeof(Elf32_Phdr) ||
579                 ehdr.e_phnum == 0) {
580                 printk(KERN_WARNING "Warning: Core image elf header is not"
581                                         "sane\n");
582                 return -EINVAL;
583         }
584
585         /* Read in all elf headers. */
586         elfcorebuf_sz = sizeof(Elf32_Ehdr) + ehdr.e_phnum * sizeof(Elf32_Phdr);
587         elfcorebuf = kmalloc(elfcorebuf_sz, GFP_KERNEL);
588         if (!elfcorebuf)
589                 return -ENOMEM;
590         addr = elfcorehdr_addr;
591         rc = read_from_oldmem(elfcorebuf, elfcorebuf_sz, &addr, 0);
592         if (rc < 0) {
593                 kfree(elfcorebuf);
594                 return rc;
595         }
596
597         /* Merge all PT_NOTE headers into one. */
598         rc = merge_note_headers_elf32(elfcorebuf, &elfcorebuf_sz, &vmcore_list);
599         if (rc) {
600                 kfree(elfcorebuf);
601                 return rc;
602         }
603         rc = process_ptload_program_headers_elf32(elfcorebuf, elfcorebuf_sz,
604                                                                 &vmcore_list);
605         if (rc) {
606                 kfree(elfcorebuf);
607                 return rc;
608         }
609         set_vmcore_list_offsets_elf32(elfcorebuf, &vmcore_list);
610         return 0;
611 }
612
613 static int __init parse_crash_elf_headers(void)
614 {
615         unsigned char e_ident[EI_NIDENT];
616         u64 addr;
617         int rc=0;
618
619         addr = elfcorehdr_addr;
620         rc = read_from_oldmem(e_ident, EI_NIDENT, &addr, 0);
621         if (rc < 0)
622                 return rc;
623         if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) {
624                 printk(KERN_WARNING "Warning: Core image elf header"
625                                         " not found\n");
626                 return -EINVAL;
627         }
628
629         if (e_ident[EI_CLASS] == ELFCLASS64) {
630                 rc = parse_crash_elf64_headers();
631                 if (rc)
632                         return rc;
633
634                 /* Determine vmcore size. */
635                 vmcore_size = get_vmcore_size_elf64(elfcorebuf);
636         } else if (e_ident[EI_CLASS] == ELFCLASS32) {
637                 rc = parse_crash_elf32_headers();
638                 if (rc)
639                         return rc;
640
641                 /* Determine vmcore size. */
642                 vmcore_size = get_vmcore_size_elf32(elfcorebuf);
643         } else {
644                 printk(KERN_WARNING "Warning: Core image elf header is not"
645                                         " sane\n");
646                 return -EINVAL;
647         }
648         return 0;
649 }
650
651 /* Init function for vmcore module. */
652 static int __init vmcore_init(void)
653 {
654         int rc = 0;
655
656         /* If elfcorehdr= has been passed in cmdline, then capture the dump.*/
657         if (!(elfcorehdr_addr < ELFCORE_ADDR_MAX))
658                 return rc;
659         rc = parse_crash_elf_headers();
660         if (rc) {
661                 printk(KERN_WARNING "Kdump: vmcore not initialized\n");
662                 return rc;
663         }
664
665         /* Initialize /proc/vmcore size if proc is already up. */
666         if (proc_vmcore)
667                 proc_vmcore->size = vmcore_size;
668         return 0;
669 }
670 module_init(vmcore_init)