summaryrefslogtreecommitdiffstats
path: root/kexec/arch/sh
diff options
context:
space:
mode:
Diffstat (limited to 'kexec/arch/sh')
-rw-r--r--kexec/arch/sh/Makefile22
-rw-r--r--kexec/arch/sh/crashdump-sh.c166
-rw-r--r--kexec/arch/sh/crashdump-sh.h9
-rw-r--r--kexec/arch/sh/include/arch/options.h42
-rw-r--r--kexec/arch/sh/kexec-elf-rel-sh.c54
-rw-r--r--kexec/arch/sh/kexec-elf-sh.c136
-rw-r--r--kexec/arch/sh/kexec-netbsd-sh.c149
-rw-r--r--kexec/arch/sh/kexec-sh.c259
-rw-r--r--kexec/arch/sh/kexec-sh.h29
-rw-r--r--kexec/arch/sh/kexec-uImage-sh.c24
-rw-r--r--kexec/arch/sh/kexec-zImage-sh.c142
-rw-r--r--kexec/arch/sh/netbsd_booter.S47
12 files changed, 1079 insertions, 0 deletions
diff --git a/kexec/arch/sh/Makefile b/kexec/arch/sh/Makefile
new file mode 100644
index 0000000..7cf40ae
--- /dev/null
+++ b/kexec/arch/sh/Makefile
@@ -0,0 +1,22 @@
+#
+# kexec sh (linux booting linux)
+#
+sh_KEXEC_SRCS += kexec/arch/sh/kexec-sh.c
+sh_KEXEC_SRCS += kexec/arch/sh/kexec-uImage-sh.c
+sh_KEXEC_SRCS += kexec/arch/sh/kexec-zImage-sh.c
+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_UIMAGE = kexec/kexec-uImage.c
+
+sh_ADD_BUFFER =
+sh_ADD_SEGMENT =
+sh_VIRT_TO_PHYS =
+
+dist += kexec/arch/sh/Makefile $(sh_KEXEC_SRCS) \
+ kexec/arch/sh/kexec-sh.h \
+ kexec/arch/sh/crashdump-sh.h \
+ kexec/arch/sh/include/arch/options.h
diff --git a/kexec/arch/sh/crashdump-sh.c b/kexec/arch/sh/crashdump-sh.c
new file mode 100644
index 0000000..36e9aaf
--- /dev/null
+++ b/kexec/arch/sh/crashdump-sh.c
@@ -0,0 +1,166 @@
+/*
+ * 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];
+
+static int crash_sh_range_nr;
+static int crash_sh_memory_range_callback(void *UNUSED(data), int UNUSED(nr),
+ char *str,
+ unsigned long long base,
+ unsigned long 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 (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;
+
+ 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,
+};
+
+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);
+
+ dbgprintf("Command line after adding elfcorehdr: %s\n", cmdline);
+
+ 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;
+}
+
+int get_crash_kernel_load_range(uint64_t *start, uint64_t *end)
+{
+ return parse_iomem_single("Crash kernel\n", start, end);
+}
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/include/arch/options.h b/kexec/arch/sh/include/arch/options.h
new file mode 100644
index 0000000..f923eb4
--- /dev/null
+++ b/kexec/arch/sh/include/arch/options.h
@@ -0,0 +1,42 @@
+#ifndef KEXEC_ARCH_SH_OPTIONS_H
+#define KEXEC_ARCH_SH_OPTIONS_H
+
+#define OPT_ARCH_MAX (OPT_MAX+0)
+#define OPT_APPEND (OPT_ARCH_MAX+1)
+#define OPT_EMPTYZERO (OPT_ARCH_MAX+2)
+#define OPT_NBSD_HOWTO (OPT_ARCH_MAX+3)
+#define OPT_NBSD_MROOT (OPT_ARCH_MAX+4)
+
+/* Options relevant to the architecture (excluding loader-specific ones): */
+#define KEXEC_ARCH_OPTIONS \
+ KEXEC_OPTIONS \
+ {"command-line", 1, 0, OPT_APPEND}, \
+ {"append", 1, 0, OPT_APPEND}, \
+ {"empty-zero", 1, 0, OPT_APPEND}, \
+ {"howto", 1, 0, OPT_NBSD_HOWTO}, \
+ {"miniroot", 1, 0, OPT_NBSD_MROOT},
+/* These options seem to be loader-specific rather than superh-specific, so
+ * ought to be moved to KEXEC_ALL_OPTIONS below and parsed in the relevant
+ * loader, e.g. kexec-netbsd-sh.c
+ */
+
+#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR ""
+
+/* The following two #defines list ALL of the options added by all of the
+ * architecture's loaders.
+ * o main() uses this complete list to scan for its options, ignoring
+ * arch-specific/loader-specific ones.
+ * o Then, arch_process_options() uses this complete list to scan for its
+ * options, ignoring general/loader-specific ones.
+ * o Then, the file_type[n].load re-scans for options, using
+ * KEXEC_ARCH_OPTIONS plus its loader-specific options subset.
+ * Any unrecognised options cause an error here.
+ *
+ * This is done so that main()'s/arch_process_options()'s getopt_long() calls
+ * don't choose a kernel filename from random arguments to options they don't
+ * recognise -- as they now recognise (if not act upon) all possible options.
+ */
+#define KEXEC_ALL_OPTIONS KEXEC_ARCH_OPTIONS
+#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR
+
+#endif /* KEXEC_ARCH_SH_OPTIONS_H */
diff --git a/kexec/arch/sh/kexec-elf-rel-sh.c b/kexec/arch/sh/kexec-elf-rel-sh.c
new file mode 100644
index 0000000..3993ee8
--- /dev/null
+++ b/kexec/arch/sh/kexec-elf-rel-sh.c
@@ -0,0 +1,54 @@
+/*
+ * kexec-elf-rel-sh.c - ELF relocations for SuperH
+ * Copyright (C) 2008 Paul Mundt
+ *
+ * Based on the SHcompact module loader (arch/sh/kernel/module.c) in the
+ * Linux kernel, which is written by:
+ *
+ * Copyright (C) 2003 - 2008 Kaz Kojima & Paul Mundt
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+#include <stdio.h>
+#include <elf.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+
+int machine_verify_elf_rel(struct mem_ehdr *ehdr)
+{
+ /* Intentionally don't bother with endianness validation, it's
+ * configurable */
+
+ if (ehdr->ei_class != ELFCLASS32)
+ return 0;
+ if (ehdr->e_machine != EM_SH)
+ return 0;
+
+ return 1;
+}
+
+void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr),
+ struct mem_sym *UNUSED(sym), unsigned long r_type, void *orig_loc,
+ unsigned long UNUSED(address), unsigned long relocation)
+{
+ uint32_t *location = orig_loc;
+ uint32_t value;
+
+ switch (r_type) {
+ case R_SH_DIR32:
+ value = get_unaligned(location);
+ value += relocation;
+ put_unaligned(value, location);
+ break;
+ case R_SH_REL32:
+ relocation = (relocation - (uint32_t)location);
+ value = get_unaligned(location);
+ value += relocation;
+ put_unaligned(value, location);
+ break;
+ default:
+ die("Unknown rela relocation: %lu\n", r_type);
+ break;
+ }
+}
diff --git a/kexec/arch/sh/kexec-elf-sh.c b/kexec/arch/sh/kexec-elf-sh.c
new file mode 100644
index 0000000..897552c
--- /dev/null
+++ b/kexec/arch/sh/kexec-elf-sh.c
@@ -0,0 +1,136 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * Based on x86 implementation,
+ * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com)
+ *
+ * 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.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <elf.h>
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#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)
+{
+ struct mem_ehdr ehdr;
+ int result;
+ result = build_elf_exec_info(buf, len, &ehdr, 0);
+ if (result < 0)
+ goto out;
+
+ /* Verify the architecuture specific bits */
+ if (ehdr.e_machine != EM_SH) {
+ result = -1;
+ goto out;
+ }
+
+ result = 0;
+ out:
+ free_elf_info(&ehdr);
+ return result;
+}
+
+void elf_sh_usage(void)
+{
+ printf(" --append=STRING Set the kernel command line to STRING\n"
+ );
+}
+
+int elf_sh_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+{
+ struct mem_ehdr ehdr;
+ char *command_line;
+ char *modified_cmdline;
+ struct mem_sym sym;
+ int opt, rc;
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { 0, 0, 0, 0 },
+ };
+
+ static const char short_options[] = KEXEC_OPT_STR "";
+
+ /*
+ * Parse the command line arguments
+ */
+ command_line = modified_cmdline = 0;
+ while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch(opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX) {
+ break;
+ }
+ case OPT_APPEND:
+ command_line = optarg;
+ break;
+ }
+ }
+
+ /* Need to append some command line parameters internally in case of
+ * taking crash dumps.
+ */
+ if (info->kexec_flags & (KEXEC_ON_CRASH | KEXEC_PRESERVE_CONTEXT)) {
+ 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 *)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 | KEXEC_PRESERVE_CONTEXT)) {
+ 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) {
+ char *zp = (void *)ehdr.e_shdr[sym.st_shndx].sh_data;
+
+ kexec_sh_setup_zero_page(zp, 4096, command_line);
+ }
+
+ return 0;
+}
diff --git a/kexec/arch/sh/kexec-netbsd-sh.c b/kexec/arch/sh/kexec-netbsd-sh.c
new file mode 100644
index 0000000..ed93759
--- /dev/null
+++ b/kexec/arch/sh/kexec-netbsd-sh.c
@@ -0,0 +1,149 @@
+/*
+ * kexec-netbsd-sh.c - kexec netbsd loader for the SH
+ * Copyright (C) 2005 kogiidena@eggplant.ddo.jp
+ *
+ * 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 <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <elf.h>
+#include <boot/elf_boot.h>
+#include <ip_checksum.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include <arch/options.h>
+
+static const int probe_debug = 0;
+extern const unsigned char netbsd_booter[];
+
+/*
+ * netbsd_sh_probe - sanity check the elf image
+ *
+ * Make sure that the file image has a reasonable chance of working.
+ */
+int netbsd_sh_probe(const char *buf, off_t UNUSED(len))
+{
+ Elf32_Ehdr *ehdr;
+
+ ehdr = (Elf32_Ehdr *)buf;
+ if(memcmp(buf, ELFMAG, SELFMAG) != 0){
+ return -1;
+ }
+ if (ehdr->e_machine != EM_SH) {
+ return -1;
+ }
+ return 0;
+}
+
+void netbsd_sh_usage(void)
+{
+ printf(
+ " --howto=VALUE NetBSD kernel boot option.\n"
+ " --miniroot=FILE NetBSD miniroot ramdisk.\n\n");
+}
+
+int netbsd_sh_load(int argc, char **argv, const char *buf, off_t UNUSED(len),
+ struct kexec_info *info)
+{
+ const char *howto, *miniroot;
+ unsigned long entry, start, size, psz;
+ char *miniroot_buf;
+ off_t miniroot_length;
+ unsigned int howto_value;
+ unsigned char *param;
+ unsigned long *paraml;
+ unsigned char *img;
+
+ int opt;
+
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ {0, 0, 0, 0},
+ };
+
+ static const char short_options[] = KEXEC_ARCH_OPT_STR "";
+
+ howto = miniroot = 0;
+ while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch (opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX) {
+ break;
+ }
+ case OPT_NBSD_HOWTO:
+ howto = optarg;
+ break;
+ case OPT_NBSD_MROOT:
+ miniroot = optarg;
+ break;
+ }
+ }
+
+ /* howto */
+ howto_value = 0;
+ if(howto){
+ howto_value = strtol(howto, NULL, 0);
+ }
+
+ psz = getpagesize();
+
+ /* Parse the Elf file */
+ {
+ Elf32_Ehdr *ehdr;
+ Elf32_Phdr *phdr;
+ unsigned long bbs;
+ ehdr = (Elf32_Ehdr *)buf;
+ phdr = (Elf32_Phdr *)&buf[ehdr->e_phoff];
+
+ entry = ehdr->e_entry;
+ img = (unsigned char *)&buf[phdr->p_offset];
+ start = (phdr->p_paddr) & 0x1fffffff;
+ bbs = phdr->p_filesz;
+ size = phdr->p_memsz;
+
+ if(size < bbs){
+ size = bbs;
+ }
+
+ size = _ALIGN(size, psz);
+ memset(&img[bbs], 0, size-bbs);
+ add_segment(info, img, size, start, size);
+ start += size;
+ }
+
+ /* miniroot file */
+ miniroot_buf = 0;
+ if (miniroot) {
+ miniroot_buf = slurp_file(miniroot, &miniroot_length);
+ howto_value |= 0x200;
+ size = _ALIGN(miniroot_length, psz);
+ add_segment(info, miniroot_buf, size, start, size);
+ start += size;
+ }
+
+ /* howto & bootinfo */
+ param = xmalloc(4096);
+ memset(param, 0, 4096);
+ paraml = (unsigned long *) &param[256];
+ memcpy(param, netbsd_booter, 256);
+ paraml[0] = entry;
+ paraml[1] = howto_value;
+ add_segment(info, param, 4096, start, 4096);
+
+ /* For now we don't have arguments to pass :( */
+ info->entry = (void *) (start | 0xa0000000);
+ return 0;
+}
diff --git a/kexec/arch/sh/kexec-sh.c b/kexec/arch/sh/kexec-sh.c
new file mode 100644
index 0000000..ce341c8
--- /dev/null
+++ b/kexec/arch/sh/kexec-sh.c
@@ -0,0 +1,259 @@
+/*
+ * kexec-sh.c - kexec for the SH
+ * Copyright (C) 2004 kogiidena@eggplant.ddo.jp
+ *
+ * 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 "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "kexec-sh.h"
+#include <arch/options.h>
+
+#define MAX_MEMORY_RANGES 64
+static struct memory_range memory_range[MAX_MEMORY_RANGES];
+
+static int kexec_sh_memory_range_callback(void *UNUSED(data), int nr,
+ char *UNUSED(str),
+ unsigned long long base,
+ unsigned long long length)
+{
+ if (nr < MAX_MEMORY_RANGES) {
+ memory_range[nr].start = base;
+ memory_range[nr].end = base + length - 1;
+ memory_range[nr].type = RANGE_RAM;
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Return a sorted list of available memory ranges. */
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long kexec_flags)
+{
+ 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;
+}
+
+/* Supported file types and callbacks */
+struct file_type file_type[] = {
+ /* uImage is probed before zImage because the latter also accepts
+ uncompressed images. */
+ { "uImage-sh", uImage_sh_probe, uImage_sh_load, zImage_sh_usage },
+ { "zImage-sh", zImage_sh_probe, zImage_sh_load, zImage_sh_usage },
+ { "elf-sh", elf_sh_probe, elf_sh_load, elf_sh_usage },
+ { "netbsd-sh", netbsd_sh_probe, netbsd_sh_load, netbsd_sh_usage },
+};
+int file_types = sizeof(file_type) / sizeof(file_type[0]);
+
+
+void arch_usage(void)
+{
+
+ printf(
+ " none\n\n"
+ "Default options:\n"
+ " --append=\"%s\"\n"
+ " STRING of --append is set from /proc/cmdline as default.\n"
+ ,get_append());
+
+}
+
+int arch_process_options(int argc, char **argv)
+{
+ /* The common options amongst loaders (e.g. --append) should be read
+ * here, and the loader-specific options (e.g. NetBSD stuff) should
+ * then be re-parsed in the loader.
+ * (e.g. in kexec-netbsd-sh.c, for example.)
+ */
+ static const struct option options[] = {
+ KEXEC_ALL_OPTIONS
+ { 0, 0, NULL, 0 },
+ };
+ static const char short_options[] = KEXEC_ARCH_OPT_STR;
+ int opt;
+
+ opterr = 0; /* Don't complain about unrecognized options here */
+ while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch(opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_MAX) {
+ break;
+ }
+ case OPT_APPEND:
+ case OPT_NBSD_HOWTO:
+ case OPT_NBSD_MROOT:
+ ;
+ }
+ }
+ /* Reset getopt for the next pass; called in other source modules */
+ opterr = 1;
+ optind = 1;
+ return 0;
+}
+
+const struct arch_map_entry arches[] = {
+ /* For compatibility with older patches
+ * use KEXEC_ARCH_DEFAULT instead of KEXEC_ARCH_SH here.
+ */
+ { "sh3", KEXEC_ARCH_DEFAULT },
+ { "sh4", KEXEC_ARCH_DEFAULT },
+ { "sh4a", KEXEC_ARCH_DEFAULT },
+ { "sh4al-dsp", KEXEC_ARCH_DEFAULT },
+ { NULL, 0 },
+};
+
+int arch_compat_trampoline(struct kexec_info *UNUSED(info))
+{
+ return 0;
+}
+
+void arch_update_purgatory(struct kexec_info *UNUSED(info))
+{
+}
+
+char append_buf[256];
+
+char *get_append(void)
+{
+ FILE *fp;
+ int len;
+ if((fp = fopen("/proc/cmdline", "r")) == NULL){
+ die("/proc/cmdline file open error !!\n");
+ }
+ fgets(append_buf, 256, fp);
+ len = strlen(append_buf);
+ append_buf[len-1] = 0;
+ fclose(fp);
+ return append_buf;
+}
+
+void kexec_sh_setup_zero_page(char *zero_page_buf, size_t zero_page_size,
+ char *cmd_line)
+{
+ size_t n = zero_page_size - 0x100;
+
+ memset(zero_page_buf, 0, zero_page_size);
+
+ if (cmd_line) {
+ if (n > strlen(cmd_line))
+ n = strlen(cmd_line);
+
+ memcpy(zero_page_buf + 0x100, cmd_line, n);
+ zero_page_buf[0x100 + n] = '\0';
+ }
+}
+
+static int is_32bit(void)
+{
+ const char *cpuinfo = "/proc/cpuinfo";
+ char line[MAX_LINE];
+ FILE *fp;
+ int status = 0;
+
+ fp = fopen(cpuinfo, "r");
+ if (!fp)
+ die("Cannot open %s\n", cpuinfo);
+
+ while(fgets(line, sizeof(line), fp) != 0) {
+ const char *key = "address sizes";
+ const char *value = " 32 bits physical";
+ char *p;
+ if (strncmp(line, key, strlen(key)))
+ continue;
+ p = strchr(line + strlen(key), ':');
+ if (!p)
+ continue;
+ if (!strncmp(p + 1, value, strlen(value)))
+ status = 1;
+ break;
+ }
+
+ fclose(fp);
+
+ return status;
+}
+
+unsigned long virt_to_phys(unsigned long addr)
+{
+ unsigned long seg = addr & 0xe0000000;
+ unsigned long long start = 0;
+ int have_32bit = is_32bit();
+
+ if (seg != 0x80000000 && (have_32bit || seg != 0xc0000000))
+ die("Virtual address %p is not in P1%s\n", (void *)addr,
+ have_32bit ? "" : " or P2");
+
+ /* If 32bit addressing is used then the base of system RAM
+ * is an offset into physical memory. */
+ if (have_32bit) {
+ unsigned long long end;
+ int ret;
+
+ /* Assume there is only one "System RAM" region */
+ ret = parse_iomem_single("System RAM\n", &start, &end);
+ if (ret)
+ die("Could not parse System RAM region "
+ "in /proc/iomem\n");
+ }
+
+ return addr - seg + start;
+}
+
+/*
+ * add_segment() should convert base to a physical address on superh,
+ * while the default is just to work with base as is */
+void add_segment(struct kexec_info *info, const void *buf, size_t bufsz,
+ unsigned long base, size_t memsz)
+{
+ add_segment_phys_virt(info, buf, bufsz, base, memsz, 1);
+}
+
+/*
+ * add_buffer() should convert base to a physical address on superh,
+ * while the default is just to work with base as is */
+unsigned long add_buffer(struct kexec_info *info, const void *buf,
+ unsigned long bufsz, unsigned long memsz,
+ unsigned long buf_align, unsigned long buf_min,
+ unsigned long buf_max, int buf_end)
+{
+ return add_buffer_phys_virt(info, buf, bufsz, memsz, buf_align,
+ buf_min, buf_max, buf_end, 1);
+}
diff --git a/kexec/arch/sh/kexec-sh.h b/kexec/arch/sh/kexec-sh.h
new file mode 100644
index 0000000..7d28ade
--- /dev/null
+++ b/kexec/arch/sh/kexec-sh.h
@@ -0,0 +1,29 @@
+#ifndef KEXEC_SH_H
+#define KEXEC_SH_H
+
+#define COMMAND_LINE_SIZE 2048
+
+int uImage_sh_probe(const char *buf, off_t len);
+int uImage_sh_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+
+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);
+void zImage_sh_usage(void);
+
+int elf_sh_probe(const char *buf, off_t len);
+int elf_sh_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void elf_sh_usage(void);
+
+int netbsd_sh_probe(const char *buf, off_t len);
+int netbsd_sh_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void netbsd_sh_usage(void);
+
+char *get_append(void);
+void kexec_sh_setup_zero_page(char *zero_page_buf, size_t zero_page_size,
+ char *cmd_line);
+
+#endif /* KEXEC_SH_H */
diff --git a/kexec/arch/sh/kexec-uImage-sh.c b/kexec/arch/sh/kexec-uImage-sh.c
new file mode 100644
index 0000000..130f12c
--- /dev/null
+++ b/kexec/arch/sh/kexec-uImage-sh.c
@@ -0,0 +1,24 @@
+/*
+ * uImage support added by Marc Andre Tanner <mat@brain-dump.org>
+ *
+ * Cloned from ARM by Paul Mundt, 2009.
+ */
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <image.h>
+#include <kexec-uImage.h>
+#include "../../kexec.h"
+#include "kexec-sh.h"
+
+int uImage_sh_probe(const char *buf, off_t len)
+{
+ return uImage_probe_kernel(buf, len, IH_ARCH_SH);
+}
+
+int uImage_sh_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+{
+ return zImage_sh_load(argc, argv, buf + sizeof(struct image_header),
+ len - sizeof(struct image_header), info);
+}
diff --git a/kexec/arch/sh/kexec-zImage-sh.c b/kexec/arch/sh/kexec-zImage-sh.c
new file mode 100644
index 0000000..6dc2c13
--- /dev/null
+++ b/kexec/arch/sh/kexec-zImage-sh.c
@@ -0,0 +1,142 @@
+/*
+ * kexec-zImage-sh.c - kexec zImage loader for the SH
+ * Copyright (C) 2005 kogiidena@eggplant.ddo.jp
+ *
+ * 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 <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <elf.h>
+#include <boot/elf_boot.h>
+#include <ip_checksum.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include <arch/options.h>
+#include "kexec-sh.h"
+
+static const int probe_debug = 0;
+
+#define HEAD32_KERNEL_START_ADDR 0
+#define HEAD32_DECOMPRESS_KERNEL_ADDR 1
+#define HEAD32_INIT_STACK_ADDR 2
+#define HEAD32_INIT_SR 3
+#define HEAD32_INIT_SR_VALUE 0x400000F0
+
+static unsigned long zImage_head32(const char *buf, int offs)
+{
+ unsigned long *values = (void *)buf;
+ int k;
+
+ for (k = (0x200 / 4) - 1; k > 0; k--)
+ if (values[k] != 0x00090009) /* not nop + nop padding*/
+ return values[k - offs];
+
+ return 0;
+}
+
+/*
+ * zImage_sh_probe - sanity check the elf image
+ *
+ * Make sure that the file image has a reasonable chance of working.
+ */
+int zImage_sh_probe(const char *buf, off_t UNUSED(len))
+{
+ if (memcmp(&buf[0x202], "HdrS", 4) != 0)
+ return -1;
+
+ if (zImage_head32(buf, HEAD32_INIT_SR) != HEAD32_INIT_SR_VALUE)
+ return -1;
+
+ return 0;
+}
+
+void zImage_sh_usage(void)
+{
+ printf(
+ " --append=STRING Set the kernel command line to STRING.\n"
+ " --empty-zero=ADDRESS Set the kernel top ADDRESS. \n\n");
+
+}
+
+int zImage_sh_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+{
+ char *command_line;
+ int opt;
+ unsigned long empty_zero, zero_page_base, zero_page_size, k;
+ unsigned long image_base;
+ char *param;
+
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ {0, 0, 0, 0},
+ };
+
+ static const char short_options[] = KEXEC_ARCH_OPT_STR "";
+
+ command_line = 0;
+ while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch (opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX) {
+ break;
+ }
+ case OPT_APPEND:
+ command_line = optarg;
+ break;
+ }
+ }
+
+ if (!command_line)
+ command_line = get_append();
+
+ /* assume the zero page is the page before the vmlinux entry point.
+ * we don't know the page size though, but 64k seems to be max.
+ * put several 4k zero page copies before the entry point to cover
+ * all combinations.
+ */
+
+ empty_zero = zImage_head32(buf, HEAD32_KERNEL_START_ADDR);
+
+ zero_page_size = 0x10000;
+ zero_page_base = virt_to_phys(empty_zero - zero_page_size);
+
+ while (!valid_memory_range(info, zero_page_base,
+ zero_page_base + zero_page_size - 1)) {
+ zero_page_base += 0x1000;
+ zero_page_size -= 0x1000;
+ if (zero_page_size == 0)
+ die("Unable to determine zero page size from %p \n",
+ (void *)empty_zero);
+ }
+
+ param = xmalloc(zero_page_size);
+ for (k = 0; k < (zero_page_size / 0x1000); k++)
+ kexec_sh_setup_zero_page(param + (k * 0x1000), 0x1000,
+ command_line);
+
+ add_segment(info, param, zero_page_size,
+ 0x80000000 | zero_page_base, zero_page_size);
+
+ /* load image a bit above the zero page, round up to 64k
+ * the zImage will relocate itself, but only up seems supported.
+ */
+
+ image_base = _ALIGN(empty_zero, 0x10000);
+ add_segment(info, buf, len, image_base, len);
+ info->entry = (void *)virt_to_phys(image_base);
+ return 0;
+}
diff --git a/kexec/arch/sh/netbsd_booter.S b/kexec/arch/sh/netbsd_booter.S
new file mode 100644
index 0000000..d4d16df
--- /dev/null
+++ b/kexec/arch/sh/netbsd_booter.S
@@ -0,0 +1,47 @@
+ .globl netbsd_booter
+netbsd_booter:
+ mov.l ccr,r0
+ mov #0,r1
+ mov.l r1,@r0
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ mova netbsd_start,r0
+ mov.l @r0,r1
+ add #4,r0
+ mov.l @r0,r4 ! howto
+ add #4,r0
+ mov r0,r5 ! bootinfo
+ jmp @r1
+ nop
+ nop
+ nop
+ .align 4
+ccr: .long 0xff00001c
+
+ .align 8
+netbsd_start:
+ .long 0x8c001000
+ .long 0x200 ! howto
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+ .long 0 ! bootinfo
+