diff options
author | Michael Brown | 2005-04-08 17:01:17 +0200 |
---|---|---|
committer | Michael Brown | 2005-04-08 17:01:17 +0200 |
commit | 0ff80b477dcff0726ebdbed95e8a93971e59e82b (patch) | |
tree | 860b7150212a07c24a9529ea072f3fb12700974c /src/arch/i386/core | |
parent | Merged this file into HEAD (diff) | |
download | ipxe-0ff80b477dcff0726ebdbed95e8a93971e59e82b.tar.gz ipxe-0ff80b477dcff0726ebdbed95e8a93971e59e82b.tar.xz ipxe-0ff80b477dcff0726ebdbed95e8a93971e59e82b.zip |
Merged mcb30-realmode-redesign back to HEAD
Diffstat (limited to 'src/arch/i386/core')
-rw-r--r-- | src/arch/i386/core/callbacks.c | 1 | ||||
-rw-r--r-- | src/arch/i386/core/cpu.c | 4 | ||||
-rw-r--r-- | src/arch/i386/core/elf.c | 1 | ||||
-rw-r--r-- | src/arch/i386/core/hooks.c | 108 | ||||
-rw-r--r-- | src/arch/i386/core/i386_timer.c | 7 | ||||
-rw-r--r-- | src/arch/i386/core/init.S | 305 | ||||
-rw-r--r-- | src/arch/i386/core/pxe_callbacks.c | 27 | ||||
-rw-r--r-- | src/arch/i386/core/realmode.c | 150 | ||||
-rw-r--r-- | src/arch/i386/core/realmode_asm.S | 564 | ||||
-rw-r--r-- | src/arch/i386/core/setjmp.S | 40 | ||||
-rw-r--r-- | src/arch/i386/core/setup.S | 158 | ||||
-rw-r--r-- | src/arch/i386/core/start16.S | 285 | ||||
-rw-r--r-- | src/arch/i386/core/start32.S | 453 | ||||
-rw-r--r-- | src/arch/i386/core/tagged_loader.c | 1 | ||||
-rw-r--r-- | src/arch/i386/core/video_subr.c | 22 | ||||
-rw-r--r-- | src/arch/i386/core/virtaddr.S | 317 |
16 files changed, 674 insertions, 1769 deletions
diff --git a/src/arch/i386/core/callbacks.c b/src/arch/i386/core/callbacks.c index d45e63e2..8818e146 100644 --- a/src/arch/i386/core/callbacks.c +++ b/src/arch/i386/core/callbacks.c @@ -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 diff --git a/src/arch/i386/core/cpu.c b/src/arch/i386/core/cpu.c index 8a0f7333..7b2533f4 100644 --- a/src/arch/i386/core/cpu.c +++ b/src/arch/i386/core/cpu.c @@ -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 */ diff --git a/src/arch/i386/core/elf.c b/src/arch/i386/core/elf.c index 6ef78c71..40a18a1f 100644 --- a/src/arch/i386/core/elf.c +++ b/src/arch/i386/core/elf.c @@ -1,5 +1,6 @@ #include "etherboot.h" #include "elf.h" +#include "memsizes.h" #define NAME "Etherboot" diff --git a/src/arch/i386/core/hooks.c b/src/arch/i386/core/hooks.c index 9ca6f480..32a137b1 100644 --- a/src/arch/i386/core/hooks.c +++ b/src/arch/i386/core/hooks.c @@ -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[]; + +/* + * 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 + * + */ -void arch_relocated_from (unsigned long old_addr ) +/* + * 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 */ diff --git a/src/arch/i386/core/i386_timer.c b/src/arch/i386/core/i386_timer.c index 531183d4..c9aa406f 100644 --- a/src/arch/i386/core/i386_timer.c +++ b/src/arch/i386/core/i386_timer.c @@ -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 ); diff --git a/src/arch/i386/core/init.S b/src/arch/i386/core/init.S deleted file mode 100644 index 71717c25..00000000 --- a/src/arch/i386/core/init.S +++ /dev/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 */ diff --git a/src/arch/i386/core/pxe_callbacks.c b/src/arch/i386/core/pxe_callbacks.c index 344d34fe..df522329 100644 --- a/src/arch/i386/core/pxe_callbacks.c +++ b/src/arch/i386/core/pxe_callbacks.c @@ -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 */ diff --git a/src/arch/i386/core/realmode.c b/src/arch/i386/core/realmode.c index ef4ede86..32252bd3 100644 --- a/src/arch/i386/core/realmode.c +++ b/src/arch/i386/core/realmode.c @@ -3,146 +3,38 @@ * Initial version by Michael Brown <mbrown@fensystems.co.uk>, January 2004. */ -#include "etherboot.h" #include "realmode.h" -#include "segoff.h" -#define RM_STACK_SIZE ( 0x1000 ) - -/* gcc won't let us use extended asm outside a function (compiler - * bug), ao we have to put these asm statements inside a dummy - * function. - */ -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) ); -} - -/* 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. +/* + * Copy data to/from base memory. + * */ -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; +#ifdef KEEP_IT_REAL - /* 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; +void memcpy_to_real ( segoff_t dest, void *src, size_t n ) { - /* 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; +void memcpy_from_real ( void *dest, segoff_t src, size_t n ) { - return virt_to_phys ( stack + p->in_stack_len ); } +#endif /* KEEP_IT_REAL */ -/* 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. - */ +#define RM_STACK_SIZE ( 0x1000 ) - __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; +/* gcc won't let us use extended asm outside a function (compiler + * bug), ao we have to put these asm statements inside a dummy + * function. + */ +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) ); } + +char *real_mode_stack; +int lock_real_mode_stack; diff --git a/src/arch/i386/core/realmode_asm.S b/src/arch/i386/core/realmode_asm.S index 28a5bfed..db8bc22b 100644 --- a/src/arch/i386/core/realmode_asm.S +++ b/src/arch/i386/core/realmode_asm.S @@ -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 diff --git a/src/arch/i386/core/setjmp.S b/src/arch/i386/core/setjmp.S new file mode 100644 index 00000000..59a1b7cb --- /dev/null +++ b/src/arch/i386/core/setjmp.S @@ -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 diff --git a/src/arch/i386/core/setup.S b/src/arch/i386/core/setup.S new file mode 100644 index 00000000..b2dd3b16 --- /dev/null +++ b/src/arch/i386/core/setup.S @@ -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: diff --git a/src/arch/i386/core/start16.S b/src/arch/i386/core/start16.S deleted file mode 100644 index 72fcfbfb..00000000 --- a/src/arch/i386/core/start16.S +++ /dev/null @@ -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: diff --git a/src/arch/i386/core/start32.S b/src/arch/i386/core/start32.S index 6dc3f203..e82454bb 100644 --- a/src/arch/i386/core/start32.S +++ b/src/arch/i386/core/start32.S @@ -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,227 +35,11 @@ * 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 **************************************************************************/ .globl xstart32 @@ -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 diff --git a/src/arch/i386/core/tagged_loader.c b/src/arch/i386/core/tagged_loader.c index 9c6d6c25..8955b0f8 100644 --- a/src/arch/i386/core/tagged_loader.c +++ b/src/arch/i386/core/tagged_loader.c @@ -1,5 +1,4 @@ #include "realmode.h" -#include "segoff.h" struct segheader { diff --git a/src/arch/i386/core/video_subr.c b/src/arch/i386/core/video_subr.c index dccdd97c..dbcfc73d 100644 --- a/src/arch/i386/core/video_subr.c +++ b/src/arch/i386/core/video_subr.c @@ -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 ); diff --git a/src/arch/i386/core/virtaddr.S b/src/arch/i386/core/virtaddr.S new file mode 100644 index 00000000..ed495c35 --- /dev/null +++ b/src/arch/i386/core/virtaddr.S @@ -0,0 +1,317 @@ +/* + * Functions to support the virtual addressing method of relocation + * that Etherboot uses. + * + */ + +#include "virtaddr.h" + + .arch i386 + +/**************************************************************************** + * GDT for initial transition to protected mode + * + * The segment values, PHYSICAL_CS et al, are defined in an external + * header file virtaddr.h, since they need to be shared with librm. + **************************************************************************** + */ + .data + .align 16 + +gdt: +gdt_limit: .word gdt_length - 1 +gdt_addr: .long 0 + .word 0 /* padding */ + + .org gdt + PHYSICAL_CS +physical_cs: + /* 32 bit protected mode code segment, physical addresses */ + .word 0xffff,0 + .byte 0,0x9f,0xcf,0 + + .org gdt + PHYSICAL_DS +physical_ds: + /* 32 bit protected mode data segment, physical addresses */ + .word 0xffff,0 + .byte 0,0x93,0xcf,0 + + .org gdt + VIRTUAL_CS +virtual_cs: + /* 32 bit protected mode code segment, virtual addresses */ + .word 0xffff,0 + .byte 0,0x9f,0xcf,0 + + .org gdt + VIRTUAL_DS +virtual_ds: + /* 32 bit protected mode data segment, virtual addresses */ + .word 0xffff,0 + .byte 0,0x93,0xcf,0 + +#ifdef CONFIG_X86_64 + + .org gdt + LONG_CS +long_cs: + /* 64bit long mode code segment, base 0 */ + .word 0xffff, 0 + .byte 0x00, 0x9f, 0xaf , 0x00 + + .org gdt + LONG_DS +long_ds: + /* 64bit long mode data segment, base 0 */ + .word 0xffff, 0 + .byte 0x00, 0x93, 0xcf, 0x00 + +#endif /* CONFIG_X86_64 */ + +gdt_end: + .equ gdt_length, gdt_end - gdt + + /* The virtual address offset */ + .globl virt_offset +virt_offset: .long 0 + + .text + .code32 + +/**************************************************************************** + * run_here (flat physical addressing, position-independent) + * + * Set up a GDT to run Etherboot at the current location with virtual + * addressing. This call does not switch to virtual addresses or move + * the stack pointer. The GDT will be located within the copy of + * Etherboot. All registers are preserved. + * + * This gets called at startup and at any subsequent relocation of + * Etherboot. + * + * Parameters: none + **************************************************************************** + */ + .globl run_here +run_here: + /* Preserve registers */ + pushl %eax + pushl %ebp + + /* Find out where we're running */ + call 1f +1: popl %ebp + subl $1b, %ebp + + /* Store as virt_offset */ + movl %ebp, virt_offset(%ebp) + + /* Set segment base addresses in GDT */ + leal virtual_cs(%ebp), %eax + pushl %eax + pushl %ebp + call set_seg_base + popl %eax /* discard */ + popl %eax /* discard */ + + /* Set physical location of GDT */ + leal gdt(%ebp), %eax + movl %eax, gdt_addr(%ebp) + + /* Load the new GDT */ + lgdt gdt(%ebp) + + /* Reload new flat physical segment registers */ + movl $PHYSICAL_DS, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %fs + movl %eax, %gs + movl %eax, %ss + + /* Restore registers, convert return address to far return + * address. + */ + popl %ebp + movl $PHYSICAL_CS, %eax + xchgl %eax, 4(%esp) /* cs now on stack, ret offset now in eax */ + xchgl %eax, 0(%esp) /* ret offset now on stack, eax restored */ + + /* Return to caller, reloading %cs with new value */ + lret + +/**************************************************************************** + * set_seg_base (any addressing, position-independent) + * + * Set the base address of a pair of segments in the GDT. This relies + * on the layout of the GDT being (CS,DS) pairs. + * + * Parameters: + * uint32_t base_address + * struct gdt_entry * code_segment + * Returns: + * none + **************************************************************************** + */ + .globl set_seg_base +set_seg_base: + pushl %eax + pushl %ebx + movl 12(%esp), %eax /* %eax = base address */ + movl 16(%esp), %ebx /* %ebx = &code_descriptor */ + movw %ax, (0+2)(%ebx) /* CS base bits 0-15 */ + movw %ax, (8+2)(%ebx) /* DS base bits 0-15 */ + shrl $16, %eax + movb %al, (0+4)(%ebx) /* CS base bits 16-23 */ + movb %al, (8+4)(%ebx) /* DS base bits 16-23 */ + movb %ah, (0+7)(%ebx) /* CS base bits 24-31 */ + movb %ah, (8+7)(%ebx) /* DS base bits 24-31 */ + popl %ebx + popl %eax + ret + +/**************************************************************************** + * _virt_to_phys (virtual addressing) + * + * Switch from virtual to flat physical addresses. %esp is adjusted + * to a physical value. Segment registers are set to flat physical + * selectors. All other registers are preserved. Flags are + * preserved. + * + * Parameters: none + * Returns: none + **************************************************************************** + */ + .globl _virt_to_phys +_virt_to_phys: + /* Preserve registers and flags */ + pushfl + pushl %eax + pushl %ebp + + /* Change return address to a physical address */ + movl virt_offset, %ebp + addl %ebp, 12(%esp) + + /* Switch to physical code segment */ + pushl $PHYSICAL_CS + leal 1f(%ebp), %eax + pushl %eax + lret +1: + /* Reload other segment registers and adjust %esp */ + movl $PHYSICAL_DS, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %fs + movl %eax, %gs + movl %eax, %ss + addl %ebp, %esp + + /* Restore registers and flags, and return */ + popl %ebp + popl %eax + popfl + ret + +/**************************************************************************** + * _phys_to_virt (flat physical addressing) + * + * Switch from flat physical to virtual addresses. %esp is adjusted + * to a virtual value. Segment registers are set to virtual + * selectors. All other registers are preserved. Flags are + * preserved. + * + * Note that this depends on the GDT already being correctly set up + * (e.g. by a call to run_here()). + * + * Parameters: none + * Returns: none + **************************************************************************** + */ + .globl _phys_to_virt +_phys_to_virt: + /* Preserve registers and flags */ + pushfl + pushl %eax + pushl %ebp + + /* Switch to virtual code segment */ + ljmp $VIRTUAL_CS, $1f +1: + /* Reload data segment registers */ + movl $VIRTUAL_DS, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %fs + movl %eax, %gs + + /* Reload stack segment and adjust %esp */ + movl virt_offset, %ebp + movl %eax, %ss + subl %ebp, %esp + + /* Change the return address to a virtual address */ + subl %ebp, 12(%esp) + + /* Restore registers and flags, and return */ + popl %ebp + popl %eax + popfl + ret + +/**************************************************************************** + * relocate_to (virtual addressing) + * + * Relocate Etherboot to the specified address. The runtime image + * (excluding the prefix, decompressor and compressed image) is copied + * to a new location, and execution continues in the new copy. This + * routine is designed to be called from C code. + * + * Parameters: + * uint32_t new_phys_addr + **************************************************************************** + */ + .globl relocate_to +relocate_to: + /* Save the callee save registers */ + pushl %ebp + pushl %esi + pushl %edi + + /* Compute the physical source address and data length */ + movl $_text, %esi + movl $_end, %ecx + subl %esi, %ecx + addl virt_offset, %esi + + /* Compute the physical destination address */ + movl 16(%esp), %edi + + /* Switch to flat physical addressing */ + call _virt_to_phys + + /* Do the copy */ + cld + rep movsb + + /* Calculate offset to new image */ + subl %esi, %edi + + /* Switch to executing in new image */ + call 1f +1: popl %ebp + leal (2f-1b)(%ebp,%edi), %eax + jmpl *%eax +2: + /* Switch to stack in new image */ + addl %edi, %esp + + /* Call run_here() to set up GDT */ + call run_here + + /* Switch to virtual addressing */ + call _phys_to_virt + + /* Restore the callee save registers */ + popl %edi + popl %esi + popl %ebp + + /* return */ + ret |