$(addprefix $(obj)/,$(zlib) main.o): $(addprefix $(obj)/,$(zliblinuxheader)) \
                $(addprefix $(obj)/,$(zlibheader))
 
-src-wlib := string.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \
+src-wlib := string.S crt0.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \
                ns16550.c serial.c simple_alloc.c div64.S util.S \
                gunzip_util.c $(zlib)
 src-plat := of.c
-src-boot := crt0.S $(src-wlib) $(src-plat) empty.c
+src-boot := $(src-wlib) $(src-plat) empty.c
 
 src-boot := $(addprefix $(obj)/, $(src-boot))
 obj-boot := $(addsuffix .o, $(basename $(src-boot)))
 
 hostprogs-y    := addnote addRamDisk hack-coff mktree
 
-extra-y                := $(obj)/crt0.o $(obj)/wrapper.a $(obj-plat) $(obj)/empty.o \
+extra-y                := $(obj)/wrapper.a $(obj-plat) $(obj)/empty.o \
                   $(obj)/zImage.lds $(obj)/zImage.coff.lds
 
 wrapper                :=$(srctree)/$(src)/wrapper
 
 _zimage_start_opd:
        .long   _zimage_start, 0, 0, 0
 
+       .weak   _zimage_start
        .globl  _zimage_start
 _zimage_start:
        /* Work out the offset between the address we were linked at
        addi    r9,r9,4
        bdnz    2b
 
-       /* Do a cache flush for our text, in case OF didn't */
+       /* Do a cache flush for our text, in case the loader didn't */
 3:     lis     r9,_start@ha
        addi    r9,r9,_start@l
        add     r9,r0,r9
        sync
        isync
 
-       mr      r6,r1
-       b       start
+       /* Clear the BSS */
+       lis     r9,__bss_start@ha
+       addi    r9,r9,__bss_start@l
+       lis     r8,_end@ha
+       addi    r8,r8,_end@l
+       li      r0,0
+5:     stw     r0,0(r9)
+       addi    r9,r9,4
+       cmplw   cr0,r9,r8
+       blt     5b
 
+       /* Possibly set up a custom stack */
+.weak  _platform_stack_top
+       lis     r8,_platform_stack_top@ha
+       addi    r8,r8,_platform_stack_top@l
+       cmpwi   r8,0
+       beq     6f
+       lwz     r1,0(r8)
+       li      r0,0
+       stwu    r0,-16(r1)      /* establish a stack frame */
+6:
+
+       /* Call platform_init() */
+       bl      platform_init
+
+       /* Call start */
+       mr      r3,r1
+       b       start
 
 struct platform_ops platform_ops;
 struct dt_ops dt_ops;
 struct console_ops console_ops;
+struct loader_info loader_info;
 
-void start(unsigned long a1, unsigned long a2, void *promptr, void *sp)
+void start(void *sp)
 {
        struct addr_range vmlinux, initrd;
        kernel_entry_t kentry;
        char cmdline[COMMAND_LINE_SIZE];
        unsigned long ft_addr = 0;
 
-       memset(__bss_start, 0, _end - __bss_start);
-       memset(&platform_ops, 0, sizeof(platform_ops));
-       memset(&dt_ops, 0, sizeof(dt_ops));
-       memset(&console_ops, 0, sizeof(console_ops));
-
-       if (platform_init(promptr, _dtb_start, _dtb_end))
-               exit();
        if (console_ops.open && (console_ops.open() < 0))
                exit();
        if (platform_ops.fixups)
               _start, sp);
 
        vmlinux = prep_kernel();
-       initrd = prep_initrd(vmlinux, a1, a2);
+       initrd = prep_initrd(vmlinux, loader_info.initrd_addr,
+                            loader_info.initrd_size);
 
        /* If cmdline came from zimage wrapper or if we can edit the one
         * in the dt, print it out and edit it, if possible.
        if (ft_addr)
                printf(" flat tree at 0x%lx\n\r", ft_addr);
        else
-               printf(" using OF tree (promptr=%p)\n\r", promptr);
+               printf(" using OF tree (promptr=%p)\n\r", loader_info.promptr);
 
        if (console_ops.close)
                console_ops.close();
        if (ft_addr)
                kentry(ft_addr, 0, NULL);
        else
-               kentry((unsigned long)initrd.addr, initrd.size, promptr);
+               kentry((unsigned long)initrd.addr, initrd.size,
+                      loader_info.promptr);
 
        /* console closed so printf below may not work */
        printf("Error: Linux kernel returned to zImage boot wrapper!\n\r");
 
        call_prom("write", 3, 1, of_stdout_handle, buf, len);
 }
 
-int platform_init(void *promptr, char *dt_blob_start, char *dt_blob_end)
+void platform_init(unsigned long a1, unsigned long a2, void *promptr)
 {
        platform_ops.image_hdr = of_image_hdr;
        platform_ops.malloc = of_try_claim;
        console_ops.write = of_console_write;
 
        prom = (int (*)(void *))promptr;
-       return 0;
+       loader_info.promptr = promptr;
+       loader_info.initrd_addr = a1;
+       loader_info.initrd_size = a2;
 }
 
        void            (*close)(void);
 };
 
-int platform_init(void *promptr, char *dt_blob_start, char *dt_blob_end);
+struct loader_info {
+       void *promptr;
+       unsigned long initrd_addr, initrd_size;
+};
+extern struct loader_info loader_info;
+
+void start(void *sp);
 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);
        for(;;);
 }
 
+#define BSS_STACK(size) \
+       static char _bss_stack[size]; \
+       void *_platform_stack_top = _bss_stack + sizeof(_bss_stack);
+
 #endif /* _PPC_BOOT_OPS_H_ */
 
 
 if [ "$platform" != "miboot" ]; then
     ${CROSS}ld -m elf32ppc -T $lds -o "$ofile" \
-       $object/crt0.o $platformo $tmp $object/wrapper.a
+       $platformo $tmp $object/wrapper.a
     rm $tmp
 fi
 
     $object/addnote "$ofile"
     ;;
 pmaccoff)
-    ${CROSS}objcopy -O aixcoff-rs6000 --set-start 0x500000 "$ofile"
+    entry=`objdump -f "$ofile" | grep '^start address ' | \
+       cut -d' ' -f3`
+    ${CROSS}objcopy -O aixcoff-rs6000 --set-start "$entry" "$ofile"
     $object/hack-coff "$ofile"
     ;;
 esac
 
 OUTPUT_ARCH(powerpc:common)
-ENTRY(_start)
+ENTRY(_zimage_start_opd)
+EXTERN(_zimage_start_opd)
 SECTIONS
 {
   . = (5*1024*1024);
 
 OUTPUT_ARCH(powerpc:common)
 ENTRY(_zimage_start)
+EXTERN(_zimage_start)
 SECTIONS
 {
   . = (4*1024*1024);