2012-09-26 23:59:58 -04:00
|
|
|
/*
|
|
|
|
Copyright (C) 2012, Aaron Lindsay <aaron@aclindsay.com>
|
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
2012-09-17 23:27:11 -04:00
|
|
|
#include <list.h>
|
|
|
|
#include <mm.h>
|
|
|
|
#include <print.h>
|
2012-11-15 00:27:49 -05:00
|
|
|
#include <types.h>
|
2012-09-17 23:27:11 -04:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2012-09-29 23:26:43 -04:00
|
|
|
//If region starts at 0x0, make it start at next page to not screw up null pointer detection, etc.
|
|
|
|
if (start == 0)
|
|
|
|
start = (char *)start + MM_PAGE_SIZE;
|
|
|
|
|
2012-09-17 23:27:11 -04:00
|
|
|
//make sure both start and end address are aligned to the size of a page
|
2012-11-15 00:27:49 -05:00
|
|
|
if ((arch_uint_ptr)start % MM_PAGE_SIZE != 0)
|
|
|
|
start = (char*)start + (MM_PAGE_SIZE - ((arch_uint_ptr)start % MM_PAGE_SIZE));
|
|
|
|
if (((arch_uint_ptr)end + 1) % MM_PAGE_SIZE != 0)
|
|
|
|
end = (char*)end - ((arch_uint_ptr)end + 1) % MM_PAGE_SIZE;
|
2012-09-25 22:44:19 -04:00
|
|
|
|
|
|
|
if ((char *)end + 1 - (char *)start < MM_PAGE_SIZE<<1) {
|
2012-11-15 00:27:49 -05:00
|
|
|
print("Error: Supplied memory area(%x,%x) is smaller than the page size (%d)\n", (arch_uint_ptr)start, (arch_uint_ptr)end, MM_PAGE_SIZE);
|
2012-09-17 23:27:11 -04:00
|
|
|
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
|
2012-09-25 22:44:19 -04:00
|
|
|
num_pages = ((char *)end + 1 - (char *)start) / MM_PAGE_SIZE;
|
2012-09-17 23:27:11 -04:00
|
|
|
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;
|
|
|
|
|
|
|
|
if (list_empty(&mm_free_page_list)) {
|
|
|
|
print("Error: Out of memory\n");
|
|
|
|
return (struct page*)0;
|
|
|
|
}
|
|
|
|
|
2012-09-29 23:26:43 -04:00
|
|
|
if (!num_pages) {
|
|
|
|
print("Error: mm_get_free_pages must be called with power from 0 to 31, inclusive (power=%d)\n", power);
|
|
|
|
return (struct page*)0;
|
|
|
|
}
|
|
|
|
|
2012-09-17 23:27:11 -04:00
|
|
|
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;
|
|
|
|
}
|