From 0b3865d16aca8c82aac3016dbb747f126b781136 Mon Sep 17 00:00:00 2001 From: Aaron Lindsay Date: Mon, 17 Sep 2012 23:27:11 -0400 Subject: [PATCH] Add simple page allocator and doubly-linked list implementation. --- include/list.h | 40 +++++++++++++++++ include/mm.h | 20 +++++++++ kernel/Makefile.inc | 2 + kernel/list.c | 58 +++++++++++++++++++++++++ kernel/mm.c | 103 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 223 insertions(+) create mode 100644 include/list.h create mode 100644 include/mm.h create mode 100644 kernel/list.c create mode 100644 kernel/mm.c diff --git a/include/list.h b/include/list.h new file mode 100644 index 0000000..2552d89 --- /dev/null +++ b/include/list.h @@ -0,0 +1,40 @@ +#ifndef LIST_H +#define LIST_H + +/* + * Assuming ptr is a pointer to a member variable of type parent_type, and ptr + * is named member in parent_type, find the address of the struct which owns + * ptr. + */ +#define container(ptr, parent_type, member) \ + ((parent_type *) ((char *)ptr - (char *)&((parent_type *)0)->member)) + +/* + * Given a pointer to a struct (head) of type dlist_node, which points to a + * list of type parent_type, and a pointer of that type (it), this macro will + * execute the contained instructions once for each list element, with 'it' set + * to each element in the list, in turn. + */ +#define for_each_list(it, head, parent_type, member) \ + for (it = container((head)->next, parent_type, member); it->member.next != (head); it = container(it->member.next, parent_type, member)) + +#define for_each_list_noinit(it, head, parent_type, member) \ + for (; it->member.next != (head); it = container(it->member.next, parent_type, member)) + +struct dlist_node { + struct dlist_node *next, *prev; +}; + +void init_list(struct dlist_node *n); +int list_empty(struct dlist_node *n); + +//functions for dealing with single list elements +void insert_after(struct dlist_node *n, struct dlist_node *to_add); +void insert_before(struct dlist_node *n, struct dlist_node *to_add); +void remove(struct dlist_node *to_remove); + +//functions for dealing with one or more elements +void remove_splice(struct dlist_node *from, struct dlist_node *to); +void insert_splice_before(struct dlist_node *n, struct dlist_node *first, struct dlist_node *last); + +#endif /* LIST_H */ diff --git a/include/mm.h b/include/mm.h new file mode 100644 index 0000000..adca722 --- /dev/null +++ b/include/mm.h @@ -0,0 +1,20 @@ +#ifndef MM_H +#define MM_H + +#include + +#define MM_PAGE_SIZE 4096 + +struct page { + void *address; + struct dlist_node list; + char free; +}; + +void mm_init(); +void mm_add_free_region(void *start, void *end); + +struct page* mm_get_free_pages(unsigned int power); +int mm_put_free_pages(struct page *p); + +#endif /* MM_H */ diff --git a/kernel/Makefile.inc b/kernel/Makefile.inc index ed6f474..6ec7562 100644 --- a/kernel/Makefile.inc +++ b/kernel/Makefile.inc @@ -3,5 +3,7 @@ KERNEL_PREFIX = kernel KOBJS += $(KERNEL_PREFIX)/console.o KOBJS += $(KERNEL_PREFIX)/font.o KOBJS += $(KERNEL_PREFIX)/framebuffer.o +KOBJS += $(KERNEL_PREFIX)/list.o +KOBJS += $(KERNEL_PREFIX)/mm.o KOBJS += $(KERNEL_PREFIX)/print.o KOBJS += $(KERNEL_PREFIX)/start_kernel.o diff --git a/kernel/list.c b/kernel/list.c new file mode 100644 index 0000000..d39bbb2 --- /dev/null +++ b/kernel/list.c @@ -0,0 +1,58 @@ +#include + +void init_list(struct dlist_node *n) { + n->prev = n; + n->next = n; +} + +int list_empty(struct dlist_node *n) { + return n->next == n; +} + +void insert_after(struct dlist_node *n, struct dlist_node *to_add) { + to_add->next = n->next; + to_add->next->prev = to_add; + n->next = to_add; + to_add->prev = n; +} + +void insert_before(struct dlist_node *n, struct dlist_node *to_add) { + to_add->prev = n->prev; + to_add->prev->next = to_add; + n->prev = to_add; + to_add->next = n; +} + +void remove(struct dlist_node *to_remove) { + if (!list_empty(to_remove)) { + to_remove->next->prev = to_remove->prev; + to_remove->prev->next = to_remove->next; + init_list(to_remove); + } +} + +/* + * Remove a series of nodes from a list. Note that this assumes the list is not + * empty, and that 'from' comes before 'to' in the list. 'from' and 'to' will + * both be removed from the list, along with any nodes which come after 'from' + * but before 'to'. + */ +void remove_splice(struct dlist_node *from, struct dlist_node *to) { + to->next->prev = from->prev; + from->prev->next = to->next; + + //make the removed splice become its own wrap-around list, for easy access to the last member when re-splicing + to->next = from; + from->prev = to; +} + +/* + * Add a splice of nodes to a list (most likely a series previously removed via + * remove_splice()). + */ +void insert_splice_before(struct dlist_node *n, struct dlist_node *first, struct dlist_node *last) { + first->prev = n->prev; + first->prev->next = first; + n->prev = last; + last->next = n; +} diff --git a/kernel/mm.c b/kernel/mm.c new file mode 100644 index 0000000..232a6ae --- /dev/null +++ b/kernel/mm.c @@ -0,0 +1,103 @@ +#include +#include +#include + +struct dlist_node mm_free_page_list; + +void mm_init() { + init_list(&mm_free_page_list); +} + +//presupposes mm_free_page_list is a properly initialized list +void insert_page(struct page *p) { + if (list_empty(&mm_free_page_list) || p->address < container(mm_free_page_list.next, struct page, list)->address) { + insert_after(&mm_free_page_list, &p->list); + } else if (p->address > container(mm_free_page_list.prev, struct page, list)->address) { + insert_before(&mm_free_page_list, &p->list); + } else { + struct page *it; + for_each_list(it, &mm_free_page_list, struct page, list) { + if (p->address < it->address) { + insert_before(&it->list, &p->list); + return; + } + } + print("Error: failed to insert page\n"); + } +} + +void mm_add_free_region(void *start, void *end) { + unsigned int num_pages, usable_pages; + struct page *p; + void *page; + + //make sure both start and end address are aligned to the size of a page + if ((unsigned int)start & MM_PAGE_SIZE || (unsigned int)(end+1) & MM_PAGE_SIZE) { + print("Error: Supplied memory area(%x,%x) is not aligned to the page size (%d)\n", (unsigned int)start, (unsigned int)end, MM_PAGE_SIZE); + return; + } + if ((char *)end - (char *)start < MM_PAGE_SIZE<<1) { + print("Error: Supplied memory area(%x,%x) is not aligned to the page size (%d)\n", (unsigned int)start, (unsigned int)end, MM_PAGE_SIZE); + return; + } + + //reserve enough pages at the front of this chunk of memory to hold the + //page structs, then build the structs and add them to the list + + //TODO we're potentially losing memory here because we're calculating + //the number of page structs we need even for those pages that will contain only page structs + num_pages = ((char *)end - (char *)start) / MM_PAGE_SIZE; + usable_pages = num_pages - num_pages * sizeof(struct page) / MM_PAGE_SIZE; + if (num_pages * sizeof(struct page) % MM_PAGE_SIZE) + usable_pages--; + + p = (struct page *)start; + for (page = ((char *)start) + (num_pages - usable_pages)*MM_PAGE_SIZE; page < end; page = (void *)(((char *)page) + MM_PAGE_SIZE)) { + p->address = page; + insert_page(p); + p++; + } +} + +struct page* mm_get_free_pages(unsigned int power) { + unsigned int num_pages = 1<list.next, struct page, list); + it2->list.next != &mm_free_page_list && curr_pages < num_pages; + it2 = container(it2->list.next, struct page, list)) { + if ((char*)it2->address != (char*)container(it2->list.prev, struct page, list)->address + MM_PAGE_SIZE) { + it = it2; //fast-forward 'it' to start of next contiguous section of pages + break; + } else { + curr_pages++; + } + } + if (curr_pages == num_pages) { + remove_splice(&it->list, it2->list.prev); + return it; + } + } + return (struct page*)0; +} + +int mm_put_free_pages(struct page *p) { + struct page *it; + for_each_list(it, &mm_free_page_list, struct page, list) { + if (p->address < it->address) { + insert_splice_before(&it->list, &p->list, p->list.prev); + return 0; + } + } + return 1; +}