x86, oprom: ensure DF is always cleared
[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 /* 256 byte buffer, used by int10 */
70         .globl __buffer
71 __buffer = RELOCATED(.)
72         .skip 256
73
74         .code32
75         .globl __realmode_call
76 __realmode_call = RELOCATED(.)
77         /* save all registers to the stack */
78         pusha
79         pushf
80
81         /* Move the protected mode stack pointer to a safe place */
82         movl    %esp, __stack
83         movl    %esp, %ebp
84
85         /* This function is called with regparm=0 and we have to
86          * skip the 36 byte from pushf+pusha. Hence start at 40.
87          */
88
89         /* entry point */
90         movl    40(%ebp), %eax
91         mov     %ax, __lcall_instr + 1
92         andl    $0xffff0000, %eax
93         shrl    $4, %eax
94         mov     %ax, __lcall_instr + 3
95
96         /* initial register values */
97         movl    44(%ebp), %eax
98         movl    %eax, __registers +  0 /* eax */
99         movl    48(%ebp), %eax
100         movl    %eax, __registers +  4 /* ebx */
101         movl    52(%ebp), %eax
102         movl    %eax, __registers +  8 /* ecx */
103         movl    56(%ebp), %eax
104         movl    %eax, __registers + 12 /* edx */
105         movl    60(%ebp), %eax
106         movl    %eax, __registers + 16 /* esi */
107         movl    64(%ebp), %eax
108         movl    %eax, __registers + 20 /* edi */
109
110         /* Activate the right segment descriptor real mode. */
111         ljmp    $0x28, $RELOCATED(1f)
112 1:
113 .code16
114         /* 16 bit code from here on... */
115
116         /* Load the segment registers w/ properly configured
117          * segment descriptors. They will retain these
118          * configurations (limits, writability, etc.) once
119          * protected mode is turned off.
120          */
121         mov     $0x30, %ax
122         mov     %ax, %ds
123         mov     %ax, %es
124         mov     %ax, %fs
125         mov     %ax, %gs
126         mov     %ax, %ss
127
128         /* Turn off protection */
129         movl    %cr0, %eax
130         andl    $~PE, %eax
131         movl    %eax, %cr0
132
133         /* Now really going into real mode */
134         ljmp    $0, $RELOCATED(1f)
135 1:
136         /* Setup a stack: Put the stack at the end of page zero.
137          * That way we can easily share it between real and
138          * protected, since the 16 bit ESP at segment 0 will
139          * work for any case. */
140         mov     $0x0, %ax
141         mov     %ax, %ss
142         movl    $0x1000, %eax
143         movl    %eax, %esp
144
145         /* Load 16 bit IDT */
146         xor     %ax, %ax
147         mov     %ax, %ds
148         lidt    __realmode_idt
149
150         /* initialize registers for option rom lcall */
151         movl    __registers +  0, %eax
152         movl    __registers +  4, %ebx
153         movl    __registers +  8, %ecx
154         movl    __registers + 12, %edx
155         movl    __registers + 16, %esi
156         movl    __registers + 20, %edi
157
158         /* Set all segments to 0x0000, ds to 0x0040 */
159         push    %ax
160         xor     %ax, %ax
161         mov     %ax, %es
162         mov     %ax, %fs
163         mov     %ax, %gs
164         mov     $0x40, %ax
165         mov     %ax, %ds
166         pop     %ax
167
168         /* ************************************ */
169 __lcall_instr = RELOCATED(.)
170         .byte 0x9a
171         .word 0x0000, 0x0000
172         /* ************************************ */
173
174         /* If we got here, we are just about done.
175          * Need to get back to protected mode.
176          */
177         movl    %cr0, %eax
178         orl     $PE, %eax
179         movl    %eax, %cr0
180
181         /* Now that we are in protected mode
182          * jump to a 32 bit code segment.
183          */
184         data32  ljmp    $0x10, $RELOCATED(1f)
185 1:
186         .code32
187         mov     $0x18, %ax
188         mov     %ax, %ds
189         mov     %ax, %es
190         mov     %ax, %fs
191         mov     %ax, %gs
192         mov     %ax, %ss
193
194         /* restore proper idt */
195         lidt    idtarg
196
197         /* restore stack pointer, eflags and register values */
198         movl    __stack, %esp
199         popf
200         popa
201
202         /* and exit */
203         // TODO return AX from OPROM call
204         ret
205
206         .globl __realmode_interrupt
207 __realmode_interrupt = RELOCATED(.)
208         /* save all registers to the stack */
209         pusha
210         pushf
211
212         /* save the stack pointer */
213         movl    %esp, __stack
214         movl    %esp, %ebp
215
216         /* This function is called with regparm=0 and we have to
217          * skip the 36 byte from pushf+pusha. Hence start at 40.
218          */
219
220         /* prepare interrupt calling code */
221         movl    40(%ebp), %eax
222         movb    %al, __intXX_instr + 1 /* intno */
223
224         /* initial register values */
225         movl    44(%ebp), %eax
226         movl    %eax, __registers +  0 /* eax */
227         movl    48(%ebp), %eax
228         movl    %eax, __registers +  4 /* ebx */
229         movl    52(%ebp), %eax
230         movl    %eax, __registers +  8 /* ecx */
231         movl    56(%ebp), %eax
232         movl    %eax, __registers + 12 /* edx */
233         movl    60(%ebp), %eax
234         movl    %eax, __registers + 16 /* esi */
235         movl    64(%ebp), %eax
236         movl    %eax, __registers + 20 /* edi */
237
238         /*  This configures CS properly for real mode. */
239         ljmp    $0x28, $RELOCATED(1f)
240 1:
241         .code16 /* 16 bit code from here on... */
242
243         /* Load the segment registers w/ properly configured segment
244          * descriptors.  They will retain these configurations (limits,
245          * writability, etc.) once protected mode is turned off.
246          */
247         mov     $0x30, %ax
248         mov     %ax, %ds
249         mov     %ax, %es
250         mov     %ax, %fs
251         mov     %ax, %gs
252         mov     %ax, %ss
253
254         /* Turn off protected mode */
255         movl    %cr0, %eax
256         andl    $~PE, %eax
257         movl    %eax, %cr0
258
259         /* Now really going into real mode */
260         data32 ljmp     $0, $RELOCATED(1f)
261 1:
262
263         /* put the stack at the end of page zero.  That way we can easily
264          * share it between real mode and protected mode, because %esp and
265          * %ss:%sp point to the same memory.
266          */
267         /* setup a stack */
268         mov     $0x0, %ax
269         mov     %ax, %ss
270         movl    $0x1000, %eax
271         movl    %eax, %esp
272
273         /* Load 16-bit intXX IDT */
274         xor     %ax, %ax
275         mov     %ax, %ds
276         lidt    __realmode_idt
277
278         /* initialize registers for intXX call */
279         movl    __registers +  0, %eax
280         movl    __registers +  4, %ebx
281         movl    __registers +  8, %ecx
282         movl    __registers + 12, %edx
283         movl    __registers + 16, %esi
284         movl    __registers + 20, %edi
285
286         /* Set all segments to 0x0000 */
287         push    %ax
288         xor     %ax, %ax
289         mov     %ax, %ds
290         mov     %ax, %es
291         mov     %ax, %fs
292         mov     %ax, %gs
293         pop     %ax
294
295 __intXX_instr = RELOCATED(.)
296         .byte 0xcd, 0x00 /* This becomes intXX */
297
298         /* Ok, the job is done, now go back to protected mode coreboot */
299         movl    %cr0, %eax
300         orl     $PE, %eax
301         movl    %eax, %cr0
302
303         /* Now that we are in protected mode jump to a 32-bit code segment. */
304         data32  ljmp    $0x10, $RELOCATED(1f)
305 1:
306         .code32
307         mov     $0x18, %ax
308         mov     %ax, %ds
309         mov     %ax, %es
310         mov     %ax, %fs
311         mov     %ax, %gs
312         mov     %ax, %ss
313
314         /* restore coreboot's 32-bit IDT */
315         lidt    idtarg
316
317         /* restore stack pointer, eflags and register values and exit */
318         movl    __stack, %esp
319         popf
320         popa
321         ret
322
323 /* This is the 16-bit interrupt entry point called by the IDT stub code.
324  *
325  * Before this code code is called, %eax is pushed to the stack, and the
326  * interrupt number is loaded into %al. On return this function cleans up
327  * for its caller.
328  */
329         .code16
330 __interrupt_handler_16bit = RELOCATED(.)
331         push    %ds
332         push    %es
333         push    %fs
334         push    %gs
335
336         /* Clear DF to not break ABI assumptions */
337         cld
338
339         /* Clean up the interrupt number. We could have done this in the stub,
340          * but it would have cost 2 more bytes per stub entry.
341          */
342         andl    $0xff, %eax
343         pushl   %eax            /* ... and make it the first parameter */
344
345         /* Switch to protected mode */
346         movl    %cr0, %eax
347         orl     $PE, %eax
348         movl    %eax, %cr0
349
350         /* ... and jump to a 32 bit code segment. */
351         data32 ljmp    $0x10, $RELOCATED(1f)
352 1:
353         .code32
354         mov     $0x18, %ax
355         mov     %ax, %ds
356         mov     %ax, %es
357         mov     %ax, %fs
358         mov     %ax, %gs
359         mov     %ax, %ss
360
361         lidt    idtarg
362
363         /* Call the C interrupt handler */
364         movl    $interrupt_handler, %eax
365         call    *%eax
366
367         /* Now return to real mode ... */
368         ljmp    $0x28, $RELOCATED(1f)
369 1:
370         .code16
371         /* Load the segment registers with properly configured segment
372          * descriptors.  They will retain these configurations (limits,
373          * writability, etc.) once protected mode is turned off.
374          */
375         mov     $0x30, %ax
376         mov     %ax, %ds
377         mov     %ax, %es
378         mov     %ax, %fs
379         mov     %ax, %gs
380         mov     %ax, %ss
381
382         /* Disable Protected Mode */
383         movl    %cr0, %eax
384         andl    $~PE, %eax
385         movl    %eax, %cr0
386
387         /* Now really going into real mode */
388         ljmp $0,  $RELOCATED(1f)
389 1:
390         /* Restore real-mode stack segment */
391         mov     $0x0, %ax
392         mov     %ax, %ss
393
394         /* Restore 16 bit IDT */
395         xor     %ax, %ax
396         mov     %ax, %ds
397         lidt    __realmode_idt
398
399         /* Restore all registers, including those
400          * manipulated by the C handler
401          */
402         popl    %eax
403         pop     %gs
404         pop     %fs
405         pop     %es
406         pop     %ds
407         popal
408         iret
409
410         .globl __realmode_code_size
411 __realmode_code_size = (. - __realmode_code)
412
413         .code32