source: bootcd/isolinux/syslinux-6.03/gpxe/src/arch/i386/transitions/librm.S

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 16.1 KB
Line 
1/*
2 * librm: a library for interfacing to real-mode code
3 *
4 * Michael Brown <mbrown@fensystems.co.uk>
5 *
6 */
7
8FILE_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
51gdt:
52gdtr:           /* The first GDT entry is unused, the GDTR can fit here. */
53gdt_limit:              .word gdt_length - 1
54gdt_base:               .long 0
55                        .word 0 /* padding */
56
57        .org    gdt + VIRTUAL_CS, 0
58virtual_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
63virtual_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
68physical_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
73physical_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
78real_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   
83real_ds:        /* 16 bit real mode data segment */
84        .word   0xffff, 0
85        .byte   0, 0x93, RM_LIMIT_16_19__AVL__SIZE__GRANULARITY, 0
86       
87gdt_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
104init_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
154set_seg_base:
1551:      movw    %ax, 2(%bx)
156        rorl    $16, %eax
157        movb    %al, 4(%bx)
158        movb    %ah, 7(%bx)
159        roll    $16, %eax
160idt_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
181real_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
2131:
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
244idtr:
245rm_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
270prot_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
3001:
301        /* Switch to real mode */
302        movl    %cr0, %eax
303        andb    $0!CR0_PE, %al
304        movl    %eax, %cr0
305        ljmp    *p2r_jump_vector
306p2r_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
331p2r_jump_vector:
332        .word   p2r_jump_target
333        .globl rm_cs
334rm_cs:  .word 0
335        .globl rm_ds
336        .section ".text16.data", "aw", @progbits
337rm_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
383prot_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
4071:
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
4231:     
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
480real_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
4911:
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
5071:
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
517rc_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
555rm_sp:  .word 0
556        .globl rm_ss
557rm_ss:  .word 0
558pm_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
577virt_offset:    .long 0
578        .globl text16
579text16:         .long 0
580        .globl data16
581data16:         .long 0
Note: See TracBrowser for help on using the repository browser.