diff options
author | H. Peter Anvin <hpa@zytor.com> | 2007-10-25 16:11:33 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-11-16 08:22:58 -0800 |
commit | 430bb2ee8d136eae9d5641a23e44c791b6d747f0 (patch) | |
tree | d408542c7155f80c751e49af610867be4b47f650 /arch | |
parent | 4b69ffe37490d0507375fbe31be833310f883a24 (diff) |
x86 setup: handle boot loaders which set up the stack incorrectly
patch 6b6815c6d5d1dc209701d1661a7a0e09a295db2f in mainline.
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>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/i386/boot/boot.h | 4 | ||||
-rw-r--r-- | arch/i386/boot/header.S | 62 |
2 files changed, 46 insertions, 20 deletions
diff --git a/arch/i386/boot/boot.h b/arch/i386/boot/boot.h index 20bab9431acb..fb3c40bb02a4 100644 --- a/arch/i386/boot/boot.h +++ b/arch/i386/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/i386/boot/header.S b/arch/i386/boot/header.S index f3140e596d40..fff705935657 100644 --- a/arch/i386/boot/header.S +++ b/arch/i386/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. @@ -225,28 +226,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 |