Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Towards a(nother) new real-mode infrastructure, in which we take
advantage of the fact that we have to have a permanently-resident block
in base memory.
  • Loading branch information
Michael Brown committed May 2, 2006
1 parent 8f62b39 commit 9fcded3
Show file tree
Hide file tree
Showing 10 changed files with 460 additions and 1,182 deletions.
78 changes: 28 additions & 50 deletions src/arch/i386/core/relocate.c
@@ -1,30 +1,11 @@
#include "virtaddr.h"
#include "memsizes.h"
#include "osdep.h"
#include "etherboot.h"
#include <gpxe/init.h>
#include "relocate.h"

#ifndef KEEP_IT_REAL

/* by Eric Biederman */

/* On some platforms etherboot is compiled as a shared library, and we use
* the ELF pic support to make it relocateable. This works very nicely
* for code, but since no one has implemented PIC data yet pointer
* values in variables are a a problem. Global variables are a
* pain but the return addresses on the stack are the worst. On these
* platforms relocate_to will restart etherboot, to ensure the stack
* is reinitialize and hopefully get the global variables
* appropriately reinitialized as well.
*
*/
#include <virtaddr.h>
#include <registers.h>
#include <memsizes.h>

/*
* relocate() must be called without any hardware resources pointing
* at the current copy of Etherboot. The easiest way to achieve this
* is to call relocate() from within arch_initialise(), before the NIC
* gets touched in any way.
* Originally by Eric Biederman
*
* Heavily modified by Michael Brown
*
*/

Expand All @@ -40,14 +21,27 @@ extern char _max_align[];
extern char _text[];
extern char _end[];

/* Post-relocation function table */
static struct post_reloc_fn post_reloc_fns[0] __table_start(post_reloc_fn);
static struct post_reloc_fn post_reloc_fns_end[0] __table_end(post_reloc_fn);
/* within 1MB of 4GB is too close.
* MAX_ADDR is the maximum address we can easily do DMA to.
*
* Not sure where this constraint comes from, but kept it from Eric's
* old code - mcb30
*/
#define MAX_ADDR (0xfff00000UL)

static void relocate ( void ) {
/**
* Relocate Etherboot
*
* @v ix86 x86 register dump from prefix
* @ret ix86 x86 registers to return to prefix
*
* This copies Etherboot to a suitable location near the top of 32-bit
* address space, and returns the physical address of the new location
* to the prefix in %edi.
*/
void relocate ( struct i386_all_regs *ix86 ) {
unsigned long addr, eaddr, size;
unsigned i;
struct post_reloc_fn *post_reloc_fn;

/* Walk through the memory map and find the highest address
* below 4GB that etherboot will fit into. Ensure etherboot
Expand Down Expand Up @@ -183,25 +177,9 @@ static void relocate ( void ) {
virt_to_phys ( _text ), virt_to_phys ( _end ),
addr, addr + _end - _text );

relocate_to ( addr );
/* Note that we cannot make real-mode calls
* (e.g. printf) at this point, because librm has just
* been moved to high memory.
*/

/* Call any registered post-relocation functions.
* librm has a post-relocation function to install a
* new librm into base memory.
*/
for ( post_reloc_fn = post_reloc_fns;
post_reloc_fn < post_reloc_fns_end ; post_reloc_fn++ ) {
if ( post_reloc_fn->post_reloc )
post_reloc_fn->post_reloc ();
}

memcpy ( phys_to_virt ( addr ), _text, _end - _text );
}

/* Let prefix know where the new copy is */
ix86->regs.edi = addr;
}

INIT_FN ( INIT_RELOCATE, relocate, NULL, NULL );

#endif /* ! KEEP_IT_REAL */
216 changes: 0 additions & 216 deletions src/arch/i386/core/virtaddr.S
Expand Up @@ -7,164 +7,9 @@
#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)
*
Expand Down Expand Up @@ -254,64 +99,3 @@ _phys_to_virt:
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

0 comments on commit 9fcded3

Please sign in to comment.