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