mirror of
https://github.com/ipxe/ipxe
synced 2025-12-30 04:28:12 +03:00
Merged mcb30-realmode-redesign back to HEAD
This commit is contained in:
@@ -1,18 +1,5 @@
|
||||
/* #defines because ljmp wants a number, probably gas bug */
|
||||
/* .equ KERN_CODE_SEG,_pmcs-_gdt */
|
||||
#define KERN_CODE_SEG 0x08
|
||||
.equ KERN_DATA_SEG,_pmds-_gdt
|
||||
/* .equ REAL_CODE_SEG,_rmcs-_gdt */
|
||||
#define REAL_CODE_SEG 0x18
|
||||
.equ REAL_DATA_SEG,_rmds-_gdt
|
||||
.equ FLAT_CODE_SEG,_pmcs2-_gdt
|
||||
.equ FLAT_DATA_SEG,_pmds2-_gdt
|
||||
.equ CR0_PE,1
|
||||
#ifdef CONFIG_X86_64
|
||||
.equ LM_CODE_SEG, _lmcs-_gdt
|
||||
.equ LM_DATA_SEG, _lmds-_gdt
|
||||
#endif
|
||||
|
||||
#include "virtaddr.h"
|
||||
|
||||
.equ MSR_K6_EFER, 0xC0000080
|
||||
.equ EFER_LME, 0x00000100
|
||||
.equ X86_CR4_PAE, 0x00000020
|
||||
@@ -29,12 +16,6 @@
|
||||
#define LJMPI(x) ljmp x
|
||||
#endif
|
||||
|
||||
#define BOCHSBP xchgw %bx, %bx
|
||||
|
||||
#include "callbacks.h"
|
||||
#define NUM_PUSHA_REGS (8)
|
||||
#define NUM_SEG_REGS (6)
|
||||
|
||||
/*
|
||||
* NOTE: if you write a subroutine that is called from C code (gcc/egcs),
|
||||
* then you only have to take care of %ebx, %esi, %edi and %ebp. These
|
||||
@@ -54,226 +35,10 @@
|
||||
* deal correctly with 16 bit return addresses. I tried it, but failed.
|
||||
*/
|
||||
|
||||
/**************************************************************************
|
||||
* START
|
||||
*
|
||||
* This file is no longer enterered from the top. init.S will jump to
|
||||
* either _in_call or _rm_in_call, depending on the processor mode
|
||||
* when init.S was entered.
|
||||
**************************************************************************/
|
||||
.text
|
||||
.arch i386
|
||||
.code32
|
||||
|
||||
/**************************************************************************
|
||||
_IN_CALL - make a call in to Etherboot.
|
||||
**************************************************************************/
|
||||
|
||||
/* There are two 32-bit entry points: _in_call and _in_call_far, for
|
||||
* near calls and far calls respectively. Both should be called with
|
||||
* flat physical addresses. They will result in a call to the C
|
||||
* routine in_call(); see there for API details.
|
||||
*
|
||||
* Note that this routine makes fairly heavy use of the stack and no
|
||||
* use of fixed data areas. This is because it must be re-entrant;
|
||||
* there may be more than one concurrent call in to Etherboot.
|
||||
*/
|
||||
|
||||
#define IC_OFFSET_VA_LIST_PTR ( 0 )
|
||||
#define IC_OFFSET_VA_LIST_PTR_E ( IC_OFFSET_VA_LIST_PTR + 4 )
|
||||
#define IC_OFFSET_REGISTERS ( IC_OFFSET_VA_LIST_PTR_E )
|
||||
#define IC_OFFSET_REGISTERS_E ( IC_OFFSET_REGISTERS + ( NUM_PUSHA_REGS * 4 ) )
|
||||
#define IC_OFFSET_SEG_REGS ( IC_OFFSET_REGISTERS_E )
|
||||
#define IC_OFFSET_SEG_REGS_E ( IC_OFFSET_SEG_REGS + ( NUM_SEG_REGS * 2 ) )
|
||||
#define IC_OFFSET_GDT ( IC_OFFSET_SEG_REGS_E )
|
||||
#define IC_OFFSET_GDT_E ( IC_OFFSET_GDT + 8 )
|
||||
#define IC_OFFSET_FLAGS ( IC_OFFSET_GDT_E )
|
||||
#define IC_OFFSET_FLAGS_E ( IC_OFFSET_FLAGS + 4 )
|
||||
#define IC_OFFSET_RETADDR ( IC_OFFSET_FLAGS_E )
|
||||
#define IC_OFFSET_RETADDR_E ( IC_OFFSET_RETADDR + 8 )
|
||||
#define IC_OFFSET_ORIG_STACK ( IC_OFFSET_RETADDR )
|
||||
#define IC_OFFSET_OPCODE ( IC_OFFSET_ORIG_STACK + 8 )
|
||||
#define IC_OFFSET_OPCODE_E ( IC_OFFSET_OPCODE + 4 )
|
||||
#define IC_OFFSET_VA_LIST ( IC_OFFSET_OPCODE_E )
|
||||
|
||||
.code32
|
||||
.globl _in_call
|
||||
.globl _in_call_far
|
||||
_in_call:
|
||||
/* Expand to far return address */
|
||||
pushl %eax /* Store %eax */
|
||||
xorl %eax, %eax
|
||||
movw %cs, %ax
|
||||
xchgl %eax, 4(%esp) /* 4(%esp) = %cs, %eax = ret addr */
|
||||
xchgl %eax, 0(%esp) /* 0(%esp) = ret addr, restore %eax */
|
||||
_in_call_far:
|
||||
/* Store flags */
|
||||
pushfl
|
||||
/* Store the GDT */
|
||||
subl $8, %esp
|
||||
sgdt 0(%esp)
|
||||
/* Store segment register values */
|
||||
pushw %gs
|
||||
pushw %fs
|
||||
pushw %es
|
||||
pushw %ds
|
||||
pushw %ss
|
||||
pushw %cs
|
||||
/* Store general-purpose register values */
|
||||
pushal
|
||||
/* Replace %esp in store with physical %esp value on entry */
|
||||
leal (IC_OFFSET_ORIG_STACK - IC_OFFSET_REGISTERS)(%esp), %eax
|
||||
movl %eax, (IC_OFFSET_REGISTERS - IC_OFFSET_REGISTERS + 12)(%esp)
|
||||
/* Store va_list pointer (physical address) */
|
||||
leal (IC_OFFSET_VA_LIST - IC_OFFSET_VA_LIST_PTR_E)(%esp), %eax
|
||||
pushl %eax
|
||||
/* IC_OFFSET_*(%esp) are now valid */
|
||||
|
||||
/* Switch to virtual addresses */
|
||||
call _phys_to_virt
|
||||
|
||||
/* Fixup the va_list pointer */
|
||||
movl virt_offset, %ebp
|
||||
subl %ebp, IC_OFFSET_VA_LIST_PTR(%esp)
|
||||
|
||||
/* Check opcode for EB_USE_INTERNAL_STACK flag */
|
||||
movl IC_OFFSET_OPCODE(%esp), %eax
|
||||
testl $EB_USE_INTERNAL_STACK, %eax
|
||||
je 2f
|
||||
/* Use internal stack flag set */
|
||||
/* Check %esp is not already in internal stack range */
|
||||
leal _stack, %esi /* %esi = bottom of internal stack */
|
||||
leal _estack, %edi /* %edi = top of internal stack */
|
||||
cmpl %esi, %esp
|
||||
jb 1f
|
||||
cmpl %edi, %esp
|
||||
jbe 2f
|
||||
1: /* %esp not currently in internal stack range */
|
||||
movl %esp, %esi /* %esi = original stack */
|
||||
movl $IC_OFFSET_OPCODE_E, %ecx /* %ecx = length to transfer */
|
||||
subl %ecx, %edi /* %edi = internal stack pos */
|
||||
movl %edi, %esp /* = new %esp */
|
||||
rep movsb /* Copy data to internal stack */
|
||||
2:
|
||||
|
||||
/* Call to C code */
|
||||
call i386_in_call
|
||||
|
||||
/* Set %eax (return code from C) in registers structure on
|
||||
* stack, so that we return it to the caller.
|
||||
*/
|
||||
movl %eax, (IC_OFFSET_REGISTERS + 28)(%esp)
|
||||
|
||||
/* Calculate physical continuation address */
|
||||
movl virt_offset, %ebp
|
||||
movzwl (IC_OFFSET_SEG_REGS + 0)(%esp), %eax /* %cs */
|
||||
movzwl (IC_OFFSET_SEG_REGS + 2)(%esp), %ebx /* %ss */
|
||||
pushl %eax /* Continuation segment */
|
||||
leal 1f(%ebp), %eax
|
||||
pushl %eax /* Continuation offset */
|
||||
|
||||
/* Restore caller's GDT */
|
||||
cli /* Temporarily disable interrupts */
|
||||
lgdt (8+IC_OFFSET_GDT)(%esp)
|
||||
/* Reset %ss and adjust %esp */
|
||||
movw %bx, %ss
|
||||
addl %ebp, %esp
|
||||
lret /* Reload %cs:eip, flush prefetch */
|
||||
1:
|
||||
|
||||
/* Skip va_list ptr */
|
||||
popl %eax
|
||||
/* Reload general-purpose registers to be returned */
|
||||
popal
|
||||
/* Reload segment registers as passed in from caller */
|
||||
popw %gs
|
||||
popw %fs
|
||||
popw %es
|
||||
popw %ds
|
||||
addl $(4+8), %esp /* Skip %cs, %ss and GDT (already reloaded) */
|
||||
/* Restore flags (including revert of interrupt status) */
|
||||
popfl
|
||||
|
||||
/* Restore physical %esp from entry. It will only be
|
||||
* different if EB_USE_INTERNAL_STACK was specified.
|
||||
*/
|
||||
movl ( 12 + IC_OFFSET_REGISTERS - IC_OFFSET_RETADDR )(%esp), %esp
|
||||
|
||||
/* Check for EB_SKIP_OPCODE */
|
||||
pushfl
|
||||
testl $EB_SKIP_OPCODE, 12(%esp)
|
||||
jnz 1f
|
||||
/* Normal return */
|
||||
popfl
|
||||
lret
|
||||
1: /* Return and skip opcode */
|
||||
popfl
|
||||
lret $4
|
||||
|
||||
/**************************************************************************
|
||||
RELOCATE_TO - relocate etherboot to the specified address
|
||||
**************************************************************************/
|
||||
.globl relocate_to
|
||||
relocate_to:
|
||||
/* Save the callee save registers */
|
||||
pushl %ebp
|
||||
pushl %esi
|
||||
pushl %edi
|
||||
|
||||
/* Compute the virtual destination address */
|
||||
movl 16(%esp), %edi # dest
|
||||
subl virt_offset, %edi
|
||||
|
||||
|
||||
/* Compute the new value of virt_offset */
|
||||
movl 16(%esp), %ebp # virt_offset
|
||||
subl $_text, %ebp
|
||||
|
||||
/* Fixup the gdt */
|
||||
pushl $_pmcs
|
||||
pushl %ebp # virt_offset
|
||||
call set_seg_base
|
||||
addl $8, %esp
|
||||
|
||||
/* Fixup gdtarg */
|
||||
leal _gdt(%ebp), %eax
|
||||
movl %eax, gdtarg +2
|
||||
|
||||
/* Fixup virt_offset */
|
||||
movl %ebp, virt_offset
|
||||
|
||||
/* Load the move parameters */
|
||||
movl $_text, %esi
|
||||
movl $_end, %ecx
|
||||
subl %esi, %ecx
|
||||
|
||||
/* Move etherboot uses %esi, %edi, %ecx */
|
||||
rep
|
||||
movsb
|
||||
|
||||
/* Reload the gdt */
|
||||
cs
|
||||
lgdt gdtarg
|
||||
|
||||
/* Reload %cs */
|
||||
ljmp $KERN_CODE_SEG, $1f
|
||||
1:
|
||||
/* reload other segment registers */
|
||||
movl $KERN_DATA_SEG, %eax
|
||||
movl %eax,%ds
|
||||
movl %eax,%es
|
||||
movl %eax,%ss
|
||||
movl %eax,%fs
|
||||
movl %eax,%gs
|
||||
|
||||
/* Restore the callee save registers */
|
||||
popl %edi
|
||||
popl %esi
|
||||
popl %ebp
|
||||
|
||||
/* return */
|
||||
ret
|
||||
|
||||
/**************************************************************************
|
||||
XSTART32 - Transfer control to the kernel just loaded
|
||||
**************************************************************************/
|
||||
@@ -301,7 +66,7 @@ xstart32:
|
||||
pushl %ebx
|
||||
|
||||
/* Store the destination address on the stack */
|
||||
pushl $FLAT_CODE_SEG
|
||||
pushl $PHYSICAL_CS
|
||||
pushl %ecx
|
||||
|
||||
/* Cache virt_offset */
|
||||
@@ -536,218 +301,6 @@ end_lm:
|
||||
.arch i386
|
||||
#endif /* CONFIG_X86_64 */
|
||||
|
||||
/**************************************************************************
|
||||
SETJMP - Save stack context for non-local goto
|
||||
**************************************************************************/
|
||||
.globl setjmp
|
||||
setjmp:
|
||||
movl 4(%esp),%ecx /* jmpbuf */
|
||||
movl 0(%esp),%edx /* return address */
|
||||
movl %edx,0(%ecx)
|
||||
movl %ebx,4(%ecx)
|
||||
movl %esp,8(%ecx)
|
||||
movl %ebp,12(%ecx)
|
||||
movl %esi,16(%ecx)
|
||||
movl %edi,20(%ecx)
|
||||
movl $0,%eax
|
||||
ret
|
||||
|
||||
/**************************************************************************
|
||||
LONGJMP - Non-local jump to a saved stack context
|
||||
**************************************************************************/
|
||||
.globl longjmp
|
||||
longjmp:
|
||||
movl 4(%esp),%edx /* jumpbuf */
|
||||
movl 8(%esp),%eax /* result */
|
||||
movl 0(%edx),%ecx
|
||||
movl 4(%edx),%ebx
|
||||
movl 8(%edx),%esp
|
||||
movl 12(%edx),%ebp
|
||||
movl 16(%edx),%esi
|
||||
movl 20(%edx),%edi
|
||||
cmpl $0,%eax
|
||||
jne 1f
|
||||
movl $1,%eax
|
||||
1: movl %ecx,0(%esp)
|
||||
ret
|
||||
|
||||
/**************************************************************************
|
||||
_VIRT_TO_PHYS - Transition from virtual to physical addresses
|
||||
Preserves all preservable registers and flags
|
||||
**************************************************************************/
|
||||
.globl _virt_to_phys
|
||||
_virt_to_phys:
|
||||
pushfl
|
||||
pushl %ebp
|
||||
pushl %eax
|
||||
movl virt_offset, %ebp /* Load virt_offset */
|
||||
addl %ebp, 12(%esp) /* Adjust the return address */
|
||||
|
||||
/* reload the code segment */
|
||||
pushl $FLAT_CODE_SEG
|
||||
leal 1f(%ebp), %eax
|
||||
pushl %eax
|
||||
lret
|
||||
|
||||
1:
|
||||
/* reload other segment registers */
|
||||
movl $FLAT_DATA_SEG, %eax
|
||||
movl %eax, %ds
|
||||
movl %eax, %es
|
||||
movl %eax, %ss
|
||||
addl %ebp, %esp /* Adjust the stack pointer */
|
||||
movl %eax, %fs
|
||||
movl %eax, %gs
|
||||
|
||||
popl %eax
|
||||
popl %ebp
|
||||
popfl
|
||||
ret
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
_PHYS_TO_VIRT - Transition from using physical to virtual addresses
|
||||
Preserves all preservable registers and flags
|
||||
**************************************************************************/
|
||||
.globl _phys_to_virt
|
||||
_phys_to_virt:
|
||||
pushfl
|
||||
pushl %ebp
|
||||
pushl %eax
|
||||
|
||||
call 1f
|
||||
1: popl %ebp
|
||||
subl $1b, %ebp
|
||||
movl %ebp, virt_offset(%ebp)
|
||||
|
||||
/* Fixup the gdt */
|
||||
leal _pmcs(%ebp), %eax
|
||||
pushl %eax
|
||||
pushl %ebp
|
||||
call set_seg_base
|
||||
addl $8, %esp
|
||||
|
||||
/* Fixup gdtarg */
|
||||
leal _gdt(%ebp), %eax
|
||||
movl %eax, (gdtarg+2)(%ebp)
|
||||
|
||||
/* Load the global descriptor table */
|
||||
cli
|
||||
lgdt %cs:gdtarg(%ebp)
|
||||
ljmp $KERN_CODE_SEG, $1f
|
||||
1:
|
||||
/* reload other segment regsters */
|
||||
movl $KERN_DATA_SEG, %eax
|
||||
movl %eax, %ds
|
||||
movl %eax, %es
|
||||
movl %eax, %ss
|
||||
subl %ebp, %esp /* Adjust the stack pointer */
|
||||
movl %eax, %fs
|
||||
movl %eax, %gs
|
||||
|
||||
subl %ebp, 12(%esp) /* Adjust the return address */
|
||||
popl %eax
|
||||
popl %ebp
|
||||
popfl
|
||||
ret
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
SET_SEG_BASE - Set the base address of a segment register
|
||||
**************************************************************************/
|
||||
.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
|
||||
|
||||
/**************************************************************************
|
||||
GLOBAL DESCRIPTOR TABLE
|
||||
**************************************************************************/
|
||||
.data
|
||||
.align 4
|
||||
|
||||
.globl _gdt
|
||||
.globl gdtarg
|
||||
_gdt:
|
||||
gdtarg:
|
||||
.word _gdt_end - _gdt - 1 /* limit */
|
||||
.long _gdt /* addr */
|
||||
.word 0
|
||||
|
||||
.globl _pmcs
|
||||
_pmcs:
|
||||
/* 32 bit protected mode code segment */
|
||||
.word 0xffff,0
|
||||
.byte 0,0x9f,0xcf,0
|
||||
|
||||
_pmds:
|
||||
/* 32 bit protected mode data segment */
|
||||
.word 0xffff,0
|
||||
.byte 0,0x93,0xcf,0
|
||||
|
||||
_rmcs:
|
||||
/* 16 bit real mode code segment */
|
||||
.word 0xffff,(0&0xffff)
|
||||
.byte (0>>16),0x9b,0x00,(0>>24)
|
||||
|
||||
_rmds:
|
||||
/* 16 bit real mode data segment */
|
||||
.word 0xffff,(0&0xffff)
|
||||
.byte (0>>16),0x93,0x00,(0>>24)
|
||||
|
||||
_pmcs2:
|
||||
/* 32 bit protected mode code segment, base 0 */
|
||||
.word 0xffff,0
|
||||
.byte 0,0x9f,0xcf,0
|
||||
|
||||
_pmds2:
|
||||
/* 32 bit protected mode data segment, base 0 */
|
||||
.word 0xffff,0
|
||||
.byte 0,0x93,0xcf,0
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
_lmcs:
|
||||
/* 64bit long mode code segment, base 0 */
|
||||
.word 0xffff, 0
|
||||
.byte 0x00, 0x9f, 0xaf , 0x00
|
||||
_lmds:
|
||||
/* 64bit long mode data segment, base 0 */
|
||||
.word 0xffff, 0
|
||||
.byte 0x00, 0x93, 0xcf, 0x00
|
||||
#endif
|
||||
_gdt_end:
|
||||
|
||||
/* The initial register contents */
|
||||
.balign 4
|
||||
.globl initial_regs
|
||||
initial_regs:
|
||||
.fill 8, 4, 0
|
||||
|
||||
/* The virtual address offset */
|
||||
.globl virt_offset
|
||||
virt_offset:
|
||||
.long 0
|
||||
|
||||
.section ".stack"
|
||||
.p2align 3
|
||||
/* allocate a 4K stack in the stack segment */
|
||||
.globl _stack
|
||||
_stack:
|
||||
.space 4096
|
||||
.globl _estack
|
||||
_estack:
|
||||
#ifdef CONFIG_X86_64
|
||||
.section ".bss"
|
||||
.p2align 12
|
||||
|
||||
Reference in New Issue
Block a user