summaryrefslogtreecommitdiff
path: root/gpxe/src/arch/i386/transitions/libkir.S
blob: 1176fcced1409d761e5df6cf93cb43d1cad2a10e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
/*
 * libkir: a transition library for -DKEEP_IT_REAL
 *
 * Michael Brown <mbrown@fensystems.co.uk>
 *
 */

FILE_LICENCE ( GPL2_OR_LATER )

/****************************************************************************
 * This file defines libkir: an interface between external and
 * internal environments when -DKEEP_IT_REAL is used, so that both
 * internal and external environments are in real mode.  It deals with
 * switching data segments and the stack.  It provides the following
 * functions:
 *
 * ext_to_kir &		switch between external and internal (kir)
 * kir_to_ext		environments, preserving all non-segment
 *			registers
 *
 * kir_call		issue a call to an internal routine from external
 *			code
 *
 * libkir is written to avoid assuming that segments are anything
 * other than opaque data types, and also avoids assuming that the
 * stack pointer is 16-bit.  This should enable it to run just as well
 * in 16:16 or 16:32 protected mode as in real mode.
 ****************************************************************************
 */

/* Breakpoint for when debugging under bochs */
#define BOCHSBP xchgw %bx, %bx

	.text
	.arch i386
	.section ".text16", "awx", @progbits
	.code16
	
/****************************************************************************
 * init_libkir (real-mode or 16:xx protected-mode far call)
 *
 * Initialise libkir ready for transitions to the kir environment
 *
 * Parameters:
 *   %cs : .text16 segment
 *   %ds : .data16 segment
 ****************************************************************************
 */
	.globl	init_libkir
init_libkir:
	/* Record segment registers */
	pushw	%ds
	popw	%cs:kir_ds
	lret
	
/****************************************************************************
 * ext_to_kir (real-mode or 16:xx protected-mode near call)
 *
 * Switch from external stack and segment registers to internal stack
 * and segment registers.  %ss:sp is restored from the saved kir_ds
 * and kir_sp.  %ds, %es, %fs and %gs are all restored from the saved
 * kir_ds.  All other registers are preserved.
 *
 * %cs:0000 must point to the start of the runtime image code segment
 * on entry.
 *
 * Parameters: none
 ****************************************************************************
 */

	.globl	ext_to_kir
ext_to_kir:
	/* Record external segment registers */
	movw	%ds, %cs:ext_ds
	pushw	%cs
	popw	%ds	/* Set %ds = %cs for easier access to variables */
	movw	%es, %ds:ext_es
	movw	%fs, %ds:ext_fs
	movw	%gs, %ds:ext_fs

	/* Preserve registers */
	movw	%ax, %ds:save_ax

	/* Extract near return address from stack */
	popw	%ds:save_retaddr

	/* Record external %ss:esp */
	movw	%ss, %ds:ext_ss
	movl	%esp, %ds:ext_esp

	/* Load internal segment registers and stack pointer */
	movw	%ds:kir_ds, %ax
	movw	%ax, %ss
	movzwl	%ds:kir_sp, %esp
	movw	%ax, %ds
	movw	%ax, %es
	movw	%ax, %fs
	movw	%ax, %gs
1:

	/* Place return address on new stack */
	pushw	%cs:save_retaddr
	
	/* Restore registers and return */
	movw	%cs:save_ax, %ax
	ret

/****************************************************************************
 * kir_to_ext (real-mode or 16:xx protected-mode near call)
 *
 * Switch from internal stack and segment registers to external stack
 * and segment registers.  %ss:%esp is restored from the saved ext_ss
 * and ext_esp.  Other segment registers are restored from the
 * corresponding locations.  All other registers are preserved.
 *
 * Note that it is actually %ss that is recorded as kir_ds, on the
 * assumption that %ss == %ds when kir_to_ext is called.
 *
 * Parameters: none
 ****************************************************************************
 */

	.globl	kir_to_ext
kir_to_ext:
	/* Record near return address */
	pushw	%cs
	popw	%ds	/* Set %ds = %cs for easier access to variables */
	popw	%ds:save_retaddr
	
	/* Record internal segment registers and %sp */
	movw	%ss, %ds:kir_ds
	movw	%sp, %ds:kir_sp

	/* Load external segment registers and stack pointer */
	movw	%ds:ext_ss, %ss
	movl	%ds:ext_esp, %esp
	movw	%ds:ext_gs, %gs
	movw	%ds:ext_fs, %fs
	movw	%ds:ext_es, %es
	movw	%ds:ext_ds, %ds

	/* Return */
	pushw	%cs:save_retaddr
	ret
	
/****************************************************************************
 * kir_call (real-mode or 16:xx protected-mode far call)
 *
 * Call a specific C function in the internal code.  The prototype of
 * the C function must be
 *   void function ( struct i386_all_resg *ix86 ); 
 * ix86 will point to a struct containing the real-mode registers
 * at entry to kir_call.
 *
 * All registers will be preserved across kir_call(), unless the C
 * function explicitly overwrites values in ix86.  Interrupt status
 * will also be preserved.
 *
 * Parameters:
 *   function : (32-bit) virtual address of C function to call
 *
 * Example usage:
 *	pushl	$pxe_api_call
 *	lcall	$UNDI_CS, $kir_call
 *	addw	$4, %sp
 * to call in to the C function
 *      void pxe_api_call ( struct i386_all_regs *ix86 );
 ****************************************************************************
 */

	.globl	kir_call
kir_call:
	/* Preserve flags.  Must do this before any operation that may
	 * affect flags.
	 */
	pushfl
	popl	%cs:save_flags

	/* Disable interrupts.  We do funny things with the stack, and
	 * we're not re-entrant.
	 */
	cli
		
	/* Extract address of internal routine from stack.  We must do
	 * this without using (%bp), because we may be called with
	 * either a 16-bit or a 32-bit stack segment.
	 */
	popl	%cs:save_retaddr	/* Scratch location */
	popl	%cs:save_function
	subl	$8, %esp		/* Restore %esp */
	
	/* Switch to internal stack.  Note that the external stack is
	 * inaccessible once we're running internally (since we have
	 * no concept of 48-bit far pointers)
	 */
	call	ext_to_kir
	
	/* Store external registers on internal stack */
	pushl	%cs:save_flags
	pushal
	pushl	%cs:ext_fs_and_gs
	pushl	%cs:ext_ds_and_es
	pushl	%cs:ext_cs_and_ss

	/* Push &ix86 on stack and call function */
	sti
	pushl	%esp
	data32 call *%cs:save_function
	popl	%eax /* discard */
	
	/* Restore external registers from internal stack */
	popl	%cs:ext_cs_and_ss
	popl	%cs:ext_ds_and_es
	popl	%cs:ext_fs_and_gs
	popal
	popl	%cs:save_flags

	/* Switch to external stack */
	call	kir_to_ext

	/* Restore flags */
	pushl	%cs:save_flags
	popfl

	/* Return */
	lret

/****************************************************************************
 * Stored internal and external stack and segment registers
 ****************************************************************************
 */
	
ext_cs_and_ss:	
ext_cs:		.word 0
ext_ss:		.word 0
ext_ds_and_es:	
ext_ds:		.word 0
ext_es:		.word 0
ext_fs_and_gs:	
ext_fs:		.word 0
ext_gs:		.word 0
ext_esp:	.long 0

		.globl kir_ds
kir_ds:		.word 0
		.globl kir_sp
kir_sp:		.word _estack

/****************************************************************************
 * Temporary variables
 ****************************************************************************
 */
save_ax:	.word 0
save_retaddr:	.long 0
save_flags:	.long 0
save_function:	.long 0