summaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2007-10-25 16:11:33 -0700
committerH. Peter Anvin <hpa@zytor.com>2007-10-25 19:55:03 -0700
commit6b6815c6d5d1dc209701d1661a7a0e09a295db2f (patch)
tree9e1c07acbc4926279fcef068efb60141c6eed2af /arch
parentc9927c2bf4f45bb85e8b502ab3fb79ad6483c244 (diff)
downloadlinux-6b6815c6d5d1dc209701d1661a7a0e09a295db2f.tar.gz
linux-6b6815c6d5d1dc209701d1661a7a0e09a295db2f.tar.bz2
linux-6b6815c6d5d1dc209701d1661a7a0e09a295db2f.zip
x86 setup: handle boot loaders which set up the stack incorrectly
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>
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/boot/boot.h4
-rw-r--r--arch/x86/boot/header.S62
2 files changed, 46 insertions, 20 deletions
diff --git a/arch/x86/boot/boot.h b/arch/x86/boot/boot.h
index 5f9a2e72a731..887874f4b49f 100644
--- a/arch/x86/boot/boot.h
+++ b/arch/x86/boot/boot.h
@@ -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;
diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S
index 8353c81c41c0..6ef5a060fa11 100644
--- a/arch/x86/boot/header.S
+++ b/arch/x86/boot/header.S
@@ -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