1 /* 2 * linux/boot/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 #include <linux/tasks.h>
20 #include <linux/segment.h>
21 #defineASSEMBLER 22 #include <linux/fd.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 */ 33 startup_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
57 1: 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
91 1:
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 %esp,%edi # save stack pointer
99 andl $0xfffffffc,%esp # align stack to avoid AC fault
100 movl $3,_x86
101 pushfl # push EFLAGS
102 popl %eax # get EFLAGS
103 movl %eax,%ecx # save original EFLAGS
104 xorl $0x40000,%eax # flip AC bit in EFLAGS
105 pushl %eax # copy to EFLAGS
106 popfl # set EFLAGS
107 pushfl # get new EFLAGS
108 popl %eax # put it in eax
109 xorl %ecx,%eax # change in flags
110 andl $0x40000,%eax # check if AC bit changed
111 je is386
112 movl $4,_x86
113 movl %ecx,%eax
114 xorl $0x200000,%eax # check ID flag
115 pushl %eax
116 popfl # if we are on a straight 486DX, SX, or
117 pushfl # 487SX we can't change it
118 popl %eax
119 xorl %ecx,%eax
120 andl $0x200000,%eax
121 je is486
122 isnew: pushl %ecx # restore original EFLAGS
123 popfl
124 movl $1, %eax # Use the CPUID instruction to
125 .byte 0x0f, 0xa2 # check the processor type
126 andl $0xf00, %eax # Set _x86 with the family
127 shrl $8, %eax # returned.
128 movl %eax, _x86
129 movl %edi,%esp # restore esp
130 movl %cr0,%eax # 486+
131 andl $0x80000011,%eax # Save PG,PE,ET
132 orl $0x50022,%eax # set AM, WP, NE and MP
133 jmp 2f
134 is486: pushl %ecx # restore original EFLAGS
135 popfl
136 movl %edi,%esp # restore esp
137 movl %cr0,%eax # 486
138 andl $0x80000011,%eax # Save PG,PE,ET
139 orl $0x50022,%eax # set AM, WP, NE and MP
140 jmp 2f
141 is386: pushl %ecx # restore original EFLAGS
142 popfl
143 movl %edi,%esp # restore esp
144 movl %cr0,%eax # 386
145 andl $0x80000011,%eax # Save PG,PE,ET
146 orl $2,%eax # set MP
147 2: movl %eax,%cr0
148 call check_x87
149 call setup_paging
150 lgdt gdt_descr
151 lidt idt_descr
152 ljmp $(KERNEL_CS),$1f
153 1: movl $(KERNEL_DS),%eax # reload all the segment registers
154 mov %ax,%ds # after changing gdt.
155 mov %ax,%es
156 mov %ax,%fs
157 mov %ax,%gs
158 lss _stack_start,%esp
159 xorl %eax,%eax
160 lldt %ax
161 pushl %eax # These are the parameters to main :-)
162 pushl %eax
163 pushl %eax
164 cld # gcc2 wants the direction flag cleared at all times
165 call _start_kernel
166 L6:
167 jmp L6 # main should never return here, but
168 # just in case, we know what happens. 169
170 /* 171 * We depend on ET to be correct. This checks for 287/387. 172 */ 173 check_x87:
174 movl $0,_hard_math
175 clts
176 fninit
177 fstsw %ax
178 cmpb $0,%al
179 je 1f
180 movl %cr0,%eax /* no coprocessor: have to set bits */ 181 xorl $4,%eax /* set EM */ 182 movl %eax,%cr0
183 ret
184 .align 2
185 1: movl $1,_hard_math
186 .byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */ 187 ret
188
189 /* 190 * setup_idt 191 * 192 * sets up a idt with 256 entries pointing to 193 * ignore_int, interrupt gates. It doesn't actually load 194 * idt - that can be done only after paging has been enabled 195 * and the kernel moved to 0xC0000000. Interrupts 196 * are enabled elsewhere, when we can be relatively 197 * sure everything is ok. 198 */ 199 setup_idt:
200 lea ignore_int,%edx
201 movl $(KERNEL_CS << 16),%eax
202 movw %dx,%ax /* selector = 0x0010 = cs */ 203 movw $0x8E00,%dx /* interrupt gate - dpl=0, present */ 204
205 lea _idt,%edi
206 mov $256,%ecx
207 rp_sidt:
208 movl %eax,(%edi)
209 movl %edx,4(%edi)
210 addl $8,%edi
211 dec %ecx
212 jne rp_sidt
213 ret
214
215
216 /* 217 * Setup_paging 218 * 219 * This routine sets up paging by setting the page bit 220 * in cr0. The page tables are set up, identity-mapping 221 * the first 4MB. The rest are initialized later. 222 * 223 * (ref: added support for up to 32mb, 17Apr92) -- Rik Faith 224 * (ref: update, 25Sept92) -- croutons@crunchy.uucp 225 * (ref: 92.10.11 - Linus Torvalds. Corrected 16M limit - no upper memory limit) 226 */ 227 .align 2
228 setup_paging:
229 movl $1024*2,%ecx /* 2 pages - swapper_pg_dir+1 page table */ 230 xorl %eax,%eax
231 movl $_swapper_pg_dir,%edi /* swapper_pg_dir is at 0x1000 */ 232 cld;rep;stosl
233 /* Identity-map the kernel in low 4MB memory for ease of transition */ 234 movl $_pg0+7,_swapper_pg_dir /* set present bit/user r/w */ 235 /* But the real place is at 0xC0000000 */ 236 movl $_pg0+7,_swapper_pg_dir+3072 /* set present bit/user r/w */ 237 movl $_pg0+4092,%edi
238 movl $0x03ff007,%eax /* 4Mb - 4096 + 7 (r/w user,p) */ 239 std
240 1: stosl /* fill the page backwards - more efficient :-) */ 241 subl $0x1000,%eax
242 jge 1b
243 cld
244 movl $_swapper_pg_dir,%eax
245 movl %eax,%cr3 /* cr3 - page directory start */ 246 movl %cr0,%eax
247 orl $0x80000000,%eax
248 movl %eax,%cr0 /* set paging (PG) bit */ 249 ret /* this also flushes the prefetch-queue */ 250
251 /* 252 * page 0 is made non-existent, so that kernel NULL pointer references get 253 * caught. Thus the swapper page directory has been moved to 0x1000 254 * 255 * XXX Actually, the swapper page directory is at 0x1000 plus 1 megabyte, 256 * with the introduction of the compressed boot code. Theoretically, 257 * the original design of overlaying the startup code with the swapper 258 * page directory is still possible --- it would reduce the size of the kernel 259 * by 2-3k. This would be a good thing to do at some point..... 260 */ 261 .org 0x1000
262 _swapper_pg_dir:
263 /* 264 * The page tables are initialized to only 4MB here - the final page 265 * tables are set up later depending on memory size. 266 */ 267 .org 0x2000
268 _pg0:
269
270 .org 0x3000
271 _empty_bad_page:
272
273 .org 0x4000
274 _empty_bad_page_table:
275
276 .org 0x5000
277 _empty_zero_page:
278
279 .org 0x6000
280 /* 281 * floppy_track_buffer is used to buffer one track of floppy data: it 282 * has to be separate from the tmp_floppy area, as otherwise a single- 283 * sector read/write can mess it up. It can contain one full cylinder (sic) of 284 * data (36*2*512 bytes). 285 */ 286 _floppy_track_buffer:
287 .fill 512*2*MAX_BUFFER_SECTORS,1,0
288
289 /* This is the default interrupt "handler" :-) */ 290 int_msg:
291 .asciz "Unknown interrupt\n"
292 .align 2
293 ignore_int:
294 cld
295 pushl %eax
296 pushl %ecx
297 pushl %edx
298 push %ds
299 push %es
300 push %fs
301 movl $(KERNEL_DS),%eax
302 mov %ax,%ds
303 mov %ax,%es
304 mov %ax,%fs
305 pushl $int_msg
306 call _printk
307 popl %eax
308 pop %fs
309 pop %es
310 pop %ds
311 popl %edx
312 popl %ecx
313 popl %eax
314 iret
315
316 /* 317 * The interrupt descriptor table has room for 256 idt's 318 */ 319 .align 4
320 .word 0
321 idt_descr:
322 .word 256*8-1 # idt contains 256 entries
323 .long 0xc0000000+_idt
324
325 .align 4
326 _idt:
327 .fill 256,8,0 # idt is uninitialized
328
329 .align 4
330 .word 0
331 gdt_descr:
332 .word (8+2*NR_TASKS)*8-1
333 .long 0xc0000000+_gdt
334
335 /* 336 * This gdt setup gives the kernel a 1GB address space at virtual 337 * address 0xC0000000 - space enough for expansion, I hope. 338 */ 339 .align 4
340 _gdt:
341 .quad 0x0000000000000000 /* NULL descriptor */ 342 .quad 0x0000000000000000 /* not used */ 343 .quad 0xc0c39a000000ffff /* 0x10 kernel 1GB code at 0xC0000000 */ 344 .quad 0xc0c392000000ffff /* 0x18 kernel 1GB data at 0xC0000000 */ 345 .quad 0x00cbfa000000ffff /* 0x23 user 3GB code at 0x00000000 */ 346 .quad 0x00cbf2000000ffff /* 0x2b user 3GB data at 0x00000000 */ 347 .quad 0x0000000000000000 /* not used */ 348 .quad 0x0000000000000000 /* not used */ 349 .fill 2*NR_TASKS,8,0 /* space for LDT's and TSS's etc */