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