2005-03-08 18:53:11 +00:00
|
|
|
#include "etherboot.h"
|
2005-04-08 15:01:17 +00:00
|
|
|
#include "init.h"
|
|
|
|
#include "memsizes.h"
|
2005-05-12 16:34:57 +00:00
|
|
|
#include "heap.h"
|
|
|
|
|
|
|
|
struct heap_block {
|
|
|
|
size_t size;
|
|
|
|
char data[0];
|
|
|
|
};
|
2005-03-08 18:53:11 +00:00
|
|
|
|
2005-05-13 11:16:14 +00:00
|
|
|
/* Linker symbols */
|
|
|
|
extern char _text[];
|
|
|
|
extern char _end[];
|
2005-03-08 18:53:11 +00:00
|
|
|
|
2005-05-13 11:16:14 +00:00
|
|
|
static unsigned long heap_start, heap_end, heap_ptr;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the largest contiguous area of memory that I can use for the
|
|
|
|
* heap.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static void init_heap ( void ) {
|
|
|
|
unsigned int i;
|
|
|
|
unsigned long eb_start, eb_end;
|
|
|
|
unsigned long size;
|
2005-04-08 15:01:17 +00:00
|
|
|
|
2005-03-08 18:53:11 +00:00
|
|
|
size = 0;
|
2005-05-13 11:16:14 +00:00
|
|
|
|
|
|
|
/* Region occupied by Etherboot */
|
|
|
|
eb_start = virt_to_phys ( _text );
|
|
|
|
eb_end = virt_to_phys ( _end );
|
|
|
|
|
|
|
|
for ( i = 0 ; i < meminfo.map_count ; i++ ) {
|
|
|
|
unsigned long r_start, r_end, r_size;
|
|
|
|
unsigned long pre_eb, post_eb;
|
|
|
|
|
|
|
|
/* Get start and end addresses of the region */
|
|
|
|
if ( meminfo.map[i].type != E820_RAM )
|
2005-03-08 18:53:11 +00:00
|
|
|
continue;
|
2005-05-13 11:16:14 +00:00
|
|
|
if ( meminfo.map[i].addr > ULONG_MAX )
|
2005-03-08 18:53:11 +00:00
|
|
|
continue;
|
|
|
|
r_start = meminfo.map[i].addr;
|
2005-05-13 11:16:14 +00:00
|
|
|
if ( r_start + meminfo.map[i].size > ULONG_MAX ) {
|
2005-03-08 18:53:11 +00:00
|
|
|
r_end = ULONG_MAX;
|
2005-05-13 11:16:14 +00:00
|
|
|
} else {
|
|
|
|
r_end = r_start + meminfo.map[i].size;
|
2005-03-08 18:53:11 +00:00
|
|
|
}
|
2005-05-13 11:16:14 +00:00
|
|
|
|
|
|
|
/* Avoid overlap with Etherboot. When Etherboot is
|
|
|
|
* completely contained within the region, choose the
|
|
|
|
* larger of the two remaining portions.
|
|
|
|
*/
|
|
|
|
if ( ( eb_start < r_end ) && ( eb_end > r_start ) ) {
|
|
|
|
pre_eb = ( eb_start > r_start ) ?
|
|
|
|
( eb_start - r_start ) : 0;
|
|
|
|
post_eb = ( r_end > eb_end ) ?
|
|
|
|
( r_end - eb_end ) : 0;
|
|
|
|
if ( pre_eb > post_eb ) {
|
|
|
|
r_end = eb_start;
|
|
|
|
} else {
|
|
|
|
r_start = eb_end;
|
2005-03-08 18:53:11 +00:00
|
|
|
}
|
|
|
|
}
|
2005-05-13 11:16:14 +00:00
|
|
|
|
|
|
|
/* Use the biggest region. Where two regions are the
|
|
|
|
* same size, use the later region. (Provided that
|
|
|
|
* the memory map is laid out in a sensible order,
|
|
|
|
* this should give us the higher region.)
|
|
|
|
*/
|
|
|
|
r_size = r_end - r_start;
|
|
|
|
if ( r_size >= size ) {
|
|
|
|
heap_start = r_start;
|
|
|
|
heap_end = r_end;
|
|
|
|
size = r_size;
|
2005-03-08 18:53:11 +00:00
|
|
|
}
|
|
|
|
}
|
2005-05-13 11:16:14 +00:00
|
|
|
|
|
|
|
ASSERT ( size != 0 );
|
2005-05-13 11:24:02 +00:00
|
|
|
DBG ( "HEAP using region [%x,%x)\n", heap_start, heap_end );
|
2005-05-13 11:16:14 +00:00
|
|
|
heap_ptr = heap_end;
|
2005-03-08 18:53:11 +00:00
|
|
|
}
|
|
|
|
|
2005-05-12 16:34:57 +00:00
|
|
|
/*
|
|
|
|
* Allocate a block from the heap.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
void * emalloc ( size_t size, unsigned int align ) {
|
|
|
|
physaddr_t addr;
|
|
|
|
struct heap_block *block;
|
|
|
|
|
2005-05-13 10:18:21 +00:00
|
|
|
ASSERT ( ( align & ( align - 1 ) ) == 0 );
|
2005-05-12 16:34:57 +00:00
|
|
|
|
|
|
|
addr = ( ( ( heap_ptr - size ) & ~( align - 1 ) )
|
|
|
|
- sizeof ( struct heap_block ) );
|
2005-05-13 11:16:14 +00:00
|
|
|
if ( addr < heap_start ) {
|
2005-05-13 11:24:02 +00:00
|
|
|
DBG ( "HEAP no space for %x bytes (alignment %d) in [%x,%x)\n",
|
|
|
|
size, align, heap_start, heap_ptr );
|
2005-05-12 16:34:57 +00:00
|
|
|
return NULL;
|
2005-03-08 18:53:11 +00:00
|
|
|
}
|
2005-05-12 16:34:57 +00:00
|
|
|
|
|
|
|
block = phys_to_virt ( addr );
|
|
|
|
block->size = ( heap_ptr - addr );
|
2005-05-13 11:24:02 +00:00
|
|
|
DBG ( "HEAP allocated %x bytes (alignment %d) at %x [%x,%x)\n",
|
|
|
|
size, align, virt_to_phys ( block->data ), addr, heap_ptr );
|
2005-05-12 16:34:57 +00:00
|
|
|
heap_ptr = addr;
|
|
|
|
return block->data;
|
2005-03-08 18:53:11 +00:00
|
|
|
}
|
|
|
|
|
2005-05-12 16:34:57 +00:00
|
|
|
/*
|
|
|
|
* Allocate all remaining space on the heap
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
void * emalloc_all ( size_t *size ) {
|
2005-05-13 11:16:14 +00:00
|
|
|
*size = heap_ptr - heap_start - sizeof ( struct heap_block );
|
2005-05-12 16:34:57 +00:00
|
|
|
return emalloc ( *size, sizeof ( void * ) );
|
|
|
|
}
|
2005-03-08 18:53:11 +00:00
|
|
|
|
2005-05-12 16:34:57 +00:00
|
|
|
/*
|
|
|
|
* Free a heap block
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
void efree ( void *ptr ) {
|
|
|
|
struct heap_block *block;
|
2005-03-08 18:53:11 +00:00
|
|
|
|
2005-05-13 10:18:21 +00:00
|
|
|
ASSERT ( ptr == phys_to_virt ( heap_ptr + sizeof ( size_t ) ) );
|
2005-03-08 18:53:11 +00:00
|
|
|
|
2005-05-12 16:34:57 +00:00
|
|
|
block = ( struct heap_block * )
|
|
|
|
( ptr - offsetof ( struct heap_block, data ) );
|
|
|
|
heap_ptr += block->size;
|
2005-03-08 18:53:11 +00:00
|
|
|
|
2005-05-13 11:24:02 +00:00
|
|
|
DBG ( "HEAP freed %x [%x,%x)\n", virt_to_phys ( ptr ),
|
|
|
|
virt_to_phys ( block ), heap_ptr );
|
|
|
|
|
2005-05-13 11:16:14 +00:00
|
|
|
ASSERT ( heap_ptr <= heap_end );
|
2005-05-12 16:34:57 +00:00
|
|
|
}
|
2005-03-08 18:53:11 +00:00
|
|
|
|
2005-05-12 16:34:57 +00:00
|
|
|
/*
|
|
|
|
* Free all allocated heap blocks
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
void efree_all ( void ) {
|
2005-05-13 11:24:02 +00:00
|
|
|
DBG ( "HEAP discarding allocated blocks in [%x,%x)\n",
|
|
|
|
heap_ptr, heap_end );
|
|
|
|
|
2005-05-13 11:16:14 +00:00
|
|
|
heap_ptr = heap_end;
|
2005-03-08 18:53:11 +00:00
|
|
|
}
|
2005-04-08 15:01:17 +00:00
|
|
|
|
2005-05-12 16:34:57 +00:00
|
|
|
INIT_FN ( INIT_HEAP, init_heap, efree_all, NULL );
|