696 lines
18 KiB
ArmAsm
696 lines
18 KiB
ArmAsm
/* Real-mode interface: assembly-language portions.
|
|
*
|
|
* Initial version by Michael Brown <mbrown@fensystems.co.uk>, January 2004.
|
|
*/
|
|
|
|
#include "realmode.h"
|
|
#include "callbacks.h"
|
|
|
|
#if 1 /* CODE16 */
|
|
|
|
#define BOCHSBP xchgw %bx,%bx
|
|
|
|
#define NUM_PUSHA_REGS (8)
|
|
#define NUM_SEG_REGS (6)
|
|
|
|
.text
|
|
.arch i386
|
|
.section ".text16.nocompress", "ax", @progbits
|
|
.code16
|
|
|
|
.equ CR0_PE,1
|
|
|
|
#ifdef GAS291
|
|
#define DATA32 data32;
|
|
#define ADDR32 addr32;
|
|
#define LJMPI(x) ljmp x
|
|
#else
|
|
#define DATA32 data32
|
|
#define ADDR32 addr32
|
|
/* newer GAS295 require #define LJMPI(x) ljmp *x */
|
|
#define LJMPI(x) ljmp x
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* REAL-MODE CALLBACK INTERFACE
|
|
*
|
|
* This must be copied down to base memory in order for external
|
|
* programs to be able to make calls in to Etherboot. Store the
|
|
* current physical address of Etherboot (i.e. virt_to_phys(_text)) in
|
|
* (uint32_t)rm_etherboot_location, then copy
|
|
* (uint16_t)rm_callback_interface_size bytes starting at
|
|
* &((void)rm_callback_interface).
|
|
*
|
|
* There are two defined entry points:
|
|
* Offset RM_IN_CALL = 0 Near call entry point
|
|
* Offset RM_IN_CALL_FAR = 2 Far call entry point
|
|
*
|
|
* Note that the routines _prot_to_real and _real_to_prot double as
|
|
* trampoline fragments for external calls (calls from Etherboot to
|
|
* real-mode code). _prot_to_real does not automatically re-enable
|
|
* interrupts; this is to allow for the potential of using Etherboot
|
|
* code as an ISR. _real_to_prot does automatically disable
|
|
* interrupts, since we don't have a protected-mode IDT.
|
|
****************************************************************************
|
|
*/
|
|
|
|
.globl rm_callback_interface
|
|
.code16
|
|
rm_callback_interface:
|
|
.globl _rm_in_call
|
|
_rm_in_call:
|
|
jmp _real_in_call
|
|
.globl _rm_in_call_far
|
|
_rm_in_call_far:
|
|
jmp _real_in_call_far
|
|
|
|
/****************************************************************************
|
|
* _real_in_call
|
|
*
|
|
* Parameters:
|
|
* 16-bit real-mode near/far return address (implicit from [l]call
|
|
* to routine) Other parameters as for _in_call_far().
|
|
*
|
|
* This routine will convert the 16-bit real-mode far return address
|
|
* to a 32-bit real-mode far return address, switch to protected mode
|
|
* using _real_to_prot and call in to _in_call_far.
|
|
****************************************************************************
|
|
*/
|
|
|
|
#define RIC_PRESERVE ( 8 )
|
|
#define RIC_OFFSET_CALLADDR ( RIC_PRESERVE )
|
|
#define RIC_OFFSET_CALLADDR_E ( RIC_OFFSET_CALLADDR + 4 )
|
|
#define RIC_OFFSET_CONTADDR ( RIC_OFFSET_CALLADDR_E )
|
|
#define RIC_OFFSET_CONTADDR_E ( RIC_OFFSET_CONTADDR + 4 )
|
|
#define RIC_OFFSET_OPCODE ( RIC_OFFSET_CONTADDR_E )
|
|
#define RIC_OFFSET_OPCODE_E ( RIC_OFFSET_OPCODE + 4 )
|
|
#define RIC_OFFSET_SEG_REGS ( RIC_OFFSET_OPCODE_E )
|
|
#define RIC_OFFSET_SEG_REGS_E ( RIC_OFFSET_SEG_REGS + ( NUM_SEG_REGS * 2 ) )
|
|
#define RIC_OFFSET_PAD ( RIC_OFFSET_SEG_REGS_E )
|
|
#define RIC_OFFSET_PAD_E ( RIC_OFFSET_PAD + 2 )
|
|
#define RIC_OFFSET_FLAGS ( RIC_OFFSET_PAD_E )
|
|
#define RIC_OFFSET_FLAGS_E ( RIC_OFFSET_FLAGS + 2 )
|
|
#define RIC_OFFSET_RETADDR ( RIC_OFFSET_FLAGS_E )
|
|
#define RIC_OFFSET_RETADDR_E ( RIC_OFFSET_RETADDR + 4 )
|
|
#define RIC_OFFSET_ORIG_OPCODE ( RIC_OFFSET_RETADDR_E )
|
|
#define RIC_INSERT_LENGTH ( RIC_OFFSET_OPCODE_E - RIC_OFFSET_CALLADDR )
|
|
|
|
.code16
|
|
_real_in_call:
|
|
/* Expand near return address to far return address
|
|
*/
|
|
pushw %ax /* Extend stack, store %ax */
|
|
pushfw
|
|
pushw %bp
|
|
movw %sp, %bp
|
|
movw %cs, %ax
|
|
xchgw %ax, 6(%bp)
|
|
xchgw %ax, 4(%bp) /* also restores %ax */
|
|
popw %bp
|
|
popfw
|
|
/* Fall through to _real_in_call_far */
|
|
|
|
_real_in_call_far:
|
|
/* Store flags and pad */
|
|
pushfw
|
|
pushw %ax
|
|
|
|
/* Store segment registers. Order matches that of seg_regs_t */
|
|
pushw %gs
|
|
pushw %fs
|
|
pushw %es
|
|
pushw %ds
|
|
pushw %ss
|
|
pushw %cs
|
|
|
|
/* Switch to protected mode */
|
|
call _real_to_prot
|
|
.code32
|
|
|
|
/* Allow space for expanded stack */
|
|
subl $RIC_INSERT_LENGTH, %esp
|
|
|
|
/* Store temporary registers */
|
|
pushl %ebp
|
|
pushl %eax
|
|
|
|
/* Copy opcode, set EB_CALL_FROM_REAL_MODE and EP_SKIP_OPCODE.
|
|
* Copy it because _in_call() and i386_in_call() expect it at
|
|
* a fixed position, not as part of the va_list.
|
|
*/
|
|
movl RIC_OFFSET_ORIG_OPCODE(%esp), %eax
|
|
orl $(EB_CALL_FROM_REAL_MODE|EB_SKIP_OPCODE), %eax
|
|
movl %eax, RIC_OFFSET_OPCODE(%esp)
|
|
|
|
/* Set up call and return addresses */
|
|
call 1f
|
|
1: popl %ebp
|
|
subl $1b, %ebp /* %ebp = offset */
|
|
movl rm_etherboot_location(%ebp), %eax /* Etherboot phys addr */
|
|
subl $_text, %eax
|
|
addl $_in_call, %eax /* _in_call phys addr */
|
|
movl %eax, RIC_OFFSET_CALLADDR(%esp)
|
|
leal 2f(%ebp), %eax /* continuation address */
|
|
movl %eax, RIC_OFFSET_CONTADDR(%esp)
|
|
|
|
/* Restore temporary registers */
|
|
popl %eax
|
|
popl %ebp
|
|
|
|
/* Call to _in_call */
|
|
ret
|
|
/* opcode will be popped automatically thanks to EB_SKIP_OPCODE */
|
|
|
|
2: /* Continuation point */
|
|
call _prot_to_real /* Return to real mode */
|
|
/* Note: the first two words of our segment register store
|
|
* happens to be exactly what we need to pass as parameters to
|
|
* _prot_to_real.
|
|
*/
|
|
.code16
|
|
popw %ds /* Restore segment registers */
|
|
popw %ds /* (skip cs&ss since these */
|
|
popw %ds /* have already been set by */
|
|
popw %es /* _prot_to_real */
|
|
popw %fs
|
|
popw %gs
|
|
addw $2, %sp /* skip pad */
|
|
|
|
/* Check for EB_SKIP_OPCODE */
|
|
pushw %bp
|
|
movw %sp, %bp
|
|
testl $EB_SKIP_OPCODE, 6(%bp)
|
|
popw %bp
|
|
jnz 1f
|
|
/* Normal return */
|
|
popfw /* Restore interrupt status */
|
|
lret /* Back to caller */
|
|
1: /* Return and skip opcode */
|
|
popfw
|
|
lret $4
|
|
|
|
/****************************************************************************
|
|
* rm_etherboot_location: the current physical location of Etherboot.
|
|
* Needed so that real-mode callback routines can locate Etherboot.
|
|
****************************************************************************
|
|
*/
|
|
.globl rm_etherboot_location
|
|
rm_etherboot_location: .long 0
|
|
|
|
/****************************************************************************
|
|
* _prot_to_real_prefix
|
|
*
|
|
* Trampoline fragment. Switch from 32-bit protected mode with flat
|
|
* physical addresses to 16-bit real mode. Store registers in the
|
|
* trampoline for restoration by _real_to_prot_suffix. Switch to
|
|
* stack in base memory.
|
|
****************************************************************************
|
|
*/
|
|
|
|
.globl _prot_to_real_prefix
|
|
.code32
|
|
_prot_to_real_prefix:
|
|
/* Registers to preserve */
|
|
pushl %ebx
|
|
pushl %esi
|
|
pushl %edi
|
|
pushl %ebp
|
|
|
|
/* Calculate offset */
|
|
call 1f
|
|
1: popl %ebp
|
|
subl $1b, %ebp /* %ebp = offset for labels in p2r*/
|
|
|
|
/* Preserve registers and return address in r2p_params */
|
|
movl p2r_r2p_params(%ebp), %ebx
|
|
subl $r2p_params, %ebx /* %ebx = offset for labels in r2p */
|
|
popl r2p_ebp(%ebx)
|
|
popl r2p_edi(%ebx)
|
|
popl r2p_esi(%ebx)
|
|
popl r2p_ebx(%ebx)
|
|
popl r2p_ret_addr(%ebx)
|
|
movl %esp, r2p_esp(%ebx)
|
|
|
|
/* Switch stacks */
|
|
movl p2r_esp(%ebp), %esp
|
|
|
|
/* Switch to real mode */
|
|
pushl p2r_segments(%ebp)
|
|
call _prot_to_real
|
|
.code16
|
|
addw $4, %sp
|
|
|
|
/* Fall through to next trampoline fragment */
|
|
jmp _prot_to_real_prefix_end
|
|
|
|
/****************************************************************************
|
|
* _prot_to_real
|
|
*
|
|
* Switch from 32-bit protected mode with flat physical addresses to
|
|
* 16-bit real mode. Stack and code must be in base memory when
|
|
* called. %cs, %ss, %eip, %esp are changed to real-mode values,
|
|
* other segment registers are destroyed, all other registers are
|
|
* preserved. Interrupts are *not* enabled.
|
|
*
|
|
* Parameters:
|
|
* %cs Real-mode code segment (word)
|
|
* %ss Real-mode stack segment (word)
|
|
****************************************************************************
|
|
*/
|
|
|
|
#define P2R_PRESERVE ( 12 )
|
|
#define P2R_OFFSET_RETADDR ( P2R_PRESERVE )
|
|
#define P2R_OFFSET_RETADDR_E ( P2R_OFFSET_RETADDR + 4 )
|
|
#define P2R_OFFSET_CS ( P2R_OFFSET_RETADDR_E )
|
|
#define P2R_OFFSET_CS_E ( P2R_OFFSET_CS + 2 )
|
|
#define P2R_OFFSET_SS ( P2R_OFFSET_CS_E )
|
|
#define P2R_OFFSET_SS_E ( P2R_OFFSET_SS + 2 )
|
|
|
|
.globl _prot_to_real
|
|
.code32
|
|
_prot_to_real:
|
|
/* Preserve registers */
|
|
pushl %ebp
|
|
pushl %ebx
|
|
pushl %eax
|
|
|
|
/* Calculate offset */
|
|
call 1f
|
|
1: popl %ebp
|
|
subl $1b, %ebp /* %ebp = offset for labels in p2r*/
|
|
|
|
/* Set up GDT with real-mode limits and appropriate bases for
|
|
* real-mode %cs and %ss. Set up protected-mode continuation
|
|
* point on stack.
|
|
*/
|
|
/* Fixup GDT */
|
|
leal p2r_gdt(%ebp), %eax
|
|
movl %eax, p2r_gdt_addr(%ebp)
|
|
|
|
/* Calculate CS base address: set GDT code segment, adjust
|
|
* return address, set up continuation address on stack.
|
|
*/
|
|
movzwl P2R_OFFSET_CS(%esp), %eax
|
|
shll $4, %eax
|
|
/* Change return address to real-mode far address */
|
|
subl %eax, P2R_OFFSET_RETADDR(%esp)
|
|
movl %eax, %ebx
|
|
shrl $4, %ebx
|
|
movw %bx, (P2R_OFFSET_RETADDR+2)(%esp)
|
|
/* First real mode address */
|
|
movl %eax, %ebx
|
|
shrl $4, %ebx
|
|
pushw %bx
|
|
leal 3f(%ebp), %ebx
|
|
subl %eax, %ebx
|
|
pushw %bx
|
|
/* Continuation address */
|
|
pushl $(p2r_rmcs - p2r_gdt)
|
|
leal 2f(%ebp), %ebx
|
|
subl %eax, %ebx
|
|
pushl %ebx
|
|
/* Code segment in GDT */
|
|
movw %ax, (p2r_rmcs+2)(%ebp)
|
|
shrl $16, %eax /* Remainder of cs base addr */
|
|
movb %al, (p2r_rmcs+4)(%ebp)
|
|
movb %ah, (p2r_rmcs+7)(%ebp)
|
|
|
|
/* Calculate SS base address: set GDT data segment, retain to
|
|
* use for adjusting %esp.
|
|
*/
|
|
movzwl (12+P2R_OFFSET_SS)(%esp), %eax /* Set ss base address */
|
|
shll $4, %eax
|
|
movw %ax, (p2r_rmds+2)(%ebp)
|
|
movl %eax, %ebx
|
|
shrl $16, %ebx
|
|
movb %bl, (p2r_rmds+4)(%ebp)
|
|
movb %bh, (p2r_rmds+7)(%ebp)
|
|
|
|
/* Load GDT */
|
|
lgdt p2r_gdt(%ebp)
|
|
/* Reload all segment registers and adjust %esp */
|
|
movw $(p2r_rmds - p2r_gdt), %bx /* Pmode DS */
|
|
movw %bx, %ss
|
|
subl %eax, %esp /* %esp now less than 0x10000 */
|
|
movw %bx, %ds
|
|
movw %bx, %es
|
|
movw %bx, %fs
|
|
movw %bx, %gs
|
|
lret /* %cs:eip */
|
|
2: /* Segment registers now have 16-bit limits. */
|
|
.code16
|
|
|
|
/* Switch to real mode */
|
|
movl %cr0, %ebx
|
|
andb $0!CR0_PE, %bl
|
|
movl %ebx, %cr0
|
|
|
|
/* Make intersegment jmp to flush the processor pipeline
|
|
* and reload %cs:%eip (to clear upper 16 bits of %eip).
|
|
*/
|
|
lret
|
|
3:
|
|
|
|
/* Load real-mode segment value to %ss. %sp already OK */
|
|
shrl $4, %eax
|
|
movw %ax, %ss
|
|
|
|
/* Restore registers */
|
|
popl %eax
|
|
popl %ebx
|
|
popl %ebp
|
|
|
|
/* Return to caller in real-mode */
|
|
lret
|
|
|
|
#ifdef FLATTEN_REAL_MODE
|
|
#define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x8f
|
|
#else
|
|
#define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x00
|
|
#endif
|
|
|
|
p2r_gdt:
|
|
p2r_gdtarg:
|
|
p2r_gdt_limit: .word p2r_gdt_end - p2r_gdt - 1
|
|
p2r_gdt_addr: .long 0
|
|
p2r_gdt_padding: .word 0
|
|
p2r_rmcs:
|
|
/* 16 bit real mode code segment */
|
|
.word 0xffff,(0&0xffff)
|
|
.byte (0>>16),0x9b,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24)
|
|
p2r_rmds:
|
|
/* 16 bit real mode data segment */
|
|
.word 0xffff,(0&0xffff)
|
|
.byte (0>>16),0x93,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24)
|
|
p2r_gdt_end:
|
|
|
|
/* This is the end of the trampoline prefix code. When used
|
|
* as a prefix, fall through to the following code in the
|
|
* trampoline.
|
|
*/
|
|
p2r_params: /* Structure must match prot_to_real_params_t in realmode.h */
|
|
p2r_esp: .long 0
|
|
p2r_segments:
|
|
p2r_cs: .word 0
|
|
p2r_ss: .word 0
|
|
p2r_r2p_params: .long 0
|
|
.globl _prot_to_real_prefix_end
|
|
_prot_to_real_prefix_end:
|
|
|
|
.globl _prot_to_real_prefix_size
|
|
.equ _prot_to_real_prefix_size, _prot_to_real_prefix_end - _prot_to_real_prefix
|
|
.globl prot_to_real_prefix_size
|
|
prot_to_real_prefix_size:
|
|
.word _prot_to_real_prefix_size
|
|
|
|
/****************************************************************************
|
|
* _real_to_prot_suffix
|
|
*
|
|
* Trampoline fragment. Switch from 16-bit real-mode to 32-bit
|
|
* protected mode with flat physical addresses. Copy returned stack
|
|
* parameters to output_stack. Restore registers preserved by
|
|
* _prot_to_real_prefix. Restore stack to previous location.
|
|
****************************************************************************
|
|
*/
|
|
|
|
.globl _real_to_prot_suffix
|
|
.code16
|
|
_real_to_prot_suffix:
|
|
|
|
/* Switch to protected mode */
|
|
call _real_to_prot
|
|
.code32
|
|
|
|
/* Calculate offset */
|
|
call 1f
|
|
1: popl %ebp
|
|
subl $1b, %ebp /* %ebp = offset for labels in r2p */
|
|
|
|
/* Copy stack to out_stack */
|
|
movl r2p_out_stack(%ebp), %edi
|
|
movl r2p_out_stack_len(%ebp), %ecx
|
|
movl %esp, %esi
|
|
cld
|
|
rep movsb
|
|
|
|
/* Switch back to original stack */
|
|
movl r2p_esp(%ebp), %esp
|
|
|
|
/* Restore registers and return */
|
|
pushl r2p_ret_addr(%ebp) /* Set up return address on stack */
|
|
movl r2p_ebx(%ebp), %ebx
|
|
movl r2p_esi(%ebp), %esi
|
|
movl r2p_edi(%ebp), %edi
|
|
movl r2p_ebp(%ebp), %ebp
|
|
ret
|
|
|
|
/****************************************************************************
|
|
* _real_to_prot
|
|
*
|
|
* Switch from 16-bit real-mode to 32-bit protected mode with flat
|
|
* physical addresses. All segment registers are destroyed, %eip and
|
|
* %esp are changed to flat physical values, all other registers are
|
|
* preserved. Interrupts are disabled.
|
|
*
|
|
* Parameters: none
|
|
****************************************************************************
|
|
*/
|
|
|
|
#define R2P_PRESERVE ( 12 )
|
|
#define R2P_OFFSET_RETADDR ( R2P_PRESERVE )
|
|
#define R2P_OFFSET_ORIG_RETADDR ( R2P_OFFSET_RETADDR + 2 )
|
|
|
|
.globl _real_to_prot
|
|
.code16
|
|
_real_to_prot:
|
|
/* Disable interrupts */
|
|
cli
|
|
/* zero extend the return address */
|
|
pushw $0
|
|
|
|
/* Preserve registers */
|
|
pushl %ebp
|
|
pushl %ebx
|
|
pushl %eax
|
|
|
|
/* Convert 16-bit real-mode near return address to
|
|
* 32-bit pmode physical near return address
|
|
*/
|
|
movw %sp, %bp
|
|
xorl %ebx, %ebx
|
|
push %cs
|
|
popw %bx
|
|
movw %bx, %ds
|
|
shll $4, %ebx
|
|
movzwl %ss:R2P_OFFSET_ORIG_RETADDR(%bp), %eax
|
|
addl %ebx, %eax
|
|
movl %eax, %ss:(R2P_OFFSET_RETADDR)(%bp)
|
|
|
|
/* Store the code segment physical base address in %ebp */
|
|
movl %ebx, %ebp
|
|
|
|
/* Find the offset within the code segment that I am running at */
|
|
xorl %ebx, %ebx
|
|
call 1f
|
|
1: popw %bx
|
|
|
|
/* Set up GDT */
|
|
leal (r2p_gdt-1b)(%bx), %eax /* %ds:ebx = %ds:bx = &(r2p_gdt) */
|
|
addl %ebp, %eax /* %eax = &r2p_gdt (physical) */
|
|
movl %eax, %ds:(r2p_gdt-1b+2)(%bx) /* Set phys. addr. in r2p_gdt */
|
|
|
|
/* Compute the first protected mode physical address */
|
|
leal (2f-1b)(%bx), %eax
|
|
addl %ebp, %eax
|
|
movl %eax, %ds:(r2p_paddr-1b)(%bx)
|
|
|
|
/* Calculate new %esp */
|
|
xorl %eax, %eax
|
|
push %ss
|
|
popw %ax
|
|
shll $4, %eax
|
|
movzwl %sp, %ebp
|
|
addl %eax, %ebp /* %ebp = new %esp */
|
|
|
|
/* Load GDT */
|
|
DATA32 lgdt %ds:(r2p_gdt-1b)(%bx) /* Load GDT */
|
|
|
|
/* Switch to protected mode */
|
|
movl %cr0, %eax
|
|
orb $CR0_PE, %al
|
|
movl %eax, %cr0
|
|
|
|
/* flush prefetch queue, and reload %cs:%eip */
|
|
DATA32 ljmp %ds:(r2p_paddr-1b)(%bx)
|
|
.code32
|
|
2:
|
|
|
|
/* Load segment registers, adjust %esp */
|
|
movw $(r2p_pmds-r2p_gdt), %ax
|
|
movw %ax, %ss
|
|
movl %ebp, %esp
|
|
movw %ax, %ds
|
|
movw %ax, %es
|
|
movw %ax, %fs
|
|
movw %ax, %gs
|
|
|
|
/* Restore registers */
|
|
popl %eax
|
|
popl %ebx
|
|
popl %ebp
|
|
|
|
/* return to caller */
|
|
ret
|
|
|
|
r2p_gdt:
|
|
.word r2p_gdt_end - r2p_gdt - 1 /* limit */
|
|
.long 0 /* addr */
|
|
.word 0
|
|
r2p_pmcs:
|
|
/* 32 bit protected mode code segment, physical addresses */
|
|
.word 0xffff, 0
|
|
.byte 0, 0x9f, 0xcf, 0
|
|
r2p_pmds:
|
|
/* 32 bit protected mode data segment, physical addresses */
|
|
.word 0xffff,0
|
|
.byte 0,0x93,0xcf,0
|
|
r2p_gdt_end:
|
|
|
|
r2p_paddr:
|
|
.long 2b
|
|
.word r2p_pmcs - r2p_gdt, 0
|
|
|
|
|
|
/* This is the end of the trampoline suffix code.
|
|
*/
|
|
r2p_params: /* Structure must match real_to_prot_params_t in realmode.h */
|
|
r2p_ret_addr: .long 0
|
|
r2p_esp: .long 0
|
|
r2p_ebx: .long 0
|
|
r2p_esi: .long 0
|
|
r2p_edi: .long 0
|
|
r2p_ebp: .long 0
|
|
r2p_out_stack: .long 0
|
|
r2p_out_stack_len: .long 0
|
|
.globl _real_to_prot_suffix_end
|
|
_real_to_prot_suffix_end:
|
|
|
|
.globl _real_to_prot_suffix_size
|
|
.equ _real_to_prot_suffix_size, _real_to_prot_suffix_end - _real_to_prot_suffix
|
|
.globl real_to_prot_suffix_size
|
|
real_to_prot_suffix_size:
|
|
.word _real_to_prot_suffix_size
|
|
|
|
rm_callback_interface_end:
|
|
|
|
.globl _rm_callback_interface_size
|
|
.equ _rm_callback_interface_size, rm_callback_interface_end - rm_callback_interface
|
|
.globl rm_callback_interface_size
|
|
rm_callback_interface_size:
|
|
.word _rm_callback_interface_size
|
|
|
|
/****************************************************************************
|
|
* END OF REAL-MODE CALLBACK INTERFACE
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
#ifdef PXE_EXPORT
|
|
/****************************************************************************
|
|
* PXE CALLBACK INTERFACE
|
|
*
|
|
* Prepend this to rm_callback_interface to create a real-mode PXE
|
|
* callback interface.
|
|
****************************************************************************
|
|
*/
|
|
.section ".text16", "ax", @progbits
|
|
.globl pxe_callback_interface
|
|
.code16
|
|
pxe_callback_interface:
|
|
|
|
/* Macro to calculate offset of labels within code segment in
|
|
* installed copy of code.
|
|
*/
|
|
#define INSTALLED(x) ( (x) - pxe_callback_interface )
|
|
|
|
/****************************************************************************
|
|
* PXE entry points (!PXE and PXENV+ APIs)
|
|
****************************************************************************
|
|
*/
|
|
/* in_call mechanism for !PXE API calls */
|
|
.globl _pxe_in_call_far
|
|
_pxe_in_call_far:
|
|
/* Prepend "PXE API call" and "API version 0x201" to stack */
|
|
pushl $0x201
|
|
jmp 1f
|
|
/* in_call mechanism for PXENV+ API calls */
|
|
.globl _pxenv_in_call_far
|
|
_pxenv_in_call_far:
|
|
/* Prepend "PXE API call" and "API version 0x200" to stack */
|
|
pushl $0x200
|
|
1: pushl $EB_OPCODE_PXE
|
|
/* Perform real-mode in_call */
|
|
call pxe_rm_in_call
|
|
/* Return */
|
|
addw $8, %sp
|
|
lret
|
|
|
|
/****************************************************************************
|
|
* PXE installation check (INT 1A) code
|
|
****************************************************************************
|
|
*/
|
|
.globl _pxe_intercept_int1a
|
|
_pxe_intercept_int1a:
|
|
pushfw
|
|
cmpw $0x5650, %ax
|
|
jne 2f
|
|
1: /* INT 1A,5650 - Intercept */
|
|
popfw
|
|
/* Set up return values according to PXE spec: */
|
|
movw $0x564e, %ax /* AX := 564Eh (VN) */
|
|
pushw %cs:INSTALLED(_pxe_pxenv_segment)
|
|
popw %es /* ES:BX := &(PXENV+ structure) */
|
|
movw %cs:INSTALLED(_pxe_pxenv_offset), %bx
|
|
clc /* CF is cleared */
|
|
lret $2 /* 'iret' without reloading flags */
|
|
2: /* INT 1A,other - Do not intercept */
|
|
popfw
|
|
ljmp %cs:*INSTALLED(_pxe_intercepted_int1a)
|
|
|
|
.globl _pxe_intercepted_int1a
|
|
_pxe_intercepted_int1a: .word 0,0
|
|
.globl _pxe_pxenv_location
|
|
_pxe_pxenv_location:
|
|
_pxe_pxenv_offset: .word 0
|
|
_pxe_pxenv_segment: .word 0
|
|
|
|
pxe_rm_in_call:
|
|
pxe_attach_rm:
|
|
/* rm_callback_interface must be appended here */
|
|
|
|
pxe_callback_interface_end:
|
|
|
|
.globl _pxe_callback_interface_size
|
|
.equ _pxe_callback_interface_size, pxe_callback_interface_end - pxe_callback_interface
|
|
.globl pxe_callback_interface_size
|
|
pxe_callback_interface_size:
|
|
.word _pxe_callback_interface_size
|
|
|
|
#else /* PXE_EXPORT */
|
|
|
|
/* Define symbols used by the linker scripts, to prevent link errors */
|
|
.globl _pxe_callback_interface_size
|
|
.equ _pxe_callback_interface_size, 0
|
|
|
|
#endif /* PXE_EXPORT */
|
|
|
|
#else /* CODE16 */
|
|
|
|
/* Define symbols used by the linker scripts, to prevent link errors */
|
|
.globl _rm_callback_interface_size
|
|
.equ _rm_callback_interface_size, 0
|
|
.globl _pxe_callback_interface_size
|
|
.equ _pxe_callback_interface_size, 0
|
|
|
|
#endif /* CODE16 */
|