diff options
author | Michael Brown | 2016-02-19 20:43:04 +0100 |
---|---|---|
committer | Michael Brown | 2016-02-21 00:09:36 +0100 |
commit | ea203e4fe1c60504023ea2604604d8092f24effc (patch) | |
tree | a35f62d6dc59334ad9118020ac1adabc567069ac /src/arch | |
parent | [build] Fix building on older versions of binutils (diff) | |
download | ipxe-ea203e4fe1c60504023ea2604604d8092f24effc.tar.gz ipxe-ea203e4fe1c60504023ea2604604d8092f24effc.tar.xz ipxe-ea203e4fe1c60504023ea2604604d8092f24effc.zip |
[librm] Add phys_call() wrapper for calling code with physical addressing
Add a phys_call() wrapper function (analogous to the existing
real_call() wrapper function) for calling code with flat physical
addressing, and use this wrapper within the PHYS_CODE() macro.
Move the relevant functionality inside librm.S, where it more
naturally belongs.
The COMBOOT code currently uses explicit calls to _virt_to_phys and
_phys_to_virt. These will need to be rewritten if our COMBOOT support
is ever generalised to be able to run in a 64-bit build.
Specifically:
- com32_exec_loop() should be restructured to use PHYS_CODE()
- com32_wrapper.S should be restructured to use an equivalent of
prot_call(), passing parameters via a struct i386_all_regs
- there appears to be no need for com32_wrapper.S to switch between
external and internal stacks; this could be omitted to simplify
the design.
For now, librm.S continues to expose _virt_to_phys and _phys_to_virt
for use by com32.c and com32_wrapper.S. Similarly, librm.S continues
to expose _intr_to_virt for use by gdbidt.S.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/arch')
-rw-r--r-- | src/arch/x86/core/virtaddr.S | 145 | ||||
-rw-r--r-- | src/arch/x86/include/librm.h | 11 | ||||
-rw-r--r-- | src/arch/x86/transitions/librm.S | 209 |
3 files changed, 212 insertions, 153 deletions
diff --git a/src/arch/x86/core/virtaddr.S b/src/arch/x86/core/virtaddr.S deleted file mode 100644 index 45beb164..00000000 --- a/src/arch/x86/core/virtaddr.S +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Functions to support the virtual addressing method of relocation - * that Etherboot uses. - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) - -#include "librm.h" - - .arch i386 - .text - .code32 - -/**************************************************************************** - * _virt_to_phys (virtual addressing) - * - * Switch from virtual to flat physical addresses. %esp is adjusted - * to a physical value. Segment registers are set to flat physical - * selectors. All other registers are preserved. Flags are - * preserved. - * - * Parameters: none - * Returns: none - **************************************************************************** - */ - .globl _virt_to_phys -_virt_to_phys: - /* Preserve registers and flags */ - pushfl - pushl %eax - pushl %ebp - - /* Change return address to a physical address */ - movl VIRTUAL(virt_offset), %ebp - addl %ebp, 12(%esp) - - /* Switch to physical code segment */ - cli - pushl $PHYSICAL_CS - leal VIRTUAL(1f)(%ebp), %eax - pushl %eax - lret -1: - /* Reload other segment registers and adjust %esp */ - movl $PHYSICAL_DS, %eax - movl %eax, %ds - movl %eax, %es - movl %eax, %fs - movl %eax, %gs - movl %eax, %ss - addl %ebp, %esp - - /* Restore registers and flags, and return */ - popl %ebp - popl %eax - popfl - ret - -/**************************************************************************** - * _phys_to_virt (flat physical addressing) - * - * Switch from flat physical to virtual addresses. %esp is adjusted - * to a virtual value. Segment registers are set to virtual - * selectors. All other registers are preserved. Flags are - * preserved. - * - * Parameters: none - * Returns: none - **************************************************************************** - */ - .globl _phys_to_virt -_phys_to_virt: - /* Preserve registers and flags */ - pushfl - pushl %eax - pushl %ebp - - /* Switch to virtual code segment */ - cli - ljmp $VIRTUAL_CS, $VIRTUAL(1f) -1: - /* Reload data segment registers */ - movl $VIRTUAL_DS, %eax - movl %eax, %ds - movl %eax, %es - movl %eax, %fs - movl %eax, %gs - - /* Reload stack segment and adjust %esp */ - movl VIRTUAL(virt_offset), %ebp - movl %eax, %ss - subl %ebp, %esp - - /* Change the return address to a virtual address */ - subl %ebp, 12(%esp) - - /* Restore registers and flags, and return */ - popl %ebp - popl %eax - popfl - ret - -/**************************************************************************** - * _intr_to_virt (virtual code segment, virtual or physical stack segment) - * - * Switch from virtual code segment with either a virtual or physical - * stack segment to using virtual addressing. %esp is adjusted if - * necessary to a virtual value. Segment registers are set to virtual - * selectors. All other registers are preserved. Flags are - * preserved. - * - * Parameters: none - * Returns: none - **************************************************************************** - */ - .globl _intr_to_virt -_intr_to_virt: - /* Preserve registers and flags */ - pushfl - pushl %eax - pushl %ebp - - /* Check whether stack segment is physical or virtual */ - movl %ss, %eax - cmpw $VIRTUAL_DS, %ax - movl $VIRTUAL_DS, %eax - - /* Reload data segment registers */ - movl %eax, %ds - movl %eax, %es - movl %eax, %fs - movl %eax, %gs - - /* Reload stack segment and adjust %esp if necessary */ - je 1f - movl VIRTUAL(virt_offset), %ebp - movl %eax, %ss - subl %ebp, %esp -1: - /* Restore registers and flags, and return */ - popl %ebp - popl %eax - popfl - ret diff --git a/src/arch/x86/include/librm.h b/src/arch/x86/include/librm.h index fc31c503..bc925a2d 100644 --- a/src/arch/x86/include/librm.h +++ b/src/arch/x86/include/librm.h @@ -250,11 +250,16 @@ extern void remove_user_from_rm_stack ( userptr_t data, size_t size ); /* PHYS_CODE: declare a fragment of code that executes in flat physical mode */ #define PHYS_CODE( asm_code_str ) \ - "call _virt_to_phys\n\t" \ + "push $1f\n\t" \ + "call phys_call\n\t" \ + ".section \".text.phys\", \"ax\", @progbits\n\t"\ ".code32\n\t" \ + "\n1:\n\t" \ asm_code_str \ - "call _phys_to_virt\n\t" \ - CODE_DEFAULT "\n\t" + "\n\t" \ + "ret\n\t" \ + CODE_DEFAULT "\n\t" \ + ".previous\n\t" /** Number of interrupts */ #define NUM_INT 256 diff --git a/src/arch/x86/transitions/librm.S b/src/arch/x86/transitions/librm.S index f3854dfe..ab4994fb 100644 --- a/src/arch/x86/transitions/librm.S +++ b/src/arch/x86/transitions/librm.S @@ -502,6 +502,150 @@ rm_gdtr: .long 0 /* Base */ /**************************************************************************** + * phys_to_prot (protected-mode near call, 32-bit physical return address) + * + * Switch from 32-bit protected mode with physical addresses to 32-bit + * protected mode with virtual addresses. %esp is adjusted to a + * virtual address. All other registers and flags are preserved. + * + * The return address for this function should be a 32-bit physical + * (sic) address. + * + **************************************************************************** + */ + .section ".text.phys_to_prot", "ax", @progbits + .code32 + .globl phys_to_prot +phys_to_prot: + /* Preserve registers and flags */ + pushfl + pushl %eax + pushl %ebp + + /* Switch to virtual code segment */ + cli + ljmp $VIRTUAL_CS, $VIRTUAL(1f) +1: + /* Switch to virtual data segment and adjust %esp */ + movw $VIRTUAL_DS, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + movl VIRTUAL(virt_offset), %ebp + subl %ebp, %esp + + /* Adjust return address to a virtual address */ + subl %ebp, 12(%esp) + + /* Restore registers and flags, and return */ + popl %ebp + popl %eax + popfl + ret + + /* Expose as _phys_to_virt for use by COMBOOT */ + .globl _phys_to_virt + .equ _phys_to_virt, phys_to_prot + +/**************************************************************************** + * prot_to_phys (protected-mode near call, 32-bit virtual return address) + * + * Switch from 32-bit protected mode with virtual addresses to 32-bit + * protected mode with physical addresses. %esp is adjusted to a + * physical address. All other registers and flags are preserved. + * + * The return address for this function should be a 32-bit virtual + * (sic) address. + * + **************************************************************************** + */ + .section ".text.prot_to_phys", "ax", @progbits + .code32 +prot_to_phys: + /* Preserve registers and flags */ + pushfl + pushl %eax + pushl %ebp + + /* Adjust return address to a physical address */ + movl VIRTUAL(virt_offset), %ebp + addl %ebp, 12(%esp) + + /* Switch to physical code segment */ + cli + pushl $PHYSICAL_CS + leal VIRTUAL(1f)(%ebp), %eax + pushl %eax + lret +1: + /* Switch to physical data segment and adjust %esp */ + movw $PHYSICAL_DS, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + addl %ebp, %esp + + /* Restore registers and flags, and return */ + popl %ebp + popl %eax + popfl + ret + + /* Expose as _virt_to_phys for use by COMBOOT */ + .globl _virt_to_phys + .equ _virt_to_phys, prot_to_phys + +/**************************************************************************** + * intr_to_prot (protected-mode near call, 32-bit virtual return address) + * + * Switch from 32-bit protected mode with a virtual code segment and + * either a physical or virtual stack segment to 32-bit protected mode + * with normal virtual addresses. %esp is adjusted if necessary to a + * virtual address. All other registers and flags are preserved. + * + * The return address for this function should be a 32-bit virtual + * address. + * + **************************************************************************** + */ + .section ".text.intr_to_prot", "ax", @progbits + .code32 + .globl intr_to_prot +intr_to_prot: + /* Preserve registers and flags */ + pushfl + pushl %eax + + /* Check whether stack segment is physical or virtual */ + movw %ss, %ax + cmpw $VIRTUAL_DS, %ax + movw $VIRTUAL_DS, %ax + + /* Reload data segment registers */ + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + + /* Reload stack segment and adjust %esp if necessary */ + je 1f + movw %ax, %ss + subl VIRTUAL(virt_offset), %esp +1: + /* Restore registers and flags, and return */ + popl %eax + popfl + ret + + /* Expose as _intr_to_virt for use by GDB */ + .globl _intr_to_virt + .equ _intr_to_virt, intr_to_prot + +/**************************************************************************** * prot_call (real-mode near call, 16-bit real-mode near return address) * * Call a specific C function in the protected-mode code. The @@ -539,6 +683,7 @@ PC_OFFSET_IDT: .space 6 PC_OFFSET_IX86: .space SIZEOF_I386_ALL_REGS PC_OFFSET_PADDING: .space 2 /* for alignment */ PC_OFFSET_RETADDR: .space 2 +PC_OFFSET_PARAMS: PC_OFFSET_FUNCTION: .space 4 PC_OFFSET_END: .previous @@ -601,7 +746,9 @@ pc_rmode: addr32 movl -20(%esp), %esp popfl popfw /* padding */ - ret $4 + + /* Return and discard function parameters */ + ret $( PC_OFFSET_END - PC_OFFSET_PARAMS ) /**************************************************************************** * real_call (protected-mode near call, 32-bit virtual return address) @@ -620,7 +767,7 @@ pc_rmode: * See librm.h and realmode.h for details and examples. * * Parameters: - * (32-bit) near pointer to real-mode function to call + * function : offset within .text16 of real-mode function to call * * Returns: none **************************************************************************** @@ -629,6 +776,7 @@ pc_rmode: RC_OFFSET_REGS: .space SIZEOF_I386_REGS RC_OFFSET_REGS_END: RC_OFFSET_RETADDR: .space 4 +RC_OFFSET_PARAMS: RC_OFFSET_FUNCTION: .space 4 RC_OFFSET_END: .previous @@ -665,9 +813,11 @@ rc_rmode: .section ".text.real_call", "ax", @progbits .code32 rc_pmode: - /* Restore registers and return */ + /* Restore registers */ popal - ret $4 + + /* Return and discard function parameters */ + ret $( RC_OFFSET_END - RC_OFFSET_PARAMS ) /* Function vector, used because "call xx(%sp)" is not a valid @@ -685,6 +835,55 @@ rm_default_gdtr_idtr: .long 0 /* Interrupt descriptor table base */ /**************************************************************************** + * phys_call (protected-mode near call, 32-bit virtual return address) + * + * Call a function with flat 32-bit physical addressing + * + * The non-segment register values will be passed directly to the + * function. The segment registers will be set for flat 32-bit + * physical addressing. The non-segment register values set by the + * function will be passed back to the caller. + * + * librm.h defines a convenient macro PHYS_CODE() for using phys_call. + * + * Parameters: + * function : virtual (sic) address of function to call + * + **************************************************************************** + */ + .struct 0 +PHC_OFFSET_RETADDR: .space 4 +PHC_OFFSET_PARAMS: +PHC_OFFSET_FUNCTION: .space 4 +PHC_OFFSET_END: + .previous + + .section ".text.phys_call", "ax", @progbits + .code32 + .globl phys_call +phys_call: + /* Adjust function pointer to a physical address */ + pushl %ebp + movl VIRTUAL(virt_offset), %ebp + addl %ebp, ( PHC_OFFSET_FUNCTION + 4 /* saved %ebp */ )(%esp) + popl %ebp + + /* Switch to physical addresses */ + call prot_to_phys + + /* Call function */ + call *PHC_OFFSET_FUNCTION(%esp) + + /* For sanity's sake, clear the direction flag as soon as possible */ + cld + + /* Switch to virtual addresses */ + call phys_to_prot + + /* Return and discard function parameters */ + ret $( PHC_OFFSET_END - PHC_OFFSET_PARAMS ) + +/**************************************************************************** * flatten_real_mode (real-mode near call) * * Switch to flat real mode @@ -733,7 +932,7 @@ interrupt_wrapper: pushl %esp /* Switch to virtual addressing */ - call _intr_to_virt + call intr_to_prot /* Expand IRQ number to whole %eax register */ movzbl %al, %eax |