aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel/fadump.c
diff options
context:
space:
mode:
authorMahesh Salgaonkar <mahesh@linux.vnet.ibm.com>2012-02-15 20:14:37 -0500
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2012-02-22 18:50:01 -0500
commit2df173d9e85d9e2c6a8933c63f0c034accff7e0f (patch)
tree126e27079f31747abe34a92eed553b70a406ec90 /arch/powerpc/kernel/fadump.c
parent3ccc00a7e04ff7718c9aebb4b0c982571c798759 (diff)
fadump: Initialize elfcore header and add PT_LOAD program headers.
Build the crash memory range list by traversing through system memory during the first kernel before we register for firmware-assisted dump. After the successful dump registration, initialize the elfcore header and populate PT_LOAD program headers with crash memory ranges. The elfcore header is saved in the scratch area within the reserved memory. The scratch area starts at the end of the memory reserved for saving RMR region contents. The scratch area contains fadump crash info structure that contains magic number for fadump validation and physical address where the eflcore header can be found. This structure will also be used to pass some important crash info data to the second kernel which will help second kernel to populate ELF core header with correct data before it gets exported through /proc/vmcore. Since the firmware preserves the entire partition memory at the time of crash the contents of the scratch area will be preserved till second kernel boot. Since the memory dump exported through /proc/vmcore is in ELF format similar to kdump, it will help us to reuse the kdump infrastructure for dump capture and filtering. Unlike phyp dump, userspace tool does not need to refer any sysfs interface while reading /proc/vmcore. NOTE: The current design implementation does not address a possibility of introducing additional fields (in future) to this structure without affecting compatibility. It's on TODO list to come up with better approach to address this. Reserved dump area start => +-------------------------------------+ | CPU state dump data | +-------------------------------------+ | HPTE region data | +-------------------------------------+ | RMR region data | Scratch area start => +-------------------------------------+ | fadump crash info structure { | | magic nummber | +------|---- elfcorehdr_addr | | | } | +----> +-------------------------------------+ | ELF core header | Reserved dump area end => +-------------------------------------+ Signed-off-by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/kernel/fadump.c')
-rw-r--r--arch/powerpc/kernel/fadump.c233
1 files changed, 232 insertions, 1 deletions
diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c
index eb8f782afade..63857e183de5 100644
--- a/arch/powerpc/kernel/fadump.c
+++ b/arch/powerpc/kernel/fadump.c
@@ -32,6 +32,7 @@
32#include <linux/delay.h> 32#include <linux/delay.h>
33#include <linux/debugfs.h> 33#include <linux/debugfs.h>
34#include <linux/seq_file.h> 34#include <linux/seq_file.h>
35#include <linux/crash_dump.h>
35 36
36#include <asm/page.h> 37#include <asm/page.h>
37#include <asm/prom.h> 38#include <asm/prom.h>
@@ -43,6 +44,8 @@ static struct fadump_mem_struct fdm;
43static const struct fadump_mem_struct *fdm_active; 44static const struct fadump_mem_struct *fdm_active;
44 45
45static DEFINE_MUTEX(fadump_mutex); 46static DEFINE_MUTEX(fadump_mutex);
47struct fad_crash_memory_ranges crash_memory_ranges[INIT_CRASHMEM_RANGES];
48int crash_mem_ranges;
46 49
47/* Scan the Firmware Assisted dump configuration details. */ 50/* Scan the Firmware Assisted dump configuration details. */
48int __init early_init_dt_scan_fw_dump(unsigned long node, 51int __init early_init_dt_scan_fw_dump(unsigned long node,
@@ -235,6 +238,10 @@ static unsigned long get_fadump_area_size(void)
235 size += fw_dump.cpu_state_data_size; 238 size += fw_dump.cpu_state_data_size;
236 size += fw_dump.hpte_region_size; 239 size += fw_dump.hpte_region_size;
237 size += fw_dump.boot_memory_size; 240 size += fw_dump.boot_memory_size;
241 size += sizeof(struct fadump_crash_info_header);
242 size += sizeof(struct elfhdr); /* ELF core header.*/
243 /* Program headers for crash memory regions. */
244 size += sizeof(struct elf_phdr) * (memblock_num_regions(memory) + 2);
238 245
239 size = PAGE_ALIGN(size); 246 size = PAGE_ALIGN(size);
240 return size; 247 return size;
@@ -300,6 +307,12 @@ int __init fadump_reserve_mem(void)
300 "for saving crash dump\n", 307 "for saving crash dump\n",
301 (unsigned long)(size >> 20), 308 (unsigned long)(size >> 20),
302 (unsigned long)(base >> 20)); 309 (unsigned long)(base >> 20));
310
311 fw_dump.fadumphdr_addr =
312 fdm_active->rmr_region.destination_address +
313 fdm_active->rmr_region.source_len;
314 pr_debug("fadumphdr_addr = %p\n",
315 (void *) fw_dump.fadumphdr_addr);
303 } else { 316 } else {
304 /* Reserve the memory at the top of memory. */ 317 /* Reserve the memory at the top of memory. */
305 size = get_fadump_area_size(); 318 size = get_fadump_area_size();
@@ -380,8 +393,210 @@ static void register_fw_dump(struct fadump_mem_struct *fdm)
380 } 393 }
381} 394}
382 395
396/*
397 * Validate and process the dump data stored by firmware before exporting
398 * it through '/proc/vmcore'.
399 */
400static int __init process_fadump(const struct fadump_mem_struct *fdm_active)
401{
402 struct fadump_crash_info_header *fdh;
403
404 if (!fdm_active || !fw_dump.fadumphdr_addr)
405 return -EINVAL;
406
407 /* Check if the dump data is valid. */
408 if ((fdm_active->header.dump_status_flag == FADUMP_ERROR_FLAG) ||
409 (fdm_active->rmr_region.error_flags != 0)) {
410 printk(KERN_ERR "Dump taken by platform is not valid\n");
411 return -EINVAL;
412 }
413 if (fdm_active->rmr_region.bytes_dumped !=
414 fdm_active->rmr_region.source_len) {
415 printk(KERN_ERR "Dump taken by platform is incomplete\n");
416 return -EINVAL;
417 }
418
419 /* Validate the fadump crash info header */
420 fdh = __va(fw_dump.fadumphdr_addr);
421 if (fdh->magic_number != FADUMP_CRASH_INFO_MAGIC) {
422 printk(KERN_ERR "Crash info header is not valid.\n");
423 return -EINVAL;
424 }
425
426 /*
427 * We are done validating dump info and elfcore header is now ready
428 * to be exported. set elfcorehdr_addr so that vmcore module will
429 * export the elfcore header through '/proc/vmcore'.
430 */
431 elfcorehdr_addr = fdh->elfcorehdr_addr;
432
433 return 0;
434}
435
436static inline void fadump_add_crash_memory(unsigned long long base,
437 unsigned long long end)
438{
439 if (base == end)
440 return;
441
442 pr_debug("crash_memory_range[%d] [%#016llx-%#016llx], %#llx bytes\n",
443 crash_mem_ranges, base, end - 1, (end - base));
444 crash_memory_ranges[crash_mem_ranges].base = base;
445 crash_memory_ranges[crash_mem_ranges].size = end - base;
446 crash_mem_ranges++;
447}
448
449static void fadump_exclude_reserved_area(unsigned long long start,
450 unsigned long long end)
451{
452 unsigned long long ra_start, ra_end;
453
454 ra_start = fw_dump.reserve_dump_area_start;
455 ra_end = ra_start + fw_dump.reserve_dump_area_size;
456
457 if ((ra_start < end) && (ra_end > start)) {
458 if ((start < ra_start) && (end > ra_end)) {
459 fadump_add_crash_memory(start, ra_start);
460 fadump_add_crash_memory(ra_end, end);
461 } else if (start < ra_start) {
462 fadump_add_crash_memory(start, ra_start);
463 } else if (ra_end < end) {
464 fadump_add_crash_memory(ra_end, end);
465 }
466 } else
467 fadump_add_crash_memory(start, end);
468}
469
470static int fadump_init_elfcore_header(char *bufp)
471{
472 struct elfhdr *elf;
473
474 elf = (struct elfhdr *) bufp;
475 bufp += sizeof(struct elfhdr);
476 memcpy(elf->e_ident, ELFMAG, SELFMAG);
477 elf->e_ident[EI_CLASS] = ELF_CLASS;
478 elf->e_ident[EI_DATA] = ELF_DATA;
479 elf->e_ident[EI_VERSION] = EV_CURRENT;
480 elf->e_ident[EI_OSABI] = ELF_OSABI;
481 memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
482 elf->e_type = ET_CORE;
483 elf->e_machine = ELF_ARCH;
484 elf->e_version = EV_CURRENT;
485 elf->e_entry = 0;
486 elf->e_phoff = sizeof(struct elfhdr);
487 elf->e_shoff = 0;
488 elf->e_flags = ELF_CORE_EFLAGS;
489 elf->e_ehsize = sizeof(struct elfhdr);
490 elf->e_phentsize = sizeof(struct elf_phdr);
491 elf->e_phnum = 0;
492 elf->e_shentsize = 0;
493 elf->e_shnum = 0;
494 elf->e_shstrndx = 0;
495
496 return 0;
497}
498
499/*
500 * Traverse through memblock structure and setup crash memory ranges. These
501 * ranges will be used create PT_LOAD program headers in elfcore header.
502 */
503static void fadump_setup_crash_memory_ranges(void)
504{
505 struct memblock_region *reg;
506 unsigned long long start, end;
507
508 pr_debug("Setup crash memory ranges.\n");
509 crash_mem_ranges = 0;
510 /*
511 * add the first memory chunk (RMA_START through boot_memory_size) as
512 * a separate memory chunk. The reason is, at the time crash firmware
513 * will move the content of this memory chunk to different location
514 * specified during fadump registration. We need to create a separate
515 * program header for this chunk with the correct offset.
516 */
517 fadump_add_crash_memory(RMA_START, fw_dump.boot_memory_size);
518
519 for_each_memblock(memory, reg) {
520 start = (unsigned long long)reg->base;
521 end = start + (unsigned long long)reg->size;
522 if (start == RMA_START && end >= fw_dump.boot_memory_size)
523 start = fw_dump.boot_memory_size;
524
525 /* add this range excluding the reserved dump area. */
526 fadump_exclude_reserved_area(start, end);
527 }
528}
529
530static int fadump_create_elfcore_headers(char *bufp)
531{
532 struct elfhdr *elf;
533 struct elf_phdr *phdr;
534 int i;
535
536 fadump_init_elfcore_header(bufp);
537 elf = (struct elfhdr *)bufp;
538 bufp += sizeof(struct elfhdr);
539
540 /* setup PT_LOAD sections. */
541
542 for (i = 0; i < crash_mem_ranges; i++) {
543 unsigned long long mbase, msize;
544 mbase = crash_memory_ranges[i].base;
545 msize = crash_memory_ranges[i].size;
546
547 if (!msize)
548 continue;
549
550 phdr = (struct elf_phdr *)bufp;
551 bufp += sizeof(struct elf_phdr);
552 phdr->p_type = PT_LOAD;
553 phdr->p_flags = PF_R|PF_W|PF_X;
554 phdr->p_offset = mbase;
555
556 if (mbase == RMA_START) {
557 /*
558 * The entire RMA region will be moved by firmware
559 * to the specified destination_address. Hence set
560 * the correct offset.
561 */
562 phdr->p_offset = fdm.rmr_region.destination_address;
563 }
564
565 phdr->p_paddr = mbase;
566 phdr->p_vaddr = (unsigned long)__va(mbase);
567 phdr->p_filesz = msize;
568 phdr->p_memsz = msize;
569 phdr->p_align = 0;
570
571 /* Increment number of program headers. */
572 (elf->e_phnum)++;
573 }
574 return 0;
575}
576
577static unsigned long init_fadump_header(unsigned long addr)
578{
579 struct fadump_crash_info_header *fdh;
580
581 if (!addr)
582 return 0;
583
584 fw_dump.fadumphdr_addr = addr;
585 fdh = __va(addr);
586 addr += sizeof(struct fadump_crash_info_header);
587
588 memset(fdh, 0, sizeof(struct fadump_crash_info_header));
589 fdh->magic_number = FADUMP_CRASH_INFO_MAGIC;
590 fdh->elfcorehdr_addr = addr;
591
592 return addr;
593}
594
383static void register_fadump(void) 595static void register_fadump(void)
384{ 596{
597 unsigned long addr;
598 void *vaddr;
599
385 /* 600 /*
386 * If no memory is reserved then we can not register for firmware- 601 * If no memory is reserved then we can not register for firmware-
387 * assisted dump. 602 * assisted dump.
@@ -389,6 +604,16 @@ static void register_fadump(void)
389 if (!fw_dump.reserve_dump_area_size) 604 if (!fw_dump.reserve_dump_area_size)
390 return; 605 return;
391 606
607 fadump_setup_crash_memory_ranges();
608
609 addr = fdm.rmr_region.destination_address + fdm.rmr_region.source_len;
610 /* Initialize fadump crash info header. */
611 addr = init_fadump_header(addr);
612 vaddr = __va(addr);
613
614 pr_debug("Creating ELF core headers at %#016lx\n", addr);
615 fadump_create_elfcore_headers(vaddr);
616
392 /* register the future kernel dump with firmware. */ 617 /* register the future kernel dump with firmware. */
393 register_fw_dump(&fdm); 618 register_fw_dump(&fdm);
394} 619}
@@ -585,8 +810,14 @@ int __init setup_fadump(void)
585 } 810 }
586 811
587 fadump_show_config(); 812 fadump_show_config();
813 /*
814 * If dump data is available then see if it is valid and prepare for
815 * saving it to the disk.
816 */
817 if (fw_dump.dump_active)
818 process_fadump(fdm_active);
588 /* Initialize the kernel dump memory structure for FAD registration. */ 819 /* Initialize the kernel dump memory structure for FAD registration. */
589 if (fw_dump.reserve_dump_area_size) 820 else if (fw_dump.reserve_dump_area_size)
590 init_fadump_mem_struct(&fdm, fw_dump.reserve_dump_area_start); 821 init_fadump_mem_struct(&fdm, fw_dump.reserve_dump_area_start);
591 fadump_init_files(); 822 fadump_init_files();
592 823