Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 | /*
* linux/boot/head.s
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
/*
* head.s contains the 32-bit startup code.
*
* NOTE!!! Startup happens at absolute address 0x00000000, which is also where
* the page directory will exist. The startup code will be overwritten by
* the page directory.
*/
.text
.globl _idt,_gdt,
.globl _swapper_pg_dir,_pg0
.globl _empty_bad_page
.globl _empty_bad_page_table
.globl _tmp_floppy_area,_floppy_track_buffer
/*
* swapper_pg_dir is the main page directory, address 0x00001000
*/
startup_32:
cld
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
mov %ax,%gs
lss _stack_start,%esp
call setup_idt
xorl %eax,%eax
1: incl %eax # check that A20 really IS enabled
movl %eax,0x000000 # loop forever if it isn't
cmpl %eax,0x100000
je 1b
/* check if it is 486 or 386. */
movl %esp,%edi # save stack pointer
andl $0xfffffffc,%esp # align stack to avoid AC fault
pushfl # push EFLAGS
popl %eax # get EFLAGS
movl %eax,%ecx # save original EFLAGS
xorl $0x40000,%eax # flip AC bit in EFLAGS
pushl %eax # copy to EFLAGS
popfl # set EFLAGS
pushfl # get new EFLAGS
popl %eax # put it in eax
xorl %ecx,%eax # check if AC bit is changed. zero is 486.
jz 1f # 486
pushl %ecx # restore original EFLAGS
popfl
movl %edi,%esp # restore esp
movl %cr0,%eax # 386
andl $0x80000011,%eax # Save PG,PE,ET
orl $2,%eax # set MP
jmp 2f
/*
* NOTE! 486 should set bit 16, to check for write-protect in supervisor
* mode. Then it would be unnecessary with the "verify_area()"-calls.
* 486 users probably want to set the NE (#5) bit also, so as to use
* int 16 for math errors.
*/
1: pushl %ecx # restore original EFLAGS
popfl
movl %edi,%esp # restore esp
movl %cr0,%eax # 486
andl $0x80000011,%eax # Save PG,PE,ET
orl $0x10022,%eax # set NE and MP
2: movl %eax,%cr0
call check_x87
call setup_paging
lgdt gdt_descr
lidt idt_descr
ljmp $0x08,$1f
1: movl $0x10,%eax # reload all the segment registers
mov %ax,%ds # after changing gdt.
mov %ax,%es
mov %ax,%fs
mov %ax,%gs
lss _stack_start,%esp
pushl $0 # These are the parameters to main :-)
pushl $0
pushl $0
cld # gcc2 wants the direction flag cleared at all times
call _start_kernel
L6:
jmp L6 # main should never return here, but
# just in case, we know what happens.
/*
* We depend on ET to be correct. This checks for 287/387.
*/
check_x87:
movl $0,_hard_math
fninit
fstsw %ax
cmpb $0,%al
je 1f
movl %cr0,%eax /* no coprocessor: have to set bits */
xorl $6,%eax /* reset MP, set EM */
movl %eax,%cr0
ret
.align 2
1: movl $1,_hard_math
.byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */
ret
/*
* setup_idt
*
* sets up a idt with 256 entries pointing to
* ignore_int, interrupt gates. It doesn't actually load
* idt - that can be done only after paging has been enabled
* and the kernel moved to 0xC0000000. Interrupts
* are enabled elsewhere, when we can be relatively
* sure everything is ok. This routine will be over-
* written by the page tables.
*/
setup_idt:
lea ignore_int,%edx
movl $0x00080000,%eax
movw %dx,%ax /* selector = 0x0008 = cs */
movw $0x8E00,%dx /* interrupt gate - dpl=0, present */
lea _idt,%edi
mov $256,%ecx
rp_sidt:
movl %eax,(%edi)
movl %edx,4(%edi)
addl $8,%edi
dec %ecx
jne rp_sidt
ret
/*
* Setup_paging
*
* This routine sets up paging by setting the page bit
* in cr0. The page tables are set up, identity-mapping
* the first 4MB. The rest are initialized later.
*
* (ref: added support for up to 32mb, 17Apr92) -- Rik Faith
* (ref: update, 25Sept92) -- croutons@crunchy.uucp
* (ref: 92.10.11 - Linus Torvalds. Corrected 16M limit - no upper memory limit)
*/
.align 2
setup_paging:
movl $1024*2,%ecx /* 2 pages - swapper_pg_dir+1 page table */
xorl %eax,%eax
movl $_swapper_pg_dir,%edi /* swapper_pg_dir is at 0x1000 */
cld;rep;stosl
/* Identity-map the kernel in low 4MB memory for ease of transition */
movl $_pg0+7,_swapper_pg_dir /* set present bit/user r/w */
/* But the real place is at 0xC0000000 */
movl $_pg0+7,_swapper_pg_dir+3072 /* set present bit/user r/w */
movl $_pg0+4092,%edi
movl $0x03ff007,%eax /* 4Mb - 4096 + 7 (r/w user,p) */
std
1: stosl /* fill the page backwards - more efficient :-) */
subl $0x1000,%eax
jge 1b
cld
movl $_swapper_pg_dir,%eax
movl %eax,%cr3 /* cr3 - page directory start */
movl %cr0,%eax
orl $0x80000000,%eax
movl %eax,%cr0 /* set paging (PG) bit */
ret /* this also flushes the prefetch-queue */
/*
* page 0 is made non-existent, so that kernel NULL pointer references get
* caught. Thus the swapper page directory has been moved to 0x1000
*/
.org 0x1000
_swapper_pg_dir:
/*
* The page tables are initialized to only 4MB here - the final page
* tables are set up later depending on memory size.
*/
.org 0x2000
_pg0:
.org 0x3000
_empty_bad_page:
.org 0x4000
_empty_bad_page_table:
.org 0x5000
/*
* tmp_floppy_area is used by the floppy-driver when DMA cannot
* reach to a buffer-block. It needs to be aligned, so that it isn't
* on a 64kB border.
*/
_tmp_floppy_area:
.fill 1024,1,0
/*
* floppy_track_buffer is used to buffer one track of floppy data: it
* has to be separate from the tmp_floppy area, as otherwise a single-
* sector read/write can mess it up. It can contain one full track of
* data (18*2*512 bytes).
*/
_floppy_track_buffer:
.fill 512*2*18,1,0
/* This is the default interrupt "handler" :-) */
int_msg:
.asciz "Unknown interrupt\n\r"
.align 2
ignore_int:
cld
pushl %eax
pushl %ecx
pushl %edx
push %ds
push %es
push %fs
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
pushl $int_msg
call _printk
popl %eax
pop %fs
pop %es
pop %ds
popl %edx
popl %ecx
popl %eax
iret
/*
* The interrupt descriptor table has room for 256 idt's
*/
.align 4
.word 0
idt_descr:
.word 256*8-1 # idt contains 256 entries
.long 0xc0000000+_idt
.align 4
_idt:
.fill 256,8,0 # idt is uninitialized
/*
* The real GDT is also 256 entries long - no real reason
*/
.align 4
.word 0
gdt_descr:
.word 256*8-1
.long 0xc0000000+_gdt
/*
* This gdt setup gives the kernel a 1GB address space at virtual
* address 0xC0000000 - space enough for expansion, I hope.
*/
.align 4
_gdt:
.quad 0x0000000000000000 /* NULL descriptor */
.quad 0xc0c39a000000ffff /* 1GB at 0xC0000000 */
.quad 0xc0c392000000ffff /* 1GB */
.quad 0x0000000000000000 /* TEMPORARY - don't use */
.fill 252,8,0 /* space for LDT's and TSS's etc */
|