Zookeeper/PageManager.cpp
2016-05-20 11:42:11 +02:00

174 lines
4.4 KiB
C++

#include "Zookeeper.hpp"
PageManager::PageManager() {
virtgroup_t group;
group.start = 0x10000000;
group.end = KBASE;
group.count = (group.end - group.start) / PAGE_SIZE;
virtGroups.push_back(group);
}
void PageManager::add_region(uint32_t base, uint32_t size) {
if(size & PAGE_MASK)
size = (size & ~PAGE_MASK) + PAGE_SIZE;
for(uint32_t addr = 0; addr < base + size; addr += PAGE_SIZE)
freePhysPages.push_back(addr);
}
uint32_t PageManager::map(uint32_t base, uint32_t count) {
if(base == 0)
base = box->pm->alloc_virt(count);
for(auto i = 0; i < count; ++i) {
auto virt = base + i * PAGE_SIZE;
if(!box->cpu->is_mapped(virt))
box->cpu->map_pages(virt, box->pm->alloc_phys(), 1);
}
return base;
}
uint32_t PageManager::map_contiguous(uint32_t base, uint32_t phys_low, uint32_t phys_high, uint32_t count) {
if(base == 0)
base = box->pm->alloc_virt(count, 0x80000000); // D3D wants a higher half address
auto phys_base = box->pm->alloc_phys(count, phys_low, phys_high);
box->cpu->map_pages(base, phys_base, count);
return base;
}
void PageManager::unmap(uint32_t base, uint32_t count) {
auto addr = base;
for(auto i = 0; i < count; ++i) {
box->pm->free_phys(box->cpu->virt2phys(addr));
addr += PAGE_SIZE;
}
box->pm->free_virt(base, count);
}
uint32_t PageManager::alloc_phys(uint32_t count, uint32_t phys_low, uint32_t phys_high) {
bailout(freePhysPages.size() < count);
if(count == 1) {
for(auto iter = freePhysPages.begin(); iter != freePhysPages.end(); ++iter) {
if(phys_low < *iter && phys_high >= (*iter + PAGE_SIZE)) {
auto page = *iter;
freePhysPages.erase(iter);
return page;
}
}
bailout(true); // Could not find page in range
} else {
for(auto base : freePhysPages) {
if(!(phys_low < base && (base + count * PAGE_SIZE) <= phys_high))
continue;
// This is the least efficient search ever.
auto found = true;
for(auto i = 1; i < count; ++i) {
if(find(freePhysPages.begin(), freePhysPages.end(), base + i * PAGE_SIZE) == freePhysPages.end()) {
found = false;
break;
}
}
if(found) {
for(auto i = 0; i < count; ++i) {
auto iter = find(freePhysPages.begin(), freePhysPages.end(), base + i * PAGE_SIZE);
freePhysPages.erase(iter);
}
return base;
}
}
bailout(true); // Could not find enough contiguous pages in range
}
}
void PageManager::free_phys(uint32_t page) {
freePhysPages.push_back(page);
}
uint32_t PageManager::alloc_virt(uint32_t count, uint32_t low) {
for(auto iter = virtGroups.begin(); iter != virtGroups.end(); ++iter) {
if(iter->count >= count && iter->end > low + count) {
if(iter->start >= low) {
auto start = iter->start;
if(iter->count == count)
virtGroups.erase(iter);
else {
iter->count -= count;
iter->start += count * PAGE_SIZE;
}
return start;
} else {
auto off = low - iter->start;
auto coff = off / PAGE_SIZE;
auto start = low;
if(iter->count == coff + count) {
iter->count -= count;
iter->end -= count * PAGE_SIZE;
} else {
auto oend = iter->end;
auto ocount = iter->count;
iter->count = coff;
iter->end = low;
virtgroup_t group;
group.count = ocount - count - coff;
group.start = low + count * PAGE_SIZE;
group.end = oend;
virtGroups.insert(++iter, group);
}
return start;
}
}
}
bailout(true); // Couldn't find a group with a large enough count
}
void PageManager::free_virt(uint32_t start, uint32_t count) {
auto end = start + count * PAGE_SIZE;
if(virtGroups.size() == 0) {
virtgroup_t group;
group.start = start;
group.end = end;
group.count = count;
virtGroups.push_back(group);
} else{
for(auto iter = virtGroups.begin(); iter != virtGroups.end(); ++iter) {
if(iter->start == end) {
iter->start = start;
iter->count += count;
break;
} else if(iter->start > end) {
virtgroup_t group;
group.start = start;
group.end = end;
group.count = count;
virtGroups.insert(iter, group);
break;
} else if(iter->end == start) {
iter->end = end;
iter->count += count;
auto group = *iter;
++iter;
if(iter->start == end) {
iter->start = group.start;
iter->count += group.count;
virtGroups.erase(--iter);
}
break;
}
}
}
}
void PageManager::debug() {
for(auto group : virtGroups) {
cout << "Group of " << dec << group.count << " pages @ " << hex << group.start << endl;
}
cout << endl;
}