diff options
author | H. Peter Anvin <hpa@zytor.com> | 2007-10-25 19:11:33 -0400 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2007-10-25 22:55:03 -0400 |
commit | 6b6815c6d5d1dc209701d1661a7a0e09a295db2f (patch) | |
tree | 9e1c07acbc4926279fcef068efb60141c6eed2af | |
parent | c9927c2bf4f45bb85e8b502ab3fb79ad6483c244 (diff) |
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>
-rw-r--r-- | arch/x86/boot/boot.h | 4 | ||||
-rw-r--r-- | arch/x86/boot/header.S | 62 |
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 @@ | |||
17 | #ifndef BOOT_BOOT_H | 17 | #ifndef BOOT_BOOT_H |
18 | #define BOOT_BOOT_H | 18 | #define BOOT_BOOT_H |
19 | 19 | ||
20 | #define STACK_SIZE 512 /* Minimum number of bytes for stack */ | ||
21 | |||
20 | #ifndef __ASSEMBLY__ | 22 | #ifndef __ASSEMBLY__ |
21 | 23 | ||
22 | #include <stdarg.h> | 24 | #include <stdarg.h> |
@@ -198,8 +200,6 @@ static inline int isdigit(int ch) | |||
198 | } | 200 | } |
199 | 201 | ||
200 | /* Heap -- available for dynamic lists. */ | 202 | /* Heap -- available for dynamic lists. */ |
201 | #define STACK_SIZE 512 /* Minimum number of bytes for stack */ | ||
202 | |||
203 | extern char _end[]; | 203 | extern char _end[]; |
204 | extern char *HEAP; | 204 | extern char *HEAP; |
205 | extern char *heap_end; | 205 | 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 | |||
173 | bootsect_kludge: | 173 | bootsect_kludge: |
174 | .long 0 # obsolete | 174 | .long 0 # obsolete |
175 | 175 | ||
176 | heap_end_ptr: .word _end+1024 # (Header version 0x0201 or later) | 176 | heap_end_ptr: .word _end+STACK_SIZE-512 |
177 | # (Header version 0x0201 or later) | ||
177 | # space from here (exclusive) down to | 178 | # space from here (exclusive) down to |
178 | # end of setup code can be used by setup | 179 | # end of setup code can be used by setup |
179 | # for local heap purposes. | 180 | # for local heap purposes. |
@@ -230,28 +231,53 @@ start_of_setup: | |||
230 | int $0x13 | 231 | int $0x13 |
231 | #endif | 232 | #endif |
232 | 233 | ||
233 | # We will have entered with %cs = %ds+0x20, normalize %cs so | ||
234 | # it is on par with the other segments. | ||
235 | pushw %ds | ||
236 | pushw $setup2 | ||
237 | lretw | ||
238 | |||
239 | setup2: | ||
240 | # Force %es = %ds | 234 | # Force %es = %ds |
241 | movw %ds, %ax | 235 | movw %ds, %ax |
242 | movw %ax, %es | 236 | movw %ax, %es |
243 | cld | 237 | cld |
244 | 238 | ||
245 | # Stack paranoia: align the stack and make sure it is good | 239 | # Apparently some ancient versions of LILO invoked the kernel |
246 | # for both 16- and 32-bit references. In particular, if we | 240 | # with %ss != %ds, which happened to work by accident for the |
247 | # were meant to have been using the full 16-bit segment, the | 241 | # old code. If the CAN_USE_HEAP flag is set in loadflags, or |
248 | # caller might have set %sp to zero, which breaks %esp-based | 242 | # %ss != %ds, then adjust the stack pointer. |
249 | # references. | 243 | |
250 | andw $~3, %sp # dword align (might as well...) | 244 | # Smallest possible stack we can tolerate |
251 | jnz 1f | 245 | movw $(_end+STACK_SIZE), %cx |
252 | movw $0xfffc, %sp # Make sure we're not zero | 246 | |
253 | 1: movzwl %sp, %esp # Clear upper half of %esp | 247 | movw heap_end_ptr, %dx |
254 | sti | 248 | addw $512, %dx |
249 | jnc 1f | ||
250 | xorw %dx, %dx # Wraparound - whole segment available | ||
251 | 1: testb $CAN_USE_HEAP, loadflags | ||
252 | jnz 2f | ||
253 | |||
254 | # No CAN_USE_HEAP | ||
255 | movw %ss, %dx | ||
256 | cmpw %ax, %dx # %ds == %ss? | ||
257 | movw %sp, %dx | ||
258 | # If so, assume %sp is reasonably set, otherwise use | ||
259 | # the smallest possible stack. | ||
260 | jne 4f # -> Smallest possible stack... | ||
261 | |||
262 | # Make sure the stack is at least minimum size. Take a value | ||
263 | # of zero to mean "full segment." | ||
264 | 2: | ||
265 | andw $~3, %dx # dword align (might as well...) | ||
266 | jnz 3f | ||
267 | movw $0xfffc, %dx # Make sure we're not zero | ||
268 | 3: cmpw %cx, %dx | ||
269 | jnb 5f | ||
270 | 4: movw %cx, %dx # Minimum value we can possibly use | ||
271 | 5: movw %ax, %ss | ||
272 | movzwl %dx, %esp # Clear upper half of %esp | ||
273 | sti # Now we should have a working stack | ||
274 | |||
275 | # We will have entered with %cs = %ds+0x20, normalize %cs so | ||
276 | # it is on par with the other segments. | ||
277 | pushw %ds | ||
278 | pushw $6f | ||
279 | lretw | ||
280 | 6: | ||
255 | 281 | ||
256 | # Check signature at end of setup | 282 | # Check signature at end of setup |
257 | cmpl $0x5a5aaa55, setup_sig | 283 | cmpl $0x5a5aaa55, setup_sig |