diff options
author | Magnus Damm <damm@igel.co.jp> | 2008-08-27 17:32:31 +1000 |
---|---|---|
committer | Simon Horman <horms@verge.net.au> | 2008-08-27 17:32:31 +1000 |
commit | e64db1cf9213d0ef9ce3c3df0ce9e23b1c2d7604 (patch) | |
tree | f8818fa43e5481391680e82db9b9373c490bad32 | |
parent | ded6432c225a10c6390c83463471977c18492fff (diff) |
sh: Autodetect zImage zero page address
Autodetect the zero page base address for zImages on SuperH.
Signed-off-by: Magnus Damm <damm@igel.co.jp>
Acked-by: Paul Mundt <lethal@linux-sh.org>
Signed-off-by: Simon Horman <horms@verge.net.au>
-rw-r--r-- | kexec/arch/sh/kexec-sh.c | 40 | ||||
-rw-r--r-- | kexec/arch/sh/kexec-sh.h | 3 | ||||
-rw-r--r-- | kexec/arch/sh/kexec-zImage-sh.c | 77 |
3 files changed, 74 insertions, 46 deletions
diff --git a/kexec/arch/sh/kexec-sh.c b/kexec/arch/sh/kexec-sh.c index 117f3e8..b94d692 100644 --- a/kexec/arch/sh/kexec-sh.c +++ b/kexec/arch/sh/kexec-sh.c @@ -65,16 +65,8 @@ void arch_usage(void) " none\n\n" "Default options:\n" " --append=\"%s\"\n" - " --empty-zero=0x%08x\n\n" " STRING of --appned is set form /proc/cmdline as default.\n" - " ADDRESS of --empty-zero can be set SHELL environment variable\n" - " KEXEC_EMPTY_ZERO as default.\n\n" - " ADDRESS can be get in the following method in your system. \n" - " 1) \"grep empty_zero /proc/kallsyms\". \n" - " 2) \"grep empty_zero System.map\". \n" - " 3) CONFIG_MEMORY_START + CONFIG_ZERO_PAGE_OFFSET in your kernel\n" - " config file.\n" - ,get_append(), (unsigned int) get_empty_zero(NULL)); + ,get_append()); } @@ -130,21 +122,6 @@ void arch_update_purgatory(struct kexec_info *info) { } - -unsigned long get_empty_zero(char *s) -{ - char *env; - - env = getenv("KEXEC_EMPTY_ZERO"); - - if(s){ - env = s; - }else if(!env){ - env = "0x0c001000"; - } - return 0x1fffffff & strtoul(env,(char **)NULL,0); -} - char append_buf[256]; char *get_append(void) @@ -162,6 +139,21 @@ char *get_append(void) return append_buf; } +void kexec_sh_setup_zero_page(char *zero_page_buf, int zero_page_size, + char *cmd_line) +{ + int 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'; + } +} int is_crashkernel_mem_reserved(void) { diff --git a/kexec/arch/sh/kexec-sh.h b/kexec/arch/sh/kexec-sh.h index ee73d8c..2b89c5c 100644 --- a/kexec/arch/sh/kexec-sh.h +++ b/kexec/arch/sh/kexec-sh.h @@ -12,6 +12,7 @@ int netbsd_sh_load(int argc, char **argv, const char *buf, off_t len, void netbsd_sh_usage(void); char *get_append(void); -unsigned long get_empty_zero(char *s); +void kexec_sh_setup_zero_page(char *zero_page_buf, int zero_page_size, + char *cmd_line); #endif /* KEXEC_SH_H */ diff --git a/kexec/arch/sh/kexec-zImage-sh.c b/kexec/arch/sh/kexec-zImage-sh.c index 6db91a9..5e4e860 100644 --- a/kexec/arch/sh/kexec-zImage-sh.c +++ b/kexec/arch/sh/kexec-zImage-sh.c @@ -28,6 +28,24 @@ 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 + +unsigned long zImage_head32(const char *buf, off_t len, 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 * @@ -35,10 +53,12 @@ static const int probe_debug = 0; */ int zImage_sh_probe(const char *buf, off_t len) { - if (memcmp(&buf[0x202], "HdrS", 4) != 0) { - fprintf(stderr, "Not a zImage\n"); + if (memcmp(&buf[0x202], "HdrS", 4) != 0) return -1; - } + + if (zImage_head32(buf, len, HEAD32_INIT_SR) != HEAD32_INIT_SR_VALUE) + return -1; + return 0; } @@ -54,10 +74,9 @@ 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, area; - unsigned char *param; - unsigned long *paraml; + int opt, k; + unsigned long empty_zero, area, zero_page_base, zero_page_size; + char *param; static const struct option options[] = { KEXEC_ARCH_OPTIONS @@ -67,7 +86,6 @@ int zImage_sh_load(int argc, char **argv, const char *buf, off_t len, static const char short_options[] = KEXEC_ARCH_OPT_STR ""; command_line = 0; - empty_zero = get_empty_zero(NULL); while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { switch (opt) { default: @@ -81,25 +99,42 @@ int zImage_sh_load(int argc, char **argv, const char *buf, off_t len, case OPT_APPEND: command_line = optarg; break; - case OPT_EMPTYZERO: - empty_zero = get_empty_zero(optarg); - break; } } - param = xmalloc(4096); - memset(param, 0, 4096); - area = empty_zero & 0x1c000000; - if (!command_line) { + + 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, len, 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); } - strncpy(¶m[256], command_line, strlen(command_line)); - paraml = (unsigned long *)param; - // paraml[0] = 1; // readonly flag is set as default - add_segment(info, param, 4096, 0x80000000 | empty_zero, 4096); - add_segment(info, buf, len, (area | 0x80210000), len); + 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); - /* For now we don't have arguments to pass :( */ + add_segment(info, param, zero_page_size, + 0x80000000 | zero_page_base, zero_page_size); + + area = empty_zero & 0x1c000000; + add_segment(info, buf, len, (area | 0x80210000), len); info->entry = (void *)(0x80210000 | area); return 0; } |