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