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