Add simple page allocator and doubly-linked list implementation.
This commit is contained in:
parent
b531496f8e
commit
0b3865d16a
40
include/list.h
Normal file
40
include/list.h
Normal file
@ -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 */
|
20
include/mm.h
Normal file
20
include/mm.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#ifndef MM_H
|
||||||
|
#define MM_H
|
||||||
|
|
||||||
|
#include <list.h>
|
||||||
|
|
||||||
|
#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 */
|
@ -3,5 +3,7 @@ KERNEL_PREFIX = kernel
|
|||||||
KOBJS += $(KERNEL_PREFIX)/console.o
|
KOBJS += $(KERNEL_PREFIX)/console.o
|
||||||
KOBJS += $(KERNEL_PREFIX)/font.o
|
KOBJS += $(KERNEL_PREFIX)/font.o
|
||||||
KOBJS += $(KERNEL_PREFIX)/framebuffer.o
|
KOBJS += $(KERNEL_PREFIX)/framebuffer.o
|
||||||
|
KOBJS += $(KERNEL_PREFIX)/list.o
|
||||||
|
KOBJS += $(KERNEL_PREFIX)/mm.o
|
||||||
KOBJS += $(KERNEL_PREFIX)/print.o
|
KOBJS += $(KERNEL_PREFIX)/print.o
|
||||||
KOBJS += $(KERNEL_PREFIX)/start_kernel.o
|
KOBJS += $(KERNEL_PREFIX)/start_kernel.o
|
||||||
|
58
kernel/list.c
Normal file
58
kernel/list.c
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#include <list.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
103
kernel/mm.c
Normal file
103
kernel/mm.c
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
#include <list.h>
|
||||||
|
#include <mm.h>
|
||||||
|
#include <print.h>
|
||||||
|
|
||||||
|
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<<power;
|
||||||
|
struct page *it;
|
||||||
|
|
||||||
|
print("power: %d\n", num_pages);
|
||||||
|
|
||||||
|
if (list_empty(&mm_free_page_list)) {
|
||||||
|
print("Error: Out of memory\n");
|
||||||
|
return (struct page*)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for_each_list(it, &mm_free_page_list, struct page, list) {
|
||||||
|
unsigned int curr_pages = 1;
|
||||||
|
struct page *it2;
|
||||||
|
for (it2 = container(it->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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user