mirror of https://github.com/ipxe/ipxe.git
258 lines
6.9 KiB
ArmAsm
258 lines
6.9 KiB
ArmAsm
/*
|
|
* libkir: a transition library for -DKEEP_IT_REAL
|
|
*
|
|
* Michael Brown <mbrown@fensystems.co.uk>
|
|
*
|
|
*/
|
|
|
|
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
|
|
|
|
/****************************************************************************
|
|
* This file defines libkir: an interface between external and
|
|
* internal environments when -DKEEP_IT_REAL is used, so that both
|
|
* internal and external environments are in real mode. It deals with
|
|
* switching data segments and the stack. It provides the following
|
|
* functions:
|
|
*
|
|
* ext_to_kir & switch between external and internal (kir)
|
|
* kir_to_ext environments, preserving all non-segment
|
|
* registers
|
|
*
|
|
* kir_call issue a call to an internal routine from external
|
|
* code
|
|
*
|
|
* libkir is written to avoid assuming that segments are anything
|
|
* other than opaque data types, and also avoids assuming that the
|
|
* stack pointer is 16-bit. This should enable it to run just as well
|
|
* in 16:16 or 16:32 protected mode as in real mode.
|
|
****************************************************************************
|
|
*/
|
|
|
|
/* Breakpoint for when debugging under bochs */
|
|
#define BOCHSBP xchgw %bx, %bx
|
|
|
|
.section ".note.GNU-stack", "", @progbits
|
|
.text
|
|
.arch i386
|
|
.section ".text16", "awx", @progbits
|
|
.code16
|
|
|
|
/****************************************************************************
|
|
* init_libkir (real-mode or 16:xx protected-mode far call)
|
|
*
|
|
* Initialise libkir ready for transitions to the kir environment
|
|
*
|
|
* Parameters:
|
|
* %cs : .text16 segment
|
|
* %ds : .data16 segment
|
|
****************************************************************************
|
|
*/
|
|
.globl init_libkir
|
|
init_libkir:
|
|
/* Record segment registers */
|
|
pushw %ds
|
|
popw %cs:kir_ds
|
|
lret
|
|
|
|
/****************************************************************************
|
|
* ext_to_kir (real-mode or 16:xx protected-mode near call)
|
|
*
|
|
* Switch from external stack and segment registers to internal stack
|
|
* and segment registers. %ss:sp is restored from the saved kir_ds
|
|
* and kir_sp. %ds, %es, %fs and %gs are all restored from the saved
|
|
* kir_ds. All other registers are preserved.
|
|
*
|
|
* %cs:0000 must point to the start of the runtime image code segment
|
|
* on entry.
|
|
*
|
|
* Parameters: none
|
|
****************************************************************************
|
|
*/
|
|
|
|
.globl ext_to_kir
|
|
ext_to_kir:
|
|
/* Record external segment registers */
|
|
movw %ds, %cs:ext_ds
|
|
pushw %cs
|
|
popw %ds /* Set %ds = %cs for easier access to variables */
|
|
movw %es, %ds:ext_es
|
|
movw %fs, %ds:ext_fs
|
|
movw %gs, %ds:ext_fs
|
|
|
|
/* Preserve registers */
|
|
movw %ax, %ds:save_ax
|
|
|
|
/* Extract near return address from stack */
|
|
popw %ds:save_retaddr
|
|
|
|
/* Record external %ss:esp */
|
|
movw %ss, %ds:ext_ss
|
|
movl %esp, %ds:ext_esp
|
|
|
|
/* Load internal segment registers and stack pointer */
|
|
movw %ds:kir_ds, %ax
|
|
movw %ax, %ss
|
|
movzwl %ds:kir_sp, %esp
|
|
movw %ax, %ds
|
|
movw %ax, %es
|
|
movw %ax, %fs
|
|
movw %ax, %gs
|
|
1:
|
|
|
|
/* Place return address on new stack */
|
|
pushw %cs:save_retaddr
|
|
|
|
/* Restore registers and return */
|
|
movw %cs:save_ax, %ax
|
|
ret
|
|
|
|
/****************************************************************************
|
|
* kir_to_ext (real-mode or 16:xx protected-mode near call)
|
|
*
|
|
* Switch from internal stack and segment registers to external stack
|
|
* and segment registers. %ss:%esp is restored from the saved ext_ss
|
|
* and ext_esp. Other segment registers are restored from the
|
|
* corresponding locations. All other registers are preserved.
|
|
*
|
|
* Note that it is actually %ss that is recorded as kir_ds, on the
|
|
* assumption that %ss == %ds when kir_to_ext is called.
|
|
*
|
|
* Parameters: none
|
|
****************************************************************************
|
|
*/
|
|
|
|
.globl kir_to_ext
|
|
kir_to_ext:
|
|
/* Record near return address */
|
|
pushw %cs
|
|
popw %ds /* Set %ds = %cs for easier access to variables */
|
|
popw %ds:save_retaddr
|
|
|
|
/* Record internal segment registers and %sp */
|
|
movw %ss, %ds:kir_ds
|
|
movw %sp, %ds:kir_sp
|
|
|
|
/* Load external segment registers and stack pointer */
|
|
movw %ds:ext_ss, %ss
|
|
movl %ds:ext_esp, %esp
|
|
movw %ds:ext_gs, %gs
|
|
movw %ds:ext_fs, %fs
|
|
movw %ds:ext_es, %es
|
|
movw %ds:ext_ds, %ds
|
|
|
|
/* Return */
|
|
pushw %cs:save_retaddr
|
|
ret
|
|
|
|
/****************************************************************************
|
|
* kir_call (real-mode or 16:xx protected-mode far call)
|
|
*
|
|
* Call a specific C function in the internal code. The prototype of
|
|
* the C function must be
|
|
* void function ( struct i386_all_resg *ix86 );
|
|
* ix86 will point to a struct containing the real-mode registers
|
|
* at entry to kir_call.
|
|
*
|
|
* All registers will be preserved across kir_call(), unless the C
|
|
* function explicitly overwrites values in ix86. Interrupt status
|
|
* will also be preserved.
|
|
*
|
|
* Parameters:
|
|
* function : (32-bit) virtual address of C function to call
|
|
*
|
|
* Example usage:
|
|
* pushl $pxe_api_call
|
|
* lcall $UNDI_CS, $kir_call
|
|
* addw $4, %sp
|
|
* to call in to the C function
|
|
* void pxe_api_call ( struct i386_all_regs *ix86 );
|
|
****************************************************************************
|
|
*/
|
|
|
|
.globl kir_call
|
|
kir_call:
|
|
/* Preserve flags. Must do this before any operation that may
|
|
* affect flags.
|
|
*/
|
|
pushfl
|
|
popl %cs:save_flags
|
|
|
|
/* Disable interrupts. We do funny things with the stack, and
|
|
* we're not re-entrant.
|
|
*/
|
|
cli
|
|
|
|
/* Extract address of internal routine from stack. We must do
|
|
* this without using (%bp), because we may be called with
|
|
* either a 16-bit or a 32-bit stack segment.
|
|
*/
|
|
popl %cs:save_retaddr /* Scratch location */
|
|
popl %cs:save_function
|
|
subl $8, %esp /* Restore %esp */
|
|
|
|
/* Switch to internal stack. Note that the external stack is
|
|
* inaccessible once we're running internally (since we have
|
|
* no concept of 48-bit far pointers)
|
|
*/
|
|
call ext_to_kir
|
|
|
|
/* Store external registers on internal stack */
|
|
pushl %cs:save_flags
|
|
pushal
|
|
pushl %cs:ext_fs_and_gs
|
|
pushl %cs:ext_ds_and_es
|
|
pushl %cs:ext_cs_and_ss
|
|
|
|
/* Push &ix86 on stack and call function */
|
|
sti
|
|
pushl %esp
|
|
data32 call *%cs:save_function
|
|
popl %eax /* discard */
|
|
|
|
/* Restore external registers from internal stack */
|
|
popl %cs:ext_cs_and_ss
|
|
popl %cs:ext_ds_and_es
|
|
popl %cs:ext_fs_and_gs
|
|
popal
|
|
popl %cs:save_flags
|
|
|
|
/* Switch to external stack */
|
|
call kir_to_ext
|
|
|
|
/* Restore flags */
|
|
pushl %cs:save_flags
|
|
popfl
|
|
|
|
/* Return */
|
|
lret
|
|
|
|
/****************************************************************************
|
|
* Stored internal and external stack and segment registers
|
|
****************************************************************************
|
|
*/
|
|
|
|
ext_cs_and_ss:
|
|
ext_cs: .word 0
|
|
ext_ss: .word 0
|
|
ext_ds_and_es:
|
|
ext_ds: .word 0
|
|
ext_es: .word 0
|
|
ext_fs_and_gs:
|
|
ext_fs: .word 0
|
|
ext_gs: .word 0
|
|
ext_esp: .long 0
|
|
|
|
.globl kir_ds
|
|
kir_ds: .word 0
|
|
.globl kir_sp
|
|
kir_sp: .word _estack
|
|
|
|
/****************************************************************************
|
|
* Temporary variables
|
|
****************************************************************************
|
|
*/
|
|
save_ax: .word 0
|
|
save_retaddr: .long 0
|
|
save_flags: .long 0
|
|
save_function: .long 0
|