it was reason for workaround rules already, and it's somewhat ugly:
[coreboot.git] / src / devices / oprom / x86_asm.S
1 /*
2  * This file is part of the coreboot project.
3  *
4  * Copyright (C) 2009 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         .code32
61         .globl __run_optionrom
62 __run_optionrom = RELOCATED(.)
63         /* save all registers to the stack */
64         pushal
65
66         /* Move the protected mode stack to a safe place */
67         mov     %esp, __stack
68
69         /* Get devfn into %ecx */
70         movl    %esp, %ebp
71         /* This function is called with regparm=0 and we have
72          * to skip the 32 byte from pushal:
73          */
74         movl    36(%ebp), %ecx
75
76         /* Activate the right segment descriptor real mode. */
77         ljmp    $0x28, $RELOCATED(1f)
78 1:
79 .code16
80         /* 16 bit code from here on... */
81
82         /* Load the segment registers w/ properly configured
83          * segment descriptors. They will retain these
84          * configurations (limits, writability, etc.) once
85          * protected mode is turned off.
86          */
87         mov     $0x30, %ax
88         mov     %ax, %ds       
89         mov     %ax, %es       
90         mov     %ax, %fs       
91         mov     %ax, %gs       
92         mov     %ax, %ss       
93
94         /* Turn off protection */
95         movl    %cr0, %eax
96         andl    $~PE, %eax
97         movl    %eax, %cr0
98
99         /* Now really going into real mode */
100         ljmp    $0, $RELOCATED(1f)
101 1:
102         /* Setup a stack: Put the stack at the end of page zero.
103          * That way we can easily share it between real and
104          * protected, since the 16-bit ESP at segment 0 will
105          * work for any case. */
106         mov     $0x0, %ax
107         mov     %ax, %ss
108         movl    $0x1000, %eax
109         movl    %eax, %esp
110
111         /* Load our 16 bit idt */
112         xor     %ax, %ax
113         mov     %ax, %ds
114         lidt    __realmode_idt
115
116         /* Set all segments to 0x0000, ds to 0x0040 */
117         mov     %ax, %es       
118         mov     %ax, %fs       
119         mov     %ax, %gs       
120         mov     $0x40, %ax
121         mov     %ax, %ds
122         mov     %cx, %ax        // restore ax
123
124         /* ************************************ */
125         // TODO this will not work for non-VGA option ROMs
126         /* run VGA BIOS at 0xc000:0003 */
127         lcall   $0xc000, $0x0003
128         /* ************************************ */
129
130         /* If we got here, just about done.
131          * Need to get back to protected mode
132          */
133         movl    %cr0, %eax
134         orl     $PE, %eax
135         movl    %eax, %cr0
136
137         /* Now that we are in protected mode
138          * jump to a 32 bit code segment.
139          */
140         data32  ljmp    $0x10, $RELOCATED(1f)
141 1:
142         .code32
143         movw    $0x18, %ax     
144         mov     %ax, %ds       
145         mov     %ax, %es
146         mov     %ax, %fs
147         mov     %ax, %gs
148         mov     %ax, %ss
149
150         /* restore proper idt */
151         lidt    idtarg
152
153         /* and exit */
154         mov     __stack, %esp
155         popal
156         ret
157
158         .globl __run_interrupt
159 __run_interrupt = RELOCATED(.)
160
161         /* paranoia -- does ecx get saved? not sure. This is
162          * the easiest safe thing to do. */
163         pushal
164         /* save the stack */
165         mov     %esp, __stack
166
167
168         /*  This configures CS properly for real mode. */
169         ljmp    $0x28, $RELOCATED(1f)
170 1:
171         .code16 /* 16 bit code from here on... */
172
173         // DEBUG
174         movb    $0xec, %al
175         outb    %al, $0x80
176
177         /* Load the segment registers w/ properly configured segment
178          * descriptors.  They will retain these configurations (limits,
179          * writability, etc.) once protected mode is turned off.
180          */
181         mov     $0x30, %ax     
182         mov     %ax, %ds       
183         mov     %ax, %es       
184         mov     %ax, %fs       
185         mov     %ax, %gs       
186         mov     %ax, %ss       
187
188         /* Turn off protected mode */
189         movl    %cr0, %eax     
190         andl    $~PE, %eax
191         movl    %eax, %cr0     
192
193         /* Now really going into real mode */
194         data32 ljmp     $0, $RELOCATED(1f)
195 1:
196
197         /* put the stack at the end of page zero.
198          * that way we can easily share it between real and protected,
199          * since the 16-bit ESP at segment 0 will work for any case.
200          */
201         /* setup a stack */
202         mov     $0x0, %ax
203         mov     %ax, %ss
204         movl    $0x1000, %eax
205         movl    %eax, %esp
206
207         /* Load 16-bit intXX IDT */
208         xor     %ax, %ax       
209         mov     %ax, %ds
210         lidt    __realmode_idt
211
212         /* Set all segments to 0x0000 */
213         mov     %ax, %ds
214         mov     %ax, %es
215         mov     %ax, %fs
216         mov     %ax, %gs
217
218         /* Call VGA BIOS int10 function 0x4f14 to enable main console
219          * Epia-M does not always autosence the main console so forcing
220          * it on is good.
221          */
222
223         /* Ask VGA option rom to enable main console */
224         movw    $0x4f14,%ax
225         movw    $0x8003,%bx
226         movw    $1, %cx
227         movw    $0, %dx
228         movw    $0, %di
229         int     $0x10
230
231         /* Ok, the job is done, now go back to protected mode coreboot */
232         movl    %cr0, %eax
233         orl     $PE, %eax
234         movl    %eax, %cr0
235
236         /* Now that we are in protected mode jump to a 32-bit code segment. */
237         data32  ljmp    $0x10, $RELOCATED(1f)
238 1:
239         .code32
240         movw    $0x18, %ax
241         mov     %ax, %ds
242         mov     %ax, %es
243         mov     %ax, %fs
244         mov     %ax, %gs
245         mov     %ax, %ss
246
247         /* restore coreboot's 32-bit IDT */
248         lidt    idtarg
249
250         /* Exit */
251         mov     __stack, %esp
252         popal
253         ret
254
255 /* This is the 16-bit interrupt entry point called by the IDT stub code.
256  * Before this code code is called, %eax is pushed to the stack, and the
257  * interrupt number is loaded into %al
258  */
259         .code16
260 __interrupt_handler_16bit = RELOCATED(.)
261         push    %ds
262         push    %es
263         push    %fs
264         push    %gs
265
266         /* Clean up the interrupt number. We could have done this in the stub,
267          * but it would have cost 2 more bytes per stub entry.
268          */
269         andl    $0xff, %eax
270         pushl   %eax            /* ... and make it the first parameter */
271
272         /* Switch to protected mode */
273         movl    %cr0, %eax
274         orl     $PE, %eax
275         movl    %eax, %cr0
276
277         /* ... and jump to a 32 bit code segment. */
278         data32 ljmp    $0x10, $RELOCATED(1f)
279 1:
280         .code32
281         movw    $0x18, %ax
282         mov     %ax, %ds
283         mov     %ax, %es
284         mov     %ax, %fs
285         mov     %ax, %gs
286         mov     %ax, %ss
287
288         lidt    idtarg
289
290         /* Call the C interrupt handler */
291         movl    $interrupt_handler, %eax
292         call    *%eax
293
294         /* Now return to real mode ... */
295         ljmp    $0x28, $RELOCATED(1f)
296 1:
297         .code16
298         /* Load the segment registers with properly configured segment
299          * descriptors.  They will retain these configurations (limits,
300          * writability, etc.) once protected mode is turned off.
301          */
302         mov     $0x30, %ax
303         mov     %ax, %ds
304         mov     %ax, %es
305         mov     %ax, %fs
306         mov     %ax, %gs
307         mov     %ax, %ss
308
309         /* Disable Protected Mode */
310         movl    %cr0, %eax
311         andl    $~PE, %eax
312         movl    %eax, %cr0
313
314         /* Now really going into real mode */
315         ljmp $0,  $RELOCATED(1f)
316 1:
317         /* Restore real-mode stack segment */
318         mov     $0x0, %ax
319         mov     %ax, %ss
320
321         /* Restore 16-bit IDT */
322         xor     %ax, %ax
323         mov     %ax, %ds
324         lidt    __realmode_idt
325
326         /* Set up our segment registers to segment 0x0000 */
327         mov     %ax, %es
328         mov     %ax, %fs
329         mov     %ax, %gs
330         mov     $0x40, %ax
331         mov     %ax, %ds
332
333         /* Restore all registers, including those
334          * manipulated by the C handler
335          */
336         popl    %eax
337         pop     %gs
338         pop     %fs
339         pop     %es
340         pop     %ds
341         popal
342         iret
343
344         .globl __realmode_code_size
345 __realmode_code_size = (. - __realmode_code)
346
347         .code32