26b25a431ceff5ca74a77594f022cd772a5d5f9c
[people/lynusvaz/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 /* Linker symbols */
12 extern char _text[];
13 extern char _end[];
14
15 static unsigned long heap_start, heap_end, heap_ptr;
16
17 /*
18  * Find the largest contiguous area of memory that I can use for the
19  * heap.
20  *
21  */
22 static void init_heap ( void ) {
23         unsigned int i;
24         unsigned long eb_start, eb_end;
25         unsigned long size;
26
27         size = 0;
28         
29         /* Region occupied by Etherboot */
30         eb_start = virt_to_phys ( _text );
31         eb_end = virt_to_phys ( _end );
32
33         for ( i = 0 ; i < meminfo.map_count ; i++ ) {
34                 unsigned long r_start, r_end, r_size;
35                 unsigned long pre_eb, post_eb;
36
37                 /* Get start and end addresses of the region */
38                 if ( meminfo.map[i].type != E820_RAM )
39                         continue;
40                 if ( meminfo.map[i].addr > ULONG_MAX )
41                         continue;
42                 r_start = meminfo.map[i].addr;
43                 if ( r_start + meminfo.map[i].size > ULONG_MAX ) {
44                         r_end = ULONG_MAX;
45                 } else {
46                         r_end = r_start + meminfo.map[i].size;
47                 }
48                 
49                 /* Avoid overlap with Etherboot.  When Etherboot is
50                  * completely contained within the region, choose the
51                  * larger of the two remaining portions.
52                  */
53                 if ( ( eb_start < r_end ) && ( eb_end > r_start ) ) {
54                         pre_eb = ( eb_start > r_start ) ?
55                                 ( eb_start - r_start ) : 0;
56                         post_eb = ( r_end > eb_end ) ?
57                                 ( r_end - eb_end ) : 0;
58                         if ( pre_eb > post_eb ) {
59                                 r_end = eb_start;
60                         } else {
61                                 r_start = eb_end;
62                         }
63                 }
64
65                 /* Use the biggest region.  Where two regions are the
66                  * same size, use the later region.  (Provided that
67                  * the memory map is laid out in a sensible order,
68                  * this should give us the higher region.)
69                  */
70                 r_size = r_end - r_start;
71                 if ( r_size >= size ) {
72                         heap_start = r_start;
73                         heap_end = r_end;
74                         size = r_size;
75                 }
76         }
77
78         ASSERT ( size != 0 );
79         heap_ptr = heap_end;
80 }
81
82 /*
83  * Allocate a block from the heap.
84  *
85  */
86 void * emalloc ( size_t size, unsigned int align ) {
87         physaddr_t addr;
88         struct heap_block *block;
89         
90         ASSERT ( ( align & ( align - 1 ) ) == 0 );
91         
92         addr = ( ( ( heap_ptr - size ) & ~( align - 1 ) )
93                  - sizeof ( struct heap_block ) );
94         if ( addr < heap_start ) {
95                 return NULL;
96         }
97
98         block = phys_to_virt ( addr );
99         block->size = ( heap_ptr - addr );
100         heap_ptr = addr;
101         return block->data;
102 }
103
104 /*
105  * Allocate all remaining space on the heap
106  *
107  */
108 void * emalloc_all ( size_t *size ) {
109         *size = heap_ptr - heap_start - sizeof ( struct heap_block );
110         return emalloc ( *size, sizeof ( void * ) );
111 }
112
113 /*
114  * Free a heap block
115  *
116  */
117 void efree ( void *ptr ) {
118         struct heap_block *block;
119
120         ASSERT ( ptr == phys_to_virt ( heap_ptr + sizeof ( size_t ) ) );
121         
122         block = ( struct heap_block * )
123                 ( ptr - offsetof ( struct heap_block, data ) );
124         heap_ptr += block->size;
125
126         ASSERT ( heap_ptr <= heap_end );
127 }
128
129 /*
130  * Free all allocated heap blocks
131  *
132  */
133 void efree_all ( void ) {
134         heap_ptr = heap_end;
135 }
136
137 INIT_FN ( INIT_HEAP, init_heap, efree_all, NULL );