malloc/calloc/free and minimal allocator testing
[lugburz] / src / amiga / mem.c
1 #include <stdio.h>
2 #include "serial.h"
3 #include "mem.h"
4 #include "amigalib.h"
5 #include "debug.h"
6
7 #define MAGIC           0xa55a
8 #define STACK_SIZE      4096
9
10 #define MEMTYPE_MASK    7
11 #define MEM_USED                0x8000
12
13 struct memrange {
14         uint16_t magic;
15         uint16_t attr;
16         uint32_t size;
17         struct memrange *next;
18         unsigned char start[];
19 };
20
21 enum {
22         POOL_FAST,
23         POOL_SLOW,
24         POOL_CHIP,
25         NUM_POOLS
26 };
27
28 static struct memrange *pool[NUM_POOLS];
29 static uint32_t stack_base, stack_top;
30
31 extern int _mem_start;
32
33 void move_init_stack(uint32_t newtop);  /* in startup.s */
34
35 static void add_range(int pidx, struct memrange *mr);
36
37
38 int init_mem(void)
39 {
40         int i;
41         struct memrange *mr, *smr;
42         struct alib_memnode *mem;
43         uint32_t mem_start = (uint32_t)&_mem_start;
44
45         printf("mem_start: %06lx\n", mem_start);
46
47         printf("Memory ranges:\n");
48         mem = execbase->memlist.head;
49         while(mem->n_next) {
50                 unsigned long start, end, size;
51                 char *stype;
52                 int type, pidx;
53
54                 /* assume we're dealing with at least 64k blocks */
55                 start = (unsigned long)mem->start & 0xff0000;
56                 end = ((unsigned long)mem->end + 0xffff) & 0xff0000;
57                 size = end - start;
58
59                 if(mem->attrib & ALIB_MEMF_CHIP) {
60                         stype = "chip";
61                         type = MEM_CHIP;
62                         pidx = POOL_CHIP;
63                 } else if(mem->attrib & ALIB_MEMF_FAST) {
64                         if(start >= 0xc00000 && end <= 0xd80000) {
65                                 stype = "slow";
66                                 type = MEM_SLOW;
67                                 pidx = POOL_SLOW;
68                         } else {
69                                 stype = "fast";
70                                 type = MEM_FAST;
71                                 pidx = POOL_FAST;
72                         }
73                 } else {
74                         printf("ignoring memory range %06lx - %06lx of unknown type: %u\n",
75                                         start, end, mem->attrib);
76                         mem = mem->n_next;
77                         continue;
78                 }
79
80                 printf(" %06lx - %06lx: %s (%ldk)\n", start, end, stype, size >> 10);
81                 mem = mem->n_next;
82
83                 if(mem_start >= start && mem_start < end) {
84                         size -= mem_start - start;
85                         start = mem_start;
86                 }
87
88                 mr = (struct memrange*)start;
89                 mr->magic = MAGIC;
90                 mr->attr = type;
91                 mr->size = size - sizeof *mr;
92                 add_range(pidx, mr);
93         }
94
95         /* allocate stack space at the top of a memory range, order of preference:
96          * fast, slow, chip
97          */
98         smr = 0;
99         for(i=0; i<NUM_POOLS; i++) {
100                 mr = pool[i];
101
102                 /* ranges in the list are sorted, so keep updating the potential stack
103                  * allocation until we reach the end of the list, and use the last one
104                  * we found that fits. Note: at this point it's not expected that any
105                  * one of the lists will have more than a single range, but this should
106                  * work regardless.
107                  */
108                 while(mr) {
109                         if(mr->size >= STACK_SIZE) {
110                                 stack_top = (uint32_t)mr->start + mr->size;
111                                 stack_base = stack_top - STACK_SIZE;
112                                 smr = mr;
113                         }
114                         mr = mr->next;
115                 }
116
117                 if(smr) {
118                         smr->size -= STACK_SIZE;
119                         break;
120                 }
121         }
122
123         if(!stack_base) {
124                 panic("Failed to allocate stack, no suitable memory ranges found!\n");
125         }
126
127         printf("Allocated %dk stack space: %06lx - %06lx\n", STACK_SIZE >> 10, stack_base, stack_top);
128         move_init_stack(stack_top);
129
130         return 0;
131 }
132
133 #define MIN_MEMRANGE    8
134
135 void *mem_alloc(unsigned int sz, unsigned int attr)
136 {
137         int i;
138         struct memrange *mr, *prev, dummy;
139         struct memrange *mem = 0;
140
141         if(!(attr & MEM_ANY)) {
142                 attr |= MEM_ANY;
143         }
144
145         for(i=0; i<NUM_POOLS; i++) {
146                 if(!(attr & (1 << i))) continue;
147
148                 dummy.next = pool[i];
149                 prev = &dummy;
150                 mr = dummy.next;
151                 while(mr) {
152                         if(mr->size >= sz) {
153                                 if(mr->size >= sz + sizeof *mr + MIN_MEMRANGE) {
154                                         /* we have enough space for a leftover piece */
155                                         mem = mr;
156
157                                         mr = (struct memrange*)(mem->start + sz);
158                                         *mr = *mem;
159                                         mr->size -= sz + sizeof *mr;
160                                         prev->next = mr;
161
162                                         mem->size = sz;
163                                         mem->attr |= MEM_USED;
164                                         mem->next = 0;
165                                 } else {
166                                         /* not enough leftover space, just allocate the whole range */
167                                         mem = mr;
168                                         mr->attr |= MEM_USED;
169                                         prev->next = mr->next;
170                                         mr->next = 0;
171                                 }
172                                 break;
173                         }
174                         prev = mr;
175                         mr = mr->next;
176                 }
177                 pool[i] = dummy.next;
178
179                 if(mem) {
180                         return mem->start;
181                 }
182         }
183
184         return 0;
185 }
186
187 void mem_free(void *ptr)
188 {
189         struct memrange *mr = (struct memrange*)ptr - 1;
190         int pidx = 0;
191
192         if(mr->magic != MAGIC || !(mr->attr & MEM_ANY)) {
193                 goto fail;
194         }
195
196         if(mr->attr & MEM_FAST) {
197                 pidx = 0;
198         } else if(mr->attr & MEM_SLOW) {
199                 pidx = 1;
200         } else if(mr->attr & MEM_CHIP) {
201                 pidx = 2;
202         } else {
203                 goto fail;
204         }
205
206         add_range(pidx, mr);
207         return;
208
209 fail:
210         printf("mem_free(%p): invalid pointer or magic corrupted\n", ptr);
211         memdump(mr, sizeof *mr);
212         panic("");
213 }
214
215 static void add_range(int pidx, struct memrange *mr)
216 {
217         struct memrange *prev, *next, dummy;
218
219         printf("DBG adding free range: %06lx - %06lx to pool %d\n", (unsigned long)mr,
220                         (unsigned long)(mr->start + mr->size), pidx);
221
222         dummy.next = pool[pidx];
223         prev = &dummy;
224
225         while(prev->next && prev->next < mr) {
226                 prev = prev->next;
227         }
228
229         mr->next = prev->next;
230         prev->next = mr;
231
232         /* coalesce */
233         if((next = mr->next) && mr->start + mr->size >= (unsigned char*)mr->next) {
234                 mr->size = next->start + next->size - mr->start;
235                 mr->next = next->next;
236                 next->magic = 0;
237                 printf("  DBG coalescing up\n");
238         }
239         if(prev != &dummy && prev->start + prev->size >= (unsigned char*)mr) {
240                 prev->size = mr->start + mr->size - prev->start;
241                 prev->next = mr->next;
242                 mr->magic = 0;
243                 printf("  DBG coalescing down\n");
244         }
245
246         pool[pidx] = dummy.next;
247 }
248
249 void dbg_memprint(void)
250 {
251         struct memrange *mr = pool[POOL_SLOW];
252
253         printf("MEM");
254         while(mr) {
255                 printf(" [%lx - %lx]", (unsigned long)mr, (unsigned long)(mr->start + mr->size));
256                 mr = mr->next;
257         }
258         putchar('\n');
259 }