aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel/fadump.c
diff options
context:
space:
mode:
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