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