cb27ef35567f4480794afa5385ff1861a676d3ef
[people/cooldavid/gpxe.git] / src / arch / i386 / transitions / librm.S
1 /*
2  * librm: a library for interfacing to real-mode code
3  *
4  * Michael Brown <mbrown@fensystems.co.uk>
5  *
6  */
7
8 FILE_LICENCE ( GPL2_OR_LATER )
9
10 /* Drag in local definitions */
11 #include "librm.h"
12
13 /* For switches to/from protected mode */
14 #define CR0_PE 1
15
16 /* Size of various C data structures */
17 #define SIZEOF_I386_SEG_REGS    12
18 #define SIZEOF_I386_REGS        32
19 #define SIZEOF_REAL_MODE_REGS   ( SIZEOF_I386_SEG_REGS + SIZEOF_I386_REGS )
20 #define SIZEOF_I386_FLAGS       4
21 #define SIZEOF_I386_ALL_REGS    ( SIZEOF_REAL_MODE_REGS + SIZEOF_I386_FLAGS )
22         
23         .arch i386
24
25 /****************************************************************************
26  * Global descriptor table
27  *
28  * Call init_librm to set up the GDT before attempting to use any
29  * protected-mode code.
30  *
31  * Define FLATTEN_REAL_MODE if you want to use so-called "flat real
32  * mode" with 4GB limits instead.
33  *
34  * NOTE: This must be located before prot_to_real, otherwise gas
35  * throws a "can't handle non absolute segment in `ljmp'" error due to
36  * not knowing the value of REAL_CS when the ljmp is encountered.
37  *
38  * Note also that putting ".word gdt_end - gdt - 1" directly into
39  * gdt_limit, rather than going via gdt_length, will also produce the
40  * "non absolute segment" error.  This is most probably a bug in gas.
41  ****************************************************************************
42  */
43         
44 #ifdef FLATTEN_REAL_MODE
45 #define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x8f
46 #else
47 #define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x00
48 #endif
49         .section ".data16", "aw", @progbits
50         .align 16
51 gdt:
52 gdtr:           /* The first GDT entry is unused, the GDTR can fit here. */
53 gdt_limit:              .word gdt_length - 1
54 gdt_base:               .long 0
55                         .word 0 /* padding */
56
57         .org    gdt + VIRTUAL_CS, 0
58 virtual_cs:     /* 32 bit protected mode code segment, virtual addresses */
59         .word   0xffff, 0
60         .byte   0, 0x9f, 0xcf, 0
61
62         .org    gdt + VIRTUAL_DS, 0
63 virtual_ds:     /* 32 bit protected mode data segment, virtual addresses */
64         .word   0xffff, 0
65         .byte   0, 0x93, 0xcf, 0
66         
67         .org    gdt + PHYSICAL_CS, 0
68 physical_cs:    /* 32 bit protected mode code segment, physical addresses */
69         .word   0xffff, 0
70         .byte   0, 0x9f, 0xcf, 0
71
72         .org    gdt + PHYSICAL_DS, 0
73 physical_ds:    /* 32 bit protected mode data segment, physical addresses */
74         .word   0xffff, 0
75         .byte   0, 0x93, 0xcf, 0        
76
77         .org    gdt + REAL_CS, 0
78 real_cs:        /* 16 bit real mode code segment */
79         .word   0xffff, 0
80         .byte   0, 0x9b, RM_LIMIT_16_19__AVL__SIZE__GRANULARITY, 0
81
82         .org    gdt + REAL_DS   
83 real_ds:        /* 16 bit real mode data segment */
84         .word   0xffff, 0
85         .byte   0, 0x93, RM_LIMIT_16_19__AVL__SIZE__GRANULARITY, 0
86         
87 gdt_end:
88         .equ    gdt_length, gdt_end - gdt
89
90 /****************************************************************************
91  * init_librm (real-mode far call, 16-bit real-mode far return address)
92  *
93  * Initialise the GDT ready for transitions to protected mode.
94  *
95  * Parameters:
96  *   %cs : .text16 segment
97  *   %ds : .data16 segment
98  *   %edi : Physical base of protected-mode code (virt_offset)
99  ****************************************************************************
100  */
101         .section ".text16", "ax", @progbits
102         .code16
103         .globl init_librm
104 init_librm:
105         /* Preserve registers */
106         pushl   %eax
107         pushl   %ebx
108
109         /* Store _virt_offset and set up virtual_cs and virtual_ds segments */
110         movl    %edi, %eax
111         movw    $virtual_cs, %bx
112         call    set_seg_base
113         movw    $virtual_ds, %bx
114         call    set_seg_base    
115         movl    %edi, _virt_offset
116
117         /* Negate virt_offset */
118         negl    %edi
119                 
120         /* Store rm_cs and _text16, set up real_cs segment */
121         xorl    %eax, %eax
122         movw    %cs, %ax
123         movw    %ax, rm_cs
124         shll    $4, %eax
125         movw    $real_cs, %bx
126         call    set_seg_base
127         addr32 leal     (%eax, %edi), %ebx
128         movl    %ebx, _text16
129
130         /* Store rm_ds and _data16, set up real_ds segment */
131         xorl    %eax, %eax
132         movw    %ds, %ax
133         movw    %ax, %cs:rm_ds
134         shll    $4, %eax
135         movw    $real_ds, %bx
136         call    set_seg_base
137         addr32 leal     (%eax, %edi), %ebx
138         movl    %ebx, _data16
139
140         /* Set GDT and IDT base */
141         movl    %eax, gdt_base
142         addl    $gdt, gdt_base
143         call    idt_init
144
145         /* Restore registers */
146         negl    %edi
147         popl    %ebx
148         popl    %eax
149         lret
150
151         .section ".text16", "ax", @progbits
152         .code16
153         .weak idt_init
154 set_seg_base:
155 1:      movw    %ax, 2(%bx)
156         rorl    $16, %eax
157         movb    %al, 4(%bx)
158         movb    %ah, 7(%bx)
159         roll    $16, %eax
160 idt_init: /* Reuse the return opcode here */
161         ret
162
163 /****************************************************************************
164  * real_to_prot (real-mode near call, 32-bit virtual return address)
165  *
166  * Switch from 16-bit real-mode to 32-bit protected mode with virtual
167  * addresses.  The real-mode %ss:sp is stored in rm_ss and rm_sp, and
168  * the protected-mode %esp is restored from the saved pm_esp.
169  * Interrupts are disabled.  All other registers may be destroyed.
170  *
171  * The return address for this function should be a 32-bit virtual
172  * address.
173  *
174  * Parameters: 
175  *   %ecx : number of bytes to move from RM stack to PM stack
176  *
177  ****************************************************************************
178  */
179         .section ".text16", "ax", @progbits
180         .code16
181 real_to_prot:
182         /* Make sure we have our data segment available */
183         movw    %cs:rm_ds, %ax
184         movw    %ax, %ds
185         
186         /* Add _virt_offset, _text16 and _data16 to stack to be
187          * copied, and also copy the return address.
188          */
189         pushl   _virt_offset
190         pushl   _text16
191         pushl   _data16
192         addw    $16, %cx /* %ecx must be less than 64kB anyway */
193         
194         /* Real-mode %ss:%sp => %ebp:%edx and virtual address => %esi */
195         xorl    %ebp, %ebp
196         movw    %ss, %bp
197         movzwl  %sp, %edx
198         movl    %ebp, %eax
199         shll    $4, %eax
200         addr32 leal (%eax,%edx), %esi
201         subl    _virt_offset, %esi
202
203         /* Switch to protected mode */
204         cli
205         data32 lgdt gdtr
206         data32 lidt idtr
207         movl    %cr0, %eax
208         orb     $CR0_PE, %al
209         movl    %eax, %cr0
210         data32 ljmp     $VIRTUAL_CS, $1f
211         .section ".text", "ax", @progbits
212         .code32
213 1:
214         /* Set up protected-mode data segments and stack pointer */
215         movw    $VIRTUAL_DS, %ax
216         movw    %ax, %ds
217         movw    %ax, %es
218         movw    %ax, %fs
219         movw    %ax, %gs
220         movw    %ax, %ss
221         movl    pm_esp, %esp
222
223         /* Record real-mode %ss:sp (after removal of data) */
224         movw    %bp, rm_ss
225         addl    %ecx, %edx
226         movw    %dx, rm_sp
227
228         /* Move data from RM stack to PM stack */
229         subl    %ecx, %esp
230         movl    %esp, %edi
231         rep movsb
232
233         /* Publish virt_offset, text16 and data16 for PM code to use */
234         popl    data16
235         popl    text16
236         popl    virt_offset
237
238         /* Return to virtual address */
239         ret
240
241         /* Default IDTR with no interrupts */
242         .section ".data16", "aw", @progbits
243         .weak idtr
244 idtr:
245 rm_idtr:
246         .word 0xffff /* limit */
247         .long 0 /* base */
248
249 /****************************************************************************
250  * prot_to_real (protected-mode near call, 32-bit real-mode return address)
251  *
252  * Switch from 32-bit protected mode with virtual addresses to 16-bit
253  * real mode.  The protected-mode %esp is stored in pm_esp and the
254  * real-mode %ss:sp is restored from the saved rm_ss and rm_sp.  The
255  * high word of the real-mode %esp is set to zero.  All real-mode data
256  * segment registers are loaded from the saved rm_ds.  Interrupts are
257  * *not* enabled, since we want to be able to use prot_to_real in an
258  * ISR.  All other registers may be destroyed.
259  *
260  * The return address for this function should be a 32-bit (sic)
261  * real-mode offset within .code16.
262  *
263  * Parameters: 
264  *   %ecx : number of bytes to move from PM stack to RM stack
265  *
266  ****************************************************************************
267  */
268         .section ".text", "ax", @progbits
269         .code32
270 prot_to_real:
271         /* Add return address to data to be moved to RM stack */
272         addl    $4, %ecx
273         
274         /* Real-mode %ss:sp => %ebp:edx and virtual address => %edi */
275         movzwl  rm_ss, %ebp
276         movzwl  rm_sp, %edx
277         subl    %ecx, %edx
278         movl    %ebp, %eax
279         shll    $4, %eax
280         leal    (%eax,%edx), %edi
281         subl    virt_offset, %edi
282         
283         /* Move data from PM stack to RM stack */
284         movl    %esp, %esi
285         rep movsb
286         
287         /* Record protected-mode %esp (after removal of data) */
288         movl    %esi, pm_esp
289
290         /* Load real-mode segment limits */
291         movw    $REAL_DS, %ax
292         movw    %ax, %ds
293         movw    %ax, %es
294         movw    %ax, %fs
295         movw    %ax, %gs
296         movw    %ax, %ss
297         ljmp    $REAL_CS, $1f
298         .section ".text16", "ax", @progbits
299         .code16
300 1:
301         /* Switch to real mode */
302         movl    %cr0, %eax
303         andb    $0!CR0_PE, %al
304         movl    %eax, %cr0
305         ljmp    *p2r_jump_vector
306 p2r_jump_target:
307
308         /* Set up real-mode data segments and stack pointer */
309         movw    %cs:rm_ds, %ax
310         movw    %ax, %ds
311         movw    %ax, %es
312         movw    %ax, %fs
313         movw    %ax, %gs
314         movw    %bp, %ss
315         movl    %edx, %esp
316
317         /* Reset IDTR to the real-mode defaults */
318         data32 lidt rm_idtr
319
320         /* Return to real-mode address */
321         data32 ret
322
323
324         /* Real-mode code and data segments.  Assigned by the call to
325          * init_librm.  rm_cs doubles as the segment part of the jump
326          * vector used by prot_to_real.  rm_ds is located in .text16
327          * rather than .data16 because code needs to be able to locate
328          * the data segment.
329          */
330         .section ".data16", "aw", @progbits
331 p2r_jump_vector:
332         .word   p2r_jump_target
333         .globl rm_cs
334 rm_cs:  .word 0
335         .globl rm_ds
336         .section ".text16.data", "aw", @progbits
337 rm_ds:  .word 0
338
339 /****************************************************************************
340  * prot_call (real-mode far call, 16-bit real-mode far return address)
341  *
342  * Call a specific C function in the protected-mode code.  The
343  * prototype of the C function must be
344  *   void function ( struct i386_all_regs *ix86 ); 
345  * ix86 will point to a struct containing the real-mode registers
346  * at entry to prot_call.  
347  *
348  * All registers will be preserved across prot_call(), unless the C
349  * function explicitly overwrites values in ix86.  Interrupt status
350  * and GDT will also be preserved.  Gate A20 will be enabled.
351  *
352  * Note that prot_call() does not rely on the real-mode stack
353  * remaining intact in order to return, since everything relevant is
354  * copied to the protected-mode stack for the duration of the call.
355  * In particular, this means that a real-mode prefix can make a call
356  * to main() which will return correctly even if the prefix's stack
357  * gets vapourised during the Etherboot run.  (The prefix cannot rely
358  * on anything else on the stack being preserved, so should move any
359  * critical data to registers before calling main()).
360  *
361  * Parameters:
362  *   function : virtual address of protected-mode function to call
363  *
364  * Example usage:
365  *      pushl   $pxe_api_call
366  *      call    prot_call
367  *      addw    $4, %sp
368  * to call in to the C function
369  *      void pxe_api_call ( struct i386_all_regs *ix86 );
370  ****************************************************************************
371  */
372
373 #define PC_OFFSET_GDT ( 0 )
374 #define PC_OFFSET_IDT ( PC_OFFSET_GDT + 8 /* pad to 8 to keep alignment */ )
375 #define PC_OFFSET_IX86 ( PC_OFFSET_IDT + 8 /* pad to 8 to keep alignment */ )
376 #define PC_OFFSET_RETADDR ( PC_OFFSET_IX86 + SIZEOF_I386_ALL_REGS )
377 #define PC_OFFSET_FUNCTION ( PC_OFFSET_RETADDR + 4 )
378 #define PC_OFFSET_END ( PC_OFFSET_FUNCTION + 4 )
379
380         .section ".text16", "ax", @progbits
381         .code16
382         .globl prot_call
383 prot_call:
384         /* Preserve registers, flags and GDT on external RM stack */
385         pushfl
386         pushal
387         pushw   %gs
388         pushw   %fs
389         pushw   %es
390         pushw   %ds
391         pushw   %ss
392         pushw   %cs
393         subw    $16, %sp
394         movw    %sp, %bp
395         sidt    8(%bp)
396         sgdt    (%bp)
397
398         /* For sanity's sake, clear the direction flag as soon as possible */
399         cld
400
401         /* Switch to protected mode and move register dump to PM stack */
402         movl    $PC_OFFSET_END, %ecx
403         pushl   $1f
404         jmp     real_to_prot
405         .section ".text", "ax", @progbits
406         .code32
407 1:
408         /* Set up environment expected by C code */
409         call    gateA20_set
410
411         /* Call function */
412         leal    PC_OFFSET_IX86(%esp), %eax
413         pushl   %eax
414         call    *(PC_OFFSET_FUNCTION+4)(%esp)
415         popl    %eax /* discard */
416
417         /* Switch to real mode and move register dump back to RM stack */
418         movl    $PC_OFFSET_END, %ecx
419         pushl   $1f
420         jmp     prot_to_real
421         .section ".text16", "ax", @progbits
422         .code16
423 1:      
424         /* Reload GDT and IDT, restore registers and flags and return */
425         movw    %sp, %bp
426         data32 lgdt (%bp)
427         data32 lidt 8(%bp)
428         addw    $20, %sp /* also skip %cs and %ss */
429         popw    %ds
430         popw    %es
431         popw    %fs
432         popw    %gs
433         popal
434         /* popal skips %esp.  We therefore want to do "movl -20(%sp),
435          * %esp", but -20(%sp) is not a valid 80386 expression.
436          * Fortunately, prot_to_real() zeroes the high word of %esp, so
437          * we can just use -20(%esp) instead.
438          */
439         addr32 movl -20(%esp), %esp
440         popfl
441         lret
442
443 /****************************************************************************
444  * real_call (protected-mode near call, 32-bit virtual return address)
445  *
446  * Call a real-mode function from protected-mode code.
447  *
448  * The non-segment register values will be passed directly to the
449  * real-mode code.  The segment registers will be set as per
450  * prot_to_real.  The non-segment register values set by the real-mode
451  * function will be passed back to the protected-mode caller.  A
452  * result of this is that this routine cannot be called directly from
453  * C code, since it clobbers registers that the C ABI expects the
454  * callee to preserve.  Gate A20 will *not* be automatically
455  * re-enabled.  Since we always run from an even megabyte of memory,
456  * we are guaranteed to return successfully to the protected-mode
457  * code, which should then call gateA20_set() if it suspects that gate
458  * A20 may have been disabled.  Note that enabling gate A20 is a
459  * potentially slow operation that may also cause keyboard input to be
460  * lost; this is why it is not done automatically.
461  *
462  * librm.h defines a convenient macro REAL_CODE() for using real_call.
463  * See librm.h and realmode.h for details and examples.
464  *
465  * Parameters:
466  *   (32-bit) near pointer to real-mode function to call
467  *
468  * Returns: none
469  ****************************************************************************
470  */
471
472 #define RC_OFFSET_PRESERVE_REGS ( 0 )
473 #define RC_OFFSET_RETADDR ( RC_OFFSET_PRESERVE_REGS + SIZEOF_I386_REGS )
474 #define RC_OFFSET_FUNCTION ( RC_OFFSET_RETADDR + 4 )
475 #define RC_OFFSET_END ( RC_OFFSET_FUNCTION + 4 )
476
477         .section ".text", "ax", @progbits
478         .code32
479         .globl real_call
480 real_call:
481         /* Create register dump and function pointer copy on PM stack */
482         pushal
483         pushl   RC_OFFSET_FUNCTION(%esp)
484
485         /* Switch to real mode and move register dump to RM stack  */
486         movl    $( RC_OFFSET_RETADDR + 4 /* function pointer copy */ ), %ecx
487         pushl   $1f
488         jmp     prot_to_real
489         .section ".text16", "ax", @progbits
490         .code16
491 1:
492         /* Call real-mode function */
493         popl    rc_function
494         popal
495         call    *rc_function
496         pushal
497
498         /* For sanity's sake, clear the direction flag as soon as possible */
499         cld
500
501         /* Switch to protected mode and move register dump back to PM stack */
502         movl    $RC_OFFSET_RETADDR, %ecx
503         pushl   $1f
504         jmp     real_to_prot
505         .section ".text", "ax", @progbits
506         .code32
507 1:
508         /* Restore registers and return */
509         popal
510         ret
511
512
513         /* Function vector, used because "call xx(%sp)" is not a valid
514          * 16-bit expression.
515          */
516         .section ".data16", "aw", @progbits
517 rc_function:    .word 0, 0
518
519 /****************************************************************************
520  * Stored real-mode and protected-mode stack pointers
521  *
522  * The real-mode stack pointer is stored here whenever real_to_prot
523  * is called and restored whenever prot_to_real is called.  The
524  * converse happens for the protected-mode stack pointer.
525  *
526  * Despite initial appearances this scheme is, in fact re-entrant,
527  * because program flow dictates that we always return via the point
528  * we left by.  For example:
529  *    PXE API call entry
530  *  1   real => prot
531  *        ...
532  *        Print a text string
533  *          ...
534  *  2       prot => real
535  *            INT 10
536  *  3       real => prot
537  *          ...
538  *        ...
539  *  4   prot => real
540  *    PXE API call exit
541  *
542  * At point 1, the RM mode stack value, say RPXE, is stored in
543  * rm_ss,sp.  We want this value to still be present in rm_ss,sp when
544  * we reach point 4.
545  *
546  * At point 2, the RM stack value is restored from RPXE.  At point 3,
547  * the RM stack value is again stored in rm_ss,sp.  This *does*
548  * overwrite the RPXE that we have stored there, but it's the same
549  * value, since the code between points 2 and 3 has managed to return
550  * to us.
551  ****************************************************************************
552  */
553         .section ".data", "aw", @progbits
554         .globl rm_sp
555 rm_sp:  .word 0
556         .globl rm_ss
557 rm_ss:  .word 0
558 pm_esp: .long _estack
559
560 /****************************************************************************
561  * Virtual address offsets
562  *
563  * These are used by the protected-mode code to map between virtual
564  * and physical addresses, and to access variables in the .text16 or
565  * .data16 segments.
566  ****************************************************************************
567  */
568         /* Internal copies, created by init_librm (which runs in real mode) */
569         .section ".data16", "aw", @progbits
570 _virt_offset:   .long 0
571 _text16:        .long 0
572 _data16:        .long 0
573
574         /* Externally-visible copies, created by real_to_prot */
575         .section ".data", "aw", @progbits
576         .globl virt_offset
577 virt_offset:    .long 0 
578         .globl text16
579 text16:         .long 0
580         .globl data16
581 data16:         .long 0