b35c62a983b9ae90ac8553a17763faeb5dfef18a
[coreboot.git] / util / mkelfImage / linux-i386 / head.S
1 /*
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
29 startup_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
40 1:      
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
62
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.
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
98         movl    switch_64, %eax
99         cmp     $1, %eax
100         jz      switch_to_64
101
102         # Clear the unspecified registers for good measure
103         xorl    %eax, %eax
104         xorl    %ecx, %ecx
105         xorl    %edx, %edx
106         xorl    %edi, %edi
107         xorl    %ebp, %ebp
108
109         # do not clear esp, we still need to use lret later
110
111         pushl $PROT_CODE_SEG
112         movl entry, %eax
113         pushl %eax
114
115         lret
116
117 switch_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
157 1:     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
167 1:     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
202
203
204         /* Routines to query the BIOS... */
205 /**************************************************************************
206 E820_MEMSIZE - Get a listing of memory regions
207 **************************************************************************/
208 #define SMAP    0x534d4150
209         .globl  meme820
210 meme820:
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
223 jmpe820:
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
234 good820:        
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
243 again820:
244         cmpl    $0, %ebx        /* check to see if %ebx is set to EOF */
245         jne     jmpe820
246
247 bail820:
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 /**************************************************************************
263 MEME801 - Determine size of extended memory 
264 **************************************************************************/
265         .globl meme801
266 meme801:
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
290 e801usecxdx:
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
298 e801absent:
299         xorl    %eax,%eax
300
301 e801out:        
302         data32 call     _real_to_prot
303         .code32
304         /* Restore Everything */
305         popl    %edi
306         popl    %esi
307         popl    %ebx
308         ret
309
310 /**************************************************************************
311 MEM88 - Determine size of extended memory 
312 **************************************************************************/
313         .globl mem88
314 mem88:
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 /**************************************************************************
336 BASEMEMSIZE - Get size of the conventional (base) memory
337 **************************************************************************/
338         .globl  basememsize
339 basememsize:
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
364 1:
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 */
395 1:
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
406 2:
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
425 boot_type:      .long 0
426 boot_data:      .long 0
427 boot_param:     .long 0
428
429 idt_real:
430         .word   0x400                           # idt limit = 256
431         .word   0, 0
432 idt_48:
433         .word   0                               # idt limit = 0
434         .word   0, 0                            # idt base = 0L
435 gdt_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
443 gdt:
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)
503
504 /*
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  */
539 gdt_end:
540
541 gdt64:
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 */
548 gdt64_end:
549         
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
554 image_params:
555
556 convert_magic:
557         .long   CONVERT_MAGIC
558 gdt_size:
559         .long   gdt_end - gdt
560 gdt64_size:
561         .long   gdt64_end - gdt64
562 pgt_size:
563         .long   4096*6
564 bss_size:
565         .long   bss_sizex
566 ramdisk_flags:
567         .word   0
568 root_dev:
569         .word   DEFAULT_ROOT_DEV
570 entry:
571         .long   0
572 switch_64:
573         .long   0
574 initrd_start:
575         .long   0
576 initrd_size:
577         .long   0
578 cmdline:
579         .asciz "BOOT_IMAGE=head.S console=ttyS0 ip=dhcp root=/dev/nfs"
580         .org cmdline + CMDLINE_MAX, 0
581 cmdline_end: