Update to cope with changes in registers.h
[people/xl0/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 /* Drag in local definitions */
9 #include "librm.h"
10
11 /* Drag in FREE_BASEMEM_HEADER_SIZE */
12 #include "basemem.h"
13
14 /****************************************************************************
15  * This file defines librm: a block of code that is designed to reside
16  * permanently in base memory and provide the interface between
17  * real-mode code running in base memory and protected-mode code
18  * running in high memory.  It provides the following functions:
19  *
20  *   real_to_prot &     switch between real and protected mode 
21  *   prot_to_real       while running in base memory, preserving 
22  *                      all non-segment registers
23  *
24  *   real_call          issue a call to a real-mode routine from
25  *                      protected-mode code running in high memory
26  *
27  *   prot_call          issue a call to a protected-mode routine from
28  *                      real-mode code running in base memory
29  *
30  * librm requires the following functions to be present in the
31  * protected-mode code:
32  *
33  *   _phys_to_virt      Switch from physical to virtual addressing.  This
34  *                      routine must be position-independent and must
35  *                      *not* assume that it is genuinely running with
36  *                      flat physical addresses
37  *
38  *   _virt_to_phys      Switch from virtual to physical addresses.
39  *
40  *   gateA20_set        Enable the A20 line to permit access to the odd
41  *                      megabytes of RAM.  (This function will be called
42  *                      with virtual addresses set up).
43  *
44  * librm needs to be linked against the protected-mode binary so that
45  * it can import the symbols for these functions.
46  *
47  * librm requires that the protected-mode code set up the following
48  * segments:
49  *
50  *   PHYSICAL_CS        32-bit pmode code and data segments with flat
51  *   PHYSICAL_DS        physical addresses.
52  *
53  *   VIRTUAL_CS         32-bit pmode code segment with virtual
54  *                      addressing, such that a protected-mode routine
55  *                      can always be found at $VIRTUAL_CS:routine.
56  *
57  * These segments must be set as #define constants when compiling
58  * librm.  Edit librm.h to change the values.
59  *
60  * librm does not know the location of the code executing in high
61  * memory.  It relies on the code running in high memory setting up a
62  * GDT such that the high-memory code is accessible at virtual
63  * addresses fixed at compile-time.
64  *
65  * librm symbols are exported as absolute values and represent offsets
66  * into librm.  This is the most useful form of the symbols, since
67  * librm is basically a binary blob that you place somewhere in base
68  * memory.
69  *
70  * librm.h provides convenient ways to use these symbols: you simply
71  * set the pointer ( char * ) installed_librm to point to wherever
72  * librm is installed, and can then use e.g. inst_rm_stack just like
73  * any other variable and have it automatically refer to the value of
74  * rm_stack in the installed librm.  Macro trickery makes this
75  * completely transparent, and the resulting assembler code is
76  * amazingly efficient.
77  *
78  * Note that librm must be called in genuine real mode, not 16:16 or
79  * 16:32 protected mode.  It makes the assumption that
80  * physical_address = 16*segment+offset, and also that it can use
81  * OFFSET(%bp) to access stack variables.  The former assumption will
82  * break in either protected mode, the latter may break in 16:32
83  * protected mode.
84  ****************************************************************************
85  */
86
87 /*
88  * Default values for pmode segments if not defined
89  */
90 #ifndef PHYSICAL_CS
91 #warning "Assuming PHYSICAL_CS = 0x08"
92 #define PHYSICAL_CS 0x08
93 #endif
94 #ifndef PHYSICAL_DS
95 #warning "Assuming PHYSICAL_DS = 0x10"
96 #define PHYSICAL_DS 0x10
97 #endif
98 #ifndef VIRTUAL_CS
99 #warning "Assuming VIRTUAL_CS = 0x18"
100 #define VIRTUAL_CS 0x18
101 #endif
102
103 /* For switches to/from protected mode */
104 #define CR0_PE 1
105
106 /* Size of various C data structures */
107 #define SIZEOF_I386_SEG_REGS    12
108 #define SIZEOF_I386_REGS        32
109 #define SIZEOF_REAL_MODE_REGS   ( SIZEOF_I386_SEG_REGS + SIZEOF_I386_REGS )
110 #define SIZEOF_I386_FLAGS       4
111 #define SIZEOF_I386_ALL_REGS    ( SIZEOF_REAL_MODE_REGS + SIZEOF_I386_FLAGS )
112 #define SIZEOF_SEGOFF_T         4
113 #define SIZEOF_REAL_CALL_PARAMS ( SIZEOF_REAL_MODE_REGS + 2 * SIZEOF_SEGOFF_T )
114         
115         .text
116         .arch i386
117         .section ".librm", "awx", @progbits
118         .align 16
119
120         .globl  librm
121 librm:
122         
123 _librm_start:
124
125 #undef OFFSET
126 #define OFFSET(sym) ( sym - _librm_start )
127
128 #undef EXPORT
129 #define EXPORT(sym) \
130         .globl sym ; \
131         .globl _ ## sym ;  \
132         .equ _ ## sym, OFFSET(sym) ; \
133         sym
134
135 /****************************************************************************
136  * Note that the first sizeof(struct free_base_memory_header) bytes of
137  * librm will get vapourised by free_base_memory().  Since we need
138  * librm to continue working even when this happens, we put some
139  * padding here.
140  *
141  * We must also ensure that the total size of librm is <1kB, otherwise
142  * free_base_memory() will stomp somewhere in the middle of us as
143  * well...
144  ****************************************************************************
145  */
146         .fill FREE_BASEMEM_HEADER_SIZE, 1, 0
147
148 /****************************************************************************
149  * Record of the current physical location of the installed copy.
150  * Used by prot_call in order to return via the current installed copy
151  * even if Etherboot has been relocated during the protected-mode
152  * call.
153  ****************************************************************************
154  */
155 EXPORT(librm_base):
156 librm_base:     .long 0
157                 
158 /****************************************************************************
159  * GDT for initial transition to protected mode
160  *
161  * PHYSICAL_CS and PHYSICAL_DS are defined in an external header file.
162  * We use only those selectors, and construct our GDT to match the
163  * selector values we're asked to use.  Use PHYSICAL_CS=0x08 and
164  * PHYSICAL_DS=0x10 to minimise the space occupied by this GDT.
165  *
166  * Note: pm_gdt is also used to store the location of the
167  * protected-mode GDT as recorded on entry to prot_to_real.
168  ****************************************************************************
169  */
170         .align 16
171 pm_gdt:
172 pm_gdt_limit:           .word pm_gdt_length - 1
173 pm_gdt_addr:            .long 0
174                         .word 0 /* padding */
175         
176         .org    pm_gdt + PHYSICAL_CS
177 pm_gdt_pm_cs:
178         /* 32 bit protected mode code segment, physical addresses */
179         .word   0xffff, 0
180         .byte   0, 0x9f, 0xcf, 0
181         
182         .org    pm_gdt + PHYSICAL_DS
183 pm_gdt_pm_ds:
184         /* 32 bit protected mode data segment, physical addresses */
185         .word   0xffff,0
186         .byte   0,0x93,0xcf,0
187         
188 pm_gdt_end:             
189         .equ    pm_gdt_length, pm_gdt_end - pm_gdt
190
191 /****************************************************************************
192  * GDT for transition to real mode
193  *
194  * This is used primarily to set 64kB segment limits.  Define
195  * FLATTEN_REAL_MODE if you want to use so-called "flat real mode"
196  * with 4GB limits instead.  The base address of each of the segments
197  * will be adjusted at run-time.
198  *
199  * NOTE: This must be located before prot_to_real, otherwise gas
200  * throws a "can't handle non absolute segment in `ljmp'" error due to
201  * not knowing the value of RM_CS when the ljmp is encountered.
202  *
203  * Note also that putting ".word rm_gdt_end - rm_gdt - 1" directly
204  * into rm_gdt_limit, rather than going via rm_gdt_length, will also
205  * produce the "non absolute segment" error.  This is most probably a
206  * bug in gas.
207  ****************************************************************************
208  */
209         
210 #ifdef FLATTEN_REAL_MODE
211 #define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x8f
212 #else
213 #define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x00
214 #endif
215         .align 16
216 rm_gdt:
217 rm_gdt_limit:           .word rm_gdt_length - 1
218 rm_gdt_base:            .long 0
219                         .word 0 /* padding */
220         
221 rm_gdt_rm_cs:   /* 16 bit real mode code segment */
222         .equ    RM_CS, rm_gdt_rm_cs - rm_gdt
223         .word   0xffff,(0&0xffff)
224         .byte   (0>>16),0x9b,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24)
225         
226 rm_gdt_rm_ds:   /* 16 bit real mode data segment */
227         .equ    RM_DS, rm_gdt_rm_ds - rm_gdt
228         .word   0xffff,(0&0xffff)
229         .byte   (0>>16),0x93,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24)
230         
231 rm_gdt_end:
232         .equ    rm_gdt_length, rm_gdt_end - rm_gdt
233
234 /****************************************************************************
235  * real_to_prot (real-mode far call)
236  *
237  * Switch from 16-bit real-mode to 32-bit protected mode with flat
238  * physical addresses.  %esp is restored from the saved pm_esp.  All
239  * segment registers are set to flat physical-mode values.  All other
240  * registers are preserved.  Interrupts are disabled.
241  *
242  * Note that this routine can be called *without* having first set up
243  * a stored pm_esp or stored GDT.  If you do this, real_to_prot will
244  * return with a temporary stack that is only *FOUR BYTES* in size.
245  * This is just enough to enable you to do a "call 1f; popl %ebp"
246  * sequence in order to find out your physical address and then load a
247  * proper 32-bit protected-mode stack pointer.  Do *NOT* use more than
248  * four bytes since this will overwrite code in librm!
249  *
250  * Parameters: none
251  ****************************************************************************
252  */
253
254         .code16         
255 EXPORT(real_to_prot):
256         /* Disable interrupts */
257         cli
258
259         /* Set %ds = %cs, for easier access to variables */
260         pushw   %cs
261         popw    %ds
262         
263         /* Preserve registers */
264         movl    %eax, %ds:OFFSET(save_eax)
265         movl    %ebx, %ds:OFFSET(save_ebx)
266
267         /* Extract real-mode far return address from stack */
268         popl    %ds:OFFSET(save_retaddr)
269
270         /* Record real-mode stack pointer */
271         movw    %sp, %ds:OFFSET(rm_sp)
272         pushw   %ss
273         popw    %ds:OFFSET(rm_ss)
274
275         /* Physical base address of librm to %ebx */
276         xorl    %ebx, %ebx
277         movw    %cs, %bx
278         shll    $4, %ebx
279
280         /* Record physical base address of librm */
281         movl    %ebx, %ds:OFFSET(librm_base)
282                 
283         /* Check base address of stored protected-mode GDT.  If it's
284          * zero, set it up to use our internal GDT (with physical
285          * segments only).
286          */
287         movl    %ds:OFFSET(pm_gdt_addr), %eax
288         testl   %eax, %eax
289         jnz     1f
290         /* Use internal GDT */
291         movl    %ebx, %eax
292         addl    $OFFSET(pm_gdt), %eax
293         movl    %eax, %ds:OFFSET(pm_gdt_addr)
294 1:      
295         
296         /* Set up protected-mode continuation address on real-mode stack */
297         pushl   $PHYSICAL_CS
298         movl    %ebx, %eax
299         addl    $OFFSET(1f), %eax
300         pushl   %eax
301         
302         /* Restore protected-mode GDT */
303         data32 lgdt     %ds:OFFSET(pm_gdt)
304
305         /* Switch to protected mode */
306         movl    %cr0, %eax
307         orb     $CR0_PE, %al
308         movl    %eax, %cr0
309
310         /* Flush prefetch queue and reload %cs:eip */
311         data32 lret
312 1:      .code32
313
314         /* Set up protected-mode stack and data segments */
315         movw    $PHYSICAL_DS, %ax
316         movw    %ax, %ds
317         movw    %ax, %es
318         movw    %ax, %fs
319         movw    %ax, %gs
320         movw    %ax, %ss
321
322         /* Switch to saved protected-mode stack.  Note that there may
323          * not actually *be* a saved protected-mode stack.
324          */
325         movl    OFFSET(pm_esp)(%ebx), %esp
326         testl   %esp, %esp
327         jnz     1f
328         /* No stack - use save_retaddr as a 4-byte temporary stack */
329         leal    OFFSET(save_retaddr+4)(%ebx), %esp
330 1:      
331         
332         /* Convert real-mode far return address to physical address
333          * and place on stack
334          */
335         pushl   OFFSET(save_retaddr)(%ebx)
336         xorl    %eax, %eax
337         xchgw   2(%esp), %ax
338         shll    $4, %eax
339         addl    %eax, 0(%esp)
340
341         /* Restore registers and return */
342         movl    OFFSET(save_eax)(%ebx), %eax
343         movl    OFFSET(save_ebx)(%ebx), %ebx
344         ret
345
346 /****************************************************************************
347  * prot_to_real (protected-mode near call, physical addresses)
348  *
349  * Switch from 32-bit protected mode with flat physical addresses to
350  * 16-bit real mode.  %ss:sp is restored from the saved rm_ss and
351  * rm_sp.  %cs is set such that %cs:0000 is the start of librm.  All
352  * other segment registers are set to %ss.  All other registers are
353  * preserved.  Interrupts are *not* enabled, since we want to be able
354  * to use this routine inside an ISR.
355  *
356  * Note that since %cs:0000 points to the start of librm on exit, it
357  * follows that the code calling prot_to_real must be located within
358  * 64kB of the start of librm.
359  *
360  * Parameters: none
361  ****************************************************************************
362  */
363
364         .code32
365 EXPORT(prot_to_real):
366         /* Calculate physical base address of librm in %ebx, preserve
367          * original %eax and %ebx in save_eax and save_ebx
368          */
369         pushl   %ebx
370         call    1f
371 1:      popl    %ebx
372         subl    $OFFSET(1b), %ebx
373         popl    OFFSET(save_ebx)(%ebx)
374         movl    %eax, OFFSET(save_eax)(%ebx)
375
376         /* Record physical base address of librm */
377         movl    %ebx, OFFSET(librm_base)(%ebx)
378
379         /* Extract return address from the stack, convert to offset
380          * within librm and save in save_retaddr
381          */
382         popl    %eax
383         subl    %ebx, %eax
384         movl    %eax, OFFSET(save_retaddr)(%ebx)
385
386         /* Record protected-mode stack pointer */
387         movl    %esp, OFFSET(pm_esp)(%ebx)
388
389         /* Record protected-mode GDT */
390         sgdt    OFFSET(pm_gdt)(%ebx)
391
392         /* Set up real-mode GDT */
393         leal    OFFSET(rm_gdt)(%ebx), %eax
394         movl    %eax, OFFSET(rm_gdt_base)(%ebx)
395         movl    %ebx, %eax
396         rorl    $16, %eax
397         movw    %bx, OFFSET(rm_gdt_rm_cs+2)(%ebx)
398         movb    %al, OFFSET(rm_gdt_rm_cs+4)(%ebx)
399         movw    %bx, OFFSET(rm_gdt_rm_ds+2)(%ebx)
400         movb    %al, OFFSET(rm_gdt_rm_ds+4)(%ebx)
401         
402         /* Switch to real-mode GDT and reload segment registers to get
403          * 64kB limits.  Stack is invalidated by this process.
404          */
405         lgdt    OFFSET(rm_gdt)(%ebx)
406         ljmp    $RM_CS, $1f
407 1:      .code16
408         movw    $RM_DS, %ax
409         movw    %ax, %ds
410         movw    %ax, %es
411         movw    %ax, %fs
412         movw    %ax, %gs
413         movw    %ax, %ss
414
415         /* Calculate real-mode code segment in %ax and store in ljmp
416          * instruction
417          */
418         movl    %ebx, %eax
419         shrl    $4, %eax
420         movw    %ax, OFFSET(p2r_ljmp) + 3
421
422         /* Switch to real mode */
423         movl    %cr0, %ebx
424         andb    $0!CR0_PE, %bl
425         movl    %ebx, %cr0
426
427         /* Intersegment jump to flush prefetch queue and reload
428          * %cs:eip.  The segment gets filled in by the above code.  We
429          * can't just use lret to achieve this, because we have no
430          * stack at the moment.
431          */
432 p2r_ljmp:
433         ljmp    $0, $OFFSET(1f)
434 1:      
435
436         /* Set %ds to point to code segment for easier data access */
437         movw    %ax, %ds
438                         
439         /* Restore registers */
440         movl    OFFSET(save_eax), %eax
441         movl    OFFSET(save_ebx), %ebx
442
443         /* Set up real-mode data segments and stack */
444         movw    OFFSET(rm_ss), %ss
445         movw    OFFSET(rm_sp), %sp
446         pushw   %ss
447         pushw   %ss
448         pushw   %ss
449         pushw   %ss
450         popw    %ds
451         popw    %es
452         popw    %fs
453         popw    %gs
454
455         /* Set up return address on stack and return */
456         pushw   %cs:OFFSET(save_retaddr)
457         ret
458
459 /****************************************************************************
460  * prot_call (real-mode far call)
461  *
462  * Call a specific C function in the protected-mode code.  The
463  * prototype of the C function must be
464  *   void function ( struct i386_all_regs *ix86 ); 
465  * ix86 will point to a struct containing the real-mode registers
466  * at entry to prot_call.  
467  *
468  * All registers will be preserved across prot_call(), unless the C
469  * function explicitly overwrites values in ix86.  Interrupt status
470  * will also be preserved.  Gate A20 will be enabled.
471  *
472  * The protected-mode code may install librm to a new location.  If it
473  * does so, it must update librm_base in *this* copy of librm to point
474  * to the new physical location.  prot_call will then return via the
475  * newly installed copy.
476  *
477  * Note that when Etherboot performs its initial relocation, "*this*"
478  * copy in the above paragraph will refer to the "master" copy, since
479  * that is the initial installed copy.  Etherboot will return to
480  * prot_call using a virtual address, so will return to the master
481  * copy in high memory (rather than the original copy in base memory).
482  * The master copy in high memory will have the physical address of
483  * the newly installed copy in librm_base, since install_librm()
484  * writes it there.  Thus, Etherboot's initialise() function will
485  * return to the master copy of prot_call(), which will then jump to
486  * the installed copy.
487  *
488  * It works, trust me.
489  *
490  * Parameters:
491  *   function : virtual address of protected-mode function to call
492  *
493  * Example usage:
494  *      pushl   $pxe_api_call
495  *      lcall   $LIBRM_SEGMENT, $prot_call
496  *      addw    $4, %sp
497  * to call in to the C function
498  *      void pxe_api_call ( struct i386_all_regs *ix86 );
499  ****************************************************************************
500  */
501
502 #define PC_OFFSET_IX86 ( 0 )
503 #define PC_OFFSET_RETADDR ( PC_OFFSET_IX86 + SIZEOF_I386_ALL_REGS )
504 #define PC_OFFSET_FUNCTION ( PC_OFFSET_RETADDR + 4 )
505         
506         .code16
507 EXPORT(prot_call):
508         /* Preserve registers and flags on RM stack */
509         pushfl
510         pushal
511         pushw   %gs
512         pushw   %fs
513         pushw   %es
514         pushw   %ds
515         pushw   %ss
516         pushw   %cs     
517         
518         /* Record RM stack pointer */
519         xorl    %ebp, %ebp
520         movw    %sp, %bp
521         
522         /* Physical address of RM stack pointer to %esi */
523         xorl    %esi, %esi
524         pushw   %ss
525         popw    %si
526         shll    $4, %esi
527         addl    %ebp, %esi
528
529         /* Address of pmode function to %ebx */
530         movl    %ss:(PC_OFFSET_FUNCTION)(%bp), %ebx
531         
532         /* Switch to protected mode */
533         pushw   %cs
534         call    real_to_prot
535         .code32
536
537         /* Copy ix86 from RM stack to PM stack */
538         movl    $SIZEOF_I386_ALL_REGS, %ecx
539         subl    %ecx, %esp
540         movl    %esp, %edi
541         pushl   %esi
542         cld
543         rep movsb
544         popl    %edi            /* %edi = phys addr of RM copy of ix86 */
545         
546         /* Switch to virtual addresses. */
547         call    1f
548         jmp     2f
549 1:      ljmp    $VIRTUAL_CS, $_phys_to_virt
550 2:      
551
552         /* Enable A20 line */
553         pushal
554         lcall   $VIRTUAL_CS, $gateA20_set
555         popl    %eax    /* discard */
556         popal
557
558         /* Push &ix86 on the stack, and call function */
559         pushl   %esp
560         call    *%ebx
561         popl    %eax /* discard */
562
563         /* Switch to physical addresses, discard PM register store */
564         lcall   $VIRTUAL_CS, $_virt_to_phys
565         popl    %eax /* discard */
566
567         /* Copy ix86 from PM stack to RM stack, and remove ix86
568          * from PM stack.  (%edi still contains physical address of
569          * ix86 on RM stack from earlier, since C code preserves
570          * %edi).
571          */
572         movl    %esp, %esi
573         movl    $SIZEOF_I386_ALL_REGS, %ecx
574         cld
575         rep movsb
576         movl    %esi, %esp      /* remove ix86 from PM stack */
577
578         /* Obtain physical base address of installed copy of librm in
579          * %ebx.  (It's possible that this *isn't* the physical base
580          * address of the copy we're currently executing in, because
581          * the protected-mode call could have moved librm.  If it does
582          * so, it must update librm_base in our copy to reflect the
583          * new location.
584          */
585         call    1f
586 1:      popl    %ebp
587         movl    OFFSET(librm_base-1b)(%ebp), %ebx
588         
589         /* Jump to running in installed copy of librm */
590         addl    $OFFSET(1f), %ebx
591         jmp     *%ebx
592 1:      
593         
594         /* Switch to real mode */
595         call    prot_to_real
596         .code16
597
598         /* Restore registers and flags, and return */
599         popw    %ax     /* skip %cs */
600         popw    %ax     /* skip %ss */
601         popw    %ds
602         popw    %es
603         popw    %fs
604         popw    %gs
605         popal
606         popfl
607         lret
608
609 /****************************************************************************
610  * real_call (protected-mode near call, virtual addresses)
611  *
612  * Call a real-mode function from protected-mode code.
613  *
614  * The non-segment register values will be passed directly to the
615  * real-mode code.  The segment registers will be set as per
616  * prot_to_real.  The non-segment register values set by the real-mode
617  * function will be passed back to the protected-mode caller.  A
618  * result of this is that this routine cannot be called directly from
619  * C code, since it clobbers registers that the C ABI expects the
620  * callee to preserve.  Gate A20 will be re-enabled in case the
621  * real-mode routine disabled it.
622  *
623  * librm.h defines two convenient macros for using real_call:
624  * REAL_CALL and REAL_EXEC.  See librm.h and realmode.h for details
625  * and examples.
626  *
627  * Parameters:
628  *   far pointer to real-mode function to call
629  *
630  * Returns: none
631  ****************************************************************************
632  */
633
634 #define RC_OFFSET_PRESERVE_REGS ( 0 )
635 #define RC_OFFSET_RETADDR ( RC_OFFSET_PRESERVE_REGS + 8 )
636 #define RC_OFFSET_RM_FUNCTION ( RC_OFFSET_RETADDR + 4 )
637         
638         .code32
639 EXPORT(real_call):
640         /* Preserve registers */
641         pushl   %ebp
642         pushl   %eax
643         
644         /* Switch to physical addresses */
645         lcall   $VIRTUAL_CS, $_virt_to_phys
646         addl    $4, %esp
647
648         /* Extract real-mode function address and store in ljmp instruction */
649         call    1f
650 1:      popl    %ebp
651         movl    RC_OFFSET_RM_FUNCTION(%esp), %eax
652         movl    %eax, (rc_ljmp + 1 - 1b)(%ebp)
653
654         /* Restore registers */
655         popl    %eax
656         popl    %ebp
657
658         /* Switch to real mode, preserving non-segment registers */
659         call    prot_to_real
660         .code16
661
662         /* Far call to real-mode routine */
663         pushw   %cs
664         call    rc_ljmp
665         jmp     2f
666 rc_ljmp:        
667         ljmp    $0, $0  /* address filled in by above code */
668 2:      
669         
670         /* Switch to protected mode */
671         pushw   %cs
672         call    real_to_prot
673         .code32
674
675         /* Switch to virtual addresses */
676         call    1f
677         jmp     2f
678 1:      ljmp    $VIRTUAL_CS, $_phys_to_virt
679 2:      
680
681         /* Enable A20 line */
682         pushal
683         lcall   $VIRTUAL_CS, $gateA20_set
684         popl    %eax    /* discard */
685         popal
686
687         /* Return */
688         ret
689         
690 /****************************************************************************
691  * Relocation lock counter
692  *
693  * librm may be moved in base memory only when this counter is zero.
694  * The counter gets incremented whenever a reference to librm is
695  * generated (e.g. a real_call is made, resulting in a return address
696  * pointing to librm being placed on the stack), and decremented when
697  * the reference goes out of scope (e.g. the real_call returns).
698  ****************************************************************************
699  */
700 EXPORT(librm_ref_count):        .byte 0
701
702 /****************************************************************************
703  * Stored real-mode and protected-mode stack pointers
704  *
705  * The real-mode stack pointer is stored here whenever real_to_prot
706  * is called and restored whenever prot_to_real is called.  The
707  * converse happens for the protected-mode stack pointer.
708  *
709  * Despite initial appearances this scheme is, in fact re-entrant,
710  * because program flow dictates that we always return via the point
711  * we left by.  For example:
712  *    PXE API call entry
713  *  1   real => prot
714  *        ...
715  *        Print a text string
716  *          ...
717  *  2       prot => real
718  *            INT 10
719  *  3       real => prot
720  *          ...
721  *        ...
722  *  4   prot => real
723  *    PXE API call exit
724  *
725  * At point 1, the RM mode stack value, say RPXE, is stored in
726  * rm_ss,sp.  We want this value to still be present in rm_ss,sp when
727  * we reach point 4.
728  *
729  * At point 2, the RM stack value is restored from RPXE.  At point 3,
730  * the RM stack value is again stored in rm_ss,sp.  This *does*
731  * overwrite the RPXE that we have stored there, but it's the same
732  * value, since the code between points 2 and 3 has managed to return
733  * to us.
734  ****************************************************************************
735  */
736
737 EXPORT(rm_stack):       /* comprises rm_ss and rm_sp */
738 rm_sp:          .word 0
739 rm_ss:          .word 0
740
741 EXPORT(pm_stack):
742 pm_esp:         .long 0
743
744 /****************************************************************************
745  * Temporary variables
746  ****************************************************************************
747  */
748 save_eax:       .long 0
749 save_ebx:       .long 0
750 save_retaddr:   .long 0
751         
752 /****************************************************************************
753  * End of librm
754  ****************************************************************************
755  */
756 _librm_end:
757         .globl _librm_size
758         .equ _librm_size, _librm_end - _librm_start