blob: b35c62a983b9ae90ac8553a17763faeb5dfef18a [file] [log] [blame]
Stefan Reinauerb34eea32008-02-15 18:16:06 +00001/*
2 * exec_kernel/user_space/head.S
3 *
4 * Copyright (C) 2000, 2002, 2003 Eric Biederman
5 *
6 * Parts of this code were take from the linux startup
7 * code of linux-2.4.0-test9
8 *
9 * Other parts were taken from etherboot-5.0.5
10 */
11
12#define ASSEMBLY 1
13
14#define RELOC 0x10000
15#define PROT_CODE_SEG 0x10
16#define PROT_DATA_SEG 0x18
17#define REAL_CODE_SEG 0x08
18#define REAL_DATA_SEG 0x20
19
20 .equ CR0_PE,1
21
22.text
23.code32
24
25
26#include "convert.h"
27
28 .globl startup_32
29startup_32:
30 cld
31 cli
32
33 # Save the arguments safely out of the way
34 movl %eax, boot_type
35 movl %ebx, boot_data
36 cmp $0,%esp
37 jz 1f
38 movl 4(%esp), %eax
39 movl %eax, boot_param
401:
41
42 movl stack_start, %esp
43
44 # Clear eflags
45 pushl $0
46 popfl
47
48 # Clear BSS
49 xorl %eax,%eax
50 movl $ _edata,%edi
51 movl $ _end,%ecx
52 subl %edi,%ecx
53 cld
54 rep
55 stosb
56
57 # Move the gdt where Linux will not smash it during decompression
58 movl $gdt, %esi
59 movl $GDTLOC, %edi
60 movl $(gdt_end - gdt), %ecx
61 rep movsb
Ward Vandewegeebc92182008-08-06 20:37:38 +000062
Stefan Reinauerb34eea32008-02-15 18:16:06 +000063 # Linux makes stupid assumptions about the segments
64 # that are already setup, so setup a new gdt & ldt
65 # and then reload the segment registers.
66
67 lgdt gdt_48
68 lidt idt_48
69
70 # Load the data segment registers
71 movl $ PROT_DATA_SEG, %eax
72 movl %eax, %ds
73 movl %eax, %es
74 movl %eax, %fs
75 movl %eax, %gs
76 movl %eax, %ss
77
78 pushl $image_params # image build time parameters as forth arg
79 pushl boot_param # boot_param pointer as third arg
80 pushl boot_data # boot data pointer as second arg
81 pushl boot_type # boot data type as first argument
82 call convert_params
83
84 movl %eax, %esi # put the real mode pointer in a safe place
85 addl $16, %esp # pop the arguments
86
87
88 # Setup the registers before jumping to linux
89
90
91 # clear eflags
92 pushl $0
93 popfl
94
95 # Flag to indicate we are the bootstrap processor
96 xorl %ebx, %ebx
97
Ward Vandewegeebc92182008-08-06 20:37:38 +000098 movl switch_64, %eax
99 cmp $1, %eax
100 jz switch_to_64
101
Stefan Reinauerb34eea32008-02-15 18:16:06 +0000102 # Clear the unspecified registers for good measure
103 xorl %eax, %eax
104 xorl %ecx, %ecx
105 xorl %edx, %edx
106 xorl %edi, %edi
Stefan Reinauerb34eea32008-02-15 18:16:06 +0000107 xorl %ebp, %ebp
108
Ward Vandewegeebc92182008-08-06 20:37:38 +0000109 # do not clear esp, we still need to use lret later
Stefan Reinauerb34eea32008-02-15 18:16:06 +0000110
Ward Vandewegeebc92182008-08-06 20:37:38 +0000111 pushl $PROT_CODE_SEG
112 movl entry, %eax
113 pushl %eax
114
115 lret
116
117switch_to_64:
118
119 /* We need to switch to 64bit before use startup_64 entry go to kernel */
120 /*
121 * Prepare for entering 64 bit mode
122 */
123 # Move the gdt64 where Linux will not smash it during decompression
124 movl %esi, %eax # save the real mode pointer
125 movl $gdt64, %esi
126 movl $GDT64LOC, %edi
127 movl $(gdt64_end - gdt64), %ecx
128 rep movsb
129 movl %eax, %esi
130
131 /* Load new GDT with the 64bit segments using 32bit descriptor */
132 lgdt gdt64
133
134 /* Enable PAE mode */
135 xorl %eax, %eax
136 btsl $5, %eax
137 movl %eax, %cr4
138
139 /*
140 * Build early 4G boot pagetable
141 */
142 /* Initialize Page tables to 0*/
143 movl $PGTLOC, %edi
144 xorl %eax, %eax
145 movl $((4096*6)/4), %ecx
146 rep stosl
147
148 /* Build Level 4 */
149 movl $(PGTLOC + 0), %edi
150 leal 0x1007 (%edi), %eax
151 movl %eax, 0(%edi)
152
153 /* Build Level 3 */
154 movl $(PGTLOC + 0x1000), %edi
155 leal 0x1007(%edi), %eax
156 movl $4, %ecx
1571: movl %eax, 0x00(%edi)
158 addl $0x00001000, %eax
159 addl $8, %edi
160 decl %ecx
161 jnz 1b
162
163 /* Build Level 2 */
164 movl $(PGTLOC + 0x2000), %edi
165 movl $0x00000183, %eax
166 movl $2048, %ecx
1671: movl %eax, 0(%edi)
168 addl $0x00200000, %eax
169 addl $8, %edi
170 decl %ecx
171 jnz 1b
172
173 /* Enable the boot page tables */
174 movl $PGTLOC, %eax
175 movl %eax, %cr3
176
177 /* Enable Long mode in EFER (Extended Feature Enable Register) */
178 movl $0xc0000080, %ecx
179 rdmsr
180 btsl $8, %eax
181 wrmsr
182
183 /* Preparing for 64bit jmp */
184 pushl $PROT_CODE_SEG
185 movl entry, %eax
186 pushl %eax
187
188 /* Enter paged protected Mode, activating Long Mode */
189 xorl %eax, %eax
190 btsl $31, %eax
191 btsl $0, %eax
192 movl %eax, %cr0
193
194 /*
195 * At this point we're in long mode but in 32bit compatibility mode
196 * with EFER.LME = 1, CS.L = 0, CS.D = 1 (and in turn
197 * EFER.LMA = 1). Now we want to jump in 64bit mode, to do that we use
198 * the new gdt/idt that has __KERNEL_CS with CS.L = 1.
199 */
200
201 lret
Stefan Reinauerb34eea32008-02-15 18:16:06 +0000202
203
204 /* Routines to query the BIOS... */
Stefan Reinauerb34eea32008-02-15 18:16:06 +0000205/**************************************************************************
206E820_MEMSIZE - Get a listing of memory regions
207**************************************************************************/
208#define SMAP 0x534d4150
209 .globl meme820
210meme820:
211 pushl %ebp
212 movl %esp, %ebp
213 pushl %ebx
214 pushl %esi
215 pushl %edi
216 movl 8(%ebp), %edi /* Address to return e820 structures at */
217 subl $RELOC, %edi
218 movl 12(%ebp), %esi /* Maximum number of e820 structurs to return */
219 pushl %esi
220 call _prot_to_real
221 .code16
222 xorl %ebx, %ebx
223jmpe820:
224 movl $0xe820, %eax
225 movl $SMAP, %edx
226 movl $20, %ecx
227 /* %di was setup earlier */
228 int $0x15
229 jc bail820
230
231 cmpl $SMAP, %eax
232 jne bail820
233
234good820:
235 /* If this is useable memory, we save it by simply advancing %di by
236 * sizeof(e820rec)
237 */
238 decl %esi
239 testl %esi,%esi
240 jz bail820
241
242 addw $20, %di
243again820:
244 cmpl $0, %ebx /* check to see if %ebx is set to EOF */
245 jne jmpe820
246
247bail820:
248 data32 call _real_to_prot
249 .code32
250 popl %eax
251 subl %esi, %eax /* Compute how many structure we read */
252
253 /* Restore everything else */
254 popl %edi
255 popl %esi
256 popl %ebx
257 movl %ebp, %esp
258 popl %ebp
259 ret
260
261
262/**************************************************************************
263MEME801 - Determine size of extended memory
264**************************************************************************/
265 .globl meme801
266meme801:
267 pushl %ebx
268 pushl %esi
269 pushl %edi
270 call _prot_to_real
271 .code16
272
273 stc # fix to work around buggy
274 xorw %cx,%cx # BIOSes which dont clear/set
275 xorw %dx,%dx # carry on pass/error of
276 # e801h memory size call
277 # or merely pass cx,dx though
278 # without changing them.
279 movw $0xe801,%ax
280 int $0x15
281 jc e801absent
282
283 cmpw $0x0, %cx # Kludge to handle BIOSes
284 jne e801usecxdx # which report their extended
285 cmpw $0x0, %dx # memory in AX/BX rather than
286 jne e801usecxdx # CX/DX. The spec I have read
287 movw %ax, %cx # seems to indicate AX/BX
288 movw %bx, %dx # are more reasonable anyway...
289
290e801usecxdx:
291 andl $0xffff, %edx # clear sign extend
292 shll $6, %edx # and go from 64k to 1k chunks
293 movl %edx, %eax # store extended memory size
294 andl $0xffff, %ecx # clear sign extend
295 addl %ecx, %eax # and add lower memory into
296
297 jmp e801out
298e801absent:
299 xorl %eax,%eax
300
301e801out:
302 data32 call _real_to_prot
303 .code32
304 /* Restore Everything */
305 popl %edi
306 popl %esi
307 popl %ebx
308 ret
309
310/**************************************************************************
311MEM88 - Determine size of extended memory
312**************************************************************************/
313 .globl mem88
314mem88:
315 pushl %ebx
316 pushl %esi
317 pushl %edi
318 call _prot_to_real
319 .code16
320
321 movb $0x88, %ah
322 int $0x15
323 andl $0xffff, %eax
324
325 data32 call _real_to_prot
326 .code32
327
328 /* Restore Everything */
329 popl %edi
330 popl %esi
331 popl %ebx
332 ret
333
334
335/**************************************************************************
336BASEMEMSIZE - Get size of the conventional (base) memory
337**************************************************************************/
338 .globl basememsize
339basememsize:
340 call _prot_to_real
341 .code16
342 int $0x12
343 movw %ax,%cx
344 DATA32 call _real_to_prot
345 .code32
346 movw %cx,%ax
347 ret
348
349/**************************************************************************
350_REAL_TO_PROT - Go from REAL mode to Protected Mode
351**************************************************************************/
352 .globl _real_to_prot
353_real_to_prot:
354 .code16
355 cli
356 cs
357 addr32 lgdt gdt_48 - RELOC
358 movl %cr0,%eax
359 orl $CR0_PE,%eax
360 movl %eax,%cr0 /* turn on protected mode */
361
362 /* flush prefetch queue, and reload %cs:%eip */
363 data32 ljmp $PROT_CODE_SEG,$1f
3641:
365 .code32
366 /* reload other segment registers */
367 movl $PROT_DATA_SEG,%eax
368 movl %eax,%ds
369 movl %eax,%es
370 movl %eax,%ss
371 addl $RELOC,%esp /* Fix up stack pointer */
372 xorl %eax,%eax
373 movl %eax,%fs
374 movl %eax,%gs
375 popl %eax /* Fix up return address */
376 addl $RELOC,%eax
377 pushl %eax
378
379 /* switch to protected mode idt */
380 cs
381 lidt idt_48
382 ret
383
384/**************************************************************************
385_PROT_TO_REAL - Go from Protected Mode to REAL Mode
386**************************************************************************/
387 .globl _prot_to_real
388_prot_to_real:
389 .code32
390 popl %eax
391 subl $RELOC,%eax /* Adjust return address */
392 pushl %eax
393 subl $RELOC,%esp /* Adjust stack pointer */
394 ljmp $REAL_CODE_SEG,$1f- RELOC /* jump to a 16 bit segment */
3951:
396 .code16
397 /* clear the PE bit of CR0 */
398 movl %cr0,%eax
399 andl $0!CR0_PE,%eax
400 movl %eax,%cr0
401
402 /* make intersegment jmp to flush the processor pipeline
403 * and reload %cs:%eip (to clear upper 16 bits of %eip).
404 */
405 data32 ljmp $(RELOC)>>4,$2f- RELOC
4062:
407 /* we are in real mode now
408 * set up the real mode segment registers : %ds, $ss, %es
409 */
410 movw %cs,%ax
411 movw %ax,%ds
412 movw %ax,%es
413 movw %ax,%ss
414 movw %ax,%fs
415 movw %ax,%gs
416
417 /* Switch to the real mode idt */
418 cs
419 addr32 lidt idt_real - RELOC
420
421 sti
422 data32 ret /* There is a 32 bit return address on the stack */
423 .code32
424
425boot_type: .long 0
426boot_data: .long 0
427boot_param: .long 0
428
429idt_real:
430 .word 0x400 # idt limit = 256
431 .word 0, 0
432idt_48:
433 .word 0 # idt limit = 0
434 .word 0, 0 # idt base = 0L
435gdt_48:
436 .word gdt_end - gdt - 1 # gdt limit=40,
437 # (5 GDT entries)
438 .long GDTLOC # gdt base
439
440# Descriptor tables
441# These need to be in a seperate section so I can be
442# certain later activities dont stomp them
443gdt:
444 /* 0x00 */
445 .word 0, 0, 0, 0 # dummy
446
447 /* 0x08 */
448 /* 16 bit real mode code segment */
449 .word 0xffff,(RELOC&0xffff)
450 .byte (RELOC>>16),0x9b,0x00,(RELOC>>24)
451
452 /* 0x10 */
453 .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb)
454 .word 0 # base address = 0
455 .word 0x9A00 # code read/exec
456 .word 0x00CF # granularity = 4096, 386
457 # (+5th nibble of limit)
458
459 /* 0x18 */
460 .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb)
461 .word 0 # base address = 0
462 .word 0x9200 # data read/write
463 .word 0x00CF # granularity = 4096, 386
464 # (+5th nibble of limit)
465
466 /* 0x20 */
467 /* 16 bit real mode data segment */
468 .word 0xffff,(RELOC&0xffff)
469 .byte (RELOC>>16),0x93,0x00,(RELOC>>24)
470
471 /* For 2.5.x the kernel segments have moved */
472
473 /* 0x28 dummy */
474 .quad 0
475
476 /* 0x30 dummy */
477 .quad 0
478 /* 0x38 dummy */
479 .quad 0
480 /* 0x40 dummy */
481 .quad 0
482 /* 0x48 dummy */
483 .quad 0
484 /* 0x50 dummy */
485 .quad 0
486 /* 0x58 dummy */
487 .quad 0
488
489
490 /* 0x60 */
491 .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb)
492 .word 0 # base address = 0
493 .word 0x9A00 # code read/exec
494 .word 0x00CF # granularity = 4096, 386
495 # (+5th nibble of limit)
496
497 /* 0x68 */
498 .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb)
499 .word 0 # base address = 0
500 .word 0x9200 # data read/write
501 .word 0x00CF # granularity = 4096, 386
502 # (+5th nibble of limit)
Ward Vandewegeebc92182008-08-06 20:37:38 +0000503
Stefan Reinauerb34eea32008-02-15 18:16:06 +0000504/*
505 * The layout of the per-CPU GDT under Linux:
506 *
507 * 0 - null
508 * 1 - reserved
509 * 2 - reserved
510 * 3 - reserved
511 *
512 * 4 - default user CS <==== new cacheline
513 * 5 - default user DS
514 *
515 * ------- start of TLS (Thread-Local Storage) segments:
516 *
517 * 6 - TLS segment #1 [ glibc's TLS segment ]
518 * 7 - TLS segment #2 [ Wine's %fs Win32 segment ]
519 * 8 - TLS segment #3
520 * 9 - reserved
521 * 10 - reserved
522 * 11 - reserved
523 *
524 * ------- start of kernel segments:
525 *
526 * 12 - kernel code segment <==== new cacheline
527 * 13 - kernel data segment
528 * 14 - TSS
529 * 15 - LDT
530 * 16 - PNPBIOS support (16->32 gate)
531 * 17 - PNPBIOS support
532 * 18 - PNPBIOS support
533 * 19 - PNPBIOS support
534 * 20 - PNPBIOS support
535 * 21 - APM BIOS support
536 * 22 - APM BIOS support
537 * 23 - APM BIOS support
538 */
539gdt_end:
540
Ward Vandewegeebc92182008-08-06 20:37:38 +0000541gdt64:
542 .word gdt64_end - gdt64
543 .long GDT64LOC
544 .word 0
545 .quad 0x0000000000000000 /* NULL descriptor */
546 .quad 0x00af9a000000ffff /* __KERNEL_CS */
547 .quad 0x00cf92000000ffff /* __KERNEL_DS */
548gdt64_end:
Stefan Reinauerb34eea32008-02-15 18:16:06 +0000549
550 .section ".trailer", "a"
551 /* Constants set at build time, these are at the very end of my image */
552 .balign 16
553 .global image_params
554image_params:
555
556convert_magic:
557 .long CONVERT_MAGIC
558gdt_size:
559 .long gdt_end - gdt
Ward Vandewegeebc92182008-08-06 20:37:38 +0000560gdt64_size:
561 .long gdt64_end - gdt64
562pgt_size:
563 .long 4096*6
Stefan Reinauerb34eea32008-02-15 18:16:06 +0000564bss_size:
Ward Vandewegeebc92182008-08-06 20:37:38 +0000565 .long bss_sizex
Stefan Reinauerb34eea32008-02-15 18:16:06 +0000566ramdisk_flags:
567 .word 0
568root_dev:
569 .word DEFAULT_ROOT_DEV
570entry:
571 .long 0
Ward Vandewegeebc92182008-08-06 20:37:38 +0000572switch_64:
573 .long 0
Stefan Reinauerb34eea32008-02-15 18:16:06 +0000574initrd_start:
575 .long 0
576initrd_size:
577 .long 0
578cmdline:
579 .asciz "BOOT_IMAGE=head.S console=ttyS0 ip=dhcp root=/dev/nfs"
580 .org cmdline + CMDLINE_MAX, 0
581cmdline_end: