Initial revision

This commit is contained in:
Michael Brown
2005-03-08 18:53:11 +00:00
commit 3d6123e69a
373 changed files with 114041 additions and 0 deletions

View File

@@ -0,0 +1,317 @@
#ifdef PCBIOS
#include "etherboot.h"
#include "realmode.h" /* for real_mode_stack */
/* Routines to allocate base memory in a BIOS-compatible way, by
* updating the Free Base Memory Size counter at 40:13h.
*
* Michael Brown <mbrown@fensystems.co.uk> (mcb30)
* $Id$
*/
#define fbms ( ( uint16_t * ) phys_to_virt ( 0x413 ) )
#define BASE_MEMORY_MAX ( 640 )
#define FREE_BLOCK_MAGIC ( ('!'<<0) + ('F'<<8) + ('R'<<16) + ('E'<<24) )
#define FREE_BASE_MEMORY ( (uint32_t) ( *fbms << 10 ) )
/* Prototypes */
void * _allot_base_memory ( size_t size );
void _forget_base_memory ( void *ptr, size_t size );
typedef struct free_base_memory_block {
uint32_t magic;
uint16_t size_kb;
} free_base_memory_block_t;
/* Return amount of free base memory in bytes
*/
uint32_t get_free_base_memory ( void ) {
return FREE_BASE_MEMORY;
}
/* Start of our image in base memory.
*/
#define __text16_nocompress __attribute__ ((section (".text16.nocompress")))
uint32_t image_basemem __text16_nocompress = 0;
uint32_t image_basemem_size __text16_nocompress = 0;
/* Allot/free the real-mode stack
*/
void allot_real_mode_stack ( void )
{
void *new_real_mode_stack;
if ( lock_real_mode_stack )
return;
/* This is evil hack.
* Until we have a real_mode stack use 0x7c00.
* Except for 0 - 0x600 membory below 0x7c00 is hardly every used.
* This stack should never be used unless the stack allocation fails,
* or if someone has placed a print statement in a dangerous location.
*/
if (!real_mode_stack) {
real_mode_stack = 0x7c00;
}
new_real_mode_stack = _allot_base_memory ( real_mode_stack_size );
if ( ! new_real_mode_stack ) {
printf ( "FATAL: No real-mode stack\n" );
while ( 1 ) {};
}
real_mode_stack = virt_to_phys ( new_real_mode_stack );
get_memsizes();
}
void forget_real_mode_stack ( void )
{
if ( lock_real_mode_stack )
return;
if ( real_mode_stack) {
_forget_base_memory ( phys_to_virt(real_mode_stack),
real_mode_stack_size );
/* get_memsizes() uses the real_mode stack we just freed
* for it's BIOS calls.
*/
get_memsizes();
real_mode_stack = 0;
}
}
/* Allocate N bytes of base memory. Amount allocated will be rounded
* up to the nearest kB, since that's the granularity of the BIOS FBMS
* counter. Returns NULL if memory cannot be allocated.
*/
static void * _allot_base_memory ( size_t size )
{
uint16_t size_kb = ( size + 1023 ) >> 10;
void *ptr = NULL;
#ifdef DEBUG_BASEMEM
printf ( "Trying to allocate %d kB of base memory from %d kB free\n",
size_kb, *fbms );
#endif
/* Free up any unused memory before we start */
free_unused_base_memory();
/* Check available base memory */
if ( size_kb > *fbms ) { return NULL; }
/* Reduce available base memory */
*fbms -= size_kb;
/* Calculate address of memory allocated */
ptr = phys_to_virt ( FREE_BASE_MEMORY );
/* Zero out memory. We do this so that allocation of
* already-used space will show up in the form of a crash as
* soon as possible.
*
* Update: there's another reason for doing this. If we don't
* zero the contents, then they could still retain our "free
* block" markers and be liable to being freed whenever a
* base-memory allocation routine is next called.
*/
memset ( ptr, 0, size_kb << 10 );
#ifdef DEBUG_BASEMEM
printf ( "Allocated %d kB at [%x,%x)\n", size_kb,
virt_to_phys ( ptr ),
virt_to_phys ( ptr ) + size_kb * 1024 );
#endif
return ptr;
}
void * allot_base_memory ( size_t size )
{
void *ptr;
/* Free real-mode stack, allocate memory, reallocate real-mode
* stack.
*/
forget_real_mode_stack();
ptr = _allot_base_memory ( size );
get_memsizes();
return ptr;
}
/* Free base memory allocated by allot_base_memory. The BIOS provides
* nothing better than a LIFO mechanism for freeing memory (i.e. it
* just has the single "total free memory" counter), but we improve
* upon this slightly; as long as you free all the allotted blocks, it
* doesn't matter what order you free them in. (This will only work
* for blocks that are freed via forget_base_memory()).
*
* Yes, it's annoying that you have to remember the size of the blocks
* you've allotted. However, since our granularity of allocation is
* 1K, the alternative is to risk wasting the occasional kB of base
* memory, which is a Bad Thing. Really, you should be using as
* little base memory as possible, so consider the awkwardness of the
* API to be a feature! :-)
*/
static void _forget_base_memory ( void *ptr, size_t size )
{
uint16_t remainder = virt_to_phys(ptr) & 1023;
uint16_t size_kb = ( size + remainder + 1023 ) >> 10;
free_base_memory_block_t *free_block =
( free_base_memory_block_t * ) ( ptr - remainder );
if ( ( ptr == NULL ) || ( size == 0 ) ) {
return;
}
#ifdef DEBUG_BASEMEM
printf ( "Trying to free %d bytes base memory at 0x%x\n",
size, virt_to_phys ( ptr ) );
if ( remainder > 0 ) {
printf ( "WARNING: destructively expanding free block "
"downwards to 0x%x\n",
virt_to_phys ( ptr - remainder ) );
}
#endif
/* Mark every kilobyte within this block as free. This is
* overkill for normal purposes, but helps when something has
* allocated base memory with a granularity finer than the
* BIOS granularity of 1kB. PXE ROMs tend to do this when
* they allocate their own memory. This method allows us to
* free their blocks (admittedly in a rather dangerous,
* tread-on-anything-either-side sort of way, but there's no
* other way to do it).
*
* Since we're marking every kB as free, there's actually no
* need for recording the size of the blocks. However, we
* keep this in so that debug messages are friendlier. It
* probably adds around 8 bytes to the overall code size.
*/
while ( size_kb > 0 ) {
/* Mark this block as unused */
free_block->magic = FREE_BLOCK_MAGIC;
free_block->size_kb = size_kb;
/* Move up by 1 kB */
free_block = (void *)(((char *)free_block) + (1 << 10));
size_kb--;
}
/* Free up unused base memory */
free_unused_base_memory();
}
void forget_base_memory ( void *ptr, size_t size )
{
/* Free memory, free real-mode stack, re-allocate real-mode
* stack. Do this so that we don't end up wasting a huge
* block of memory trapped behind the real-mode stack.
*/
_forget_base_memory ( ptr, size );
forget_real_mode_stack();
get_memsizes();
}
/* Do the actual freeing of memory. This is split out from
* forget_base_memory() so that it may be called separately. It
* should be called whenever base memory is deallocated by an external
* entity (if we can detect that it has done so) so that we get the
* chance to free up our own blocks.
*/
static void free_unused_base_memory ( void ) {
free_base_memory_block_t *free_block = NULL;
/* Try to release memory back to the BIOS. Free all
* consecutive blocks marked as free.
*/
while ( 1 ) {
/* Calculate address of next potential free block */
free_block = ( free_base_memory_block_t * )
phys_to_virt ( FREE_BASE_MEMORY );
/* Stop processing if we're all the way up to 640K or
* if this is not a free block
*/
if ( ( *fbms == BASE_MEMORY_MAX ) ||
( free_block->magic != FREE_BLOCK_MAGIC ) ) {
break;
}
/* Return memory to BIOS */
*fbms += free_block->size_kb;
#ifdef DEBUG_BASEMEM
printf ( "Freed %d kB base memory, %d kB now free\n",
free_block->size_kb, *fbms );
#endif
/* Zero out freed block. We do this in case
* the block contained any structures that
* might be located by scanning through
* memory.
*/
memset ( free_block, 0, free_block->size_kb << 10 );
}
}
/* Free base memory used by the prefix. Called once at start of
* Etherboot by arch_main().
*/
void forget_prefix_base_memory ( void )
{
/* runtime_start_kb is _text rounded down to a physical kB boundary */
uint32_t runtime_start_kb = virt_to_phys(_text) & ~0x3ff;
/* prefix_size_kb is the prefix size excluding any portion
* that overlaps into the first kB used by the runtime image
*/
uint32_t prefix_size_kb = runtime_start_kb - image_basemem;
#ifdef DEBUG_BASEMEM
printf ( "Attempting to free base memory used by prefix\n" );
#endif
/* If the decompressor is in allocated base memory
* *and* the Etherboot text is in base
* memory, then free the decompressor.
*/
if ( ( image_basemem >= FREE_BASE_MEMORY ) &&
( runtime_start_kb >= FREE_BASE_MEMORY ) &&
( runtime_start_kb <= ( BASE_MEMORY_MAX << 10 ) ) )
{
forget_base_memory ( phys_to_virt ( image_basemem ),
prefix_size_kb );
/* Update image_basemem and image_basemem_size to
* indicate that our allocation now starts with _text
*/
image_basemem = runtime_start_kb;
image_basemem_size -= prefix_size_kb;
}
}
/* Free base memory used by the runtime image. Called after
* relocation by arch_relocated_from().
*/
void forget_runtime_base_memory ( unsigned long old_addr )
{
/* text_start_kb is old _text rounded down to a physical KB boundary */
uint32_t old_text_start_kb = old_addr & ~0x3ff;
#ifdef DEBUG_BASEMEM
printf ( "Attempting to free base memory used by runtime image\n" );
#endif
if ( ( image_basemem >= FREE_BASE_MEMORY ) &&
( image_basemem == old_text_start_kb ) )
{
forget_base_memory ( phys_to_virt ( image_basemem ),
image_basemem_size );
/* Update image_basemem to show no longer in use */
image_basemem = 0;
image_basemem_size = 0;
}
}
#endif /* PCBIOS */

