Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 292468
b: refs/heads/master
c: 2df173d
h: refs/heads/master
v: v3
  • Loading branch information
Mahesh Salgaonkar authored and Benjamin Herrenschmidt committed Feb 22, 2012
1 parent 70904fd commit f608e9d
Show file tree
Hide file tree
Showing 3 changed files with 276 additions and 2 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 3ccc00a7e04ff7718c9aebb4b0c982571c798759
refs/heads/master: 2df173d9e85d9e2c6a8933c63f0c034accff7e0f
43 changes: 43 additions & 0 deletions trunk/arch/powerpc/include/asm/fadump.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@
#define MIN_BOOT_MEM (((RMA_END < (0x1UL << 28)) ? (0x1UL << 28) : RMA_END) \
+ (0x1UL << 26))

#define memblock_num_regions(memblock_type) (memblock.memblock_type.cnt)

#ifndef ELF_CORE_EFLAGS
#define ELF_CORE_EFLAGS 0
#endif

/* Firmware provided dump sections */
#define FADUMP_CPU_STATE_DATA 0x0001
#define FADUMP_HPTE_REGION 0x0002
Expand All @@ -56,6 +62,9 @@
#define FADUMP_UNREGISTER 2
#define FADUMP_INVALIDATE 3

/* Dump status flag */
#define FADUMP_ERROR_FLAG 0x2000

/* Kernel Dump section info */
struct fadump_section {
u32 request_flag;
Expand Down Expand Up @@ -109,6 +118,7 @@ struct fw_dump {
/* cmd line option during boot */
unsigned long reserve_bootvar;

unsigned long fadumphdr_addr;
int ibm_configure_kernel_dump;

unsigned long fadump_enabled:1;
Expand All @@ -117,6 +127,39 @@ struct fw_dump {
unsigned long dump_registered:1;
};

/*
* Copy the ascii values for first 8 characters from a string into u64
* variable at their respective indexes.
* e.g.
* The string "FADMPINF" will be converted into 0x4641444d50494e46
*/
static inline u64 str_to_u64(const char *str)
{
u64 val = 0;
int i;

for (i = 0; i < sizeof(val); i++)
val = (*str) ? (val << 8) | *str++ : val << 8;
return val;
}
#define STR_TO_HEX(x) str_to_u64(x)

#define FADUMP_CRASH_INFO_MAGIC STR_TO_HEX("FADMPINF")

/* fadump crash info structure */
struct fadump_crash_info_header {
u64 magic_number;
u64 elfcorehdr_addr;
};

/* Crash memory ranges */
#define INIT_CRASHMEM_RANGES (INIT_MEMBLOCK_REGIONS + 2)

struct fad_crash_memory_ranges {
unsigned long long base;
unsigned long long size;
};

extern int early_init_dt_scan_fw_dump(unsigned long node,
const char *uname, int depth, void *data);
extern int fadump_reserve_mem(void);
Expand Down
233 changes: 232 additions & 1 deletion trunk/arch/powerpc/kernel/fadump.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <linux/delay.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/crash_dump.h>

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

static DEFINE_MUTEX(fadump_mutex);
struct fad_crash_memory_ranges crash_memory_ranges[INIT_CRASHMEM_RANGES];
int crash_mem_ranges;

/* Scan the Firmware Assisted dump configuration details. */
int __init early_init_dt_scan_fw_dump(unsigned long node,
Expand Down Expand Up @@ -235,6 +238,10 @@ static unsigned long get_fadump_area_size(void)
size += fw_dump.cpu_state_data_size;
size += fw_dump.hpte_region_size;
size += fw_dump.boot_memory_size;
size += sizeof(struct fadump_crash_info_header);
size += sizeof(struct elfhdr); /* ELF core header.*/
/* Program headers for crash memory regions. */
size += sizeof(struct elf_phdr) * (memblock_num_regions(memory) + 2);

size = PAGE_ALIGN(size);
return size;
Expand Down Expand Up @@ -300,6 +307,12 @@ int __init fadump_reserve_mem(void)
"for saving crash dump\n",
(unsigned long)(size >> 20),
(unsigned long)(base >> 20));

fw_dump.fadumphdr_addr =
fdm_active->rmr_region.destination_address +
fdm_active->rmr_region.source_len;
pr_debug("fadumphdr_addr = %p\n",
(void *) fw_dump.fadumphdr_addr);
} else {
/* Reserve the memory at the top of memory. */
size = get_fadump_area_size();
Expand Down Expand Up @@ -380,15 +393,227 @@ static void register_fw_dump(struct fadump_mem_struct *fdm)
}
}

