xref: /haiku/src/system/boot/platform/bios_ia32/bios_asm.S (revision 2e8aa19c638e27939fd6aaa4e2570b2411e15368)
1*2e8aa19cSIngo Weinhold/*
2*2e8aa19cSIngo Weinhold * Copyright 2004-2010, Axel Dörfler, axeld@pinc-software.de.
3*2e8aa19cSIngo Weinhold * Distributed under the terms of the MIT License.
4*2e8aa19cSIngo Weinhold */
5*2e8aa19cSIngo Weinhold
6*2e8aa19cSIngo Weinhold
7*2e8aa19cSIngo Weinhold/*!	This file contains code to call BIOS functions out of a protected
8*2e8aa19cSIngo Weinhold	mode environment. It doesn't use the virtual86 mode - it switches
9*2e8aa19cSIngo Weinhold	to real mode, make the BIOS call, and switch back to protected
10*2e8aa19cSIngo Weinhold	mode again. It's meant to be used in a single-threaded boot loader,
11*2e8aa19cSIngo Weinhold	not in a multi-tasking operating system.
12*2e8aa19cSIngo Weinhold	It relies on the real mode segment descriptors found in shell.S.
13*2e8aa19cSIngo Weinhold*/
14*2e8aa19cSIngo Weinhold
15*2e8aa19cSIngo Weinhold
16*2e8aa19cSIngo Weinhold#define FUNCTION(x) .globl x ; x ## :
17*2e8aa19cSIngo Weinhold
18*2e8aa19cSIngo Weinhold#define REAL_MODE_STACK	0x9000
19*2e8aa19cSIngo Weinhold	// the location of the stack in real mode
20*2e8aa19cSIngo Weinhold
21*2e8aa19cSIngo Weinhold#define SAVED_ESP		0x10000
22*2e8aa19cSIngo Weinhold#define SAVED_CR3		0x10004
23*2e8aa19cSIngo Weinhold#define SAVED_EAX		0x10008
24*2e8aa19cSIngo Weinhold#define SAVED_ES		0x1000c
25*2e8aa19cSIngo Weinhold#define SAVED_FLAGS		0x10010
26*2e8aa19cSIngo Weinhold#define SAVED_EBP		0x10014
27*2e8aa19cSIngo Weinhold	// we're overwriting the start of our boot loader to hold some
28*2e8aa19cSIngo Weinhold	// temporary values - the first 1024 bytes of it are used at
29*2e8aa19cSIngo Weinhold	// startup only, and we avoid some linking issues this way
30*2e8aa19cSIngo Weinhold
31*2e8aa19cSIngo Weinhold
32*2e8aa19cSIngo Weinhold.text
33*2e8aa19cSIngo Weinhold.code32
34*2e8aa19cSIngo Weinhold
35*2e8aa19cSIngo Weinhold
36*2e8aa19cSIngo Weinhold/*!	This function brings you back to protected mode after you've
37*2e8aa19cSIngo Weinhold	switched to it using switch_to_real_mode().
38*2e8aa19cSIngo Weinhold	Should restore the whole environment to what it looked like
39*2e8aa19cSIngo Weinhold	before. Clobbers %eax.
40*2e8aa19cSIngo Weinhold*/
41*2e8aa19cSIngo WeinholdFUNCTION(switch_to_protected_mode)
42*2e8aa19cSIngo Weinhold	cli						// turn off interrupts
43*2e8aa19cSIngo Weinhold
44*2e8aa19cSIngo Weinhold	.code16
45*2e8aa19cSIngo Weinhold	movl	%cr0, %eax		// set the PE bit (0) to switch to protected mode
46*2e8aa19cSIngo Weinhold	orb		$0x1, %al
47*2e8aa19cSIngo Weinhold	movl	%eax, %cr0
48*2e8aa19cSIngo Weinhold
49*2e8aa19cSIngo Weinhold	.code32
50*2e8aa19cSIngo Weinhold	.byte	0x66			// jump to the protected mode segment
51*2e8aa19cSIngo Weinhold	ljmp	$0x8, $_protected_code_segment
52*2e8aa19cSIngo Weinhold_protected_code_segment:
53*2e8aa19cSIngo Weinhold	movw	$0x10, %ax		// setup data and stack selectors
54*2e8aa19cSIngo Weinhold	movw	%ax, %ds
55*2e8aa19cSIngo Weinhold	movw	%ax, %es
56*2e8aa19cSIngo Weinhold	movw	%ax, %fs
57*2e8aa19cSIngo Weinhold	movw	%ax, %gs
58*2e8aa19cSIngo Weinhold	movw	%ax, %ss
59*2e8aa19cSIngo Weinhold
60*2e8aa19cSIngo Weinhold	// turn on paging again
61*2e8aa19cSIngo Weinhold	movl	SAVED_CR3, %eax	// restore the saved page directory
62*2e8aa19cSIngo Weinhold	orl		%eax, %eax		// is there a paging directory at all?
63*2e8aa19cSIngo Weinhold	jz		_no_paging;
64*2e8aa19cSIngo Weinhold
65*2e8aa19cSIngo Weinhold	movl	%eax, %cr3
66*2e8aa19cSIngo Weinhold
67*2e8aa19cSIngo Weinhold	movl	%cr0, %eax		// set the PG bit (31) to enable paging
68*2e8aa19cSIngo Weinhold	orl		$0x80000000, %eax
69*2e8aa19cSIngo Weinhold	movl	%eax, %cr0
70*2e8aa19cSIngo Weinhold
71*2e8aa19cSIngo Weinhold_no_paging:
72*2e8aa19cSIngo Weinhold	// save the return address so that we can pick it up again later
73*2e8aa19cSIngo Weinhold	movl	(%esp), %eax
74*2e8aa19cSIngo Weinhold	movl	%eax, REAL_MODE_STACK
75*2e8aa19cSIngo Weinhold
76*2e8aa19cSIngo Weinhold	// setup protected stack frame again
77*2e8aa19cSIngo Weinhold	movl	SAVED_ESP, %eax
78*2e8aa19cSIngo Weinhold	movl	%eax, %esp
79*2e8aa19cSIngo Weinhold	movl	SAVED_EBP, %ebp
80*2e8aa19cSIngo Weinhold
81*2e8aa19cSIngo Weinhold	// copy the return address to the current stack
82*2e8aa19cSIngo Weinhold	movl	REAL_MODE_STACK, %eax
83*2e8aa19cSIngo Weinhold	movl	%eax, (%esp)
84*2e8aa19cSIngo Weinhold
85*2e8aa19cSIngo Weinhold	ret
86*2e8aa19cSIngo Weinhold
87*2e8aa19cSIngo Weinhold//--------------------------------------------------------------
88*2e8aa19cSIngo Weinhold
89*2e8aa19cSIngo Weinhold
90*2e8aa19cSIngo Weinhold/*!	Switches from protected mode back to real mode.
91*2e8aa19cSIngo Weinhold	It will disable paging and set the real mode segment selectors to 0x1000,
92*2e8aa19cSIngo Weinhold	except for the stack selector, which will be 0x0 (the stack is at 0x9000
93*2e8aa19cSIngo Weinhold	which is where the BFS boot loader puts it as well).
94*2e8aa19cSIngo Weinhold	Clobbers %eax.
95*2e8aa19cSIngo Weinhold*/
96*2e8aa19cSIngo WeinholdFUNCTION(switch_to_real_mode)
97*2e8aa19cSIngo Weinhold	// save the %esp register
98*2e8aa19cSIngo Weinhold	movl	%esp, %eax
99*2e8aa19cSIngo Weinhold	movl	%eax, SAVED_ESP
100*2e8aa19cSIngo Weinhold
101*2e8aa19cSIngo Weinhold	movl	%ebp, SAVED_EBP
102*2e8aa19cSIngo Weinhold
103*2e8aa19cSIngo Weinhold	// put the return address on the real mode stack
104*2e8aa19cSIngo Weinhold	movl	(%esp), %eax
105*2e8aa19cSIngo Weinhold	movl	%eax, REAL_MODE_STACK
106*2e8aa19cSIngo Weinhold
107*2e8aa19cSIngo Weinhold	// disable paging
108*2e8aa19cSIngo Weinhold	movl	%cr3, %eax			// save the page directory address
109*2e8aa19cSIngo Weinhold	movl	%eax, SAVED_CR3
110*2e8aa19cSIngo Weinhold
111*2e8aa19cSIngo Weinhold	movl	%cr0, %eax
112*2e8aa19cSIngo Weinhold	andl	$0x7fffffff, %eax	// clear PG bit (31)
113*2e8aa19cSIngo Weinhold	movl	%eax, %cr0
114*2e8aa19cSIngo Weinhold
115*2e8aa19cSIngo Weinhold	xor		%eax, %eax			// clear page directory to flush TLBs
116*2e8aa19cSIngo Weinhold	movl	%eax, %cr3
117*2e8aa19cSIngo Weinhold
118*2e8aa19cSIngo Weinhold	// setup real mode stack
119*2e8aa19cSIngo Weinhold	movl	$REAL_MODE_STACK, %eax
120*2e8aa19cSIngo Weinhold	movl	%eax, %esp
121*2e8aa19cSIngo Weinhold	movl	%eax, %ebp
122*2e8aa19cSIngo Weinhold
123*2e8aa19cSIngo Weinhold	// setup selectors to point to our 16 bit segments
124*2e8aa19cSIngo Weinhold	movw	$0x20, %ax
125*2e8aa19cSIngo Weinhold	movw	%ax, %ds
126*2e8aa19cSIngo Weinhold	movw	%ax, %es
127*2e8aa19cSIngo Weinhold	movw	%ax, %fs
128*2e8aa19cSIngo Weinhold	movw	%ax, %gs
129*2e8aa19cSIngo Weinhold	movw	$0x28, %ax
130*2e8aa19cSIngo Weinhold	movw	%ax, %ss
131*2e8aa19cSIngo Weinhold
132*2e8aa19cSIngo Weinhold	ljmp	$0x18, $(_almost_real_code_segment - 0x10000)
133*2e8aa19cSIngo Weinhold
134*2e8aa19cSIngo Weinhold_almost_real_code_segment:
135*2e8aa19cSIngo Weinhold	movl	%cr0, %eax			// switch to real mode
136*2e8aa19cSIngo Weinhold	andb	$0xfe, %al			// clear PE bit (0)
137*2e8aa19cSIngo Weinhold	movl	%eax, %cr0
138*2e8aa19cSIngo Weinhold
139*2e8aa19cSIngo Weinhold	.byte	0x66
140*2e8aa19cSIngo Weinhold	ljmp	$0x1000, $(_real_code_segment - 0x10000)
141*2e8aa19cSIngo Weinhold
142*2e8aa19cSIngo Weinhold_real_code_segment:
143*2e8aa19cSIngo Weinhold	.code16
144*2e8aa19cSIngo Weinhold	movw	$0x1000, %ax		// setup data & stack segments
145*2e8aa19cSIngo Weinhold	movw	%ax, %ds			// data in segment 0x1000,
146*2e8aa19cSIngo Weinhold	movw	%ax, %es
147*2e8aa19cSIngo Weinhold	movw	%ax, %fs
148*2e8aa19cSIngo Weinhold	movw	%ax, %gs
149*2e8aa19cSIngo Weinhold
150*2e8aa19cSIngo Weinhold	xor		%ax, %ax			// stack in segment 0x0
151*2e8aa19cSIngo Weinhold	movw	%ax, %ss
152*2e8aa19cSIngo Weinhold
153*2e8aa19cSIngo Weinhold	sti							// turn on interrupts again
154*2e8aa19cSIngo Weinhold	ret
155*2e8aa19cSIngo Weinhold
156*2e8aa19cSIngo Weinhold	.code32
157*2e8aa19cSIngo Weinhold
158*2e8aa19cSIngo Weinhold//--------------------------------------------------------------
159*2e8aa19cSIngo Weinhold
160*2e8aa19cSIngo Weinhold
161*2e8aa19cSIngo Weinhold/*!	void call_bios_internal(uint8 num, struct bios_regs *regs)
162*2e8aa19cSIngo Weinhold	Does a BIOS call by triggering a software interrupt in real
163*2e8aa19cSIngo Weinhold	mode.
164*2e8aa19cSIngo Weinhold*/
165*2e8aa19cSIngo WeinholdFUNCTION(call_bios_internal)
166*2e8aa19cSIngo Weinhold	pushal
167*2e8aa19cSIngo Weinhold	pushfl
168*2e8aa19cSIngo Weinhold
169*2e8aa19cSIngo Weinhold	// make sure the correct IDT is in place
170*2e8aa19cSIngo Weinhold	lidt 	idt_descriptor
171*2e8aa19cSIngo Weinhold
172*2e8aa19cSIngo Weinhold	// get the interrupt vector and patch the instruction at the target address
173*2e8aa19cSIngo Weinhold	movl	40(%esp), %eax
174*2e8aa19cSIngo Weinhold	mov		%al, int_number
175*2e8aa19cSIngo Weinhold
176*2e8aa19cSIngo Weinhold	// Fills registers from the passed in structure
177*2e8aa19cSIngo Weinhold	// Since switch_to_real_mode() clobbers %eax, we have to handle
178*2e8aa19cSIngo Weinhold	// it specially here (by temporarily storing it to an arbitrary
179*2e8aa19cSIngo Weinhold	// memory location, SAVED_EAX)
180*2e8aa19cSIngo Weinhold	movl	44(%esp), %ebp
181*2e8aa19cSIngo Weinhold
182*2e8aa19cSIngo Weinhold	movl	(%ebp), %eax
183*2e8aa19cSIngo Weinhold	movl	%eax, SAVED_EAX
184*2e8aa19cSIngo Weinhold	movl	4(%ebp), %ebx
185*2e8aa19cSIngo Weinhold	movl	8(%ebp), %ecx
186*2e8aa19cSIngo Weinhold	movl	12(%ebp), %edx
187*2e8aa19cSIngo Weinhold	movl	16(%ebp), %esi
188*2e8aa19cSIngo Weinhold	movl	20(%ebp), %edi
189*2e8aa19cSIngo Weinhold	movw	24(%ebp), %ax
190*2e8aa19cSIngo Weinhold	movw	%ax, SAVED_ES
191*2e8aa19cSIngo Weinhold
192*2e8aa19cSIngo Weinhold	call	switch_to_real_mode
193*2e8aa19cSIngo Weinhold
194*2e8aa19cSIngo Weinhold	.code16
195*2e8aa19cSIngo Weinhold
196*2e8aa19cSIngo Weinhold	// restore %eax and %es from saved location
197*2e8aa19cSIngo Weinhold	movl	(SAVED_EAX - 0x10000), %eax
198*2e8aa19cSIngo Weinhold	movw	(SAVED_ES - 0x10000), %es
199*2e8aa19cSIngo Weinhold
200*2e8aa19cSIngo Weinhold	// call the interrupt (will be dynamically changed above)
201*2e8aa19cSIngo Weinhold	.byte	0xcd
202*2e8aa19cSIngo Weinholdint_number:
203*2e8aa19cSIngo Weinhold	.byte	0
204*2e8aa19cSIngo Weinhold
205*2e8aa19cSIngo Weinhold	// we're interested in the flags state as well
206*2e8aa19cSIngo Weinhold	pushf
207*2e8aa19cSIngo Weinhold
208*2e8aa19cSIngo Weinhold	// save %eax from the call
209*2e8aa19cSIngo Weinhold	movl	%eax, (SAVED_EAX - 0x10000)
210*2e8aa19cSIngo Weinhold
211*2e8aa19cSIngo Weinhold	// save flags from the call
212*2e8aa19cSIngo Weinhold	pop		%ax
213*2e8aa19cSIngo Weinhold	movw	%ax, (SAVED_FLAGS - 0x10000)
214*2e8aa19cSIngo Weinhold
215*2e8aa19cSIngo Weinhold	// back to protected mode
216*2e8aa19cSIngo Weinhold	call	switch_to_protected_mode
217*2e8aa19cSIngo Weinhold	.code32
218*2e8aa19cSIngo Weinhold
219*2e8aa19cSIngo Weinhold	// store the register state into the structure that has been passed in
220*2e8aa19cSIngo Weinhold	movl	44(%esp), %eax
221*2e8aa19cSIngo Weinhold
222*2e8aa19cSIngo Weinhold	movl	%ebx, 4(%eax)
223*2e8aa19cSIngo Weinhold	movl	%ecx, 8(%eax)
224*2e8aa19cSIngo Weinhold	movl	%edx, 12(%eax)
225*2e8aa19cSIngo Weinhold	movl	%esi, 16(%eax)
226*2e8aa19cSIngo Weinhold	movl	%edi, 20(%eax)
227*2e8aa19cSIngo Weinhold	movl	SAVED_EAX, %ecx		// special handling for %eax and flags
228*2e8aa19cSIngo Weinhold	movl	%ecx, (%eax)
229*2e8aa19cSIngo Weinhold	movw	SAVED_FLAGS, %cx
230*2e8aa19cSIngo Weinhold	movw	%cx, 26(%eax)
231*2e8aa19cSIngo Weinhold
232*2e8aa19cSIngo Weinhold	popfl
233*2e8aa19cSIngo Weinhold	popal
234*2e8aa19cSIngo Weinhold
235*2e8aa19cSIngo Weinhold	ret
236*2e8aa19cSIngo Weinhold
237*2e8aa19cSIngo Weinhold//--------------------------------------------------------------
238*2e8aa19cSIngo Weinhold
239*2e8aa19cSIngo Weinhold
240*2e8aa19cSIngo Weinhold/*!	uint32  boot_key_in_keyboard_buffer()
241*2e8aa19cSIngo Weinhold	Search keyboard buffer for the keycodes for space in the first run, and,
242*2e8aa19cSIngo Weinhold	if not found - for the escape key at the last two positions of this buffer
243*2e8aa19cSIngo Weinhold*/
244*2e8aa19cSIngo WeinholdFUNCTION(boot_key_in_keyboard_buffer)
245*2e8aa19cSIngo Weinhold	pushal
246*2e8aa19cSIngo Weinhold	pushfl
247*2e8aa19cSIngo Weinhold
248*2e8aa19cSIngo Weinhold	// make sure the correct IDT is in place
249*2e8aa19cSIngo Weinhold	lidt 	idt_descriptor
250*2e8aa19cSIngo Weinhold
251*2e8aa19cSIngo Weinhold	call	switch_to_real_mode
252*2e8aa19cSIngo Weinhold	.code16
253*2e8aa19cSIngo Weinhold
254*2e8aa19cSIngo Weinhold	cld
255*2e8aa19cSIngo Weinhold	push	%ds
256*2e8aa19cSIngo Weinhold	xorl	%eax, %eax
257*2e8aa19cSIngo Weinhold	mov		%ax, %ds
258*2e8aa19cSIngo Weinhold	mov		$0x41E, %si		// BIOS kbd buffer
259*2e8aa19cSIngo Weinholdsearch_cycle1:
260*2e8aa19cSIngo Weinhold	lodsw
261*2e8aa19cSIngo Weinhold	cmp		$0x3920, %ax	// test space key
262*2e8aa19cSIngo Weinhold	jz		to_ret
263*2e8aa19cSIngo Weinhold	cmp		$0x440, %si
264*2e8aa19cSIngo Weinhold	jnz		search_cycle1
265*2e8aa19cSIngo Weinhold
266*2e8aa19cSIngo Weinhold	addw	0x41C, %si
267*2e8aa19cSIngo Weinhold	movw	-0x42(%si), %ax
268*2e8aa19cSIngo Weinhold	cmp		$0x011B, %ax	// test ESC key
269*2e8aa19cSIngo Weinhold	jz		to_ret
270*2e8aa19cSIngo Weinhold	movw	-0x44(%si), %ax
271*2e8aa19cSIngo Weinhold	cmp		$0x011B, %ax	// test ESC key
272*2e8aa19cSIngo Weinholdto_ret:
273*2e8aa19cSIngo Weinhold	pop		%ds
274*2e8aa19cSIngo Weinhold
275*2e8aa19cSIngo Weinhold	// save %eax
276*2e8aa19cSIngo Weinhold	movl	%eax, (SAVED_EAX - 0x10000)
277*2e8aa19cSIngo Weinhold
278*2e8aa19cSIngo Weinhold	call	switch_to_protected_mode
279*2e8aa19cSIngo Weinhold	.code32
280*2e8aa19cSIngo Weinhold
281*2e8aa19cSIngo Weinhold	popfl
282*2e8aa19cSIngo Weinhold	popal
283*2e8aa19cSIngo Weinhold
284*2e8aa19cSIngo Weinhold	// restore %eax
285*2e8aa19cSIngo Weinhold	movl	SAVED_EAX, %eax
286*2e8aa19cSIngo Weinhold
287*2e8aa19cSIngo Weinhold	ret
288*2e8aa19cSIngo Weinhold
289*2e8aa19cSIngo Weinhold//--------------------------------------------------------------
290*2e8aa19cSIngo Weinhold
291*2e8aa19cSIngo Weinhold
292*2e8aa19cSIngo Weinhold.globl idt_descriptor
293*2e8aa19cSIngo Weinholdidt_descriptor:
294*2e8aa19cSIngo Weinhold	.short	0x7ff				// IDT at 0x0, default real mode location
295*2e8aa19cSIngo Weinhold	.long	0x0
296