View File

@@ -0,0 +1,155 @@
/* Etherboot routines for PCBIOS firmware.
*
* Body of routines taken from old pcbios.S
*/
#ifdef PCBIOS
#include "etherboot.h"
#include "realmode.h"
#include "segoff.h"
#define CF ( 1 << 0 )
/**************************************************************************
CURRTICKS - Get Time
Use direct memory access to BIOS variables, longword 0040:006C (ticks
today) and byte 0040:0070 (midnight crossover flag) instead of calling
timeofday BIOS interrupt.
**************************************************************************/
#if defined(CONFIG_TSC_CURRTICKS)
#undef CONFIG_BIOS_CURRTICKS
#else
#define CONFIG_BIOS_CURRTICKS 1
#endif
#if defined(CONFIG_BIOS_CURRTICKS)
unsigned long currticks (void)
{
static uint32_t days = 0;
uint32_t *ticks = VIRTUAL(0x0040,0x006c);
uint8_t *midnight = VIRTUAL(0x0040,0x0070);
/* Re-enable interrupts so that the timer interrupt can occur
*/
RM_FRAGMENT(rm_currticks,
"sti\n\t"
"nop\n\t"
"nop\n\t"
"cli\n\t"
);
real_call ( rm_currticks, NULL, NULL );
if ( *midnight ) {
*midnight = 0;
days += 0x1800b0;
}
return ( days + *ticks );
}
#endif /* CONFIG_BIOS_CURRTICKS */
/**************************************************************************
INT15 - Call Interrupt 0x15
**************************************************************************/
int int15 ( int ax )
{
struct {
reg16_t ax;
} PACKED in_stack;
struct {
reg16_t flags;
} PACKED out_stack;
reg16_t ret_ax;
RM_FRAGMENT(rm_int15,
"sti\n\t"
"popw %ax\n\t"
"stc\n\t"
"int $0x15\n\t"
"pushf\n\t"
"cli\n\t"
);
in_stack.ax.word = ax;
ret_ax.word = real_call ( rm_int15, &in_stack, &out_stack );
/* Carry flag clear indicates function not supported */
if ( ! ( out_stack.flags.word & CF ) ) return 0;
return ret_ax.h;
}
#ifdef POWERSAVE
/**************************************************************************
CPU_NAP - Save power by halting the CPU until the next interrupt
**************************************************************************/
void cpu_nap ( void )
{
RM_FRAGMENT(rm_cpu_nap,
"sti\n\t"
"hlt\n\t"
"cli\n\t"
);
real_call ( rm_cpu_nap, NULL, NULL );
}
#endif /* POWERSAVE */
#if (TRY_FLOPPY_FIRST > 0)
/**************************************************************************
DISK_INIT - Initialize the disk system
**************************************************************************/
void disk_init ( void )
{
RM_FRAGMENT(rm_disk_init,
"sti\n\t"
"xorw %ax,%ax\n\t"
"movb $0x80,%dl\n\t"
"int $0x13\n\t"
"cli\n\t"
);
real_call ( rm_disk_init, NULL, NULL );
}
/**************************************************************************
DISK_READ - Read a sector from disk
**************************************************************************/
unsigned int pcbios_disk_read ( int drive, int cylinder, int head, int sector,
char *buf ) {
struct {
reg16_t ax;
reg16_t cx;
reg16_t dx;
segoff_t buffer;
} PACKED in_stack;
struct {
reg16_t flags;
} PACKED out_stack;
reg16_t ret_ax;
RM_FRAGMENT(rm_pcbios_disk_read,
"sti\n\t"
"popw %ax\n\t"
"popw %cx\n\t"
"popw %dx\n\t"
"popw %bx\n\t"
"popw %es\n\t"
"int $0x13\n\t"
"pushfw\n\t"
"cli\n\t"
);
in_stack.ax.h = 2; /* INT 13,2 - Read disk sector */
in_stack.ax.l = 1; /* Read one sector */
in_stack.cx.h = cylinder & 0xff;
in_stack.cx.l = ( ( cylinder >> 8 ) & 0x3 ) | sector;
in_stack.dx.h = head;
in_stack.dx.l = drive;
in_stack.buffer.segment = SEGMENT ( buf );
in_stack.buffer.offset = OFFSET ( buf );
ret_ax.word = real_call ( rm_pcbios_disk_read, &in_stack, &out_stack );
return ( out_stack.flags.word & CF ) ? ret_ax.word : 0;
}
#endif /* TRY_FLOPPY_FIRST */
#endif /* PCBIOS */

