summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--kexec/arch/sh/Makefile1
-rw-r--r--kexec/arch/sh/crashdump-sh.c186
-rw-r--r--kexec/arch/sh/crashdump-sh.h9
-rw-r--r--kexec/arch/sh/kexec-elf-sh.c31
-rw-r--r--kexec/arch/sh/kexec-sh.c30
-rw-r--r--kexec/arch/sh/kexec-sh.h2
6 files changed, 249 insertions, 10 deletions
diff --git a/kexec/arch/sh/Makefile b/kexec/arch/sh/Makefile
index 1a67dff..436071f 100644
--- a/kexec/arch/sh/Makefile
+++ b/kexec/arch/sh/Makefile
@@ -7,6 +7,7 @@ sh_KEXEC_SRCS += kexec/arch/sh/kexec-netbsd-sh.c
sh_KEXEC_SRCS += kexec/arch/sh/kexec-elf-sh.c
sh_KEXEC_SRCS += kexec/arch/sh/kexec-elf-rel-sh.c
sh_KEXEC_SRCS += kexec/arch/sh/netbsd_booter.S
+sh_KEXEC_SRCS += kexec/arch/sh/crashdump-sh.c
sh_ADD_BUFFER =
sh_ADD_SEGMENT =
diff --git a/kexec/arch/sh/crashdump-sh.c b/kexec/arch/sh/crashdump-sh.c
new file mode 100644
index 0000000..e23a5f8
--- /dev/null
+++ b/kexec/arch/sh/crashdump-sh.c
@@ -0,0 +1,186 @@
+/*
+ * crashdump-sh.c - crashdump for SuperH
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * Based on x86 and ppc64 implementation, written by
+ * Vivek Goyal (vgoyal@in.ibm.com), R Sharada (sharada@in.ibm.com)
+ * Copyright (C) IBM Corporation, 2005. All rights reserved
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#define _GNU_SOURCE
+#include <stddef.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <elf.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "../../kexec-elf-boot.h"
+#include "../../kexec-syscall.h"
+#include "../../crashdump.h"
+#include "kexec-sh.h"
+#include "crashdump-sh.h"
+#include <arch/options.h>
+
+#define CRASH_MAX_MEMORY_RANGES 64
+static struct memory_range crash_memory_range[CRASH_MAX_MEMORY_RANGES];
+
+uint64_t saved_max_mem;
+
+static int crash_sh_range_nr;
+static int crash_sh_memory_range_callback(void *data, int nr,
+ char *str,
+ unsigned long base,
+ unsigned long length)
+{
+
+ struct memory_range *range = crash_memory_range;
+ struct memory_range *range2 = crash_memory_range;
+
+ range += crash_sh_range_nr;
+
+ if (crash_sh_range_nr >= CRASH_MAX_MEMORY_RANGES) {
+ return 1;
+ }
+
+ if (strncmp(str, "System RAM\n", 11) == 0) {
+ range->start = base;
+ range->end = base + length - 1;
+ range->type = RANGE_RAM;
+ crash_sh_range_nr++;
+
+ if (saved_max_mem < range->end)
+ saved_max_mem = range->end;
+ }
+
+ if (strncmp(str, "Crash kernel\n", 13) == 0) {
+ if (!crash_sh_range_nr)
+ die("Unsupported /proc/iomem format\n");
+
+ range2 = range - 1;
+ if ((base + length - 1) < range2->end) {
+ range->start = base + length;
+ range->end = range2->end;
+ range->type = RANGE_RAM;
+ crash_sh_range_nr++;
+ }
+ range2->end = base - 1;
+ }
+
+ return 0;
+}
+
+/* Return a sorted list of available memory ranges. */
+static int crash_get_memory_ranges(struct memory_range **range, int *ranges)
+{
+ crash_sh_range_nr = 0;
+ saved_max_mem = 0;
+
+ kexec_iomem_for_each_line(NULL, crash_sh_memory_range_callback, NULL);
+ *range = crash_memory_range;
+ *ranges = crash_sh_range_nr;
+ return 0;
+}
+
+static struct crash_elf_info elf_info32 =
+{
+ class: ELFCLASS32,
+ data: ELFDATA2LSB,
+ machine: EM_SH,
+ page_offset: PAGE_OFFSET,
+};
+
+/* 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, uint64_t 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 storing elf headers for crash memory image.
+ */
+int load_crashdump_segments(struct kexec_info *info, char* mod_cmdline)
+{
+ void *tmp;
+ unsigned long sz, elfcorehdr;
+ int nr_ranges;
+ struct memory_range *mem_range;
+
+ if (crash_get_memory_ranges(&mem_range, &nr_ranges) < 0)
+ return -1;
+
+ if (crash_create_elf32_headers(info, &elf_info32,
+ mem_range, nr_ranges,
+ &tmp, &sz,
+ ELF_CORE_HEADER_ALIGN) < 0)
+ return -1;
+
+ elfcorehdr = add_buffer_phys_virt(info, tmp, sz, sz, 1024,
+ 0, 0xffffffff, -1, 0);
+
+ dbgprintf("Created elf header segment at 0x%lx\n", elfcorehdr);
+ add_cmdline_param(mod_cmdline, elfcorehdr, " elfcorehdr=", "K");
+ add_cmdline_param(mod_cmdline, elfcorehdr - mem_min, " mem=", "K");
+
+ return 0;
+}
+
+int is_crashkernel_mem_reserved(void)
+{
+ uint64_t start, end;
+
+ return parse_iomem_single("Crash kernel\n", &start, &end) == 0 ?
+ (start != end) : 0;
+}
diff --git a/kexec/arch/sh/crashdump-sh.h b/kexec/arch/sh/crashdump-sh.h
new file mode 100644
index 0000000..c5d4102
--- /dev/null
+++ b/kexec/arch/sh/crashdump-sh.h
@@ -0,0 +1,9 @@
+#ifndef CRASHDUMP_SH_H
+#define CRASHDUMP_SH_H
+
+struct kexec_info;
+int load_crashdump_segments(struct kexec_info *info, char* mod_cmdline);
+
+#define PAGE_OFFSET 0x80000000
+
+#endif /* CRASHDUMP_SH_H */
diff --git a/kexec/arch/sh/kexec-elf-sh.c b/kexec/arch/sh/kexec-elf-sh.c
index a5f0967..bec3bb2 100644
--- a/kexec/arch/sh/kexec-elf-sh.c
+++ b/kexec/arch/sh/kexec-elf-sh.c
@@ -37,6 +37,7 @@
#include "../../kexec-elf.h"
#include "../../kexec-elf-boot.h"
#include <arch/options.h>
+#include "crashdump-sh.h"
#include "kexec-sh.h"
int elf_sh_probe(const char *buf, off_t len)
@@ -70,8 +71,9 @@ int elf_sh_load(int argc, char **argv, const char *buf, off_t len,
{
struct mem_ehdr ehdr;
char *command_line;
+ char *modified_cmdline;
struct mem_sym sym;
- int opt;
+ int opt, rc;
static const struct option options[] = {
KEXEC_ARCH_OPTIONS
{ 0, 0, 0, 0 },
@@ -82,7 +84,7 @@ int elf_sh_load(int argc, char **argv, const char *buf, off_t len,
/*
* Parse the command line arguments
*/
- command_line = 0;
+ command_line = modified_cmdline = 0;
while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
switch(opt) {
default:
@@ -99,9 +101,32 @@ int elf_sh_load(int argc, char **argv, const char *buf, off_t len,
}
}
+ /* 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 (command_line) {
+ strncpy(modified_cmdline, command_line,
+ COMMAND_LINE_SIZE);
+ modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0';
+ }
+ }
+
/* Load the ELF executable */
elf_exec_build_load(info, &ehdr, buf, len, 0);
- info->entry = (void *)ehdr.e_entry;
+ info->entry = (void *)virt_to_phys(ehdr.e_entry);
+
+ /* If panic kernel is being loaded, additional segments need
+ * to be created. */
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ rc = load_crashdump_segments(info, modified_cmdline);
+ if (rc < 0)
+ return -1;
+ /* Use new command line. */
+ command_line = modified_cmdline;
+ }
/* If we're booting a vmlinux then fill in empty_zero_page */
if (elf_rel_find_symbol(&ehdr, "empty_zero_page", &sym) == 0) {
diff --git a/kexec/arch/sh/kexec-sh.c b/kexec/arch/sh/kexec-sh.c
index bde12d8..141ea8b 100644
--- a/kexec/arch/sh/kexec-sh.c
+++ b/kexec/arch/sh/kexec-sh.c
@@ -41,12 +41,33 @@ static int kexec_sh_memory_range_callback(void *data, int nr,
int get_memory_ranges(struct memory_range **range, int *ranges,
unsigned long kexec_flags)
{
- int nr;
-
+ int nr, ret;
nr = kexec_iomem_for_each_line("System RAM\n",
kexec_sh_memory_range_callback, NULL);
*range = memory_range;
*ranges = nr;
+
+ /*
+ * Redefine the memory region boundaries if kernel
+ * exports the limits and if it is panic kernel.
+ * Override user values only if kernel exported values are
+ * subset of user defined values.
+ */
+ if (kexec_flags & KEXEC_ON_CRASH) {
+ unsigned long long start, end;
+
+ ret = parse_iomem_single("Crash kernel\n", &start, &end);
+ if (ret != 0) {
+ fprintf(stderr, "parse_iomem_single failed.\n");
+ return -1;
+ }
+
+ if (start > mem_min)
+ mem_min = start;
+ if (end < mem_max)
+ mem_max = end;
+ }
+
return 0;
}
@@ -156,11 +177,6 @@ void kexec_sh_setup_zero_page(char *zero_page_buf, int zero_page_size,
}
}
-int is_crashkernel_mem_reserved(void)
-{
- return 0; /* kdump is not supported on this platform (yet) */
-}
-
unsigned long virt_to_phys(unsigned long addr)
{
unsigned long seg = addr & 0xe0000000;
diff --git a/kexec/arch/sh/kexec-sh.h b/kexec/arch/sh/kexec-sh.h
index 683494d..3b46a47 100644
--- a/kexec/arch/sh/kexec-sh.h
+++ b/kexec/arch/sh/kexec-sh.h
@@ -1,6 +1,8 @@
#ifndef KEXEC_SH_H
#define KEXEC_SH_H
+#define COMMAND_LINE_SIZE 2048
+
int zImage_sh_probe(const char *buf, off_t len);
int zImage_sh_load(int argc, char **argv, const char *buf, off_t len,
struct kexec_info *info);