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