View File

@@ -0,0 +1,85 @@
/* Etherboot routines for PCBIOS firmware.
*
* Body of routines taken from old pcbios.S
*/
#ifdef PCBIOS
#include "etherboot.h"
#include "realmode.h"
#include "segoff.h"
#define ZF ( 1 << 6 )
/**************************************************************************
CONSOLE_PUTC - Print a character on console
**************************************************************************/
void console_putc ( int character )
{
struct {
reg16_t ax;
} PACKED in_stack;
RM_FRAGMENT(rm_console_putc,
"sti\n\t"
"popw %ax\n\t"
"movb $0x0e, %ah\n\t"
"movl $1, %ebx\n\t"
"int $0x10\n\t"
"cli\n\t"
);
in_stack.ax.l = character;
real_call ( rm_console_putc, &in_stack, NULL );
}
/**************************************************************************
CONSOLE_GETC - Get a character from console
**************************************************************************/
int console_getc ( void )
{
RM_FRAGMENT(rm_console_getc,
"sti\n\t"
"xorw %ax, %ax\n\t"
"int $0x16\n\t"
"xorb %ah, %ah\n\t"
"cli\n\t"
);
return real_call ( rm_console_getc, NULL, NULL );
}
/**************************************************************************
CONSOLE_ISCHAR - Check for keyboard interrupt
**************************************************************************/
int console_ischar ( void )
{
RM_FRAGMENT(rm_console_ischar,
"sti\n\t"
"movb $1, %ah\n\t"
"int $0x16\n\t"
"pushfw\n\t"
"popw %ax\n\t"
"cli\n\t"
);
return ( ( real_call ( rm_console_ischar, NULL, NULL ) & ZF ) == 0 );
}
/**************************************************************************
GETSHIFT - Get keyboard shift state
**************************************************************************/
int getshift ( void )
{
RM_FRAGMENT(rm_getshift,
"sti\n\t"
"movb $2, %ah\n\t"
"int $0x16\n\t"
"andw $0x3, %ax\n\t"
"cli\n\t"
);
return real_call ( rm_getshift, NULL, NULL );
}
#endif /* PCBIOS */

