Merge ../linux-2.6-watchdog-mm
[pandora-kernel.git] / arch / i386 / boot / compressed / head.S
index b5893e4..3517a32 100644 (file)
 #include <linux/linkage.h>
 #include <asm/segment.h>
 #include <asm/page.h>
+#include <asm/boot.h>
 
+.section ".text.head","ax",@progbits
        .globl startup_32
-       
+
 startup_32:
        cld
        cli
@@ -37,93 +39,142 @@ startup_32:
        movl %eax,%es
        movl %eax,%fs
        movl %eax,%gs
+       movl %eax,%ss
 
-       lss stack_start,%esp
-       xorl %eax,%eax
-1:     incl %eax               # check that A20 really IS enabled
-       movl %eax,0x000000      # loop forever if it isn't
-       cmpl %eax,0x100000
-       je 1b
+/* Calculate the delta between where we were compiled to run
+ * at and where we were actually loaded at.  This can only be done
+ * with a short local call on x86.  Nothing  else will tell us what
+ * address we are running at.  The reserved chunk of the real-mode
+ * data at 0x34-0x3f are used as the stack for this calculation.
+ * Only 4 bytes are needed.
+ */
+       leal 0x40(%esi), %esp
+       call 1f
+1:     popl %ebp
+       subl $1b, %ebp
+
+/* %ebp contains the address we are loaded at by the boot loader and %ebx
+ * contains the address where we should move the kernel image temporarily
+ * for safe in-place decompression.
+ */
+
+#ifdef CONFIG_RELOCATABLE
+       movl    %ebp, %ebx
+       addl    $(CONFIG_PHYSICAL_ALIGN - 1), %ebx
+       andl    $(~(CONFIG_PHYSICAL_ALIGN - 1)), %ebx
+#else
+       movl $LOAD_PHYSICAL_ADDR, %ebx
+#endif
+
+       /* Replace the compressed data size with the uncompressed size */
+       subl input_len(%ebp), %ebx
+       movl output_len(%ebp), %eax
+       addl %eax, %ebx
+       /* Add 8 bytes for every 32K input block */
+       shrl $12, %eax
+       addl %eax, %ebx
+       /* Add 32K + 18 bytes of extra slack */
+       addl $(32768 + 18), %ebx
+       /* Align on a 4K boundary */
+       addl $4095, %ebx
+       andl $~4095, %ebx
+
+/* Copy the compressed kernel to the end of our buffer
+ * where decompression in place becomes safe.
+ */
+       pushl %esi
+       leal _end(%ebp), %esi
+       leal _end(%ebx), %edi
+       movl $(_end - startup_32), %ecx
+       std
+       rep
+       movsb
+       cld
+       popl %esi
+
+/* Compute the kernel start address.
+ */
+#ifdef CONFIG_RELOCATABLE
+       addl    $(CONFIG_PHYSICAL_ALIGN - 1), %ebp
+       andl    $(~(CONFIG_PHYSICAL_ALIGN - 1)), %ebp
+#else
+       movl    $LOAD_PHYSICAL_ADDR, %ebp
+#endif
 
 /*
- * Initialize eflags.  Some BIOS's leave bits like NT set.  This would
- * confuse the debugger if this code is traced.
- * XXX - best to initialize before switching to protected mode.
+ * Jump to the relocated address.
  */
-       pushl $0
-       popfl
+       leal relocated(%ebx), %eax
+       jmp *%eax
+.section ".text"
+relocated:
+
 /*
  * Clear BSS
  */
        xorl %eax,%eax
-       movl $_edata,%edi
-       movl $_end,%ecx
+       leal _edata(%ebx),%edi
+       leal _end(%ebx), %ecx
        subl %edi,%ecx
        cld
        rep
        stosb
+
+/*
+ * Setup the stack for the decompressor
+ */
+       leal stack_end(%ebx), %esp
+
 /*
  * Do the decompression, and jump to the new kernel..
  */
-       subl $16,%esp   # place for structure on the stack
-       movl %esp,%eax
+       movl output_len(%ebx), %eax
+       pushl %eax
+       pushl %ebp      # output address
+       movl input_len(%ebx), %eax
+       pushl %eax      # input_len
+       leal input_data(%ebx), %eax
+       pushl %eax      # input_data
+       leal _end(%ebx), %eax
+       pushl %eax      # end of the image as third argument
        pushl %esi      # real mode pointer as second arg
-       pushl %eax      # address of structure as first arg
        call decompress_kernel
-       orl  %eax,%eax 
-       jnz  3f
-       popl %esi       # discard address
-       popl %esi       # real mode pointer
-       xorl %ebx,%ebx
-       ljmp $(__BOOT_CS), $__PHYSICAL_START
+       addl $20, %esp
+       popl %ecx
 
+#if CONFIG_RELOCATABLE
+/* Find the address of the relocations.
+ */
+       movl %ebp, %edi
+       addl %ecx, %edi
+
+/* Calculate the delta between where vmlinux was compiled to run
+ * and where it was actually loaded.
+ */
+       movl %ebp, %ebx
+       subl $LOAD_PHYSICAL_ADDR, %ebx
+       jz   2f         /* Nothing to be done if loaded at compiled addr. */
 /*
- * We come here, if we were loaded high.
- * We need to move the move-in-place routine down to 0x1000
- * and then start it with the buffer addresses in registers,
- * which we got from the stack.
+ * Process relocations.
  */
-3:
-       movl $move_routine_start,%esi
-       movl $0x1000,%edi
-       movl $move_routine_end,%ecx
-       subl %esi,%ecx
-       addl $3,%ecx
-       shrl $2,%ecx
-       cld
-       rep
-       movsl
-
-       popl %esi       # discard the address
-       popl %ebx       # real mode pointer
-       popl %esi       # low_buffer_start
-       popl %ecx       # lcount
-       popl %edx       # high_buffer_start
-       popl %eax       # hcount
-       movl $__PHYSICAL_START,%edi
-       cli             # make sure we don't get interrupted
-       ljmp $(__BOOT_CS), $0x1000 # and jump to the move routine
+
+1:     subl $4, %edi
+       movl 0(%edi), %ecx
+       testl %ecx, %ecx
+       jz 2f
+       addl %ebx, -__PAGE_OFFSET(%ebx, %ecx)
+       jmp 1b
+2:
+#endif
 
 /*
- * Routine (template) for moving the decompressed kernel in place,
- * if we were high loaded. This _must_ PIC-code !
+ * Jump to the decompressed kernel.
  */
-move_routine_start:
-       movl %ecx,%ebp
-       shrl $2,%ecx
-       rep
-       movsl
-       movl %ebp,%ecx
-       andl $3,%ecx
-       rep
-       movsb
-       movl %edx,%esi
-       movl %eax,%ecx  # NOTE: rep movsb won't move if %ecx == 0
-       addl $3,%ecx
-       shrl $2,%ecx
-       rep
-       movsl
-       movl %ebx,%esi  # Restore setup pointer
        xorl %ebx,%ebx
-       ljmp $(__BOOT_CS), $__PHYSICAL_START
-move_routine_end:
+       jmp *%ebp
+
+.bss
+.balign 4
+stack:
+       .fill 4096, 1, 0
+stack_end: