/* * 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 #include #include #include #include #include #include #include "../../kexec.h" #include "../../kexec-syscall.h" #include "kexec-sh.h" #include #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); }