View File

@@ -0,0 +1,296 @@
#undef CODE16
#if defined(PCBIOS)
#define CODE16
#endif
#ifdef CODE16
#define BOCHSBP xchgw %bx,%bx
.text
.arch i386
.section ".text16", "ax", @progbits
.code16
/****************************************************************************
* Memory map mangling code
****************************************************************************
*/
.globl e820mangler
e820mangler:
/* Macro to calculate offset of labels within code segment in
* installed copy of code.
*/
#define INSTALLED(x) ( (x) - e820mangler )
/****************************************************************************
* Intercept INT 15 memory calls and remove the hidden memory ranges
* from the resulting memory map.
****************************************************************************
*/
.globl _intercept_int15
_intercept_int15:
/* Preserve registers */
pushw %bp
/* Store %ax for future reference */
pushw %ax
/* Make INT-style call to old INT15 routine */
pushfw
lcall %cs:*INSTALLED(_intercepted_int15)
/* Preserve flags returned by original E820 routine */
pushfw
/* Check for valid INT15 routine */
jc intercept_int15_exit
/* Check for a routine we want to intercept */
movw %sp, %bp
cmpw $0xe820, 2(%bp)
je intercept_e820
cmpw $0xe801, 2(%bp)
je intercept_e801
cmpb $0x88, 3(%bp)
je intercept_88
intercept_int15_exit:
/* Restore registers and return */
popfw
popw %bp /* discard original %ax */
popw %bp
lret $2 /* 'iret' - flags already loaded */
.globl _intercepted_int15
_intercepted_int15: .word 0,0
/****************************************************************************
* Exclude an address range from a potentially overlapping address range
*
* Note: this *can* be called even if the range doesn't overlap; it
* will simply return the range unaltered. It copes with all the
* possible cases of overlap, including total overlap (which will
* modify the range to length zero). If the to-be-excluded range is
* in the middle of the target range, then the larger remaining
* portion will be returned. If %di is nonzero on entry then the
* range will only be truncated from the high end, i.e. the base
* address will never be altered. All this in less than 30
* instructions. :)
*
* Parameters:
* %eax Base address of memory range
* %ecx Length of memory range
* %ebx Base address of memory range to exclude
* %edx Length of memory range to exclude
* %di 0 => truncate either end, 1 => truncate high end only
* Returns:
* %eax Updated base address of range
* %ecx Updated length of range
* %ebx,%edx Undefined
* All other registers (including %di) preserved
*
* Note: "ja" is used rather than "jg" because we are comparing
* unsigned ints
****************************************************************************
*/
#ifdef TEST_EXCLUDE_ALGORITHM
.code32
#endif /* TEST_EXCLUDE_ALGORITHM */
exclude_memory_range:
/* Convert (start,length) to (start,end) */
addl %eax, %ecx
addl %ebx, %edx
/* Calculate "prefix" length */
subl %eax, %ebx /* %ebx = "prefix" length */
ja 1f
xorl %ebx, %ebx /* Truncate to zero if negative */
1: /* %di == 0 => truncate either end
* %di != 0 => truncate only high end
*/
testw %di, %di
je use_either
cmpl %eax, %edx
jbe 99f /* excl. range is below target range */
use_prefix: /* Use prefix, discard suffix */
addl %eax, %ebx /* %ebx = candidate end address */
cmpl %ecx, %ebx /* %ecx = min ( %ebx, %ecx ) */
ja 1f
movl %ebx, %ecx
1: jmp 99f
use_either:
/* Calculate "suffix" length */
subl %ecx, %edx /* %edx = -( "suffix" length ) */
jb 1f
xorl %edx, %edx /* Truncate to zero if negative */
1: negl %edx /* %edx = "suffix" length */
/* Use whichever is longest of "prefix" and "suffix" */
cmpl %ebx, %edx
jbe use_prefix
use_suffix: /* Use suffix, discard prefix */
negl %edx
addl %ecx, %edx /* %edx = candidate start address */
cmpl %eax, %edx /* %eax = max ( %eax, %edx ) */
jb 1f
movl %edx, %eax
1:
99: subl %eax, %ecx /* Convert back to (start,length) */
ret
#ifdef TEST_EXCLUDE_ALGORITHM
.globl __test_exclude
__test_exclude:
pushl %ebx
pushl %edi
movl 12(%esp), %eax
movl 16(%esp), %ecx
movl 20(%esp), %ebx
movl 24(%esp), %edx
movl 28(%esp), %edi
call exclude_memory_range
shll $16, %eax
orl %ecx, %eax
popl %edi
popl %ebx
ret
.code16
#endif /* TEST_EXCLUDE_ALGORITHM */
/****************************************************************************
* Exclude Etherboot-reserved address ranges from a potentially
* overlapping address range
*
* Parameters:
* %eax Base address of memory range
* %ecx Length of memory range
* %di 0 => truncate either end, 1 => truncate high end only
* Returns:
* %eax Updated base address of range
* %ecx Updated length of range
* All other registers (including %di) preserved
****************************************************************************
*/
exclude_hidden_memory_ranges:
pushw %si
pushl %ebx
pushl %edx
movw $INSTALLED(_hide_memory), %si
2: movl %cs:0(%si), %ebx
movl %cs:4(%si), %edx
call exclude_memory_range
addw $8, %si
cmpw $INSTALLED(_hide_memory_end), %si
jl 2b
popl %edx
popl %ebx
popw %si
ret
.globl _hide_memory
_hide_memory:
.long 0,0 /* Etherboot text (base,length) */
.long 0,0 /* Heap (base,length) */
_hide_memory_end:
/****************************************************************************
* Intercept INT 15,E820 calls and remove the hidden memory ranges
* from the resulting memory map.
****************************************************************************
*/
#define SMAP ( 0x534d4150 )
intercept_e820:
/* Check for valid E820 routine */
cmpl $SMAP, %eax
jne intercept_int15_exit
/* If base address isn't in the low 4GB, return unaltered
* (since we never claim memory above 4GB). WARNING: we cheat
* by assuming that no E820 region will straddle the 4GB
* boundary: if this is not a valid assumption then things
* will probably break.
*/
cmpl $0, %es:4(%di)
jne intercept_int15_exit
/* Preserve registers */
pushl %eax
pushl %ecx
/* Update returned memory range */
movl %es:0(%di), %eax /* Base */
movl %es:8(%di), %ecx /* Length */
pushw %di
xorw %di, %di /* "truncate either end" flag */
call exclude_hidden_memory_ranges
popw %di
movl %eax, %es:0(%di) /* Store updated base */
movl %ecx, %es:8(%di) /* Store updated length */
/* Restore registers and return */
popl %ecx
popl %eax
jmp intercept_int15_exit
/****************************************************************************
* Intercept INT 15,E801 calls and remove the hidden memory ranges
* from the resulting memory map.
****************************************************************************
*/
intercept_e801:
/* Adjust return values */
call e801_adjust
xchgw %ax, %cx
xchgw %bx, %dx
call e801_adjust
xchgw %ax, %cx
xchgw %bx, %dx
jmp intercept_int15_exit
/* %ax = #KB from 1MB+, %bx = #64KB from 16MB+
* Return with modified values in %ax, %bx. Preserver other regs.
*/
e801_adjust:
pushw %di
pushl %ecx
pushl %eax
movw $1, %di /* "truncate only high end" flag */
/* Truncate #64KB from 16MB+ as appropriate */
movw %bx, %cx /* (no need to zero high word) */
shll $16, %ecx /* %ecx = length in bytes */
movl $(1<<24), %eax /* 16MB start address */
call exclude_hidden_memory_ranges
shrl $16, %ecx /* %cx = updated length in 64KB */
movw %cx, %bx /* Return in %bx */
/* Truncate #KB from 1MB+ as appropriate */
popw %cx /* Orig. %ax (high word already 0) */
shll $10, %ecx /* %ecx = length in bytes */
shrl $4, %eax /* 1MB start address */
call exclude_hidden_memory_ranges
shrl $10, %ecx /* %cx = updated length in KB */
pushw %cx /* Will be picked up in %eax */
popl %eax
popl %ecx
popw %di
ret
/****************************************************************************
* Intercept INT 15,88 calls and remove the hidden memory ranges
* from the resulting memory map.
****************************************************************************
*/
intercept_88:
pushw %bx /* E801 adjust, ignore %bx */
call e801_adjust
popw %bx
jmp intercept_int15_exit
.globl e820mangler_end
e820mangler_end:
.globl _e820mangler_size
.equ _e820mangler_size, e820mangler_end - e820mangler
.globl e820mangler_size
e820mangler_size:
.word _e820mangler_size
#else
.globl _e820mangler_size
.equ _e820mangler_size, 0
#endif /* CODE16 */

