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.
This commit is contained in:
parent
8f62b39c03
commit
9fcded3d23
|
@ -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
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
*
|
||||
|
@ -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
|
||||
|
|
|
@ -15,63 +15,15 @@
|
|||
*
|
||||
*/
|
||||
|
||||
/* Real-mode call parameter block, as passed to real_call */
|
||||
struct real_call_params {
|
||||
struct i386_seg_regs segs;
|
||||
struct i386_regs regs;
|
||||
segoff_t rm_code;
|
||||
segoff_t reserved;
|
||||
} PACKED;
|
||||
|
||||
/* Current location of librm in base memory */
|
||||
extern char *installed_librm;
|
||||
|
||||
/* Start and size of our source copy of librm (i.e. the one that we
|
||||
* can install by copying it to base memory and setting
|
||||
* installed_librm)
|
||||
*/
|
||||
extern char librm[];
|
||||
extern size_t _librm_size[];
|
||||
|
||||
/* Linker symbols for offsets within librm. Other symbols should
|
||||
* almost certainly not be referred to from C code.
|
||||
*/
|
||||
extern void (*_real_to_prot[]) ( void );
|
||||
extern void (*_prot_to_real[]) ( void );
|
||||
extern void (*_prot_call[]) ( void );
|
||||
extern void (*_real_call[]) ( void );
|
||||
extern uint32_t _librm_base[];
|
||||
extern segoff_t _rm_stack[];
|
||||
extern uint32_t _pm_stack[];
|
||||
extern char _librm_ref_count[];
|
||||
|
||||
/* Symbols within current installation of librm */
|
||||
#define LIBRM_VAR( sym ) \
|
||||
( * ( ( typeof ( * _ ## sym ) * ) \
|
||||
& ( installed_librm [ (int) _ ## sym ] ) ) )
|
||||
#define LIBRM_FN( sym ) \
|
||||
( ( typeof ( * _ ## sym ) ) \
|
||||
& ( installed_librm [ (int) _ ## sym ] ) )
|
||||
#define LIBRM_CONSTANT( sym ) \
|
||||
( ( typeof ( * _ ## sym ) ) ( _ ## sym ) )
|
||||
#define inst_real_to_prot LIBRM_FN ( real_to_prot )
|
||||
#define inst_prot_to_real LIBRM_FN ( prot_to_real )
|
||||
#define inst_prot_call LIBRM_FN ( prot_call )
|
||||
#define inst_real_call LIBRM_FN ( real_call )
|
||||
#define inst_librm_base LIBRM_VAR ( librm_base )
|
||||
#define inst_rm_stack LIBRM_VAR ( rm_stack )
|
||||
#define inst_pm_stack LIBRM_VAR ( pm_stack )
|
||||
#define inst_librm_ref_count LIBRM_VAR ( librm_ref_count )
|
||||
#define librm_size LIBRM_CONSTANT ( librm_size )
|
||||
|
||||
/* Symbols within local (uninstalled) copy of librm */
|
||||
extern uint32_t librm_base;
|
||||
/* Variables in librm.S, present in the normal data segment */
|
||||
extern uint16_t rm_sp;
|
||||
extern uint16_t rm_ss;
|
||||
extern uint16_t rm_cs;
|
||||
extern uint32_t pm_esp;
|
||||
|
||||
/* Functions that librm expects to be able to link to. Included here
|
||||
* so that the compiler will catch prototype mismatches.
|
||||
*/
|
||||
extern void _phys_to_virt ( void );
|
||||
extern void _virt_to_phys ( void );
|
||||
extern void gateA20_set ( void );
|
||||
|
||||
/*
|
||||
|
@ -132,7 +84,7 @@ extern void remove_from_rm_stack ( void *data, size_t size );
|
|||
".arch i386\n\t" \
|
||||
#name ":\n\t" \
|
||||
asm_code_str "\n\t" \
|
||||
"lret\n\t" \
|
||||
"ret\n\t" \
|
||||
#name "_end:\n\t" \
|
||||
".equ " #name "_size, " #name "_end - " #name "\n\t" \
|
||||
".code32\n\t" \
|
||||
|
@ -143,26 +95,19 @@ extern void remove_from_rm_stack ( void *data, size_t size );
|
|||
|
||||
/* REAL_CALL: call a real-mode routine via librm */
|
||||
#define OUT_CONSTRAINTS(...) __VA_ARGS__
|
||||
#define IN_CONSTRAINTS(...) "m" ( __routine ), ## __VA_ARGS__
|
||||
#define IN_CONSTRAINTS(...) __VA_ARGS__
|
||||
#define CLOBBER(...) __VA_ARGS__
|
||||
#define REAL_CALL( routine, num_out_constraints, out_constraints, \
|
||||
in_constraints, clobber ) \
|
||||
do { \
|
||||
segoff_t __routine = routine; \
|
||||
__asm__ __volatile__ ( \
|
||||
"pushl %" #num_out_constraints "\n\t" \
|
||||
"call 1f\n\t" \
|
||||
"jmp 2f\n\t" \
|
||||
"\n1:\n\t" \
|
||||
"pushl installed_librm\n\t" \
|
||||
"addl $_real_call, 0(%%esp)\n\t" \
|
||||
"ret\n\t" \
|
||||
"\n2:\n\t" \
|
||||
"addl $4, %%esp\n\t" \
|
||||
: out_constraints \
|
||||
: in_constraints \
|
||||
: clobber \
|
||||
); \
|
||||
#define REAL_CALL( routine, num_out_constraints, out_constraints, \
|
||||
in_constraints, clobber ) \
|
||||
do { \
|
||||
__asm__ __volatile__ ( \
|
||||
"pushl $" #routine "\n\t" \
|
||||
"call real_call\n\t" \
|
||||
"addl $4, %%esp\n\t" \
|
||||
: out_constraints \
|
||||
: in_constraints \
|
||||
: clobber \
|
||||
); \
|
||||
} while ( 0 )
|
||||
|
||||
/* REAL_EXEC: combine RM_FRAGMENT and REAL_CALL into one handy unit */
|
||||
|
@ -170,20 +115,12 @@ extern void remove_from_rm_stack ( void *data, size_t size );
|
|||
#define REAL_EXEC( name, asm_code_str, num_out_constraints, out_constraints, \
|
||||
in_constraints, clobber ) \
|
||||
do { \
|
||||
segoff_t fragment; \
|
||||
\
|
||||
REAL_FRAGMENT ( name, asm_code_str ); \
|
||||
\
|
||||
fragment.segment = inst_rm_stack.segment; \
|
||||
fragment.offset = \
|
||||
copy_to_rm_stack ( name, FRAGMENT_SIZE ( name ) ); \
|
||||
\
|
||||
REAL_CALL ( fragment, num_out_constraints, \
|
||||
REAL_CALL ( name, num_out_constraints, \
|
||||
PASSTHRU ( out_constraints ), \
|
||||
PASSTHRU ( in_constraints ), \
|
||||
PASSTHRU ( clobber ) ); \
|
||||
\
|
||||
remove_from_rm_stack ( NULL, FRAGMENT_SIZE ( name ) ); \
|
||||
} while ( 0 )
|
||||
|
||||
#endif /* ASSEMBLY */
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
typedef struct {
|
||||
uint16_t offset;
|
||||
uint16_t segment;
|
||||
} segoff_t PACKED;
|
||||
} __attribute__ (( packed )) segoff_t;
|
||||
|
||||
/* Macro hackery needed to stringify bits of inline assembly */
|
||||
#define RM_XSTR(x) #x
|
||||
|
|
|
@ -5,12 +5,17 @@
|
|||
*
|
||||
* Don't change these unless you really know what you're doing.
|
||||
*/
|
||||
#define PHYSICAL_CS 0x08
|
||||
#define PHYSICAL_DS 0x10
|
||||
#define VIRTUAL_CS 0x18
|
||||
#define VIRTUAL_DS 0x20
|
||||
#define LONG_CS 0x28
|
||||
#define LONG_DS 0x30
|
||||
|
||||
#define VIRTUAL_CS 0x08
|
||||
#define VIRTUAL_DS 0x10
|
||||
#define PHYSICAL_CS 0x18
|
||||
#define PHYSICAL_DS 0x20
|
||||
#define REAL_CS 0x28
|
||||
#define REAL_DS 0x30
|
||||
#if 0
|
||||
#define LONG_CS 0x38
|
||||
#define LONG_DS 0x40
|
||||
#endif
|
||||
|
||||
#ifndef ASSEMBLY
|
||||
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
*
|
||||
* SYS_SIZE is the number of clicks (16 bytes) to be loaded.
|
||||
*/
|
||||
.equ SYSSIZE, 8192 # 8192 * 16 bytes = 128kB maximum size of .ROM file
|
||||
.globl SYSSIZE
|
||||
.equ SYSSIZE, _load_size_pgh
|
||||
|
||||
/* floppyload.S Copyright (C) 1991, 1992 Linus Torvalds
|
||||
* modified by Drew Eckhardt
|
||||
|
@ -143,36 +144,8 @@ got_sectors:
|
|||
* start of loaded image.
|
||||
*/
|
||||
|
||||
start_runtime:
|
||||
|
||||
#ifdef COMPRESS
|
||||
/* Decompress runtime image. %es:0000 points to decompressed
|
||||
* image on exit.
|
||||
*/
|
||||
lcall $SYSSEG, $decompress16
|
||||
#endif
|
||||
|
||||
/* Set up internal environment. Address of entry-point
|
||||
* function is returned in %es:di.
|
||||
*/
|
||||
pushw %es /* setup16 says %ds:0000 must point to image */
|
||||
popw %ds
|
||||
movw $setup16, %di
|
||||
pushw %cs
|
||||
call ljmp_to_es_di
|
||||
|
||||
/* Call to arch_main. Register INT19 as an exit path. This
|
||||
* call will never return.
|
||||
*/
|
||||
movl $exit_via_int19, %eax
|
||||
pushl $arch_main
|
||||
pushl %eax /* Dummy return address */
|
||||
|
||||
/* Do the equivalent of ljmp *%es:di */
|
||||
ljmp_to_es_di:
|
||||
pushw %es
|
||||
pushw %di
|
||||
lret
|
||||
/* Jump to loaded copy */
|
||||
ljmp $SYSSEG, $start_runtime
|
||||
|
||||
/* This routine loads the system at address SYSSEG<<4, making sure no 64kB
|
||||
* boundaries are crossed. We try to load it as fast as possible, loading whole
|
||||
|
@ -181,7 +154,7 @@ ljmp_to_es_di:
|
|||
* in: es - starting address segment (normally SYSSEG)
|
||||
*/
|
||||
read_it:
|
||||
movw $1,sread /* don't reload the prefix */
|
||||
movw $0,sread /* load whole image including prefix */
|
||||
movw %es,%ax
|
||||
testw $0x0fff, %ax
|
||||
die: jne die /* es must be at 64kB boundary */
|
||||
|
@ -374,3 +347,29 @@ msg1end:
|
|||
.org 510, 0
|
||||
.word 0xAA55
|
||||
|
||||
start_runtime:
|
||||
/* Install .text16 and .data16 to top of base memory */
|
||||
call alloc_basemem
|
||||
call install_basemem
|
||||
|
||||
/* Install .text and .data to 2MB mark */
|
||||
movl $(2<<20), %edi
|
||||
call install_highmem
|
||||
|
||||
/* Jump to .text16 segment */
|
||||
pushw %ax
|
||||
pushw $1f
|
||||
lret
|
||||
.section ".text16", "awx", @progbits
|
||||
1:
|
||||
call init_gdt
|
||||
pushl $initialise
|
||||
data32 call prot_call
|
||||
popl %eax /* discard */
|
||||
pushl $main
|
||||
data32 call prot_call
|
||||
popl %eax /* discard */
|
||||
|
||||
/* Boot next device */
|
||||
int $0x18
|
||||
|
||||
|
|
|
@ -15,14 +15,12 @@ SECTIONS {
|
|||
* addresses, but may have individual link addresses depending on
|
||||
* the memory model being used.
|
||||
*
|
||||
* The linker symbols {prefix,decompress,text,data}_link_addr,
|
||||
* load_addr, and _max_align may be specified explicitly. If not
|
||||
* specified, they will default to:
|
||||
* The linker symbols {prefix,text}_link_addr, load_addr, and
|
||||
* _max_align may be specified explicitly. If not specified, they
|
||||
* will default to:
|
||||
*
|
||||
* _prefix_link_addr = 0
|
||||
* _decompress_link_addr = 0
|
||||
* _text_link_addr = 0
|
||||
* _data_link_addr = _text_link_addr + sizeof ( text sections )
|
||||
* _load_addr = 0
|
||||
* _max_align = 16
|
||||
*
|
||||
|
@ -39,24 +37,6 @@ SECTIONS {
|
|||
* >16-byte alignment of physical addresses when -DKEEP_IT_REAL is
|
||||
* used (though virtual addresses will still be fully aligned).
|
||||
*
|
||||
* The real-mode prefixes rely on _text_link_addr and
|
||||
* _decompress_link_addr being 0, since they issue far calls into
|
||||
* those sections, thus requiring that symbol_value ==
|
||||
* symbol_offset therein. Using the linker to calculate
|
||||
* e.g. offset_setup16=setup16-_text will not work, since you then
|
||||
* cannot use the reference from the prefix to setup16 to drag in
|
||||
* setup16.o. Life is hard.
|
||||
*
|
||||
* If librm is included, then it must go at offset 0 within the
|
||||
* text section. This is because librm is dual-usage: it is
|
||||
* called from setup16 with %cs:0000 pointing to the start of the
|
||||
* text section, and later it will be copied to base memory and
|
||||
* called with %cs:0000 pointing to the start of librm.
|
||||
*
|
||||
* The decompressor is designed to decompress in-place. After
|
||||
* calling the decompressor, the image will look exactly the same
|
||||
* as the uncompressed image; the compressed data and the
|
||||
* decompressor code itself will have been overwritten.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -72,48 +52,66 @@ SECTIONS {
|
|||
_entry = .;
|
||||
*(.prefix)
|
||||
*(.prefix.*)
|
||||
_eprefix_progbits = .;
|
||||
}
|
||||
|
||||
_eprefix = .;
|
||||
|
||||
/*
|
||||
* The decompressor (may be absent)
|
||||
* The 16-bit sections, if present
|
||||
*/
|
||||
|
||||
_decompress_link_addr = DEFINED ( _decompress_link_addr ) ?
|
||||
_decompress_link_addr : 0;
|
||||
. = _decompress_link_addr;
|
||||
_decompress = .;
|
||||
_text16_link_addr = 0;
|
||||
. = _text16_link_addr;
|
||||
_text16 = .;
|
||||
|
||||
.decompress : AT ( _decompress_load_offset + __decompress ) {
|
||||
__decompress = .;
|
||||
*(.decompress)
|
||||
*(.decompress.*)
|
||||
.text16 : AT ( _text16_load_offset + __text16 ) {
|
||||
__text16 = .;
|
||||
*(.text16)
|
||||
*(.text16.*)
|
||||
_etext16_progbits = .;
|
||||
} = 0x9090
|
||||
|
||||
_etext16 = .;
|
||||
|
||||
_data16_link_addr = 0;
|
||||
. = _data16_link_addr;
|
||||
_data16 = .;
|
||||
|
||||
.rodata16 : AT ( _data16_load_offset + __rodata16 ) {
|
||||
__rodata16 = .;
|
||||
*(.rodata16)
|
||||
*(.rodata16.*)
|
||||
}
|
||||
.data16 : AT ( _data16_load_offset + __data16 ) {
|
||||
__data16 = .;
|
||||
*(.data16)
|
||||
*(.data16.*)
|
||||
_edata16_progbits = .;
|
||||
}
|
||||
.bss16 : AT ( _data16_load_offset + __bss16 ) {
|
||||
__bss16 = .;
|
||||
_bss16 = .;
|
||||
*(.bss16)
|
||||
*(.bss16.*)
|
||||
_ebss16 = .;
|
||||
}
|
||||
.stack16 : AT ( _data16_load_offset + __stack16 ) {
|
||||
__stack16 = .;
|
||||
*(.stack16)
|
||||
*(.stack16.*)
|
||||
}
|
||||
|
||||
_edecompress = .;
|
||||
_edata16 = .;
|
||||
|
||||
/*
|
||||
* The text sections
|
||||
* The 32-bit sections
|
||||
*/
|
||||
|
||||
_text_link_addr = DEFINED ( _text_link_addr ) ? _text_link_addr : 0;
|
||||
. = _text_link_addr;
|
||||
_text = .;
|
||||
|
||||
.text16 : AT ( _text_load_offset + __text16 ) {
|
||||
__text16 = .;
|
||||
|
||||
/* librm is a special case; it must go at the start of the
|
||||
* text section if it is included.
|
||||
*/
|
||||
_assert = ASSERT ( ( . == _text_link_addr ), "librm cannot go first" );
|
||||
*(.librm)
|
||||
|
||||
*(.text16)
|
||||
*(.text16.*)
|
||||
} = 0x9090
|
||||
|
||||
.text : AT ( _text_load_offset + __text ) {
|
||||
__text = .;
|
||||
*(.text)
|
||||
|
@ -122,29 +120,21 @@ SECTIONS {
|
|||
|
||||
_etext = .;
|
||||
|
||||
/*
|
||||
* The data sections
|
||||
*/
|
||||
|
||||
_data_link_addr = DEFINED ( _data_link_addr ) ? _data_link_addr : .;
|
||||
. = _data_link_addr;
|
||||
_data = .;
|
||||
|
||||
.rodata : AT ( _data_load_offset + __rodata ) {
|
||||
.rodata : AT ( _text_load_offset + __rodata ) {
|
||||
__rodata = .;
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
}
|
||||
|
||||
.data : AT ( _data_load_offset + __data ) {
|
||||
.data : AT ( _text_load_offset + __data ) {
|
||||
__data = .;
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
*(SORT(.tbl.*)) /* Various tables. See include/tables.h */
|
||||
_progbits_end = .;
|
||||
_etext_progbits = .;
|
||||
}
|
||||
|
||||
.bss : AT ( _data_load_offset + __bss ) {
|
||||
.bss : AT ( _text_load_offset + __bss ) {
|
||||
__bss = .;
|
||||
_bss = .;
|
||||
*(.bss)
|
||||
|
@ -152,8 +142,7 @@ SECTIONS {
|
|||
*(COMMON)
|
||||
_ebss = .;
|
||||
}
|
||||
|
||||
.stack : AT ( _data_load_offset + __stack ) {
|
||||
.stack : AT ( _text_load_offset + __stack ) {
|
||||
__stack = .;
|
||||
*(.stack)
|
||||
*(.stack.*)
|
||||
|
@ -188,25 +177,33 @@ SECTIONS {
|
|||
_prefix_load_offset = ALIGN ( _max_align );
|
||||
_prefix_load_addr = _prefix_link_addr + _prefix_load_offset;
|
||||
_prefix_size = _eprefix - _prefix;
|
||||
. = _prefix_load_addr + _prefix_size;
|
||||
_prefix_progbits_size = _eprefix_progbits - _prefix;
|
||||
. = _prefix_load_addr + _prefix_progbits_size;
|
||||
|
||||
. -= _decompress_link_addr;
|
||||
_decompress_load_offset = ALIGN ( _max_align );
|
||||
_decompress_load_addr = _decompress_link_addr + _decompress_load_offset;
|
||||
_decompress_size = _edecompress - _decompress;
|
||||
. = _decompress_load_addr + _decompress_size;
|
||||
. -= _text16_link_addr;
|
||||
_text16_load_offset = ALIGN ( _max_align );
|
||||
_text16_load_addr = _text16_link_addr + _text16_load_offset;
|
||||
_text16_size = _etext16 - _text16;
|
||||
_text16_progbits_size = _etext16_progbits - _text16;
|
||||
. = _text16_load_addr + _text16_progbits_size;
|
||||
|
||||
. -= _data16_link_addr;
|
||||
_data16_load_offset = ALIGN ( _max_align );
|
||||
_data16_load_addr = _data16_link_addr + _data16_load_offset;
|
||||
_data16_size = _edata16 - _data16;
|
||||
_data16_progbits_size = _edata16_progbits - _data16;
|
||||
. = _data16_load_addr + _data16_progbits_size;
|
||||
|
||||
. -= _text_link_addr;
|
||||
_text_load_offset = ALIGN ( _max_align );
|
||||
_text_load_addr = _text_link_addr + _text_load_offset;
|
||||
_text_size = _etext - _text;
|
||||
. = _text_load_addr + _text_size;
|
||||
_text_progbits_size = _etext_progbits - _text;
|
||||
. = _text_load_addr + _text_progbits_size;
|
||||
|
||||
. -= _data_link_addr;
|
||||
_data_load_offset = ALIGN ( _max_align );
|
||||
_data_load_addr = _data_link_addr + _data_load_offset;
|
||||
_data_size = _edata - _data;
|
||||
. = _data_load_addr + _data_size;
|
||||
. = ALIGN ( _max_align );
|
||||
|
||||
_load_size = . - _load_addr;
|
||||
|
||||
/*
|
||||
* Alignment checks. ALIGN() can only operate on the location
|
||||
|
@ -218,34 +215,20 @@ SECTIONS {
|
|||
_assert = ASSERT ( ( . == ALIGN ( _max_align ) ),
|
||||
"_prefix is badly aligned" );
|
||||
|
||||
. = _decompress_load_addr - _prefix_link_addr;
|
||||
. = _text16_load_addr - _text16_link_addr;
|
||||
_assert = ASSERT ( ( . == ALIGN ( _max_align ) ),
|
||||
"_decompress is badly aligned" );
|
||||
"_text16 is badly aligned" );
|
||||
|
||||
. = _data16_load_addr - _data16_link_addr;
|
||||
_assert = ASSERT ( ( . == ALIGN ( _max_align ) ),
|
||||
"_data16 is badly aligned" );
|
||||
|
||||
. = _text_load_addr - _text_link_addr;
|
||||
_assert = ASSERT ( ( . == ALIGN ( _max_align ) ),
|
||||
"_text is badly aligned" );
|
||||
|
||||
. = _data_load_addr - _data_link_addr;
|
||||
_assert = ASSERT ( ( . == ALIGN ( _max_align ) ),
|
||||
"_data is badly aligned" );
|
||||
|
||||
/*
|
||||
* setup16 needs to know this when KEEP_IT_REAL is used. There
|
||||
* are no harmful side-effects of calculating it all the time.
|
||||
* Values calculated to save code from doing it
|
||||
*/
|
||||
_text_load_size_pgh = ( _data_load_addr - _text_load_addr ) / 16 ;
|
||||
|
||||
/*
|
||||
* Useful-to-know values.
|
||||
*/
|
||||
|
||||
/* Size of the decompressed runtime image */
|
||||
_runtime_size = _edata - _text;
|
||||
/* Size of the initialised-contents portion of the runtime image */
|
||||
_runtime_progbits_size = _progbits_end - _text;
|
||||
/* Size of the (non-compressed) binary file */
|
||||
_file_size = _prefix_size + _runtime_progbits_size;
|
||||
/* Size of the non-compressed portion of the compressed binary file */
|
||||
_zfile_noncompressed_size = _prefix_size + _decompress_size;
|
||||
_load_size_pgh = ( _load_size / 16 );
|
||||
}
|
||||
|
|
|
@ -8,98 +8,6 @@
|
|||
/* Drag in local definitions */
|
||||
#include "librm.h"
|
||||
|
||||
/* Drag in FREE_BASEMEM_HEADER_SIZE */
|
||||
#include "basemem.h"
|
||||
|
||||
/****************************************************************************
|
||||
* This file defines librm: a block of code that is designed to reside
|
||||
* permanently in base memory and provide the interface between
|
||||
* real-mode code running in base memory and protected-mode code
|
||||
* running in high memory. It provides the following functions:
|
||||
*
|
||||
* real_to_prot & switch between real and protected mode
|
||||
* prot_to_real while running in base memory, preserving
|
||||
* all non-segment registers
|
||||
*
|
||||
* real_call issue a call to a real-mode routine from
|
||||
* protected-mode code running in high memory
|
||||
*
|
||||
* prot_call issue a call to a protected-mode routine from
|
||||
* real-mode code running in base memory
|
||||
*
|
||||
* librm requires the following functions to be present in the
|
||||
* protected-mode code:
|
||||
*
|
||||
* _phys_to_virt Switch from physical to virtual addressing. This
|
||||
* routine must be position-independent and must
|
||||
* *not* assume that it is genuinely running with
|
||||
* flat physical addresses
|
||||
*
|
||||
* _virt_to_phys Switch from virtual to physical addresses.
|
||||
*
|
||||
* gateA20_set Enable the A20 line to permit access to the odd
|
||||
* megabytes of RAM. (This function will be called
|
||||
* with virtual addresses set up).
|
||||
*
|
||||
* librm needs to be linked against the protected-mode binary so that
|
||||
* it can import the symbols for these functions.
|
||||
*
|
||||
* librm requires that the protected-mode code set up the following
|
||||
* segments:
|
||||
*
|
||||
* PHYSICAL_CS 32-bit pmode code and data segments with flat
|
||||
* PHYSICAL_DS physical addresses.
|
||||
*
|
||||
* VIRTUAL_CS 32-bit pmode code segment with virtual
|
||||
* addressing, such that a protected-mode routine
|
||||
* can always be found at $VIRTUAL_CS:routine.
|
||||
*
|
||||
* These segments must be set as #define constants when compiling
|
||||
* librm. Edit librm.h to change the values.
|
||||
*
|
||||
* librm does not know the location of the code executing in high
|
||||
* memory. It relies on the code running in high memory setting up a
|
||||
* GDT such that the high-memory code is accessible at virtual
|
||||
* addresses fixed at compile-time.
|
||||
*
|
||||
* librm symbols are exported as absolute values and represent offsets
|
||||
* into librm. This is the most useful form of the symbols, since
|
||||
* librm is basically a binary blob that you place somewhere in base
|
||||
* memory.
|
||||
*
|
||||
* librm.h provides convenient ways to use these symbols: you simply
|
||||
* set the pointer ( char * ) installed_librm to point to wherever
|
||||
* librm is installed, and can then use e.g. inst_rm_stack just like
|
||||
* any other variable and have it automatically refer to the value of
|
||||
* rm_stack in the installed librm. Macro trickery makes this
|
||||
* completely transparent, and the resulting assembler code is
|
||||
* amazingly efficient.
|
||||
*
|
||||
* Note that librm must be called in genuine real mode, not 16:16 or
|
||||
* 16:32 protected mode. It makes the assumption that
|
||||
* physical_address = 16*segment+offset, and also that it can use
|
||||
* OFFSET(%bp) to access stack variables. The former assumption will
|
||||
* break in either protected mode, the latter may break in 16:32
|
||||
* protected mode.
|
||||
****************************************************************************
|
||||
*/
|
||||
|
||||
/*
|
||||
* Default values for pmode segments if not defined
|
||||
*/
|
||||
#ifndef PHYSICAL_CS
|
||||
#warning "Assuming PHYSICAL_CS = 0x08"
|
||||
#define PHYSICAL_CS 0x08
|
||||
#endif
|
||||
#ifndef PHYSICAL_DS
|
||||
#warning "Assuming PHYSICAL_DS = 0x10"
|
||||
#define PHYSICAL_DS 0x10
|
||||
#endif
|
||||
#ifndef VIRTUAL_CS
|
||||
#warning "Assuming VIRTUAL_CS = 0x18"
|
||||
#define VIRTUAL_CS 0x18
|
||||
#endif
|
||||
|
||||
/* For switches to/from protected mode */
|
||||
#define CR0_PE 1
|
||||
|
||||
|
@ -109,101 +17,26 @@
|
|||
#define SIZEOF_REAL_MODE_REGS ( SIZEOF_I386_SEG_REGS + SIZEOF_I386_REGS )
|
||||
#define SIZEOF_I386_FLAGS 4
|
||||
#define SIZEOF_I386_ALL_REGS ( SIZEOF_REAL_MODE_REGS + SIZEOF_I386_FLAGS )
|
||||
#define SIZEOF_SEGOFF_T 4
|
||||
#define SIZEOF_REAL_CALL_PARAMS ( SIZEOF_REAL_MODE_REGS + 2 * SIZEOF_SEGOFF_T )
|
||||
|
||||
.text
|
||||
.arch i386
|
||||
.section ".librm", "awx", @progbits
|
||||
.align 16
|
||||
|
||||
.globl librm
|
||||
librm:
|
||||
|
||||
_librm_start:
|
||||
|
||||
#undef OFFSET
|
||||
#define OFFSET(sym) ( sym - _librm_start )
|
||||
|
||||
#undef EXPORT
|
||||
#define EXPORT(sym) \
|
||||
.globl sym ; \
|
||||
.globl _ ## sym ; \
|
||||
.equ _ ## sym, OFFSET(sym) ; \
|
||||
sym
|
||||
.section ".text16", "awx", @progbits
|
||||
|
||||
/****************************************************************************
|
||||
* Note that the first sizeof(struct free_base_memory_header) bytes of
|
||||
* librm will get vapourised by free_base_memory(). Since we need
|
||||
* librm to continue working even when this happens, we put some
|
||||
* padding here.
|
||||
* Global descriptor table
|
||||
*
|
||||
* We must also ensure that the total size of librm is <1kB, otherwise
|
||||
* free_base_memory() will stomp somewhere in the middle of us as
|
||||
* well...
|
||||
****************************************************************************
|
||||
*/
|
||||
.fill FREE_BASEMEM_HEADER_SIZE, 1, 0
|
||||
|
||||
/****************************************************************************
|
||||
* Record of the current physical location of the installed copy.
|
||||
* Used by prot_call in order to return via the current installed copy
|
||||
* even if Etherboot has been relocated during the protected-mode
|
||||
* call.
|
||||
****************************************************************************
|
||||
*/
|
||||
EXPORT(librm_base):
|
||||
librm_base: .long 0
|
||||
|
||||
/****************************************************************************
|
||||
* GDT for initial transition to protected mode
|
||||
* Call init_gdt to set up the GDT before attempting to use any
|
||||
* protected-mode code.
|
||||
*
|
||||
* PHYSICAL_CS and PHYSICAL_DS are defined in an external header file.
|
||||
* We use only those selectors, and construct our GDT to match the
|
||||
* selector values we're asked to use. Use PHYSICAL_CS=0x08 and
|
||||
* PHYSICAL_DS=0x10 to minimise the space occupied by this GDT.
|
||||
*
|
||||
* Note: pm_gdt is also used to store the location of the
|
||||
* protected-mode GDT as recorded on entry to prot_to_real.
|
||||
****************************************************************************
|
||||
*/
|
||||
.align 16
|
||||
pm_gdt:
|
||||
pm_gdt_limit: .word pm_gdt_length - 1
|
||||
pm_gdt_addr: .long 0
|
||||
.word 0 /* padding */
|
||||
|
||||
.org pm_gdt + PHYSICAL_CS
|
||||
pm_gdt_pm_cs:
|
||||
/* 32 bit protected mode code segment, physical addresses */
|
||||
.word 0xffff, 0
|
||||
.byte 0, 0x9f, 0xcf, 0
|
||||
|
||||
.org pm_gdt + PHYSICAL_DS
|
||||
pm_gdt_pm_ds:
|
||||
/* 32 bit protected mode data segment, physical addresses */
|
||||
.word 0xffff,0
|
||||
.byte 0,0x93,0xcf,0
|
||||
|
||||
pm_gdt_end:
|
||||
.equ pm_gdt_length, pm_gdt_end - pm_gdt
|
||||
|
||||
/****************************************************************************
|
||||
* GDT for transition to real mode
|
||||
*
|
||||
* This is used primarily to set 64kB segment limits. Define
|
||||
* FLATTEN_REAL_MODE if you want to use so-called "flat real mode"
|
||||
* with 4GB limits instead. The base address of each of the segments
|
||||
* will be adjusted at run-time.
|
||||
* Define FLATTEN_REAL_MODE if you want to use so-called "flat real
|
||||
* mode" with 4GB limits instead.
|
||||
*
|
||||
* NOTE: This must be located before prot_to_real, otherwise gas
|
||||
* throws a "can't handle non absolute segment in `ljmp'" error due to
|
||||
* not knowing the value of RM_CS when the ljmp is encountered.
|
||||
* not knowing the value of REAL_CS when the ljmp is encountered.
|
||||
*
|
||||
* Note also that putting ".word rm_gdt_end - rm_gdt - 1" directly
|
||||
* into rm_gdt_limit, rather than going via rm_gdt_length, will also
|
||||
* produce the "non absolute segment" error. This is most probably a
|
||||
* bug in gas.
|
||||
* Note also that putting ".word gdt_end - gdt - 1" directly into
|
||||
* gdt_limit, rather than going via gdt_length, will also produce the
|
||||
* "non absolute segment" error. This is most probably a bug in gas.
|
||||
****************************************************************************
|
||||
*/
|
||||
|
||||
|
@ -212,252 +45,242 @@ pm_gdt_end:
|
|||
#else
|
||||
#define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x00
|
||||
#endif
|
||||
.section ".text16"
|
||||
.align 16
|
||||
rm_gdt:
|
||||
rm_gdt_limit: .word rm_gdt_length - 1
|
||||
rm_gdt_base: .long 0
|
||||
gdt:
|
||||
gdt_limit: .word gdt_length - 1
|
||||
gdt_base: .long 0
|
||||
.word 0 /* padding */
|
||||
|
||||
.org gdt + VIRTUAL_CS, 0
|
||||
virtual_cs: /* 32 bit protected mode code segment, virtual addresses */
|
||||
.word 0xffff, 0
|
||||
.byte 0, 0x9f, 0xcf, 0
|
||||
|
||||
.org gdt + VIRTUAL_DS, 0
|
||||
virtual_ds: /* 32 bit protected mode data segment, virtual addresses */
|
||||
.word 0xffff, 0
|
||||
.byte 0, 0x93, 0xcf, 0
|
||||
|
||||
rm_gdt_rm_cs: /* 16 bit real mode code segment */
|
||||
.equ RM_CS, rm_gdt_rm_cs - rm_gdt
|
||||
.word 0xffff,(0&0xffff)
|
||||
.byte (0>>16),0x9b,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24)
|
||||
.org gdt + PHYSICAL_CS, 0
|
||||
physical_cs: /* 32 bit protected mode code segment, physical addresses */
|
||||
.word 0xffff, 0
|
||||
.byte 0, 0x9f, 0xcf, 0
|
||||
|
||||
.org gdt + PHYSICAL_DS, 0
|
||||
physical_ds: /* 32 bit protected mode data segment, physical addresses */
|
||||
.word 0xffff, 0
|
||||
.byte 0, 0x93, 0xcf, 0
|
||||
|
||||
.org gdt + REAL_CS, 0
|
||||
real_cs: /* 16 bit real mode code segment */
|
||||
.word 0xffff, 0
|
||||
.byte 0, 0x9b, RM_LIMIT_16_19__AVL__SIZE__GRANULARITY, 0
|
||||
|
||||
.org gdt + REAL_DS
|
||||
real_ds: /* 16 bit real mode data segment */
|
||||
.word 0xffff, 0
|
||||
.byte 0, 0x93, RM_LIMIT_16_19__AVL__SIZE__GRANULARITY, 0
|
||||
|
||||
rm_gdt_rm_ds: /* 16 bit real mode data segment */
|
||||
.equ RM_DS, rm_gdt_rm_ds - rm_gdt
|
||||
.word 0xffff,(0&0xffff)
|
||||
.byte (0>>16),0x93,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24)
|
||||
|
||||
rm_gdt_end:
|
||||
.equ rm_gdt_length, rm_gdt_end - rm_gdt
|
||||
gdt_end:
|
||||
.equ gdt_length, gdt_end - gdt
|
||||
|
||||
/****************************************************************************
|
||||
* real_to_prot (real-mode far call)
|
||||
* init_gdt (real-mode near call, 16-bit real-mode return address)
|
||||
*
|
||||
* Switch from 16-bit real-mode to 32-bit protected mode with flat
|
||||
* physical addresses. %esp is restored from the saved pm_esp. All
|
||||
* segment registers are set to flat physical-mode values. All other
|
||||
* registers are preserved. Interrupts are disabled.
|
||||
* Initialise the GDT ready for transitions to protected mode.
|
||||
*
|
||||
* Note that this routine can be called *without* having first set up
|
||||
* a stored pm_esp or stored GDT. If you do this, real_to_prot will
|
||||
* return with a temporary stack that is only *FOUR BYTES* in size.
|
||||
* This is just enough to enable you to do a "call 1f; popl %ebp"
|
||||
* sequence in order to find out your physical address and then load a
|
||||
* proper 32-bit protected-mode stack pointer. Do *NOT* use more than
|
||||
* four bytes since this will overwrite code in librm!
|
||||
*
|
||||
* Parameters: none
|
||||
* Parameters:
|
||||
* %edi : Physical base of protected-mode code
|
||||
****************************************************************************
|
||||
*/
|
||||
|
||||
.code16
|
||||
EXPORT(real_to_prot):
|
||||
/* Disable interrupts */
|
||||
cli
|
||||
|
||||
/* Set %ds = %cs, for easier access to variables */
|
||||
pushw %cs
|
||||
popw %ds
|
||||
|
||||
.section ".text16"
|
||||
.code16
|
||||
.globl init_gdt
|
||||
init_gdt:
|
||||
/* Preserve registers */
|
||||
movl %eax, %ds:OFFSET(save_eax)
|
||||
movl %ebx, %ds:OFFSET(save_ebx)
|
||||
|
||||
/* Extract real-mode far return address from stack */
|
||||
popl %ds:OFFSET(save_retaddr)
|
||||
|
||||
/* Record real-mode stack pointer */
|
||||
movw %sp, %ds:OFFSET(rm_sp)
|
||||
pushw %ss
|
||||
popw %ds:OFFSET(rm_ss)
|
||||
|
||||
/* Physical base address of librm to %ebx */
|
||||
xorl %ebx, %ebx
|
||||
movw %cs, %bx
|
||||
shll $4, %ebx
|
||||
|
||||
/* Record physical base address of librm */
|
||||
movl %ebx, %ds:OFFSET(librm_base)
|
||||
|
||||
/* Check base address of stored protected-mode GDT. If it's
|
||||
* zero, set it up to use our internal GDT (with physical
|
||||
* segments only).
|
||||
*/
|
||||
movl %ds:OFFSET(pm_gdt_addr), %eax
|
||||
testl %eax, %eax
|
||||
jnz 1f
|
||||
/* Use internal GDT */
|
||||
movl %ebx, %eax
|
||||
addl $OFFSET(pm_gdt), %eax
|
||||
movl %eax, %ds:OFFSET(pm_gdt_addr)
|
||||
1:
|
||||
|
||||
/* Set up protected-mode continuation address on real-mode stack */
|
||||
pushl $PHYSICAL_CS
|
||||
movl %ebx, %eax
|
||||
addl $OFFSET(1f), %eax
|
||||
pushl %eax
|
||||
pushw %bx
|
||||
|
||||
/* Record virt_offset */
|
||||
movl %edi, %cs:virt_offset_rm_copy
|
||||
|
||||
/* Set virtual_cs and virtual_ds base */
|
||||
movl %edi, %eax
|
||||
movw $virtual_cs, %bx
|
||||
call set_seg_base
|
||||
|
||||
/* Set real_cs and real_ds base, and GDT base */
|
||||
movw $real_cs, %bx
|
||||
xorl %eax, %eax
|
||||
movw %cs, %ax
|
||||
shll $4, %eax
|
||||
call set_seg_base
|
||||
addl $gdt, %eax
|
||||
movl %eax, %cs:gdt_base
|
||||
|
||||
/* Restore registers */
|
||||
popw %bx
|
||||
popl %eax
|
||||
ret
|
||||
|
||||
.section ".text16"
|
||||
.code16
|
||||
set_seg_base:
|
||||
pushl %eax
|
||||
movw %ax, %cs:(0+2)(%bx)
|
||||
movw %ax, %cs:(8+2)(%bx)
|
||||
shrl $16, %eax
|
||||
movb %al, %cs:(0+4)(%bx)
|
||||
movb %al, %cs:(8+4)(%bx)
|
||||
movb %ah, %cs:(0+7)(%bx)
|
||||
movb %ah, %cs:(8+7)(%bx)
|
||||
popl %eax
|
||||
ret
|
||||
|
||||
/* Restore protected-mode GDT */
|
||||
data32 lgdt %ds:OFFSET(pm_gdt)
|
||||
/****************************************************************************
|
||||
* real_to_prot (real-mode near call, 32-bit virtual return address)
|
||||
*
|
||||
* Switch from 16-bit real-mode to 32-bit protected mode with virtual
|
||||
* addresses. The real-mode %ss:sp is stored in rm_ss and rm_sp, and
|
||||
* the protected-mode %esp is restored from the saved pm_esp.
|
||||
* Interrupts are disabled. All other registers may be destroyed.
|
||||
*
|
||||
* The return address for this function should be a 32-bit virtual
|
||||
* address.
|
||||
*
|
||||
* Parameters:
|
||||
* %ecx : number of bytes to move from RM stack to PM stack
|
||||
*
|
||||
****************************************************************************
|
||||
*/
|
||||
.section ".text16"
|
||||
.code16
|
||||
real_to_prot:
|
||||
/* Protected-mode return address => %ebx */
|
||||
popl %ebx
|
||||
|
||||
/* Real-mode %cs => %dx, %ss => %bp */
|
||||
movw %cs, %dx
|
||||
movw %ss, %bp
|
||||
|
||||
/* virt_offset => %edi */
|
||||
movl %cs:virt_offset_rm_copy, %edi
|
||||
|
||||
/* Switch to protected mode */
|
||||
cli
|
||||
data32 lgdt %cs:gdt
|
||||
movl %cr0, %eax
|
||||
orb $CR0_PE, %al
|
||||
movl %eax, %cr0
|
||||
|
||||
/* Flush prefetch queue and reload %cs:eip */
|
||||
data32 lret
|
||||
1: .code32
|
||||
|
||||
/* Set up protected-mode stack and data segments */
|
||||
movw $PHYSICAL_DS, %ax
|
||||
data32 ljmp $VIRTUAL_CS, $1f
|
||||
.section ".text"
|
||||
.code32
|
||||
1:
|
||||
/* Set up protected-mode data segments */
|
||||
movw $VIRTUAL_DS, %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %fs
|
||||
movw %ax, %gs
|
||||
|
||||
/* Record virt_offset */
|
||||
movl %edi, virt_offset
|
||||
|
||||
/* Move data from RM stack to PM stack and set up PM stack */
|
||||
movzwl %sp, %esi
|
||||
movl pm_esp, %esp
|
||||
subl %ecx, %esp
|
||||
movl %esp, %edi
|
||||
rep ss movsb
|
||||
movw %ax, %ss
|
||||
|
||||
/* Switch to saved protected-mode stack. Note that there may
|
||||
* not actually *be* a saved protected-mode stack.
|
||||
*/
|
||||
movl OFFSET(pm_esp)(%ebx), %esp
|
||||
testl %esp, %esp
|
||||
jnz 1f
|
||||
/* No stack - use save_retaddr as a 4-byte temporary stack */
|
||||
leal OFFSET(save_retaddr+4)(%ebx), %esp
|
||||
1:
|
||||
|
||||
/* Convert real-mode far return address to physical address
|
||||
* and place on stack
|
||||
*/
|
||||
pushl OFFSET(save_retaddr)(%ebx)
|
||||
xorl %eax, %eax
|
||||
xchgw 2(%esp), %ax
|
||||
shll $4, %eax
|
||||
addl %eax, 0(%esp)
|
||||
/* Record real-mode %cs and %ss:sp */
|
||||
movw %dx, rm_cs
|
||||
movw %bp, rm_ss
|
||||
movw %si, rm_sp
|
||||
|
||||
/* Restore registers and return */
|
||||
movl OFFSET(save_eax)(%ebx), %eax
|
||||
movl OFFSET(save_ebx)(%ebx), %ebx
|
||||
ret
|
||||
/* Return to virtual address */
|
||||
jmp *%ebx
|
||||
|
||||
/****************************************************************************
|
||||
* prot_to_real (protected-mode near call, physical addresses)
|
||||
* prot_to_real (protected-mode near call, 32-bit real-mode return address)
|
||||
*
|
||||
* Switch from 32-bit protected mode with flat physical addresses to
|
||||
* 16-bit real mode. %ss:sp is restored from the saved rm_ss and
|
||||
* rm_sp. %cs is set such that %cs:0000 is the start of librm. All
|
||||
* other segment registers are set to %ss. All other registers are
|
||||
* preserved. Interrupts are *not* enabled, since we want to be able
|
||||
* to use this routine inside an ISR.
|
||||
* Switch from 32-bit protected mode with virtual addresses to 16-bit
|
||||
* real mode. The protected-mode %esp is stored in pm_esp and the
|
||||
* real-mode %ss:sp is restored from the saved rm_ss and rm_sp. All
|
||||
* real-mode data segment registers are set equal to %ss. Interrupts
|
||||
* are *not* enabled, since we want to be able to use prot_to_real in
|
||||
* an ISR. All other registers may be destroyed.
|
||||
*
|
||||
* Note that since %cs:0000 points to the start of librm on exit, it
|
||||
* follows that the code calling prot_to_real must be located within
|
||||
* 64kB of the start of librm.
|
||||
* The return address for this function should be a 32-bit (sic)
|
||||
* real-mode offset within .code16.
|
||||
*
|
||||
* Parameters:
|
||||
* %ecx : number of bytes to move from PM stack to RM stack
|
||||
*
|
||||
* Parameters: none
|
||||
****************************************************************************
|
||||
*/
|
||||
|
||||
.section ".text"
|
||||
.code32
|
||||
EXPORT(prot_to_real):
|
||||
/* Calculate physical base address of librm in %ebx, preserve
|
||||
* original %eax and %ebx in save_eax and save_ebx
|
||||
*/
|
||||
pushl %ebx
|
||||
call 1f
|
||||
1: popl %ebx
|
||||
subl $OFFSET(1b), %ebx
|
||||
popl OFFSET(save_ebx)(%ebx)
|
||||
movl %eax, OFFSET(save_eax)(%ebx)
|
||||
|
||||
/* Record physical base address of librm */
|
||||
movl %ebx, OFFSET(librm_base)(%ebx)
|
||||
|
||||
/* Extract return address from the stack, convert to offset
|
||||
* within librm and save in save_retaddr
|
||||
*/
|
||||
popl %eax
|
||||
subl %ebx, %eax
|
||||
movl %eax, OFFSET(save_retaddr)(%ebx)
|
||||
|
||||
/* Record protected-mode stack pointer */
|
||||
movl %esp, OFFSET(pm_esp)(%ebx)
|
||||
|
||||
/* Record protected-mode GDT */
|
||||
sgdt OFFSET(pm_gdt)(%ebx)
|
||||
|
||||
/* Set up real-mode GDT */
|
||||
leal OFFSET(rm_gdt)(%ebx), %eax
|
||||
movl %eax, OFFSET(rm_gdt_base)(%ebx)
|
||||
movl %ebx, %eax
|
||||
rorl $16, %eax
|
||||
movw %bx, OFFSET(rm_gdt_rm_cs+2)(%ebx)
|
||||
movb %al, OFFSET(rm_gdt_rm_cs+4)(%ebx)
|
||||
movw %bx, OFFSET(rm_gdt_rm_ds+2)(%ebx)
|
||||
movb %al, OFFSET(rm_gdt_rm_ds+4)(%ebx)
|
||||
prot_to_real:
|
||||
/* Real-mode return address => %ebx */
|
||||
popl %ebx
|
||||
|
||||
/* Switch to real-mode GDT and reload segment registers to get
|
||||
* 64kB limits. Stack is invalidated by this process.
|
||||
*/
|
||||
lgdt OFFSET(rm_gdt)(%ebx)
|
||||
ljmp $RM_CS, $1f
|
||||
1: .code16
|
||||
movw $RM_DS, %ax
|
||||
/* Real-mode %ss:sp => %ebp:edx */
|
||||
movzwl rm_ss, %ebp
|
||||
movzwl rm_sp, %edx
|
||||
subl %ecx, %edx
|
||||
|
||||
/* Copy data from PM stack to RM stack */
|
||||
movl %ebp, %eax
|
||||
shll $4, %eax
|
||||
leal (%eax,%edx), %edi
|
||||
subl virt_offset, %edi
|
||||
movl %esp, %esi
|
||||
rep movsb
|
||||
|
||||
/* Record protected-mode %esp */
|
||||
movl %esi, pm_esp
|
||||
|
||||
/* Real-mode %cs => %di */
|
||||
movw rm_cs, %di
|
||||
|
||||
/* Load real-mode segment limits */
|
||||
movw $REAL_DS, %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %fs
|
||||
movw %ax, %gs
|
||||
movw %ax, %ss
|
||||
|
||||
/* Calculate real-mode code segment in %ax and store in ljmp
|
||||
* instruction
|
||||
*/
|
||||
movl %ebx, %eax
|
||||
shrl $4, %eax
|
||||
movw %ax, OFFSET(p2r_ljmp) + 3
|
||||
|
||||
ljmp $REAL_CS, $1f
|
||||
.section ".text16"
|
||||
.code16
|
||||
1:
|
||||
/* Set up real-mode ljmp instruction */
|
||||
movw %di, %ds:(p2r_ljmp + 3)
|
||||
|
||||
/* Switch to real mode */
|
||||
movl %cr0, %ebx
|
||||
andb $0!CR0_PE, %bl
|
||||
movl %ebx, %cr0
|
||||
movl %cr0, %eax
|
||||
andb $0!CR0_PE, %al
|
||||
movl %eax, %cr0
|
||||
|
||||
/* Intersegment jump to flush prefetch queue and reload
|
||||
* %cs:eip. The segment gets filled in by the above code. We
|
||||
* can't just use lret to achieve this, because we have no
|
||||
* stack at the moment.
|
||||
*/
|
||||
p2r_ljmp:
|
||||
ljmp $0, $OFFSET(1f)
|
||||
1:
|
||||
|
||||
/* Set %ds to point to code segment for easier data access */
|
||||
movw %ax, %ds
|
||||
|
||||
/* Restore registers */
|
||||
movl OFFSET(save_eax), %eax
|
||||
movl OFFSET(save_ebx), %ebx
|
||||
|
||||
/* Set up real-mode data segments and stack */
|
||||
movw OFFSET(rm_ss), %ss
|
||||
movw OFFSET(rm_sp), %sp
|
||||
pushw %ss
|
||||
pushw %ss
|
||||
pushw %ss
|
||||
pushw %ss
|
||||
popw %ds
|
||||
popw %es
|
||||
popw %fs
|
||||
popw %gs
|
||||
|
||||
/* Set up return address on stack and return */
|
||||
pushw %cs:OFFSET(save_retaddr)
|
||||
ret
|
||||
ljmp $0, $1f /* Segment is filled in by above code */
|
||||
1:
|
||||
/* Set up real-mode stack and data segments, and stack pointer */
|
||||
movw %bp, %ds
|
||||
movw %bp, %es
|
||||
movw %bp, %fs
|
||||
movw %bp, %gs
|
||||
movw %bp, %ss
|
||||
movw %dx, %sp
|
||||
|
||||
/* Return to real-mode address */
|
||||
jmp *%bx
|
||||
|
||||
/****************************************************************************
|
||||
* prot_call (real-mode far call)
|
||||
* prot_call (real-mode near call, 32-bit real-mode return address)
|
||||
*
|
||||
* Call a specific C function in the protected-mode code. The
|
||||
* prototype of the C function must be
|
||||
|
@ -469,30 +292,12 @@ p2r_ljmp:
|
|||
* function explicitly overwrites values in ix86. Interrupt status
|
||||
* will also be preserved. Gate A20 will be enabled.
|
||||
*
|
||||
* The protected-mode code may install librm to a new location. If it
|
||||
* does so, it must update librm_base in *this* copy of librm to point
|
||||
* to the new physical location. prot_call will then return via the
|
||||
* newly installed copy.
|
||||
*
|
||||
* Note that when Etherboot performs its initial relocation, "*this*"
|
||||
* copy in the above paragraph will refer to the "master" copy, since
|
||||
* that is the initial installed copy. Etherboot will return to
|
||||
* prot_call using a virtual address, so will return to the master
|
||||
* copy in high memory (rather than the original copy in base memory).
|
||||
* The master copy in high memory will have the physical address of
|
||||
* the newly installed copy in librm_base, since install_librm()
|
||||
* writes it there. Thus, Etherboot's initialise() function will
|
||||
* return to the master copy of prot_call(), which will then jump to
|
||||
* the installed copy.
|
||||
*
|
||||
* It works, trust me.
|
||||
*
|
||||
* Parameters:
|
||||
* function : virtual address of protected-mode function to call
|
||||
*
|
||||
* Example usage:
|
||||
* pushl $pxe_api_call
|
||||
* lcall $LIBRM_SEGMENT, $prot_call
|
||||
* call prot_call
|
||||
* addw $4, %sp
|
||||
* to call in to the C function
|
||||
* void pxe_api_call ( struct i386_all_regs *ix86 );
|
||||
|
@ -502,10 +307,13 @@ p2r_ljmp:
|
|||
#define PC_OFFSET_IX86 ( 0 )
|
||||
#define PC_OFFSET_RETADDR ( PC_OFFSET_IX86 + SIZEOF_I386_ALL_REGS )
|
||||
#define PC_OFFSET_FUNCTION ( PC_OFFSET_RETADDR + 4 )
|
||||
|
||||
#define PC_OFFSET_END ( PC_OFFSET_FUNCTION + 4 )
|
||||
|
||||
.section ".text16"
|
||||
.code16
|
||||
EXPORT(prot_call):
|
||||
/* Preserve registers and flags on RM stack */
|
||||
.globl prot_call
|
||||
prot_call:
|
||||
/* Preserve registers and flags on external RM stack */
|
||||
pushfl
|
||||
pushal
|
||||
pushw %gs
|
||||
|
@ -513,101 +321,46 @@ EXPORT(prot_call):
|
|||
pushw %es
|
||||
pushw %ds
|
||||
pushw %ss
|
||||
pushw %cs
|
||||
|
||||
/* Record RM stack pointer */
|
||||
xorl %ebp, %ebp
|
||||
movw %sp, %bp
|
||||
|
||||
/* Physical address of RM stack pointer to %esi */
|
||||
xorl %esi, %esi
|
||||
pushw %ss
|
||||
popw %si
|
||||
shll $4, %esi
|
||||
addl %ebp, %esi
|
||||
|
||||
/* Address of pmode function to %ebx */
|
||||
movl %ss:(PC_OFFSET_FUNCTION)(%bp), %ebx
|
||||
|
||||
/* Switch to protected mode */
|
||||
pushw %cs
|
||||
call real_to_prot
|
||||
|
||||
/* For sanity's sake, clear the direction flag as soon as possible */
|
||||
cld
|
||||
|
||||
/* Switch to protected mode and move register dump to PM stack */
|
||||
movl $PC_OFFSET_END, %ecx
|
||||
pushl $1f
|
||||
jmp real_to_prot
|
||||
.section ".text"
|
||||
.code32
|
||||
1:
|
||||
/* Set up environment expected by C code */
|
||||
call gateA20_set
|
||||
|
||||
/* Copy ix86 from RM stack to PM stack */
|
||||
movl $SIZEOF_I386_ALL_REGS, %ecx
|
||||
subl %ecx, %esp
|
||||
movl %esp, %edi
|
||||
pushl %esi
|
||||
cld
|
||||
rep movsb
|
||||
popl %edi /* %edi = phys addr of RM copy of ix86 */
|
||||
|
||||
/* Switch to virtual addresses. */
|
||||
call 1f
|
||||
jmp 2f
|
||||
1: ljmp $VIRTUAL_CS, $_phys_to_virt
|
||||
2:
|
||||
|
||||
/* Enable A20 line */
|
||||
pushal
|
||||
lcall $VIRTUAL_CS, $gateA20_set
|
||||
popl %eax /* discard */
|
||||
popal
|
||||
|
||||
/* Push &ix86 on the stack, and call function */
|
||||
/* Call function */
|
||||
pushl %esp
|
||||
call *%ebx
|
||||
call *(PC_OFFSET_FUNCTION+4)(%esp)
|
||||
popl %eax /* discard */
|
||||
|
||||
/* Switch to physical addresses, discard PM register store */
|
||||
lcall $VIRTUAL_CS, $_virt_to_phys
|
||||
popl %eax /* discard */
|
||||
|
||||
/* Copy ix86 from PM stack to RM stack, and remove ix86
|
||||
* from PM stack. (%edi still contains physical address of
|
||||
* ix86 on RM stack from earlier, since C code preserves
|
||||
* %edi).
|
||||
*/
|
||||
movl %esp, %esi
|
||||
movl $SIZEOF_I386_ALL_REGS, %ecx
|
||||
cld
|
||||
rep movsb
|
||||
movl %esi, %esp /* remove ix86 from PM stack */
|
||||
|
||||
/* Obtain physical base address of installed copy of librm in
|
||||
* %ebx. (It's possible that this *isn't* the physical base
|
||||
* address of the copy we're currently executing in, because
|
||||
* the protected-mode call could have moved librm. If it does
|
||||
* so, it must update librm_base in our copy to reflect the
|
||||
* new location.
|
||||
*/
|
||||
call 1f
|
||||
1: popl %ebp
|
||||
movl (librm_base-1b)(%ebp), %ebx
|
||||
|
||||
/* Jump to running in installed copy of librm */
|
||||
addl $OFFSET(1f), %ebx
|
||||
jmp *%ebx
|
||||
1:
|
||||
|
||||
/* Switch to real mode */
|
||||
call prot_to_real
|
||||
/* Switch to real mode and move register dump back to RM stack */
|
||||
movl $PC_OFFSET_END, %ecx
|
||||
pushl $1f
|
||||
jmp prot_to_real
|
||||
.section ".text16"
|
||||
.code16
|
||||
|
||||
/* Restore registers and flags, and return */
|
||||
popw %ax /* skip %cs */
|
||||
popw %ax /* skip %ss */
|
||||
1:
|
||||
/* Restore registers and flags and return */
|
||||
popw %ax /* skip %cs - it is already set */
|
||||
popw %ax /* skip %ss - it is already set */
|
||||
popw %ds
|
||||
popw %es
|
||||
popw %fs
|
||||
popw %gs
|
||||
popal
|
||||
popfl
|
||||
lret
|
||||
ret
|
||||
|
||||
/****************************************************************************
|
||||
* real_call (protected-mode near call, virtual addresses)
|
||||
* real_call (protected-mode near call, 32-bit virtual return address)
|
||||
*
|
||||
* Call a real-mode function from protected-mode code.
|
||||
*
|
||||
|
@ -625,80 +378,58 @@ EXPORT(prot_call):
|
|||
* and examples.
|
||||
*
|
||||
* Parameters:
|
||||
* far pointer to real-mode function to call
|
||||
* (32-bit) near pointer to real-mode function to call
|
||||
*
|
||||
* Returns: none
|
||||
****************************************************************************
|
||||
*/
|
||||
|
||||
#define RC_OFFSET_PRESERVE_REGS ( 0 )
|
||||
#define RC_OFFSET_RETADDR ( RC_OFFSET_PRESERVE_REGS + 8 )
|
||||
#define RC_OFFSET_RM_FUNCTION ( RC_OFFSET_RETADDR + 4 )
|
||||
|
||||
#define RC_OFFSET_RETADDR ( RC_OFFSET_PRESERVE_REGS + SIZEOF_I386_REGS )
|
||||
#define RC_OFFSET_FUNCTION ( RC_OFFSET_RETADDR + 4 )
|
||||
#define RC_OFFSET_END ( RC_OFFSET_FUNCTION + 4 )
|
||||
|
||||
.section ".text"
|
||||
.code32
|
||||
EXPORT(real_call):
|
||||
/* Preserve registers */
|
||||
pushl %ebp
|
||||
pushl %eax
|
||||
|
||||
/* Switch to physical addresses */
|
||||
lcall $VIRTUAL_CS, $_virt_to_phys
|
||||
addl $4, %esp
|
||||
|
||||
/* Extract real-mode function address and store in ljmp instruction */
|
||||
call 1f
|
||||
1: popl %ebp
|
||||
movl RC_OFFSET_RM_FUNCTION(%esp), %eax
|
||||
movl %eax, (rc_ljmp + 1 - 1b)(%ebp)
|
||||
|
||||
/* Restore registers */
|
||||
popl %eax
|
||||
popl %ebp
|
||||
|
||||
/* Switch to real mode, preserving non-segment registers */
|
||||
call prot_to_real
|
||||
.code16
|
||||
|
||||
/* Far call to real-mode routine */
|
||||
pushw %cs
|
||||
call rc_ljmp
|
||||
jmp 2f
|
||||
rc_ljmp:
|
||||
ljmp $0, $0 /* address filled in by above code */
|
||||
2:
|
||||
|
||||
/* Switch to protected mode */
|
||||
pushw %cs
|
||||
call real_to_prot
|
||||
.code32
|
||||
|
||||
/* Switch to virtual addresses */
|
||||
call 1f
|
||||
jmp 2f
|
||||
1: ljmp $VIRTUAL_CS, $_phys_to_virt
|
||||
2:
|
||||
|
||||
/* Enable A20 line */
|
||||
.globl real_call
|
||||
real_call:
|
||||
/* Create register dump on PM stack */
|
||||
pushal
|
||||
lcall $VIRTUAL_CS, $gateA20_set
|
||||
popl %eax /* discard */
|
||||
|
||||
/* Switch to real mode and move register dump to RM stack */
|
||||
movl $RC_OFFSET_END, %ecx
|
||||
pushl $1f
|
||||
jmp prot_to_real
|
||||
.section ".text16"
|
||||
.code16
|
||||
1:
|
||||
/* Construct call to real-mode function */
|
||||
movw %sp, %bp
|
||||
movw RC_OFFSET_FUNCTION(%bp), %ax
|
||||
movw %ax, %cs:rc_function
|
||||
|
||||
/* Call real-mode function */
|
||||
popal
|
||||
call *%cs:rc_function
|
||||
pushal
|
||||
|
||||
/* Return */
|
||||
/* Switch to protected mode and move register dump back to PM stack */
|
||||
movl $RC_OFFSET_END, %ecx
|
||||
pushl $1f
|
||||
jmp real_to_prot
|
||||
.section ".text"
|
||||
.code32
|
||||
1:
|
||||
/* Set up environment expected by C code */
|
||||
call gateA20_set
|
||||
|
||||
/* Restore registers and return */
|
||||
popal
|
||||
ret
|
||||
|
||||
/****************************************************************************
|
||||
* Relocation lock counter
|
||||
*
|
||||
* librm may be moved in base memory only when this counter is zero.
|
||||
* The counter gets incremented whenever a reference to librm is
|
||||
* generated (e.g. a real_call is made, resulting in a return address
|
||||
* pointing to librm being placed on the stack), and decremented when
|
||||
* the reference goes out of scope (e.g. the real_call returns).
|
||||
****************************************************************************
|
||||
*/
|
||||
EXPORT(librm_ref_count): .byte 0
|
||||
|
||||
.section ".text16"
|
||||
rc_function: .word 0
|
||||
|
||||
/****************************************************************************
|
||||
* Stored real-mode and protected-mode stack pointers
|
||||
*
|
||||
|
@ -734,25 +465,18 @@ EXPORT(librm_ref_count): .byte 0
|
|||
****************************************************************************
|
||||
*/
|
||||
|
||||
EXPORT(rm_stack): /* comprises rm_ss and rm_sp */
|
||||
rm_sp: .word 0
|
||||
rm_ss: .word 0
|
||||
.section ".data"
|
||||
.globl rm_sp
|
||||
rm_sp: .word 0
|
||||
.globl rm_ss
|
||||
rm_ss: .word 0
|
||||
.globl rm_cs
|
||||
rm_cs: .word 0
|
||||
.globl pm_esp
|
||||
pm_esp: .long _estack
|
||||
|
||||
EXPORT(pm_stack):
|
||||
pm_esp: .long 0
|
||||
|
||||
/****************************************************************************
|
||||
* Temporary variables
|
||||
****************************************************************************
|
||||
*/
|
||||
save_eax: .long 0
|
||||
save_ebx: .long 0
|
||||
save_retaddr: .long 0
|
||||
|
||||
/****************************************************************************
|
||||
* End of librm
|
||||
****************************************************************************
|
||||
*/
|
||||
_librm_end:
|
||||
.globl _librm_size
|
||||
.equ _librm_size, _librm_end - _librm_start
|
||||
.section ".text16"
|
||||
virt_offset_rm_copy: .long 0
|
||||
.section ".data"
|
||||
.globl virt_offset
|
||||
virt_offset: .long 0
|
||||
|
|
|
@ -9,44 +9,28 @@
|
|||
/* Build a null object under -DKEEP_IT_REAL */
|
||||
#else
|
||||
|
||||
#include "stdint.h"
|
||||
#include "stddef.h"
|
||||
#include "string.h"
|
||||
#include "basemem.h"
|
||||
#include "relocate.h"
|
||||
#include <gpxe/init.h>
|
||||
#include "librm.h"
|
||||
#include <stdint.h>
|
||||
#include <librm.h>
|
||||
|
||||
/*
|
||||
* This file provides functions for managing librm.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Current location of librm in base memory */
|
||||
char *installed_librm = librm;
|
||||
|
||||
/* Whether or not we have base memory currently allocated for librm.
|
||||
* Note that we *can* have a working librm present in unallocated base
|
||||
* memory; this is the situation at startup for all real-mode
|
||||
* prefixes.
|
||||
*/
|
||||
static int allocated_librm = 0;
|
||||
|
||||
/*
|
||||
* Allocate space on the real-mode stack and copy data there.
|
||||
*
|
||||
*/
|
||||
uint16_t copy_to_rm_stack ( void *data, size_t size ) {
|
||||
#ifdef DEBUG_LIBRM
|
||||
if ( inst_rm_stack.offset <= size ) {
|
||||
if ( rm_sp <= size ) {
|
||||
printf ( "librm: out of space in RM stack\n" );
|
||||
lockup();
|
||||
}
|
||||
#endif
|
||||
inst_rm_stack.offset -= size;
|
||||
copy_to_real ( inst_rm_stack.segment, inst_rm_stack.offset,
|
||||
data, size );
|
||||
return inst_rm_stack.offset;
|
||||
rm_sp -= size;
|
||||
copy_to_real ( rm_ss, rm_sp, data, size );
|
||||
return rm_sp;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -56,120 +40,9 @@ uint16_t copy_to_rm_stack ( void *data, size_t size ) {
|
|||
*/
|
||||
void remove_from_rm_stack ( void *data, size_t size ) {
|
||||
if ( data ) {
|
||||
copy_from_real ( data,
|
||||
inst_rm_stack.segment, inst_rm_stack.offset,
|
||||
size );
|
||||
copy_from_real ( data, rm_ss, rm_sp, size );
|
||||
}
|
||||
inst_rm_stack.offset += size;
|
||||
rm_sp += size;
|
||||
};
|
||||
|
||||
/*
|
||||
* Install librm to base memory
|
||||
*
|
||||
*/
|
||||
static void install_librm ( char *addr ) {
|
||||
librm_base = virt_to_phys ( addr );
|
||||
memcpy ( addr, librm, librm_size );
|
||||
installed_librm = addr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Uninstall librm from base memory. This copies librm back to the
|
||||
* "master" copy, so that it can be reinstalled to a new location,
|
||||
* preserving the values for rm_ss and rm_sp from the old installed
|
||||
* copy.
|
||||
*
|
||||
* We deliberately leave the old copy intact and effectively installed
|
||||
* (apart from being in unallocated memory) so that we can use it for
|
||||
* any real-mode calls required when allocating memory for the new
|
||||
* copy, or for the real-mode exit path.
|
||||
*/
|
||||
static void uninstall_librm ( void ) {
|
||||
|
||||
/* Copy installed librm back to master copy */
|
||||
memcpy ( librm, installed_librm, librm_size );
|
||||
|
||||
/* Free but do not zero the base memory */
|
||||
if ( allocated_librm ) {
|
||||
free_base_memory ( installed_librm, librm_size );
|
||||
allocated_librm = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If librm isn't installed (i.e. if we have librm, but weren't
|
||||
* entered via it), then install librm and a real-mode stack to a
|
||||
* fixed temporary location, just so that we can e.g. issue printf()
|
||||
*
|
||||
* [ If we were entered via librm, then the real_to_prot call will
|
||||
* have filled in librm_base. ]
|
||||
*/
|
||||
static void librm_init ( void ) {
|
||||
if ( ! librm_base ) {
|
||||
install_librm ( phys_to_virt ( 0x7c00 ) );
|
||||
inst_rm_stack.segment = 0x7c0;
|
||||
inst_rm_stack.offset = 0x1000;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* librm_post_reloc gets called immediately after relocation.
|
||||
*
|
||||
*/
|
||||
static void librm_post_reloc ( void ) {
|
||||
/* Point installed_librm back at last known physical location.
|
||||
*/
|
||||
installed_librm = phys_to_virt ( librm_base );
|
||||
|
||||
/* Allocate base memory for librm and place a copy there */
|
||||
if ( ! allocated_librm ) {
|
||||
char *new_librm = alloc_base_memory ( librm_size );
|
||||
uninstall_librm ();
|
||||
install_librm ( new_librm );
|
||||
allocated_librm = 1;
|
||||
}
|
||||
}
|
||||
|
||||
INIT_FN ( INIT_LIBRM, librm_init, NULL, uninstall_librm );
|
||||
POST_RELOC_FN ( POST_RELOC_LIBRM, librm_post_reloc );
|
||||
|
||||
/*
|
||||
* Wrapper for initialise() when librm is being used. We have to
|
||||
* install a copy of librm to allocated base memory and return the
|
||||
* pointer to this new librm's entry point via es:di.
|
||||
*
|
||||
*/
|
||||
void initialise_via_librm ( struct i386_all_regs *ix86 ) {
|
||||
/* Hand off to initialise() */
|
||||
initialise ();
|
||||
|
||||
/* Point es:di to new librm's entry point. Fortunately, di is
|
||||
* already set up by setup16, so all we need to do is point
|
||||
* es:0000 to the start of the new librm.
|
||||
*/
|
||||
ix86->segs.es = librm_base >> 4;
|
||||
}
|
||||
|
||||
/*
|
||||
* Increment lock count of librm
|
||||
*
|
||||
*/
|
||||
void lock_librm ( void ) {
|
||||
inst_librm_ref_count++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decrement lock count of librm
|
||||
*
|
||||
*/
|
||||
void unlock_librm ( void ) {
|
||||
#ifdef DEBUG_LIBRM
|
||||
if ( inst_librm_ref_count == 0 ) {
|
||||
printf ( "librm: ref count gone negative\n" );
|
||||
lockup();
|
||||
}
|
||||
#endif
|
||||
inst_librm_ref_count--;
|
||||
}
|
||||
|
||||
#endif /* KEEP_IT_REAL */
|
||||
|
|
|
@ -14,11 +14,6 @@
|
|||
#include "setjmp.h"
|
||||
#include "latch.h"
|
||||
|
||||
/* within 1MB of 4GB is too close.
|
||||
* MAX_ADDR is the maximum address we can easily do DMA to.
|
||||
*/
|
||||
#define MAX_ADDR (0xfff00000UL)
|
||||
|
||||
typedef unsigned long Address;
|
||||
|
||||
/* ANSI prototyping macro */
|
||||
|
|
Reference in New Issue