biostramp.S revision 1.3 1 /* $NetBSD: biostramp.S,v 1.3 1996/09/09 18:42:51 mycroft Exp $ */
2 /*-
3 * Copyright (c) 1996 John T. Kohl. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 *
33 */
34
35 /*
36 * biostramp.S: provide a means for NetBSD to call BIOS interrupts
37 * by switching to real mode, calling it, and switching
38 * back to protected & paging mode.
39 */
40
41 /*
42 * Micro$haft's book on i386/i486 programming says you should do the following
43 * to return to real mode from protected mode:
44 *
45 * 1) disable paging, by jumping to code with identical virtual and physical
46 * addresses, clearing PG in CR0, and zeroing CR3 (PDBR).
47 *
48 * 2) segment descriptors must be byte-granular with limit 64k-1, def32 = 0,
49 * (i.e. 16-bit data accesses and/or 80286 instructions)
50 * CS must be executable; DS,ES,FS,GS should be writable
51 *
52 * 3) disable interrupts, load IDTR with original value (base 0, limit 1023)
53 *
54 * 4) clear PE in CR0, execute FAR jump to load CS.
55 *
56 * 5) load SP, and off you go
57 *
58 */
59
60 #include "assym.h"
61 #include "apm.h"
62 #if NAPM > 0
63 #include <machine/param.h>
64 #include <machine/specialreg.h>
65 #include <machine/segments.h>
66 #include <machine/apmvar.h>
67 #include <machine/psl.h>
68 #include <machine/asm.h>
69 #define addr32 .byte 0x67
70 #define data32 .byte 0x66
71
72 .set MYBASE,NBPG
73 .set MYSCRATCH,NBPG+NBPG
74 .set CR3_ADDR,(MYSCRATCH-4)
75 .set IDTR_SAVE_ADDR,CR3_ADDR-6
76 .set GDTR_SAVE_ADDR,IDTR_SAVE_ADDR-6
77 .set GDTR_LOCAL_ADDR,GDTR_SAVE_ADDR-6
78 .set STACK_PTR_ADDR,GDTR_LOCAL_ADDR-4
79 .set BASE_PTR_ADDR,STACK_PTR_ADDR-4
80 .set FUNCTION_ADDR,(BASE_PTR_ADDR-2)
81 .set GDT_COPY_ADDR,(FUNCTION_ADDR-NGDT*8)
82 .set AX_REGADDR,(GDT_COPY_ADDR-2)
83 .set BX_REGADDR,(AX_REGADDR-2)
84 .set CX_REGADDR,(BX_REGADDR-2)
85 .set DX_REGADDR,(CX_REGADDR-2)
86 .set SI_REGADDR,(DX_REGADDR-2)
87 .set DI_REGADDR,(SI_REGADDR-2)
88 .set FLAGS_REGADDR,(DI_REGADDR-2)
89 .set ENDREGADDR,(FLAGS_REGADDR-2)
90
91 .set REALSTACK,ENDREGADDR-16 # leave a red zone?
92
93 #define COPY_FLAGS (PSL_C|PSL_PF|PSL_AF|PSL_Z|PSL_N|PSL_D|PSL_V)
94
95 /*
96 * do_bios_call(int function, struct apmregs *regs)
97 */
98
99 ENTRY(do_bios_call)
100 pushl %ebp
101 movl %esp,%ebp /* set up frame ptr */
102 pushl %esi
103 pushl %edi
104 pushl %ebx
105 pushl %ds
106 pushl %es
107 pushl %fs
108 pushl %gs
109
110 # copy data to where the real-mode hook can handle it
111 movl 8(%ebp),%eax
112 movw %ax,FUNCTION_ADDR
113 movl 12(%ebp),%ebx
114 movw APMREG_AX(%ebx),%ax
115 movw %ax,AX_REGADDR
116 movw APMREG_BX(%ebx),%ax
117 movw %ax,BX_REGADDR
118 movw APMREG_CX(%ebx),%ax
119 movw %ax,CX_REGADDR
120 movw APMREG_DX(%ebx),%ax
121 movw %ax,DX_REGADDR
122 movw APMREG_SI(%ebx),%ax
123 movw %ax,SI_REGADDR
124 movw APMREG_DI(%ebx),%ax
125 movw %ax,DI_REGADDR
126 # merge current flags with certain provided flags
127 movw APMREG_FLAGS(%ebx),%cx
128 pushfl
129 popl %eax
130 andl $~(COPY_FLAGS|PSL_I),%eax
131 andl $COPY_FLAGS,%ecx
132 orl %ecx,%eax
133 movw %ax,FLAGS_REGADDR
134
135 # save flags, disable interrupts, do real mode stuff
136 pushfl
137
138 # save GDT
139 sgdt GDTR_SAVE_ADDR
140
141 # copy the GDT to local area
142 movl GDTR_SAVE_ADDR+2,%esi
143 movl $GDT_COPY_ADDR,%edi
144 movl $(NGDT*8),%ecx
145 cld
146 rep
147 movsb
148 movw $(NGDT*8)-1,GDTR_LOCAL_ADDR
149 movl $GDT_COPY_ADDR,GDTR_LOCAL_ADDR+2
150
151 # install GDT copy
152 lgdt GDTR_LOCAL_ADDR
153
154 cli
155
156 # save IDT
157 sidt IDTR_SAVE_ADDR
158
159 # set up new stack: save old ones, create new segs
160 movl %esp,STACK_PTR_ADDR
161 movl %ebp,BASE_PTR_ADDR
162 movl $REALSTACK,%esp
163 movl $0,%ebp # leave no trace, there is none.
164
165 # save CR3
166 movl %cr3,%eax
167 movl %eax,CR3_ADDR
168
169 # turn off paging
170 movl %cr0,%eax
171 andl $~(CR0_PG),%eax
172 movl %eax,%cr0
173
174 # flush TLB, drop PDBR
175 xorl %eax,%eax
176 movl %eax,%cr3
177
178 ## load 16-bit segment descriptors
179 movw $GSEL(GBIOSDATA_SEL,SEL_KPL),%bx
180 movw %bx,%ds
181 movw %bx,%es
182 movw %bx,%fs
183 movw %bx,%gs
184
185 ljmp $GSEL(GBIOSCODE_SEL,SEL_KPL),$x16+MYBASE
186
187 x16:
188 # turn off protected mode--yikes!
189 mov %cr0,%eax
190 data32
191 and $~CR0_PE,%eax
192 mov %eax,%cr0
193
194 # need inter-segment jump to reload real-mode CS
195 data32
196 ljmp $(MYBASE>>4),$xreal
197
198 xreal: # really in real mode now
199 # set up segment selectors. Note: everything is now relative
200 # to zero-base in this file, except %ss.
201 # data items in our scratch area need to reflect MYADDR
202 xorl %ax,%ax
203 movw %ax,%ss
204
205 movw %cs,%ax
206 movw %ax,%es
207 movw %ax,%fs
208 movw %ax,%gs
209 movw %ax,%ds
210
211 ## load IDT, now that we are here.
212 addr32
213 lidt IDT_bios
214
215 # Don't forget that we're in real mode, with 16-bit default data.
216 # all these movl's are really movw's !
217 addr32
218 movl DI_REGADDR-MYBASE,%edi
219 addr32
220 movl SI_REGADDR-MYBASE,%esi
221 addr32
222 movl DX_REGADDR-MYBASE,%edx
223 addr32
224 movl CX_REGADDR-MYBASE,%ecx
225 addr32
226 movl BX_REGADDR-MYBASE,%ebx
227 addr32
228 movb FUNCTION_ADDR-MYBASE,%al
229 addr32
230 movb %al,intaddr+1 # self modifying code, yuck. no indirect interrupt instruction!
231 # long jump to flush processor cache to reflect code modification
232 data32
233 ljmp $(MYBASE>>4),$flushit
234 flushit:
235 addr32
236 movl FLAGS_REGADDR-MYBASE,%eax
237 pushl %eax
238 popfl
239 addr32
240 movl AX_REGADDR-MYBASE,%eax
241
242 intaddr:
243 int $0xff
244
245 # save results
246 pushf
247 addr32
248 movl %eax,AX_REGADDR-MYBASE
249 addr32
250 movl %ebx,BX_REGADDR-MYBASE
251 addr32
252 movl %ecx,CX_REGADDR-MYBASE
253 addr32
254 movl %edx,DX_REGADDR-MYBASE
255 addr32
256 movl %esi,SI_REGADDR-MYBASE
257 addr32
258 movl %edi,DI_REGADDR-MYBASE
259 pop %eax
260 addr32
261 movl %eax,FLAGS_REGADDR-MYBASE
262
263 # and return to protected mode
264 cli # just to be sure
265
266 mov %cr0,%eax
267 data32
268 or $CR0_PE,%eax
269 mov %eax,%cr0
270
271 # long jump to 32-bit code segment
272 data32
273 ljmp $GSEL(GCODE_SEL,SEL_KPL),$x32+MYBASE
274 x32:
275 #back in 32-bit mode/protected mode (but not paging yet).
276 # Reload the segment registers & IDT
277
278 movw $GSEL(GDATA_SEL,SEL_KPL),%bx
279 movw %bx,%ds
280 movw %bx,%ss
281 movw %bx,%es
282
283 # reload PDBR
284 movl CR3_ADDR,%eax
285 movl %eax,%cr3
286 movl %cr0,%eax
287 orl $CR0_PG,%eax
288 movl %eax,%cr0
289
290 # reload system copy of GDT
291 lgdt GDTR_SAVE_ADDR
292
293 # restore protected-mode stack
294 movl STACK_PTR_ADDR,%esp
295 movl BASE_PTR_ADDR,%ebp
296
297 #restore protected-mode IDT
298 lidt IDTR_SAVE_ADDR
299
300 # copy back arguments from holding pen
301
302 movl 12(%ebp),%ebx
303 movw AX_REGADDR,%ax
304 movw %ax,APMREG_AX(%ebx)
305 movw BX_REGADDR,%ax
306 movw %ax,APMREG_BX(%ebx)
307 movw CX_REGADDR,%ax
308 movw %ax,APMREG_CX(%ebx)
309 movw DX_REGADDR,%ax
310 movw %ax,APMREG_DX(%ebx)
311 movw SI_REGADDR,%ax
312 movw %ax,APMREG_SI(%ebx)
313 movw DI_REGADDR,%ax
314 movw %ax,APMREG_DI(%ebx)
315 movw FLAGS_REGADDR,%ax
316 movw %ax,APMREG_FLAGS(%ebx)
317
318 # finish up, restore registers, and return
319 popfl
320 popl %gs
321 popl %fs
322 popl %es
323 popl %ds # see above
324 popl %ebx
325 popl %edi
326 popl %esi
327 leave
328 ret
329
330 .align 4
331 IDT_bios: # BIOS IDT descriptor (real-mode)
332 .word 1023
333 .long 0
334 #endif
335