View File

@@ -0,0 +1,94 @@
/* Utility functions to hide Etherboot by manipulating the E820 memory
* map. These could go in memsizes.c, but are placed here because not
* all images will need them.
*/
#include "etherboot.h"
#include "hidemem.h"
#ifdef CODE16
static int mangling = 0;
static void *mangler = NULL;
#define INSTALLED(x) ( (typeof(&x)) ( (void*)(&x) - (void*)e820mangler \
+ mangler ) )
#define intercept_int15 INSTALLED(_intercept_int15)
#define intercepted_int15 INSTALLED(_intercepted_int15)
#define hide_memory INSTALLED(_hide_memory)
#define INT15_VECTOR ( (segoff_t*) ( phys_to_virt( 4 * 0x15 ) ) )
int install_e820mangler ( void *new_mangler ) {
if ( mangling ) return 0;
memcpy ( new_mangler, &e820mangler, e820mangler_size );
mangler = new_mangler;
return 1;
}
/* Intercept INT15 calls and pass them through the mangler. The
* mangler must have been copied to base memory before making this
* call, and "mangler" must point to the base memory copy, which must
* be 16-byte aligned.
*/
int hide_etherboot ( void ) {
if ( mangling ) return 1;
if ( !mangler ) return 0;
/* Hook INT15 handler */
*intercepted_int15 = *INT15_VECTOR;
(*hide_memory)[0].start = virt_to_phys(_text);
(*hide_memory)[0].length = _end - _text;
/* IMPORTANT, possibly even FIXME:
*
* Etherboot has a tendency to claim a very large area of
* memory as possible heap; enough to make it impossible to
* load an OS if we hide all of it. We hide only the portion
* that's currently in use. This means that we MUST NOT
* perform further allocations from the heap while the mangler
* is active.
*/
(*hide_memory)[1].start = heap_ptr;
(*hide_memory)[1].length = heap_bot - heap_ptr;
INT15_VECTOR->segment = SEGMENT(mangler);
INT15_VECTOR->offset = 0;
mangling = 1;
return 1;
}
int unhide_etherboot ( void ) {
if ( !mangling ) return 1;
/* Restore original INT15 handler
*/
if ( VIRTUAL(INT15_VECTOR->segment,INT15_VECTOR->offset) != mangler ) {
/* Oh dear... */
#ifdef WORK_AROUND_BPBATCH_BUG
/* BpBatch intercepts INT15, so can't unhook it, and
* then proceeds to ignore our PXENV_KEEP_UNDI return
* status, which means that it ends up zeroing out the
* INT15 handler routine.
*
* This rather ugly hack involves poking into
* BpBatch's code and changing it's stored value for
* the "next handler" in the INT15 chain.
*/
segoff_t *bp_chain = VIRTUAL ( 0x0060, 0x8254 );
if ( ( bp_chain->segment == SEGMENT(mangler) ) &&
( bp_chain->offset == 0 ) ) {
printf ( "\nBPBATCH bug workaround enabled\n" );
*bp_chain = *intercepted_int15;
}
#endif /* WORK_AROUND_BPBATCH_BUG */
return 0;
}
*INT15_VECTOR = *intercepted_int15;
mangling = 0;
return 1;
}
#endif /* CODE16 */

