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