/*
* Validate and process the dump data stored by firmware before exporting
* it through '/proc/vmcore'.
*/
static int __init process_fadump(const struct fadump_mem_struct *fdm_active)
{
struct fadump_crash_info_header *fdh;

if (!fdm_active || !fw_dump.fadumphdr_addr)
return -EINVAL;

/* Check if the dump data is valid. */
if ((fdm_active->header.dump_status_flag == FADUMP_ERROR_FLAG) ||
(fdm_active->rmr_region.error_flags != 0)) {
printk(KERN_ERR "Dump taken by platform is not valid\n");
return -EINVAL;
}
if (fdm_active->rmr_region.bytes_dumped !=
fdm_active->rmr_region.source_len) {
printk(KERN_ERR "Dump taken by platform is incomplete\n");
return -EINVAL;
}

/* Validate the fadump crash info header */
fdh = __va(fw_dump.fadumphdr_addr);
if (fdh->magic_number != FADUMP_CRASH_INFO_MAGIC) {
printk(KERN_ERR "Crash info header is not valid.\n");
return -EINVAL;
}

/*
* We are done validating dump info and elfcore header is now ready
* to be exported. set elfcorehdr_addr so that vmcore module will
* export the elfcore header through '/proc/vmcore'.
*/
elfcorehdr_addr = fdh->elfcorehdr_addr;

return 0;
}

static inline void fadump_add_crash_memory(unsigned long long base,
unsigned long long end)
{
if (base == end)
return;

pr_debug("crash_memory_range[%d] [%#016llx-%#016llx], %#llx bytes\n",
crash_mem_ranges, base, end - 1, (end - base));
crash_memory_ranges[crash_mem_ranges].base = base;
crash_memory_ranges[crash_mem_ranges].size = end - base;
crash_mem_ranges++;
}

static void fadump_exclude_reserved_area(unsigned long long start,
unsigned long long end)
{
unsigned long long ra_start, ra_end;

ra_start = fw_dump.reserve_dump_area_start;
ra_end = ra_start + fw_dump.reserve_dump_area_size;

if ((ra_start < end) && (ra_end > start)) {
if ((start < ra_start) && (end > ra_end)) {
fadump_add_crash_memory(start, ra_start);
fadump_add_crash_memory(ra_end, end);
} else if (start < ra_start) {
fadump_add_crash_memory(start, ra_start);
} else if (ra_end < end) {
fadump_add_crash_memory(ra_end, end);
}
} else
fadump_add_crash_memory(start, end);
}

static int fadump_init_elfcore_header(char *bufp)
{
struct elfhdr *elf;

elf = (struct elfhdr *) bufp;
bufp += sizeof(struct elfhdr);
memcpy(elf->e_ident, ELFMAG, SELFMAG);
elf->e_ident[EI_CLASS] = ELF_CLASS;
elf->e_ident[EI_DATA] = ELF_DATA;
elf->e_ident[EI_VERSION] = EV_CURRENT;
elf->e_ident[EI_OSABI] = ELF_OSABI;
memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
elf->e_type = ET_CORE;
elf->e_machine = ELF_ARCH;
elf->e_version = EV_CURRENT;
elf->e_entry = 0;
elf->e_phoff = sizeof(struct elfhdr);
elf->e_shoff = 0;
elf->e_flags = ELF_CORE_EFLAGS;
elf->e_ehsize = sizeof(struct elfhdr);
elf->e_phentsize = sizeof(struct elf_phdr);
elf->e_phnum = 0;
elf->e_shentsize = 0;
elf->e_shnum = 0;
elf->e_shstrndx = 0;

return 0;
}

