e0d6c57c2043079fa1325dff03eb3d205c3e8a30
[people/lynusvaz/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  * ext_to_kir (real-mode or 16:xx protected-mode near call)
39  *
40  * Switch from external stack and segment registers to internal stack
41  * and segment registers.  %ss:sp is restored from the saved kir_ds
42  * and kir_sp.  %ds, %es, %fs and %gs are all restored from the saved
43  * kir_ds.  All other registers are preserved.
44  *
45  * %cs:0000 must point to the start of the runtime image code segment
46  * on entry.
47  *
48  * Note that this routine can be called *without* having first set up
49  * a stored kir_ds and kir_sp.  If you do this, ext_to_kir will return
50  * without altering the segment registers or stack pointer.
51  *
52  * Parameters: none
53  ****************************************************************************
54  */
55
56         .globl  ext_to_kir
57 ext_to_kir:
58         /* Record external segment registers */
59         movw    %ds, %cs:ext_ds
60         pushw   %cs
61         popw    %ds     /* Set %ds = %cs for easier access to variables */
62         movw    %es, %ds:ext_es
63         movw    %fs, %ds:ext_fs
64         movw    %gs, %ds:ext_fs
65
66         /* Preserve registers */
67         movw    %ax, %ds:save_ax
68
69         /* Extract near return address from stack */
70         popw    %ds:save_retaddr
71
72         /* Record external %ss:esp */
73         movw    %ss, %ds:ext_ss
74         movl    %esp, %ds:ext_esp
75
76         /* Load internal segment registers and stack pointer, if available */
77         movw    %ds:kir_ds, %ax
78         testw   %ax, %ax
79         jz      1f
80         movw    %ax, %ss
81         movzwl  %ds:kir_sp, %esp
82         movw    %ax, %ds
83         movw    %ax, %es
84         movw    %ax, %fs
85         movw    %ax, %gs
86 1:
87
88         /* Place return address on new stack */
89         pushw   %cs:save_retaddr
90         
91         /* Restore registers and return */
92         movw    %cs:save_ax, %ax
93         ret
94
95 /****************************************************************************
96  * kir_to_ext (real-mode or 16:xx protected-mode near call)
97  *
98  * Switch from internal stack and segment registers to external stack
99  * and segment registers.  %ss:%esp is restored from the saved ext_ss
100  * and ext_esp.  Other segment registers are restored from the
101  * corresponding locations.  All other registers are preserved.
102  *
103  * Note that it is actually %ss that is recorded as kir_ds, on the
104  * assumption that %ss == %ds when kir_to_ext is called.
105  *
106  * Parameters: none
107  ****************************************************************************
108  */
109
110         .globl  kir_to_ext
111 kir_to_ext:
112         /* Record near return address */
113         pushw   %cs
114         popw    %ds     /* Set %ds = %cs for easier access to variables */
115         popw    %ds:save_retaddr
116         
117         /* Record internal segment registers and %sp */
118         movw    %ss, %ds:kir_ds
119         movw    %sp, %ds:kir_sp
120
121         /* Load external segment registers and stack pointer */
122         movw    %ds:ext_ss, %ss
123         movl    %ds:ext_esp, %esp
124         movw    %ds:ext_gs, %gs
125         movw    %ds:ext_fs, %fs
126         movw    %ds:ext_es, %es
127         movw    %ds:ext_ds, %ds
128
129         /* Return */
130         pushw   %cs:save_retaddr
131         ret
132         
133 /****************************************************************************
134  * kir_call (real-mode or 16:xx protected-mode far call)
135  *
136  * Call a specific C function in the internal code.  The prototype of
137  * the C function must be
138  *   void function ( struct i386_all_resg *ix86 ); 
139  * ix86 will point to a struct containing the real-mode registers
140  * at entry to kir_call.
141  *
142  * All registers will be preserved across kir_call(), unless the C
143  * function explicitly overwrites values in ix86.  Interrupt status
144  * will also be preserved.
145  *
146  * Parameters:
147  *   function : (16-bit) virtual address of protected-mode function to call
148  *
149  * Example usage:
150  *      pushw   $pxe_api_call
151  *      lcall   $UNDI_CS, $kir_call
152  *      addw    $2, %sp
153  * to call in to the C function
154  *      void pxe_api_call ( struct i386_all_regs *ix86 );
155  ****************************************************************************
156  */
157
158         .globl  kir_call
159 kir_call:
160
161         /* Preserve flags.  Must do this before any operation that may
162          * affect flags.
163          */
164         pushfl
165         popl    %cs:save_flags
166
167         /* Disable interrupts.  We do funny things with the stack, and
168          * we're not re-entrant.
169          */
170         cli
171                 
172         /* Extract address of internal routine from stack.  We must do
173          * this without using (%bp), because we may be called with
174          * either a 16-bit or a 32-bit stack segment.
175          */
176         popl    %cs:save_retaddr        /* Scratch location */
177         popw    %cs:save_function
178         subl    $6, %esp                /* Restore %esp */
179         
180         /* Switch to internal stack.  Note that the external stack is
181          * inaccessible once we're running internally (since we have
182          * no concept of 48-bit far pointers)
183          */
184         call    ext_to_kir
185         
186         /* Store external registers on internal stack */
187         pushl   %cs:save_flags
188         pushal
189         pushl   %cs:ext_fs_and_gs
190         pushl   %cs:ext_ds_and_es
191         pushl   %cs:ext_cs_and_ss
192
193         /* Push &ix86 on stack and call function */
194         pushl   %esp
195         data32 call *%cs:save_function
196         popl    %eax /* discard */
197         
198         /* Restore external registers from internal stack */
199         popl    %cs:ext_cs_and_ss
200         popl    %cs:ext_ds_and_es
201         popl    %cs:ext_fs_and_gs
202         popal
203         popl    %cs:save_flags
204
205         /* Switch to external stack */
206         call    kir_to_ext
207
208         /* Restore flags */
209         pushl   %cs:save_flags
210         popfl
211
212         /* Return */
213         lret
214
215 /****************************************************************************
216  * Stored internal and external stack and segment registers
217  ****************************************************************************
218  */
219         
220 ext_cs_and_ss:  
221 ext_cs:         .word 0
222 ext_ss:         .word 0
223 ext_ds_and_es:  
224 ext_ds:         .word 0
225 ext_es:         .word 0
226 ext_fs_and_gs:  
227 ext_fs:         .word 0
228 ext_gs:         .word 0
229 ext_esp:        .long 0
230
231                 .globl kir_ds
232 kir_ds:         .word 0
233                 .globl kir_sp
234 kir_sp:         .word 0
235
236 /****************************************************************************
237  * Temporary variables
238  ****************************************************************************
239  */
240 save_ax:        .word 0
241 save_retaddr:   .word 0
242 save_flags:     .long 0
243 save_function:  .long 0