[RFC] bootwrapper: prevent fdt from being overwritten by kernel
Mark A. Greer
mgreer at mvista.com
Fri Oct 27 08:50:45 EST 2006
Paul,
If the fdt that's wrapped in a zImage isn't edited, it will still be
sitting in the zImage when the kernel boots
(i.e., at 0x400000 + <some offset into the zImage>).
That's okay until you get a large enough kernel (e.g., initramfs)
which will overwrite the fdt when the kernel is relocated to 0.
To handle that, I changed the sandpoint.c:platform_init() to base the
simple_alloc heap at the greater of either the end of the zImage or the end
of the kernel once its decompressed & relocated to 0. It then copies
the dtb to an area allocated by simple_alloc so I know its safe from
being overwritten.
In addition, I moved the vmlinux elf header decompress code from
prep_kernel() to its own routine and called it before platform_init.
I then pass the elf header ptr into platform_init. That way,
platform_init can figure out where the end of the kernel will
be and put the dtb beyond it. The OF code can now change
claim_base in platform_init instead of needing the platform_ops.image_hdr
hook.
Here is the patch that I'm talking about with the sandpoint piece there
for your reference.
If you're okay with the approach, let me know and I'll submit a real patch.
Mark
---
main.c | 14 +++++++++-----
of.c | 29 +++++++++++++----------------
ops.h | 4 ++--
sandpoint.c | 31 ++++++++++++++++++++++++++++---
4 files changed, 52 insertions(+), 26 deletions(-)
---
diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c
index 234bb5b..4927c17 100644
--- a/arch/powerpc/boot/main.c
+++ b/arch/powerpc/boot/main.c
@@ -169,7 +169,7 @@ static int is_elf32(void *hdr)
return 1;
}
-static void prep_kernel(unsigned long *a1, unsigned long *a2)
+static void get_elfheader(void)
{
int len;
@@ -186,11 +186,13 @@ static void prep_kernel(unsigned long *a
sizeof(elfheader));
if (!is_elf64(elfheader) && !is_elf32(elfheader)) {
- printf("Error: not a valid PPC32 or PPC64 ELF file!\n\r");
exit();
}
- if (platform_ops.image_hdr)
- platform_ops.image_hdr(elfheader);
+}
+
+static void prep_kernel(unsigned long *a1, unsigned long *a2)
+{
+ int len;
/* We need to alloc the memsize plus the file offset since gzip
* will expand the header (file offset), then the kernel, then
@@ -297,7 +299,9 @@ void start(unsigned long a1, unsigned lo
memset(&dt_ops, 0, sizeof(dt_ops));
memset(&console_ops, 0, sizeof(console_ops));
- if (platform_init(promptr, _dtb_start, _dtb_end))
+ get_elfheader();
+
+ if (platform_init(promptr, elfheader, _dtb_start, _dtb_end))
exit();
if (console_ops.open && (console_ops.open() < 0))
exit();
diff --git a/arch/powerpc/boot/of.c b/arch/powerpc/boot/of.c
index 0182f38..8a81212 100644
--- a/arch/powerpc/boot/of.c
+++ b/arch/powerpc/boot/of.c
@@ -194,20 +194,6 @@ #endif
return (void *)addr;
}
-static void of_image_hdr(const void *hdr)
-{
- const Elf64_Ehdr *elf64 = hdr;
-
- if (elf64->e_ident[EI_CLASS] == ELFCLASS64) {
- /*
- * Maintain a "magic" minimum address. This keeps some older
- * firmware platforms running.
- */
- if (claim_base < PROG_START)
- claim_base = PROG_START;
- }
-}
-
static void of_exit(void)
{
call_prom("exit", 0, 0);
@@ -256,9 +242,11 @@ static void of_console_write(char *buf,
call_prom("write", 3, 1, of_stdout_handle, buf, len);
}
-int platform_init(void *promptr, char *dt_blob_start, char *dt_blob_end)
+int platform_init(void *promptr, const void *elfhdr, char *dt_blob_start,
+ char *dt_blob_end)
{
- platform_ops.image_hdr = of_image_hdr;
+ const Elf64_Ehdr *elf64 = elfhdr;
+
platform_ops.malloc = of_try_claim;
platform_ops.exit = of_exit;
@@ -270,5 +258,14 @@ int platform_init(void *promptr, char *d
console_ops.write = of_console_write;
prom = (int (*)(void *))promptr;
+
+ if (elf64->e_ident[EI_CLASS] == ELFCLASS64) {
+ /*
+ * Maintain a "magic" minimum address. This keeps some older
+ * firmware platforms running.
+ */
+ if (claim_base < PROG_START)
+ claim_base = PROG_START;
+ }
return 0;
}
diff --git a/arch/powerpc/boot/ops.h b/arch/powerpc/boot/ops.h
index 59832fb..97ca5d7 100644
--- a/arch/powerpc/boot/ops.h
+++ b/arch/powerpc/boot/ops.h
@@ -20,7 +20,6 @@ #define MAX_PROP_LEN 256 /* What should
/* Platform specific operations */
struct platform_ops {
void (*fixups)(void);
- void (*image_hdr)(const void *);
void * (*malloc)(u32 size);
void (*free)(void *ptr);
void * (*realloc)(void *ptr, unsigned long size);
@@ -59,7 +58,8 @@ struct serial_console_data {
void (*close)(void);
};
-int platform_init(void *promptr, char *dt_blob_start, char *dt_blob_end);
+int platform_init(void *promptr, const void *elfhdr, char *dt_blob_start,
+ char *dt_blob_end);
int ft_init(void *dt_blob, unsigned int max_size, unsigned int max_find_device);
int serial_console_init(void);
int ns16550_console_init(void *devp, struct serial_console_data *scdp);
diff --git a/arch/powerpc/boot/sandpoint.c b/arch/powerpc/boot/sandpoint.c
index 496cafd..61217c4 100644
--- a/arch/powerpc/boot/sandpoint.c
+++ b/arch/powerpc/boot/sandpoint.c
@@ -12,6 +12,8 @@
#include <stdarg.h>
#include <stddef.h>
#include "types.h"
+#include "elf.h"
+#include "page.h"
#include "string.h"
#include "stdio.h"
#include "io.h"
@@ -139,17 +141,40 @@ static void sandpoint_reset(void)
#define HEAP_SIZE (16*MB)
-int platform_init(void *promptr, char *dt_blob_start, char *dt_blob_end)
+int platform_init(void *promptr, const void *elfhdr, char *dt_blob_start,
+ char *dt_blob_end)
{
+ const Elf32_Ehdr *elf32 = elfhdr;
+ Elf32_Phdr *elf32ph = (Elf32_Phdr *)((unsigned long)elf32
+ + elf32->e_phoff);
+ char *heap_start, *dtb;
int dt_size = dt_blob_end - dt_blob_start;
if (dt_size <= 0) /* No fdt */
goto err_out;
- if ((unsigned)simple_alloc_init(_end, HEAP_SIZE, 2*KB, 16) > (32*MB))
+ if (elf32->e_ident[EI_CLASS] != ELFCLASS32)
goto err_out;
- if (ft_init(dt_blob_start, dt_size, 16))
+ /* Start heap after end of the zImage or end of the kernel,
+ * whichever is higher. That's so things allocated by
+ * simple_alloc won't overwrite any part of the zImage or
+ * get overwritten when the early kernel code relocates
+ * the kernel to 0.
+ */
+ heap_start = (char *)_ALIGN(elf32ph->p_memsz + elf32ph->p_offset, 4096);
+ heap_start = max(heap_start, (char *)_end);
+
+ if ((unsigned)simple_alloc_init(heap_start, HEAP_SIZE, 2*KB, 16)
+ > (32*MB))
+ goto err_out;
+
+ /* Relocate dtb to safe area past end of zImage & kernel */
+ dtb = malloc(dt_size);
+ if (!dtb)
+ goto err_out;
+ memmove(dtb, dt_blob_start, dt_size);
+ if (ft_init(dtb, dt_size, 16))
goto err_out;
platform_ops.fixups = sandpoint_fixups;
More information about the Linuxppc-dev
mailing list