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 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 | /* * linux/arch/i386/head.S * * Copyright (C) 1991, 1992 Linus Torvalds */ /* * head.S contains the 32-bit startup code. */ .text #include <linux/tasks.h> #include <linux/fd.h> #include <linux/linkage.h> #include <asm/segment.h> #define CL_MAGIC_ADDR 0x90020 #define CL_MAGIC 0xA33F #define CL_BASE_ADDR 0x90000 #define CL_OFFSET 0x90022 /* * swapper_pg_dir is the main page directory, address 0x00001000 (or at * address 0x00101000 for a compressed boot). */ ENTRY(stext) ENTRY(_stext) startup_32: cld movl $(KERNEL_DS),%eax mov %ax,%ds mov %ax,%es mov %ax,%fs mov %ax,%gs lss stack_start,%esp /* * Clear BSS first so that there are no surprises... */ xorl %eax,%eax movl $ SYMBOL_NAME(_edata),%edi movl $ SYMBOL_NAME(_end),%ecx subl %edi,%ecx cld rep stosb /* * start system 32-bit setup. We need to re-do some of the things done * in 16-bit mode for the "real" operations. */ 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 /* * Copy bootup parameters out of the way. First 2kB of * _empty_zero_page is for boot parameters, second 2kB * is for the command line. */ movl $0x90000,%esi movl $ SYMBOL_NAME(empty_zero_page),%edi movl $512,%ecx cld rep movsl xorl %eax,%eax movl $512,%ecx rep stosl cmpw $(CL_MAGIC),CL_MAGIC_ADDR jne 1f movl $ SYMBOL_NAME(empty_zero_page)+2048,%edi movzwl CL_OFFSET,%esi addl $(CL_BASE_ADDR),%esi movl $2048,%ecx rep movsb 1: /* 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 $3, SYMBOL_NAME(x86) 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 je is386 movl $4,SYMBOL_NAME(x86) movl %ecx,%eax xorl $0x200000,%eax # check ID flag pushl %eax popfl # if we are on a straight 486DX, SX, or pushfl # 487SX we can't change it popl %eax xorl %ecx,%eax andl $0x200000,%eax je is486 isnew: pushl %ecx # restore original EFLAGS popfl /* get processor type */ movl $1, %eax # Use the CPUID instruction to .byte 0x0f, 0xa2 # check the processor type movb %al, %cl # save reg for future use andb $0x0f,%ah # mask processor family movb %ah,SYMBOL_NAME(x86) andb $0xf0, %eax # mask model shrb $4, %al movb %al,SYMBOL_NAME(x86_model) andb $0x0f, %cl # mask mask revision movb %cl,SYMBOL_NAME(x86_mask) movl %edx,SYMBOL_NAME(x86_capability) /* get vendor info */ xorl %eax, %eax # call CPUID with 0 -> return vendor ID .byte 0x0f, 0xa2 # CPUID movl %ebx,SYMBOL_NAME(x86_vendor_id) # lo 4 chars movl %edx,SYMBOL_NAME(x86_vendor_id)+4 # next 4 chars movl %ecx,SYMBOL_NAME(x86_vendor_id)+8 # last 4 chars movl %cr0,%eax # 486+ andl $0x80000011,%eax # Save PG,PE,ET orl $0x50022,%eax # set AM, WP, NE and MP jmp 2f is486: pushl %ecx # restore original EFLAGS popfl movl %cr0,%eax # 486 andl $0x80000011,%eax # Save PG,PE,ET orl $0x50022,%eax # set AM, WP, NE and MP jmp 2f is386: pushl %ecx # restore original EFLAGS popfl movl %cr0,%eax # 386 andl $0x80000011,%eax # Save PG,PE,ET orl $2,%eax # set MP 2: movl %eax,%cr0 call check_x87 call setup_paging lgdt gdt_descr lidt idt_descr ljmp $(KERNEL_CS),$1f 1: movl $(KERNEL_DS),%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 xorl %eax,%eax lldt %ax pushl %eax # These are the parameters to main :-) pushl %eax pushl %eax cld # gcc2 wants the direction flag cleared at all times call SYMBOL_NAME(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: movb $0,SYMBOL_NAME(hard_math) clts fninit fstsw %ax cmpb $0,%al je 1f movl %cr0,%eax /* no coprocessor: have to set bits */ xorl $4,%eax /* set EM */ movl %eax,%cr0 ret ALIGN 1: movb $1,SYMBOL_NAME(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. */ setup_idt: lea ignore_int,%edx movl $(KERNEL_CS << 16),%eax movw %dx,%ax /* selector = 0x0010 = cs */ movw $0x8E00,%dx /* interrupt gate - dpl=0, present */ lea SYMBOL_NAME(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 setup_paging: movl $1024*2,%ecx /* 2 pages - swapper_pg_dir+1 page table */ xorl %eax,%eax movl $ SYMBOL_NAME(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 */ /* set present bit/user r/w */ movl $ SYMBOL_NAME(pg0)+7,SYMBOL_NAME(swapper_pg_dir) /* But the real place is at 0xC0000000 */ /* set present bit/user r/w */ movl $ SYMBOL_NAME(pg0)+7,SYMBOL_NAME(swapper_pg_dir)+3072 movl $ SYMBOL_NAME(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 $ SYMBOL_NAME(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 * * XXX Actually, the swapper page directory is at 0x1000 plus 1 megabyte, * with the introduction of the compressed boot code. Theoretically, * the original design of overlaying the startup code with the swapper * page directory is still possible --- it would reduce the size of the kernel * by 2-3k. This would be a good thing to do at some point..... */ .org 0x1000 ENTRY(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 ENTRY(pg0) .org 0x3000 ENTRY(empty_bad_page) .org 0x4000 ENTRY(empty_bad_page_table) .org 0x5000 ENTRY(empty_zero_page) .org 0x6000 stack_start: .long SYMBOL_NAME(init_user_stack)+4096 .long KERNEL_DS /* This is the default interrupt "handler" :-) */ int_msg: .asciz "Unknown interrupt\n" ALIGN ignore_int: cld pushl %eax pushl %ecx pushl %edx push %ds push %es push %fs movl $(KERNEL_DS),%eax mov %ax,%ds mov %ax,%es mov %ax,%fs pushl $int_msg call SYMBOL_NAME(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 .word 0 idt_descr: .word 256*8-1 # idt contains 256 entries .long 0xc0000000+SYMBOL_NAME(idt) ENTRY(idt) .fill 256,8,0 # idt is uninitialized ALIGN .word 0 gdt_descr: .word (8+2*NR_TASKS)*8-1 .long 0xc0000000+SYMBOL_NAME(gdt) /* * This gdt setup gives the kernel a 1GB address space at virtual * address 0xC0000000 - space enough for expansion, I hope. */ ENTRY(gdt) .quad 0x0000000000000000 /* NULL descriptor */ .quad 0x0000000000000000 /* not used */ .quad 0xc0c39a000000ffff /* 0x10 kernel 1GB code at 0xC0000000 */ .quad 0xc0c392000000ffff /* 0x18 kernel 1GB data at 0xC0000000 */ .quad 0x00cbfa000000ffff /* 0x23 user 3GB code at 0x00000000 */ .quad 0x00cbf2000000ffff /* 0x2b user 3GB data at 0x00000000 */ .quad 0x0000000000000000 /* not used */ .quad 0x0000000000000000 /* not used */ .fill 2*NR_TASKS,8,0 /* space for LDT's and TSS's etc */ |