3476f4d252b6b5d970afdbf04d1913e56c7032ec
[people/xl0/gpxe.git] / src / core / heap.c
1 #include "etherboot.h"
2 #include "init.h"
3 #include "memsizes.h"
4 #include "heap.h"
5
6 #define ASSERT(...)
7
8 struct heap_block {
9         size_t size;
10         char data[0];
11 };
12
13 size_t heap_ptr, heap_top, heap_bot;
14
15 #define _virt_start 0
16
17 static void init_heap(void)
18 {
19         size_t size;
20         size_t start, end;
21         unsigned i;
22         /* Find the largest contiguous area of memory that
23          * I can use for the heap, which is organized as 
24          * a stack that grows backwards through memory.
25          */
26
27         /* If I have virtual address that do not equal physical addresses
28          * there is a change I will try to use memory from both sides of
29          * the virtual address space simultaneously, which can cause all kinds
30          * of interesting problems.
31          * Avoid it by logically extending etherboot.  Once I know that relocation
32          * works I can just start the virtual address space at 0, and this problem goes
33          * away so that is probably a better solution.
34          */
35 #if 0
36         start = virt_to_phys(_text);
37 #else
38         /* segment wrap around is nasty don't chance it. */
39         start = virt_to_phys(_virt_start);
40 #endif
41         end  = virt_to_phys(_end);
42         size = 0;
43         for(i = 0; i < meminfo.map_count; i++) {
44                 unsigned long r_start, r_end;
45                 if (meminfo.map[i].type != E820_RAM)
46                         continue;
47                 if (meminfo.map[i].addr > ULONG_MAX)
48                         continue;
49                 if (meminfo.map[i].size > ULONG_MAX)
50                         continue;
51                 
52                 r_start = meminfo.map[i].addr;
53                 r_end = r_start + meminfo.map[i].size;
54                 if (r_end < r_start) {
55                         r_end = ULONG_MAX;
56                 }
57                 /* Handle areas that overlap etherboot */
58                 if ((end > r_start) && (start < r_end)) {
59                         /* Etherboot completely covers the region */
60                         if ((start <= r_start) && (end >= r_end))
61                                 continue;
62                         /* Etherboot is completely contained in the region */
63                         if ((start > r_start) && (end < r_end)) {
64                                 /* keep the larger piece */
65                                 if ((r_end - end) >= (r_start - start)) {
66                                         r_start = end;
67                                 }
68                                 else {
69                                         r_end = start;
70                                 }
71                         }
72                         /* Etherboot covers one end of the region.
73                          * Shrink the region.
74                          */
75                         else if (end >= r_end) {
76                                 r_end = start;
77                         }
78                         else if (start <= r_start) {
79                                 r_start = end;
80                         }
81                 }
82                 /* If two areas are the size prefer the greater address */
83                 if (((r_end - r_start) > size) ||
84                         (((r_end - r_start) == size) && (r_start > heap_top))) {
85                         size = r_end - r_start;
86                         heap_top = r_start;
87                         heap_bot = r_end;
88                 }
89         }
90         if (size == 0) {
91                 printf("init_heap: No heap found.\n");
92                 exit(1);
93         }
94         heap_ptr = heap_bot;
95 }
96
97 /*
98  * Allocate a block from the heap.
99  *
100  */
101 void * emalloc ( size_t size, unsigned int align ) {
102         physaddr_t addr;
103         struct heap_block *block;
104         
105         ASSERT ( ! ( align & ( align - 1 ) ) );
106         
107         addr = ( ( ( heap_ptr - size ) & ~( align - 1 ) )
108                  - sizeof ( struct heap_block ) );
109         if ( addr < heap_top ) {
110                 return NULL;
111         }
112
113         block = phys_to_virt ( addr );
114         block->size = ( heap_ptr - addr );
115         heap_ptr = addr;
116         return block->data;
117 }
118
119 /*
120  * Allocate all remaining space on the heap
121  *
122  */
123 void * emalloc_all ( size_t *size ) {
124         *size = heap_ptr - heap_top - sizeof ( struct heap_block );
125         return emalloc ( *size, sizeof ( void * ) );
126 }
127
128 /*
129  * Free a heap block
130  *
131  */
132 void efree ( void *ptr ) {
133         struct heap_block *block;
134
135         ASSERT ( ptr == ( heap_ptr + sizeof ( size_t ) ) );
136         
137         block = ( struct heap_block * )
138                 ( ptr - offsetof ( struct heap_block, data ) );
139         heap_ptr += block->size;
140
141         ASSERT ( heap_ptr <= heap_bot );
142 }
143
144 /*
145  * Free all allocated heap blocks
146  *
147  */
148 void efree_all ( void ) {
149         heap_ptr = heap_bot;
150 }
151
152 INIT_FN ( INIT_HEAP, init_heap, efree_all, NULL );