Moved os_regs into start32.S
[people/andreif/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 */
120         movl    52(%esp), %esp
121
122         /* Enable the virtual addresses */
123         leal    _phys_to_virt(%ebp), %eax
124         call    *%eax
125
126         /* Restore the callee save registers */
127         movl    os_regs + 32, %ebp
128         movl    os_regs + 36, %esi
129         movl    os_regs + 40, %edi
130         movl    os_regs + 44, %ebx
131         movl    os_regs + 48, %edx
132         movl    os_regs + 52, %esp
133
134         /* Get the C return value */
135         movl    os_regs + 28, %eax
136
137         jmpl    *%edx
138
139 #ifdef CONFIG_X86_64
140         .arch   sledgehammer
141 /**************************************************************************
142 XSTART_lm - Transfer control to the kernel just loaded in long mode
143 **************************************************************************/
144         .globl xstart_lm
145 xstart_lm:
146         /* Save the callee save registers */
147         pushl   %ebp
148         pushl   %esi
149         pushl   %edi
150         pushl   %ebx
151
152         /* Cache virt_offset && (virt_offset & 0xfffff000) */
153         movl    virt_offset, %ebp
154         movl    %ebp, %ebx
155         andl    $0xfffff000, %ebx
156
157         /* Switch to using physical addresses */
158         call    _virt_to_phys
159
160         /* Initialize the page tables */
161         /* Level 4 */
162         leal    0x23 + pgt_level3(%ebx), %eax
163         leal    pgt_level4(%ebx), %edi
164         movl    %eax, (%edi)
165
166         /* Level 3 */
167         leal    0x23 + pgt_level2(%ebx), %eax
168         leal    pgt_level3(%ebx), %edi
169         movl    %eax, 0x00(%edi)
170         addl    $4096, %eax
171         movl    %eax, 0x08(%edi)
172         addl    $4096, %eax
173         movl    %eax, 0x10(%edi)
174         addl    $4096, %eax
175         movl    %eax, 0x18(%edi)
176
177         /* Level 2 */
178         movl    $0xe3, %eax
179         leal    pgt_level2(%ebx), %edi
180         leal    16384(%edi), %esi
181 pgt_level2_loop:
182         movl    %eax, (%edi)
183         addl    $8, %edi
184         addl    $0x200000, %eax
185         cmp     %esi, %edi
186         jne     pgt_level2_loop
187
188         /* Point at the x86_64 page tables */
189         leal    pgt_level4(%ebx), %edi
190         movl    %edi, %cr3
191
192
193         /* Setup for the return from 64bit mode */
194         /* 64bit align the stack */
195         movl    %esp, %ebx              /* original stack pointer + 16 */
196         andl    $0xfffffff8, %esp
197
198         /* Save original stack pointer + 16 */
199         pushl   %ebx
200
201         /* Save virt_offset */
202         pushl   %ebp
203
204         /* Setup for the jmp to 64bit long mode */
205         leal    start_lm(%ebp), %eax
206         movl    %eax, 0x00 + start_lm_addr(%ebp)
207         movl    $LM_CODE_SEG, %eax
208         movl    %eax, 0x04 + start_lm_addr(%ebp)
209
210         /* Setup for the jump out of 64bit long mode */
211         leal    end_lm(%ebp), %eax
212         movl    %eax, 0x00 + end_lm_addr(%ebp)
213         movl    $FLAT_CODE_SEG, %eax
214         movl    %eax, 0x04 + end_lm_addr(%ebp)
215
216         /* Enable PAE mode */
217         movl    %cr4, %eax
218         orl     $X86_CR4_PAE, %eax
219         movl    %eax, %cr4
220
221         /* Enable long mode */
222         movl    $MSR_K6_EFER, %ecx
223         rdmsr
224         orl     $EFER_LME, %eax
225         wrmsr
226
227         /* Start paging, entering 32bit compatiblity mode */
228         movl    %cr0, %eax
229         orl     $CR0_PG, %eax
230         movl    %eax, %cr0
231
232         /* Enter 64bit long mode */
233         ljmp    *start_lm_addr(%ebp)
234         .code64
235 start_lm:
236         /* Load 64bit data segments */
237         movl    $LM_DATA_SEG, %eax
238         movl    %eax, %ds
239         movl    %eax, %es
240         movl    %eax, %ss
241
242         andq    $0xffffffff, %rbx
243         /* Get the address to jump to */
244         movl    20(%rbx), %edx
245         andq    $0xffffffff, %rdx
246         
247         /* Get the argument pointer */
248         movl    24(%rbx), %ebx
249         andq    $0xffffffff, %rbx
250
251         /* Jump to the 64bit code */
252         call    *%rdx
253
254         /* Preserve the result */
255         movl    %eax, %edx
256
257         /* Fixup %eflags */
258         cli
259         cld
260
261         /* Switch to 32bit compatibility mode */
262         ljmp    *end_lm_addr(%rip)
263
264         .code32
265 end_lm:
266         /* Disable paging */
267         movl    %cr0, %eax
268         andl    $~CR0_PG, %eax
269         movl    %eax, %cr0
270
271         /* Disable long mode */
272         movl    $MSR_K6_EFER, %ecx
273         rdmsr
274         andl    $~EFER_LME, %eax
275         wrmsr
276
277         /* Disable PAE */
278         movl    %cr4, %eax
279         andl    $~X86_CR4_PAE, %eax
280         movl    %eax, %cr4
281         
282         /* Compute virt_offset */
283         popl    %ebp
284
285         /* Compute the original stack pointer + 16 */
286         popl    %ebx
287         movl    %ebx, %esp
288
289         /* Enable the virtual addresses */
290         leal    _phys_to_virt(%ebp), %eax
291         call    *%eax
292
293         /* Restore the callee save registers */
294         popl    %ebx
295         popl    %esi
296         popl    %edi
297         popl    %ebp
298
299         /* Get the C return value */
300         movl    %edx, %eax
301
302         /* Return */
303         ret
304
305         .arch i386
306 #endif /* CONFIG_X86_64 */
307
308 #ifdef CONFIG_X86_64
309         .section ".bss"
310         .p2align 12
311         /* Include a dummy space in case we are loaded badly aligned */
312         .space 4096
313         /* Reserve enough space for a page table convering 4GB with 2MB pages */
314 pgt_level4:     
315         .space 4096
316 pgt_level3:     
317         .space 4096
318 pgt_level2:     
319         .space 16384
320 start_lm_addr:
321         .space  8
322 end_lm_addr:
323         .space  8
324 #endif