diff --git a/src/arch/i386/core/relocate.c b/src/arch/i386/core/relocate.c index 7d5ba322..a51d515e 100644 --- a/src/arch/i386/core/relocate.c +++ b/src/arch/i386/core/relocate.c @@ -1,30 +1,11 @@ -#include "virtaddr.h" -#include "memsizes.h" -#include "osdep.h" -#include "etherboot.h" -#include -#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 +#include +#include /* - * 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 */ diff --git a/src/arch/i386/core/virtaddr.S b/src/arch/i386/core/virtaddr.S index ed495c35..5d762375 100644 --- a/src/arch/i386/core/virtaddr.S +++ b/src/arch/i386/core/virtaddr.S @@ -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 diff --git a/src/arch/i386/include/librm.h b/src/arch/i386/include/librm.h index 1b82a982..7171448f 100644 --- a/src/arch/i386/include/librm.h +++ b/src/arch/i386/include/librm.h @@ -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 */ diff --git a/src/arch/i386/include/realmode.h b/src/arch/i386/include/realmode.h index fe011184..f14aae23 100644 --- a/src/arch/i386/include/realmode.h +++ b/src/arch/i386/include/realmode.h @@ -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 diff --git a/src/arch/i386/include/virtaddr.h b/src/arch/i386/include/virtaddr.h index 4d248b0a..f2ffa2a1 100644 --- a/src/arch/i386/include/virtaddr.h +++ b/src/arch/i386/include/virtaddr.h @@ -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 diff --git a/src/arch/i386/prefix/dskprefix.S b/src/arch/i386/prefix/dskprefix.S index 62b8f5ff..05bf45bc 100644 --- a/src/arch/i386/prefix/dskprefix.S +++ b/src/arch/i386/prefix/dskprefix.S @@ -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 + diff --git a/src/arch/i386/scripts/i386.lds b/src/arch/i386/scripts/i386.lds index 4329164c..71a7f74d 100644 --- a/src/arch/i386/scripts/i386.lds +++ b/src/arch/i386/scripts/i386.lds @@ -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 ); } diff --git a/src/arch/i386/transitions/librm.S b/src/arch/i386/transitions/librm.S index 9d55cff7..2fbffb9e 100644 --- a/src/arch/i386/transitions/librm.S +++ b/src/arch/i386/transitions/librm.S @@ -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 diff --git a/src/arch/i386/transitions/librm_mgmt.c b/src/arch/i386/transitions/librm_mgmt.c index 6780fdc3..d0ff4ea5 100644 --- a/src/arch/i386/transitions/librm_mgmt.c +++ b/src/arch/i386/transitions/librm_mgmt.c @@ -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 -#include "librm.h" +#include +#include /* * 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 */ diff --git a/src/include/osdep.h b/src/include/osdep.h index 262c2d96..bc1f1668 100644 --- a/src/include/osdep.h +++ b/src/include/osdep.h @@ -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 */