Root/
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 | /* 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) |
41 | { |
42 | unsigned long pfn, offset; |
43 | size_t nr_bytes; |
44 | ssize_t read = 0, tmp; |
45 | |
46 | if (!count) |
47 | return 0; |
48 | |
49 | offset = (unsigned long)(*ppos % PAGE_SIZE); |
50 | pfn = (unsigned long)(*ppos / PAGE_SIZE); |
51 | |
52 | do { |
53 | if (count > (PAGE_SIZE - offset)) |
54 | nr_bytes = PAGE_SIZE - offset; |
55 | else |
56 | nr_bytes = count; |
57 | |
58 | tmp = copy_oldmem_page(pfn, buf, nr_bytes, offset, userbuf); |
59 | if (tmp < 0) |
60 | return tmp; |
61 | *ppos += nr_bytes; |
62 | count -= nr_bytes; |
63 | buf += nr_bytes; |
64 | read += nr_bytes; |
65 | ++pfn; |
66 | offset = 0; |
67 | } while (count); |
68 | |
69 | return read; |
70 | } |
71 | |
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) |
75 | { |
76 | struct vmcore *m; |
77 | u64 paddr; |
78 | |
79 | list_for_each_entry(m, vc_list, list) { |
80 | u64 start, end; |
81 | start = m->offset; |
82 | end = m->offset + m->size - 1; |
83 | if (offset >= start && offset <= end) { |
84 | paddr = m->paddr + offset - start; |
85 | *m_ptr = m; |
86 | return paddr; |
87 | } |
88 | } |
89 | *m_ptr = NULL; |
90 | return 0; |
91 | } |
92 | |
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. |
95 | */ |
96 | static ssize_t read_vmcore(struct file *file, char __user *buffer, |
97 | size_t buflen, loff_t *fpos) |
98 | { |
99 | ssize_t acc = 0, tmp; |
100 | size_t tsz; |
101 | u64 start, nr_bytes; |
102 | struct vmcore *curr_m = NULL; |
103 | |
104 | if (buflen == 0 || *fpos >= vmcore_size) |
105 | return 0; |
106 | |
107 | /* trim buflen to not go beyond EOF */ |
108 | if (buflen > vmcore_size - *fpos) |
109 | buflen = vmcore_size - *fpos; |
110 | |
111 | /* Read ELF core header */ |
112 | if (*fpos < elfcorebuf_sz) { |
113 | tsz = elfcorebuf_sz - *fpos; |
114 | if (buflen < tsz) |
115 | tsz = buflen; |
116 | if (copy_to_user(buffer, elfcorebuf + *fpos, tsz)) |
117 | return -EFAULT; |
118 | buflen -= tsz; |
119 | *fpos += tsz; |
120 | buffer += tsz; |
121 | acc += tsz; |
122 | |
123 | /* leave now if filled buffer already */ |
124 | if (buflen == 0) |
125 | return acc; |
126 | } |
127 | |
128 | start = map_offset_to_paddr(*fpos, &vmcore_list, &curr_m); |
129 | if (!curr_m) |
130 | return -EINVAL; |
131 | if ((tsz = (PAGE_SIZE - (start & ~PAGE_MASK))) > buflen) |
132 | tsz = buflen; |
133 | |
134 | /* Calculate left bytes in current memory segment. */ |
135 | nr_bytes = (curr_m->size - (start - curr_m->paddr)); |
136 | if (tsz > nr_bytes) |
137 | tsz = nr_bytes; |
138 | |
139 | while (buflen) { |
140 | tmp = read_from_oldmem(buffer, tsz, &start, 1); |
141 | if (tmp < 0) |
142 | return tmp; |
143 | buflen -= tsz; |
144 | *fpos += tsz; |
145 | buffer += tsz; |
146 | acc += tsz; |
147 | if (start >= (curr_m->paddr + curr_m->size)) { |
148 | if (curr_m->list.next == &vmcore_list) |
149 | return acc; /*EOF*/ |
150 | curr_m = list_entry(curr_m->list.next, |
151 | struct vmcore, list); |
152 | start = curr_m->paddr; |
153 | } |
154 | if ((tsz = (PAGE_SIZE - (start & ~PAGE_MASK))) > buflen) |
155 | tsz = buflen; |
156 | /* Calculate left bytes in current memory segment. */ |
157 | nr_bytes = (curr_m->size - (start - curr_m->paddr)); |
158 | if (tsz > nr_bytes) |
159 | tsz = nr_bytes; |
160 | } |
161 | return acc; |
162 | } |
163 | |
164 | static const struct file_operations proc_vmcore_operations = { |
165 | .read = read_vmcore, |
166 | }; |
167 | |
168 | static struct vmcore* __init get_new_element(void) |
169 | { |
170 | return kzalloc(sizeof(struct vmcore), GFP_KERNEL); |
171 | } |
172 | |
173 | static u64 __init get_vmcore_size_elf64(char *elfptr) |
174 | { |
175 | int i; |
176 | u64 size; |
177 | Elf64_Ehdr *ehdr_ptr; |
178 | Elf64_Phdr *phdr_ptr; |
179 | |
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; |
185 | phdr_ptr++; |
186 | } |
187 | return size; |
188 | } |
189 | |
190 | static u64 __init get_vmcore_size_elf32(char *elfptr) |
191 | { |
192 | int i; |
193 | u64 size; |
194 | Elf32_Ehdr *ehdr_ptr; |
195 | Elf32_Phdr *phdr_ptr; |
196 | |
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; |
202 | phdr_ptr++; |
203 | } |
204 | return size; |
205 | } |
206 | |
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) |
210 | { |
211 | int i, nr_ptnote=0, rc=0; |
212 | char *tmp; |
213 | Elf64_Ehdr *ehdr_ptr; |
214 | Elf64_Phdr phdr, *phdr_ptr; |
215 | Elf64_Nhdr *nhdr_ptr; |
216 | u64 phdr_sz = 0, note_off; |
217 | |
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++) { |
221 | int j; |
222 | void *notes_section; |
223 | struct vmcore *new; |
224 | u64 offset, max_sz, sz, real_sz = 0; |
225 | if (phdr_ptr->p_type != PT_NOTE) |
226 | continue; |
227 | nr_ptnote++; |
228 | max_sz = phdr_ptr->p_memsz; |
229 | offset = phdr_ptr->p_offset; |
230 | notes_section = kmalloc(max_sz, GFP_KERNEL); |
231 | if (!notes_section) |
232 | return -ENOMEM; |
233 | rc = read_from_oldmem(notes_section, max_sz, &offset, 0); |
234 | if (rc < 0) { |
235 | kfree(notes_section); |
236 | return rc; |
237 | } |
238 | nhdr_ptr = notes_section; |
239 | for (j = 0; j < max_sz; j += sz) { |
240 | if (nhdr_ptr->n_namesz == 0) |
241 | break; |
242 | sz = sizeof(Elf64_Nhdr) + |
243 | ((nhdr_ptr->n_namesz + 3) & ~3) + |
244 | ((nhdr_ptr->n_descsz + 3) & ~3); |
245 | real_sz += sz; |
246 | nhdr_ptr = (Elf64_Nhdr*)((char*)nhdr_ptr + sz); |
247 | } |
248 | |
249 | /* Add this contiguous chunk of notes section to vmcore list.*/ |
250 | new = get_new_element(); |
251 | if (!new) { |
252 | kfree(notes_section); |
253 | return -ENOMEM; |
254 | } |
255 | new->paddr = phdr_ptr->p_offset; |
256 | new->size = real_sz; |
257 | list_add_tail(&new->list, vc_list); |
258 | phdr_sz += real_sz; |
259 | kfree(notes_section); |
260 | } |
261 | |
262 | /* Prepare merged PT_NOTE program header. */ |
263 | phdr.p_type = PT_NOTE; |
264 | phdr.p_flags = 0; |
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; |
270 | phdr.p_align = 0; |
271 | |
272 | /* Add merged PT_NOTE program header*/ |
273 | tmp = elfptr + sizeof(Elf64_Ehdr); |
274 | memcpy(tmp, &phdr, sizeof(phdr)); |
275 | tmp += sizeof(phdr); |
276 | |
277 | /* Remove unwanted PT_NOTE program headers. */ |
278 | i = (nr_ptnote - 1) * sizeof(Elf64_Phdr); |
279 | *elfsz = *elfsz - i; |
280 | memmove(tmp, tmp+i, ((*elfsz)-sizeof(Elf64_Ehdr)-sizeof(Elf64_Phdr))); |
281 | |
282 | /* Modify e_phnum to reflect merged headers. */ |
283 | ehdr_ptr->e_phnum = ehdr_ptr->e_phnum - nr_ptnote + 1; |
284 | |
285 | return 0; |
286 | } |
287 | |
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) |
291 | { |
292 | int i, nr_ptnote=0, rc=0; |
293 | char *tmp; |
294 | Elf32_Ehdr *ehdr_ptr; |
295 | Elf32_Phdr phdr, *phdr_ptr; |
296 | Elf32_Nhdr *nhdr_ptr; |
297 | u64 phdr_sz = 0, note_off; |
298 | |
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++) { |
302 | int j; |
303 | void *notes_section; |
304 | struct vmcore *new; |
305 | u64 offset, max_sz, sz, real_sz = 0; |
306 | if (phdr_ptr->p_type != PT_NOTE) |
307 | continue; |
308 | nr_ptnote++; |
309 | max_sz = phdr_ptr->p_memsz; |
310 | offset = phdr_ptr->p_offset; |
311 | notes_section = kmalloc(max_sz, GFP_KERNEL); |
312 | if (!notes_section) |
313 | return -ENOMEM; |
314 | rc = read_from_oldmem(notes_section, max_sz, &offset, 0); |
315 | if (rc < 0) { |
316 | kfree(notes_section); |
317 | return rc; |
318 | } |
319 | nhdr_ptr = notes_section; |
320 | for (j = 0; j < max_sz; j += sz) { |
321 | if (nhdr_ptr->n_namesz == 0) |
322 | break; |
323 | sz = sizeof(Elf32_Nhdr) + |
324 | ((nhdr_ptr->n_namesz + 3) & ~3) + |
325 | ((nhdr_ptr->n_descsz + 3) & ~3); |
326 | real_sz += sz; |
327 | nhdr_ptr = (Elf32_Nhdr*)((char*)nhdr_ptr + sz); |
328 | } |
329 | |
330 | /* Add this contiguous chunk of notes section to vmcore list.*/ |
331 | new = get_new_element(); |
332 | if (!new) { |
333 | kfree(notes_section); |
334 | return -ENOMEM; |
335 | } |
336 | new->paddr = phdr_ptr->p_offset; |
337 | new->size = real_sz; |
338 | list_add_tail(&new->list, vc_list); |
339 | phdr_sz += real_sz; |
340 | kfree(notes_section); |
341 | } |
342 | |
343 | /* Prepare merged PT_NOTE program header. */ |
344 | phdr.p_type = PT_NOTE; |
345 | phdr.p_flags = 0; |
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; |
351 | phdr.p_align = 0; |
352 | |
353 | /* Add merged PT_NOTE program header*/ |
354 | tmp = elfptr + sizeof(Elf32_Ehdr); |
355 | memcpy(tmp, &phdr, sizeof(phdr)); |
356 | tmp += sizeof(phdr); |
357 | |
358 | /* Remove unwanted PT_NOTE program headers. */ |
359 | i = (nr_ptnote - 1) * sizeof(Elf32_Phdr); |
360 | *elfsz = *elfsz - i; |
361 | memmove(tmp, tmp+i, ((*elfsz)-sizeof(Elf32_Ehdr)-sizeof(Elf32_Phdr))); |
362 | |
363 | /* Modify e_phnum to reflect merged headers. */ |
364 | ehdr_ptr->e_phnum = ehdr_ptr->e_phnum - nr_ptnote + 1; |
365 | |
366 | return 0; |
367 | } |
368 | |
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, |
372 | size_t elfsz, |
373 | struct list_head *vc_list) |
374 | { |
375 | int i; |
376 | Elf64_Ehdr *ehdr_ptr; |
377 | Elf64_Phdr *phdr_ptr; |
378 | loff_t vmcore_off; |
379 | struct vmcore *new; |
380 | |
381 | ehdr_ptr = (Elf64_Ehdr *)elfptr; |
382 | phdr_ptr = (Elf64_Phdr*)(elfptr + sizeof(Elf64_Ehdr)); /* PT_NOTE hdr */ |
383 | |
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 */ |
388 | |
389 | for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) { |
390 | if (phdr_ptr->p_type != PT_LOAD) |
391 | continue; |
392 | |
393 | /* Add this contiguous chunk of memory to vmcore list.*/ |
394 | new = get_new_element(); |
395 | if (!new) |
396 | return -ENOMEM; |
397 | new->paddr = phdr_ptr->p_offset; |
398 | new->size = phdr_ptr->p_memsz; |
399 | list_add_tail(&new->list, vc_list); |
400 | |
401 | /* Update the program header offset. */ |
402 | phdr_ptr->p_offset = vmcore_off; |
403 | vmcore_off = vmcore_off + phdr_ptr->p_memsz; |
404 | } |
405 | return 0; |
406 | } |
407 | |
408 | static int __init process_ptload_program_headers_elf32(char *elfptr, |
409 | size_t elfsz, |
410 | struct list_head *vc_list) |
411 | { |
412 | int i; |
413 | Elf32_Ehdr *ehdr_ptr; |
414 | Elf32_Phdr *phdr_ptr; |
415 | loff_t vmcore_off; |
416 | struct vmcore *new; |
417 | |
418 | ehdr_ptr = (Elf32_Ehdr *)elfptr; |
419 | phdr_ptr = (Elf32_Phdr*)(elfptr + sizeof(Elf32_Ehdr)); /* PT_NOTE hdr */ |
420 | |
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 */ |
425 | |
426 | for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) { |
427 | if (phdr_ptr->p_type != PT_LOAD) |
428 | continue; |
429 | |
430 | /* Add this contiguous chunk of memory to vmcore list.*/ |
431 | new = get_new_element(); |
432 | if (!new) |
433 | return -ENOMEM; |
434 | new->paddr = phdr_ptr->p_offset; |
435 | new->size = phdr_ptr->p_memsz; |
436 | list_add_tail(&new->list, vc_list); |
437 | |
438 | /* Update the program header offset */ |
439 | phdr_ptr->p_offset = vmcore_off; |
440 | vmcore_off = vmcore_off + phdr_ptr->p_memsz; |
441 | } |
442 | return 0; |
443 | } |
444 | |
445 | /* Sets offset fields of vmcore elements. */ |
446 | static void __init set_vmcore_list_offsets_elf64(char *elfptr, |
447 | struct list_head *vc_list) |
448 | { |
449 | loff_t vmcore_off; |
450 | Elf64_Ehdr *ehdr_ptr; |
451 | struct vmcore *m; |
452 | |
453 | ehdr_ptr = (Elf64_Ehdr *)elfptr; |
454 | |
455 | /* Skip Elf header and program headers. */ |
456 | vmcore_off = sizeof(Elf64_Ehdr) + |
457 | (ehdr_ptr->e_phnum) * sizeof(Elf64_Phdr); |
458 | |
459 | list_for_each_entry(m, vc_list, list) { |
460 | m->offset = vmcore_off; |
461 | vmcore_off += m->size; |
462 | } |
463 | } |
464 | |
465 | /* Sets offset fields of vmcore elements. */ |
466 | static void __init set_vmcore_list_offsets_elf32(char *elfptr, |
467 | struct list_head *vc_list) |
468 | { |
469 | loff_t vmcore_off; |
470 | Elf32_Ehdr *ehdr_ptr; |
471 | struct vmcore *m; |
472 | |
473 | ehdr_ptr = (Elf32_Ehdr *)elfptr; |
474 | |
475 | /* Skip Elf header and program headers. */ |
476 | vmcore_off = sizeof(Elf32_Ehdr) + |
477 | (ehdr_ptr->e_phnum) * sizeof(Elf32_Phdr); |
478 | |
479 | list_for_each_entry(m, vc_list, list) { |
480 | m->offset = vmcore_off; |
481 | vmcore_off += m->size; |
482 | } |
483 | } |
484 | |
485 | static int __init parse_crash_elf64_headers(void) |
486 | { |
487 | int rc=0; |
488 | Elf64_Ehdr ehdr; |
489 | u64 addr; |
490 | |
491 | addr = elfcorehdr_addr; |
492 | |
493 | /* Read Elf header */ |
494 | rc = read_from_oldmem((char*)&ehdr, sizeof(Elf64_Ehdr), &addr, 0); |
495 | if (rc < 0) |
496 | return rc; |
497 | |
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) || |
507 | ehdr.e_phnum == 0) { |
508 | printk(KERN_WARNING "Warning: Core image elf header is not" |
509 | "sane\n"); |
510 | return -EINVAL; |
511 | } |
512 | |
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); |
516 | if (!elfcorebuf) |
517 | return -ENOMEM; |
518 | addr = elfcorehdr_addr; |
519 | rc = read_from_oldmem(elfcorebuf, elfcorebuf_sz, &addr, 0); |
520 | if (rc < 0) { |
521 | kfree(elfcorebuf); |
522 | return rc; |
523 | } |
524 | |
525 | /* Merge all PT_NOTE headers into one. */ |
526 | rc = merge_note_headers_elf64(elfcorebuf, &elfcorebuf_sz, &vmcore_list); |
527 | if (rc) { |
528 | kfree(elfcorebuf); |
529 | return rc; |
530 | } |
531 | rc = process_ptload_program_headers_elf64(elfcorebuf, elfcorebuf_sz, |
532 | &vmcore_list); |
533 | if (rc) { |
534 | kfree(elfcorebuf); |
535 | return rc; |
536 | } |
537 | set_vmcore_list_offsets_elf64(elfcorebuf, &vmcore_list); |
538 | return 0; |
539 | } |
540 | |
541 | static int __init parse_crash_elf32_headers(void) |
542 | { |
543 | int rc=0; |
544 | Elf32_Ehdr ehdr; |
545 | u64 addr; |
546 | |
547 | addr = elfcorehdr_addr; |
548 | |
549 | /* Read Elf header */ |
550 | rc = read_from_oldmem((char*)&ehdr, sizeof(Elf32_Ehdr), &addr, 0); |
551 | if (rc < 0) |
552 | return rc; |
553 | |
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) || |
563 | ehdr.e_phnum == 0) { |
564 | printk(KERN_WARNING "Warning: Core image elf header is not" |
565 | "sane\n"); |
566 | return -EINVAL; |
567 | } |
568 | |
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); |
572 | if (!elfcorebuf) |
573 | return -ENOMEM; |
574 | addr = elfcorehdr_addr; |
575 | rc = read_from_oldmem(elfcorebuf, elfcorebuf_sz, &addr, 0); |
576 | if (rc < 0) { |
577 | kfree(elfcorebuf); |
578 | return rc; |
579 | } |
580 | |
581 | /* Merge all PT_NOTE headers into one. */ |
582 | rc = merge_note_headers_elf32(elfcorebuf, &elfcorebuf_sz, &vmcore_list); |
583 | if (rc) { |
584 | kfree(elfcorebuf); |
585 | return rc; |
586 | } |
587 | rc = process_ptload_program_headers_elf32(elfcorebuf, elfcorebuf_sz, |
588 | &vmcore_list); |
589 | if (rc) { |
590 | kfree(elfcorebuf); |
591 | return rc; |
592 | } |
593 | set_vmcore_list_offsets_elf32(elfcorebuf, &vmcore_list); |
594 | return 0; |
595 | } |
596 | |
597 | static int __init parse_crash_elf_headers(void) |
598 | { |
599 | unsigned char e_ident[EI_NIDENT]; |
600 | u64 addr; |
601 | int rc=0; |
602 | |
603 | addr = elfcorehdr_addr; |
604 | rc = read_from_oldmem(e_ident, EI_NIDENT, &addr, 0); |
605 | if (rc < 0) |
606 | return rc; |
607 | if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) { |
608 | printk(KERN_WARNING "Warning: Core image elf header" |
609 | " not found\n"); |
610 | return -EINVAL; |
611 | } |
612 | |
613 | if (e_ident[EI_CLASS] == ELFCLASS64) { |
614 | rc = parse_crash_elf64_headers(); |
615 | if (rc) |
616 | return rc; |
617 | |
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(); |
622 | if (rc) |
623 | return rc; |
624 | |
625 | /* Determine vmcore size. */ |
626 | vmcore_size = get_vmcore_size_elf32(elfcorebuf); |
627 | } else { |
628 | printk(KERN_WARNING "Warning: Core image elf header is not" |
629 | " sane\n"); |
630 | return -EINVAL; |
631 | } |
632 | return 0; |
633 | } |
634 | |
635 | /* Init function for vmcore module. */ |
636 | static int __init vmcore_init(void) |
637 | { |
638 | int rc = 0; |
639 | |
640 | /* If elfcorehdr= has been passed in cmdline, then capture the dump.*/ |
641 | if (!(is_vmcore_usable())) |
642 | return rc; |
643 | rc = parse_crash_elf_headers(); |
644 | if (rc) { |
645 | printk(KERN_WARNING "Kdump: vmcore not initialized\n"); |
646 | return rc; |
647 | } |
648 | |
649 | proc_vmcore = proc_create("vmcore", S_IRUSR, NULL, &proc_vmcore_operations); |
650 | if (proc_vmcore) |
651 | proc_vmcore->size = vmcore_size; |
652 | return 0; |
653 | } |
654 | module_init(vmcore_init) |
655 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9