2 * exec_kernel/user_space/head.S
4 * Copyright (C) 2000, 2002, 2003 Eric Biederman
6 * Parts of this code were take from the linux startup
7 * code of linux-2.4.0-test9
9 * Other parts were taken from etherboot-5.0.5
15 #define PROT_CODE_SEG 0x10
16 #define PROT_DATA_SEG 0x18
17 #define REAL_CODE_SEG 0x08
18 #define REAL_DATA_SEG 0x20
33 # Save the arguments safely out of the way
42 movl stack_start, %esp
57 # Move the gdt where Linux will not smash it during decompression
60 movl $(gdt_end - gdt), %ecx
63 # 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.
70 # Load the data segment registers
71 movl $ PROT_DATA_SEG, %eax
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
84 movl %eax, %esi # put the real mode pointer in a safe place
85 addl $16, %esp # pop the arguments
88 # Setup the registers before jumping to linux
95 # Flag to indicate we are the bootstrap processor
102 # Clear the unspecified registers for good measure
109 # do not clear esp, we still need to use lret later
119 /* We need to switch to 64bit before use startup_64 entry go to kernel */
121 * Prepare for entering 64 bit mode
123 # Move the gdt64 where Linux will not smash it during decompression
124 movl %esi, %eax # save the real mode pointer
127 movl $(gdt64_end - gdt64), %ecx
131 /* Load new GDT with the 64bit segments using 32bit descriptor */
134 /* Enable PAE mode */
140 * Build early 4G boot pagetable
142 /* Initialize Page tables to 0*/
145 movl $((4096*6)/4), %ecx
149 movl $(PGTLOC + 0), %edi
150 leal 0x1007 (%edi), %eax
154 movl $(PGTLOC + 0x1000), %edi
155 leal 0x1007(%edi), %eax
157 1: movl %eax, 0x00(%edi)
158 addl $0x00001000, %eax
164 movl $(PGTLOC + 0x2000), %edi
165 movl $0x00000183, %eax
167 1: movl %eax, 0(%edi)
168 addl $0x00200000, %eax
173 /* Enable the boot page tables */
177 /* Enable Long mode in EFER (Extended Feature Enable Register) */
178 movl $0xc0000080, %ecx
183 /* Preparing for 64bit jmp */
188 /* Enter paged protected Mode, activating Long Mode */
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.
204 /* Routines to query the BIOS... */
205 /**************************************************************************
206 E820_MEMSIZE - Get a listing of memory regions
207 **************************************************************************/
208 #define SMAP 0x534d4150
216 movl 8(%ebp), %edi /* Address to return e820 structures at */
218 movl 12(%ebp), %esi /* Maximum number of e820 structurs to return */
227 /* %di was setup earlier */
235 /* If this is useable memory, we save it by simply advancing %di by
244 cmpl $0, %ebx /* check to see if %ebx is set to EOF */
248 data32 call _real_to_prot
251 subl %esi, %eax /* Compute how many structure we read */
253 /* Restore everything else */
262 /**************************************************************************
263 MEME801 - Determine size of extended memory
264 **************************************************************************/
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.
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...
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
302 data32 call _real_to_prot
304 /* Restore Everything */
310 /**************************************************************************
311 MEM88 - Determine size of extended memory
312 **************************************************************************/
325 data32 call _real_to_prot
328 /* Restore Everything */
335 /**************************************************************************
336 BASEMEMSIZE - Get size of the conventional (base) memory
337 **************************************************************************/
344 DATA32 call _real_to_prot
349 /**************************************************************************
350 _REAL_TO_PROT - Go from REAL mode to Protected Mode
351 **************************************************************************/
357 addr32 lgdt gdt_48 - RELOC
360 movl %eax,%cr0 /* turn on protected mode */
362 /* flush prefetch queue, and reload %cs:%eip */
363 data32 ljmp $PROT_CODE_SEG,$1f
366 /* reload other segment registers */
367 movl $PROT_DATA_SEG,%eax
371 addl $RELOC,%esp /* Fix up stack pointer */
375 popl %eax /* Fix up return address */
379 /* switch to protected mode idt */
384 /**************************************************************************
385 _PROT_TO_REAL - Go from Protected Mode to REAL Mode
386 **************************************************************************/
391 subl $RELOC,%eax /* Adjust return address */
393 subl $RELOC,%esp /* Adjust stack pointer */
394 ljmp $REAL_CODE_SEG,$1f- RELOC /* jump to a 16 bit segment */
397 /* clear the PE bit of CR0 */
402 /* make intersegment jmp to flush the processor pipeline
403 * and reload %cs:%eip (to clear upper 16 bits of %eip).
405 data32 ljmp $(RELOC)>>4,$2f- RELOC
407 /* we are in real mode now
408 * set up the real mode segment registers : %ds, $ss, %es
417 /* Switch to the real mode idt */
419 addr32 lidt idt_real - RELOC
422 data32 ret /* There is a 32 bit return address on the stack */
430 .word 0x400 # idt limit = 256
433 .word 0 # idt limit = 0
434 .word 0, 0 # idt base = 0L
436 .word gdt_end - gdt - 1 # gdt limit=40,
438 .long GDTLOC # gdt base
441 # These need to be in a seperate section so I can be
442 # certain later activities dont stomp them
445 .word 0, 0, 0, 0 # dummy
448 /* 16 bit real mode code segment */
449 .word 0xffff,(RELOC&0xffff)
450 .byte (RELOC>>16),0x9b,0x00,(RELOC>>24)
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)
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)
467 /* 16 bit real mode data segment */
468 .word 0xffff,(RELOC&0xffff)
469 .byte (RELOC>>16),0x93,0x00,(RELOC>>24)
471 /* For 2.5.x the kernel segments have moved */
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)
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)
505 * The layout of the per-CPU GDT under Linux:
512 * 4 - default user CS <==== new cacheline
513 * 5 - default user DS
515 * ------- start of TLS (Thread-Local Storage) segments:
517 * 6 - TLS segment #1 [ glibc's TLS segment ]
518 * 7 - TLS segment #2 [ Wine's %fs Win32 segment ]
524 * ------- start of kernel segments:
526 * 12 - kernel code segment <==== new cacheline
527 * 13 - kernel data segment
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
542 .word gdt64_end - gdt64
545 .quad 0x0000000000000000 /* NULL descriptor */
546 .quad 0x00af9a000000ffff /* __KERNEL_CS */
547 .quad 0x00cf92000000ffff /* __KERNEL_DS */
550 .section ".trailer", "a"
551 /* Constants set at build time, these are at the very end of my image */
561 .long gdt64_end - gdt64
569 .word DEFAULT_ROOT_DEV
579 .asciz "BOOT_IMAGE=head.S console=ttyS0 ip=dhcp root=/dev/nfs"
580 .org cmdline + CMDLINE_MAX, 0