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