x86 setup: handle boot loaders which set up the stack incorrectly
authorH. Peter Anvin <hpa@zytor.com>
Thu, 25 Oct 2007 23:11:33 +0000 (16:11 -0700)
committerH. Peter Anvin <hpa@zytor.com>
Fri, 26 Oct 2007 02:55:03 +0000 (19:55 -0700)
Apparently some specific versions of LILO enter the kernel with a
stack pointer that doesn't match the rest of the segments.  Make our
best attempt at untangling the resulting mess.

Signed-off-by: H. Peter Anvin <hpa@zytor.com>
arch/x86/boot/boot.h
arch/x86/boot/header.S

index 5f9a2e72a731f48bc7b0565b89aebfc6e956c6d1..887874f4b49fa9ac900042fed96aae42467552b7 100644 (file)
@@ -17,6 +17,8 @@
 #ifndef BOOT_BOOT_H
 #define BOOT_BOOT_H
 
+#define STACK_SIZE     512     /* Minimum number of bytes for stack */
+
 #ifndef __ASSEMBLY__
 
 #include <stdarg.h>
@@ -198,8 +200,6 @@ static inline int isdigit(int ch)
 }
 
 /* Heap -- available for dynamic lists. */
-#define STACK_SIZE     512     /* Minimum number of bytes for stack */
-
 extern char _end[];
 extern char *HEAP;
 extern char *heap_end;
index 8353c81c41c02865384e227b24e4af8e0879bcee..6ef5a060fa11085df5be8d4616acd5541b53d5d2 100644 (file)
@@ -173,7 +173,8 @@ ramdisk_size:       .long   0               # its size in bytes
 bootsect_kludge:
                .long   0               # obsolete
 
-heap_end_ptr:  .word   _end+1024       # (Header version 0x0201 or later)
+heap_end_ptr:  .word   _end+STACK_SIZE-512
+                                       # (Header version 0x0201 or later)
                                        # space from here (exclusive) down to
                                        # end of setup code can be used by setup
                                        # for local heap purposes.
@@ -230,28 +231,53 @@ start_of_setup:
        int     $0x13
 #endif
 
-# We will have entered with %cs = %ds+0x20, normalize %cs so
-# it is on par with the other segments.
-       pushw   %ds
-       pushw   $setup2
-       lretw
-
-setup2:
 # Force %es = %ds
        movw    %ds, %ax
        movw    %ax, %es
        cld
 
-# Stack paranoia: align the stack and make sure it is good
-# for both 16- and 32-bit references.  In particular, if we
-# were meant to have been using the full 16-bit segment, the
-# caller might have set %sp to zero, which breaks %esp-based
-# references.
-       andw    $~3, %sp        # dword align (might as well...)
-       jnz     1f
-       movw    $0xfffc, %sp    # Make sure we're not zero
-1:     movzwl  %sp, %esp       # Clear upper half of %esp
-       sti
+# Apparently some ancient versions of LILO invoked the kernel
+# with %ss != %ds, which happened to work by accident for the
+# old code.  If the CAN_USE_HEAP flag is set in loadflags, or
+# %ss != %ds, then adjust the stack pointer.
+
+       # Smallest possible stack we can tolerate
+       movw    $(_end+STACK_SIZE), %cx
+
+       movw    heap_end_ptr, %dx
+       addw    $512, %dx
+       jnc     1f
+       xorw    %dx, %dx        # Wraparound - whole segment available
+1:     testb   $CAN_USE_HEAP, loadflags
+       jnz     2f
+
+       # No CAN_USE_HEAP
+       movw    %ss, %dx
+       cmpw    %ax, %dx        # %ds == %ss?
+       movw    %sp, %dx
+       # If so, assume %sp is reasonably set, otherwise use
+       # the smallest possible stack.
+       jne     4f              # -> Smallest possible stack...
+
+       # Make sure the stack is at least minimum size.  Take a value
+       # of zero to mean "full segment."
+2:
+       andw    $~3, %dx        # dword align (might as well...)
+       jnz     3f
+       movw    $0xfffc, %dx    # Make sure we're not zero
+3:     cmpw    %cx, %dx
+       jnb     5f
+4:     movw    %cx, %dx        # Minimum value we can possibly use
+5:     movw    %ax, %ss
+       movzwl  %dx, %esp       # Clear upper half of %esp
+       sti                     # Now we should have a working stack
+
+# We will have entered with %cs = %ds+0x20, normalize %cs so
+# it is on par with the other segments.
+       pushw   %ds
+       pushw   $6f
+       lretw
+6:
 
 # Check signature at end of setup
        cmpl    $0x5a5aaa55, setup_sig