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