diff options
Diffstat (limited to 'arch/powerpc/kernel/fadump.c')
-rw-r--r-- | arch/powerpc/kernel/fadump.c | 233 |
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; | |||
43 | static const struct fadump_mem_struct *fdm_active; | 44 | static const struct fadump_mem_struct *fdm_active; |
44 | 45 | ||
45 | static DEFINE_MUTEX(fadump_mutex); | 46 | static DEFINE_MUTEX(fadump_mutex); |
47 | struct fad_crash_memory_ranges crash_memory_ranges[INIT_CRASHMEM_RANGES]; | ||
48 | int crash_mem_ranges; | ||
46 | 49 | ||
47 | /* Scan the Firmware Assisted dump configuration details. */ | 50 | /* Scan the Firmware Assisted dump configuration details. */ |
48 | int __init early_init_dt_scan_fw_dump(unsigned long node, | 51 | int __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 | */ | ||
400 | static 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 | |||
436 | static 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 | |||
449 | static 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 | |||
470 | static 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 | */ | ||
503 | static 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 | |||
530 | static 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 | |||
577 | static 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 | |||
383 | static void register_fadump(void) | 595 | static 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 | ||