1
0
Fork 0
aedrix-kernel/kernel/frames.c

146 lines
4.8 KiB
C

/*
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.
*/
#include <list.h>
#include <frames.h>
#include <print.h>
#include <types.h>
struct dlist_node free_frames_list;
void frames_init() {
init_list(&free_frames_list);
}
/*
* Adds a page frame struct to the list of free page frames. Presupposes
* free_frames_list is a properly initialized list.
*/
static void insert_page_frame(struct frame *p) {
if (list_empty(&free_frames_list) || p->address < container(free_frames_list.next, struct frame, list)->address) {
insert_after(&free_frames_list, &p->list);
} else if (p->address > container(free_frames_list.prev, struct frame, list)->address) {
insert_before(&free_frames_list, &p->list);
} else {
struct frame *it;
for_each_list(it, &free_frames_list, struct frame, list) {
if (p->address < it->address) {
insert_before(&it->list, &p->list);
return;
}
}
print("Error: failed to insert page frame\n");
}
}
/*
* Called to add a segment of memory to the frame allocation pool.
*/
static void add_physical_memory(void *start, void *end) {
unsigned int num_pages, usable_pages;
struct frame *p;
void *page;
//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 + CONFIG_PAGE_SIZE;
//make sure both start and end address are aligned to the size of a page
if ((arch_uint_ptr)start % CONFIG_PAGE_SIZE != 0)
start = (char*)start + (CONFIG_PAGE_SIZE - ((arch_uint_ptr)start % CONFIG_PAGE_SIZE));
if (((arch_uint_ptr)end + 1) % CONFIG_PAGE_SIZE != 0)
end = (char*)end - ((arch_uint_ptr)end + 1) % CONFIG_PAGE_SIZE;
if ((char *)end + 1 - (char *)start < CONFIG_PAGE_SIZE<<1) {
print("Error: Supplied memory area(%x,%x) is smaller than the page size (%d)\n", (arch_uint_ptr)start, (arch_uint_ptr)end, CONFIG_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 + 1 - (char *)start) / CONFIG_PAGE_SIZE;
usable_pages = num_pages - num_pages * sizeof(struct frame) / CONFIG_PAGE_SIZE;
if (num_pages * sizeof(struct frame) % CONFIG_PAGE_SIZE)
usable_pages--;
p = (struct frame *)start;
for (page = ((char *)start) + (num_pages - usable_pages)*CONFIG_PAGE_SIZE; page < end; page = (void *)(((char *)page) + CONFIG_PAGE_SIZE)) {
p->address = page;
insert_page_frame(p);
p++;
}
}
/*
* Attempt to get 2^power contiguous page frames. Returns the frame struct of
* the first frame on success or a null pointer on failure.
*/
struct frame* get_free_frames(unsigned int power) {
unsigned int num_pages = 1<<power;
struct frame *it;
if (list_empty(&free_frames_list)) {
print("Error: Out of memory\n");
return (struct frame*)0;
}
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 frame*)0;
}
for_each_list(it, &free_frames_list, struct frame, list) {
unsigned int curr_pages = 1;
struct frame *it2;
for (it2 = container(it->list.next, struct frame, list);
it2->list.next != &free_frames_list && curr_pages < num_pages;
it2 = container(it2->list.next, struct frame, list)) {
if ((char*)it2->address != (char*)container(it2->list.prev, struct frame, list)->address + CONFIG_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 frame*)0;
}
/*
* Return pages allocated to the pool of unused pages.
*/
int put_free_frames(struct frame *f) {
struct frame *it;
for_each_list(it, &free_frames_list, struct frame, list) {
if (f->address < it->address) {
insert_splice_before(&it->list, &f->list, f->list.prev);
return 0;
}
}
return 1;
}