mirror of
https://github.com/ipxe/ipxe
synced 2025-12-26 01:22:37 +03:00
Merged mcb30-realmode-redesign back to HEAD
This commit is contained in:
@@ -9,7 +9,6 @@
|
||||
#include "etherboot.h"
|
||||
#include "callbacks.h"
|
||||
#include "realmode.h"
|
||||
#include "segoff.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
/* Maximum amount of stack data that prefix may request to be passed
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "stdint.h"
|
||||
#include "string.h"
|
||||
#include "bits/cpu.h"
|
||||
#include "init.h"
|
||||
|
||||
|
||||
/* Standard macro to see if a specific flag is changeable */
|
||||
@@ -83,4 +84,7 @@ void cpu_setup(void)
|
||||
{
|
||||
identify_cpu(&cpu_info);
|
||||
}
|
||||
|
||||
INIT_FN ( INIT_CPU, cpu_setup, NULL, NULL );
|
||||
|
||||
#endif /* CONFIG_X86_64 */
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "etherboot.h"
|
||||
#include "elf.h"
|
||||
#include "memsizes.h"
|
||||
|
||||
|
||||
#define NAME "Etherboot"
|
||||
|
||||
@@ -1,35 +1,93 @@
|
||||
#include "etherboot.h"
|
||||
#include "callbacks.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
void arch_main ( in_call_data_t *data __unused, va_list params __unused )
|
||||
{
|
||||
#ifdef PCBIOS
|
||||
/* Deallocate base memory used for the prefix, if applicable
|
||||
*/
|
||||
forget_prefix_base_memory();
|
||||
#include "stdint.h"
|
||||
#include "stddef.h"
|
||||
#include "registers.h"
|
||||
#include "string.h"
|
||||
#include "hooks.h"
|
||||
#include "init.h"
|
||||
#include "main.h"
|
||||
#ifdef REALMODE
|
||||
#include "realmode.h"
|
||||
#endif
|
||||
|
||||
}
|
||||
/* Symbols defined by the linker */
|
||||
extern char _bss[], _ebss[];
|
||||
|
||||
void arch_relocated_from (unsigned long old_addr )
|
||||
/*
|
||||
* This file provides the basic entry points from assembly code. See
|
||||
* README.i386 for a description of the entry code path.
|
||||
*
|
||||
* This file is compiled to two different object files: hooks.o and
|
||||
* hooks_rm.o. REALMODE is defined when compiling hooks_rm.o
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* arch_initialise(): perform any required initialisation such as
|
||||
* setting up the console device and relocating to high memory. Note
|
||||
* that if we relocate to high memory and the prefix is in base
|
||||
* memory, then we will need to install a copy of librm in base memory
|
||||
* and adjust retaddr so that we return to the installed copy.
|
||||
*
|
||||
*/
|
||||
#ifdef REALMODE
|
||||
void arch_rm_initialise ( struct i386_all_regs *regs,
|
||||
void (*retaddr) (void) )
|
||||
#else /* REALMODE */
|
||||
void arch_initialise ( struct i386_all_regs *regs,
|
||||
void (*retaddr) (void) __unused )
|
||||
#endif /* REALMODE */
|
||||
{
|
||||
/* Zero the BSS */
|
||||
memset ( _bss, 0, _ebss - _bss );
|
||||
|
||||
#ifdef PCBIOS
|
||||
/* Deallocate base memory used for the Etherboot runtime,
|
||||
* if applicable
|
||||
/* Call all registered initialisation functions.
|
||||
*/
|
||||
forget_runtime_base_memory( old_addr );
|
||||
#endif
|
||||
|
||||
call_init_fns ();
|
||||
}
|
||||
|
||||
void arch_on_exit ( int exit_status __unused )
|
||||
{
|
||||
#ifdef PCBIOS
|
||||
/* Deallocate the real-mode stack now. We will reallocate
|
||||
* the stack if are going to use it after this point.
|
||||
#ifdef REALMODE
|
||||
|
||||
/*
|
||||
* arch_rm_main() : call main() and then exit via whatever exit mechanism
|
||||
* the prefix requested.
|
||||
*
|
||||
*/
|
||||
void arch_rm_main ( struct i386_all_regs *regs ) {
|
||||
struct i386_all_regs regs_copy;
|
||||
void (*exit_fn) ( struct i386_all_regs *regs );
|
||||
|
||||
/* Take a copy of the registers, because the memory holding
|
||||
* them will probably be trashed by the time main() returns.
|
||||
*/
|
||||
forget_real_mode_stack();
|
||||
#endif
|
||||
regs_copy = *regs;
|
||||
exit_fn = ( typeof ( exit_fn ) ) regs_copy.eax;
|
||||
|
||||
/* Call to main() */
|
||||
regs_copy.eax = main();
|
||||
|
||||
/* Call registered per-object exit functions */
|
||||
call_exit_fns ();
|
||||
|
||||
if ( exit_fn ) {
|
||||
/* Prefix requested that we use a particular function
|
||||
* as the exit path, so we call this function, which
|
||||
* must not return.
|
||||
*/
|
||||
exit_fn ( ®s_copy );
|
||||
}
|
||||
}
|
||||
|
||||
#else /* REALMODE */
|
||||
|
||||
/*
|
||||
* arch_main() : call main() and return
|
||||
*
|
||||
*/
|
||||
void arch_main ( struct i386_all_regs *regs ) {
|
||||
regs->eax = main();
|
||||
|
||||
/* Call registered per-object exit functions */
|
||||
call_exit_fns ();
|
||||
};
|
||||
|
||||
#endif /* REALMODE */
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "etherboot.h"
|
||||
#include "timer.h"
|
||||
#include "latch.h"
|
||||
#include "init.h"
|
||||
|
||||
void __load_timer2(unsigned int ticks)
|
||||
{
|
||||
@@ -40,7 +41,7 @@ static int __timer2_running(void)
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_TSC_CURRTICKS)
|
||||
void setup_timers(void)
|
||||
static void setup_timers(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -126,7 +127,7 @@ bad_ctc:
|
||||
}
|
||||
|
||||
static unsigned long clocks_per_tick;
|
||||
void setup_timers(void)
|
||||
static void setup_timers(void)
|
||||
{
|
||||
if (!clocks_per_tick) {
|
||||
clocks_per_tick = calibrate_tsc();
|
||||
@@ -189,3 +190,5 @@ int timer2_running(void)
|
||||
}
|
||||
|
||||
#endif /* RTC_CURRTICKS */
|
||||
|
||||
INIT_FN ( INIT_TIMERS, setup_timers, NULL, NULL );
|
||||
|
||||
@@ -1,305 +0,0 @@
|
||||
#include "callbacks.h"
|
||||
.equ CR0_PE, 1
|
||||
|
||||
.text
|
||||
.arch i386
|
||||
.section ".prefix", "ax", @progbits
|
||||
|
||||
#undef CODE16
|
||||
#if defined(PCBIOS)
|
||||
#define CODE16
|
||||
#endif
|
||||
|
||||
/* We have two entry points: "conventional" (at the start of the file)
|
||||
* and "callback" (at _entry, 2 bytes in). The "callback" entry
|
||||
* should be used if the caller wishes to provide a specific opcode.
|
||||
* It is equivalent to a call to in_call. Using the "conventional"
|
||||
* entry point is equivalent to using the "callback" entry point with
|
||||
* an opcode of EB_OPCODE_MAIN.
|
||||
*
|
||||
* Both entry points can be called in either 16-bit real or 32-bit
|
||||
* protected mode with flat physical addresses. We detect which mode
|
||||
* the processor is in and call either in_call or rm_in_call as
|
||||
* appropriate. Note that the mode detection code must therefore be
|
||||
* capable of doing the same thing in either mode, even though the
|
||||
* machine code instructions will be interpreted differently.
|
||||
*
|
||||
* The decompressor will be invoked if necessary to decompress
|
||||
* Etherboot before attempting to jump to it.
|
||||
*/
|
||||
|
||||
/******************************************************************************
|
||||
* Entry points and mode detection code
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
.code32
|
||||
/* "Conventional" entry point: caller provides no opcode */
|
||||
.globl _start
|
||||
_start:
|
||||
/* Set flag to indicate conventional entry point used */
|
||||
pushl $0 /* "pushw $0" in 16-bit code */
|
||||
/* Fall through to "callback" entry point */
|
||||
|
||||
/* "Callback" entry point */
|
||||
.globl _entry
|
||||
_entry:
|
||||
|
||||
#ifdef CODE16
|
||||
/* CPU mode detection code */
|
||||
pushl %eax /* "pushw %ax" in 16-bit code */
|
||||
pushw %ax /* "pushl %eax" in 16-bit code */
|
||||
movl %cr0, %eax /* Test protected mode bit */
|
||||
testb $CR0_PE, %al
|
||||
popw %ax /* "popl %eax" in 16-bit code */
|
||||
popl %eax /* "popw %eax" in 16-bit code */
|
||||
jz rmode
|
||||
#endif /* CODE16 */
|
||||
|
||||
/******************************************************************************
|
||||
* Entered in protected mode
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
.code32
|
||||
pmode:
|
||||
cmpl $0, 0(%esp) /* Conventional entry point used? */
|
||||
jne 1f
|
||||
/* Entered via conventional entry point: set up stack */
|
||||
xchgl %eax, 4(%esp) /* %eax = return addr, store %eax */
|
||||
movl %eax, 0(%esp) /* 0(%esp) = return address */
|
||||
movl $(EB_OPCODE_MAIN|EB_USE_INTERNAL_STACK|EB_SKIP_OPCODE), %eax
|
||||
xchgl %eax, 4(%esp) /* 4(%esp) = opcode, restore %eax */
|
||||
1:
|
||||
/* Run decompressor if necessary */
|
||||
pushl %eax
|
||||
movl $decompress, %eax
|
||||
testl %eax, %eax
|
||||
jz 1f
|
||||
call decompress
|
||||
1: popl %eax
|
||||
|
||||
/* Make in_call to Etherboot */
|
||||
jmp _prefix_in_call
|
||||
|
||||
/******************************************************************************
|
||||
* Entered in real mode
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifdef CODE16
|
||||
.code16
|
||||
rmode:
|
||||
pushw %ax /* Padding */
|
||||
pushw %bp
|
||||
movw %sp, %bp
|
||||
cmpw $0, 6(%bp) /* Conventional entry point used? */
|
||||
jne 1f
|
||||
/* Entered via conventional entry point: set up stack */
|
||||
pushw %ax
|
||||
movw 6(%bp), %ax
|
||||
movw %ax, 2(%bp) /* Move return address down */
|
||||
movl $(EB_OPCODE_MAIN|EB_USE_INTERNAL_STACK|EB_SKIP_OPCODE), 4(%bp)
|
||||
popw %ax
|
||||
popw %bp
|
||||
jmp 2f
|
||||
1: /* Entered via callback entry point: do nothing */
|
||||
popw %bp
|
||||
popw %ax
|
||||
2:
|
||||
/* Preserve registers */
|
||||
pushw %ds
|
||||
pushl %eax
|
||||
|
||||
/* Run decompressor if necessary. Decompressor is 32-bit
|
||||
* code, so we must switch to pmode first. Save and restore
|
||||
* GDT over transition to pmode.
|
||||
*/
|
||||
movl $decompress, %eax
|
||||
testl %eax, %eax
|
||||
jz 1f
|
||||
pushw %ds
|
||||
pushw %es
|
||||
pushw %fs
|
||||
pushw %gs
|
||||
subw $8, %sp
|
||||
pushw %bp
|
||||
movw %sp, %bp
|
||||
sgdt 2(%bp)
|
||||
pushw %ss /* Store params for _prot_to_real */
|
||||
pushw %cs
|
||||
call _prefix_real_to_prot
|
||||
.code32
|
||||
call decompress
|
||||
call _prefix_prot_to_real
|
||||
.code16
|
||||
popw %ax /* skip */
|
||||
popw %ax /* skip */
|
||||
lgdt 2(%bp)
|
||||
popw %bp
|
||||
addw $8, %sp
|
||||
popw %gs
|
||||
popw %fs
|
||||
popw %es
|
||||
popw %ds
|
||||
1:
|
||||
|
||||
/* Set rm_etherboot_location */
|
||||
xorl %eax, %eax
|
||||
movw %cs, %ax
|
||||
movw %ax, %ds
|
||||
shll $4, %eax
|
||||
addl $_prefix_size, %eax
|
||||
movl %eax, _prefix_rm_etherboot_location
|
||||
|
||||
/* Restore registers */
|
||||
popl %eax
|
||||
popw %ds
|
||||
|
||||
/* Make real-mode in_call to Etherboot */
|
||||
jmp _prefix_rm_in_call
|
||||
#endif /* CODE16 */
|
||||
|
||||
/******************************************************************************
|
||||
* Utility routines that can be called by the "prefix".
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifdef CODE16
|
||||
|
||||
/* Prelocate code: either to an area at the top of free base memory.
|
||||
* Switch stacks to use the stack within the resulting
|
||||
* Etherboot image.
|
||||
*
|
||||
* On entry, %cs:0000 must be the start of the prefix: this is used to
|
||||
* locate the code to be copied.
|
||||
*
|
||||
* This routine takes a single word parameter: the number of bytes to
|
||||
* be transferred from the old stack to the new stack (excluding the
|
||||
* return address and this parameter itself, which will always be
|
||||
* copied). If this value is negative, the stacks will not be
|
||||
* switched.
|
||||
*
|
||||
* Control will "return" to the appropriate point in the relocated
|
||||
* image.
|
||||
*/
|
||||
|
||||
#define PRELOC_PRESERVE ( 20 )
|
||||
#define PRELOC_OFFSET_RETADDR ( PRELOC_PRESERVE )
|
||||
#define PRELOC_OFFSET_RETADDR_E ( PRELOC_OFFSET_RETADDR + 4 )
|
||||
#define PRELOC_OFFSET_COPY ( PRELOC_OFFSET_RETADDR_E )
|
||||
#define PRELOC_OFFSET_COPY_E ( PRELOC_OFFSET_COPY + 2 )
|
||||
|
||||
#define PRELOC_ALWAYS_COPY ( PRELOC_OFFSET_COPY_E )
|
||||
|
||||
.code16
|
||||
.globl prelocate
|
||||
prelocate:
|
||||
/* Pad to allow for expansion of return address */
|
||||
pushw %ax
|
||||
|
||||
/* Preserve registers */
|
||||
pushaw
|
||||
pushw %ds
|
||||
pushw %es
|
||||
|
||||
/* Claim an area of base memory from the BIOS and put the
|
||||
* payload there.
|
||||
*/
|
||||
movw $0x40, %bx
|
||||
movw %bx, %es
|
||||
movw %es:(0x13), %bx /* FBMS in kb to %ax */
|
||||
shlw $6, %bx /* ... in paragraphs */
|
||||
subw $_image_size_pgh, %bx /* Subtract space for image */
|
||||
shrw $6, %bx /* Round down to nearest kb */
|
||||
movw %bx, %es:(0x13) /* ...and claim memory from BIOS */
|
||||
shlw $6, %bx
|
||||
|
||||
/* At this point %bx contains the segment address for the
|
||||
* start of the image (image = prefix + runtime).
|
||||
*/
|
||||
|
||||
/* Switch stacks */
|
||||
movw %ss, %ax
|
||||
movw %ax, %ds
|
||||
movw %sp, %si /* %ds:si = current %ss:sp */
|
||||
movw %ss:PRELOC_OFFSET_COPY(%si), %cx
|
||||
testw %cx, %cx
|
||||
js 1f
|
||||
leaw _stack_offset_pgh(%bx), %ax /* %ax = new %ss */
|
||||
movw %ax, %es
|
||||
movw $_stack_size, %di
|
||||
addw $PRELOC_ALWAYS_COPY, %cx
|
||||
subw %cx, %di /* %es:di = new %ss:sp */
|
||||
movw %ax, %ss /* Set new %ss:sp */
|
||||
movw %di, %sp
|
||||
cld
|
||||
rep movsb /* Copy stack contents */
|
||||
1:
|
||||
|
||||
/* Do the image copy backwards, since if there's overlap with
|
||||
* a forward copy then it means we're going to get trashed
|
||||
* during the copy anyway...
|
||||
*/
|
||||
pushal /* Preserve 32-bit registers */
|
||||
movw %bx, %es /* Destination base for copy */
|
||||
pushw %cs
|
||||
popw %ds /* Source base for copy */
|
||||
movl $_verbatim_size-1, %ecx /* Offset to last byte */
|
||||
movl %ecx, %esi
|
||||
movl %ecx, %edi
|
||||
incl %ecx /* Length */
|
||||
std /* Backwards copy of binary */
|
||||
ADDR32 rep movsb
|
||||
cld
|
||||
popal /* Restore 32-bit registers */
|
||||
|
||||
/* Store (%bx<<4) as image_basemem to be picked up by
|
||||
* basemem.c. Also store image_size, since there's no other
|
||||
* way that we can later know how much memory we allocated.
|
||||
* (_zfile_size is unavailable when rt2 is linked).
|
||||
*/
|
||||
pushl %eax
|
||||
xorl %eax, %eax
|
||||
movw %bx, %ax
|
||||
shll $4, %eax
|
||||
movl %eax, %es:_prefix_image_basemem
|
||||
movl $_image_size, %es:_prefix_image_basemem_size
|
||||
popl %eax
|
||||
|
||||
/* Expand original near return address into far return to new
|
||||
* code location.
|
||||
*/
|
||||
movw %sp, %bp
|
||||
xchgw %bx, (PRELOC_OFFSET_RETADDR+2)(%bp)
|
||||
movw %bx, (PRELOC_OFFSET_RETADDR+0)(%bp)
|
||||
|
||||
/* Restore registers and return */
|
||||
popw %es
|
||||
popw %ds
|
||||
popaw
|
||||
lret /* Jump to relocated code */
|
||||
|
||||
/* Utility routine to free base memory allocated by prelocate.
|
||||
* Ensure that said memory is not in use (e.g. for the CPU
|
||||
* stack) before calling this routine.
|
||||
*/
|
||||
.globl deprelocate
|
||||
deprelocate:
|
||||
/* Claim an area of base memory from the BIOS and put the
|
||||
* payload there.
|
||||
*/
|
||||
pushw %ax
|
||||
pushw %es
|
||||
movw $0x40, %ax
|
||||
movw %ax, %es
|
||||
movw %es:(0x13), %ax /* FBMS in kb to %ax */
|
||||
shlw $6, %ax /* ... in paragraphs */
|
||||
addw $_image_size_pgh+0x40-1, %ax /* Add space for image and... */
|
||||
shrw $6, %ax /* ...round up to nearest kb */
|
||||
movw %ax, %es:(0x13) /* Give memory back to BIOS */
|
||||
popw %es
|
||||
popw %ax
|
||||
ret
|
||||
|
||||
#endif /* CODE16 */
|
||||
@@ -3,6 +3,31 @@
|
||||
* an NBP to the PXE stack and for starting an NBP from the PXE stack.
|
||||
*/
|
||||
|
||||
#warning "pxe_callbacks.c is temporarily broken"
|
||||
|
||||
void xstartpxe ( void ) {
|
||||
}
|
||||
|
||||
void install_pxe_stack ( void ) {
|
||||
}
|
||||
|
||||
void remove_pxe_stack ( void ) {
|
||||
}
|
||||
|
||||
void hook_pxe_stack ( void ) {
|
||||
}
|
||||
|
||||
void unhook_pxe_stack ( void ) {
|
||||
}
|
||||
|
||||
void pxe_in_call ( void ) {
|
||||
}
|
||||
|
||||
void use_undi_ds_for_rm_stack ( void ) {
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
#ifdef PXE_EXPORT
|
||||
|
||||
#include "etherboot.h"
|
||||
@@ -362,3 +387,5 @@ __asm__ ( ".globl _pxe_stack_t_size" );
|
||||
__asm__ ( ".equ _pxe_stack_t_size, 0" );
|
||||
|
||||
#endif /* PXE_EXPORT */
|
||||
|
||||
#endif /* 0 */
|
||||
|
||||
@@ -3,9 +3,25 @@
|
||||
* Initial version by Michael Brown <mbrown@fensystems.co.uk>, January 2004.
|
||||
*/
|
||||
|
||||
#include "etherboot.h"
|
||||
#include "realmode.h"
|
||||
#include "segoff.h"
|
||||
|
||||
/*
|
||||
* Copy data to/from base memory.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef KEEP_IT_REAL
|
||||
|
||||
void memcpy_to_real ( segoff_t dest, void *src, size_t n ) {
|
||||
|
||||
}
|
||||
|
||||
void memcpy_from_real ( void *dest, segoff_t src, size_t n ) {
|
||||
|
||||
}
|
||||
|
||||
#endif /* KEEP_IT_REAL */
|
||||
|
||||
|
||||
#define RM_STACK_SIZE ( 0x1000 )
|
||||
|
||||
@@ -16,133 +32,9 @@
|
||||
static void work_around_gcc_bug ( void ) __attribute__ ((used));
|
||||
static void work_around_gcc_bug ( void ) {
|
||||
/* Export _real_mode_stack_size as absolute linker symbol */
|
||||
__asm__ ( ".globl _real_mode_stack_size" );
|
||||
__asm__ ( ".equ _real_mode_stack_size, %c0" : : "i" (RM_STACK_SIZE) );
|
||||
__asm__ ( ".globl real_mode_stack_size" );
|
||||
__asm__ ( ".equ real_mode_stack_size, %c0" : : "i" (RM_STACK_SIZE) );
|
||||
}
|
||||
|
||||
/* While Etherboot remains in base memory the real-mode stack is
|
||||
* placed in the Etherboot main stack. The first allocation or
|
||||
* deallocation of base memory will cause a 'proper' real-mode stack
|
||||
* to be allocated. This will happen before Etherboot is relocated to
|
||||
* high memory.
|
||||
*/
|
||||
uint32_t real_mode_stack = 0;
|
||||
size_t real_mode_stack_size = RM_STACK_SIZE;
|
||||
int lock_real_mode_stack = 0; /* Set to make stack immobile */
|
||||
|
||||
/* Make a call to a real-mode code block.
|
||||
*/
|
||||
|
||||
/* These is the structure that exists on the stack as the paramters
|
||||
* passed in to _real_call. We pass a pointer to this struct to
|
||||
* prepare_real_call(), to save stack space.
|
||||
*/
|
||||
typedef struct {
|
||||
void *fragment;
|
||||
int fragment_len;
|
||||
void *in_stack;
|
||||
int in_stack_len;
|
||||
void *out_stack;
|
||||
int out_stack_len;
|
||||
} real_call_params_t;
|
||||
|
||||
uint32_t prepare_real_call ( real_call_params_t *p,
|
||||
int local_stack_len, char *local_stack ) {
|
||||
char *stack_base;
|
||||
char *stack_end;
|
||||
char *stack;
|
||||
char *s;
|
||||
prot_to_real_params_t *p2r_params;
|
||||
real_to_prot_params_t *r2p_params;
|
||||
|
||||
/* Work out where we're putting the stack */
|
||||
if ( virt_to_phys(local_stack) < 0xa0000 ) {
|
||||
/* Current stack is in base memory. We can therefore
|
||||
* use it directly, with a constraint on the size that
|
||||
* we don't know; assume that we can use up to
|
||||
* real_mode_stack_size. (Not a valid assumption, but
|
||||
* it will do).
|
||||
*/
|
||||
stack_end = local_stack + local_stack_len;
|
||||
stack_base = stack_end - real_mode_stack_size;
|
||||
} else {
|
||||
if (!real_mode_stack) {
|
||||
allot_real_mode_stack();
|
||||
}
|
||||
/* Use the allocated real-mode stack in base memory.
|
||||
* This has fixed start and end points.
|
||||
*/
|
||||
stack_base = phys_to_virt(real_mode_stack);
|
||||
stack_end = stack_base + real_mode_stack_size;
|
||||
}
|
||||
s = stack = stack_end - local_stack_len;
|
||||
|
||||
/* Compile input stack and trampoline code to stack */
|
||||
if ( p->in_stack_len ) {
|
||||
memcpy ( s, p->in_stack, p->in_stack_len );
|
||||
s += p->in_stack_len;
|
||||
}
|
||||
memcpy ( s, _prot_to_real_prefix, prot_to_real_prefix_size );
|
||||
s += prot_to_real_prefix_size;
|
||||
p2r_params = (prot_to_real_params_t*) ( s - sizeof(*p2r_params) );
|
||||
memcpy ( s, p->fragment, p->fragment_len );
|
||||
s += p->fragment_len;
|
||||
memcpy ( s, _real_to_prot_suffix, real_to_prot_suffix_size );
|
||||
s += real_to_prot_suffix_size;
|
||||
r2p_params = (real_to_prot_params_t*) ( s - sizeof(*r2p_params) );
|
||||
|
||||
/* Set parameters within compiled stack */
|
||||
p2r_params->ss = p2r_params->cs = SEGMENT ( stack_base );
|
||||
p2r_params->esp = virt_to_phys ( stack );
|
||||
p2r_params->r2p_params = virt_to_phys ( r2p_params );
|
||||
r2p_params->out_stack = ( p->out_stack == NULL ) ?
|
||||
0 : virt_to_phys ( p->out_stack );
|
||||
r2p_params->out_stack_len = p->out_stack_len;
|
||||
|
||||
return virt_to_phys ( stack + p->in_stack_len );
|
||||
}
|
||||
|
||||
|
||||
/* Parameters are not genuinely unused; they are passed to
|
||||
* prepare_real_call() as part of a real_call_params_t struct.
|
||||
*/
|
||||
uint16_t _real_call ( void *fragment, int fragment_len,
|
||||
void *in_stack __unused, int in_stack_len,
|
||||
void *out_stack __unused, int out_stack_len __unused ) {
|
||||
uint16_t retval;
|
||||
|
||||
/* This code is basically equivalent to
|
||||
*
|
||||
* uint32_t trampoline;
|
||||
* char local_stack[ in_stack_len + prot_to_real_prefix_size +
|
||||
* fragment_len + real_to_prot_suffix_size ];
|
||||
* trampoline = prepare_real_call ( &fragment, local_stack );
|
||||
* __asm__ ( "call _virt_to_phys\n\t"
|
||||
* "call %%eax\n\t"
|
||||
* "call _phys_to_virt\n\t"
|
||||
* : "=a" (retval) : "0" (trampoline) );
|
||||
*
|
||||
* but implemented in assembly to avoid problems with not
|
||||
* being certain exactly how gcc handles %esp.
|
||||
*/
|
||||
|
||||
__asm__ ( "pushl %%ebp\n\t"
|
||||
"movl %%esp, %%ebp\n\t" /* %esp preserved via %ebp */
|
||||
"subl %%ecx, %%esp\n\t" /* space for inline RM stack */
|
||||
"pushl %%esp\n\t" /* set up RM stack */
|
||||
"pushl %%ecx\n\t"
|
||||
"pushl %%eax\n\t"
|
||||
"call prepare_real_call\n\t" /* %eax = trampoline addr */
|
||||
"addl $12, %%esp\n\t"
|
||||
"call _virt_to_phys\n\t" /* switch to phys addr */
|
||||
"call *%%eax\n\t" /* call to trampoline */
|
||||
"call _phys_to_virt\n\t" /* switch to virt addr */
|
||||
"movl %%ebp, %%esp\n\t" /* restore %esp & %ebp */
|
||||
"popl %%ebp\n\t"
|
||||
: "=a" ( retval )
|
||||
: "0" ( &fragment )
|
||||
, "c" ( ( ( in_stack_len + prot_to_real_prefix_size +
|
||||
fragment_len + real_to_prot_suffix_size )
|
||||
+ 0x3 ) & ~0x3 ) );
|
||||
return retval;
|
||||
}
|
||||
char *real_mode_stack;
|
||||
int lock_real_mode_stack;
|
||||
|
||||
@@ -31,570 +31,6 @@
|
||||
#define LJMPI(x) ljmp x
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* REAL-MODE CALLBACK INTERFACE
|
||||
*
|
||||
* This must be copied down to base memory in order for external
|
||||
* programs to be able to make calls in to Etherboot. Store the
|
||||
* current physical address of Etherboot (i.e. virt_to_phys(_text)) in
|
||||
* (uint32_t)rm_etherboot_location, then copy
|
||||
* (uint16_t)rm_callback_interface_size bytes starting at
|
||||
* &((void)rm_callback_interface).
|
||||
*
|
||||
* There are two defined entry points:
|
||||
* Offset RM_IN_CALL = 0 Near call entry point
|
||||
* Offset RM_IN_CALL_FAR = 2 Far call entry point
|
||||
*
|
||||
* Note that the routines _prot_to_real and _real_to_prot double as
|
||||
* trampoline fragments for external calls (calls from Etherboot to
|
||||
* real-mode code). _prot_to_real does not automatically re-enable
|
||||
* interrupts; this is to allow for the potential of using Etherboot
|
||||
* code as an ISR. _real_to_prot does automatically disable
|
||||
* interrupts, since we don't have a protected-mode IDT.
|
||||
****************************************************************************
|
||||
*/
|
||||
|
||||
.globl rm_callback_interface
|
||||
.code16
|
||||
rm_callback_interface:
|
||||
.globl _rm_in_call
|
||||
_rm_in_call:
|
||||
jmp _real_in_call
|
||||
.globl _rm_in_call_far
|
||||
_rm_in_call_far:
|
||||
jmp _real_in_call_far
|
||||
|
||||
/****************************************************************************
|
||||
* _real_in_call
|
||||
*
|
||||
* Parameters:
|
||||
* 16-bit real-mode near/far return address (implicit from [l]call
|
||||
* to routine) Other parameters as for _in_call_far().
|
||||
*
|
||||
* This routine will convert the 16-bit real-mode far return address
|
||||
* to a 32-bit real-mode far return address, switch to protected mode
|
||||
* using _real_to_prot and call in to _in_call_far.
|
||||
****************************************************************************
|
||||
*/
|
||||
|
||||
#define RIC_PRESERVE ( 8 )
|
||||
#define RIC_OFFSET_CALLADDR ( RIC_PRESERVE )
|
||||
#define RIC_OFFSET_CALLADDR_E ( RIC_OFFSET_CALLADDR + 4 )
|
||||
#define RIC_OFFSET_CONTADDR ( RIC_OFFSET_CALLADDR_E )
|
||||
#define RIC_OFFSET_CONTADDR_E ( RIC_OFFSET_CONTADDR + 4 )
|
||||
#define RIC_OFFSET_OPCODE ( RIC_OFFSET_CONTADDR_E )
|
||||
#define RIC_OFFSET_OPCODE_E ( RIC_OFFSET_OPCODE + 4 )
|
||||
#define RIC_OFFSET_SEG_REGS ( RIC_OFFSET_OPCODE_E )
|
||||
#define RIC_OFFSET_SEG_REGS_E ( RIC_OFFSET_SEG_REGS + ( NUM_SEG_REGS * 2 ) )
|
||||
#define RIC_OFFSET_PAD ( RIC_OFFSET_SEG_REGS_E )
|
||||
#define RIC_OFFSET_PAD_E ( RIC_OFFSET_PAD + 2 )
|
||||
#define RIC_OFFSET_FLAGS ( RIC_OFFSET_PAD_E )
|
||||
#define RIC_OFFSET_FLAGS_E ( RIC_OFFSET_FLAGS + 2 )
|
||||
#define RIC_OFFSET_RETADDR ( RIC_OFFSET_FLAGS_E )
|
||||
#define RIC_OFFSET_RETADDR_E ( RIC_OFFSET_RETADDR + 4 )
|
||||
#define RIC_OFFSET_ORIG_OPCODE ( RIC_OFFSET_RETADDR_E )
|
||||
#define RIC_INSERT_LENGTH ( RIC_OFFSET_OPCODE_E - RIC_OFFSET_CALLADDR )
|
||||
|
||||
.code16
|
||||
_real_in_call:
|
||||
/* Expand near return address to far return address
|
||||
*/
|
||||
pushw %ax /* Extend stack, store %ax */
|
||||
pushfw
|
||||
pushw %bp
|
||||
movw %sp, %bp
|
||||
movw %cs, %ax
|
||||
xchgw %ax, 6(%bp)
|
||||
xchgw %ax, 4(%bp) /* also restores %ax */
|
||||
popw %bp
|
||||
popfw
|
||||
/* Fall through to _real_in_call_far */
|
||||
|
||||
_real_in_call_far:
|
||||
/* Store flags and pad */
|
||||
pushfw
|
||||
pushw %ax
|
||||
|
||||
/* Store segment registers. Order matches that of seg_regs_t */
|
||||
pushw %gs
|
||||
pushw %fs
|
||||
pushw %es
|
||||
pushw %ds
|
||||
pushw %ss
|
||||
pushw %cs
|
||||
|
||||
/* Switch to protected mode */
|
||||
call _real_to_prot
|
||||
.code32
|
||||
|
||||
/* Allow space for expanded stack */
|
||||
subl $RIC_INSERT_LENGTH, %esp
|
||||
|
||||
/* Store temporary registers */
|
||||
pushl %ebp
|
||||
pushl %eax
|
||||
|
||||
/* Copy opcode, set EB_CALL_FROM_REAL_MODE and EP_SKIP_OPCODE.
|
||||
* Copy it because _in_call() and i386_in_call() expect it at
|
||||
* a fixed position, not as part of the va_list.
|
||||
*/
|
||||
movl RIC_OFFSET_ORIG_OPCODE(%esp), %eax
|
||||
orl $(EB_CALL_FROM_REAL_MODE|EB_SKIP_OPCODE), %eax
|
||||
movl %eax, RIC_OFFSET_OPCODE(%esp)
|
||||
|
||||
/* Set up call and return addresses */
|
||||
call 1f
|
||||
1: popl %ebp
|
||||
subl $1b, %ebp /* %ebp = offset */
|
||||
movl rm_etherboot_location(%ebp), %eax /* Etherboot phys addr */
|
||||
subl $_text, %eax
|
||||
addl $_in_call, %eax /* _in_call phys addr */
|
||||
movl %eax, RIC_OFFSET_CALLADDR(%esp)
|
||||
leal 2f(%ebp), %eax /* continuation address */
|
||||
movl %eax, RIC_OFFSET_CONTADDR(%esp)
|
||||
|
||||
/* Restore temporary registers */
|
||||
popl %eax
|
||||
popl %ebp
|
||||
|
||||
/* Call to _in_call */
|
||||
ret
|
||||
/* opcode will be popped automatically thanks to EB_SKIP_OPCODE */
|
||||
|
||||
2: /* Continuation point */
|
||||
call _prot_to_real /* Return to real mode */
|
||||
/* Note: the first two words of our segment register store
|
||||
* happens to be exactly what we need to pass as parameters to
|
||||
* _prot_to_real.
|
||||
*/
|
||||
.code16
|
||||
popw %ds /* Restore segment registers */
|
||||
popw %ds /* (skip cs&ss since these */
|
||||
popw %ds /* have already been set by */
|
||||
popw %es /* _prot_to_real */
|
||||
popw %fs
|
||||
popw %gs
|
||||
addw $2, %sp /* skip pad */
|
||||
|
||||
/* Check for EB_SKIP_OPCODE */
|
||||
pushw %bp
|
||||
movw %sp, %bp
|
||||
testl $EB_SKIP_OPCODE, 6(%bp)
|
||||
popw %bp
|
||||
jnz 1f
|
||||
/* Normal return */
|
||||
popfw /* Restore interrupt status */
|
||||
lret /* Back to caller */
|
||||
1: /* Return and skip opcode */
|
||||
popfw
|
||||
lret $4
|
||||
|
||||
/****************************************************************************
|
||||
* rm_etherboot_location: the current physical location of Etherboot.
|
||||
* Needed so that real-mode callback routines can locate Etherboot.
|
||||
****************************************************************************
|
||||
*/
|
||||
.globl rm_etherboot_location
|
||||
rm_etherboot_location: .long 0
|
||||
|
||||
/****************************************************************************
|
||||
* _prot_to_real_prefix
|
||||
*
|
||||
* Trampoline fragment. Switch from 32-bit protected mode with flat
|
||||
* physical addresses to 16-bit real mode. Store registers in the
|
||||
* trampoline for restoration by _real_to_prot_suffix. Switch to
|
||||
* stack in base memory.
|
||||
****************************************************************************
|
||||
*/
|
||||
|
||||
.globl _prot_to_real_prefix
|
||||
.code32
|
||||
_prot_to_real_prefix:
|
||||
/* Registers to preserve */
|
||||
pushl %ebx
|
||||
pushl %esi
|
||||
pushl %edi
|
||||
pushl %ebp
|
||||
|
||||
/* Calculate offset */
|
||||
call 1f
|
||||
1: popl %ebp
|
||||
subl $1b, %ebp /* %ebp = offset for labels in p2r*/
|
||||
|
||||
/* Preserve registers and return address in r2p_params */
|
||||
movl p2r_r2p_params(%ebp), %ebx
|
||||
subl $r2p_params, %ebx /* %ebx = offset for labels in r2p */
|
||||
popl r2p_ebp(%ebx)
|
||||
popl r2p_edi(%ebx)
|
||||
popl r2p_esi(%ebx)
|
||||
popl r2p_ebx(%ebx)
|
||||
popl r2p_ret_addr(%ebx)
|
||||
movl %esp, r2p_esp(%ebx)
|
||||
|
||||
/* Switch stacks */
|
||||
movl p2r_esp(%ebp), %esp
|
||||
|
||||
/* Switch to real mode */
|
||||
pushl p2r_segments(%ebp)
|
||||
call _prot_to_real
|
||||
.code16
|
||||
addw $4, %sp
|
||||
|
||||
/* Fall through to next trampoline fragment */
|
||||
jmp _prot_to_real_prefix_end
|
||||
|
||||
/****************************************************************************
|
||||
* _prot_to_real
|
||||
*
|
||||
* Switch from 32-bit protected mode with flat physical addresses to
|
||||
* 16-bit real mode. Stack and code must be in base memory when
|
||||
* called. %cs, %ss, %eip, %esp are changed to real-mode values,
|
||||
* other segment registers are destroyed, all other registers are
|
||||
* preserved. Interrupts are *not* enabled.
|
||||
*
|
||||
* Parameters:
|
||||
* %cs Real-mode code segment (word)
|
||||
* %ss Real-mode stack segment (word)
|
||||
****************************************************************************
|
||||
*/
|
||||
|
||||
#define P2R_PRESERVE ( 12 )
|
||||
#define P2R_OFFSET_RETADDR ( P2R_PRESERVE )
|
||||
#define P2R_OFFSET_RETADDR_E ( P2R_OFFSET_RETADDR + 4 )
|
||||
#define P2R_OFFSET_CS ( P2R_OFFSET_RETADDR_E )
|
||||
#define P2R_OFFSET_CS_E ( P2R_OFFSET_CS + 2 )
|
||||
#define P2R_OFFSET_SS ( P2R_OFFSET_CS_E )
|
||||
#define P2R_OFFSET_SS_E ( P2R_OFFSET_SS + 2 )
|
||||
|
||||
.globl _prot_to_real
|
||||
.code32
|
||||
_prot_to_real:
|
||||
/* Preserve registers */
|
||||
pushl %ebp
|
||||
pushl %ebx
|
||||
pushl %eax
|
||||
|
||||
/* Calculate offset */
|
||||
call 1f
|
||||
1: popl %ebp
|
||||
subl $1b, %ebp /* %ebp = offset for labels in p2r*/
|
||||
|
||||
/* Set up GDT with real-mode limits and appropriate bases for
|
||||
* real-mode %cs and %ss. Set up protected-mode continuation
|
||||
* point on stack.
|
||||
*/
|
||||
/* Fixup GDT */
|
||||
leal p2r_gdt(%ebp), %eax
|
||||
movl %eax, p2r_gdt_addr(%ebp)
|
||||
|
||||
/* Calculate CS base address: set GDT code segment, adjust
|
||||
* return address, set up continuation address on stack.
|
||||
*/
|
||||
movzwl P2R_OFFSET_CS(%esp), %eax
|
||||
shll $4, %eax
|
||||
/* Change return address to real-mode far address */
|
||||
subl %eax, P2R_OFFSET_RETADDR(%esp)
|
||||
movl %eax, %ebx
|
||||
shrl $4, %ebx
|
||||
movw %bx, (P2R_OFFSET_RETADDR+2)(%esp)
|
||||
/* First real mode address */
|
||||
movl %eax, %ebx
|
||||
shrl $4, %ebx
|
||||
pushw %bx
|
||||
leal 3f(%ebp), %ebx
|
||||
subl %eax, %ebx
|
||||
pushw %bx
|
||||
/* Continuation address */
|
||||
pushl $(p2r_rmcs - p2r_gdt)
|
||||
leal 2f(%ebp), %ebx
|
||||
subl %eax, %ebx
|
||||
pushl %ebx
|
||||
/* Code segment in GDT */
|
||||
movw %ax, (p2r_rmcs+2)(%ebp)
|
||||
shrl $16, %eax /* Remainder of cs base addr */
|
||||
movb %al, (p2r_rmcs+4)(%ebp)
|
||||
movb %ah, (p2r_rmcs+7)(%ebp)
|
||||
|
||||
/* Calculate SS base address: set GDT data segment, retain to
|
||||
* use for adjusting %esp.
|
||||
*/
|
||||
movzwl (12+P2R_OFFSET_SS)(%esp), %eax /* Set ss base address */
|
||||
shll $4, %eax
|
||||
movw %ax, (p2r_rmds+2)(%ebp)
|
||||
movl %eax, %ebx
|
||||
shrl $16, %ebx
|
||||
movb %bl, (p2r_rmds+4)(%ebp)
|
||||
movb %bh, (p2r_rmds+7)(%ebp)
|
||||
|
||||
/* Load GDT */
|
||||
lgdt p2r_gdt(%ebp)
|
||||
/* Reload all segment registers and adjust %esp */
|
||||
movw $(p2r_rmds - p2r_gdt), %bx /* Pmode DS */
|
||||
movw %bx, %ss
|
||||
subl %eax, %esp /* %esp now less than 0x10000 */
|
||||
movw %bx, %ds
|
||||
movw %bx, %es
|
||||
movw %bx, %fs
|
||||
movw %bx, %gs
|
||||
lret /* %cs:eip */
|
||||
2: /* Segment registers now have 16-bit limits. */
|
||||
.code16
|
||||
|
||||
/* Switch to real mode */
|
||||
movl %cr0, %ebx
|
||||
andb $0!CR0_PE, %bl
|
||||
movl %ebx, %cr0
|
||||
|
||||
/* Make intersegment jmp to flush the processor pipeline
|
||||
* and reload %cs:%eip (to clear upper 16 bits of %eip).
|
||||
*/
|
||||
lret
|
||||
3:
|
||||
|
||||
/* Load real-mode segment value to %ss. %sp already OK */
|
||||
shrl $4, %eax
|
||||
movw %ax, %ss
|
||||
|
||||
/* Restore registers */
|
||||
popl %eax
|
||||
popl %ebx
|
||||
popl %ebp
|
||||
|
||||
/* Return to caller in real-mode */
|
||||
lret
|
||||
|
||||
#ifdef FLATTEN_REAL_MODE
|
||||
#define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x8f
|
||||
#else
|
||||
#define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x00
|
||||
#endif
|
||||
|
||||
p2r_gdt:
|
||||
p2r_gdtarg:
|
||||
p2r_gdt_limit: .word p2r_gdt_end - p2r_gdt - 1
|
||||
p2r_gdt_addr: .long 0
|
||||
p2r_gdt_padding: .word 0
|
||||
p2r_rmcs:
|
||||
/* 16 bit real mode code segment */
|
||||
.word 0xffff,(0&0xffff)
|
||||
.byte (0>>16),0x9b,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24)
|
||||
p2r_rmds:
|
||||
/* 16 bit real mode data segment */
|
||||
.word 0xffff,(0&0xffff)
|
||||
.byte (0>>16),0x93,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24)
|
||||
p2r_gdt_end:
|
||||
|
||||
/* This is the end of the trampoline prefix code. When used
|
||||
* as a prefix, fall through to the following code in the
|
||||
* trampoline.
|
||||
*/
|
||||
p2r_params: /* Structure must match prot_to_real_params_t in realmode.h */
|
||||
p2r_esp: .long 0
|
||||
p2r_segments:
|
||||
p2r_cs: .word 0
|
||||
p2r_ss: .word 0
|
||||
p2r_r2p_params: .long 0
|
||||
.globl _prot_to_real_prefix_end
|
||||
_prot_to_real_prefix_end:
|
||||
|
||||
.globl _prot_to_real_prefix_size
|
||||
.equ _prot_to_real_prefix_size, _prot_to_real_prefix_end - _prot_to_real_prefix
|
||||
.globl prot_to_real_prefix_size
|
||||
prot_to_real_prefix_size:
|
||||
.word _prot_to_real_prefix_size
|
||||
|
||||
/****************************************************************************
|
||||
* _real_to_prot_suffix
|
||||
*
|
||||
* Trampoline fragment. Switch from 16-bit real-mode to 32-bit
|
||||
* protected mode with flat physical addresses. Copy returned stack
|
||||
* parameters to output_stack. Restore registers preserved by
|
||||
* _prot_to_real_prefix. Restore stack to previous location.
|
||||
****************************************************************************
|
||||
*/
|
||||
|
||||
.globl _real_to_prot_suffix
|
||||
.code16
|
||||
_real_to_prot_suffix:
|
||||
|
||||
/* Switch to protected mode */
|
||||
call _real_to_prot
|
||||
.code32
|
||||
|
||||
/* Calculate offset */
|
||||
call 1f
|
||||
1: popl %ebp
|
||||
subl $1b, %ebp /* %ebp = offset for labels in r2p */
|
||||
|
||||
/* Copy stack to out_stack */
|
||||
movl r2p_out_stack(%ebp), %edi
|
||||
movl r2p_out_stack_len(%ebp), %ecx
|
||||
movl %esp, %esi
|
||||
cld
|
||||
rep movsb
|
||||
|
||||
/* Switch back to original stack */
|
||||
movl r2p_esp(%ebp), %esp
|
||||
|
||||
/* Restore registers and return */
|
||||
pushl r2p_ret_addr(%ebp) /* Set up return address on stack */
|
||||
movl r2p_ebx(%ebp), %ebx
|
||||
movl r2p_esi(%ebp), %esi
|
||||
movl r2p_edi(%ebp), %edi
|
||||
movl r2p_ebp(%ebp), %ebp
|
||||
ret
|
||||
|
||||
/****************************************************************************
|
||||
* _real_to_prot
|
||||
*
|
||||
* Switch from 16-bit real-mode to 32-bit protected mode with flat
|
||||
* physical addresses. All segment registers are destroyed, %eip and
|
||||
* %esp are changed to flat physical values, all other registers are
|
||||
* preserved. Interrupts are disabled.
|
||||
*
|
||||
* Parameters: none
|
||||
****************************************************************************
|
||||
*/
|
||||
|
||||
#define R2P_PRESERVE ( 12 )
|
||||
#define R2P_OFFSET_RETADDR ( R2P_PRESERVE )
|
||||
#define R2P_OFFSET_ORIG_RETADDR ( R2P_OFFSET_RETADDR + 2 )
|
||||
|
||||
.globl _real_to_prot
|
||||
.code16
|
||||
_real_to_prot:
|
||||
/* Disable interrupts */
|
||||
cli
|
||||
/* zero extend the return address */
|
||||
pushw $0
|
||||
|
||||
/* Preserve registers */
|
||||
pushl %ebp
|
||||
pushl %ebx
|
||||
pushl %eax
|
||||
|
||||
/* Convert 16-bit real-mode near return address to
|
||||
* 32-bit pmode physical near return address
|
||||
*/
|
||||
movw %sp, %bp
|
||||
xorl %ebx, %ebx
|
||||
push %cs
|
||||
popw %bx
|
||||
movw %bx, %ds
|
||||
shll $4, %ebx
|
||||
movzwl %ss:R2P_OFFSET_ORIG_RETADDR(%bp), %eax
|
||||
addl %ebx, %eax
|
||||
movl %eax, %ss:(R2P_OFFSET_RETADDR)(%bp)
|
||||
|
||||
/* Store the code segment physical base address in %ebp */
|
||||
movl %ebx, %ebp
|
||||
|
||||
/* Find the offset within the code segment that I am running at */
|
||||
xorl %ebx, %ebx
|
||||
call 1f
|
||||
1: popw %bx
|
||||
|
||||
/* Set up GDT */
|
||||
leal (r2p_gdt-1b)(%bx), %eax /* %ds:ebx = %ds:bx = &(r2p_gdt) */
|
||||
addl %ebp, %eax /* %eax = &r2p_gdt (physical) */
|
||||
movl %eax, %ds:(r2p_gdt-1b+2)(%bx) /* Set phys. addr. in r2p_gdt */
|
||||
|
||||
/* Compute the first protected mode physical address */
|
||||
leal (2f-1b)(%bx), %eax
|
||||
addl %ebp, %eax
|
||||
movl %eax, %ds:(r2p_paddr-1b)(%bx)
|
||||
|
||||
/* Calculate new %esp */
|
||||
xorl %eax, %eax
|
||||
push %ss
|
||||
popw %ax
|
||||
shll $4, %eax
|
||||
movzwl %sp, %ebp
|
||||
addl %eax, %ebp /* %ebp = new %esp */
|
||||
|
||||
/* Load GDT */
|
||||
DATA32 lgdt %ds:(r2p_gdt-1b)(%bx) /* Load GDT */
|
||||
|
||||
/* Switch to protected mode */
|
||||
movl %cr0, %eax
|
||||
orb $CR0_PE, %al
|
||||
movl %eax, %cr0
|
||||
|
||||
/* flush prefetch queue, and reload %cs:%eip */
|
||||
DATA32 ljmp %ds:(r2p_paddr-1b)(%bx)
|
||||
.code32
|
||||
2:
|
||||
|
||||
/* Load segment registers, adjust %esp */
|
||||
movw $(r2p_pmds-r2p_gdt), %ax
|
||||
movw %ax, %ss
|
||||
movl %ebp, %esp
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %fs
|
||||
movw %ax, %gs
|
||||
|
||||
/* Restore registers */
|
||||
popl %eax
|
||||
popl %ebx
|
||||
popl %ebp
|
||||
|
||||
/* return to caller */
|
||||
ret
|
||||
|
||||
r2p_gdt:
|
||||
.word r2p_gdt_end - r2p_gdt - 1 /* limit */
|
||||
.long 0 /* addr */
|
||||
.word 0
|
||||
r2p_pmcs:
|
||||
/* 32 bit protected mode code segment, physical addresses */
|
||||
.word 0xffff, 0
|
||||
.byte 0, 0x9f, 0xcf, 0
|
||||
r2p_pmds:
|
||||
/* 32 bit protected mode data segment, physical addresses */
|
||||
.word 0xffff,0
|
||||
.byte 0,0x93,0xcf,0
|
||||
r2p_gdt_end:
|
||||
|
||||
r2p_paddr:
|
||||
.long 2b
|
||||
.word r2p_pmcs - r2p_gdt, 0
|
||||
|
||||
|
||||
/* This is the end of the trampoline suffix code.
|
||||
*/
|
||||
r2p_params: /* Structure must match real_to_prot_params_t in realmode.h */
|
||||
r2p_ret_addr: .long 0
|
||||
r2p_esp: .long 0
|
||||
r2p_ebx: .long 0
|
||||
r2p_esi: .long 0
|
||||
r2p_edi: .long 0
|
||||
r2p_ebp: .long 0
|
||||
r2p_out_stack: .long 0
|
||||
r2p_out_stack_len: .long 0
|
||||
.globl _real_to_prot_suffix_end
|
||||
_real_to_prot_suffix_end:
|
||||
|
||||
.globl _real_to_prot_suffix_size
|
||||
.equ _real_to_prot_suffix_size, _real_to_prot_suffix_end - _real_to_prot_suffix
|
||||
.globl real_to_prot_suffix_size
|
||||
real_to_prot_suffix_size:
|
||||
.word _real_to_prot_suffix_size
|
||||
|
||||
rm_callback_interface_end:
|
||||
|
||||
.globl _rm_callback_interface_size
|
||||
.equ _rm_callback_interface_size, rm_callback_interface_end - rm_callback_interface
|
||||
.globl rm_callback_interface_size
|
||||
rm_callback_interface_size:
|
||||
.word _rm_callback_interface_size
|
||||
|
||||
/****************************************************************************
|
||||
* END OF REAL-MODE CALLBACK INTERFACE
|
||||
****************************************************************************
|
||||
*/
|
||||
|
||||
|
||||
#ifdef PXE_EXPORT
|
||||
/****************************************************************************
|
||||
* PXE CALLBACK INTERFACE
|
||||
|
||||
40
src/arch/i386/core/setjmp.S
Normal file
40
src/arch/i386/core/setjmp.S
Normal file
@@ -0,0 +1,40 @@
|
||||
/* setjmp and longjmp. Use of these functions is deprecated. */
|
||||
|
||||
.text
|
||||
.arch i386
|
||||
.code32
|
||||
|
||||
/**************************************************************************
|
||||
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
|
||||
158
src/arch/i386/core/setup.S
Normal file
158
src/arch/i386/core/setup.S
Normal file
@@ -0,0 +1,158 @@
|
||||
/****************************************************************************
|
||||
* This file provides the setup() and setup16() functions. The
|
||||
* purpose of these functions is to set up the internal environment so
|
||||
* that C code can execute. This includes setting up the internal
|
||||
* stack and (where applicable) setting up a GDT for virtual
|
||||
* addressing.
|
||||
*
|
||||
* These functions are designed to be called by the prefix.
|
||||
*
|
||||
* The same basic assembly code is used to compile both setup()
|
||||
* and setup16().
|
||||
****************************************************************************
|
||||
*/
|
||||
|
||||
.text
|
||||
.arch i386
|
||||
|
||||
#ifdef CODE16
|
||||
/****************************************************************************
|
||||
* setup16 (real-mode far call)
|
||||
*
|
||||
* This function can be called by a 16-bit prefix in order to set up
|
||||
* the internal (either 16-bit or 32-bit) environment.
|
||||
*
|
||||
* Parameters: none
|
||||
*
|
||||
* %cs:0000, %ds:0000 and %es:0000 must point to the start of the
|
||||
* (decompressed) runtime image.
|
||||
*
|
||||
* If KEEP_IT_REAL is defined, then %ds:0000 may instead point to the
|
||||
* start of the (decompressed) data segment portion of the runtime
|
||||
* image. If %ds==%cs, then it will be assumed that the data segment
|
||||
* follows immediately after the code segment.
|
||||
****************************************************************************
|
||||
*/
|
||||
|
||||
#ifdef KEEP_IT_REAL
|
||||
|
||||
#define ENTER_FROM_EXTERNAL call ext_to_kir
|
||||
#define RETURN_TO_EXTERNAL call kir_to_ext
|
||||
#define ENTRY_POINT kir_call
|
||||
|
||||
#else /* KEEP_IT_REAL */
|
||||
|
||||
#define ENTER_FROM_EXTERNAL \
|
||||
pushw %cs ; \
|
||||
call real_to_prot ; \
|
||||
.code32
|
||||
#define RETURN_TO_EXTERNAL \
|
||||
call prot_to_real ; \
|
||||
.code16
|
||||
#define ENTRY_POINT _prot_call /* _prot_call = OFFSET ( prot_call ) in librm */
|
||||
|
||||
#endif /* KEEP_IT_REAL */
|
||||
|
||||
#define ENTRY_POINT_REGISTER di
|
||||
|
||||
.section ".text16"
|
||||
.code16
|
||||
.globl setup16
|
||||
setup16:
|
||||
|
||||
#else /* CODE16 */
|
||||
|
||||
/****************************************************************************
|
||||
* setup (32-bit protected-mode near call)
|
||||
*
|
||||
* This function can be called by a 32-bit prefix in order to set up
|
||||
* the internal 32-bit environment.
|
||||
*
|
||||
* Parameters: none
|
||||
****************************************************************************
|
||||
*/
|
||||
|
||||
#define ENTER_FROM_EXTERNAL call ext_to_int
|
||||
#define RETURN_TO_EXTERNAL call int_to_ext
|
||||
#define ENTRY_POINT int_call
|
||||
#define ENTRY_POINT_REGISTER edi
|
||||
|
||||
.section ".text"
|
||||
.code32
|
||||
.globl setup
|
||||
setup:
|
||||
|
||||
#endif /* CODE16 */
|
||||
|
||||
/* Preserve flags (including interrupt status) */
|
||||
pushfl
|
||||
|
||||
/* Switch to (uninitialised) internal environment. This will
|
||||
* preserve the external environment for when we call
|
||||
* RETURN_TO_EXTERNAL.
|
||||
*/
|
||||
ENTER_FROM_EXTERNAL
|
||||
/* NOTE: We may have only four bytes of stack at this point */
|
||||
|
||||
#if defined(CODE16) && defined(KEEP_IT_REAL)
|
||||
|
||||
/* If %ds == %cs, then the data segment is located immediately
|
||||
* after the code segment.
|
||||
*/
|
||||
pushw %ax
|
||||
pushw %bx
|
||||
movw %cs, %ax
|
||||
movw %ds, %bx
|
||||
cmpw %ax, %bx
|
||||
jne 1f
|
||||
addw $_text_load_size_pgh, %ax
|
||||
movw %ax, %ds
|
||||
1: popw %bx
|
||||
popw %ax
|
||||
|
||||
/* Switch to internal stack */
|
||||
pushw %ds
|
||||
popw %ss
|
||||
movl $_estack, %esp
|
||||
|
||||
#else /* CODE16 && KEEP_IT_REAL */
|
||||
|
||||
/* Work out where we're running */
|
||||
call 1f
|
||||
1: popl %ebp
|
||||
|
||||
/* Switch to internal pmode stack */
|
||||
leal (_estack-1b)(%ebp), %esp
|
||||
|
||||
/* Set up GDT for virtual addressing */
|
||||
call run_here
|
||||
|
||||
#endif /* CODE16 && KEEP_IT_REAL */
|
||||
|
||||
/* Switch back to external environment. This will preserve
|
||||
* the internal environment ready for the next call.
|
||||
*/
|
||||
RETURN_TO_EXTERNAL
|
||||
|
||||
/* Pass pointer to entry-point function back to prefix. %es
|
||||
* may, by now, have been destroyed, so we re-initialise it
|
||||
* from %cs.
|
||||
*/
|
||||
pushw %cs
|
||||
popw %es
|
||||
mov $ENTRY_POINT, %ENTRY_POINT_REGISTER
|
||||
|
||||
/* Restore flags (including interrupt status) */
|
||||
popfl
|
||||
|
||||
lret
|
||||
|
||||
/****************************************************************************
|
||||
* Internal stack
|
||||
****************************************************************************
|
||||
*/
|
||||
.section ".stack"
|
||||
.align 8
|
||||
_stack:
|
||||
.space 4096
|
||||
_estack:
|
||||
@@ -1,285 +0,0 @@
|
||||
/*****************************************************************************
|
||||
*
|
||||
* THIS FILE IS NOW OBSOLETE.
|
||||
*
|
||||
* The functions of this file are now placed in init.S.
|
||||
*
|
||||
*****************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef PCBIOS
|
||||
#error "16bit code is only supported with the PCBIOS"
|
||||
#endif
|
||||
|
||||
#define CODE_SEG 0x08
|
||||
#define DATA_SEG 0x10
|
||||
|
||||
#define EXEC_IN_SITU_MAGIC 0x45524548 /* 'HERE' */
|
||||
|
||||
.equ CR0_PE, 1
|
||||
|
||||
#ifdef GAS291
|
||||
#define DATA32 data32;
|
||||
#define ADDR32 addr32;
|
||||
#define LJMPI(x) ljmp x
|
||||
#else
|
||||
#define DATA32 data32
|
||||
#define ADDR32 addr32
|
||||
/* newer GAS295 require #define LJMPI(x) ljmp *x */
|
||||
#define LJMPI(x) ljmp x
|
||||
#endif
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* start16 : move payload to desired area of memory, set up for exit
|
||||
* back to prefix, set up for 32-bit code.
|
||||
*
|
||||
* Enter (from prefix) with es:di = 0x4552:0x4548 if you want to
|
||||
* prevent start16 from moving the payload. There are three
|
||||
* motivations for moving the payload:
|
||||
*
|
||||
* 1. It may be in ROM, in which case we need to move it to RAM.
|
||||
* 2. Whatever loaded us probably didn't know about our memory usage
|
||||
* beyond the end of the image file. We should claim this memory
|
||||
* before using it.
|
||||
*
|
||||
* Unless the prefix instructs us otherwise we will move the payload to:
|
||||
*
|
||||
* An area of memory claimed from the BIOS via 40:13.
|
||||
*
|
||||
* We use the main Etherboot stack (within the image target) as our
|
||||
* stack; we don't rely on the prefix passing us a stack usable for
|
||||
* anything other than the prefix's return address. The (first 512
|
||||
* bytes of the) prefix code segment is copied to a safe archive
|
||||
* location.
|
||||
*
|
||||
* When we return to the prefix (from start32), we copy this code back
|
||||
* to a new area of memory, restore the prefix's ss:sp and ljmp back
|
||||
* to the copy of the prefix. The prefix will see a return from
|
||||
* start16 *but* may be executing at a new location. Code following
|
||||
* the lcall to start16 must therefore be position-independent and
|
||||
* must also be within [cs:0000,cs:01ff]. We make absolutely no
|
||||
* guarantees about the stack contents when the prefix regains
|
||||
* control.
|
||||
*
|
||||
* Trashes just about all registers, including all the segment
|
||||
* registers.
|
||||
*
|
||||
*****************************************************************************
|
||||
*/
|
||||
|
||||
.text
|
||||
.code16
|
||||
.arch i386
|
||||
.org 0
|
||||
.globl _start16
|
||||
_start16:
|
||||
|
||||
/*****************************************************************************
|
||||
* Work out where we are going to place our image (image = optional
|
||||
* decompressor + runtime). Exit this stage with %ax containing the
|
||||
* runtime target address divided by 16 (i.e. a real-mode segment
|
||||
* address).
|
||||
*****************************************************************************
|
||||
*/
|
||||
movw %es, %ax
|
||||
cmpw $(EXEC_IN_SITU_MAGIC >> 16), %ax
|
||||
jne exec_moved
|
||||
cmpw $(EXEC_IN_SITU_MAGIC & 0xffff), %di
|
||||
jne exec_moved
|
||||
exec_in_situ:
|
||||
/* Prefix has warned us not to move the payload. Simply
|
||||
* calculate where the image is going to end up, so we can
|
||||
* work out where to put our stack.
|
||||
*/
|
||||
movw %cs, %ax
|
||||
addw $((payload-_start16)/16), %ax
|
||||
jmp 99f
|
||||
exec_moved:
|
||||
/* Claim an area of base memory from the BIOS and put the
|
||||
* payload there. arch_relocated_to() will deal with freeing
|
||||
* up this memory once we've relocated to high memory.
|
||||
*/
|
||||
movw $0x40, %ax
|
||||
movw %ax, %es
|
||||
movw %es:(0x13), %ax /* FBMS in kb to %ax */
|
||||
shlw $6, %ax /* ... in paragraphs */
|
||||
subw $__image_size_pgh, %ax /* Subtract space for image */
|
||||
shrw $6, %ax /* Round down to nearest kb */
|
||||
movw %ax, %es:(0x13) /* ...and claim memory from BIOS */
|
||||
shlw $6, %ax
|
||||
99:
|
||||
/* At this point %ax contains the segment address for the
|
||||
* start of the image (image = optional decompressor + runtime).
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* Set up stack in start32's stack space within the place we're going
|
||||
* to copy Etherboot to, reserve space for GDT, copy return address
|
||||
* from prefix stack, store prefix stack address
|
||||
*****************************************************************************
|
||||
*/
|
||||
popl %esi /* Return address */
|
||||
mov %ss, %bx /* %es:di = prefix stack address */
|
||||
mov %bx, %es /* (*after* pop of return address) */
|
||||
movw %sp, %di
|
||||
movw $__offset_stack_pgh, %bx /* Set up Etherboot stack */
|
||||
addw %ax, %bx
|
||||
movw %bx, %ss
|
||||
movw $__stack_size, %sp
|
||||
subw $(_gdt_end - _gdt), %sp /* Reserve space for GDT */
|
||||
movw %sp, %bp /* Record GDT location */
|
||||
/* Set up i386_rm_in_call_data_t structure on stack. This is
|
||||
* the same structure as is set up by rm_in_call.
|
||||
*/
|
||||
pushl $0 /* Dummy opcode */
|
||||
pushl %esi /* Prefix return address */
|
||||
pushfw /* Flags */
|
||||
pushw %di /* Prefix %sp */
|
||||
pushw %gs /* Segment registers */
|
||||
pushw %fs
|
||||
pushw %es
|
||||
pushw %ds
|
||||
pushw %es /* Prefix %ss */
|
||||
pushw %cs
|
||||
/* Stack is now 32-bit aligned */
|
||||
|
||||
/* %ax still contains image target segment address */
|
||||
|
||||
/*****************************************************************************
|
||||
* Calculate image target and prefix code physical addresses, store on stack
|
||||
* for use in copy routine.
|
||||
*****************************************************************************
|
||||
*/
|
||||
movzwl %es:-2(%di), %ebx /* Prefix code segment */
|
||||
shll $4, %ebx
|
||||
pushl %ebx /* Prefix code physical address */
|
||||
movzwl %ax, %edi /* Image target segment */
|
||||
shll $4, %edi
|
||||
pushl %edi /* Image target physical address */
|
||||
|
||||
/*****************************************************************************
|
||||
* Transition to 32-bit protected mode. Set up all segment
|
||||
* descriptors to use flat physical addresses.
|
||||
*****************************************************************************
|
||||
*/
|
||||
/* Copy gdt to area reserved on stack
|
||||
*/
|
||||
push %cs /* GDT source location -> %ds:%si */
|
||||
pop %ds
|
||||
mov $(_gdt - _start16), %si
|
||||
push %ss /* GDT target location -> %es:%di */
|
||||
pop %es
|
||||
mov %bp, %di
|
||||
mov $(_gdt_end - _gdt), %cx
|
||||
cld
|
||||
rep movsb /* Copy GDT to stack */
|
||||
movl %ss, %eax
|
||||
shll $4, %eax
|
||||
movzwl %bp, %ebx
|
||||
addl %eax, %ebx /* Physical addr of GDT copy -> %ebx */
|
||||
movl %ebx, 2(%bp) /* Fill in addr field in GDT */
|
||||
|
||||
/* Compute the offset I am running at.
|
||||
*/
|
||||
movl %cs, %ebx
|
||||
shll $4, %ebx /* %ebx = offset for start16 symbols */
|
||||
|
||||
/* Switch to 32bit protected mode.
|
||||
*/
|
||||
cli /* Disable interrupts */
|
||||
lgdt (%bp) /* Load GDT from stack */
|
||||
movl %cr0, %eax /* Set protected mode bit */
|
||||
orb $CR0_PE, %al
|
||||
movl %eax, %cr0
|
||||
movl %ss, %eax /* Convert stack pointer to 32bit */
|
||||
shll $4, %eax
|
||||
movzwl %sp, %esp
|
||||
addl %eax, %esp
|
||||
movl $DATA_SEG, %eax /* Reload the segment registers */
|
||||
movl %eax, %ds
|
||||
movl %eax, %es
|
||||
movl %eax, %ss
|
||||
movl %eax, %fs
|
||||
movl %eax, %gs
|
||||
/* Flush prefetch queue, and reload %cs:%eip by effectively ljmping
|
||||
* to code32_start. Do the jump via pushl and lret because the text
|
||||
* may not be writable/
|
||||
*/
|
||||
pushl $CODE_SEG
|
||||
ADDR32 leal (code32_start-_start16)(%ebx), %eax
|
||||
pushl %eax
|
||||
DATA32 lret /* DATA32 needed, because we're still in 16-bit mode */
|
||||
|
||||
_gdt:
|
||||
gdtarg:
|
||||
.word _gdt_end - _gdt - 1 /* limit */
|
||||
.long 0 /* addr */
|
||||
.word 0
|
||||
_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
|
||||
_gdt_end:
|
||||
|
||||
.code32
|
||||
code32_start:
|
||||
|
||||
/*****************************************************************************
|
||||
* Copy payload to target location. Do the copy backwards, since if
|
||||
* there's overlap with a forward copy then it means start16 is going
|
||||
* to get trashed during the copy anyway...
|
||||
*****************************************************************************
|
||||
*/
|
||||
popl %edi /* Image target physical address */
|
||||
pushl %edi
|
||||
leal (payload-_start16)(%ebx), %esi /* Image source physical addr */
|
||||
movl $__payload_size, %ecx /* Payload size (not image size) */
|
||||
addl %ecx, %edi /* Start at last byte (length - 1) */
|
||||
decl %edi
|
||||
addl %ecx, %esi
|
||||
decl %esi
|
||||
std /* Backward copy of image */
|
||||
rep movsb
|
||||
cld
|
||||
popl %edi /* Restore image target physical address */
|
||||
leal __decompressor_uncompressed(%edi), %ebx
|
||||
subl $_text, %ebx /* %ebx = offset for runtime symbols */
|
||||
|
||||
/*****************************************************************************
|
||||
* Copy prefix to storage area within Etherboot image.
|
||||
*****************************************************************************
|
||||
*/
|
||||
popl %esi /* Prefix source physical address */
|
||||
pushl %edi
|
||||
leal _prefix_copy(%ebx), %edi /* Prefix copy phys. addr. */
|
||||
leal _eprefix_copy(%ebx), %ecx
|
||||
subl %edi, %ecx /* Prefix copy size */
|
||||
rep movsb /* Forward copy of prefix */
|
||||
popl %edi /* Restore image target physical address */
|
||||
|
||||
/*****************************************************************************
|
||||
* Record base memory used by Etherboot image
|
||||
*****************************************************************************
|
||||
*/
|
||||
movl %edi, _prefix_image_basemem (%ebx)
|
||||
|
||||
/*****************************************************************************
|
||||
* Jump to start of the image (i.e. the decompressor, or start32 if
|
||||
* non-compressed).
|
||||
*****************************************************************************
|
||||
*/
|
||||
pushl $0 /* Inform start32 that exit path is 16-bit */
|
||||
jmpl *%edi /* Jump to image */
|
||||
|
||||
.balign 16
|
||||
/* Etherboot needs to be 16byte aligned or data that
|
||||
* is virtually aligned is no longer physically aligned
|
||||
* which is just nasty in general. 16byte alignment
|
||||
* should be sufficient though.
|
||||
*/
|
||||
payload:
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "realmode.h"
|
||||
#include "segoff.h"
|
||||
|
||||
struct segheader
|
||||
{
|
||||
|
||||
@@ -5,10 +5,14 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef CONSOLE_DIRECT_VGA
|
||||
#include "stddef.h"
|
||||
#include "string.h"
|
||||
#include "io.h"
|
||||
#include "console.h"
|
||||
#include "init.h"
|
||||
#include "vga.h"
|
||||
|
||||
#include <etherboot.h>
|
||||
#include <vga.h>
|
||||
static struct console_driver vga_console;
|
||||
|
||||
static char *vidmem; /* The video buffer */
|
||||
static int video_line, video_col;
|
||||
@@ -17,7 +21,7 @@ static int video_line, video_col;
|
||||
|
||||
static void memsetw(void *s, int c, unsigned int n)
|
||||
{
|
||||
int i;
|
||||
unsigned int i;
|
||||
u16 *ss = (u16 *) s;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
@@ -25,7 +29,7 @@ static void memsetw(void *s, int c, unsigned int n)
|
||||
}
|
||||
}
|
||||
|
||||
void video_init(void)
|
||||
static void video_init(void)
|
||||
{
|
||||
static int inited=0;
|
||||
|
||||
@@ -50,7 +54,7 @@ static void video_scroll(void)
|
||||
vidmem[i] = ' ';
|
||||
}
|
||||
|
||||
void vga_putc(unsigned char byte)
|
||||
static void vga_putc(int byte)
|
||||
{
|
||||
if (byte == '\n') {
|
||||
video_line++;
|
||||
@@ -90,5 +94,9 @@ void vga_putc(unsigned char byte)
|
||||
write_crtc((video_col + (video_line *COLS)) & 0x0ff, CRTC_CURSOR_LO);
|
||||
}
|
||||
|
||||
#endif
|
||||
static struct console_driver vga_console __console_driver = {
|
||||
.putchar = vga_putc,
|
||||
.disabled = 1,
|
||||
};
|
||||
|
||||
INIT_FN ( INIT_CONSOLE, video_init, NULL, NULL );
|
||||
|
||||
317
src/arch/i386/core/virtaddr.S
Normal file
317
src/arch/i386/core/virtaddr.S
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user