xref: /netbsd/sys/arch/i386/bioscall/biostramp.S (revision ce099b40)
1*ce099b40Smartin/*	$NetBSD: biostramp.S,v 1.14 2008/04/28 20:23:23 martin Exp $	*/
24620e806Sjtc
3fda7345cSjtk/*-
44620e806Sjtc * Copyright (c) 1996 The NetBSD Foundation, Inc.
54620e806Sjtc * All rights reserved.
64620e806Sjtc *
74620e806Sjtc * This code is derived from software contributed to The NetBSD Foundation
84620e806Sjtc * by John Kohl.
9fda7345cSjtk *
10fda7345cSjtk * Redistribution and use in source and binary forms, with or without
11fda7345cSjtk * modification, are permitted provided that the following conditions
12fda7345cSjtk * are met:
13fda7345cSjtk * 1. Redistributions of source code must retain the above copyright
14fda7345cSjtk *    notice, this list of conditions and the following disclaimer.
15fda7345cSjtk * 2. Redistributions in binary form must reproduce the above copyright
16fda7345cSjtk *    notice, this list of conditions and the following disclaimer in the
17fda7345cSjtk *    documentation and/or other materials provided with the distribution.
18fda7345cSjtk *
194620e806Sjtc * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
204620e806Sjtc * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
214620e806Sjtc * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
221bcecdd6Sjtc * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
231bcecdd6Sjtc * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
244620e806Sjtc * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
254620e806Sjtc * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
264620e806Sjtc * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
274620e806Sjtc * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
284620e806Sjtc * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29fda7345cSjtk * POSSIBILITY OF SUCH DAMAGE.
30fda7345cSjtk */
31fda7345cSjtk
32fda7345cSjtk/*
33fda7345cSjtk * biostramp.S:		provide a means for NetBSD to call BIOS interrupts
34fda7345cSjtk *			by switching to real mode, calling it, and switching
35fda7345cSjtk *			back to protected & paging mode.
36fda7345cSjtk */
37fda7345cSjtk
38fda7345cSjtk/*
39fda7345cSjtk * Micro$haft's book on i386/i486 programming says you should do the following
40fda7345cSjtk * to return to real mode from protected mode:
41fda7345cSjtk *
42fda7345cSjtk * 1) disable paging, by jumping to code with identical virtual and physical
43fda7345cSjtk * addresses, clearing PG in CR0, and zeroing CR3 (PDBR).
44fda7345cSjtk *
45fda7345cSjtk * 2) segment descriptors must be byte-granular with limit 64k-1, def32 = 0,
46fda7345cSjtk * (i.e. 16-bit data accesses and/or 80286 instructions)
47fda7345cSjtk * CS must be executable; DS,ES,FS,GS should be writable
48fda7345cSjtk *
49fda7345cSjtk * 3) disable interrupts, load IDTR with original value (base 0, limit 1023)
50fda7345cSjtk *
51fda7345cSjtk * 4) clear PE in CR0, execute FAR jump to load CS.
52fda7345cSjtk *
53fda7345cSjtk * 5) load SP, and off you go
54fda7345cSjtk *
55fda7345cSjtk */
56fda7345cSjtk
57fda7345cSjtk#include "assym.h"
5804f1e23fSjtk
5904f1e23fSjtk#include <i386/include/param.h>
6004f1e23fSjtk#include <i386/include/specialreg.h>
6104f1e23fSjtk#include <i386/include/segments.h>
6204f1e23fSjtk#include <i386/include/apmvar.h>
6304f1e23fSjtk#include <i386/include/psl.h>
6404f1e23fSjtk#include <i386/include/asm.h>
6504f1e23fSjtk
66dbacbd36Smycroft#define	addr32	.byte 0x67
67dbacbd36Smycroft#define	data32	.byte 0x66
68fda7345cSjtk
6915eb4971Smycroft	.set MYBASE,NBPG
7015eb4971Smycroft	.set MYSCRATCH,NBPG*2
71fda7345cSjtk	.set CR3_ADDR,(MYSCRATCH-4)
72fda7345cSjtk	.set IDTR_SAVE_ADDR,CR3_ADDR-6
73fda7345cSjtk	.set GDTR_SAVE_ADDR,IDTR_SAVE_ADDR-6
74fda7345cSjtk	.set GDTR_LOCAL_ADDR,GDTR_SAVE_ADDR-6
75fda7345cSjtk	.set STACK_PTR_ADDR,GDTR_LOCAL_ADDR-4
76fda7345cSjtk	.set BASE_PTR_ADDR,STACK_PTR_ADDR-4
77fda7345cSjtk	.set FUNCTION_ADDR,(BASE_PTR_ADDR-2)
78fda7345cSjtk	.set GDT_COPY_ADDR,(FUNCTION_ADDR-NGDT*8)
7910ec6359Sjtk	.set EAX_REGADDR,(GDT_COPY_ADDR-4)
8010ec6359Sjtk	.set EBX_REGADDR,(EAX_REGADDR-4)
8110ec6359Sjtk	.set ECX_REGADDR,(EBX_REGADDR-4)
8210ec6359Sjtk	.set EDX_REGADDR,(ECX_REGADDR-4)
8310ec6359Sjtk	.set ESI_REGADDR,(EDX_REGADDR-4)
8410ec6359Sjtk	.set EDI_REGADDR,(ESI_REGADDR-4)
8510ec6359Sjtk	.set EFLAGS_REGADDR,(EDI_REGADDR-4)
860b263476Sjdolecek	.set ES_REGADDR, (EFLAGS_REGADDR-4)
870b263476Sjdolecek	.set ENDREGADDR,(ES_REGADDR-4)
88fda7345cSjtk
890b263476Sjdolecek	.set REALSTACK,ENDREGADDR-20		# leave a red zone?
90fda7345cSjtk
91fda7345cSjtk#define COPY_FLAGS (PSL_C|PSL_PF|PSL_AF|PSL_Z|PSL_N|PSL_D|PSL_V)
92fda7345cSjtk
93fda7345cSjtk/*
9410ec6359Sjtk * do_bios_call(int function, struct bioscall *regs)
95fda7345cSjtk */
96fda7345cSjtk
97fda7345cSjtkENTRY(do_bios_call)
98fda7345cSjtk	pushl	%ebp
99fda7345cSjtk	movl	%esp,%ebp		/* set up frame ptr */
100fda7345cSjtk	pushl	%esi
101fda7345cSjtk	pushl	%edi
102fda7345cSjtk	pushl	%ebx
103fda7345cSjtk	pushl	%ds
104fda7345cSjtk	pushl	%es
105fda7345cSjtk	pushl	%fs
106fda7345cSjtk	pushl	%gs
107fda7345cSjtk
108fda7345cSjtk	# copy data to where the real-mode hook can handle it
109fda7345cSjtk	movl 8(%ebp),%eax
110fda7345cSjtk	movw %ax,FUNCTION_ADDR
111fda7345cSjtk	movl 12(%ebp),%ebx
11210ec6359Sjtk	movl BIOSCALLREG_EAX(%ebx),%eax
11310ec6359Sjtk	movl %eax,EAX_REGADDR
11410ec6359Sjtk	movl BIOSCALLREG_EBX(%ebx),%eax
11510ec6359Sjtk	movl %eax,EBX_REGADDR
11610ec6359Sjtk	movl BIOSCALLREG_ECX(%ebx),%eax
11710ec6359Sjtk	movl %eax,ECX_REGADDR
11810ec6359Sjtk	movl BIOSCALLREG_EDX(%ebx),%eax
11910ec6359Sjtk	movl %eax,EDX_REGADDR
12010ec6359Sjtk	movl BIOSCALLREG_ESI(%ebx),%eax
12110ec6359Sjtk	movl %eax,ESI_REGADDR
12210ec6359Sjtk	movl BIOSCALLREG_EDI(%ebx),%eax
12310ec6359Sjtk	movl %eax,EDI_REGADDR
124fda7345cSjtk	# merge current flags with certain provided flags
12510ec6359Sjtk	movl BIOSCALLREG_EFLAGS(%ebx),%ecx
126fda7345cSjtk	pushfl
127fda7345cSjtk	popl %eax
128fda7345cSjtk	andl $~(COPY_FLAGS|PSL_I),%eax
129fda7345cSjtk	andl $COPY_FLAGS,%ecx
130fda7345cSjtk	orl %ecx,%eax
13110ec6359Sjtk	movl %eax,EFLAGS_REGADDR
1320b263476Sjdolecek	movl $0, ES_REGADDR
133fda7345cSjtk
134fda7345cSjtk	# save flags, disable interrupts, do real mode stuff
135fda7345cSjtk	pushfl
136fda7345cSjtk
137fda7345cSjtk	# save GDT
138fda7345cSjtk	sgdt GDTR_SAVE_ADDR
139fda7345cSjtk
140fda7345cSjtk	# copy the GDT to local area
141fda7345cSjtk	movl GDTR_SAVE_ADDR+2,%esi
142fda7345cSjtk	movl $GDT_COPY_ADDR,%edi
143fda7345cSjtk	movl $(NGDT*8),%ecx
144fda7345cSjtk	cld
145fda7345cSjtk	rep
146fda7345cSjtk	movsb
147fda7345cSjtk	movw $(NGDT*8)-1,GDTR_LOCAL_ADDR
148fda7345cSjtk	movl $GDT_COPY_ADDR,GDTR_LOCAL_ADDR+2
149fda7345cSjtk
150fda7345cSjtk	# install GDT copy
151fda7345cSjtk	lgdt GDTR_LOCAL_ADDR
152fda7345cSjtk
153fda7345cSjtk	cli
154fda7345cSjtk
155fda7345cSjtk	# save IDT
156fda7345cSjtk	sidt IDTR_SAVE_ADDR
157fda7345cSjtk
158fda7345cSjtk	# set up new stack: save old ones, create new segs
159fda7345cSjtk	movl %esp,STACK_PTR_ADDR
160fda7345cSjtk	movl %ebp,BASE_PTR_ADDR
161fda7345cSjtk	movl $REALSTACK,%esp
162fda7345cSjtk	movl $0,%ebp		# leave no trace, there is none.
163fda7345cSjtk
164fda7345cSjtk	# save CR3
165fda7345cSjtk	movl %cr3,%eax
166fda7345cSjtk	movl %eax,CR3_ADDR
167fda7345cSjtk
168fda7345cSjtk	# turn off paging
169fda7345cSjtk	movl %cr0,%eax
170fda7345cSjtk	andl $~(CR0_PG),%eax
171fda7345cSjtk	movl %eax,%cr0
172fda7345cSjtk
173fda7345cSjtk	# flush TLB, drop PDBR
174fda7345cSjtk	xorl %eax,%eax
175fda7345cSjtk	movl %eax,%cr3
176fda7345cSjtk
177fda7345cSjtk	## load 16-bit segment descriptors
178fda7345cSjtk	movw $GSEL(GBIOSDATA_SEL,SEL_KPL),%bx
179fda7345cSjtk	movw %bx,%ds
180fda7345cSjtk	movw %bx,%es
181fda7345cSjtk	movw %bx,%fs
182fda7345cSjtk	movw %bx,%gs
183fda7345cSjtk
184fda7345cSjtk	ljmp $GSEL(GBIOSCODE_SEL,SEL_KPL),$x16+MYBASE
185fda7345cSjtk
186fda7345cSjtkx16:
187fda7345cSjtk	# turn off protected mode--yikes!
188fda7345cSjtk	mov	%cr0,%eax
189fda7345cSjtk	data32
190fda7345cSjtk	and	$~CR0_PE,%eax
191fda7345cSjtk	mov	%eax,%cr0
192fda7345cSjtk
193fda7345cSjtk	# need inter-segment jump to reload real-mode CS
194fda7345cSjtk	data32
195fda7345cSjtk	ljmp $(MYBASE>>4),$xreal
196fda7345cSjtk
197fda7345cSjtkxreal:	# really in real mode now
198fda7345cSjtk	# set up segment selectors.  Note: everything is now relative
199fda7345cSjtk	# to zero-base in this file, except %ss.
200fda7345cSjtk	# data items in our scratch area need to reflect MYADDR
201d259e610Schristos	xorl %eax,%eax
202fda7345cSjtk	movw %ax,%ss
203fda7345cSjtk
204fda7345cSjtk	movw %cs,%ax
205fda7345cSjtk	movw %ax,%es
206fda7345cSjtk	movw %ax,%fs
207fda7345cSjtk	movw %ax,%gs
208fda7345cSjtk	movw %ax,%ds
209fda7345cSjtk
210fda7345cSjtk	## load IDT, now that we are here.
211fda7345cSjtk	addr32
212fda7345cSjtk	lidt IDT_bios
213fda7345cSjtk
214fda7345cSjtk	# Don't forget that we're in real mode, with 16-bit default data.
21510ec6359Sjtk	# all these movl's are really movw's, and movw's are movl's!
216fda7345cSjtk	addr32
217d259e610Schristos	movw EDI_REGADDR-MYBASE,%di
218fda7345cSjtk	addr32
219d259e610Schristos	movw ESI_REGADDR-MYBASE,%si
220fda7345cSjtk	addr32
221d259e610Schristos	movw EDX_REGADDR-MYBASE,%dx
222fda7345cSjtk	addr32
223d259e610Schristos	movw ECX_REGADDR-MYBASE,%cx
224fda7345cSjtk	addr32
225d259e610Schristos	movw EBX_REGADDR-MYBASE,%bx
226fda7345cSjtk	addr32
227fda7345cSjtk	movb FUNCTION_ADDR-MYBASE,%al
228fda7345cSjtk	addr32
229fda7345cSjtk	movb %al,intaddr+1	# self modifying code, yuck. no indirect interrupt instruction!
230fda7345cSjtk	# long jump to flush processor cache to reflect code modification
231fda7345cSjtk	data32
232fda7345cSjtk	ljmp $(MYBASE>>4),$flushit
233fda7345cSjtkflushit:
234fda7345cSjtk	addr32
235d259e610Schristos	movw EFLAGS_REGADDR-MYBASE,%ax
236fda7345cSjtk	pushl %eax
237fda7345cSjtk	popfl
238fda7345cSjtk	addr32
239d259e610Schristos	movw EAX_REGADDR-MYBASE,%ax
240fda7345cSjtk
241fda7345cSjtkintaddr:
242fda7345cSjtk	int $0xff
243fda7345cSjtk
244fda7345cSjtk	# save results
245fda7345cSjtk	pushf
246fda7345cSjtk	addr32
247d259e610Schristos	movw %ax,EAX_REGADDR-MYBASE
248fda7345cSjtk	addr32
249d259e610Schristos	movw %bx,EBX_REGADDR-MYBASE
250fda7345cSjtk	addr32
251d259e610Schristos	movw %cx,ECX_REGADDR-MYBASE
252fda7345cSjtk	addr32
253d259e610Schristos	movw %dx,EDX_REGADDR-MYBASE
254fda7345cSjtk	addr32
255d259e610Schristos	movw %si,ESI_REGADDR-MYBASE
256fda7345cSjtk	addr32
257d259e610Schristos	movw %di,EDI_REGADDR-MYBASE
258d259e610Schristos	pop %ax
259fda7345cSjtk	addr32
260d259e610Schristos	movw %ax,EFLAGS_REGADDR-MYBASE
2610b263476Sjdolecek	addr32
2620b263476Sjdolecek	movw %es,ES_REGADDR-MYBASE
263fda7345cSjtk
264fda7345cSjtk	# and return to protected mode
265fda7345cSjtk	cli	# just to be sure
266fda7345cSjtk
267fda7345cSjtk	mov %cr0,%eax
268fda7345cSjtk	data32
269fda7345cSjtk	or $CR0_PE,%eax
270fda7345cSjtk	mov %eax,%cr0
271fda7345cSjtk
272fda7345cSjtk	# long jump to 32-bit code segment
273fda7345cSjtk	data32
274fda7345cSjtk	ljmp $GSEL(GCODE_SEL,SEL_KPL),$x32+MYBASE
275fda7345cSjtkx32:
276fda7345cSjtk	#back in 32-bit mode/protected mode (but not paging yet).
277fda7345cSjtk	# Reload the segment registers & IDT
278fda7345cSjtk
279fda7345cSjtk	movw $GSEL(GDATA_SEL,SEL_KPL),%bx
280fda7345cSjtk	movw %bx,%ds
281fda7345cSjtk	movw %bx,%ss
282fda7345cSjtk	movw %bx,%es
283fda7345cSjtk
284fda7345cSjtk	# reload PDBR
285fda7345cSjtk	movl CR3_ADDR,%eax
286fda7345cSjtk	movl %eax,%cr3
287fda7345cSjtk	movl %cr0,%eax
288fda7345cSjtk	orl $CR0_PG,%eax
289fda7345cSjtk	movl %eax,%cr0
290fda7345cSjtk
291fda7345cSjtk	# reload system copy of GDT
292fda7345cSjtk	lgdt GDTR_SAVE_ADDR
293fda7345cSjtk
294fda7345cSjtk	# restore protected-mode stack
295fda7345cSjtk	movl STACK_PTR_ADDR,%esp
296fda7345cSjtk	movl BASE_PTR_ADDR,%ebp
297fda7345cSjtk
298fda7345cSjtk	#restore protected-mode IDT
299fda7345cSjtk	lidt IDTR_SAVE_ADDR
300fda7345cSjtk
301fda7345cSjtk	# copy back arguments from holding pen
302fda7345cSjtk
303fda7345cSjtk	movl 12(%ebp),%ebx
30410ec6359Sjtk	movl EAX_REGADDR,%eax
30510ec6359Sjtk	movl %eax,BIOSCALLREG_EAX(%ebx)
30610ec6359Sjtk	movl EBX_REGADDR,%eax
30710ec6359Sjtk	movl %eax,BIOSCALLREG_EBX(%ebx)
30810ec6359Sjtk	movl ECX_REGADDR,%eax
30910ec6359Sjtk	movl %eax,BIOSCALLREG_ECX(%ebx)
31010ec6359Sjtk	movl EDX_REGADDR,%eax
31110ec6359Sjtk	movl %eax,BIOSCALLREG_EDX(%ebx)
31210ec6359Sjtk	movl ESI_REGADDR,%eax
31310ec6359Sjtk	movl %eax,BIOSCALLREG_ESI(%ebx)
31410ec6359Sjtk	movl EDI_REGADDR,%eax
31510ec6359Sjtk	movl %eax,BIOSCALLREG_EDI(%ebx)
31610ec6359Sjtk	movl EFLAGS_REGADDR,%eax
31710ec6359Sjtk	movl %eax,BIOSCALLREG_EFLAGS(%ebx)
3180b263476Sjdolecek	movl ES_REGADDR, %eax
3190b263476Sjdolecek	movl %eax,BIOSCALLREG_ES(%ebx)
320fda7345cSjtk
321fda7345cSjtk	# finish up, restore registers, and return
322fda7345cSjtk	popfl
323fda7345cSjtk	popl	%gs
324fda7345cSjtk	popl	%fs
325fda7345cSjtk	popl	%es
326fda7345cSjtk	popl	%ds		# see above
327fda7345cSjtk	popl	%ebx
328fda7345cSjtk	popl	%edi
329fda7345cSjtk	popl	%esi
330fda7345cSjtk	leave
331fda7345cSjtk	ret
332fda7345cSjtk
3335663256dSkleink#ifdef __ELF__
3345663256dSkleink	.align 16
3355663256dSkleink#else
336fda7345cSjtk	.align 4
3375663256dSkleink#endif
338fda7345cSjtkIDT_bios:			# BIOS IDT descriptor (real-mode)
339fda7345cSjtk	.word 1023
340fda7345cSjtk	.long 0
341