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

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