View File

@@ -0,0 +1,201 @@
#ifdef PCBIOS
#include "etherboot.h"
#include "realmode.h"
#define CF ( 1 << 0 )
#ifndef MEMSIZES_DEBUG
#define MEMSIZES_DEBUG 0
#endif
/* by Eric Biederman */
struct meminfo meminfo;
/**************************************************************************
BASEMEMSIZE - Get size of the conventional (base) memory
**************************************************************************/
unsigned short basememsize ( void )
{
RM_FRAGMENT(rm_basememsize,
"int $0x12\n\t"
);
return real_call ( rm_basememsize, NULL, NULL );
}
/**************************************************************************
MEMSIZE - Determine size of extended memory
**************************************************************************/
unsigned int memsize ( void )
{
struct {
reg16_t ax;
} PACKED in_stack;
struct {
reg16_t ax;
reg16_t bx;
reg16_t cx;
reg16_t dx;
reg16_t flags;
} PACKED out_stack;
int memsize;
RM_FRAGMENT(rm_memsize,
/* Some buggy BIOSes don't clear/set carry on pass/error of
* e801h memory size call or merely pass cx,dx through without
* changing them, so we set carry and zero cx,dx before call.
*/
"stc\n\t"
"xorw %cx,%cx\n\t"
"xorw %dx,%dx\n\t"
"popw %ax\n\t"
"int $0x15\n\t"
"pushfw\n\t"
"pushw %dx\n\t"
"pushw %cx\n\t"
"pushw %bx\n\t"
"pushw %ax\n\t"
);
/* Try INT 15,e801 first */
in_stack.ax.word = 0xe801;
real_call ( rm_memsize, &in_stack, &out_stack );
if ( out_stack.flags.word & CF ) {
/* INT 15,e801 not supported: try INT 15,88 */
in_stack.ax.word = 0x8800;
memsize = real_call ( rm_memsize, &in_stack, &out_stack );
} else {
/* Some BIOSes report extended memory via ax,bx rather
* than cx,dx
*/
if ( (out_stack.cx.word==0) && (out_stack.dx.word==0) ) {
/* Use ax,bx */
memsize = ( out_stack.bx.word<<6 ) + out_stack.ax.word;
} else {
/* Use cx,dx */
memsize = ( out_stack.dx.word<<6 ) + out_stack.cx.word;
}
}
return memsize;
}
#define SMAP ( 0x534d4150 )
int meme820 ( struct e820entry *buf, int count )
{
struct {
reg16_t flags;
reg32_t eax;
reg32_t ebx;
struct e820entry entry;
} PACKED stack;
int index = 0;
RM_FRAGMENT(rm_meme820,
"addw $6, %sp\n\t" /* skip flags, eax */
"popl %ebx\n\t"
"pushw %ss\n\t" /* es:di = ss:sp */
"popw %es\n\t"
"movw %sp, %di\n\t"
"movl $0xe820, %eax\n\t"
"movl $" RM_STR(SMAP) ", %edx\n\t"
"movl $" RM_STR(E820ENTRY_SIZE) ", %ecx\n\t"
"int $0x15\n\t"
"pushl %ebx\n\t"
"pushl %eax\n\t"
"pushfw\n\t"
);
stack.ebx.dword = 0; /* 'EOF' marker */
while ( ( index < count ) &&
( ( index == 0 ) || ( stack.ebx.dword != 0 ) ) ) {
real_call ( rm_meme820, &stack, &stack );
if ( stack.eax.dword != SMAP ) return 0;
if ( stack.flags.word & CF ) return 0;
buf[index++] = stack.entry;
}
return index;
}
void get_memsizes(void)
{
/* Ensure we don't stomp bios data structutres.
* the interrupt table: 0x000 - 0x3ff
* the bios data area: 0x400 - 0x502
* Dos variables: 0x502 - 0x5ff
*/
static const unsigned min_addr = 0x600;
unsigned i;
unsigned basemem;
basemem = get_free_base_memory();
meminfo.basememsize = basememsize();
meminfo.memsize = memsize();
#ifndef IGNORE_E820_MAP
meminfo.map_count = meme820(meminfo.map, E820MAX);
#else
meminfo.map_count = 0;
#endif
if (meminfo.map_count == 0) {
/* If we don't have an e820 memory map fake it */
meminfo.map_count = 2;
meminfo.map[0].addr = 0;
meminfo.map[0].size = meminfo.basememsize << 10;
meminfo.map[0].type = E820_RAM;
meminfo.map[1].addr = 1024*1024;
meminfo.map[1].size = meminfo.memsize << 10;
meminfo.map[1].type = E820_RAM;
}
/* Scrub the e820 map */
for(i = 0; i < meminfo.map_count; i++) {
if (meminfo.map[i].type != E820_RAM) {
continue;
}
/* Reserve the bios data structures */
if (meminfo.map[i].addr < min_addr) {
unsigned long delta;
delta = min_addr - meminfo.map[i].addr;
if (delta > meminfo.map[i].size) {
delta = meminfo.map[i].size;
}
meminfo.map[i].addr = min_addr;
meminfo.map[i].size -= delta;
}
/* Ensure the returned e820 map is in sync
* with the actual memory state
*/
if ((meminfo.map[i].addr < 0xa0000) &&
((meminfo.map[i].addr + meminfo.map[i].size) > basemem))
{
if (meminfo.map[i].addr <= basemem) {
meminfo.map[i].size = basemem - meminfo.map[i].addr;
} else {
meminfo.map[i].addr = basemem;
meminfo.map[i].size = 0;
}
}
}
#if MEMSIZES_DEBUG
{
int i;
printf("basememsize %d\n", meminfo.basememsize);
printf("memsize %d\n", meminfo.memsize);
printf("Memory regions(%d):\n", meminfo.map_count);
for(i = 0; i < meminfo.map_count; i++) {
unsigned long long r_start, r_end;
r_start = meminfo.map[i].addr;
r_end = r_start + meminfo.map[i].size;
printf("[%X%X, %X%X) type %d\n",
(unsigned long)(r_start >> 32),
(unsigned long)r_start,
(unsigned long)(r_end >> 32),
(unsigned long)r_end,
meminfo.map[i].type);
#if defined(CONSOLE_FIRMWARE)
sleep(1); /* No way to see 32 entries on a standard 80x25 screen... */
#endif
}
}
#endif
}
#endif /* PCBIOS */