summaryrefslogtreecommitdiffstats
path: root/src/arch/i386/transitions/librm.S
diff options
context:
space:
mode:
authorMichael Brown2006-05-02 17:41:21 +0200
committerMichael Brown2006-05-02 17:41:21 +0200
commit9fcded3d23d641847f4e6925c135d04d304d9406 (patch)
tree70595ff60ef2fdebfc2723d558cde7fa04582bcd /src/arch/i386/transitions/librm.S
parentMove stack out to separate object, so that having a stack doesn't drag in (diff)
downloadipxe-9fcded3d23d641847f4e6925c135d04d304d9406.tar.gz
ipxe-9fcded3d23d641847f4e6925c135d04d304d9406.tar.xz
ipxe-9fcded3d23d641847f4e6925c135d04d304d9406.zip
Towards a(nother) new real-mode infrastructure, in which we take
advantage of the fact that we have to have a permanently-resident block in base memory.
Diffstat (limited to 'src/arch/i386/transitions/librm.S')
-rw-r--r--src/arch/i386/transitions/librm.S842
1 files changed, 283 insertions, 559 deletions
diff --git a/src/arch/i386/transitions/librm.S b/src/arch/i386/transitions/librm.S
index 9d55cff7..2fbffb9e 100644
--- a/src/arch/i386/transitions/librm.S
+++ b/src/arch/i386/transitions/librm.S
@@ -8,98 +8,6 @@
/* Drag in local definitions */
#include "librm.h"
-/* Drag in FREE_BASEMEM_HEADER_SIZE */
-#include "basemem.h"
-
-/****************************************************************************
- * This file defines librm: a block of code that is designed to reside
- * permanently in base memory and provide the interface between
- * real-mode code running in base memory and protected-mode code
- * running in high memory. It provides the following functions:
- *
- * real_to_prot & switch between real and protected mode
- * prot_to_real while running in base memory, preserving
- * all non-segment registers
- *
- * real_call issue a call to a real-mode routine from
- * protected-mode code running in high memory
- *
- * prot_call issue a call to a protected-mode routine from
- * real-mode code running in base memory
- *
- * librm requires the following functions to be present in the
- * protected-mode code:
- *
- * _phys_to_virt Switch from physical to virtual addressing. This
- * routine must be position-independent and must
- * *not* assume that it is genuinely running with
- * flat physical addresses
- *
- * _virt_to_phys Switch from virtual to physical addresses.
- *
- * gateA20_set Enable the A20 line to permit access to the odd
- * megabytes of RAM. (This function will be called
- * with virtual addresses set up).
- *
- * librm needs to be linked against the protected-mode binary so that
- * it can import the symbols for these functions.
- *
- * librm requires that the protected-mode code set up the following
- * segments:
- *
- * PHYSICAL_CS 32-bit pmode code and data segments with flat
- * PHYSICAL_DS physical addresses.
- *
- * VIRTUAL_CS 32-bit pmode code segment with virtual
- * addressing, such that a protected-mode routine
- * can always be found at $VIRTUAL_CS:routine.
- *
- * These segments must be set as #define constants when compiling
- * librm. Edit librm.h to change the values.
- *
- * librm does not know the location of the code executing in high
- * memory. It relies on the code running in high memory setting up a
- * GDT such that the high-memory code is accessible at virtual
- * addresses fixed at compile-time.
- *
- * librm symbols are exported as absolute values and represent offsets
- * into librm. This is the most useful form of the symbols, since
- * librm is basically a binary blob that you place somewhere in base
- * memory.
- *
- * librm.h provides convenient ways to use these symbols: you simply
- * set the pointer ( char * ) installed_librm to point to wherever
- * librm is installed, and can then use e.g. inst_rm_stack just like
- * any other variable and have it automatically refer to the value of
- * rm_stack in the installed librm. Macro trickery makes this
- * completely transparent, and the resulting assembler code is
- * amazingly efficient.
- *
- * Note that librm must be called in genuine real mode, not 16:16 or
- * 16:32 protected mode. It makes the assumption that
- * physical_address = 16*segment+offset, and also that it can use
- * OFFSET(%bp) to access stack variables. The former assumption will
- * break in either protected mode, the latter may break in 16:32
- * protected mode.
- ****************************************************************************
- */
-
-/*
- * Default values for pmode segments if not defined
- */
-#ifndef PHYSICAL_CS
-#warning "Assuming PHYSICAL_CS = 0x08"
-#define PHYSICAL_CS 0x08
-#endif
-#ifndef PHYSICAL_DS
-#warning "Assuming PHYSICAL_DS = 0x10"
-#define PHYSICAL_DS 0x10
-#endif
-#ifndef VIRTUAL_CS
-#warning "Assuming VIRTUAL_CS = 0x18"
-#define VIRTUAL_CS 0x18
-#endif
-
/* For switches to/from protected mode */
#define CR0_PE 1
@@ -109,101 +17,26 @@
#define SIZEOF_REAL_MODE_REGS ( SIZEOF_I386_SEG_REGS + SIZEOF_I386_REGS )
#define SIZEOF_I386_FLAGS 4
#define SIZEOF_I386_ALL_REGS ( SIZEOF_REAL_MODE_REGS + SIZEOF_I386_FLAGS )
-#define SIZEOF_SEGOFF_T 4
-#define SIZEOF_REAL_CALL_PARAMS ( SIZEOF_REAL_MODE_REGS + 2 * SIZEOF_SEGOFF_T )
- .text
.arch i386
- .section ".librm", "awx", @progbits
- .align 16
-
- .globl librm
-librm:
-
-_librm_start:
-
-#undef OFFSET
-#define OFFSET(sym) ( sym - _librm_start )
-
-#undef EXPORT
-#define EXPORT(sym) \
- .globl sym ; \
- .globl _ ## sym ; \
- .equ _ ## sym, OFFSET(sym) ; \
- sym
+ .section ".text16", "awx", @progbits
/****************************************************************************
- * Note that the first sizeof(struct free_base_memory_header) bytes of
- * librm will get vapourised by free_base_memory(). Since we need
- * librm to continue working even when this happens, we put some
- * padding here.
+ * Global descriptor table
*
- * We must also ensure that the total size of librm is <1kB, otherwise
- * free_base_memory() will stomp somewhere in the middle of us as
- * well...
- ****************************************************************************
- */
- .fill FREE_BASEMEM_HEADER_SIZE, 1, 0
-
-/****************************************************************************
- * Record of the current physical location of the installed copy.
- * Used by prot_call in order to return via the current installed copy
- * even if Etherboot has been relocated during the protected-mode
- * call.
- ****************************************************************************
- */
-EXPORT(librm_base):
-librm_base: .long 0
-
-/****************************************************************************
- * GDT for initial transition to protected mode
+ * Call init_gdt to set up the GDT before attempting to use any
+ * protected-mode code.
*
- * PHYSICAL_CS and PHYSICAL_DS are defined in an external header file.
- * We use only those selectors, and construct our GDT to match the
- * selector values we're asked to use. Use PHYSICAL_CS=0x08 and
- * PHYSICAL_DS=0x10 to minimise the space occupied by this GDT.
- *
- * Note: pm_gdt is also used to store the location of the
- * protected-mode GDT as recorded on entry to prot_to_real.
- ****************************************************************************
- */
- .align 16
-pm_gdt:
-pm_gdt_limit: .word pm_gdt_length - 1
-pm_gdt_addr: .long 0
- .word 0 /* padding */
-
- .org pm_gdt + PHYSICAL_CS
-pm_gdt_pm_cs:
- /* 32 bit protected mode code segment, physical addresses */
- .word 0xffff, 0
- .byte 0, 0x9f, 0xcf, 0
-
- .org pm_gdt + PHYSICAL_DS
-pm_gdt_pm_ds:
- /* 32 bit protected mode data segment, physical addresses */
- .word 0xffff,0
- .byte 0,0x93,0xcf,0
-
-pm_gdt_end:
- .equ pm_gdt_length, pm_gdt_end - pm_gdt
-
-/****************************************************************************
- * GDT for transition to real mode
- *
- * This is used primarily to set 64kB segment limits. Define
- * FLATTEN_REAL_MODE if you want to use so-called "flat real mode"
- * with 4GB limits instead. The base address of each of the segments
- * will be adjusted at run-time.
+ * Define FLATTEN_REAL_MODE if you want to use so-called "flat real
+ * mode" with 4GB limits instead.
*
* NOTE: This must be located before prot_to_real, otherwise gas
* throws a "can't handle non absolute segment in `ljmp'" error due to
- * not knowing the value of RM_CS when the ljmp is encountered.
+ * not knowing the value of REAL_CS when the ljmp is encountered.
*
- * Note also that putting ".word rm_gdt_end - rm_gdt - 1" directly
- * into rm_gdt_limit, rather than going via rm_gdt_length, will also
- * produce the "non absolute segment" error. This is most probably a
- * bug in gas.
+ * Note also that putting ".word gdt_end - gdt - 1" directly into
+ * gdt_limit, rather than going via gdt_length, will also produce the
+ * "non absolute segment" error. This is most probably a bug in gas.
****************************************************************************
*/
@@ -212,252 +45,242 @@ pm_gdt_end:
#else
#define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x00
#endif
+ .section ".text16"
.align 16
-rm_gdt:
-rm_gdt_limit: .word rm_gdt_length - 1
-rm_gdt_base: .long 0
+gdt:
+gdt_limit: .word gdt_length - 1
+gdt_base: .long 0
.word 0 /* padding */
+
+ .org gdt + VIRTUAL_CS, 0
+virtual_cs: /* 32 bit protected mode code segment, virtual addresses */
+ .word 0xffff, 0
+ .byte 0, 0x9f, 0xcf, 0
+
+ .org gdt + VIRTUAL_DS, 0
+virtual_ds: /* 32 bit protected mode data segment, virtual addresses */
+ .word 0xffff, 0
+ .byte 0, 0x93, 0xcf, 0
-rm_gdt_rm_cs: /* 16 bit real mode code segment */
- .equ RM_CS, rm_gdt_rm_cs - rm_gdt
- .word 0xffff,(0&0xffff)
- .byte (0>>16),0x9b,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24)
-
-rm_gdt_rm_ds: /* 16 bit real mode data segment */
- .equ RM_DS, rm_gdt_rm_ds - rm_gdt
- .word 0xffff,(0&0xffff)
- .byte (0>>16),0x93,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24)
+ .org gdt + PHYSICAL_CS, 0
+physical_cs: /* 32 bit protected mode code segment, physical addresses */
+ .word 0xffff, 0
+ .byte 0, 0x9f, 0xcf, 0
+
+ .org gdt + PHYSICAL_DS, 0
+physical_ds: /* 32 bit protected mode data segment, physical addresses */
+ .word 0xffff, 0
+ .byte 0, 0x93, 0xcf, 0
+
+ .org gdt + REAL_CS, 0
+real_cs: /* 16 bit real mode code segment */
+ .word 0xffff, 0
+ .byte 0, 0x9b, RM_LIMIT_16_19__AVL__SIZE__GRANULARITY, 0
+
+ .org gdt + REAL_DS
+real_ds: /* 16 bit real mode data segment */
+ .word 0xffff, 0
+ .byte 0, 0x93, RM_LIMIT_16_19__AVL__SIZE__GRANULARITY, 0
-rm_gdt_end:
- .equ rm_gdt_length, rm_gdt_end - rm_gdt
+gdt_end:
+ .equ gdt_length, gdt_end - gdt
/****************************************************************************
- * real_to_prot (real-mode far call)
- *
- * Switch from 16-bit real-mode to 32-bit protected mode with flat
- * physical addresses. %esp is restored from the saved pm_esp. All
- * segment registers are set to flat physical-mode values. All other
- * registers are preserved. Interrupts are disabled.
+ * init_gdt (real-mode near call, 16-bit real-mode return address)
*
- * Note that this routine can be called *without* having first set up
- * a stored pm_esp or stored GDT. If you do this, real_to_prot will
- * return with a temporary stack that is only *FOUR BYTES* in size.
- * This is just enough to enable you to do a "call 1f; popl %ebp"
- * sequence in order to find out your physical address and then load a
- * proper 32-bit protected-mode stack pointer. Do *NOT* use more than
- * four bytes since this will overwrite code in librm!
+ * Initialise the GDT ready for transitions to protected mode.
*
- * Parameters: none
+ * Parameters:
+ * %edi : Physical base of protected-mode code
****************************************************************************
*/
+ .section ".text16"
+ .code16
+ .globl init_gdt
+init_gdt:
+ /* Preserve registers */
+ pushl %eax
+ pushw %bx
- .code16
-EXPORT(real_to_prot):
- /* Disable interrupts */
- cli
+ /* Record virt_offset */
+ movl %edi, %cs:virt_offset_rm_copy
- /* Set %ds = %cs, for easier access to variables */
- pushw %cs
- popw %ds
-
- /* Preserve registers */
- movl %eax, %ds:OFFSET(save_eax)
- movl %ebx, %ds:OFFSET(save_ebx)
+ /* Set virtual_cs and virtual_ds base */
+ movl %edi, %eax
+ movw $virtual_cs, %bx
+ call set_seg_base
- /* Extract real-mode far return address from stack */
- popl %ds:OFFSET(save_retaddr)
+ /* Set real_cs and real_ds base, and GDT base */
+ movw $real_cs, %bx
+ xorl %eax, %eax
+ movw %cs, %ax
+ shll $4, %eax
+ call set_seg_base
+ addl $gdt, %eax
+ movl %eax, %cs:gdt_base
- /* Record real-mode stack pointer */
- movw %sp, %ds:OFFSET(rm_sp)
- pushw %ss
- popw %ds:OFFSET(rm_ss)
-
- /* Physical base address of librm to %ebx */
- xorl %ebx, %ebx
- movw %cs, %bx
- shll $4, %ebx
-
- /* Record physical base address of librm */
- movl %ebx, %ds:OFFSET(librm_base)
-
- /* Check base address of stored protected-mode GDT. If it's
- * zero, set it up to use our internal GDT (with physical
- * segments only).
- */
- movl %ds:OFFSET(pm_gdt_addr), %eax
- testl %eax, %eax
- jnz 1f
- /* Use internal GDT */
- movl %ebx, %eax
- addl $OFFSET(pm_gdt), %eax
- movl %eax, %ds:OFFSET(pm_gdt_addr)
-1:
-
- /* Set up protected-mode continuation address on real-mode stack */
- pushl $PHYSICAL_CS
- movl %ebx, %eax
- addl $OFFSET(1f), %eax
+ /* Restore registers */
+ popw %bx
+ popl %eax
+ ret
+
+ .section ".text16"
+ .code16
+set_seg_base:
pushl %eax
+ movw %ax, %cs:(0+2)(%bx)
+ movw %ax, %cs:(8+2)(%bx)
+ shrl $16, %eax
+ movb %al, %cs:(0+4)(%bx)
+ movb %al, %cs:(8+4)(%bx)
+ movb %ah, %cs:(0+7)(%bx)
+ movb %ah, %cs:(8+7)(%bx)
+ popl %eax
+ ret
- /* Restore protected-mode GDT */
- data32 lgdt %ds:OFFSET(pm_gdt)
+/****************************************************************************
+ * real_to_prot (real-mode near call, 32-bit virtual return address)
+ *
+ * Switch from 16-bit real-mode to 32-bit protected mode with virtual
+ * addresses. The real-mode %ss:sp is stored in rm_ss and rm_sp, and
+ * the protected-mode %esp is restored from the saved pm_esp.
+ * Interrupts are disabled. All other registers may be destroyed.
+ *
+ * The return address for this function should be a 32-bit virtual
+ * address.
+ *
+ * Parameters:
+ * %ecx : number of bytes to move from RM stack to PM stack
+ *
+ ****************************************************************************
+ */
+ .section ".text16"
+ .code16
+real_to_prot:
+ /* Protected-mode return address => %ebx */
+ popl %ebx
+
+ /* Real-mode %cs => %dx, %ss => %bp */
+ movw %cs, %dx
+ movw %ss, %bp
+
+ /* virt_offset => %edi */
+ movl %cs:virt_offset_rm_copy, %edi
/* Switch to protected mode */
+ cli
+ data32 lgdt %cs:gdt
movl %cr0, %eax
orb $CR0_PE, %al
movl %eax, %cr0
-
- /* Flush prefetch queue and reload %cs:eip */
- data32 lret
-1: .code32
-
- /* Set up protected-mode stack and data segments */
- movw $PHYSICAL_DS, %ax
+ data32 ljmp $VIRTUAL_CS, $1f
+ .section ".text"
+ .code32
+1:
+ /* Set up protected-mode data segments */
+ movw $VIRTUAL_DS, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
+
+ /* Record virt_offset */
+ movl %edi, virt_offset
+
+ /* Move data from RM stack to PM stack and set up PM stack */
+ movzwl %sp, %esi
+ movl pm_esp, %esp
+ subl %ecx, %esp
+ movl %esp, %edi
+ rep ss movsb
movw %ax, %ss
- /* Switch to saved protected-mode stack. Note that there may
- * not actually *be* a saved protected-mode stack.
- */
- movl OFFSET(pm_esp)(%ebx), %esp
- testl %esp, %esp
- jnz 1f
- /* No stack - use save_retaddr as a 4-byte temporary stack */
- leal OFFSET(save_retaddr+4)(%ebx), %esp
-1:
-
- /* Convert real-mode far return address to physical address
- * and place on stack
- */
- pushl OFFSET(save_retaddr)(%ebx)
- xorl %eax, %eax
- xchgw 2(%esp), %ax
- shll $4, %eax
- addl %eax, 0(%esp)
+ /* Record real-mode %cs and %ss:sp */
+ movw %dx, rm_cs
+ movw %bp, rm_ss
+ movw %si, rm_sp
- /* Restore registers and return */
- movl OFFSET(save_eax)(%ebx), %eax
- movl OFFSET(save_ebx)(%ebx), %ebx
- ret
+ /* Return to virtual address */
+ jmp *%ebx
/****************************************************************************
- * prot_to_real (protected-mode near call, physical addresses)
+ * prot_to_real (protected-mode near call, 32-bit real-mode return address)
*
- * Switch from 32-bit protected mode with flat physical addresses to
- * 16-bit real mode. %ss:sp is restored from the saved rm_ss and
- * rm_sp. %cs is set such that %cs:0000 is the start of librm. All
- * other segment registers are set to %ss. All other registers are
- * preserved. Interrupts are *not* enabled, since we want to be able
- * to use this routine inside an ISR.
+ * Switch from 32-bit protected mode with virtual addresses to 16-bit
+ * real mode. The protected-mode %esp is stored in pm_esp and the
+ * real-mode %ss:sp is restored from the saved rm_ss and rm_sp. All
+ * real-mode data segment registers are set equal to %ss. Interrupts
+ * are *not* enabled, since we want to be able to use prot_to_real in
+ * an ISR. All other registers may be destroyed.
*
- * Note that since %cs:0000 points to the start of librm on exit, it
- * follows that the code calling prot_to_real must be located within
- * 64kB of the start of librm.
+ * The return address for this function should be a 32-bit (sic)
+ * real-mode offset within .code16.
+ *
+ * Parameters:
+ * %ecx : number of bytes to move from PM stack to RM stack
*
- * Parameters: none
****************************************************************************
*/
-
+ .section ".text"
.code32
-EXPORT(prot_to_real):
- /* Calculate physical base address of librm in %ebx, preserve
- * original %eax and %ebx in save_eax and save_ebx
- */
- pushl %ebx
- call 1f
-1: popl %ebx
- subl $OFFSET(1b), %ebx
- popl OFFSET(save_ebx)(%ebx)
- movl %eax, OFFSET(save_eax)(%ebx)
-
- /* Record physical base address of librm */
- movl %ebx, OFFSET(librm_base)(%ebx)
-
- /* Extract return address from the stack, convert to offset
- * within librm and save in save_retaddr
- */
- popl %eax
- subl %ebx, %eax
- movl %eax, OFFSET(save_retaddr)(%ebx)
-
- /* Record protected-mode stack pointer */
- movl %esp, OFFSET(pm_esp)(%ebx)
-
- /* Record protected-mode GDT */
- sgdt OFFSET(pm_gdt)(%ebx)
-
- /* Set up real-mode GDT */
- leal OFFSET(rm_gdt)(%ebx), %eax
- movl %eax, OFFSET(rm_gdt_base)(%ebx)
- movl %ebx, %eax
- rorl $16, %eax
- movw %bx, OFFSET(rm_gdt_rm_cs+2)(%ebx)
- movb %al, OFFSET(rm_gdt_rm_cs+4)(%ebx)
- movw %bx, OFFSET(rm_gdt_rm_ds+2)(%ebx)
- movb %al, OFFSET(rm_gdt_rm_ds+4)(%ebx)
+prot_to_real:
+ /* Real-mode return address => %ebx */
+ popl %ebx
- /* Switch to real-mode GDT and reload segment registers to get
- * 64kB limits. Stack is invalidated by this process.
- */
- lgdt OFFSET(rm_gdt)(%ebx)
- ljmp $RM_CS, $1f
-1: .code16
- movw $RM_DS, %ax
+ /* Real-mode %ss:sp => %ebp:edx */
+ movzwl rm_ss, %ebp
+ movzwl rm_sp, %edx
+ subl %ecx, %edx
+
+ /* Copy data from PM stack to RM stack */
+ movl %ebp, %eax
+ shll $4, %eax
+ leal (%eax,%edx), %edi
+ subl virt_offset, %edi
+ movl %esp, %esi
+ rep movsb
+
+ /* Record protected-mode %esp */
+ movl %esi, pm_esp
+
+ /* Real-mode %cs => %di */
+ movw rm_cs, %di
+
+ /* Load real-mode segment limits */
+ movw $REAL_DS, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movw %ax, %ss
-
- /* Calculate real-mode code segment in %ax and store in ljmp
- * instruction
- */
- movl %ebx, %eax
- shrl $4, %eax
- movw %ax, OFFSET(p2r_ljmp) + 3
-
+ ljmp $REAL_CS, $1f
+ .section ".text16"
+ .code16
+1:
+ /* Set up real-mode ljmp instruction */
+ movw %di, %ds:(p2r_ljmp + 3)
+
/* Switch to real mode */
- movl %cr0, %ebx
- andb $0!CR0_PE, %bl
- movl %ebx, %cr0
-
- /* Intersegment jump to flush prefetch queue and reload
- * %cs:eip. The segment gets filled in by the above code. We
- * can't just use lret to achieve this, because we have no
- * stack at the moment.
- */
-p2r_ljmp:
- ljmp $0, $OFFSET(1f)
-1:
-
- /* Set %ds to point to code segment for easier data access */
- movw %ax, %ds
-
- /* Restore registers */
- movl OFFSET(save_eax), %eax
- movl OFFSET(save_ebx), %ebx
-
- /* Set up real-mode data segments and stack */
- movw OFFSET(rm_ss), %ss
- movw OFFSET(rm_sp), %sp
- pushw %ss
- pushw %ss
- pushw %ss
- pushw %ss
- popw %ds
- popw %es
- popw %fs
- popw %gs
-
- /* Set up return address on stack and return */
- pushw %cs:OFFSET(save_retaddr)
- ret
+ movl %cr0, %eax
+ andb $0!CR0_PE, %al
+ movl %eax, %cr0
+p2r_ljmp:
+ ljmp $0, $1f /* Segment is filled in by above code */
+1:
+ /* Set up real-mode stack and data segments, and stack pointer */
+ movw %bp, %ds
+ movw %bp, %es
+ movw %bp, %fs
+ movw %bp, %gs
+ movw %bp, %ss
+ movw %dx, %sp
+
+ /* Return to real-mode address */
+ jmp *%bx
+
/****************************************************************************
- * prot_call (real-mode far call)
+ * prot_call (real-mode near call, 32-bit real-mode return address)
*
* Call a specific C function in the protected-mode code. The
* prototype of the C function must be
@@ -469,30 +292,12 @@ p2r_ljmp:
* function explicitly overwrites values in ix86. Interrupt status
* will also be preserved. Gate A20 will be enabled.
*
- * The protected-mode code may install librm to a new location. If it
- * does so, it must update librm_base in *this* copy of librm to point
- * to the new physical location. prot_call will then return via the
- * newly installed copy.
- *
- * Note that when Etherboot performs its initial relocation, "*this*"
- * copy in the above paragraph will refer to the "master" copy, since
- * that is the initial installed copy. Etherboot will return to
- * prot_call using a virtual address, so will return to the master
- * copy in high memory (rather than the original copy in base memory).
- * The master copy in high memory will have the physical address of
- * the newly installed copy in librm_base, since install_librm()
- * writes it there. Thus, Etherboot's initialise() function will
- * return to the master copy of prot_call(), which will then jump to
- * the installed copy.
- *
- * It works, trust me.
- *
* Parameters:
* function : virtual address of protected-mode function to call
*
* Example usage:
* pushl $pxe_api_call
- * lcall $LIBRM_SEGMENT, $prot_call
+ * call prot_call
* addw $4, %sp
* to call in to the C function
* void pxe_api_call ( struct i386_all_regs *ix86 );
@@ -502,10 +307,13 @@ p2r_ljmp:
#define PC_OFFSET_IX86 ( 0 )
#define PC_OFFSET_RETADDR ( PC_OFFSET_IX86 + SIZEOF_I386_ALL_REGS )
#define PC_OFFSET_FUNCTION ( PC_OFFSET_RETADDR + 4 )
-
+#define PC_OFFSET_END ( PC_OFFSET_FUNCTION + 4 )
+
+ .section ".text16"
.code16
-EXPORT(prot_call):
- /* Preserve registers and flags on RM stack */
+ .globl prot_call
+prot_call:
+ /* Preserve registers and flags on external RM stack */
pushfl
pushal
pushw %gs
@@ -513,101 +321,46 @@ EXPORT(prot_call):
pushw %es
pushw %ds
pushw %ss
- pushw %cs
-
- /* Record RM stack pointer */
- xorl %ebp, %ebp
- movw %sp, %bp
-
- /* Physical address of RM stack pointer to %esi */
- xorl %esi, %esi
- pushw %ss
- popw %si
- shll $4, %esi
- addl %ebp, %esi
-
- /* Address of pmode function to %ebx */
- movl %ss:(PC_OFFSET_FUNCTION)(%bp), %ebx
-
- /* Switch to protected mode */
pushw %cs
- call real_to_prot
- .code32
- /* Copy ix86 from RM stack to PM stack */
- movl $SIZEOF_I386_ALL_REGS, %ecx
- subl %ecx, %esp
- movl %esp, %edi
- pushl %esi
+ /* For sanity's sake, clear the direction flag as soon as possible */
cld
- rep movsb
- popl %edi /* %edi = phys addr of RM copy of ix86 */
-
- /* Switch to virtual addresses. */
- call 1f
- jmp 2f
-1: ljmp $VIRTUAL_CS, $_phys_to_virt
-2:
- /* Enable A20 line */
- pushal
- lcall $VIRTUAL_CS, $gateA20_set
- popl %eax /* discard */
- popal
+ /* Switch to protected mode and move register dump to PM stack */
+ movl $PC_OFFSET_END, %ecx
+ pushl $1f
+ jmp real_to_prot
+ .section ".text"
+ .code32
+1:
+ /* Set up environment expected by C code */
+ call gateA20_set
- /* Push &ix86 on the stack, and call function */
+ /* Call function */
pushl %esp
- call *%ebx
+ call *(PC_OFFSET_FUNCTION+4)(%esp)
popl %eax /* discard */
- /* Switch to physical addresses, discard PM register store */
- lcall $VIRTUAL_CS, $_virt_to_phys
- popl %eax /* discard */
-
- /* Copy ix86 from PM stack to RM stack, and remove ix86
- * from PM stack. (%edi still contains physical address of
- * ix86 on RM stack from earlier, since C code preserves
- * %edi).
- */
- movl %esp, %esi
- movl $SIZEOF_I386_ALL_REGS, %ecx
- cld
- rep movsb
- movl %esi, %esp /* remove ix86 from PM stack */
-
- /* Obtain physical base address of installed copy of librm in
- * %ebx. (It's possible that this *isn't* the physical base
- * address of the copy we're currently executing in, because
- * the protected-mode call could have moved librm. If it does
- * so, it must update librm_base in our copy to reflect the
- * new location.
- */
- call 1f
-1: popl %ebp
- movl (librm_base-1b)(%ebp), %ebx
-
- /* Jump to running in installed copy of librm */
- addl $OFFSET(1f), %ebx
- jmp *%ebx
-1:
-
- /* Switch to real mode */
- call prot_to_real
+ /* Switch to real mode and move register dump back to RM stack */
+ movl $PC_OFFSET_END, %ecx
+ pushl $1f
+ jmp prot_to_real
+ .section ".text16"
.code16
-
- /* Restore registers and flags, and return */
- popw %ax /* skip %cs */
- popw %ax /* skip %ss */
+1:
+ /* Restore registers and flags and return */
+ popw %ax /* skip %cs - it is already set */
+ popw %ax /* skip %ss - it is already set */
popw %ds
popw %es
popw %fs
popw %gs
popal
popfl
- lret
+ ret
/****************************************************************************
- * real_call (protected-mode near call, virtual addresses)
+ * real_call (protected-mode near call, 32-bit virtual return address)
*
* Call a real-mode function from protected-mode code.
*
@@ -625,80 +378,58 @@ EXPORT(prot_call):
* and examples.
*
* Parameters:
- * far pointer to real-mode function to call
+ * (32-bit) near pointer to real-mode function to call
*
* Returns: none
****************************************************************************
*/
#define RC_OFFSET_PRESERVE_REGS ( 0 )
-#define RC_OFFSET_RETADDR ( RC_OFFSET_PRESERVE_REGS + 8 )
-#define RC_OFFSET_RM_FUNCTION ( RC_OFFSET_RETADDR + 4 )
-
- .code32
-EXPORT(real_call):
- /* Preserve registers */
- pushl %ebp
- pushl %eax
-
- /* Switch to physical addresses */
- lcall $VIRTUAL_CS, $_virt_to_phys
- addl $4, %esp
-
- /* Extract real-mode function address and store in ljmp instruction */
- call 1f
-1: popl %ebp
- movl RC_OFFSET_RM_FUNCTION(%esp), %eax
- movl %eax, (rc_ljmp + 1 - 1b)(%ebp)
+#define RC_OFFSET_RETADDR ( RC_OFFSET_PRESERVE_REGS + SIZEOF_I386_REGS )
+#define RC_OFFSET_FUNCTION ( RC_OFFSET_RETADDR + 4 )
+#define RC_OFFSET_END ( RC_OFFSET_FUNCTION + 4 )
- /* Restore registers */
- popl %eax
- popl %ebp
+ .section ".text"
+ .code32
+ .globl real_call
+real_call:
+ /* Create register dump on PM stack */
+ pushal
- /* Switch to real mode, preserving non-segment registers */
- call prot_to_real
+ /* Switch to real mode and move register dump to RM stack */
+ movl $RC_OFFSET_END, %ecx
+ pushl $1f
+ jmp prot_to_real
+ .section ".text16"
.code16
+1:
+ /* Construct call to real-mode function */
+ movw %sp, %bp
+ movw RC_OFFSET_FUNCTION(%bp), %ax
+ movw %ax, %cs:rc_function
- /* Far call to real-mode routine */
- pushw %cs
- call rc_ljmp
- jmp 2f
-rc_ljmp:
- ljmp $0, $0 /* address filled in by above code */
-2:
-
- /* Switch to protected mode */
- pushw %cs
- call real_to_prot
- .code32
+ /* Call real-mode function */
+ popal
+ call *%cs:rc_function
+ pushal
- /* Switch to virtual addresses */
- call 1f
- jmp 2f
-1: ljmp $VIRTUAL_CS, $_phys_to_virt
-2:
+ /* Switch to protected mode and move register dump back to PM stack */
+ movl $RC_OFFSET_END, %ecx
+ pushl $1f
+ jmp real_to_prot
+ .section ".text"
+ .code32
+1:
+ /* Set up environment expected by C code */
+ call gateA20_set
- /* Enable A20 line */
- pushal
- lcall $VIRTUAL_CS, $gateA20_set
- popl %eax /* discard */
+ /* Restore registers and return */
popal
-
- /* Return */
ret
-
-/****************************************************************************
- * Relocation lock counter
- *
- * librm may be moved in base memory only when this counter is zero.
- * The counter gets incremented whenever a reference to librm is
- * generated (e.g. a real_call is made, resulting in a return address
- * pointing to librm being placed on the stack), and decremented when
- * the reference goes out of scope (e.g. the real_call returns).
- ****************************************************************************
- */
-EXPORT(librm_ref_count): .byte 0
+ .section ".text16"
+rc_function: .word 0
+
/****************************************************************************
* Stored real-mode and protected-mode stack pointers
*
@@ -734,25 +465,18 @@ EXPORT(librm_ref_count): .byte 0
****************************************************************************
*/
-EXPORT(rm_stack): /* comprises rm_ss and rm_sp */
-rm_sp: .word 0
-rm_ss: .word 0
-
-EXPORT(pm_stack):
-pm_esp: .long 0
-
-/****************************************************************************
- * Temporary variables
- ****************************************************************************
- */
-save_eax: .long 0
-save_ebx: .long 0
-save_retaddr: .long 0
-
-/****************************************************************************
- * End of librm
- ****************************************************************************
- */
-_librm_end:
- .globl _librm_size
- .equ _librm_size, _librm_end - _librm_start
+ .section ".data"
+ .globl rm_sp
+rm_sp: .word 0
+ .globl rm_ss
+rm_ss: .word 0
+ .globl rm_cs
+rm_cs: .word 0
+ .globl pm_esp
+pm_esp: .long _estack
+
+ .section ".text16"
+virt_offset_rm_copy: .long 0
+ .section ".data"
+ .globl virt_offset
+virt_offset: .long 0