Merge branch 'master' into upstream
[pandora-kernel.git] / arch / i386 / kernel / head.S
index a6b8bd8..3fa7f93 100644 (file)
  * any particular GDT layout, because we load our own as soon as we
  * can.
  */
+.section .text.head,"ax",@progbits
 ENTRY(startup_32)
 
+#ifdef CONFIG_PARAVIRT
+        movl %cs, %eax
+        testl $0x3, %eax
+        jnz startup_paravirt
+#endif
+
 /*
  * Set segments to known values.
  */
@@ -97,7 +104,7 @@ ENTRY(startup_32)
        movzwl OLD_CL_OFFSET,%esi
        addl $(OLD_CL_BASE_ADDR),%esi
 2:
-       movl $(saved_command_line - __PAGE_OFFSET),%edi
+       movl $(boot_command_line - __PAGE_OFFSET),%edi
        movl $(COMMAND_LINE_SIZE/4),%ecx
        rep
        movsl
@@ -135,16 +142,25 @@ page_pde_offset = (__PAGE_OFFSET >> 20);
        jb 10b
        movl %edi,(init_pg_tables_end - __PAGE_OFFSET)
 
-#ifdef CONFIG_SMP
        xorl %ebx,%ebx                          /* This is the boot CPU (BSP) */
        jmp 3f
-
 /*
  * Non-boot CPU entry point; entered from trampoline.S
  * We can't lgdt here, because lgdt itself uses a data segment, but
  * we know the trampoline has already loaded the boot_gdt_table GDT
  * for us.
+ *
+ * If cpu hotplug is not supported then this code can go in init section
+ * which will be freed later
  */
+
+#ifdef CONFIG_HOTPLUG_CPU
+.section .text,"ax",@progbits
+#else
+.section .init.text,"ax",@progbits
+#endif
+
+#ifdef CONFIG_SMP
 ENTRY(startup_32_smp)
        cld
        movl $(__BOOT_DS),%eax
@@ -202,8 +218,8 @@ ENTRY(startup_32_smp)
        xorl %ebx,%ebx
        incl %ebx
 
-3:
 #endif /* CONFIG_SMP */
+3:
 
 /*
  * Enable paging
@@ -302,7 +318,8 @@ is386:      movl $2,%ecx            # set MP
        movl %eax,%cr0
 
        call check_x87
-       lgdt cpu_gdt_descr
+       call setup_pda
+       lgdt early_gdt_descr
        lidt idt_descr
        ljmp $(__KERNEL_CS),$1f
 1:     movl $(__KERNEL_DS),%eax        # reload all the segment registers
@@ -312,12 +329,15 @@ is386:    movl $2,%ecx            # set MP
        movl %eax,%ds
        movl %eax,%es
 
-       xorl %eax,%eax                  # Clear FS/GS and LDT
-       movl %eax,%fs
+       xorl %eax,%eax                  # Clear GS and LDT
        movl %eax,%gs
        lldt %ax
+
+       movl $(__KERNEL_PDA),%eax
+       mov  %eax,%fs
+
        cld                     # gcc2 wants the direction flag cleared at all times
-       pushl %eax              # fake return address
+       pushl $0                # fake return address for unwinder
 #ifdef CONFIG_SMP
        movb ready, %cl
        movb $1, ready
@@ -345,6 +365,23 @@ check_x87:
        .byte 0xDB,0xE4         /* fsetpm for 287, ignored by 387 */
        ret
 
+/*
+ * Point the GDT at this CPU's PDA.  On boot this will be
+ * cpu_gdt_table and boot_pda; for secondary CPUs, these will be
+ * that CPU's GDT and PDA.
+ */
+ENTRY(setup_pda)
+       /* get the PDA pointer */
+       movl start_pda, %eax
+
+       /* slot the PDA address into the GDT */
+       mov early_gdt_descr+2, %ecx
+       mov %ax, (__KERNEL_PDA+0+2)(%ecx)               /* base & 0x0000ffff */
+       shr $16, %eax
+       mov %al, (__KERNEL_PDA+4+0)(%ecx)               /* base & 0x00ff0000 */
+       mov %ah, (__KERNEL_PDA+4+3)(%ecx)               /* base & 0xff000000 */
+       ret
+
 /*
  *  setup_idt
  *
@@ -371,8 +408,65 @@ rp_sidt:
        addl $8,%edi
        dec %ecx
        jne rp_sidt
+
+.macro set_early_handler handler,trapno
+       lea \handler,%edx
+       movl $(__KERNEL_CS << 16),%eax
+       movw %dx,%ax
+       movw $0x8E00,%dx        /* interrupt gate - dpl=0, present */
+       lea idt_table,%edi
+       movl %eax,8*\trapno(%edi)
+       movl %edx,8*\trapno+4(%edi)
+.endm
+
+       set_early_handler handler=early_divide_err,trapno=0
+       set_early_handler handler=early_illegal_opcode,trapno=6
+       set_early_handler handler=early_protection_fault,trapno=13
+       set_early_handler handler=early_page_fault,trapno=14
+
        ret
 