/*
* Traverse through memblock structure and setup crash memory ranges. These
* ranges will be used create PT_LOAD program headers in elfcore header.
*/
static void fadump_setup_crash_memory_ranges(void)
{
struct memblock_region *reg;
unsigned long long start, end;

pr_debug("Setup crash memory ranges.\n");
crash_mem_ranges = 0;
/*
* add the first memory chunk (RMA_START through boot_memory_size) as
* a separate memory chunk. The reason is, at the time crash firmware
* will move the content of this memory chunk to different location
* specified during fadump registration. We need to create a separate
* program header for this chunk with the correct offset.
*/
fadump_add_crash_memory(RMA_START, fw_dump.boot_memory_size);

for_each_memblock(memory, reg) {
start = (unsigned long long)reg->base;
end = start + (unsigned long long)reg->size;
if (start == RMA_START && end >= fw_dump.boot_memory_size)
start = fw_dump.boot_memory_size;

/* add this range excluding the reserved dump area. */
fadump_exclude_reserved_area(start, end);
}
}

static int fadump_create_elfcore_headers(char *bufp)
{
struct elfhdr *elf;
struct elf_phdr *phdr;
int i;

fadump_init_elfcore_header(bufp);
elf = (struct elfhdr *)bufp;
bufp += sizeof(struct elfhdr);

/* setup PT_LOAD sections. */

for (i = 0; i < crash_mem_ranges; i++) {
unsigned long long mbase, msize;
mbase = crash_memory_ranges[i].base;
msize = crash_memory_ranges[i].size;

if (!msize)
continue;

phdr = (struct elf_phdr *)bufp;
bufp += sizeof(struct elf_phdr);
phdr->p_type = PT_LOAD;
phdr->p_flags = PF_R|PF_W|PF_X;
phdr->p_offset = mbase;

if (mbase == RMA_START) {
/*
* The entire RMA region will be moved by firmware
* to the specified destination_address. Hence set
* the correct offset.
*/
phdr->p_offset = fdm.rmr_region.destination_address;
}

phdr->p_paddr = mbase;
phdr->p_vaddr = (unsigned long)__va(mbase);
phdr->p_filesz = msize;
phdr->p_memsz = msize;
phdr->p_align = 0;

/* Increment number of program headers. */
(elf->e_phnum)++;
}
return 0;
}

static unsigned long init_fadump_header(unsigned long addr)
{
struct fadump_crash_info_header *fdh;

if (!addr)
return 0;

fw_dump.fadumphdr_addr = addr;
fdh = __va(addr);
addr += sizeof(struct fadump_crash_info_header);

memset(fdh, 0, sizeof(struct fadump_crash_info_header));
fdh->magic_number = FADUMP_CRASH_INFO_MAGIC;
fdh->elfcorehdr_addr = addr;

return addr;
}

static void register_fadump(void)
{
unsigned long addr;
void *vaddr;

/*
* If no memory is reserved then we can not register for firmware-
* assisted dump.
*/
if (!fw_dump.reserve_dump_area_size)
return;

fadump_setup_crash_memory_ranges();

addr = fdm.rmr_region.destination_address + fdm.rmr_region.source_len;
/* Initialize fadump crash info header. */
addr = init_fadump_header(addr);
vaddr = __va(addr);

pr_debug("Creating ELF core headers at %#016lx\n", addr);
fadump_create_elfcore_headers(vaddr);

/* register the future kernel dump with firmware. */
register_fw_dump(&fdm);
}
Expand Down Expand Up @@ -585,8 +810,14 @@ int __init setup_fadump(void)
}

fadump_show_config();
/*
* If dump data is available then see if it is valid and prepare for
* saving it to the disk.
*/
if (fw_dump.dump_active)
process_fadump(fdm_active);
/* Initialize the kernel dump memory structure for FAD registration. */
if (fw_dump.reserve_dump_area_size)
else if (fw_dump.reserve_dump_area_size)
init_fadump_mem_struct(&fdm, fw_dump.reserve_dump_area_start);
fadump_init_files();

Expand Down

0 comments on commit f608e9d

Please sign in to comment.