summaryrefslogtreecommitdiffstats
path: root/src/arch/i386/core/virtaddr.S
diff options
context:
space:
mode:
Diffstat (limited to 'src/arch/i386/core/virtaddr.S')
-rw-r--r--src/arch/i386/core/virtaddr.S317
1 files changed, 317 insertions, 0 deletions
diff --git a/src/arch/i386/core/virtaddr.S b/src/arch/i386/core/virtaddr.S
new file mode 100644
index 00000000..ed495c35
--- /dev/null
+++ b/src/arch/i386/core/virtaddr.S
@@ -0,0 +1,317 @@
+/*
+ * Functions to support the virtual addressing method of relocation
+ * that Etherboot uses.
+ *
+ */
+
+#include "virtaddr.h"
+
+ .arch i386
+
+/****************************************************************************
+ * GDT for initial transition to protected mode
+ *
+ * The segment values, PHYSICAL_CS et al, are defined in an external
+ * header file virtaddr.h, since they need to be shared with librm.
+ ****************************************************************************
+ */
+ .data
+ .align 16
+
+gdt:
+gdt_limit: .word gdt_length - 1
+gdt_addr: .long 0
+ .word 0 /* padding */
+
+ .org gdt + PHYSICAL_CS
+physical_cs:
+ /* 32 bit protected mode code segment, physical addresses */
+ .word 0xffff,0
+ .byte 0,0x9f,0xcf,0
+
+ .org gdt + PHYSICAL_DS
+physical_ds:
+ /* 32 bit protected mode data segment, physical addresses */
+ .word 0xffff,0
+ .byte 0,0x93,0xcf,0
+
+ .org gdt + VIRTUAL_CS
+virtual_cs:
+ /* 32 bit protected mode code segment, virtual addresses */
+ .word 0xffff,0
+ .byte 0,0x9f,0xcf,0
+
+ .org gdt + VIRTUAL_DS
+virtual_ds:
+ /* 32 bit protected mode data segment, virtual addresses */
+ .word 0xffff,0
+ .byte 0,0x93,0xcf,0
+
+#ifdef CONFIG_X86_64
+
+ .org gdt + LONG_CS
+long_cs:
+ /* 64bit long mode code segment, base 0 */
+ .word 0xffff, 0
+ .byte 0x00, 0x9f, 0xaf , 0x00
+
+ .org gdt + LONG_DS
+long_ds:
+ /* 64bit long mode data segment, base 0 */
+ .word 0xffff, 0
+ .byte 0x00, 0x93, 0xcf, 0x00
+
+#endif /* CONFIG_X86_64 */
+
+gdt_end:
+ .equ gdt_length, gdt_end - gdt
+
+ /* The virtual address offset */
+ .globl virt_offset
+virt_offset: .long 0
+
+ .text
+ .code32
+
+/****************************************************************************
+ * run_here (flat physical addressing, position-independent)
+ *
+ * Set up a GDT to run Etherboot at the current location with virtual
+ * addressing. This call does not switch to virtual addresses or move
+ * the stack pointer. The GDT will be located within the copy of
+ * Etherboot. All registers are preserved.
+ *
+ * This gets called at startup and at any subsequent relocation of
+ * Etherboot.
+ *
+ * Parameters: none
+ ****************************************************************************
+ */
+ .globl run_here
+run_here:
+ /* Preserve registers */
+ pushl %eax
+ pushl %ebp
+
+ /* Find out where we're running */
+ call 1f
+1: popl %ebp
+ subl $1b, %ebp
+
+ /* Store as virt_offset */
+ movl %ebp, virt_offset(%ebp)
+
+ /* Set segment base addresses in GDT */
+ leal virtual_cs(%ebp), %eax
+ pushl %eax
+ pushl %ebp
+ call set_seg_base
+ popl %eax /* discard */
+ popl %eax /* discard */
+
+ /* Set physical location of GDT */
+ leal gdt(%ebp), %eax
+ movl %eax, gdt_addr(%ebp)
+
+ /* Load the new GDT */
+ lgdt gdt(%ebp)
+
+ /* Reload new flat physical segment registers */
+ movl $PHYSICAL_DS, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %fs
+ movl %eax, %gs
+ movl %eax, %ss
+
+ /* Restore registers, convert return address to far return
+ * address.
+ */
+ popl %ebp
+ movl $PHYSICAL_CS, %eax
+ xchgl %eax, 4(%esp) /* cs now on stack, ret offset now in eax */
+ xchgl %eax, 0(%esp) /* ret offset now on stack, eax restored */
+
+ /* Return to caller, reloading %cs with new value */
+ lret
+
+/****************************************************************************
+ * set_seg_base (any addressing, position-independent)
+ *
+ * Set the base address of a pair of segments in the GDT. This relies
+ * on the layout of the GDT being (CS,DS) pairs.
+ *
+ * Parameters:
+ * uint32_t base_address
+ * struct gdt_entry * code_segment
+ * Returns:
+ * none
+ ****************************************************************************
+ */
+ .globl set_seg_base
+set_seg_base:
+ pushl %eax
+ pushl %ebx
+ movl 12(%esp), %eax /* %eax = base address */
+ movl 16(%esp), %ebx /* %ebx = &code_descriptor */
+ movw %ax, (0+2)(%ebx) /* CS base bits 0-15 */
+ movw %ax, (8+2)(%ebx) /* DS base bits 0-15 */
+ shrl $16, %eax
+ movb %al, (0+4)(%ebx) /* CS base bits 16-23 */
+ movb %al, (8+4)(%ebx) /* DS base bits 16-23 */
+ movb %ah, (0+7)(%ebx) /* CS base bits 24-31 */
+ movb %ah, (8+7)(%ebx) /* DS base bits 24-31 */
+ popl %ebx
+ popl %eax
+ ret
+
+/****************************************************************************
+ * _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 virt_offset, %ebp
+ addl %ebp, 12(%esp)
+
+ /* Switch to physical code segment */
+ pushl $PHYSICAL_CS
+ leal 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.
+ *
+ * Note that this depends on the GDT already being correctly set up
+ * (e.g. by a call to run_here()).
+ *
+ * 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 */
+ ljmp $VIRTUAL_CS, $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 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
+
+/****************************************************************************
+ * relocate_to (virtual addressing)
+ *
+ * Relocate Etherboot to the specified address. The runtime image
+ * (excluding the prefix, decompressor and compressed image) is copied
+ * to a new location, and execution continues in the new copy. This
+ * routine is designed to be called from C code.
+ *
+ * Parameters:
+ * uint32_t new_phys_addr
+ ****************************************************************************
+ */
+ .globl relocate_to
+relocate_to:
+ /* Save the callee save registers */
+ pushl %ebp
+ pushl %esi
+ pushl %edi
+
+ /* Compute the physical source address and data length */
+ movl $_text, %esi
+ movl $_end, %ecx
+ subl %esi, %ecx
+ addl virt_offset, %esi
+
+ /* Compute the physical destination address */
+ movl 16(%esp), %edi
+
+ /* Switch to flat physical addressing */
+ call _virt_to_phys
+
+ /* Do the copy */
+ cld
+ rep movsb
+
+ /* Calculate offset to new image */
+ subl %esi, %edi
+
+ /* Switch to executing in new image */
+ call 1f
+1: popl %ebp
+ leal (2f-1b)(%ebp,%edi), %eax
+ jmpl *%eax
+2:
+ /* Switch to stack in new image */
+ addl %edi, %esp
+
+ /* Call run_here() to set up GDT */
+ call run_here
+
+ /* Switch to virtual addressing */
+ call _phys_to_virt
+
+ /* Restore the callee save registers */
+ popl %edi
+ popl %esi
+ popl %ebp
+
+ /* return */
+ ret