diff options
author | R Sharada <sharada@in.ibm.com> | 2006-07-27 10:45:32 -0600 |
---|---|---|
committer | Eric W. Biederman <ebiederm@xmission.com> | 2006-07-27 10:47:02 -0600 |
commit | 089c003250471f636141cf8b3db2acd259c9331f (patch) | |
tree | 197894865f33475e62c1e4c79faa85c8d3476391 | |
parent | 34d39f48bb5534a3f48b942ca53f57e1c5f8e48d (diff) |
ppc64 kdump crash memory support
This patch provides the support for setting up te various
memory regions required for crashdump functionality.
- limit mem_min and mem_max to crash_base and crash_end
- create usable_memory_regions for use by second kernel
- exclude regions not to be dumped by second kernel.
For example - tce-table and crash reserved region
- include rtas region in crash_memory_ranges if it falls
within crash reserved region, as we want to obtain the
rtas image in the dump core file.
Signed-off-by: R Sharada <sharada@in.ibm.com>
Signed-off-by: Haren Myneni <hbabu@us.ibm.com>
Signed-off-by: Maneesh Soni <maneesh@in.ibm.com>
-rw-r--r-- | kexec/arch/ppc64/crashdump-ppc64.c | 293 | ||||
-rw-r--r-- | kexec/arch/ppc64/crashdump-ppc64.h | 6 | ||||
-rw-r--r-- | kexec/arch/ppc64/kexec-ppc64.c | 159 | ||||
-rw-r--r-- | kexec/arch/ppc64/kexec-ppc64.h | 10 |
4 files changed, 443 insertions, 25 deletions
diff --git a/kexec/arch/ppc64/crashdump-ppc64.c b/kexec/arch/ppc64/crashdump-ppc64.c new file mode 100644 index 0000000..52c6ce4 --- /dev/null +++ b/kexec/arch/ppc64/crashdump-ppc64.c @@ -0,0 +1,293 @@ +/* + * kexec: Linux boots Linux + * + * Created by: R Sharada (sharada@in.ibm.com) + * Copyright (C) IBM Corporation, 2005. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <elf.h> +#include <dirent.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "../../kexec-syscall.h" +#include "../../crashdump.h" +#include "kexec-ppc64.h" +#include "crashdump-ppc64.h" + +/* Stores a sorted list of RAM memory ranges for which to create elf headers. + * A separate program header is created for backup region + */ +static struct memory_range crash_memory_range[CRASH_MAX_MEMORY_RANGES]; + +/* + * Used to save various memory ranges/regions needed for the captured + * kernel to boot. (lime memmap= option in other archs) + */ +mem_rgns_t usablemem_rgns = {0, }; + +/* array to store memory regions to be excluded from elf header creation */ +mem_rgns_t exclude_rgns = {0, }; + +static int sort_regions(mem_rgns_t *rgn); + +/* Reads the appropriate file and retrieves the SYSTEM RAM regions for whom to + * create Elf headers. Keeping it separate from get_memory_ranges() as + * requirements are different in the case of normal kexec and crashdumps. + * + * Normal kexec needs to look at all of available physical memory irrespective + * of the fact how much of it is being used by currently running kernel. + * Crashdumps need to have access to memory regions actually being used by + * running kernel. Expecting a different file/data structure than /proc/iomem + * to look into down the line. May be something like /proc/kernelmem or may + * be zone data structures exported from kernel. + */ +static int get_crash_memory_ranges(struct memory_range **range, int *ranges) +{ + + int memory_ranges = 0; + char device_tree[256] = "/proc/device-tree/"; + char fname[256]; + char buf[MAXBYTES-1]; + DIR *dir, *dmem; + FILE *file; + struct dirent *dentry, *mentry; + int i, n, match; + unsigned long long start, end, cstart, cend; + + /* create a separate program header for the backup region */ + crash_memory_range[0].start = 0x0000000000000000; + crash_memory_range[0].end = 0x0000000000008000; + crash_memory_range[0].type = RANGE_RAM; + memory_ranges++; + + if ((dir = opendir(device_tree)) == NULL) { + perror(device_tree); + return -1; + } + while ((dentry = readdir(dir)) != NULL) { + if (strncmp(dentry->d_name, "memory@", 7)) + continue; + strcpy(fname, device_tree); + strcat(fname, dentry->d_name); + if ((dmem = opendir(fname)) == NULL) { + perror(fname); + closedir(dir); + return -1; + } + while ((mentry = readdir(dmem)) != NULL) { + if (strcmp(mentry->d_name, "reg")) + continue; + strcat(fname, "/reg"); + if ((file = fopen(fname, "r")) == NULL) { + perror(fname); + closedir(dmem); + closedir(dir); + return -1; + } + if ((n = fread(buf, 1, MAXBYTES, file)) < 0) { + perror(fname); + fclose(file); + closedir(dmem); + closedir(dir); + return -1; + } + if (memory_ranges >= MAX_MEMORY_RANGES) + break; + start = ((unsigned long long *)buf)[0]; + end = start + ((unsigned long long *)buf)[1]; + if (start == 0 && end >= 0x8000) + start = 0x8000; + match = 0; + sort_regions(&exclude_rgns); + + /* exclude crash reserved regions */ + for (i = 0; i < exclude_rgns.size; i++) { + cstart = exclude_rgns.ranges[i].start; + cend = exclude_rgns.ranges[i].end; + if (cstart < end && cend > start) { + if ((cstart == start) && (cend == end)) { + match = 1; + continue; + } + if (start < cstart && end > cend) { + match = 1; + crash_memory_range[memory_ranges].start = start; + crash_memory_range[memory_ranges].end = cstart - 1; + crash_memory_range[memory_ranges].type = RANGE_RAM; + memory_ranges++; + crash_memory_range[memory_ranges].start = cend + 1; + crash_memory_range[memory_ranges].end = end; + crash_memory_range[memory_ranges].type = RANGE_RAM; + memory_ranges++; + break; + } else if (start < cstart) { + match = 1; + crash_memory_range[memory_ranges].start = start; + crash_memory_range[memory_ranges].end = cstart - 1; + crash_memory_range[memory_ranges].type = RANGE_RAM; + memory_ranges++; + end = cstart - 1; + continue; + } else if (end > cend){ + match = 1; + crash_memory_range[memory_ranges].start = cend + 1; + crash_memory_range[memory_ranges].end = end; + crash_memory_range[memory_ranges].type = RANGE_RAM; + memory_ranges++; + start = cend + 1; + continue; + } + } + + } /* end of for loop */ + if (!match) { + crash_memory_range[memory_ranges].start = start; + crash_memory_range[memory_ranges].end = end; + crash_memory_range[memory_ranges].type = RANGE_RAM; + memory_ranges++; + } + + fclose(file); + } + closedir(dmem); + } + closedir(dir); + + /* + * Can not trust the memory regions order that we read from + * device-tree. Hence, get the MAX end value. + */ + for (i = 0; i < memory_ranges; i++) + if (saved_max_mem < crash_memory_range[i].end) + saved_max_mem = crash_memory_range[i].end; + + *range = crash_memory_range; + *ranges = memory_ranges; +#if DEBUG + int i; + printf("CRASH MEMORY RANGES\n"); + for(i = 0; i < *ranges; i++) { + start = crash_memory_range[i].start; + end = crash_memory_range[i].end; + fprintf(stderr, "%016Lx-%016Lx\n", start, end); + } +#endif + return 0; +} + +/* + * Used to save various memory regions needed for the captured kernel. + */ + +void add_usable_mem_rgns(unsigned long long base, unsigned long long size) +{ + int i; + unsigned long long end = base + size; + unsigned long long ustart, uend; + + base = _ALIGN_DOWN(base, PAGE_SIZE); + end = _ALIGN_UP(end, PAGE_SIZE); + + for (i=0; i < usablemem_rgns.size; i++) { + ustart = usablemem_rgns.ranges[i].start; + uend = usablemem_rgns.ranges[i].end; + if (base < uend && end > ustart) { + if ((base >= ustart) && (end <= uend)) + return; + if (base < ustart && end > uend) { + usablemem_rgns.ranges[i].start = base; + usablemem_rgns.ranges[i].end = end; + return; + } else if (base < ustart) { + usablemem_rgns.ranges[i].start = base; + return; + } else if (end > uend){ + usablemem_rgns.ranges[i].end = end; + return; + } + } + } + usablemem_rgns.ranges[usablemem_rgns.size].start = base; + usablemem_rgns.ranges[usablemem_rgns.size++].end = end; + +#ifdef DEBUG + fprintf(stderr, "usable memory rgns size:%d base:%lx size:%lx\n", usablemem_rgns.size, base, size); +#endif +} + +/* + * Used to exclude various memory regions that do not need elf hdr generation + */ + +void add_exclude_rgns(unsigned long long base, unsigned long long size) +{ + int i; + unsigned long long end = base + size; + unsigned long long xstart, xend; + + for (i=0; i < exclude_rgns.size; i++) { + xstart = exclude_rgns.ranges[i].start; + xend = exclude_rgns.ranges[i].end; + if (base < xend && end > xstart) { + if ((base >= xstart) && (end <= xend)) + return; + if (base < xstart && end > xend) { + exclude_rgns.ranges[i].start = base; + exclude_rgns.ranges[i].end = end; + return; + } else if (base < xstart) { + exclude_rgns.ranges[i].start = base; + exclude_rgns.ranges[i].end = xend; + return; + } else if (end > xend){ + exclude_rgns.ranges[i].start = xstart; + exclude_rgns.ranges[i].end = end; + return; + } + } + } + exclude_rgns.ranges[exclude_rgns.size].start = base; + exclude_rgns.ranges[exclude_rgns.size++].end = end; + +#ifdef DEBUG + fprintf(stderr, "exclude rgns size:%d base:%lx end:%lx size:%lx\n", exclude_rgns.size, base, end, size); +#endif +} + +static int sort_regions(mem_rgns_t *rgn) +{ + int i, j; + unsigned long long tstart, tend; + for (i = 0; i < rgn->size; i++) { + for (j = 0; j < rgn->size - i - 1; j++) { + if (rgn->ranges[j].start > rgn->ranges[j+1].start) { + tstart = rgn->ranges[j].start; + tend = rgn->ranges[j].end; + rgn->ranges[j].start = rgn->ranges[j+1].start; + rgn->ranges[j].end = rgn->ranges[j+1].end; + rgn->ranges[j+1].start = tstart; + rgn->ranges[j+1].end = tend; + } + } + } + return 0; + +} + diff --git a/kexec/arch/ppc64/crashdump-ppc64.h b/kexec/arch/ppc64/crashdump-ppc64.h index e080cb3..76c05fe 100644 --- a/kexec/arch/ppc64/crashdump-ppc64.h +++ b/kexec/arch/ppc64/crashdump-ppc64.h @@ -1,7 +1,10 @@ #ifndef CRASHDUMP_PPC64_H #define CRASHDUMP_PPC64_H +int load_crashdump_segments(struct kexec_info *info, char *mod_cmdline, + unsigned long max_addr, unsigned long min_base); void add_usable_mem_rgns(unsigned long long base, unsigned long long size); +void add_exclude_rgns(unsigned long long base, unsigned long long size); #define PAGE_OFFSET 0xC000000000000000 #define KERNELBASE PAGE_OFFSET @@ -24,4 +27,7 @@ void add_usable_mem_rgns(unsigned long long base, unsigned long long size); #define _ALIGN_DOWN(addr,size) ((addr)&(~((size)-1))) #define PAGE_SIZE 4096 +extern unsigned long long crash_base; +extern unsigned long long crash_size; + #endif /* CRASHDUMP_PPC64_H */ diff --git a/kexec/arch/ppc64/kexec-ppc64.c b/kexec/arch/ppc64/kexec-ppc64.c index a98bbf0..c880b7a 100644 --- a/kexec/arch/ppc64/kexec-ppc64.c +++ b/kexec/arch/ppc64/kexec-ppc64.c @@ -31,27 +31,23 @@ #include "../../kexec.h" #include "../../kexec-syscall.h" #include "kexec-ppc64.h" +#include "crashdump-ppc64.h" #include <arch/options.h> -#define MAX_MEMORY_RANGES 64 -#define MAX_LINE 160 -#define MAXBYTES 128 /* Platforms supported by kexec on PPC64 */ #define PLATFORM_PSERIES 0x0100 #define PLATFORM_PSERIES_LPAR 0x0101 -struct exclude_range { - unsigned long long start, end; -}; static struct exclude_range exclude_range[MAX_MEMORY_RANGES]; - static unsigned long long rmo_top; static unsigned int platform; static struct memory_range memory_range[MAX_MEMORY_RANGES]; static struct memory_range base_memory_range[MAX_MEMORY_RANGES]; unsigned long long memory_max = 0; -static int nr_memory_ranges; -static int nr_exclude_ranges; +static int nr_memory_ranges, nr_exclude_ranges; +unsigned long long crash_base, crash_size; + +static int sort_base_ranges(); /* Get base memory ranges */ static int get_base_ranges() @@ -105,17 +101,44 @@ static int get_base_ranges() ((unsigned long long *)buf)[1]; base_memory_range[local_memory_ranges].type = RANGE_RAM; local_memory_ranges++; -#if 0 +#ifdef DEBUG fprintf(stderr, "%016Lx-%016Lx : %x\n", memory_range[local_memory_ranges-1].start, memory_range[local_memory_ranges-1].end, memory_range[local_memory_ranges].type); #endif fclose(file); } - memory_max = base_memory_range[local_memory_ranges - 1].end; closedir(dmem); } closedir(dir); nr_memory_ranges = local_memory_ranges; + sort_base_ranges(); + memory_max = base_memory_range[nr_memory_ranges - 1].end; +#ifdef DEBUG fprintf(stderr, "get base memory ranges:%d\n", nr_memory_ranges); +#endif + return 0; +} + +/* Sort the base ranges in memory - this is useful for ensuring that our + * ranges are in ascending order, even if device-tree read of memory nodes + * is done differently. Also, could be used for other range coalescing later + */ +static int sort_base_ranges() +{ + int i, j; + unsigned long long tstart, tend; + + for (i = 0; i < nr_memory_ranges - 1; i++) { + for (j = 0; j < nr_memory_ranges - i - 1; j++) { + if (base_memory_range[j].start > base_memory_range[j+1].start) { + tstart = base_memory_range[j].start; + tend = base_memory_range[j].end; + base_memory_range[j].start = base_memory_range[j+1].start; + base_memory_range[j].end = base_memory_range[j+1].end; + base_memory_range[j+1].start = tstart; + base_memory_range[j+1].end = tend; + } + } + } return 0; } @@ -139,12 +162,17 @@ static int sort_ranges() return 0; } -/* Get devtree details and create exclude_range array */ -static int get_devtree_details() +/* Get devtree details and create exclude_range array + * Also create usablemem_ranges for KEXEC_ON_CRASH + */ +static int get_devtree_details(unsigned long kexec_flags) { unsigned long long rmo_base; unsigned long long tce_base; unsigned int tce_size; + unsigned int rtas_base, rtas_size; + unsigned long long htab_base, htab_size; + unsigned long long kernel_end; char buf[MAXBYTES-1]; char device_tree[256] = "/proc/device-tree/"; char fname[256]; @@ -189,6 +217,7 @@ static int get_devtree_details() return -1; } fclose(file); + memset(fname, 0, sizeof(fname)); strcpy(fname, device_tree); strcat(fname, dentry->d_name); @@ -199,7 +228,6 @@ static int get_devtree_details() closedir(dir); return -1; } - unsigned long long kernel_end; if (fread(&kernel_end, sizeof(unsigned long), 1, file) != 1) { perror(fname); fclose(file); @@ -207,10 +235,62 @@ static int get_devtree_details() closedir(dir); return -1; } + fclose(file); + /* Add kernel memory to exclude_range */ exclude_range[i].start = 0x0UL; exclude_range[i].end = kernel_end; i++; + + if (kexec_flags & KEXEC_ON_CRASH) { + memset(fname, 0, sizeof(fname)); + strcpy(fname, device_tree); + strcat(fname, dentry->d_name); + strcat(fname, "/linux,crashkernel-base"); + if ((file = fopen(fname, "r")) == NULL) { + perror(fname); + closedir(cdir); + closedir(dir); + return -1; + } + if (fread(&crash_base, sizeof(unsigned long), 1, + file) != 1) { + perror(fname); + fclose(file); + closedir(cdir); + closedir(dir); + return -1; + } + fclose(file); + + memset(fname, 0, sizeof(fname)); + strcpy(fname, device_tree); + strcat(fname, dentry->d_name); + strcat(fname, "/linux,crashkernel-size"); + if ((file = fopen(fname, "r")) == NULL) { + perror(fname); + closedir(cdir); + closedir(dir); + return -1; + } + if (fread(&crash_size, sizeof(unsigned long), 1, + file) != 1) { + perror(fname); + fclose(file); + closedir(cdir); + closedir(dir); + return -1; + } + + if (crash_base > mem_min) + mem_min = crash_base; + if (crash_base + crash_size < mem_max) + mem_max = crash_base + crash_size; + + add_usable_mem_rgns(0, crash_base + crash_size); + reserve(KDUMP_BACKUP_LIMIT, crash_base-KDUMP_BACKUP_LIMIT); + } + /* if LPAR, no need to read any more from /chosen */ if (platform != PLATFORM_PSERIES) { closedir(cdir); @@ -226,7 +306,6 @@ static int get_devtree_details() closedir(dir); return -1; } - unsigned long long htab_base, htab_size; if (fread(&htab_base, sizeof(unsigned long), 1, file) != 1) { perror(fname); fclose(file); @@ -265,7 +344,6 @@ static int get_devtree_details() closedir(dir); return -1; } - unsigned int rtas_base, rtas_size; if (fread(&rtas_base, sizeof(unsigned int), 1, file) != 1) { perror(fname); fclose(file); @@ -295,6 +373,8 @@ static int get_devtree_details() exclude_range[i].start = rtas_base; exclude_range[i].end = rtas_base + rtas_size; i++; + if (kexec_flags & KEXEC_ON_CRASH) + add_usable_mem_rgns(rtas_base, rtas_size); } /* rtas */ if (strncmp(dentry->d_name, "memory@0", 8) == 0) { @@ -314,8 +394,10 @@ static int get_devtree_details() } rmo_base = ((unsigned long long *)buf)[0]; rmo_top = rmo_base + ((unsigned long long *)buf)[1]; - if (platform == PLATFORM_PSERIES) - if (memory_max > 0x40000000UL? (rmo_top = 0x40000000UL) : (rmo_top = memory_max)); + if (platform == PLATFORM_PSERIES) { + if (rmo_top > 0x30000000UL) + rmo_top = 0x30000000UL; + } fclose(file); closedir(cdir); } /* memory */ @@ -360,6 +442,8 @@ static int get_devtree_details() exclude_range[i].start = tce_base; exclude_range[i].end = tce_base + tce_size; i++; + if (kexec_flags & KEXEC_ON_CRASH) + add_usable_mem_rgns(tce_base, tce_size); closedir(cdir); } /* pci */ } @@ -368,7 +452,31 @@ static int get_devtree_details() nr_exclude_ranges = i; sort_ranges(); -#if 0 + + /* add crash_region and remove rtas range from exclude regions if it + * lies within crash region + */ + if (kexec_flags & KEXEC_ON_CRASH) { + unsigned long new_crash_size; + if (crash_base < rtas_base && + ((crash_base + crash_size) > (rtas_base + rtas_size))){ + new_crash_size = rtas_base - crash_base; + add_exclude_rgns(crash_base, new_crash_size); + new_crash_size = (crash_base + crash_size) - (rtas_base + rtas_size); + add_exclude_rgns(rtas_base + rtas_size, new_crash_size); + } else if (crash_base < rtas_base && + ((rtas_base + rtas_size) > (crash_base + crash_size))){ + new_crash_size = rtas_base - crash_base; + add_exclude_rgns(crash_base, new_crash_size); + } else if (crash_base > rtas_base && + ((rtas_base + rtas_size) < (crash_base + crash_size))){ + new_crash_size = (crash_base + crash_size) - (rtas_base + rtas_size); + add_exclude_rgns(rtas_base + rtas_size, new_crash_size); + } else + add_exclude_rgns(crash_base, crash_size); + } + +#ifdef DEBUG int k; for (k = 0; k < i; k++) fprintf(stderr, "exclude_range sorted exclude_range[%d] start:%lx, end:%lx\n", k, exclude_range[k].start, exclude_range[k].end); @@ -377,7 +485,7 @@ static int get_devtree_details() } /* Setup a sorted list of memory ranges. */ -int setup_memory_ranges(void) +int setup_memory_ranges(unsigned long kexec_flags) { int i, j = 0; @@ -386,7 +494,7 @@ int setup_memory_ranges(void) */ get_base_ranges(); - get_devtree_details(); + get_devtree_details(kexec_flags); for (i = 0; i < nr_exclude_ranges; i++) { /* If first exclude range does not start with 0, include the @@ -435,17 +543,18 @@ int setup_memory_ranges(void) j--; break; } - if ((memory_range[j-1].start < rmo_top) && (memory_range[j-1].end >= rmo_top)) { + if ((memory_range[j-1].start < rmo_top) && + (memory_range[j-1].end >= rmo_top)) { memory_range[j-1].end = rmo_top; break; } } nr_memory_ranges = j; -#if 0 +#ifdef DEBUG int k; for (k = 0; k < j; k++) - fprintf(stderr, "seup_memory_ranges memory_range[%d] start:%lx, end:%lx\n", k, memory_range[k].start, memory_range[k].end); + fprintf(stderr, "setup_memory_ranges memory_range[%d] start:%lx, end:%lx\n", k, memory_range[k].start, memory_range[k].end); #endif return 0; } @@ -454,7 +563,7 @@ int setup_memory_ranges(void) int get_memory_ranges(struct memory_range **range, int *ranges, unsigned long kexec_flags) { - setup_memory_ranges(); + setup_memory_ranges(kexec_flags); *range = memory_range; *ranges = nr_memory_ranges; fprintf(stderr, "get memory ranges:%d\n", nr_memory_ranges); diff --git a/kexec/arch/ppc64/kexec-ppc64.h b/kexec/arch/ppc64/kexec-ppc64.h index c6175a3..df13488 100644 --- a/kexec/arch/ppc64/kexec-ppc64.h +++ b/kexec/arch/ppc64/kexec-ppc64.h @@ -1,6 +1,12 @@ #ifndef KEXEC_PPC64_H #define KEXEC_PPC64_H +#define MAX_MEMORY_RANGES 256 /* TO FIX - needs to be dynamically set */ +#define MAXBYTES 128 +#define MAX_LINE 160 + +int setup_memory_ranges(unsigned long kexec_flags); + int elf_ppc64_probe(const char *buf, off_t len); int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len, struct kexec_info *info); @@ -20,6 +26,10 @@ struct bootblock { boot_physid; }; +struct exclude_range { + unsigned long long start, end; +}; + typedef struct mem_rgns { unsigned int size; struct exclude_range ranges[MAX_MEMORY_RANGES]; |