biostramp.S revision 1.2 1 /* $NetBSD: biostramp.S,v 1.2 1996/09/09 11:31:41 jtk 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 #define ASM
61 #define _LOCORE
62
63 #define addr32 .byte 0x67
64 #define data32 .byte 0x66
65
66 /* these from locore.s: */
67 #define ALIGN_TEXT .align 2,0x90 /* 4-byte boundaries, NOP-filled */
68 #define ENTRY(name) .globl _ ## name; ALIGN_TEXT; _ ## name:
69
70 #include "assym.h"
71 #include "apm.h"
72 #if NAPM > 0
73 #include <machine/param.h>
74 #include <machine/specialreg.h>
75 #include <machine/segments.h>
76 #include <machine/apmvar.h>
77 #include <machine/psl.h>
78
79 .set MYBASE,NBPG
80 .set MYSCRATCH,NBPG+NBPG
81 .set CR3_ADDR,(MYSCRATCH-4)
82 .set IDTR_SAVE_ADDR,CR3_ADDR-6
83 .set GDTR_SAVE_ADDR,IDTR_SAVE_ADDR-6
84 .set GDTR_LOCAL_ADDR,GDTR_SAVE_ADDR-6
85 .set STACK_PTR_ADDR,GDTR_LOCAL_ADDR-4
86 .set BASE_PTR_ADDR,STACK_PTR_ADDR-4
87 .set FUNCTION_ADDR,(BASE_PTR_ADDR-2)
88 .set GDT_COPY_ADDR,(FUNCTION_ADDR-NGDT*8)
89 .set AX_REGADDR,(GDT_COPY_ADDR-2)
90 .set BX_REGADDR,(AX_REGADDR-2)
91 .set CX_REGADDR,(BX_REGADDR-2)
92 .set DX_REGADDR,(CX_REGADDR-2)
93 .set SI_REGADDR,(DX_REGADDR-2)
94 .set DI_REGADDR,(SI_REGADDR-2)
95 .set FLAGS_REGADDR,(DI_REGADDR-2)
96 .set ENDREGADDR,(FLAGS_REGADDR-2)
97
98 .set REALSTACK,ENDREGADDR-16 # leave a red zone?
99
100 #define COPY_FLAGS (PSL_C|PSL_PF|PSL_AF|PSL_Z|PSL_N|PSL_D|PSL_V)
101
102 /*
103 * do_bios_call(int function, struct apmregs *regs)
104 */
105
106 ENTRY(do_bios_call)
107 pushl %ebp
108 movl %esp,%ebp /* set up frame ptr */
109 pushl %esi
110 pushl %edi
111 pushl %ebx
112 pushl %ds
113 pushl %es
114 pushl %fs
115 pushl %gs
116
117 # copy data to where the real-mode hook can handle it
118 movl 8(%ebp),%eax
119 movw %ax,FUNCTION_ADDR
120 movl 12(%ebp),%ebx
121 movw APMREG_AX(%ebx),%ax
122 movw %ax,AX_REGADDR
123 movw APMREG_BX(%ebx),%ax
124 movw %ax,BX_REGADDR
125 movw APMREG_CX(%ebx),%ax
126 movw %ax,CX_REGADDR
127 movw APMREG_DX(%ebx),%ax
128 movw %ax,DX_REGADDR
129 movw APMREG_SI(%ebx),%ax
130 movw %ax,SI_REGADDR
131 movw APMREG_DI(%ebx),%ax
132 movw %ax,DI_REGADDR
133 # merge current flags with certain provided flags
134 movw APMREG_FLAGS(%ebx),%cx
135 pushfl
136 popl %eax
137 andl $~(COPY_FLAGS|PSL_I),%eax
138 andl $COPY_FLAGS,%ecx
139 orl %ecx,%eax
140 movw %ax,FLAGS_REGADDR
141
142 # save flags, disable interrupts, do real mode stuff
143 pushfl
144
145 # save GDT
146 sgdt GDTR_SAVE_ADDR
147
148 # copy the GDT to local area
149 movl GDTR_SAVE_ADDR+2,%esi
150 movl $GDT_COPY_ADDR,%edi
151 movl $(NGDT*8),%ecx
152 cld
153 rep
154 movsb
155 movw $(NGDT*8)-1,GDTR_LOCAL_ADDR
156 movl $GDT_COPY_ADDR,GDTR_LOCAL_ADDR+2
157
158 # install GDT copy
159 lgdt GDTR_LOCAL_ADDR
160
161 cli
162
163 # save IDT
164 sidt IDTR_SAVE_ADDR
165
166 # set up new stack: save old ones, create new segs
167 movl %esp,STACK_PTR_ADDR
168 movl %ebp,BASE_PTR_ADDR
169 movl $REALSTACK,%esp
170 movl $0,%ebp # leave no trace, there is none.
171
172 # save CR3
173 movl %cr3,%eax
174 movl %eax,CR3_ADDR
175
176 # turn off paging
177 movl %cr0,%eax
178 andl $~(CR0_PG),%eax
179 movl %eax,%cr0
180
181 # flush TLB, drop PDBR
182 xorl %eax,%eax
183 movl %eax,%cr3
184
185 ## load 16-bit segment descriptors
186 movw $GSEL(GBIOSDATA_SEL,SEL_KPL),%bx
187 movw %bx,%ds
188 movw %bx,%es
189 movw %bx,%fs
190 movw %bx,%gs
191
192 ljmp $GSEL(GBIOSCODE_SEL,SEL_KPL),$x16+MYBASE
193
194 x16:
195 # turn off protected mode--yikes!
196 mov %cr0,%eax
197 data32
198 and $~CR0_PE,%eax
199 mov %eax,%cr0
200
201 # need inter-segment jump to reload real-mode CS
202 data32
203 ljmp $(MYBASE>>4),$xreal
204
205 xreal: # really in real mode now
206 # set up segment selectors. Note: everything is now relative
207 # to zero-base in this file, except %ss.
208 # data items in our scratch area need to reflect MYADDR
209 xorl %ax,%ax
210 movw %ax,%ss
211
212 movw %cs,%ax
213 movw %ax,%es
214 movw %ax,%fs
215 movw %ax,%gs
216 movw %ax,%ds
217
218 ## load IDT, now that we are here.
219 addr32
220 lidt IDT_bios
221
222 # Don't forget that we're in real mode, with 16-bit default data.
223 # all these movl's are really movw's !
224 addr32
225 movl DI_REGADDR-MYBASE,%edi
226 addr32
227 movl SI_REGADDR-MYBASE,%esi
228 addr32
229 movl DX_REGADDR-MYBASE,%edx
230 addr32
231 movl CX_REGADDR-MYBASE,%ecx
232 addr32
233 movl BX_REGADDR-MYBASE,%ebx
234 addr32
235 movb FUNCTION_ADDR-MYBASE,%al
236 addr32
237 movb %al,intaddr+1 # self modifying code, yuck. no indirect interrupt instruction!
238 # long jump to flush processor cache to reflect code modification
239 data32
240 ljmp $(MYBASE>>4),$flushit
241 flushit:
242 addr32
243 movl FLAGS_REGADDR-MYBASE,%eax
244 pushl %eax
245 popfl
246 addr32
247 movl AX_REGADDR-MYBASE,%eax
248
249 intaddr:
250 int $0xff
251
252 # save results
253 pushf
254 addr32
255 movl %eax,AX_REGADDR-MYBASE
256 addr32
257 movl %ebx,BX_REGADDR-MYBASE
258 addr32
259 movl %ecx,CX_REGADDR-MYBASE
260 addr32
261 movl %edx,DX_REGADDR-MYBASE
262 addr32
263 movl %esi,SI_REGADDR-MYBASE
264 addr32
265 movl %edi,DI_REGADDR-MYBASE
266 pop %eax
267 addr32
268 movl %eax,FLAGS_REGADDR-MYBASE
269
270 # and return to protected mode
271 cli # just to be sure
272
273 mov %cr0,%eax
274 data32
275 or $CR0_PE,%eax
276 mov %eax,%cr0
277
278 # long jump to 32-bit code segment
279 data32
280 ljmp $GSEL(GCODE_SEL,SEL_KPL),$x32+MYBASE
281 x32:
282 #back in 32-bit mode/protected mode (but not paging yet).
283 # Reload the segment registers & IDT
284
285 movw $GSEL(GDATA_SEL,SEL_KPL),%bx
286 movw %bx,%ds
287 movw %bx,%ss
288 movw %bx,%es
289
290 # reload PDBR
291 movl CR3_ADDR,%eax
292 movl %eax,%cr3
293 movl %cr0,%eax
294 orl $CR0_PG,%eax
295 movl %eax,%cr0
296
297 # reload system copy of GDT
298 lgdt GDTR_SAVE_ADDR
299
300 # restore protected-mode stack
301 movl STACK_PTR_ADDR,%esp
302 movl BASE_PTR_ADDR,%ebp
303
304 #restore protected-mode IDT
305 lidt IDTR_SAVE_ADDR
306
307 # copy back arguments from holding pen
308
309 movl 12(%ebp),%ebx
310 movw AX_REGADDR,%ax
311 movw %ax,APMREG_AX(%ebx)
312 movw BX_REGADDR,%ax
313 movw %ax,APMREG_BX(%ebx)
314 movw CX_REGADDR,%ax
315 movw %ax,APMREG_CX(%ebx)
316 movw DX_REGADDR,%ax
317 movw %ax,APMREG_DX(%ebx)
318 movw SI_REGADDR,%ax
319 movw %ax,APMREG_SI(%ebx)
320 movw DI_REGADDR,%ax
321 movw %ax,APMREG_DI(%ebx)
322 movw FLAGS_REGADDR,%ax
323 movw %ax,APMREG_FLAGS(%ebx)
324
325 # finish up, restore registers, and return
326 popfl
327 popl %gs
328 popl %fs
329 popl %es
330 popl %ds # see above
331 popl %ebx
332 popl %edi
333 popl %esi
334 leave
335 ret
336
337 .align 4
338 IDT_bios: # BIOS IDT descriptor (real-mode)
339 .word 1023
340 .long 0
341 #endif
342