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

