aboutsummaryrefslogtreecommitdiffstats
path: root/arch/i386/kernel/head.S
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/i386/kernel/head.S
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'arch/i386/kernel/head.S')
-rw-r--r--arch/i386/kernel/head.S521
1 files changed, 521 insertions, 0 deletions
diff --git a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S
new file mode 100644
index 000000000000..d273fd746192
--- /dev/null
+++ b/arch/i386/kernel/head.S
@@ -0,0 +1,521 @@
1/*
2 * linux/arch/i386/kernel/head.S -- the 32-bit startup code.
3 *
4 * Copyright (C) 1991, 1992 Linus Torvalds
5 *
6 * Enhanced CPU detection and feature setting code by Mike Jagdis
7 * and Martin Mares, November 1997.
8 */
9
10.text
11#include <linux/config.h>
12#include <linux/threads.h>
13#include <linux/linkage.h>
14#include <asm/segment.h>
15#include <asm/page.h>
16#include <asm/pgtable.h>
17#include <asm/desc.h>
18#include <asm/cache.h>
19#include <asm/thread_info.h>
20#include <asm/asm_offsets.h>
21#include <asm/setup.h>
22
23/*
24 * References to members of the new_cpu_data structure.
25 */
26
27#define X86 new_cpu_data+CPUINFO_x86
28#define X86_VENDOR new_cpu_data+CPUINFO_x86_vendor
29#define X86_MODEL new_cpu_data+CPUINFO_x86_model
30#define X86_MASK new_cpu_data+CPUINFO_x86_mask
31#define X86_HARD_MATH new_cpu_data+CPUINFO_hard_math
32#define X86_CPUID new_cpu_data+CPUINFO_cpuid_level
33#define X86_CAPABILITY new_cpu_data+CPUINFO_x86_capability
34#define X86_VENDOR_ID new_cpu_data+CPUINFO_x86_vendor_id
35
36/*
37 * This is how much memory *in addition to the memory covered up to
38 * and including _end* we need mapped initially. We need one bit for
39 * each possible page, but only in low memory, which means
40 * 2^32/4096/8 = 128K worst case (4G/4G split.)
41 *
42 * Modulo rounding, each megabyte assigned here requires a kilobyte of
43 * memory, which is currently unreclaimed.
44 *
45 * This should be a multiple of a page.
46 */
47#define INIT_MAP_BEYOND_END (128*1024)
48
49
50/*
51 * 32-bit kernel entrypoint; only used by the boot CPU. On entry,
52 * %esi points to the real-mode code as a 32-bit pointer.
53 * CS and DS must be 4 GB flat segments, but we don't depend on
54 * any particular GDT layout, because we load our own as soon as we
55 * can.
56 */
57ENTRY(startup_32)
58
59/*
60 * Set segments to known values.
61 */
62 cld
63 lgdt boot_gdt_descr - __PAGE_OFFSET
64 movl $(__BOOT_DS),%eax
65 movl %eax,%ds
66 movl %eax,%es
67 movl %eax,%fs
68 movl %eax,%gs
69
70/*
71 * Clear BSS first so that there are no surprises...
72 * No need to cld as DF is already clear from cld above...
73 */
74 xorl %eax,%eax
75 movl $__bss_start - __PAGE_OFFSET,%edi
76 movl $__bss_stop - __PAGE_OFFSET,%ecx
77 subl %edi,%ecx
78 shrl $2,%ecx
79 rep ; stosl
80
81/*
82 * Initialize page tables. This creates a PDE and a set of page
83 * tables, which are located immediately beyond _end. The variable
84 * init_pg_tables_end is set up to point to the first "safe" location.
85 * Mappings are created both at virtual address 0 (identity mapping)
86 * and PAGE_OFFSET for up to _end+sizeof(page tables)+INIT_MAP_BEYOND_END.
87 *
88 * Warning: don't use %esi or the stack in this code. However, %esp
89 * can be used as a GPR if you really need it...
90 */
91page_pde_offset = (__PAGE_OFFSET >> 20);
92
93 movl $(pg0 - __PAGE_OFFSET), %edi
94 movl $(swapper_pg_dir - __PAGE_OFFSET), %edx
95 movl $0x007, %eax /* 0x007 = PRESENT+RW+USER */
9610:
97 leal 0x007(%edi),%ecx /* Create PDE entry */
98 movl %ecx,(%edx) /* Store identity PDE entry */
99 movl %ecx,page_pde_offset(%edx) /* Store kernel PDE entry */
100 addl $4,%edx
101 movl $1024, %ecx
10211:
103 stosl
104 addl $0x1000,%eax
105 loop 11b
106 /* End condition: we must map up to and including INIT_MAP_BEYOND_END */
107 /* bytes beyond the end of our own page tables; the +0x007 is the attribute bits */
108 leal (INIT_MAP_BEYOND_END+0x007)(%edi),%ebp
109 cmpl %ebp,%eax
110 jb 10b
111 movl %edi,(init_pg_tables_end - __PAGE_OFFSET)
112
113#ifdef CONFIG_SMP
114 xorl %ebx,%ebx /* This is the boot CPU (BSP) */
115 jmp 3f
116
117/*
118 * Non-boot CPU entry point; entered from trampoline.S
119 * We can't lgdt here, because lgdt itself uses a data segment, but
120 * we know the trampoline has already loaded the boot_gdt_table GDT
121 * for us.
122 */
123ENTRY(startup_32_smp)
124 cld
125 movl $(__BOOT_DS),%eax
126 movl %eax,%ds
127 movl %eax,%es
128 movl %eax,%fs
129 movl %eax,%gs
130
131/*
132 * New page tables may be in 4Mbyte page mode and may
133 * be using the global pages.
134 *
135 * NOTE! If we are on a 486 we may have no cr4 at all!
136 * So we do not try to touch it unless we really have
137 * some bits in it to set. This won't work if the BSP
138 * implements cr4 but this AP does not -- very unlikely
139 * but be warned! The same applies to the pse feature
140 * if not equally supported. --macro
141 *
142 * NOTE! We have to correct for the fact that we're
143 * not yet offset PAGE_OFFSET..
144 */
145#define cr4_bits mmu_cr4_features-__PAGE_OFFSET
146 movl cr4_bits,%edx
147 andl %edx,%edx
148 jz 6f
149 movl %cr4,%eax # Turn on paging options (PSE,PAE,..)
150 orl %edx,%eax
151 movl %eax,%cr4
152
153 btl $5, %eax # check if PAE is enabled
154 jnc 6f
155
156 /* Check if extended functions are implemented */
157 movl $0x80000000, %eax
158 cpuid
159 cmpl $0x80000000, %eax
160 jbe 6f
161 mov $0x80000001, %eax
162 cpuid
163 /* Execute Disable bit supported? */
164 btl $20, %edx
165 jnc 6f
166
167 /* Setup EFER (Extended Feature Enable Register) */
168 movl $0xc0000080, %ecx
169 rdmsr
170
171 btsl $11, %eax
172 /* Make changes effective */
173 wrmsr
174
1756:
176 /* This is a secondary processor (AP) */
177 xorl %ebx,%ebx
178 incl %ebx
179
1803:
181#endif /* CONFIG_SMP */
182
183/*
184 * Enable paging
185 */
186 movl $swapper_pg_dir-__PAGE_OFFSET,%eax
187 movl %eax,%cr3 /* set the page table pointer.. */
188 movl %cr0,%eax
189 orl $0x80000000,%eax
190 movl %eax,%cr0 /* ..and set paging (PG) bit */
191 ljmp $__BOOT_CS,$1f /* Clear prefetch and normalize %eip */
1921:
193 /* Set up the stack pointer */
194 lss stack_start,%esp
195
196/*
197 * Initialize eflags. Some BIOS's leave bits like NT set. This would
198 * confuse the debugger if this code is traced.
199 * XXX - best to initialize before switching to protected mode.
200 */
201 pushl $0
202 popfl
203
204#ifdef CONFIG_SMP
205 andl %ebx,%ebx
206 jz 1f /* Initial CPU cleans BSS */
207 jmp checkCPUtype
2081:
209#endif /* CONFIG_SMP */
210
211/*
212 * start system 32-bit setup. We need to re-do some of the things done
213 * in 16-bit mode for the "real" operations.
214 */
215 call setup_idt
216
217/*
218 * Copy bootup parameters out of the way.
219 * Note: %esi still has the pointer to the real-mode data.
220 */
221 movl $boot_params,%edi
222 movl $(PARAM_SIZE/4),%ecx
223 cld
224 rep
225 movsl
226 movl boot_params+NEW_CL_POINTER,%esi
227 andl %esi,%esi
228 jnz 2f # New command line protocol
229 cmpw $(OLD_CL_MAGIC),OLD_CL_MAGIC_ADDR
230 jne 1f
231 movzwl OLD_CL_OFFSET,%esi
232 addl $(OLD_CL_BASE_ADDR),%esi
2332:
234 movl $saved_command_line,%edi
235 movl $(COMMAND_LINE_SIZE/4),%ecx
236 rep
237 movsl
2381:
239checkCPUtype:
240
241 movl $-1,X86_CPUID # -1 for no CPUID initially
242
243/* check if it is 486 or 386. */
244/*
245 * XXX - this does a lot of unnecessary setup. Alignment checks don't
246 * apply at our cpl of 0 and the stack ought to be aligned already, and
247 * we don't need to preserve eflags.
248 */
249
250 movb $3,X86 # at least 386
251 pushfl # push EFLAGS
252 popl %eax # get EFLAGS
253 movl %eax,%ecx # save original EFLAGS
254 xorl $0x240000,%eax # flip AC and ID bits in EFLAGS
255 pushl %eax # copy to EFLAGS
256 popfl # set EFLAGS
257 pushfl # get new EFLAGS
258 popl %eax # put it in eax
259 xorl %ecx,%eax # change in flags
260 pushl %ecx # restore original EFLAGS
261 popfl
262 testl $0x40000,%eax # check if AC bit changed
263 je is386
264
265 movb $4,X86 # at least 486
266 testl $0x200000,%eax # check if ID bit changed
267 je is486
268
269 /* get vendor info */
270 xorl %eax,%eax # call CPUID with 0 -> return vendor ID
271 cpuid
272 movl %eax,X86_CPUID # save CPUID level
273 movl %ebx,X86_VENDOR_ID # lo 4 chars
274 movl %edx,X86_VENDOR_ID+4 # next 4 chars
275 movl %ecx,X86_VENDOR_ID+8 # last 4 chars
276
277 orl %eax,%eax # do we have processor info as well?
278 je is486
279
280 movl $1,%eax # Use the CPUID instruction to get CPU type
281 cpuid
282 movb %al,%cl # save reg for future use
283 andb $0x0f,%ah # mask processor family
284 movb %ah,X86
285 andb $0xf0,%al # mask model
286 shrb $4,%al
287 movb %al,X86_MODEL
288 andb $0x0f,%cl # mask mask revision
289 movb %cl,X86_MASK
290 movl %edx,X86_CAPABILITY
291
292is486: movl $0x50022,%ecx # set AM, WP, NE and MP
293 jmp 2f
294
295is386: movl $2,%ecx # set MP
2962: movl %cr0,%eax
297 andl $0x80000011,%eax # Save PG,PE,ET
298 orl %ecx,%eax
299 movl %eax,%cr0
300
301 call check_x87
302 incb ready
303 lgdt cpu_gdt_descr
304 lidt idt_descr
305 ljmp $(__KERNEL_CS),$1f
3061: movl $(__KERNEL_DS),%eax # reload all the segment registers
307 movl %eax,%ss # after changing gdt.
308
309 movl $(__USER_DS),%eax # DS/ES contains default USER segment
310 movl %eax,%ds
311 movl %eax,%es
312
313 xorl %eax,%eax # Clear FS/GS and LDT
314 movl %eax,%fs
315 movl %eax,%gs
316 lldt %ax
317 cld # gcc2 wants the direction flag cleared at all times
318#ifdef CONFIG_SMP
319 movb ready, %cl
320 cmpb $1,%cl
321 je 1f # the first CPU calls start_kernel
322 # all other CPUs call initialize_secondary
323 call initialize_secondary
324 jmp L6
3251:
326#endif /* CONFIG_SMP */
327 call start_kernel
328L6:
329 jmp L6 # main should never return here, but
330 # just in case, we know what happens.
331
332/*
333 * We depend on ET to be correct. This checks for 287/387.
334 */
335check_x87:
336 movb $0,X86_HARD_MATH
337 clts
338 fninit
339 fstsw %ax
340 cmpb $0,%al
341 je 1f
342 movl %cr0,%eax /* no coprocessor: have to set bits */
343 xorl $4,%eax /* set EM */
344 movl %eax,%cr0
345 ret
346 ALIGN
3471: movb $1,X86_HARD_MATH
348 .byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */
349 ret
350
351/*
352 * setup_idt
353 *
354 * sets up a idt with 256 entries pointing to
355 * ignore_int, interrupt gates. It doesn't actually load
356 * idt - that can be done only after paging has been enabled
357 * and the kernel moved to PAGE_OFFSET. Interrupts
358 * are enabled elsewhere, when we can be relatively
359 * sure everything is ok.
360 *
361 * Warning: %esi is live across this function.
362 */
363setup_idt:
364 lea ignore_int,%edx
365 movl $(__KERNEL_CS << 16),%eax
366 movw %dx,%ax /* selector = 0x0010 = cs */
367 movw $0x8E00,%dx /* interrupt gate - dpl=0, present */
368
369 lea idt_table,%edi
370 mov $256,%ecx
371rp_sidt:
372 movl %eax,(%edi)
373 movl %edx,4(%edi)
374 addl $8,%edi
375 dec %ecx
376 jne rp_sidt
377 ret
378
379/* This is the default interrupt "handler" :-) */
380 ALIGN
381ignore_int:
382 cld
383 pushl %eax
384 pushl %ecx
385 pushl %edx
386 pushl %es
387 pushl %ds
388 movl $(__KERNEL_DS),%eax
389 movl %eax,%ds
390 movl %eax,%es
391 pushl 16(%esp)
392 pushl 24(%esp)
393 pushl 32(%esp)
394 pushl 40(%esp)
395 pushl $int_msg
396 call printk
397 addl $(5*4),%esp
398 popl %ds
399 popl %es
400 popl %edx
401 popl %ecx
402 popl %eax
403 iret
404
405/*
406 * Real beginning of normal "text" segment
407 */
408ENTRY(stext)
409ENTRY(_stext)
410
411/*
412 * BSS section
413 */
414.section ".bss.page_aligned","w"
415ENTRY(swapper_pg_dir)
416 .fill 1024,4,0
417ENTRY(empty_zero_page)
418 .fill 4096,1,0
419
420/*
421 * This starts the data section.
422 */
423.data
424
425ENTRY(stack_start)
426 .long init_thread_union+THREAD_SIZE
427 .long __BOOT_DS
428
429ready: .byte 0
430
431int_msg:
432 .asciz "Unknown interrupt or fault at EIP %p %p %p\n"
433
434/*
435 * The IDT and GDT 'descriptors' are a strange 48-bit object
436 * only used by the lidt and lgdt instructions. They are not
437 * like usual segment descriptors - they consist of a 16-bit
438 * segment size, and 32-bit linear address value:
439 */
440
441.globl boot_gdt_descr
442.globl idt_descr
443.globl cpu_gdt_descr
444
445 ALIGN
446# early boot GDT descriptor (must use 1:1 address mapping)
447 .word 0 # 32 bit align gdt_desc.address
448boot_gdt_descr:
449 .word __BOOT_DS+7
450 .long boot_gdt_table - __PAGE_OFFSET
451
452 .word 0 # 32-bit align idt_desc.address
453idt_descr:
454 .word IDT_ENTRIES*8-1 # idt contains 256 entries
455 .long idt_table
456
457# boot GDT descriptor (later on used by CPU#0):
458 .word 0 # 32 bit align gdt_desc.address
459cpu_gdt_descr:
460 .word GDT_ENTRIES*8-1
461 .long cpu_gdt_table
462
463 .fill NR_CPUS-1,8,0 # space for the other GDT descriptors
464
465/*
466 * The boot_gdt_table must mirror the equivalent in setup.S and is
467 * used only for booting.
468 */
469 .align L1_CACHE_BYTES
470ENTRY(boot_gdt_table)
471 .fill GDT_ENTRY_BOOT_CS,8,0
472 .quad 0x00cf9a000000ffff /* kernel 4GB code at 0x00000000 */
473 .quad 0x00cf92000000ffff /* kernel 4GB data at 0x00000000 */
474
475/*
476 * The Global Descriptor Table contains 28 quadwords, per-CPU.
477 */
478 .align PAGE_SIZE_asm
479ENTRY(cpu_gdt_table)
480 .quad 0x0000000000000000 /* NULL descriptor */
481 .quad 0x0000000000000000 /* 0x0b reserved */
482 .quad 0x0000000000000000 /* 0x13 reserved */
483 .quad 0x0000000000000000 /* 0x1b reserved */
484 .quad 0x0000000000000000 /* 0x20 unused */
485 .quad 0x0000000000000000 /* 0x28 unused */
486 .quad 0x0000000000000000 /* 0x33 TLS entry 1 */
487 .quad 0x0000000000000000 /* 0x3b TLS entry 2 */
488 .quad 0x0000000000000000 /* 0x43 TLS entry 3 */
489 .quad 0x0000000000000000 /* 0x4b reserved */
490 .quad 0x0000000000000000 /* 0x53 reserved */
491 .quad 0x0000000000000000 /* 0x5b reserved */
492
493 .quad 0x00cf9a000000ffff /* 0x60 kernel 4GB code at 0x00000000 */
494 .quad 0x00cf92000000ffff /* 0x68 kernel 4GB data at 0x00000000 */
495 .quad 0x00cffa000000ffff /* 0x73 user 4GB code at 0x00000000 */
496 .quad 0x00cff2000000ffff /* 0x7b user 4GB data at 0x00000000 */
497
498 .quad 0x0000000000000000 /* 0x80 TSS descriptor */
499 .quad 0x0000000000000000 /* 0x88 LDT descriptor */
500
501 /* Segments used for calling PnP BIOS */
502 .quad 0x00c09a0000000000 /* 0x90 32-bit code */
503 .quad 0x00809a0000000000 /* 0x98 16-bit code */
504 .quad 0x0080920000000000 /* 0xa0 16-bit data */
505 .quad 0x0080920000000000 /* 0xa8 16-bit data */
506 .quad 0x0080920000000000 /* 0xb0 16-bit data */
507 /*
508 * The APM segments have byte granularity and their bases
509 * and limits are set at run time.
510 */
511 .quad 0x00409a0000000000 /* 0xb8 APM CS code */
512 .quad 0x00009a0000000000 /* 0xc0 APM CS 16 code (16 bit) */
513 .quad 0x0040920000000000 /* 0xc8 APM DS data */
514
515 .quad 0x0000920000000000 /* 0xd0 - ESPFIX 16-bit SS */
516 .quad 0x0000000000000000 /* 0xd8 - unused */
517 .quad 0x0000000000000000 /* 0xe0 - unused */
518 .quad 0x0000000000000000 /* 0xe8 - unused */
519 .quad 0x0000000000000000 /* 0xf0 - unused */
520 .quad 0x0000000000000000 /* 0xf8 - GDT entry 31: double-fault TSS */
521