summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMagnus Damm <damm@igel.co.jp>2008-08-27 17:32:31 +1000
committerSimon Horman <horms@verge.net.au>2008-08-27 17:32:31 +1000
commite64db1cf9213d0ef9ce3c3df0ce9e23b1c2d7604 (patch)
treef8818fa43e5481391680e82db9b9373c490bad32
parentded6432c225a10c6390c83463471977c18492fff (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.c40
-rw-r--r--kexec/arch/sh/kexec-sh.h3
-rw-r--r--kexec/arch/sh/kexec-zImage-sh.c77
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(&param[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;
}