1/*
2 * libkir: a transition library for -DKEEP_IT_REAL
3 *
4 * Michael Brown <mbrown@fensystems.co.uk>
5 *
6 */
7
8FILE_LICENCE ( GPL2_OR_LATER )
9
10/****************************************************************************
11 * This file defines libkir: an interface between external and
12 * internal environments when -DKEEP_IT_REAL is used, so that both
13 * internal and external environments are in real mode.  It deals with
14 * switching data segments and the stack.  It provides the following
15 * functions:
16 *
17 * ext_to_kir &		switch between external and internal (kir)
18 * kir_to_ext		environments, preserving all non-segment
19 *			registers
20 *
21 * kir_call		issue a call to an internal routine from external
22 *			code
23 *
24 * libkir is written to avoid assuming that segments are anything
25 * other than opaque data types, and also avoids assuming that the
26 * stack pointer is 16-bit.  This should enable it to run just as well
27 * in 16:16 or 16:32 protected mode as in real mode.
28 ****************************************************************************
29 */
30
31/* Breakpoint for when debugging under bochs */
32#define BOCHSBP xchgw %bx, %bx
33
34	.text
35	.arch i386
36	.section ".text16", "awx", @progbits
37	.code16
38
39/****************************************************************************
40 * init_libkir (real-mode or 16:xx protected-mode far call)
41 *
42 * Initialise libkir ready for transitions to the kir environment
43 *
44 * Parameters:
45 *   %cs : .text16 segment
46 *   %ds : .data16 segment
47 ****************************************************************************
48 */
49	.globl	init_libkir
50init_libkir:
51	/* Record segment registers */
52	pushw	%ds
53	popw	%cs:kir_ds
54	lret
55
56/****************************************************************************
57 * ext_to_kir (real-mode or 16:xx protected-mode near call)
58 *
59 * Switch from external stack and segment registers to internal stack
60 * and segment registers.  %ss:sp is restored from the saved kir_ds
61 * and kir_sp.  %ds, %es, %fs and %gs are all restored from the saved
62 * kir_ds.  All other registers are preserved.
63 *
64 * %cs:0000 must point to the start of the runtime image code segment
65 * on entry.
66 *
67 * Parameters: none
68 ****************************************************************************
69 */
70
71	.globl	ext_to_kir
72ext_to_kir:
73	/* Record external segment registers */
74	movw	%ds, %cs:ext_ds
75	pushw	%cs
76	popw	%ds	/* Set %ds = %cs for easier access to variables */
77	movw	%es, %ds:ext_es
78	movw	%fs, %ds:ext_fs
79	movw	%gs, %ds:ext_fs
80
81	/* Preserve registers */
82	movw	%ax, %ds:save_ax
83
84	/* Extract near return address from stack */
85	popw	%ds:save_retaddr
86
87	/* Record external %ss:esp */
88	movw	%ss, %ds:ext_ss
89	movl	%esp, %ds:ext_esp
90
91	/* Load internal segment registers and stack pointer */
92	movw	%ds:kir_ds, %ax
93	movw	%ax, %ss
94	movzwl	%ds:kir_sp, %esp
95	movw	%ax, %ds
96	movw	%ax, %es
97	movw	%ax, %fs
98	movw	%ax, %gs
991:
100
101	/* Place return address on new stack */
102	pushw	%cs:save_retaddr
103
104	/* Restore registers and return */
105	movw	%cs:save_ax, %ax
106	ret
107
108/****************************************************************************
109 * kir_to_ext (real-mode or 16:xx protected-mode near call)
110 *
111 * Switch from internal stack and segment registers to external stack
112 * and segment registers.  %ss:%esp is restored from the saved ext_ss
113 * and ext_esp.  Other segment registers are restored from the
114 * corresponding locations.  All other registers are preserved.
115 *
116 * Note that it is actually %ss that is recorded as kir_ds, on the
117 * assumption that %ss == %ds when kir_to_ext is called.
118 *
119 * Parameters: none
120 ****************************************************************************
121 */
122
123	.globl	kir_to_ext
124kir_to_ext:
125	/* Record near return address */
126	pushw	%cs
127	popw	%ds	/* Set %ds = %cs for easier access to variables */
128	popw	%ds:save_retaddr
129
130	/* Record internal segment registers and %sp */
131	movw	%ss, %ds:kir_ds
132	movw	%sp, %ds:kir_sp
133
134	/* Load external segment registers and stack pointer */
135	movw	%ds:ext_ss, %ss
136	movl	%ds:ext_esp, %esp
137	movw	%ds:ext_gs, %gs
138	movw	%ds:ext_fs, %fs
139	movw	%ds:ext_es, %es
140	movw	%ds:ext_ds, %ds
141
142	/* Return */
143	pushw	%cs:save_retaddr
144	ret
145
146/****************************************************************************
147 * kir_call (real-mode or 16:xx protected-mode far call)
148 *
149 * Call a specific C function in the internal code.  The prototype of
150 * the C function must be
151 *   void function ( struct i386_all_resg *ix86 );
152 * ix86 will point to a struct containing the real-mode registers
153 * at entry to kir_call.
154 *
155 * All registers will be preserved across kir_call(), unless the C
156 * function explicitly overwrites values in ix86.  Interrupt status
157 * will also be preserved.
158 *
159 * Parameters:
160 *   function : (32-bit) virtual address of C function to call
161 *
162 * Example usage:
163 *	pushl	$pxe_api_call
164 *	lcall	$UNDI_CS, $kir_call
165 *	addw	$4, %sp
166 * to call in to the C function
167 *      void pxe_api_call ( struct i386_all_regs *ix86 );
168 ****************************************************************************
169 */
170
171	.globl	kir_call
172kir_call:
173	/* Preserve flags.  Must do this before any operation that may
174	 * affect flags.
175	 */
176	pushfl
177	popl	%cs:save_flags
178
179	/* Disable interrupts.  We do funny things with the stack, and
180	 * we're not re-entrant.
181	 */
182	cli
183
184	/* Extract address of internal routine from stack.  We must do
185	 * this without using (%bp), because we may be called with
186	 * either a 16-bit or a 32-bit stack segment.
187	 */
188	popl	%cs:save_retaddr	/* Scratch location */
189	popl	%cs:save_function
190	subl	$8, %esp		/* Restore %esp */
191
192	/* Switch to internal stack.  Note that the external stack is
193	 * inaccessible once we're running internally (since we have
194	 * no concept of 48-bit far pointers)
195	 */
196	call	ext_to_kir
197
198	/* Store external registers on internal stack */
199	pushl	%cs:save_flags
200	pushal
201	pushl	%cs:ext_fs_and_gs
202	pushl	%cs:ext_ds_and_es
203	pushl	%cs:ext_cs_and_ss
204
205	/* Push &ix86 on stack and call function */
206	sti
207	pushl	%esp
208	data32 call *%cs:save_function
209	popl	%eax /* discard */
210
211	/* Restore external registers from internal stack */
212	popl	%cs:ext_cs_and_ss
213	popl	%cs:ext_ds_and_es
214	popl	%cs:ext_fs_and_gs
215	popal
216	popl	%cs:save_flags
217
218	/* Switch to external stack */
219	call	kir_to_ext
220
221	/* Restore flags */
222	pushl	%cs:save_flags
223	popfl
224
225	/* Return */
226	lret
227
228/****************************************************************************
229 * Stored internal and external stack and segment registers
230 ****************************************************************************
231 */
232
233ext_cs_and_ss:
234ext_cs:		.word 0
235ext_ss:		.word 0
236ext_ds_and_es:
237ext_ds:		.word 0
238ext_es:		.word 0
239ext_fs_and_gs:
240ext_fs:		.word 0
241ext_gs:		.word 0
242ext_esp:	.long 0
243
244		.globl kir_ds
245kir_ds:		.word 0
246		.globl kir_sp
247kir_sp:		.word _estack
248
249/****************************************************************************
250 * Temporary variables
251 ****************************************************************************
252 */
253save_ax:	.word 0
254save_retaddr:	.long 0
255save_flags:	.long 0
256save_function:	.long 0
257