Skip to content

Commit 2df173d

Browse files
maheshsalozbenh
authored andcommitted
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 <[email protected]> Signed-off-by: Benjamin Herrenschmidt <[email protected]>
1 parent 3ccc00a commit 2df173d

File tree

2 files changed

+275
-1
lines changed

2 files changed

+275
-1
lines changed

arch/powerpc/include/asm/fadump.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@
4343
#define MIN_BOOT_MEM (((RMA_END < (0x1UL << 28)) ? (0x1UL << 28) : RMA_END) \
4444
+ (0x1UL << 26))
4545

46+
#define memblock_num_regions(memblock_type) (memblock.memblock_type.cnt)
47+
48+
#ifndef ELF_CORE_EFLAGS
49+
#define ELF_CORE_EFLAGS 0
50+
#endif
51+
4652
/* Firmware provided dump sections */
4753
#define FADUMP_CPU_STATE_DATA 0x0001
4854
#define FADUMP_HPTE_REGION 0x0002
@@ -56,6 +62,9 @@
5662
#define FADUMP_UNREGISTER 2
5763
#define FADUMP_INVALIDATE 3
5864

65+
/* Dump status flag */
66+
#define FADUMP_ERROR_FLAG 0x2000
67+
5968
/* Kernel Dump section info */
6069
struct fadump_section {
6170
u32 request_flag;
@@ -109,6 +118,7 @@ struct fw_dump {
109118
/* cmd line option during boot */
110119
unsigned long reserve_bootvar;
111120

121+
unsigned long fadumphdr_addr;
112122
int ibm_configure_kernel_dump;
113123

114124
unsigned long fadump_enabled:1;
@@ -117,6 +127,39 @@ struct fw_dump {
117127
unsigned long dump_registered:1;
118128
};
119129

130+
/*
131+
* Copy the ascii values for first 8 characters from a string into u64
132+
* variable at their respective indexes.
133+
* e.g.
134+
* The string "FADMPINF" will be converted into 0x4641444d50494e46
135+
*/
136+
static inline u64 str_to_u64(const char *str)
137+
{
138+
u64 val = 0;
139+
int i;
140+
141+
for (i = 0; i < sizeof(val); i++)
142+
val = (*str) ? (val << 8) | *str++ : val << 8;
143+
return val;
144+
}
145+
#define STR_TO_HEX(x) str_to_u64(x)
146+
147+
#define FADUMP_CRASH_INFO_MAGIC STR_TO_HEX("FADMPINF")
148+
149+
/* fadump crash info structure */
150+
struct fadump_crash_info_header {
151+
u64 magic_number;
152+
u64 elfcorehdr_addr;
153+
};
154+
155+
/* Crash memory ranges */
156+
#define INIT_CRASHMEM_RANGES (INIT_MEMBLOCK_REGIONS + 2)
157+
158+
struct fad_crash_memory_ranges {
159+
unsigned long long base;
160+
unsigned long long size;
161+
};
162+
120163
extern int early_init_dt_scan_fw_dump(unsigned long node,
121164
const char *uname, int depth, void *data);
122165
extern int fadump_reserve_mem(void);

arch/powerpc/kernel/fadump.c

Lines changed: 232 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <linux/delay.h>
3333
#include <linux/debugfs.h>
3434
#include <linux/seq_file.h>
35+
#include <linux/crash_dump.h>
3536

3637
#include <asm/page.h>
3738
#include <asm/prom.h>
@@ -43,6 +44,8 @@ static struct fadump_mem_struct fdm;
4344
static const struct fadump_mem_struct *fdm_active;
4445

4546
static DEFINE_MUTEX(fadump_mutex);
47+
struct fad_crash_memory_ranges crash_memory_ranges[INIT_CRASHMEM_RANGES];
48+
int crash_mem_ranges;
4649

4750
/* Scan the Firmware Assisted dump configuration details. */
4851
int __init early_init_dt_scan_fw_dump(unsigned long node,
@@ -235,6 +238,10 @@ static unsigned long get_fadump_area_size(void)
235238
size += fw_dump.cpu_state_data_size;
236239
size += fw_dump.hpte_region_size;
237240
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);
238245

239246
size = PAGE_ALIGN(size);
240247
return size;
@@ -300,6 +307,12 @@ int __init fadump_reserve_mem(void)
300307
"for saving crash dump\n",
301308
(unsigned long)(size >> 20),
302309
(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);
303316
} else {
304317
/* Reserve the memory at the top of memory. */
305318
size = get_fadump_area_size();
@@ -380,15 +393,227 @@ static void register_fw_dump(struct fadump_mem_struct *fdm)
380393
}
381394
}
382395

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+
383595
static void register_fadump(void)
384596
{
597+
unsigned long addr;
598+
void *vaddr;
599+
385600
/*
386601
* If no memory is reserved then we can not register for firmware-
387602
* assisted dump.
388603
*/
389604
if (!fw_dump.reserve_dump_area_size)
390605
return;
391606

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+
392617
/* register the future kernel dump with firmware. */
393618
register_fw_dump(&fdm);
394619
}
@@ -585,8 +810,14 @@ int __init setup_fadump(void)
585810
}
586811

587812
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);
588819
/* 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)
590821
init_fadump_mem_struct(&fdm, fw_dump.reserve_dump_area_start);
591822
fadump_init_files();
592823

0 commit comments

Comments
 (0)