x86-32: Make sure the stack is set up before we use it

Since checkin ebba638ae723d8a8fc2f7abce5ec18b688b791d7 we call
verify_cpu even in 32-bit mode. Unfortunately, calling a function
means using the stack, and the stack pointer was not initialized in
the 32-bit setup code! This code initializes the stack pointer, and
simplifies the interface slightly since it is easier to rely on just a
pointer value rather than a descriptor; we need to have different
values for the segment register anyway.

This retains start_stack as a virtual address, even though a physical
address would be more convenient for 32 bits; the 64-bit code wants
the other way around...

Reported-by: Matthieu Castet <castet.matthieu@free.fr>
LKML-Reference: <4D41E86D.8060205@free.fr>
Tested-by: Kees Cook <kees.cook@canonical.com>
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>

authored by H. Peter Anvin and committed by H. Peter Anvin 11d4c3f9 f7448548

+17 -24
+1 -4
arch/x86/include/asm/smp.h
··· 40 DECLARE_EARLY_PER_CPU(u16, x86_bios_cpu_apicid); 41 42 /* Static state in head.S used to set up a CPU */ 43 - extern struct { 44 - void *sp; 45 - unsigned short ss; 46 - } stack_start; 47 48 struct smp_ops { 49 void (*smp_prepare_boot_cpu)(void);
··· 40 DECLARE_EARLY_PER_CPU(u16, x86_bios_cpu_apicid); 41 42 /* Static state in head.S used to set up a CPU */ 43 + extern unsigned long stack_start; /* Initial stack pointer address */ 44 45 struct smp_ops { 46 void (*smp_prepare_boot_cpu)(void);
+1 -1
arch/x86/kernel/acpi/sleep.c
··· 100 #else /* CONFIG_64BIT */ 101 header->trampoline_segment = setup_trampoline() >> 4; 102 #ifdef CONFIG_SMP 103 - stack_start.sp = temp_stack + sizeof(temp_stack); 104 early_gdt_descr.address = 105 (unsigned long)get_cpu_gdt_table(smp_processor_id()); 106 initial_gs = per_cpu_offset(smp_processor_id());
··· 100 #else /* CONFIG_64BIT */ 101 header->trampoline_segment = setup_trampoline() >> 4; 102 #ifdef CONFIG_SMP 103 + stack_start = (unsigned long)temp_stack + sizeof(temp_stack); 104 early_gdt_descr.address = 105 (unsigned long)get_cpu_gdt_table(smp_processor_id()); 106 initial_gs = per_cpu_offset(smp_processor_id());
+13 -17
arch/x86/kernel/head_32.S
··· 85 */ 86 __HEAD 87 ENTRY(startup_32) 88 /* test KEEP_SEGMENTS flag to see if the bootloader is asking 89 us to not reload segments */ 90 testb $(1<<6), BP_loadflags(%esi) ··· 101 movl %eax,%es 102 movl %eax,%fs 103 movl %eax,%gs 104 2: 105 106 /* 107 * Clear BSS first so that there are no surprises... ··· 149 * _brk_end is set up to point to the first "safe" location. 150 * Mappings are created both at virtual address 0 (identity mapping) 151 * and PAGE_OFFSET for up to _end. 152 - * 153 - * Note that the stack is not yet set up! 154 */ 155 #ifdef CONFIG_X86_PAE 156 ··· 284 movl %eax,%es 285 movl %eax,%fs 286 movl %eax,%gs 287 #endif /* CONFIG_SMP */ 288 default_entry: 289 ··· 352 movl %eax,%cr0 /* ..and set paging (PG) bit */ 353 ljmp $__BOOT_CS,$1f /* Clear prefetch and normalize %eip */ 354 1: 355 - /* Set up the stack pointer */ 356 - lss stack_start,%esp 357 358 /* 359 * Initialize eflags. Some BIOS's leave bits like NT set. This would ··· 365 366 #ifdef CONFIG_SMP 367 cmpb $0, ready 368 - jz 1f /* Initial CPU cleans BSS */ 369 - jmp checkCPUtype 370 - 1: 371 #endif /* CONFIG_SMP */ 372 373 /* ··· 473 474 cld # gcc2 wants the direction flag cleared at all times 475 pushl $0 # fake return address for unwinder 476 - #ifdef CONFIG_SMP 477 - movb ready, %cl 478 movb $1, ready 479 - cmpb $0,%cl # the first CPU calls start_kernel 480 - je 1f 481 - movl (stack_start), %esp 482 - 1: 483 - #endif /* CONFIG_SMP */ 484 jmp *(initial_code) 485 486 /* ··· 666 #endif 667 668 .data 669 ENTRY(stack_start) 670 .long init_thread_union+THREAD_SIZE 671 - .long __BOOT_DS 672 - 673 - ready: .byte 0 674 675 early_recursion_flag: 676 .long 0 677 678 int_msg: 679 .asciz "Unknown interrupt or fault at: %p %p %p\n"
··· 85 */ 86 __HEAD 87 ENTRY(startup_32) 88 + movl pa(stack_start),%ecx 89 + 90 /* test KEEP_SEGMENTS flag to see if the bootloader is asking 91 us to not reload segments */ 92 testb $(1<<6), BP_loadflags(%esi) ··· 99 movl %eax,%es 100 movl %eax,%fs 101 movl %eax,%gs 102 + movl %eax,%ss 103 2: 104 + leal -__PAGE_OFFSET(%ecx),%esp 105 106 /* 107 * Clear BSS first so that there are no surprises... ··· 145 * _brk_end is set up to point to the first "safe" location. 146 * Mappings are created both at virtual address 0 (identity mapping) 147 * and PAGE_OFFSET for up to _end. 148 */ 149 #ifdef CONFIG_X86_PAE 150 ··· 282 movl %eax,%es 283 movl %eax,%fs 284 movl %eax,%gs 285 + movl pa(stack_start),%ecx 286 + movl %eax,%ss 287 + leal -__PAGE_OFFSET(%ecx),%esp 288 #endif /* CONFIG_SMP */ 289 default_entry: 290 ··· 347 movl %eax,%cr0 /* ..and set paging (PG) bit */ 348 ljmp $__BOOT_CS,$1f /* Clear prefetch and normalize %eip */ 349 1: 350 + /* Shift the stack pointer to a virtual address */ 351 + addl $__PAGE_OFFSET, %esp 352 353 /* 354 * Initialize eflags. Some BIOS's leave bits like NT set. This would ··· 360 361 #ifdef CONFIG_SMP 362 cmpb $0, ready 363 + jnz checkCPUtype 364 #endif /* CONFIG_SMP */ 365 366 /* ··· 470 471 cld # gcc2 wants the direction flag cleared at all times 472 pushl $0 # fake return address for unwinder 473 movb $1, ready 474 jmp *(initial_code) 475 476 /* ··· 670 #endif 671 672 .data 673 + .balign 4 674 ENTRY(stack_start) 675 .long init_thread_union+THREAD_SIZE 676 677 early_recursion_flag: 678 .long 0 679 + 680 + ready: .byte 0 681 682 int_msg: 683 .asciz "Unknown interrupt or fault at: %p %p %p\n"
+2 -2
arch/x86/kernel/smpboot.c
··· 638 * target processor state. 639 */ 640 startup_ipi_hook(phys_apicid, (unsigned long) start_secondary, 641 - (unsigned long)stack_start.sp); 642 643 /* 644 * Run STARTUP IPI loop. ··· 785 #endif 786 early_gdt_descr.address = (unsigned long)get_cpu_gdt_table(cpu); 787 initial_code = (unsigned long)start_secondary; 788 - stack_start.sp = (void *) c_idle.idle->thread.sp; 789 790 /* start_ip had better be page-aligned! */ 791 start_ip = setup_trampoline();
··· 638 * target processor state. 639 */ 640 startup_ipi_hook(phys_apicid, (unsigned long) start_secondary, 641 + stack_start); 642 643 /* 644 * Run STARTUP IPI loop. ··· 785 #endif 786 early_gdt_descr.address = (unsigned long)get_cpu_gdt_table(cpu); 787 initial_code = (unsigned long)start_secondary; 788 + stack_start = c_idle.idle->thread.sp; 789 790 /* start_ip had better be page-aligned! */ 791 start_ip = setup_trampoline();