root/arch/i386/kernel/head.S

/* [previous][next][first][last][top][bottom][index][help] */
   1 /*
   2  *  linux/arch/i386/head.S
   3  *
   4  *  Copyright (C) 1991, 1992  Linus Torvalds
   5  */
   6 
   7 /*
   8  *  head.S contains the 32-bit startup code.
   9  */
  10 
  11 .text
  12 #include <linux/tasks.h>
  13 #include <linux/fd.h>
  14 #include <linux/linkage.h>
  15 #include <asm/segment.h>
  16 
  17 #define CL_MAGIC_ADDR   0x90020
  18 #define CL_MAGIC        0xA33F
  19 #define CL_BASE_ADDR    0x90000
  20 #define CL_OFFSET       0x90022
  21 
  22 /*
  23  * swapper_pg_dir is the main page directory, address 0x00001000 (or at
  24  * address 0x00101000 for a compressed boot).
  25  */
  26 ENTRY(stext)
  27 ENTRY(_stext)
  28 startup_32:
  29         cld
  30         movl $(KERNEL_DS),%eax
  31         mov %ax,%ds
  32         mov %ax,%es
  33         mov %ax,%fs
  34         mov %ax,%gs
  35         lss stack_start,%esp
  36 /*
  37  * Clear BSS first so that there are no surprises...
  38  */
  39         xorl %eax,%eax
  40         movl $ SYMBOL_NAME(_edata),%edi
  41         movl $ SYMBOL_NAME(_end),%ecx
  42         subl %edi,%ecx
  43         cld
  44         rep
  45         stosb
  46 /*
  47  * start system 32-bit setup. We need to re-do some of the things done
  48  * in 16-bit mode for the "real" operations.
  49  */
  50         call setup_idt
  51         xorl %eax,%eax
  52 1:      incl %eax               # check that A20 really IS enabled
  53         movl %eax,0x000000      # loop forever if it isn't
  54         cmpl %eax,0x100000
  55         je 1b
  56 /*
  57  * Initialize eflags.  Some BIOS's leave bits like NT set.  This would
  58  * confuse the debugger if this code is traced.
  59  * XXX - best to initialize before switching to protected mode.
  60  */
  61         pushl $0
  62         popfl
  63 /*
  64  * Copy bootup parameters out of the way. First 2kB of
  65  * _empty_zero_page is for boot parameters, second 2kB
  66  * is for the command line.
  67  */
  68         movl $0x90000,%esi
  69         movl $ SYMBOL_NAME(empty_zero_page),%edi
  70         movl $512,%ecx
  71         cld
  72         rep
  73         movsl
  74         xorl %eax,%eax
  75         movl $512,%ecx
  76         rep
  77         stosl
  78         cmpw $(CL_MAGIC),CL_MAGIC_ADDR
  79         jne 1f
  80         movl $ SYMBOL_NAME(empty_zero_page)+2048,%edi
  81         movzwl CL_OFFSET,%esi
  82         addl $(CL_BASE_ADDR),%esi
  83         movl $2048,%ecx
  84         rep
  85         movsb
  86 1:
  87 /* check if it is 486 or 386. */
  88 /*
  89  * XXX - this does a lot of unnecessary setup.  Alignment checks don't
  90  * apply at our cpl of 0 and the stack ought to be aligned already, and
  91  * we don't need to preserve eflags.
  92  */
  93         movl $3, SYMBOL_NAME(x86)
  94         pushfl                  # push EFLAGS
  95         popl %eax               # get EFLAGS
  96         movl %eax,%ecx          # save original EFLAGS
  97         xorl $0x40000,%eax      # flip AC bit in EFLAGS
  98         pushl %eax              # copy to EFLAGS
  99         popfl                   # set EFLAGS
 100         pushfl                  # get new EFLAGS
 101         popl %eax               # put it in eax
 102         xorl %ecx,%eax          # change in flags
 103         andl $0x40000,%eax      # check if AC bit changed
 104         je is386
 105         movl $4,SYMBOL_NAME(x86)
 106         movl %ecx,%eax
 107         xorl $0x200000,%eax     # check ID flag
 108         pushl %eax
 109         popfl                   # if we are on a straight 486DX, SX, or
 110         pushfl                  # 487SX we can't change it
 111         popl %eax
 112         xorl %ecx,%eax
 113         andl $0x200000,%eax
 114         je is486
 115 isnew:  pushl %ecx              # restore original EFLAGS
 116         popfl
 117         /* get processor type */
 118         movl $1, %eax           # Use the CPUID instruction to 
 119         .byte 0x0f, 0xa2        # check the processor type
 120         movb %al, %cl           # save reg for future use
 121         andb $0x0f,%ah          # mask processor family
 122         movb %ah,SYMBOL_NAME(x86)
 123         andb $0xf0, %eax        # mask model
 124         shrb $4, %al
 125         movb %al,SYMBOL_NAME(x86_model)
 126         andb $0x0f, %cl         # mask mask revision
 127         movb %cl,SYMBOL_NAME(x86_mask)
 128         movl %edx,SYMBOL_NAME(x86_capability)
 129         /* get vendor info */
 130         xorl %eax, %eax                 # call CPUID with 0 -> return vendor ID
 131         .byte 0x0f, 0xa2                # CPUID
 132         movl %ebx,SYMBOL_NAME(x86_vendor_id)    # lo 4 chars
 133         movl %edx,SYMBOL_NAME(x86_vendor_id)+4  # next 4 chars
 134         movl %ecx,SYMBOL_NAME(x86_vendor_id)+8  # last 4 chars
 135 
 136         movl %cr0,%eax          # 486+
 137         andl $0x80000011,%eax   # Save PG,PE,ET
 138         orl $0x50022,%eax       # set AM, WP, NE and MP
 139         jmp 2f
 140 is486:  pushl %ecx              # restore original EFLAGS
 141         popfl
 142         movl %cr0,%eax          # 486
 143         andl $0x80000011,%eax   # Save PG,PE,ET
 144         orl $0x50022,%eax       # set AM, WP, NE and MP
 145         jmp 2f
 146 is386:  pushl %ecx              # restore original EFLAGS
 147         popfl
 148         movl %cr0,%eax          # 386
 149         andl $0x80000011,%eax   # Save PG,PE,ET
 150         orl $2,%eax             # set MP
 151 2:      movl %eax,%cr0
 152         call check_x87
 153         call setup_paging
 154         lgdt gdt_descr
 155         lidt idt_descr
 156         ljmp $(KERNEL_CS),$1f
 157 1:      movl $(KERNEL_DS),%eax  # reload all the segment registers
 158         mov %ax,%ds             # after changing gdt.
 159         mov %ax,%es
 160         mov %ax,%fs
 161         mov %ax,%gs
 162         lss stack_start,%esp
 163         xorl %eax,%eax
 164         lldt %ax
 165         pushl %eax              # These are the parameters to main :-)
 166         pushl %eax
 167         pushl %eax
 168         cld                     # gcc2 wants the direction flag cleared at all times
 169         call SYMBOL_NAME(start_kernel)
 170 L6:
 171         jmp L6                  # main should never return here, but
 172                                 # just in case, we know what happens.
 173 
 174 /*
 175  * We depend on ET to be correct. This checks for 287/387.
 176  */
 177 check_x87:
 178         movb $0,SYMBOL_NAME(hard_math)
 179         clts
 180         fninit
 181         fstsw %ax
 182         cmpb $0,%al
 183         je 1f
 184         movl %cr0,%eax          /* no coprocessor: have to set bits */
 185         xorl $4,%eax            /* set EM */
 186         movl %eax,%cr0
 187         ret
 188         ALIGN
 189 1:      movb $1,SYMBOL_NAME(hard_math)
 190         .byte 0xDB,0xE4         /* fsetpm for 287, ignored by 387 */
 191         ret
 192 
 193 /*
 194  *  setup_idt
 195  *
 196  *  sets up a idt with 256 entries pointing to
 197  *  ignore_int, interrupt gates. It doesn't actually load
 198  *  idt - that can be done only after paging has been enabled
 199  *  and the kernel moved to 0xC0000000. Interrupts
 200  *  are enabled elsewhere, when we can be relatively
 201  *  sure everything is ok.
 202  */
 203 setup_idt:
 204         lea ignore_int,%edx
 205         movl $(KERNEL_CS << 16),%eax
 206         movw %dx,%ax            /* selector = 0x0010 = cs */
 207         movw $0x8E00,%dx        /* interrupt gate - dpl=0, present */
 208 
 209         lea SYMBOL_NAME(idt),%edi
 210         mov $256,%ecx
 211 rp_sidt:
 212         movl %eax,(%edi)
 213         movl %edx,4(%edi)
 214         addl $8,%edi
 215         dec %ecx
 216         jne rp_sidt
 217         ret
 218 
 219 
 220 /*
 221  * Setup_paging
 222  *
 223  * This routine sets up paging by setting the page bit
 224  * in cr0. The page tables are set up, identity-mapping
 225  * the first 4MB.  The rest are initialized later.
 226  *
 227  * (ref: added support for up to 32mb, 17Apr92)  -- Rik Faith
 228  * (ref: update, 25Sept92)  -- croutons@crunchy.uucp 
 229  * (ref: 92.10.11 - Linus Torvalds. Corrected 16M limit - no upper memory limit)
 230  */
 231         ALIGN
 232 setup_paging:
 233         movl $1024*2,%ecx               /* 2 pages - swapper_pg_dir+1 page table */
 234         xorl %eax,%eax
 235         movl $ SYMBOL_NAME(swapper_pg_dir),%edi /* swapper_pg_dir is at 0x1000 */
 236         cld;rep;stosl
 237 /* Identity-map the kernel in low 4MB memory for ease of transition */
 238 /* set present bit/user r/w */
 239         movl $ SYMBOL_NAME(pg0)+7,SYMBOL_NAME(swapper_pg_dir)
 240 /* But the real place is at 0xC0000000 */
 241 /* set present bit/user r/w */
 242         movl $ SYMBOL_NAME(pg0)+7,SYMBOL_NAME(swapper_pg_dir)+3072
 243         movl $ SYMBOL_NAME(pg0)+4092,%edi
 244         movl $0x03ff007,%eax            /*  4Mb - 4096 + 7 (r/w user,p) */
 245         std
 246 1:      stosl                   /* fill the page backwards - more efficient :-) */
 247         subl $0x1000,%eax
 248         jge 1b
 249         cld
 250         movl $ SYMBOL_NAME(swapper_pg_dir),%eax
 251         movl %eax,%cr3                  /* cr3 - page directory start */
 252         movl %cr0,%eax
 253         orl $0x80000000,%eax
 254         movl %eax,%cr0          /* set paging (PG) bit */
 255         ret                     /* this also flushes the prefetch-queue */
 256 
 257 /*
 258  * page 0 is made non-existent, so that kernel NULL pointer references get
 259  * caught. Thus the swapper page directory has been moved to 0x1000
 260  *
 261  * XXX Actually, the swapper page directory is at 0x1000 plus 1 megabyte,
 262  * with the introduction of the compressed boot code.  Theoretically,
 263  * the original design of overlaying the startup code with the swapper
 264  * page directory is still possible --- it would reduce the size of the kernel
 265  * by 2-3k.  This would be a good thing to do at some point.....
 266  */
 267 .org 0x1000
 268 ENTRY(swapper_pg_dir)
 269 /*
 270  * The page tables are initialized to only 4MB here - the final page
 271  * tables are set up later depending on memory size.
 272  */
 273 .org 0x2000
 274 ENTRY(pg0)
 275 
 276 .org 0x3000
 277 ENTRY(empty_bad_page)
 278 
 279 .org 0x4000
 280 ENTRY(empty_bad_page_table)
 281 
 282 .org 0x5000
 283 ENTRY(empty_zero_page)
 284 
 285 .org 0x6000
 286 
 287 stack_start:
 288         .long SYMBOL_NAME(init_user_stack)+4096
 289         .long KERNEL_DS
 290 
 291 /* This is the default interrupt "handler" :-) */
 292 int_msg:
 293         .asciz "Unknown interrupt\n"
 294         ALIGN
 295 ignore_int:
 296         cld
 297         pushl %eax
 298         pushl %ecx
 299         pushl %edx
 300         push %ds
 301         push %es
 302         push %fs
 303         movl $(KERNEL_DS),%eax
 304         mov %ax,%ds
 305         mov %ax,%es
 306         mov %ax,%fs
 307         pushl $int_msg
 308         call SYMBOL_NAME(printk)
 309         popl %eax
 310         pop %fs
 311         pop %es
 312         pop %ds
 313         popl %edx
 314         popl %ecx
 315         popl %eax
 316         iret
 317 
 318 /*
 319  * The interrupt descriptor table has room for 256 idt's
 320  */
 321         ALIGN
 322 .word 0
 323 idt_descr:
 324         .word 256*8-1           # idt contains 256 entries
 325         .long 0xc0000000+SYMBOL_NAME(idt)
 326 
 327 ENTRY(idt)
 328         .fill 256,8,0           # idt is uninitialized
 329 
 330         ALIGN
 331 .word 0
 332 gdt_descr:
 333         .word (8+2*NR_TASKS)*8-1
 334         .long 0xc0000000+SYMBOL_NAME(gdt)
 335 
 336 /*
 337  * This gdt setup gives the kernel a 1GB address space at virtual
 338  * address 0xC0000000 - space enough for expansion, I hope.
 339  */
 340 ENTRY(gdt)
 341         .quad 0x0000000000000000        /* NULL descriptor */
 342         .quad 0x0000000000000000        /* not used */
 343         .quad 0xc0c39a000000ffff        /* 0x10 kernel 1GB code at 0xC0000000 */
 344         .quad 0xc0c392000000ffff        /* 0x18 kernel 1GB data at 0xC0000000 */
 345         .quad 0x00cbfa000000ffff        /* 0x23 user   3GB code at 0x00000000 */
 346         .quad 0x00cbf2000000ffff        /* 0x2b user   3GB data at 0x00000000 */
 347         .quad 0x0000000000000000        /* not used */
 348         .quad 0x0000000000000000        /* not used */
 349         .fill 2*NR_TASKS,8,0            /* space for LDT's and TSS's etc */

/* [previous][next][first][last][top][bottom][index][help] */