Add simple page allocator and doubly-linked list implementation.
This commit is contained in:
		
							
								
								
									
										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; | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user