blob: 2c4dc948b477462c553735a2a7ad13250160ee5f [file] [log] [blame]
/*
* 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
.code16
.arch i386
.section ".text16", "awx", @progbits
/****************************************************************************
* 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