summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/core/heap.c135
1 files changed, 61 insertions, 74 deletions
diff --git a/src/core/heap.c b/src/core/heap.c
index 05fbb6bac..26b25a431 100644
--- a/src/core/heap.c
+++ b/src/core/heap.c
@@ -8,88 +8,75 @@ struct heap_block {
char data[0];
};
-size_t heap_ptr, heap_top, heap_bot;
+/* Linker symbols */
+extern char _text[];
+extern char _end[];
-#define _virt_start 0
+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;
-static void init_heap(void)
-{
- size_t size;
- size_t start, end;
- unsigned i;
- /* Find the largest contiguous area of memory that
- * I can use for the heap, which is organized as
- * a stack that grows backwards through memory.
- */
-
- /* If I have virtual address that do not equal physical addresses
- * there is a change I will try to use memory from both sides of
- * the virtual address space simultaneously, which can cause all kinds
- * of interesting problems.
- * Avoid it by logically extending etherboot. Once I know that relocation
- * works I can just start the virtual address space at 0, and this problem goes
- * away so that is probably a better solution.
- */
-#if 0
- start = virt_to_phys(_text);
-#else
- /* segment wrap around is nasty don't chance it. */
- start = virt_to_phys(_virt_start);
-#endif
- end = virt_to_phys(_end);
size = 0;
- for(i = 0; i < meminfo.map_count; i++) {
- unsigned long r_start, r_end;
- if (meminfo.map[i].type != E820_RAM)
- continue;
- if (meminfo.map[i].addr > ULONG_MAX)
+
+ /* 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 )
continue;
- if (meminfo.map[i].size > ULONG_MAX)
+ if ( meminfo.map[i].addr > ULONG_MAX )
continue;
-
r_start = meminfo.map[i].addr;
- r_end = r_start + meminfo.map[i].size;
- if (r_end < r_start) {
+ if ( r_start + meminfo.map[i].size > ULONG_MAX ) {
r_end = ULONG_MAX;
+ } else {
+ r_end = r_start + meminfo.map[i].size;
}
- /* Handle areas that overlap etherboot */
- if ((end > r_start) && (start < r_end)) {
- /* Etherboot completely covers the region */
- if ((start <= r_start) && (end >= r_end))
- continue;
- /* Etherboot is completely contained in the region */
- if ((start > r_start) && (end < r_end)) {
- /* keep the larger piece */
- if ((r_end - end) >= (r_start - start)) {
- r_start = end;
- }
- else {
- r_end = start;
- }
- }
- /* Etherboot covers one end of the region.
- * Shrink the region.
- */
- else if (end >= r_end) {
- r_end = start;
- }
- else if (start <= r_start) {
- r_start = end;
+
+ /* 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;
}
}
- /* If two areas are the size prefer the greater address */
- if (((r_end - r_start) > size) ||
- (((r_end - r_start) == size) && (r_start > heap_top))) {
- size = r_end - r_start;
- heap_top = r_start;
- heap_bot = r_end;
+
+ /* 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;
}
}
- if (size == 0) {
- printf("init_heap: No heap found.\n");
- exit(1);
- }
- heap_ptr = heap_bot;
+
+ ASSERT ( size != 0 );
+ heap_ptr = heap_end;
}
/*
@@ -104,7 +91,7 @@ void * emalloc ( size_t size, unsigned int align ) {
addr = ( ( ( heap_ptr - size ) & ~( align - 1 ) )
- sizeof ( struct heap_block ) );
- if ( addr < heap_top ) {
+ if ( addr < heap_start ) {
return NULL;
}
@@ -119,7 +106,7 @@ void * emalloc ( size_t size, unsigned int align ) {
*
*/
void * emalloc_all ( size_t *size ) {
- *size = heap_ptr - heap_top - sizeof ( struct heap_block );
+ *size = heap_ptr - heap_start - sizeof ( struct heap_block );
return emalloc ( *size, sizeof ( void * ) );
}
@@ -136,7 +123,7 @@ void efree ( void *ptr ) {
( ptr - offsetof ( struct heap_block, data ) );
heap_ptr += block->size;
- ASSERT ( heap_ptr <= heap_bot );
+ ASSERT ( heap_ptr <= heap_end );
}
/*
@@ -144,7 +131,7 @@ void efree ( void *ptr ) {
*
*/
void efree_all ( void ) {
- heap_ptr = heap_bot;
+ heap_ptr = heap_end;
}
INIT_FN ( INIT_HEAP, init_heap, efree_all, NULL );