+early_divide_err:
+       xor %edx,%edx
+       pushl $0        /* fake errcode */
+       jmp early_fault
+
+early_illegal_opcode:
+       movl $6,%edx
+       pushl $0        /* fake errcode */
+       jmp early_fault
+
+early_protection_fault:
+       movl $13,%edx
+       jmp early_fault
+
+early_page_fault:
+       movl $14,%edx
+       jmp early_fault
+
+early_fault:
+       cld
+#ifdef CONFIG_PRINTK
+       movl $(__KERNEL_DS),%eax
+       movl %eax,%ds
+       movl %eax,%es
+       cmpl $2,early_recursion_flag
+       je hlt_loop
+       incl early_recursion_flag
+       movl %cr2,%eax
+       pushl %eax
+       pushl %edx              /* trapno */
+       pushl $fault_msg
+#ifdef CONFIG_EARLY_PRINTK
+       call early_printk
+#else
+       call printk
+#endif
+#endif
+hlt_loop:
+       hlt
+       jmp hlt_loop
+
 /* This is the default interrupt "handler" :-) */
        ALIGN
 ignore_int:
@@ -386,6 +480,9 @@ ignore_int:
        movl $(__KERNEL_DS),%eax
        movl %eax,%ds
        movl %eax,%es
+       cmpl $2,early_recursion_flag
+       je hlt_loop
+       incl early_recursion_flag
        pushl 16(%esp)
        pushl 24(%esp)
        pushl 32(%esp)
@@ -405,6 +502,39 @@ ignore_int:
 #endif
        iret
 
+.section .text
+#ifdef CONFIG_PARAVIRT
+startup_paravirt:
+       cld
+       movl $(init_thread_union+THREAD_SIZE),%esp
+
+       /* We take pains to preserve all the regs. */
+       pushl   %edx
+       pushl   %ecx
+       pushl   %eax
+
+       pushl   $__start_paravirtprobe
+1:
+       movl    0(%esp), %eax
+       cmpl    $__stop_paravirtprobe, %eax
+       je      unhandled_paravirt
+       pushl   (%eax)
+       movl    8(%esp), %eax
+       call    *(%esp)
+       popl    %eax
+
+       movl    4(%esp), %eax
+       movl    8(%esp), %ecx
+       movl    12(%esp), %edx
+
+       addl    $4, (%esp)
+       jmp     1b
+
+unhandled_paravirt:
+       /* Nothing wanted us: we're screwed. */
+       ud2
+#endif
+
 /*
  * Real beginning of normal "text" segment
  */
@@ -424,6 +554,8 @@ ENTRY(empty_zero_page)
  * This starts the data section.
  */
 .data
+ENTRY(start_pda)
+       .long boot_pda
 
 ENTRY(stack_start)
        .long init_thread_union+THREAD_SIZE
@@ -431,9 +563,16 @@ ENTRY(stack_start)
 
 ready: .byte 0
 
+early_recursion_flag:
+       .long 0
+
 int_msg:
        .asciz "Unknown interrupt or fault at EIP %p %p %p\n"
 
+fault_msg:
+       .ascii "Int %d: CR2 %p  err %p  EIP %p  CS %p  flags %p\n"
+       .asciz "Stack: %p %p %p %p %p %p %p %p\n"
+
 /*
  * The IDT and GDT 'descriptors' are a strange 48-bit object
  * only used by the lidt and lgdt instructions. They are not
@@ -458,7 +597,7 @@ idt_descr:
 
 # boot GDT descriptor (later on used by CPU#0):
        .word 0                         # 32 bit align gdt_desc.address
-cpu_gdt_descr:
+ENTRY(early_gdt_descr)
        .word GDT_ENTRIES*8-1
        .long cpu_gdt_table
 
@@ -517,8 +656,8 @@ ENTRY(cpu_gdt_table)
        .quad 0x00009a000000ffff        /* 0xc0 APM CS 16 code (16 bit) */
        .quad 0x004092000000ffff        /* 0xc8 APM DS    data */
 
-       .quad 0x0000920000000000        /* 0xd0 - ESPFIX 16-bit SS */
-       .quad 0x0000000000000000        /* 0xd8 - unused */
+       .quad 0x00c0920000000000        /* 0xd0 - ESPFIX SS */
+       .quad 0x00cf92000000ffff        /* 0xd8 - PDA */
        .quad 0x0000000000000000        /* 0xe0 - unused */
        .quad 0x0000000000000000        /* 0xe8 - unused */
        .quad 0x0000000000000000        /* 0xf0 - unused */