diff --git a/include/kmalloc.h b/include/kmalloc.h new file mode 100644 index 0000000..ae4bc4d --- /dev/null +++ b/include/kmalloc.h @@ -0,0 +1,27 @@ +/* + Copyright (C) 2012, Aaron Lindsay + + This file is part of Aedrix. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef KMALLOC_H +#define KMALLOC_H + +void* kmalloc(unsigned int bytes); +void kfree(void *p); + +#endif /* KMALLOC_H */ diff --git a/kernel/kmalloc.c b/kernel/kmalloc.c new file mode 100644 index 0000000..44460b7 --- /dev/null +++ b/kernel/kmalloc.c @@ -0,0 +1,155 @@ +/* + Copyright (C) 2012, Aaron Lindsay + + This file is part of Aedrix. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include + +struct kmalloc_region { + struct dlist_node list; + void *end; +}; + +#define KMALLOC_MIN_ALIGNMENT 4 +#define KMALLOC_REGION_SIZE (sizeof(struct kmalloc_region) + (sizeof(struct kmalloc_region) % KMALLOC_MIN_ALIGNMENT ? KMALLOC_MIN_ALIGNMENT - sizeof(struct kmalloc_region) % KMALLOC_MIN_ALIGNMENT : 0)) //round actual struct size up so its aligned +#define KMALLOC_MAX_TRIES 3 //Max number of times the heap can be grown for a single request + + +int curr_page_power = 0; +struct dlist_node kmalloc_free_regions; + +void kmalloc_init() { + init_list(&kmalloc_free_regions); +} + +struct kmalloc_region *find_free_region(unsigned int bytes) { + struct kmalloc_region *it; + for_each_list(it, &kmalloc_free_regions, struct kmalloc_region, list) { + unsigned int size = (char *)it->end - (char*)it + 1; + if (size >= KMALLOC_REGION_SIZE + bytes) { + return it; + } + } + return (struct kmalloc_region *)0; +} + +struct kmalloc_region *join_regions(struct kmalloc_region *first, struct kmalloc_region *second) { + first->end = second->end; + remove(&second->list); + + //TODO free pages here if we get a big enough free space + return first; +} + +void coalesce(struct kmalloc_region *region) { + struct kmalloc_region *prev = 0, *next = 0; + + if (region->list.next != &kmalloc_free_regions) + next = container(region->list.next, struct kmalloc_region, list); + if (region->list.prev != &kmalloc_free_regions) + prev = container(region->list.prev, struct kmalloc_region, list); + + if (next && (char *)next == (char *)region->end + 1) { + region = join_regions(region, next); + coalesce(region); + } + if (prev && (char *)region == (char *)prev->end + 1) { + region = join_regions(prev, region); + coalesce(region); + } +} + +void add_region(struct kmalloc_region *region) { + struct kmalloc_region *it; + for_each_list(it, &kmalloc_free_regions, struct kmalloc_region, list) { + if (region < it) { + insert_before(&it->list, ®ion->list); + goto coalesce; + } + } + + insert_before(&kmalloc_free_regions, ®ion->list); + +coalesce: + coalesce(region); +} + +void *_kmalloc(unsigned int bytes, unsigned int tries); + +void *grow_and_retry(unsigned int bytes, unsigned int tries) { + struct page *p; + + if ((p = mm_get_free_pages(curr_page_power))) { + struct kmalloc_region *region = (struct kmalloc_region *)p->address; + //TODO don't throw away p, but keep it in a list (allocate a little at the beginning of this chunk for a list element), so we can free pages later if we want to + region->end = (char *)region + MM_PAGE_SIZE * (1 << curr_page_power) - 1; + add_region(region); + curr_page_power++; + return _kmalloc(bytes, tries); + } + + return 0; +} + +void *_kmalloc(unsigned int bytes, unsigned int tries) { + struct kmalloc_region *region; + + if (tries > KMALLOC_MAX_TRIES) + return 0; + + if (bytes % KMALLOC_MIN_ALIGNMENT) + bytes += KMALLOC_MIN_ALIGNMENT - (bytes % KMALLOC_MIN_ALIGNMENT); + + if ((region = find_free_region(bytes))) { + //if there's enough space leftover in the region after this allocation + //for another, split the region. + if ((unsigned int)((char *)region->end - (char *)region) + >= 2*KMALLOC_REGION_SIZE + KMALLOC_MIN_ALIGNMENT + bytes) { + struct kmalloc_region *leftovers; + leftovers = (struct kmalloc_region *)((char *)region + KMALLOC_REGION_SIZE + bytes); + leftovers->end = region->end; + region->end = (char *)leftovers - 1; + insert_after(®ion->list, &leftovers->list); + } + + remove(®ion->list); + return (char *)region + KMALLOC_REGION_SIZE; + + } else { + return grow_and_retry(bytes, tries + 1); + } +} + +void *kmalloc(unsigned int bytes) { + return _kmalloc(bytes, 1); +} + +void kfree(void *p) { + struct kmalloc_region *region; + + if (!p) { + print("Error: kfree was passed a null pointer. Ignoring. This indicates a possible memory leak.\n"); + return; + } + + region = (struct kmalloc_region *)((char *)p - KMALLOC_REGION_SIZE); + add_region(region); +} diff --git a/kernel/start_kernel.c b/kernel/start_kernel.c index 225ecd1..851641b 100644 --- a/kernel/start_kernel.c +++ b/kernel/start_kernel.c @@ -19,6 +19,7 @@ */ #include +#include #include #include #include @@ -41,8 +42,11 @@ void video(void) { console_init(&myfb); } -void test_memory() { +void test_mm() { struct page *p, *q; + + print("\ntest_mm():\n"); + p = mm_get_free_pages(0); if (p) print("%x, %x\n", p, p->address); @@ -69,8 +73,46 @@ void test_memory() { print("%x, %x\n", p, p->address); else print("Error: failed to allocate memory for p\n"); + mm_put_free_pages(p); + mm_put_free_pages(q); } + +void test_kmalloc() { + void *a, *b, *c, *d; + + print("\ntest_kmalloc():\n"); + + a = kmalloc(4); + print("a: %x\n", a); + b = kmalloc(13); + print("b: %x\n", b); + c = kmalloc(4); + print("c: %x\n", c); + d = kmalloc(25); + print("d: %x\n", d); + + kfree(c); + kfree(b); + kfree(a); + kfree(d); + + a = kmalloc(13); + print("a: %x\n", a); + b = kmalloc(4); + print("b: %x\n", b); + c = kmalloc(25); + print("c: %x\n", c); + d = kmalloc(7); + print("d: %x\n", d); +} + +void test_memory() { + test_mm(); + test_kmalloc(); +} + +void kmalloc_init(); int main(void) { char *lower, *upper; @@ -84,6 +126,7 @@ int main(void) { //setup memory mm_init(); + kmalloc_init(); if (atags_first_mem_region(&atags)) { print("Error: atags must contain at least one memory region\n");