[librm] Use libflat to enable A20 line on each real-to-protected transition
[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 FILE_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
50 init_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
72 ext_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
99 1:
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
124 kir_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
172 kir_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         
233 ext_cs_and_ss:  
234 ext_cs:         .word 0
235 ext_ss:         .word 0
236 ext_ds_and_es:  
237 ext_ds:         .word 0
238 ext_es:         .word 0
239 ext_fs_and_gs:  
240 ext_fs:         .word 0
241 ext_gs:         .word 0
242 ext_esp:        .long 0
243
244                 .globl kir_ds
245 kir_ds:         .word 0
246                 .globl kir_sp
247 kir_sp:         .word _estack
248
249 /****************************************************************************
250  * Temporary variables
251  ****************************************************************************
252  */
253 save_ax:        .word 0
254 save_retaddr:   .long 0
255 save_flags:     .long 0
256 save_function:  .long 0