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 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 | /* * 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 #include <linux/tasks.h> /* * 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 /* * Initialize eflags. Some BIOS's leave bits like NT set. This would * confuse the debugger if this code is traced. * XXX - best to initialize before switching to protected mode. */ pushl $0 popfl /* check if it is 486 or 386. */ /* * XXX - this does a lot of unnecessary setup. Alignment checks don't * apply at our cpl of 0 and the stack ought to be aligned already, and * we don't need to preserve eflags. */ 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 # change in flags andl $0x40000,%eax # check if AC bit changed jnz 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. * XXX - the above is out of date. We set all the bits, but don't take * advantage of WP (26 Dec 92). */ 1: pushl %ecx # restore original EFLAGS popfl movl %edi,%esp # restore esp movl %cr0,%eax # 486 andl $0x80000011,%eax # Save PG,PE,ET orl $0x50022,%eax # set AM, WP, 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 (4+2*NR_TASKS)*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 2*NR_TASKS,8,0 /* space for LDT's and TSS's etc */ |