Since some people disapprove of white space cleanups mixed in regular commits
[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 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
123         /* ************************************ */
124         mov     %cx, %ax        // restore ax
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 #if defined(CONFIG_GEODE_VSA) && CONFIG_GEODE_VSA
159 #define VSA2_ENTRY_POINT        0x60020
160
161         .globl __run_vsa
162 __run_vsa = RELOCATED(.)
163         /* save all registers to the stack */
164         pushal
165
166         /* Move the protected mode stack to a safe place */
167         mov     %esp, __stack
168
169         movl    %esp, %ebp
170         /* This function is called with regparm=0 and we have
171          * to skip the 32 byte from pushal:
172          */
173         movl    36(%ebp), %ecx
174         movl    40(%ebp), %edx
175
176         /* Activate the right segment descriptor real mode. */
177         ljmp    $0x28, $RELOCATED(1f)
178 1:
179 .code16
180         /* 16 bit code from here on... */
181
182         /* Load the segment registers w/ properly configured
183          * segment descriptors. They will retain these
184          * configurations (limits, writability, etc.) once
185          * protected mode is turned off.
186          */
187         mov     $0x30, %ax
188         mov     %ax, %ds
189         mov     %ax, %es
190         mov     %ax, %fs
191         mov     %ax, %gs
192         mov     %ax, %ss
193
194         /* Turn off protection */
195         movl    %cr0, %eax
196         andl    $~PE, %eax
197         movl    %eax, %cr0
198
199         /* Now really going into real mode */
200         ljmp    $0, $RELOCATED(1f)
201 1:
202         /* Setup a stack: Put the stack at the end of page zero.
203          * That way we can easily share it between real and
204          * protected, since the 16 bit ESP at segment 0 will
205          * work for any case. */
206         mov     $0x0, %ax
207         mov     %ax, %ss
208         movl    $0x1000, %eax
209         movl    %eax, %esp
210
211         /* Load our 16 bit idt */
212         xor     %ax, %ax
213         mov     %ax, %ds
214         lidt    __realmode_idt
215
216         /* Set all segments to 0x0000, ds to 0x0040 */
217         mov     %ax, %es
218         mov     %ax, %fs
219         mov     %ax, %gs
220         mov     $0x40, %ax
221         mov     %ax, %ds
222         mov     %cx, %ax        // restore ax
223
224         /* ************************************ */
225         lcall   $((VSA2_ENTRY_POINT & 0xffff0000) >> 4), $(VSA2_ENTRY_POINT & 0xffff)
226         /* ************************************ */
227
228         /* If we got here, just about done.
229          * Need to get back to protected mode
230          */
231         movl    %cr0, %eax
232         orl     $PE, %eax
233         movl    %eax, %cr0
234
235         /* Now that we are in protected mode
236          * jump to a 32 bit code segment.
237          */
238         data32  ljmp    $0x10, $RELOCATED(1f)
239 1:
240         .code32
241         movw    $0x18, %ax
242         mov     %ax, %ds
243         mov     %ax, %es
244         mov     %ax, %fs
245         mov     %ax, %gs
246         mov     %ax, %ss
247
248         /* restore proper idt */
249         lidt    idtarg
250
251         /* and exit */
252         mov     __stack, %esp
253         popal
254         ret
255 #endif
256
257         .globl __run_interrupt
258 __run_interrupt = RELOCATED(.)
259
260         pushal
261         /* save the stack */
262         mov     %esp, __stack
263
264
265         /*  This configures CS properly for real mode. */
266         ljmp    $0x28, $RELOCATED(1f)
267 1:
268         .code16 /* 16 bit code from here on... */
269
270         // DEBUG
271         movb    $0xec, %al
272         outb    %al, $0x80
273
274         /* Load the segment registers w/ properly configured segment
275          * descriptors.  They will retain these configurations (limits,
276          * writability, etc.) once protected mode is turned off.
277          */
278         mov     $0x30, %ax
279         mov     %ax, %ds
280         mov     %ax, %es
281         mov     %ax, %fs
282         mov     %ax, %gs
283         mov     %ax, %ss
284
285         /* Turn off protected mode */
286         movl    %cr0, %eax
287         andl    $~PE, %eax
288         movl    %eax, %cr0
289
290         /* Now really going into real mode */
291         data32 ljmp     $0, $RELOCATED(1f)
292 1:
293
294         /* put the stack at the end of page zero.
295          * that way we can easily share it between real and protected,
296          * since the 16-bit ESP at segment 0 will work for any case.
297          */
298         /* setup a stack */
299         mov     $0x0, %ax
300         mov     %ax, %ss
301         movl    $0x1000, %eax
302         movl    %eax, %esp
303
304         /* Load 16-bit intXX IDT */
305         xor     %ax, %ax
306         mov     %ax, %ds
307         lidt    __realmode_idt
308
309         /* Set all segments to 0x0000 */
310         mov     %ax, %ds
311         mov     %ax, %es
312         mov     %ax, %fs
313         mov     %ax, %gs
314
315         /* Call VGA BIOS int10 function 0x4f14 to enable main console
316          * Epia-M does not always autosence the main console so forcing
317          * it on is good.
318          */
319
320         /* Ask VGA option rom to enable main console */
321         movw    $0x4f14,%ax
322         movw    $0x8003,%bx
323         movw    $1, %cx
324         movw    $0, %dx
325         movw    $0, %di
326         int     $0x10
327
328         /* Ok, the job is done, now go back to protected mode coreboot */
329         movl    %cr0, %eax
330         orl     $PE, %eax
331         movl    %eax, %cr0
332
333         /* Now that we are in protected mode jump to a 32-bit code segment. */
334         data32  ljmp    $0x10, $RELOCATED(1f)
335 1:
336         .code32
337         movw    $0x18, %ax
338         mov     %ax, %ds
339         mov     %ax, %es
340         mov     %ax, %fs
341         mov     %ax, %gs
342         mov     %ax, %ss
343
344         /* restore coreboot's 32-bit IDT */
345         lidt    idtarg
346
347         /* Exit */
348         mov     __stack, %esp
349         popal
350         ret
351
352 /* This is the 16-bit interrupt entry point called by the IDT stub code.
353  * Before this code code is called, %eax is pushed to the stack, and the
354  * interrupt number is loaded into %al
355  */
356         .code16
357 __interrupt_handler_16bit = RELOCATED(.)
358         push    %ds
359         push    %es
360         push    %fs
361         push    %gs
362
363         /* Clean up the interrupt number. We could have done this in the stub,
364          * but it would have cost 2 more bytes per stub entry.
365          */
366         andl    $0xff, %eax
367         pushl   %eax            /* ... and make it the first parameter */
368
369         /* Switch to protected mode */
370         movl    %cr0, %eax
371         orl     $PE, %eax
372         movl    %eax, %cr0
373
374         /* ... and jump to a 32 bit code segment. */
375         data32 ljmp    $0x10, $RELOCATED(1f)
376 1:
377         .code32
378         movw    $0x18, %ax
379         mov     %ax, %ds
380         mov     %ax, %es
381         mov     %ax, %fs
382         mov     %ax, %gs
383         mov     %ax, %ss
384
385         lidt    idtarg
386
387         /* Call the C interrupt handler */
388         movl    $interrupt_handler, %eax
389         call    *%eax
390
391         /* Now return to real mode ... */
392         ljmp    $0x28, $RELOCATED(1f)
393 1:
394         .code16
395         /* Load the segment registers with properly configured segment
396          * descriptors.  They will retain these configurations (limits,
397          * writability, etc.) once protected mode is turned off.
398          */
399         mov     $0x30, %ax
400         mov     %ax, %ds
401         mov     %ax, %es
402         mov     %ax, %fs
403         mov     %ax, %gs
404         mov     %ax, %ss
405
406         /* Disable Protected Mode */
407         movl    %cr0, %eax
408         andl    $~PE, %eax
409         movl    %eax, %cr0
410
411         /* Now really going into real mode */
412         ljmp $0,  $RELOCATED(1f)
413 1:
414         /* Restore real-mode stack segment */
415         mov     $0x0, %ax
416         mov     %ax, %ss
417
418         /* Restore 16 bit IDT */
419         xor     %ax, %ax
420         mov     %ax, %ds
421         lidt    __realmode_idt
422
423         /* Set up segment registers to segment 0x0000 */
424         mov     %ax, %es
425         mov     %ax, %fs
426         mov     %ax, %gs
427         mov     $0x40, %ax
428         mov     %ax, %ds
429
430         /* Restore all registers, including those
431          * manipulated by the C handler
432          */
433         popl    %eax
434         pop     %gs
435         pop     %fs
436         pop     %es
437         pop     %ds
438         popal
439         iret
440
441         .globl __realmode_code_size
442 __realmode_code_size = (. - __realmode_code)
443
444         .code32