Merge from Etherboot 5.4
[people/balajirrao/gpxe.git] / src / arch / i386 / core / start32.S
1 #include "virtaddr.h"
2         
3         .equ    MSR_K6_EFER,   0xC0000080
4         .equ    EFER_LME,      0x00000100
5         .equ    X86_CR4_PAE,   0x00000020
6         .equ    CR0_PG,        0x80000000
7
8 #ifdef  GAS291
9 #define DATA32 data32;
10 #define ADDR32 addr32;
11 #define LJMPI(x)        ljmp    x
12 #else
13 #define DATA32 data32
14 #define ADDR32 addr32
15 /* newer GAS295 require #define LJMPI(x)        ljmp    *x */
16 #define LJMPI(x)        ljmp    x
17 #endif
18
19 /*
20  * NOTE: if you write a subroutine that is called from C code (gcc/egcs),
21  * then you only have to take care of %ebx, %esi, %edi and %ebp.  These
22  * registers must not be altered under any circumstance.  All other registers
23  * may be clobbered without any negative side effects.  If you don't follow
24  * this rule then you'll run into strange effects that only occur on some
25  * gcc versions (because the register allocator may use different registers).
26  *
27  * All the data32 prefixes for the ljmp instructions are necessary, because
28  * the assembler emits code with a relocation address of 0.  This means that
29  * all destinations are initially negative, which the assembler doesn't grok,
30  * because for some reason negative numbers don't fit into 16 bits. The addr32
31  * prefixes are there for the same reasons, because otherwise the memory
32  * references are only 16 bit wide.  Theoretically they are all superfluous.
33  * One last note about prefixes: the data32 prefixes on all call _real_to_prot
34  * instructions could be removed if the _real_to_prot function is changed to
35  * deal correctly with 16 bit return addresses.  I tried it, but failed.
36  */
37
38         .text
39         .arch i386
40         .code32
41
42         /* This is a struct os_entry_regs */
43         .globl os_regs
44 os_regs:        .space 56
45                 
46 /**************************************************************************
47 XSTART32 - Transfer control to the kernel just loaded
48 **************************************************************************/
49         .globl xstart32
50 xstart32:
51         /* Save the callee save registers */
52         movl    %ebp, os_regs + 32
53         movl    %esi, os_regs + 36
54         movl    %edi, os_regs + 40
55         movl    %ebx, os_regs + 44
56
57         /* save the return address */
58         popl    %eax
59         movl    %eax, os_regs + 48
60
61         /* save the stack pointer */
62         movl    %esp, os_regs + 52
63
64         /* Get the new destination address */
65         popl    %ecx
66
67         /* Store the physical address of xend on the stack */
68         movl    $xend32, %ebx
69         addl    virt_offset, %ebx
70         pushl   %ebx
71
72         /* Store the destination address on the stack */
73         pushl   $PHYSICAL_CS
74         pushl   %ecx
75
76         /* Cache virt_offset */
77         movl    virt_offset, %ebp
78         
79         /* Switch to using physical addresses */
80         call    _virt_to_phys
81
82         /* Save the target stack pointer */
83         movl    %esp, os_regs + 12(%ebp)
84         leal    os_regs(%ebp), %esp
85
86         /* Store the pointer to os_regs */
87         movl    %esp, os_regs_ptr(%ebp)
88
89         /* Load my new registers */
90         popal
91         movl    (-32 + 12)(%esp), %esp
92
93         /* Jump to the new kernel
94          * The lret switches to a flat code segment
95          */
96         lret
97
98         .balign 4
99         .globl xend32
100 xend32:
101         /* Fixup %eflags */
102         nop
103         cli
104         cld
105         
106         /* Load %esp with &os_regs + virt_offset */
107         .byte   0xbc /* movl $0, %esp */
108 os_regs_ptr:
109         .long   0
110
111         /* Save the result registers */
112         addl    $32, %esp
113         pushal
114
115         /* Compute virt_offset */
116         movl    %esp, %ebp
117         subl    $os_regs, %ebp
118         
119         /* Load the stack pointer and convert it to physical address */
120         movl    52(%esp), %esp
121         addl    %ebp, %esp
122
123         /* Enable the virtual addresses */
124         leal    _phys_to_virt(%ebp), %eax
125         call    *%eax
126
127         /* Restore the callee save registers */
128         movl    os_regs + 32, %ebp
129         movl    os_regs + 36, %esi
130         movl    os_regs + 40, %edi
131         movl    os_regs + 44, %ebx
132         movl    os_regs + 48, %edx
133         movl    os_regs + 52, %esp
134
135         /* Get the C return value */
136         movl    os_regs + 28, %eax
137
138         jmpl    *%edx
139
140 #ifdef CONFIG_X86_64
141         .arch   sledgehammer
142 /**************************************************************************
143 XSTART_lm - Transfer control to the kernel just loaded in long mode
144 **************************************************************************/
145         .globl xstart_lm
146 xstart_lm:
147         /* Save the callee save registers */
148         pushl   %ebp
149         pushl   %esi
150         pushl   %edi
151         pushl   %ebx
152
153         /* Cache virt_offset && (virt_offset & 0xfffff000) */
154         movl    virt_offset, %ebp
155         movl    %ebp, %ebx
156         andl    $0xfffff000, %ebx
157
158         /* Switch to using physical addresses */
159         call    _virt_to_phys
160
161         /* Initialize the page tables */
162         /* Level 4 */
163         leal    0x23 + pgt_level3(%ebx), %eax
164         leal    pgt_level4(%ebx), %edi
165         movl    %eax, (%edi)
166
167         /* Level 3 */
168         leal    0x23 + pgt_level2(%ebx), %eax
169         leal    pgt_level3(%ebx), %edi
170         movl    %eax, 0x00(%edi)
171         addl    $4096, %eax
172         movl    %eax, 0x08(%edi)
173         addl    $4096, %eax
174         movl    %eax, 0x10(%edi)
175         addl    $4096, %eax
176         movl    %eax, 0x18(%edi)
177
178         /* Level 2 */
179         movl    $0xe3, %eax
180         leal    pgt_level2(%ebx), %edi
181         leal    16384(%edi), %esi
182 pgt_level2_loop:
183         movl    %eax, (%edi)
184         addl    $8, %edi
185         addl    $0x200000, %eax
186         cmp     %esi, %edi
187         jne     pgt_level2_loop
188
189         /* Point at the x86_64 page tables */
190         leal    pgt_level4(%ebx), %edi
191         movl    %edi, %cr3
192
193
194         /* Setup for the return from 64bit mode */
195         /* 64bit align the stack */
196         movl    %esp, %ebx              /* original stack pointer + 16 */
197         andl    $0xfffffff8, %esp
198
199         /* Save original stack pointer + 16 */
200         pushl   %ebx
201
202         /* Save virt_offset */
203         pushl   %ebp
204
205         /* Setup for the jmp to 64bit long mode */
206         leal    start_lm(%ebp), %eax
207         movl    %eax, 0x00 + start_lm_addr(%ebp)
208         movl    $LM_CODE_SEG, %eax
209         movl    %eax, 0x04 + start_lm_addr(%ebp)
210
211         /* Setup for the jump out of 64bit long mode */
212         leal    end_lm(%ebp), %eax
213         movl    %eax, 0x00 + end_lm_addr(%ebp)
214         movl    $FLAT_CODE_SEG, %eax
215         movl    %eax, 0x04 + end_lm_addr(%ebp)
216
217         /* Enable PAE mode */
218         movl    %cr4, %eax
219         orl     $X86_CR4_PAE, %eax
220         movl    %eax, %cr4
221
222         /* Enable long mode */
223         movl    $MSR_K6_EFER, %ecx
224         rdmsr
225         orl     $EFER_LME, %eax
226         wrmsr
227
228         /* Start paging, entering 32bit compatiblity mode */
229         movl    %cr0, %eax
230         orl     $CR0_PG, %eax
231         movl    %eax, %cr0
232
233         /* Enter 64bit long mode */
234         ljmp    *start_lm_addr(%ebp)
235         .code64
236 start_lm:
237         /* Load 64bit data segments */
238         movl    $LM_DATA_SEG, %eax
239         movl    %eax, %ds
240         movl    %eax, %es
241         movl    %eax, %ss
242
243         andq    $0xffffffff, %rbx
244         /* Get the address to jump to */
245         movl    20(%rbx), %edx
246         andq    $0xffffffff, %rdx
247         
248         /* Get the argument pointer */
249         movl    24(%rbx), %ebx
250         andq    $0xffffffff, %rbx
251
252         /* Jump to the 64bit code */
253         call    *%rdx
254
255         /* Preserve the result */
256         movl    %eax, %edx
257
258         /* Fixup %eflags */
259         cli
260         cld
261
262         /* Switch to 32bit compatibility mode */
263         ljmp    *end_lm_addr(%rip)
264
265         .code32
266 end_lm:
267         /* Disable paging */
268         movl    %cr0, %eax
269         andl    $~CR0_PG, %eax
270         movl    %eax, %cr0
271
272         /* Disable long mode */
273         movl    $MSR_K6_EFER, %ecx
274         rdmsr
275         andl    $~EFER_LME, %eax
276         wrmsr
277
278         /* Disable PAE */
279         movl    %cr4, %eax
280         andl    $~X86_CR4_PAE, %eax
281         movl    %eax, %cr4
282         
283         /* Compute virt_offset */
284         popl    %ebp
285
286         /* Compute the original stack pointer + 16 */
287         popl    %ebx
288         movl    %ebx, %esp
289
290         /* Enable the virtual addresses */
291         leal    _phys_to_virt(%ebp), %eax
292         call    *%eax
293
294         /* Restore the callee save registers */
295         popl    %ebx
296         popl    %esi
297         popl    %edi
298         popl    %ebp
299
300         /* Get the C return value */
301         movl    %edx, %eax
302
303         /* Return */
304         ret
305
306         .arch i386
307 #endif /* CONFIG_X86_64 */
308
309 #ifdef CONFIG_X86_64
310         .section ".bss"
311         .p2align 12
312         /* Include a dummy space in case we are loaded badly aligned */
313         .space 4096
314         /* Reserve enough space for a page table convering 4GB with 2MB pages */
315 pgt_level4:     
316         .space 4096
317 pgt_level3:     
318         .space 4096
319 pgt_level2:     
320         .space 16384
321 start_lm_addr:
322         .space  8
323 end_lm_addr:
324         .space  8
325 #endif