diff options
-rw-r--r-- | kexec/arch/ppc64/Makefile | 1 | ||||
-rw-r--r-- | kexec/arch/ppc64/crashdump-ppc64.c | 123 | ||||
-rw-r--r-- | kexec/arch/ppc64/kexec-elf-ppc64.c | 167 |
3 files changed, 188 insertions, 103 deletions
diff --git a/kexec/arch/ppc64/Makefile b/kexec/arch/ppc64/Makefile index f3b7fad..187a303 100644 --- a/kexec/arch/ppc64/Makefile +++ b/kexec/arch/ppc64/Makefile @@ -6,5 +6,6 @@ KEXEC_C_SRCS+= kexec/arch/ppc64/kexec-zImage-ppc64.c KEXEC_C_SRCS+= kexec/arch/ppc64/fs2dt.c KEXEC_C_SRCS+= kexec/arch/ppc64/kexec-elf-ppc64.c KEXEC_C_SRCS+= kexec/arch/ppc64/kexec-ppc64.c +KEXEC_C_SRCS+= kexec/arch/ppc64/crashdump-ppc64.c KEXEC_S_SRCS+= diff --git a/kexec/arch/ppc64/crashdump-ppc64.c b/kexec/arch/ppc64/crashdump-ppc64.c index 52c6ce4..e949198 100644 --- a/kexec/arch/ppc64/crashdump-ppc64.c +++ b/kexec/arch/ppc64/crashdump-ppc64.c @@ -31,6 +31,8 @@ #include "kexec-ppc64.h" #include "crashdump-ppc64.h" +extern struct arch_options_t arch_options; + /* Stores a sorted list of RAM memory ranges for which to create elf headers. * A separate program header is created for backup region */ @@ -45,6 +47,17 @@ mem_rgns_t usablemem_rgns = {0, }; /* array to store memory regions to be excluded from elf header creation */ mem_rgns_t exclude_rgns = {0, }; +/* + * To store the memory size of the first kernel and this value will be + * passed to the second kernel as command line (savemaxmem=xM). + * The second kernel will be calculated saved_max_pfn based on this + * variable. + * Since we are creating/using usable-memory property, there is no way + * we can determine the RAM size unless parsing the device-tree/memoy@/reg + * property in the kernel. + */ +unsigned long saved_max_mem = 0; + static int sort_regions(mem_rgns_t *rgn); /* Reads the appropriate file and retrieves the SYSTEM RAM regions for whom to @@ -192,6 +205,116 @@ static int get_crash_memory_ranges(struct memory_range **range, int *ranges) return 0; } +/* Converts unsigned long to ascii string. */ +static void ultoa(unsigned long i, char *str) +{ + int j = 0, k; + char tmp; + + do { + str[j++] = i % 10 + '0'; + } while ((i /=10) > 0); + str[j] = '\0'; + + /* Reverse the string. */ + for (j = 0, k = strlen(str) - 1; j < k; j++, k--) { + tmp = str[k]; + str[k] = str[j]; + str[j] = tmp; + } +} + +static int add_cmdline_param(char *cmdline, unsigned long addr, + char *cmdstr, char *byte) +{ + int cmdlen, len, align = 1024; + char str[COMMAND_LINE_SIZE], *ptr; + + /* Passing in =xxxK / =xxxM format. Saves space required in cmdline.*/ + switch (byte[0]) { + case 'K': + if (addr%align) + return -1; + addr = addr/align; + break; + case 'M': + addr = addr/(align *align); + break; + } + ptr = str; + strcpy(str, cmdstr); + ptr += strlen(str); + ultoa(addr, ptr); + strcat(str, byte); + len = strlen(str); + cmdlen = strlen(cmdline) + len; + if (cmdlen > (COMMAND_LINE_SIZE - 1)) + die("Command line overflow\n"); + strcat(cmdline, str); +#if DEBUG + fprintf(stderr, "Command line after adding elfcorehdr: %s\n", cmdline); +#endif + return 0; +} + +/* Loads additional segments in case of a panic kernel is being loaded. + * One segment for backup region, another segment for storing elf headers + * for crash memory image. + */ +int load_crashdump_segments(struct kexec_info *info, char* mod_cmdline, + unsigned long max_addr, unsigned long min_base) +{ + void *tmp; + unsigned long sz, elfcorehdr; + int nr_ranges, align = 1024; + long int nr_cpus = 0; + struct memory_range *mem_range; + + if (get_crash_memory_ranges(&mem_range, &nr_ranges) < 0) + return -1; + + /* Create a backup region segment to store backup data*/ + sz = (BACKUP_SIZE + align - 1) & ~(align - 1); + tmp = xmalloc(sz); + memset(tmp, 0, sz); + info->backup_start = add_buffer(info, tmp, sz, sz, align, + 0, max_addr, 1); + reserve(info->backup_start, sz); + /* Create elf header segment and store crash image data. */ + nr_cpus = sysconf(_SC_NPROCESSORS_CONF); + if (nr_cpus < 0) { + fprintf(stderr,"kexec_load (elf header segment)" + " failed: %s\n", strerror(errno)); + return -1; + } + if (arch_options.core_header_type == CORE_TYPE_ELF64) { + sz = sizeof(Elf64_Ehdr) + + nr_cpus * sizeof(Elf64_Phdr) + + nr_ranges * sizeof(Elf64_Phdr); + } else { + sz = sizeof(Elf32_Ehdr) + + nr_cpus * sizeof(Elf32_Phdr) + + nr_ranges * sizeof(Elf32_Phdr); + } + sz = (sz + align - 1) & ~(align -1); + tmp = xmalloc(sz); + memset(tmp, 0, sz); + if (arch_options.core_header_type == CORE_TYPE_ELF64) { + if (prepare_crash_memory_elf64_headers(info, tmp, sz) < 0) + return -1; + } + + elfcorehdr = add_buffer(info, tmp, sz, sz, align, min_base, + max_addr, 1); + reserve(elfcorehdr, sz); + /* modify and store the cmdline in a global array. This is later + * read by flatten_device_tree and modified if required + */ + add_cmdline_param(mod_cmdline, elfcorehdr, " elfcorehdr=", "K"); + add_cmdline_param(mod_cmdline, saved_max_mem, " savemaxmem=", "M"); + return 0; +} + /* * Used to save various memory regions needed for the captured kernel. */ diff --git a/kexec/arch/ppc64/kexec-elf-ppc64.c b/kexec/arch/ppc64/kexec-elf-ppc64.c index 581b846..08106ce 100644 --- a/kexec/arch/ppc64/kexec-elf-ppc64.c +++ b/kexec/arch/ppc64/kexec-elf-ppc64.c @@ -33,18 +33,18 @@ #include <linux/elf.h> #include "../../kexec.h" #include "../../kexec-elf.h" +#include "../../kexec-syscall.h" #include "kexec-ppc64.h" +#include "crashdump-ppc64.h" #include <arch/options.h> #define BOOTLOADER "kexec" #define BOOTLOADER_VERSION VERSION -#define MAX_COMMAND_LINE 256 unsigned long initrd_base, initrd_size; -int create_flatten_tree(struct kexec_info *, unsigned char **, unsigned long *); -int parse_options(char *); -int setup_memory_ranges(void); +int create_flatten_tree(struct kexec_info *, unsigned char **, unsigned long *, + char *); int elf_ppc64_probe(const char *buf, off_t len) { @@ -68,23 +68,29 @@ 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) + struct kexec_info *info) { struct mem_ehdr ehdr; - const char *command_line; - const char *input_options; - int command_line_len; - const char *ramdisk; - const char *devicetreeblob; - unsigned long *lp; - int result; - int opt; + char *cmdline, *modified_cmdline; + const char *ramdisk, *devicetreeblob; + int cmdline_len, modified_cmdline_len; + unsigned long long max_addr, hole_addr; + unsigned char *seg_buf = NULL; + off_t seg_size = 0; + struct mem_phdr *phdr; + size_t size; + unsigned long long *rsvmap_ptr; + struct bootblock *bb_ptr; + unsigned int nr_segments, i; + int result, opt; + #define OPT_APPEND (OPT_ARCH_MAX+0) #define OPT_RAMDISK (OPT_ARCH_MAX+1) #define OPT_DEVICETREEBLOB (OPT_ARCH_MAX+2) static const struct option options[] = { KEXEC_ARCH_OPTIONS + { "command-line", 1, NULL, OPT_APPEND }, { "append", 1, NULL, OPT_APPEND }, { "ramdisk", 1, NULL, OPT_RAMDISK }, { "devicetreeblob", 1, NULL, OPT_DEVICETREEBLOB }, @@ -96,23 +102,24 @@ int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len, /* Parse command line arguments */ initrd_base = 0; initrd_size = 0; - command_line = 0; - input_options = 0; + cmdline = 0; ramdisk = 0; devicetreeblob = 0; + max_addr = 0xFFFFFFFFFFFFFFFFUL; + hole_addr = 0; - while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { + while ((opt = getopt_long(argc, argv, short_options, + options, 0)) != -1) { switch (opt) { default: /* Ignore core options */ - if (opt < OPT_ARCH_MAX) { + if (opt < OPT_ARCH_MAX) break; - } case '?': usage(); return -1; case OPT_APPEND: - input_options = optarg; + cmdline = optarg; break; case OPT_RAMDISK: ramdisk = optarg; @@ -123,13 +130,24 @@ int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len, } } - command_line_len = 0; - if (command_line) { - command_line_len = strlen(command_line) + 1; - } + cmdline_len = 0; + if (cmdline) + cmdline_len = strlen(cmdline) + 1; + + setup_memory_ranges(info->kexec_flags); - if (input_options) - parse_options(input_options); + /* Need to append some command line parameters internally in case of + * taking crash dumps. + */ + if (info->kexec_flags & KEXEC_ON_CRASH) { + modified_cmdline = xmalloc(COMMAND_LINE_SIZE); + memset((void *)modified_cmdline, 0, COMMAND_LINE_SIZE); + if (cmdline) { + strncpy(modified_cmdline, cmdline, COMMAND_LINE_SIZE); + modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0'; + } + modified_cmdline_len = strlen(modified_cmdline); + } /* Parse the Elf file */ result = build_elf_exec_info(buf, len, &ehdr); @@ -138,25 +156,19 @@ int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len, return result; } - /* Load the Elf data */ - setup_memory_ranges(); /* Load the Elf data. Physical load addresses in elf64 header do not * show up correctly. Use user supplied address for now to patch the * elf header */ - unsigned long long base_addr; - struct mem_phdr *phdr; - size_t size; phdr = &ehdr.e_phdr[0]; size = phdr->p_filesz; - if (size > phdr->p_memsz) { + if (size > phdr->p_memsz) size = phdr->p_memsz; - } - base_addr = (unsigned long)locate_hole(info, size, 0, 0, + hole_addr = (unsigned long)locate_hole(info, size, 0, 0, 0xFFFFFFFFFFFFFFFFUL, 1); - ehdr.e_phdr[0].p_paddr = base_addr; + ehdr.e_phdr[0].p_paddr = hole_addr; result = elf_exec_load(&ehdr, info); if (result < 0) { free_elf_info(&ehdr); @@ -165,10 +177,10 @@ int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len, /* Add a ram-disk to the current image */ if (ramdisk) { - if (devicetreeblob) { - fprintf(stderr, "Can't use ramdisk with device tree blob input\n"); - return -1; - } + if (devicetreeblob) { + fprintf(stderr, "Can't use ramdisk with device tree blob input\n"); + return -1; + } unsigned char *ramdisk_buf = NULL; off_t ramdisk_size = 0; unsigned long long ramdisk_addr; @@ -181,6 +193,19 @@ int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len, initrd_size = ramdisk_size; } + /* If panic kernel is being loaded, additional segments need + * to be created. + */ + if (info->kexec_flags & KEXEC_ON_CRASH) { + result = load_crashdump_segments(info, modified_cmdline, + max_addr, 0); + if (result < 0) + return -1; + /* Use new command line. */ + cmdline = modified_cmdline; + cmdline_len = strlen(modified_cmdline) + 1; + } + /* Add v2wrap to the current image */ unsigned char *v2wrap_buf = NULL; off_t v2wrap_size = 0; @@ -229,7 +254,7 @@ int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len, rsvmap_ptr += 2; } rsvmap_ptr -= 2; - *rsvmap_ptr = (unsigned long long)( + *rsvmap_ptr = (unsigned long long)( info->segment[(info->nr_segments)-1].mem + 0x100); rsvmap_ptr++; *rsvmap_ptr = (unsigned long long)bb_ptr->totalsize; @@ -252,67 +277,3 @@ void elf_ppc64_usage(void) { fprintf(stderr, "elf support is still broken\n"); } - -struct param_struct { - const char *name; - void *val; -}; -struct param_struct params; - -static char *next_arg(char *args, char **param, char **val) -{ - unsigned int i, equals = 0; - char *next; - - /* Chew any extra spaces */ - while (*args == ' ') args++; - for (i = 0; args[i]; i++) { - if (args[i] == ' ') - break; - if (equals == 0) { - if (args[i] == '=') - equals = i; - } - } - *param = args; - if (!equals) - *val = NULL; - else { - args[equals] = '\0'; - *val = args + equals + 1; - } - - if (args[i]) { - args[i] = '\0'; - next = args + i + 1; - } else - next = args + i; - return next; -} - -static int add_arg(char *param, char*val) -{ - int ret = 0; - if (strcmp(param, "initrd-base")==0) - initrd_base=strtoul(val, NULL, 0); - else if (strcmp(param, "initrd-size")==0) - initrd_size=strtoul(val, NULL, 0); - else { - printf("invalid option\n"); - ret = 1; - } - return ret; -} - -int parse_options(char *options) -{ - char *param, *val; - /* initrd-addr , initrd-size */ - while (*options) { - int ret; - options = next_arg(options, ¶m, &val); - ret = add_arg(param, val); - } - /* All parsed OK */ - return 0; -} |