4baa4509f2143567d767be844ec6e414a4230e36
[etherboot.git] / src / arch / i386 / core / realmode_asm.S
1 /* Real-mode interface: assembly-language portions.
2  *
3  * Initial version by Michael Brown <mbrown@fensystems.co.uk>, January 2004.
4  */
5
6 #include "realmode.h"
7 #include "callbacks.h"
8
9 #if     1       /* CODE16 */
10         
11 #define BOCHSBP xchgw %bx,%bx
12
13 #define NUM_PUSHA_REGS (8)
14 #define NUM_SEG_REGS (6)
15
16         .text
17         .arch i386
18         .section ".text16.nocompress", "ax", @progbits
19         .code16
20         
21         .equ    CR0_PE,1
22
23 #ifdef  GAS291
24 #define DATA32 data32;
25 #define ADDR32 addr32;
26 #define LJMPI(x)        ljmp    x
27 #else
28 #define DATA32 data32
29 #define ADDR32 addr32
30 /* newer GAS295 require #define LJMPI(x)        ljmp    *x */
31 #define LJMPI(x)        ljmp    x
32 #endif
33
34 /****************************************************************************
35  * REAL-MODE CALLBACK INTERFACE
36  *
37  * This must be copied down to base memory in order for external
38  * programs to be able to make calls in to Etherboot.  Store the
39  * current physical address of Etherboot (i.e. virt_to_phys(_text)) in
40  * (uint32_t)rm_etherboot_location, then copy
41  * (uint16_t)rm_callback_interface_size bytes starting at
42  * &((void)rm_callback_interface).
43  *
44  * There are two defined entry points:
45  *   Offset RM_IN_CALL     = 0          Near call entry point
46  *   Offset RM_IN_CALL_FAR = 2          Far call entry point
47  *
48  * Note that the routines _prot_to_real and _real_to_prot double as
49  * trampoline fragments for external calls (calls from Etherboot to
50  * real-mode code).  _prot_to_real does not automatically re-enable
51  * interrupts; this is to allow for the potential of using Etherboot
52  * code as an ISR.  _real_to_prot does automatically disable
53  * interrupts, since we don't have a protected-mode IDT.
54  ****************************************************************************
55  */
56
57         .globl  rm_callback_interface
58         .code16
59 rm_callback_interface:
60         .globl  _rm_in_call
61 _rm_in_call:
62         jmp     _real_in_call
63         .globl  _rm_in_call_far
64 _rm_in_call_far:
65         jmp     _real_in_call_far
66
67 /****************************************************************************
68  * _real_in_call
69  *
70  * Parameters:
71  *   16-bit real-mode near/far return address (implicit from [l]call
72  *   to routine) Other parameters as for _in_call_far().
73  *
74  * This routine will convert the 16-bit real-mode far return address
75  * to a 32-bit real-mode far return address, switch to protected mode
76  * using _real_to_prot and call in to _in_call_far.
77  ****************************************************************************
78  */
79
80 #define RIC_PRESERVE ( 8 )
81 #define RIC_OFFSET_CALLADDR ( RIC_PRESERVE )
82 #define RIC_OFFSET_CALLADDR_E ( RIC_OFFSET_CALLADDR + 4 )
83 #define RIC_OFFSET_CONTADDR ( RIC_OFFSET_CALLADDR_E )
84 #define RIC_OFFSET_CONTADDR_E ( RIC_OFFSET_CONTADDR + 4 )
85 #define RIC_OFFSET_OPCODE ( RIC_OFFSET_CONTADDR_E )
86 #define RIC_OFFSET_OPCODE_E ( RIC_OFFSET_OPCODE + 4 )
87 #define RIC_OFFSET_SEG_REGS ( RIC_OFFSET_OPCODE_E )
88 #define RIC_OFFSET_SEG_REGS_E ( RIC_OFFSET_SEG_REGS + ( NUM_SEG_REGS * 2 ) )
89 #define RIC_OFFSET_PAD ( RIC_OFFSET_SEG_REGS_E )
90 #define RIC_OFFSET_PAD_E ( RIC_OFFSET_PAD + 2 )
91 #define RIC_OFFSET_FLAGS ( RIC_OFFSET_PAD_E )
92 #define RIC_OFFSET_FLAGS_E ( RIC_OFFSET_FLAGS + 2 )
93 #define RIC_OFFSET_RETADDR ( RIC_OFFSET_FLAGS_E )
94 #define RIC_OFFSET_RETADDR_E ( RIC_OFFSET_RETADDR + 4 )
95 #define RIC_OFFSET_ORIG_OPCODE ( RIC_OFFSET_RETADDR_E )
96 #define RIC_INSERT_LENGTH ( RIC_OFFSET_OPCODE_E - RIC_OFFSET_CALLADDR )
97         
98         .code16
99 _real_in_call:
100         /* Expand near return address to far return address
101          */
102         pushw   %ax             /* Extend stack, store %ax */
103         pushfw
104         pushw   %bp
105         movw    %sp, %bp
106         movw    %cs, %ax
107         xchgw   %ax, 6(%bp)
108         xchgw   %ax, 4(%bp)     /* also restores %ax */
109         popw    %bp
110         popfw
111         /* Fall through to _real_in_call_far */
112         
113 _real_in_call_far:
114         /* Store flags and pad */
115         pushfw
116         pushw   %ax
117
118         /* Store segment registers.  Order matches that of seg_regs_t */
119         pushw   %gs
120         pushw   %fs
121         pushw   %es
122         pushw   %ds
123         pushw   %ss
124         pushw   %cs
125
126         /* Switch to protected mode */
127         call _real_to_prot
128         .code32
129
130         /* Allow space for expanded stack */
131         subl    $RIC_INSERT_LENGTH, %esp
132         
133         /* Store temporary registers */
134         pushl   %ebp
135         pushl   %eax
136
137         /* Copy opcode, set EB_CALL_FROM_REAL_MODE and EP_SKIP_OPCODE.
138          * Copy it because _in_call() and i386_in_call() expect it at
139          * a fixed position, not as part of the va_list.
140          */
141         movl    RIC_OFFSET_ORIG_OPCODE(%esp), %eax
142         orl     $(EB_CALL_FROM_REAL_MODE|EB_SKIP_OPCODE), %eax
143         movl    %eax, RIC_OFFSET_OPCODE(%esp)
144         
145         /* Set up call and return addresses */
146         call    1f
147 1:      popl    %ebp
148         subl    $1b, %ebp                       /* %ebp = offset */
149         movl    rm_etherboot_location(%ebp), %eax  /* Etherboot phys addr */
150         subl    $_text, %eax
151         addl    $_in_call, %eax                 /* _in_call phys addr */
152         movl    %eax, RIC_OFFSET_CALLADDR(%esp)
153         leal    2f(%ebp), %eax                  /* continuation address */
154         movl    %eax, RIC_OFFSET_CONTADDR(%esp)
155         
156         /* Restore temporary registers */
157         popl    %eax
158         popl    %ebp
159
160         /* Call to _in_call */
161         ret
162         /* opcode will be popped automatically thanks to EB_SKIP_OPCODE */
163
164 2:      /* Continuation point */
165         call    _prot_to_real                   /* Return to real mode */
166         /* Note: the first two words of our segment register store
167          * happens to be exactly what we need to pass as parameters to
168          * _prot_to_real.
169          */
170         .code16
171         popw    %ds                             /* Restore segment registers */
172         popw    %ds                             /* (skip cs&ss since these   */
173         popw    %ds                             /* have already been set by  */
174         popw    %es                             /* _prot_to_real             */
175         popw    %fs
176         popw    %gs
177         addw    $2, %sp                         /* skip pad */
178
179         /* Check for EB_SKIP_OPCODE */
180         pushw   %bp
181         movw    %sp, %bp
182         testl   $EB_SKIP_OPCODE, 6(%bp)
183         popw    %bp
184         jnz     1f
185         /* Normal return */
186         popfw                                   /* Restore interrupt status */
187         lret                                    /* Back to caller */
188 1:      /* Return and skip opcode */
189         popfw
190         lret    $4
191
192 /****************************************************************************
193  * rm_etherboot_location: the current physical location of Etherboot.
194  * Needed so that real-mode callback routines can locate Etherboot.
195  ****************************************************************************
196  */
197         .globl rm_etherboot_location
198 rm_etherboot_location:  .long 0
199                 
200 /****************************************************************************
201  * _prot_to_real_prefix
202  *
203  * Trampoline fragment.  Switch from 32-bit protected mode with flat
204  * physical addresses to 16-bit real mode.  Store registers in the
205  * trampoline for restoration by _real_to_prot_suffix.  Switch to
206  * stack in base memory.
207  ****************************************************************************
208  */
209         
210         .globl _prot_to_real_prefix
211         .code32
212 _prot_to_real_prefix:
213         /* Registers to preserve */
214         pushl   %ebx
215         pushl   %esi
216         pushl   %edi
217         pushl   %ebp
218
219         /* Calculate offset */
220         call    1f
221 1:      popl    %ebp
222         subl    $1b, %ebp               /* %ebp = offset for labels in p2r*/
223
224         /* Preserve registers and return address in r2p_params */
225         movl    p2r_r2p_params(%ebp), %ebx
226         subl    $r2p_params, %ebx       /* %ebx = offset for labels in r2p */
227         popl    r2p_ebp(%ebx)
228         popl    r2p_edi(%ebx)
229         popl    r2p_esi(%ebx)
230         popl    r2p_ebx(%ebx)
231         popl    r2p_ret_addr(%ebx)
232         movl    %esp, r2p_esp(%ebx)
233
234         /* Switch stacks */
235         movl    p2r_esp(%ebp), %esp
236
237         /* Switch to real mode */
238         pushl   p2r_segments(%ebp)
239         call    _prot_to_real
240         .code16
241         addw    $4, %sp
242                 
243         /* Fall through to next trampoline fragment */
244         jmp     _prot_to_real_prefix_end
245         
246 /****************************************************************************
247  * _prot_to_real
248  *
249  * Switch from 32-bit protected mode with flat physical addresses to
250  * 16-bit real mode.  Stack and code must be in base memory when
251  * called.  %cs, %ss, %eip, %esp are changed to real-mode values,
252  * other segment registers are destroyed, all other registers are
253  * preserved.  Interrupts are *not* enabled.
254  *
255  * Parameters:
256  *   %cs                Real-mode code segment (word)
257  *   %ss                Real-mode stack segment (word)
258  ****************************************************************************
259  */
260
261 #define P2R_PRESERVE ( 12 )
262 #define P2R_OFFSET_RETADDR ( P2R_PRESERVE )
263 #define P2R_OFFSET_RETADDR_E ( P2R_OFFSET_RETADDR + 4 )
264 #define P2R_OFFSET_CS ( P2R_OFFSET_RETADDR_E )
265 #define P2R_OFFSET_CS_E ( P2R_OFFSET_CS + 2 )
266 #define P2R_OFFSET_SS ( P2R_OFFSET_CS_E )
267 #define P2R_OFFSET_SS_E ( P2R_OFFSET_SS + 2 )
268
269         .globl _prot_to_real
270         .code32
271 _prot_to_real:
272         /* Preserve registers */
273         pushl   %ebp
274         pushl   %ebx
275         pushl   %eax
276         
277         /* Calculate offset */
278         call    1f
279 1:      popl    %ebp
280         subl    $1b, %ebp               /* %ebp = offset for labels in p2r*/
281
282         /* Set up GDT with real-mode limits and appropriate bases for
283          * real-mode %cs and %ss.  Set up protected-mode continuation
284          * point on stack.
285          */
286         /* Fixup GDT */
287         leal    p2r_gdt(%ebp), %eax
288         movl    %eax, p2r_gdt_addr(%ebp)
289
290         /* Calculate CS base address: set GDT code segment, adjust
291          * return address, set up continuation address on stack.
292          */
293         movzwl  P2R_OFFSET_CS(%esp), %eax
294         shll    $4, %eax
295         /* Change return address to real-mode far address */
296         subl    %eax, P2R_OFFSET_RETADDR(%esp)
297         movl    %eax, %ebx
298         shrl    $4, %ebx
299         movw    %bx, (P2R_OFFSET_RETADDR+2)(%esp)
300         /* First real mode address */
301         movl    %eax, %ebx
302         shrl    $4, %ebx
303         pushw   %bx
304         leal    3f(%ebp), %ebx
305         subl    %eax, %ebx
306         pushw   %bx
307         /* Continuation address */
308         pushl   $(p2r_rmcs - p2r_gdt)
309         leal    2f(%ebp), %ebx
310         subl    %eax, %ebx
311         pushl   %ebx
312         /* Code segment in GDT */
313         movw    %ax, (p2r_rmcs+2)(%ebp)
314         shrl    $16, %eax                       /* Remainder of cs base addr */
315         movb    %al, (p2r_rmcs+4)(%ebp)
316         movb    %ah, (p2r_rmcs+7)(%ebp)
317
318         /* Calculate SS base address: set GDT data segment, retain to
319          * use for adjusting %esp.
320          */
321         movzwl  (12+P2R_OFFSET_SS)(%esp), %eax  /* Set ss base address */
322         shll    $4, %eax
323         movw    %ax, (p2r_rmds+2)(%ebp)
324         movl    %eax, %ebx
325         shrl    $16, %ebx
326         movb    %bl, (p2r_rmds+4)(%ebp)
327         movb    %bh, (p2r_rmds+7)(%ebp)
328
329         /* Load GDT */
330         lgdt    p2r_gdt(%ebp)
331         /* Reload all segment registers and adjust %esp */
332         movw    $(p2r_rmds - p2r_gdt), %bx /* Pmode DS */
333         movw    %bx, %ss
334         subl    %eax, %esp              /* %esp now less than 0x10000 */
335         movw    %bx, %ds
336         movw    %bx, %es
337         movw    %bx, %fs
338         movw    %bx, %gs
339         lret                            /* %cs:eip */
340 2:      /* Segment registers now have 16-bit limits. */
341         .code16
342
343         /* Switch to real mode */
344         movl    %cr0, %ebx
345         andb    $0!CR0_PE, %bl
346         movl    %ebx, %cr0
347
348         /* Make intersegment jmp to flush the processor pipeline
349          * and reload %cs:%eip (to clear upper 16 bits of %eip).
350          */
351         lret
352 3:              
353         
354         /* Load real-mode segment value to %ss.  %sp already OK */
355         shrl    $4, %eax
356         movw    %ax, %ss
357
358         /* Restore registers */
359         popl    %eax
360         popl    %ebx
361         popl    %ebp
362
363         /* Return to caller in real-mode */
364         lret
365
366 #ifdef FLATTEN_REAL_MODE
367 #define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x8f
368 #else
369 #define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x00
370 #endif
371
372 p2r_gdt:
373 p2r_gdtarg:
374 p2r_gdt_limit:          .word p2r_gdt_end - p2r_gdt - 1
375 p2r_gdt_addr:           .long 0
376 p2r_gdt_padding:        .word 0
377 p2r_rmcs:
378         /* 16 bit real mode code segment */
379         .word   0xffff,(0&0xffff)
380         .byte   (0>>16),0x9b,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24)
381 p2r_rmds:
382         /* 16 bit real mode data segment */
383         .word   0xffff,(0&0xffff)
384         .byte   (0>>16),0x93,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24)
385 p2r_gdt_end:
386
387         /* This is the end of the trampoline prefix code.  When used
388          * as a prefix, fall through to the following code in the
389          * trampoline.
390          */
391 p2r_params: /* Structure must match prot_to_real_params_t in realmode.h */
392 p2r_esp:        .long 0
393 p2r_segments:
394 p2r_cs:         .word 0
395 p2r_ss:         .word 0
396 p2r_r2p_params: .long 0
397         .globl  _prot_to_real_prefix_end
398 _prot_to_real_prefix_end:
399         
400         .globl  _prot_to_real_prefix_size
401         .equ    _prot_to_real_prefix_size, _prot_to_real_prefix_end - _prot_to_real_prefix
402         .globl  prot_to_real_prefix_size
403 prot_to_real_prefix_size:       
404         .word   _prot_to_real_prefix_size
405         
406 /****************************************************************************
407  * _real_to_prot_suffix
408  *
409  * Trampoline fragment.  Switch from 16-bit real-mode to 32-bit
410  * protected mode with flat physical addresses.  Copy returned stack
411  * parameters to output_stack.  Restore registers preserved by
412  * _prot_to_real_prefix.  Restore stack to previous location.
413  ****************************************************************************
414  */
415
416         .globl _real_to_prot_suffix
417         .code16
418 _real_to_prot_suffix:
419
420         /* Switch to protected mode */
421         call    _real_to_prot
422         .code32
423
424         /* Calculate offset */
425         call    1f
426 1:      popl    %ebp
427         subl    $1b, %ebp               /* %ebp = offset for labels in r2p */
428
429         /* Copy stack to out_stack */
430         movl    r2p_out_stack(%ebp), %edi
431         movl    r2p_out_stack_len(%ebp), %ecx
432         movl    %esp, %esi
433         cld
434         rep movsb
435
436         /* Switch back to original stack */
437         movl    r2p_esp(%ebp), %esp
438
439         /* Restore registers and return */
440         pushl   r2p_ret_addr(%ebp)      /* Set up return address on stack */
441         movl    r2p_ebx(%ebp), %ebx
442         movl    r2p_esi(%ebp), %esi
443         movl    r2p_edi(%ebp), %edi
444         movl    r2p_ebp(%ebp), %ebp
445         ret
446
447 /****************************************************************************
448  * _real_to_prot
449  *
450  * Switch from 16-bit real-mode to 32-bit protected mode with flat
451  * physical addresses.  All segment registers are destroyed, %eip and
452  * %esp are changed to flat physical values, all other registers are
453  * preserved.  Interrupts are disabled.
454  *
455  * Parameters: none
456  ****************************************************************************
457  */
458
459 #define R2P_PRESERVE ( 12 )
460 #define R2P_OFFSET_RETADDR ( R2P_PRESERVE )
461 #define R2P_OFFSET_ORIG_RETADDR ( R2P_OFFSET_RETADDR + 2 )
462
463         .globl _real_to_prot
464         .code16         
465 _real_to_prot:
466         /* Disable interrupts */
467         cli
468         /* zero extend the return address */
469         pushw   $0
470
471         /* Preserve registers */
472         pushl   %ebp
473         pushl   %ebx
474         pushl   %eax
475
476         /* Convert 16-bit real-mode near return address to
477          * 32-bit pmode physical near return address
478          */
479         movw    %sp, %bp
480         xorl    %ebx, %ebx
481         push    %cs
482         popw    %bx
483         movw    %bx, %ds
484         shll    $4, %ebx
485         movzwl  %ss:R2P_OFFSET_ORIG_RETADDR(%bp), %eax
486         addl    %ebx, %eax
487         movl    %eax, %ss:(R2P_OFFSET_RETADDR)(%bp)
488
489         /* Store the code segment physical base address in %ebp */
490         movl    %ebx, %ebp
491
492         /* Find the offset within the code segment that I am running at */
493         xorl    %ebx, %ebx
494         call    1f
495 1:      popw    %bx
496
497         /* Set up GDT */
498         leal    (r2p_gdt-1b)(%bx), %eax /* %ds:ebx = %ds:bx = &(r2p_gdt) */
499         addl    %ebp, %eax              /* %eax = &r2p_gdt (physical) */
500         movl    %eax, %ds:(r2p_gdt-1b+2)(%bx) /* Set phys. addr. in r2p_gdt */
501
502         /* Compute the first protected mode physical address */
503         leal    (2f-1b)(%bx), %eax
504         addl    %ebp, %eax
505         movl    %eax, %ds:(r2p_paddr-1b)(%bx)
506
507         /* Calculate new %esp */
508         xorl    %eax, %eax
509         push    %ss
510         popw    %ax
511         shll    $4, %eax
512         movzwl  %sp, %ebp
513         addl    %eax, %ebp              /* %ebp = new %esp */
514         
515         /* Load GDT */
516         DATA32 lgdt %ds:(r2p_gdt-1b)(%bx)       /* Load GDT */
517
518         /* Switch to protected mode */
519         movl    %cr0, %eax
520         orb     $CR0_PE, %al
521         movl    %eax, %cr0
522
523         /* flush prefetch queue, and reload %cs:%eip */
524         DATA32 ljmp %ds:*(r2p_paddr-1b)(%bx)
525         .code32
526 2:
527         
528         /* Load segment registers, adjust %esp */
529         movw    $(r2p_pmds-r2p_gdt), %ax
530         movw    %ax, %ss
531         movl    %ebp, %esp
532         movw    %ax, %ds
533         movw    %ax, %es
534         movw    %ax, %fs
535         movw    %ax, %gs
536
537         /* Restore registers */
538         popl    %eax
539         popl    %ebx
540         popl    %ebp
541
542         /* return to caller */
543         ret
544
545 r2p_gdt:
546         .word   r2p_gdt_end - r2p_gdt - 1       /* limit */
547         .long 0                                 /* addr */
548         .word 0
549 r2p_pmcs:
550         /* 32 bit protected mode code segment, physical addresses */
551         .word   0xffff, 0
552         .byte   0, 0x9f, 0xcf, 0
553 r2p_pmds:
554         /* 32 bit protected mode data segment, physical addresses */
555         .word   0xffff,0
556         .byte   0,0x93,0xcf,0
557 r2p_gdt_end:
558
559 r2p_paddr:
560         .long 2b
561         .word r2p_pmcs - r2p_gdt, 0
562
563
564         /* This is the end of the trampoline suffix code.
565          */
566 r2p_params: /* Structure must match real_to_prot_params_t in realmode.h */
567 r2p_ret_addr:           .long 0
568 r2p_esp:                .long 0
569 r2p_ebx:                .long 0
570 r2p_esi:                .long 0
571 r2p_edi:                .long 0
572 r2p_ebp:                .long 0
573 r2p_out_stack:          .long 0
574 r2p_out_stack_len:      .long 0
575         .globl  _real_to_prot_suffix_end
576 _real_to_prot_suffix_end:
577
578         .globl  _real_to_prot_suffix_size
579         .equ    _real_to_prot_suffix_size, _real_to_prot_suffix_end - _real_to_prot_suffix
580         .globl  real_to_prot_suffix_size
581 real_to_prot_suffix_size:
582         .word   _real_to_prot_suffix_size
583
584 rm_callback_interface_end:
585
586         .globl  _rm_callback_interface_size
587         .equ    _rm_callback_interface_size, rm_callback_interface_end - rm_callback_interface
588         .globl  rm_callback_interface_size
589 rm_callback_interface_size:
590         .word   _rm_callback_interface_size
591
592 /****************************************************************************
593  * END OF REAL-MODE CALLBACK INTERFACE
594  ****************************************************************************
595  */
596
597
598 #ifdef PXE_EXPORT
599 /****************************************************************************
600  * PXE CALLBACK INTERFACE
601  *
602  * Prepend this to rm_callback_interface to create a real-mode PXE
603  * callback interface.
604  ****************************************************************************
605  */
606         .section ".text16", "ax", @progbits
607         .globl  pxe_callback_interface
608         .code16
609 pxe_callback_interface:
610
611 /* Macro to calculate offset of labels within code segment in
612  * installed copy of code.
613  */
614 #define INSTALLED(x) ( (x) - pxe_callback_interface )
615
616 /****************************************************************************
617  * PXE entry points (!PXE and PXENV+ APIs)
618  ****************************************************************************
619  */
620         /* in_call mechanism for !PXE API calls */
621         .globl  _pxe_in_call_far
622 _pxe_in_call_far:
623         /* Prepend "PXE API call" and "API version 0x201" to stack */
624         pushl   $0x201
625         jmp     1f
626         /* in_call mechanism for PXENV+ API calls */
627         .globl  _pxenv_in_call_far
628 _pxenv_in_call_far:
629         /* Prepend "PXE API call" and "API version 0x200" to stack */
630         pushl   $0x200
631 1:      pushl   $EB_OPCODE_PXE
632         /* Perform real-mode in_call */
633         call    pxe_rm_in_call
634         /* Return */
635         addw    $8, %sp
636         lret
637
638 /****************************************************************************
639  * PXE installation check (INT 1A) code
640  ****************************************************************************
641  */
642         .globl  _pxe_intercept_int1a
643 _pxe_intercept_int1a:
644         pushfw
645         cmpw    $0x5650, %ax
646         jne     2f
647 1:      /* INT 1A,5650 - Intercept */
648         popfw
649         /* Set up return values according to PXE spec: */
650         movw    $0x564e, %ax            /* AX := 564Eh (VN) */
651         pushw   %cs:INSTALLED(_pxe_pxenv_segment)
652         popw    %es                     /* ES:BX := &(PXENV+ structure) */
653         movw    %cs:INSTALLED(_pxe_pxenv_offset), %bx
654         clc                             /* CF is cleared */
655         lret    $2                      /* 'iret' without reloading flags */
656 2:      /* INT 1A,other - Do not intercept */
657         popfw
658         ljmp    %cs:*INSTALLED(_pxe_intercepted_int1a)
659
660         .globl  _pxe_intercepted_int1a
661 _pxe_intercepted_int1a: .word 0,0
662         .globl  _pxe_pxenv_location
663 _pxe_pxenv_location:
664 _pxe_pxenv_offset:      .word 0
665 _pxe_pxenv_segment:     .word 0
666         
667 pxe_rm_in_call: 
668 pxe_attach_rm:
669         /* rm_callback_interface must be appended here */
670
671 pxe_callback_interface_end:
672
673         .globl  _pxe_callback_interface_size
674         .equ    _pxe_callback_interface_size, pxe_callback_interface_end - pxe_callback_interface
675         .globl  pxe_callback_interface_size
676 pxe_callback_interface_size:
677         .word   _pxe_callback_interface_size
678
679 #else   /* PXE_EXPORT */
680         
681 /* Define symbols used by the linker scripts, to prevent link errors */
682         .globl  _pxe_callback_interface_size
683         .equ    _pxe_callback_interface_size, 0 
684         
685 #endif  /* PXE_EXPORT */
686
687 #else   /* CODE16 */
688
689 /* Define symbols used by the linker scripts, to prevent link errors */
690         .globl  _rm_callback_interface_size
691         .equ    _rm_callback_interface_size, 0  
692         .globl  _pxe_callback_interface_size
693         .equ    _pxe_callback_interface_size, 0 
694                 
695 #endif  /* CODE16 */