diff options
Diffstat (limited to 'kexec/arch/sh/kexec-sh.c')
-rw-r--r-- | kexec/arch/sh/kexec-sh.c | 259 |
1 files changed, 259 insertions, 0 deletions
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); +} |