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