123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317 |
- /*
- * 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
|