tly cosmetical. don't use movw because we use mov in most places.
[coreboot.git] / src / devices / oprom / x86_asm.S
1 /*
2  * This file is part of the coreboot project.
3  *
4  * Copyright (C) 2009-2010 coresystems GmbH
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19
20 #define REALMODE_BASE           0x600
21 #define RELOCATED(x)    (x - __realmode_code + REALMODE_BASE)
22
23 /* CR0 bits */
24 #define PE              (1 << 0)
25
26 /* This is the intXX interrupt handler stub code. It gets copied
27  * to the IDT and to some fixed addresses in the F segment. Before
28  * the code can used, it gets patched up by the C function copying
29  * it: byte 3 (the $0 in movb $0, %al) is overwritten with the int#.
30  */
31
32         .code16
33         .globl __idt_handler
34 __idt_handler:
35         pushal
36         movb    $0, %al /* This instruction gets modified */
37         ljmp    $0, $__interrupt_handler_16bit
38         .globl __idt_handler_size
39 __idt_handler_size = ( . - __idt_handler)
40
41
42 /* In order to be independent of coreboot's position in RAM
43  * we relocate a part of the code to the low megabyte, so the
44  * CPU can use it in real-mode. This code lives at __realmode_code.
45  */
46         .globl __realmode_code
47 __realmode_code:
48
49 /* Realmode IDT pointer structure. */
50         .globl __realmode_idt
51 __realmode_idt = RELOCATED(.)
52         .word 1023      /* 16 bit limit */
53         .long 0         /* 24 bit base */
54         .word 0
55
56 /* Preserve old stack */
57 __stack = RELOCATED(.)
58         .long 0
59
60 /* Register store for realmode_call and realmode_interrupt */
61 __registers = RELOCATED(.)
62         .long 0 /*  0 - EAX */
63         .long 0 /*  4 - EBX */
64         .long 0 /*  8 - ECX */
65         .long 0 /* 12 - EDX */
66         .long 0 /* 16 - ESI */
67         .long 0 /* 20 - EDI */
68
69         .code32
70         .globl __realmode_call
71 __realmode_call = RELOCATED(.)
72         /* save all registers to the stack */
73         pushal
74
75         /* Move the protected mode stack to a safe place */
76         movl    %esp, __stack
77         movl    %esp, %ebp
78
79         /* This function is called with regparm=0 and we have
80          * to skip the 32 byte from pushal. Hence start at 36.
81          */
82
83         /* entry point */
84         movl    36(%ebp), %eax
85         mov     %ax, __lcall_instr + 1
86         andl    $0xffff0000, %eax
87         shrl    $4, %eax
88         mov     %ax, __lcall_instr + 3
89
90         /* initial register values */
91         movl    40(%ebp), %eax
92         movl    %eax, __registers +  0 /* eax */
93         movl    44(%ebp), %eax
94         movl    %eax, __registers +  4 /* ebx */
95         movl    48(%ebp), %eax
96         movl    %eax, __registers +  8 /* ecx */
97         movl    52(%ebp), %eax
98         movl    %eax, __registers + 12 /* edx */
99         movl    56(%ebp), %eax
100         movl    %eax, __registers + 16 /* esi */
101         movl    60(%ebp), %eax
102         movl    %eax, __registers + 20 /* edi */
103
104         /* Activate the right segment descriptor real mode. */
105         ljmp    $0x28, $RELOCATED(1f)
106 1:
107 .code16
108         /* 16 bit code from here on... */
109
110         /* Load the segment registers w/ properly configured
111          * segment descriptors. They will retain these
112          * configurations (limits, writability, etc.) once
113          * protected mode is turned off.
114          */
115         mov     $0x30, %ax
116         mov     %ax, %ds
117         mov     %ax, %es
118         mov     %ax, %fs
119         mov     %ax, %gs
120         mov     %ax, %ss
121
122         /* Turn off protection */
123         movl    %cr0, %eax
124         andl    $~PE, %eax
125         movl    %eax, %cr0
126
127         /* Now really going into real mode */
128         ljmp    $0, $RELOCATED(1f)
129 1:
130         /* Setup a stack: Put the stack at the end of page zero.
131          * That way we can easily share it between real and
132          * protected, since the 16 bit ESP at segment 0 will
133          * work for any case. */
134         mov     $0x0, %ax
135         mov     %ax, %ss
136         movl    $0x1000, %eax
137         movl    %eax, %esp
138
139         /* Load 16 bit IDT */
140         xor     %ax, %ax
141         mov     %ax, %ds
142         lidt    __realmode_idt
143
144         /* initialize registers for option rom lcall */
145         movl    __registers +  0, %eax
146         movl    __registers +  4, %ebx
147         movl    __registers +  8, %ecx
148         movl    __registers + 12, %edx
149         movl    __registers + 16, %esi
150         movl    __registers + 20, %edi
151
152         /* Set all segments to 0x0000, ds to 0x0040 */
153         push    %ax
154         xor     %ax, %ax
155         mov     %ax, %es
156         mov     %ax, %fs
157         mov     %ax, %gs
158         mov     $0x40, %ax
159         mov     %ax, %ds
160         pop     %ax
161
162         /* ************************************ */
163 __lcall_instr = RELOCATED(.)
164         .byte 0x9a
165         .word 0x0000, 0x0000
166         /* ************************************ */
167
168         /* If we got here, we are just about done.
169          * Need to get back to protected mode.
170          */
171         movl    %cr0, %eax
172         orl     $PE, %eax
173         movl    %eax, %cr0
174
175         /* Now that we are in protected mode
176          * jump to a 32 bit code segment.
177          */
178         data32  ljmp    $0x10, $RELOCATED(1f)
179 1:
180         .code32
181         mov     $0x18, %ax
182         mov     %ax, %ds
183         mov     %ax, %es
184         mov     %ax, %fs
185         mov     %ax, %gs
186         mov     %ax, %ss
187
188         /* restore proper idt */
189         lidt    idtarg
190
191         /* and exit */
192         movl    __stack, %esp
193         popal
194
195         // TODO return AX from OPROM call
196         ret
197
198         .globl __realmode_interrupt
199 __realmode_interrupt = RELOCATED(.)
200         /* save all registers to the stack */
201         pushal
202         /* save the stack */
203         movl    %esp, __stack
204         movl    %esp, %ebp
205
206         /* This function is called with regparm=0 and we have
207          * to skip the 32 byte from pushal. Hence start at 36.
208          */
209
210         /* prepare interrupt calling code */
211         movl    36(%ebp), %eax
212         movb    %al, __intXX_instr + 1 /* intno */
213
214         /* initial register values */
215         movl    40(%ebp), %eax
216         movl    %eax, __registers +  0 /* eax */
217         movl    44(%ebp), %eax
218         movl    %eax, __registers +  4 /* ebx */
219         movl    48(%ebp), %eax
220         movl    %eax, __registers +  8 /* ecx */
221         movl    52(%ebp), %eax
222         movl    %eax, __registers + 12 /* edx */
223         movl    56(%ebp), %eax
224         movl    %eax, __registers + 16 /* esi */
225         movl    60(%ebp), %eax
226         movl    %eax, __registers + 20 /* edi */
227
228         /*  This configures CS properly for real mode. */
229         ljmp    $0x28, $RELOCATED(1f)
230 1:
231         .code16 /* 16 bit code from here on... */
232
233         /* Load the segment registers w/ properly configured segment
234          * descriptors.  They will retain these configurations (limits,
235          * writability, etc.) once protected mode is turned off.
236          */
237         mov     $0x30, %ax
238         mov     %ax, %ds
239         mov     %ax, %es
240         mov     %ax, %fs
241         mov     %ax, %gs
242         mov     %ax, %ss
243
244         /* Turn off protected mode */
245         movl    %cr0, %eax
246         andl    $~PE, %eax
247         movl    %eax, %cr0
248
249         /* Now really going into real mode */
250         data32 ljmp     $0, $RELOCATED(1f)
251 1:
252
253         /* put the stack at the end of page zero.  That way we can easily 
254          * share it between real mode and protected mode, because %esp and
255          * %ss:%sp point to the same memory.
256          */
257         /* setup a stack */
258         mov     $0x0, %ax
259         mov     %ax, %ss
260         movl    $0x1000, %eax
261         movl    %eax, %esp
262
263         /* Load 16-bit intXX IDT */
264         xor     %ax, %ax
265         mov     %ax, %ds
266         lidt    __realmode_idt
267
268         /* initialize registers for intXX call */
269         movl    __registers +  0, %eax
270         movl    __registers +  4, %ebx
271         movl    __registers +  8, %ecx
272         movl    __registers + 12, %edx
273         movl    __registers + 16, %esi
274         movl    __registers + 20, %edi
275
276         /* Set all segments to 0x0000 */
277         push    %ax
278         xor     %ax, %ax
279         mov     %ax, %ds
280         mov     %ax, %es
281         mov     %ax, %fs
282         mov     %ax, %gs
283         pop     %ax
284
285 __intXX_instr = RELOCATED(.)
286         .byte 0xcd, 0x00 /* This becomes intXX */
287
288         /* Ok, the job is done, now go back to protected mode coreboot */
289         movl    %cr0, %eax
290         orl     $PE, %eax
291         movl    %eax, %cr0
292
293         /* Now that we are in protected mode jump to a 32-bit code segment. */
294         data32  ljmp    $0x10, $RELOCATED(1f)
295 1:
296         .code32
297         mov     $0x18, %ax
298         mov     %ax, %ds
299         mov     %ax, %es
300         mov     %ax, %fs
301         mov     %ax, %gs
302         mov     %ax, %ss
303
304         /* restore coreboot's 32-bit IDT */
305         lidt    idtarg
306
307         /* Exit */
308         movl    __stack, %esp
309         popal
310         ret
311
312 /* This is the 16-bit interrupt entry point called by the IDT stub code.
313  *
314  * Before this code code is called, %eax is pushed to the stack, and the
315  * interrupt number is loaded into %al. On return this function cleans up
316  * for its caller.
317  */
318         .code16
319 __interrupt_handler_16bit = RELOCATED(.)
320         push    %ds
321         push    %es
322         push    %fs
323         push    %gs
324
325         /* Clean up the interrupt number. We could have done this in the stub,
326          * but it would have cost 2 more bytes per stub entry.
327          */
328         andl    $0xff, %eax
329         pushl   %eax            /* ... and make it the first parameter */
330
331         /* Switch to protected mode */
332         movl    %cr0, %eax
333         orl     $PE, %eax
334         movl    %eax, %cr0
335
336         /* ... and jump to a 32 bit code segment. */
337         data32 ljmp    $0x10, $RELOCATED(1f)
338 1:
339         .code32
340         mov     $0x18, %ax
341         mov     %ax, %ds
342         mov     %ax, %es
343         mov     %ax, %fs
344         mov     %ax, %gs
345         mov     %ax, %ss
346
347         lidt    idtarg
348
349         /* Call the C interrupt handler */
350         movl    $interrupt_handler, %eax
351         call    *%eax
352
353         /* Now return to real mode ... */
354         ljmp    $0x28, $RELOCATED(1f)
355 1:
356         .code16
357         /* Load the segment registers with properly configured segment
358          * descriptors.  They will retain these configurations (limits,
359          * writability, etc.) once protected mode is turned off.
360          */
361         mov     $0x30, %ax
362         mov     %ax, %ds
363         mov     %ax, %es
364         mov     %ax, %fs
365         mov     %ax, %gs
366         mov     %ax, %ss
367
368         /* Disable Protected Mode */
369         movl    %cr0, %eax
370         andl    $~PE, %eax
371         movl    %eax, %cr0
372
373         /* Now really going into real mode */
374         ljmp $0,  $RELOCATED(1f)
375 1:
376         /* Restore real-mode stack segment */
377         mov     $0x0, %ax
378         mov     %ax, %ss
379
380         /* Restore 16 bit IDT */
381         xor     %ax, %ax
382         mov     %ax, %ds
383         lidt    __realmode_idt
384
385         /* Restore all registers, including those
386          * manipulated by the C handler
387          */
388         popl    %eax
389         pop     %gs
390         pop     %fs
391         pop     %es
392         pop     %ds
393         popal
394         iret
395
396         .globl __realmode_code_size
397 __realmode_code_size = (. - __realmode_code)
398
399         .code32