[kernel] Add VMA interface

Finished the VMA kobject and added the related syscalls. Processes can
now allocate memory! Other changes in this commit:

- stop using g_frame_allocator and add frame_allocator::get()
- make sure to release all handles in the process dtor
- fix kutil::map::iterator never comparing to end()
This commit is contained in:
2020-09-23 00:29:05 -07:00
parent d4283731e4
commit 0e0975e5f6
13 changed files with 245 additions and 173 deletions

View File

@@ -1,6 +1,7 @@
#include "frame_allocator.h"
#include "kernel_memory.h"
#include "objects/process.h"
#include "objects/vm_area.h"
#include "vm_space.h"
using memory::frame_size;
@@ -13,6 +14,12 @@ vm_area::vm_area(size_t size, vm_flags flags) :
vm_area::~vm_area()
{
for (auto &it : m_spaces)
remove_from(it.key);
frame_allocator &fa = frame_allocator::get();
for (auto &m : m_mappings)
fa.free(m.phys, m.count);
}
size_t
@@ -21,40 +28,38 @@ vm_area::resize(size_t size)
return m_size;
}
j6_status_t
vm_area::add_to(vm_space *space, uintptr_t *base)
void
vm_area::add_to(vm_space *space, uintptr_t base)
{
if (!base || !space)
return j6_err_invalid_arg;
kassert(space, "null vm_space passed to vm_area::add_to");
uintptr_t *prev = m_procs.find(space);
if (prev) {
*base = *prev;
return j6_status_exists;
// Multiple copies in the same space not yet supported
uintptr_t *prev = m_spaces.find(space);
if (prev) return;
if (m_spaces.count()) {
// Just get the first address space in the map
auto it = m_spaces.begin();
vm_space *source = it->key;
uintptr_t from = it->val;
size_t pages = memory::page_count(m_size);
space->copy_from(*source, from, base, pages);
}
if (!*base)
return j6_err_nyi;
m_procs.insert(space, *base);
for (auto &m : m_mappings)
if (m.state == state::mapped)
space->page_in(*base + m.offset, m.count, m.phys);
return j6_status_ok;
m_spaces.insert(space, base);
space->add(base, this);
}
j6_status_t
void
vm_area::remove_from(vm_space *space)
{
uintptr_t *base = m_procs.find(space);
size_t pages = memory::page_count(m_size);
uintptr_t *base = m_spaces.find(space);
if (space && base) {
for (auto &m : m_mappings)
if (m.state == state::mapped)
space->clear(*base + m.offset, m.count);
m_procs.erase(space);
space->clear(*base, pages);
m_spaces.erase(space);
}
return j6_status_ok;
space->remove(this);
}
size_t
@@ -82,84 +87,34 @@ vm_area::overlaps(uintptr_t offset, size_t pages, size_t *count)
bool
vm_area::commit(uintptr_t phys, uintptr_t offset, size_t count)
{
return add(offset, count, state::mapped, phys);
}
bool
vm_area::uncommit(uintptr_t offset, size_t count)
{
return remove(offset, count, state::reserved);
}
bool
vm_area::reserve(uintptr_t offset, size_t count)
{
return add(offset, count, state::reserved, 0);
}
bool
vm_area::unreserve(uintptr_t offset, size_t count)
{
return remove(offset, count, state::reserved);
}
vm_area::state
vm_area::get(uintptr_t offset, uintptr_t *phys)
{
size_t n = 0;
size_t o = overlaps(offset, 1, &n);
if (n) {
mapping &m = m_mappings[o];
if (phys) *phys = m.phys;
return m.state;
}
return state::none;
}
bool
vm_area::add(uintptr_t offset, size_t count, state desired, uintptr_t phys)
{
const bool do_map = desired == state::mapped;
size_t n = 0;
size_t o = overlaps(offset, count, &n);
if (!n) {
// In the clear, map it
size_t o = m_mappings.sorted_insert({
.offset = offset,
.count = count,
.phys = phys,
.state = desired});
n = 1;
if (do_map)
map(offset, count, phys);
} else if (desired == state::mapped) {
// Mapping overlaps not allowed
return false;
}
// Mapping overlaps not allowed
if (n) return false;
// Any overlaps with different states is not allowed
for (size_t i = o; i < o+n; ++i)
if (m_mappings[i].state != desired)
return false;
o = m_mappings.sorted_insert({
.offset = offset,
.count = count,
.phys = phys});
n = 1;
map(offset, count, phys);
// Try to expand to abutting similar areas
if (o > 0 &&
m_mappings[o-1].state == desired &&
m_mappings[o-1].end() == offset &&
(!do_map || m_mappings[o-1].phys_end() == phys)) {
m_mappings[o-1].phys_end() == phys) {
--o;
++n;
}
uintptr_t end = offset + count * frame_size;
uintptr_t pend = offset + count * frame_size;
uintptr_t pend = phys + count * frame_size;
if (o + n < m_mappings.count() &&
m_mappings[o+n].state == desired &&
end == m_mappings[o+n].offset &&
(!do_map || m_mappings[o-1].phys == pend)) {
m_mappings[o-1].phys == pend) {
++n;
}
@@ -174,7 +129,7 @@ vm_area::add(uintptr_t offset, size_t count, state desired, uintptr_t phys)
(end > last.end() ? end : last.end()) -
first.offset;
first.count = diff / frame_size;
first.count = memory::page_count(diff);
if (n > 1)
m_mappings.remove_at(o+1, n-1);
@@ -182,19 +137,13 @@ vm_area::add(uintptr_t offset, size_t count, state desired, uintptr_t phys)
return true;
}
bool
vm_area::remove(uintptr_t offset, size_t count, state expected)
vm_area::uncommit(uintptr_t offset, size_t count)
{
size_t n = 0;
size_t o = overlaps(offset, count, &n);
if (!n) return true;
// Any overlaps with different states is not allowed
for (size_t i = o; i < o+n; ++i)
if (m_mappings[i].state != expected)
return false;
mapping *first = &m_mappings[o];
mapping *last = &m_mappings[o+n-1];
@@ -208,22 +157,19 @@ vm_area::remove(uintptr_t offset, size_t count, state expected)
size_t i = m_mappings.sorted_insert({
.offset = end,
.count = trailing / frame_size,
.state = first->state,
});
last = &m_mappings[i];
trailing = 0;
first->count -= last->count;
if (first->state == state::mapped)
last->phys = first->phys + first->count * frame_size;
last->phys = first->phys + first->count * frame_size;
}
if (leading) {
size_t remove_pages = first->count;
first->count = leading / frame_size;
remove_pages -= first->count;
if (expected == state::mapped)
unmap(first->end(), remove_pages);
unmap(first->end(), remove_pages);
}
if (trailing) {
@@ -232,10 +178,8 @@ vm_area::remove(uintptr_t offset, size_t count, state expected)
last->offset = end;
last->count = trailing / frame_size;
remove_pages -= last->count;
if (expected == state::mapped) {
unmap(remove_off, remove_pages);
last->phys += remove_pages * frame_size;
}
unmap(remove_off, remove_pages);
last->phys += remove_pages * frame_size;
}
size_t delete_start = 0;
@@ -245,8 +189,7 @@ vm_area::remove(uintptr_t offset, size_t count, state expected)
if (offset <= m.offset && end >= m.end()) {
if (!delete_count) delete_start = i;
++delete_count;
if (expected == state::mapped)
unmap(m.offset, m.count);
unmap(m.offset, m.count);
}
}
@@ -259,20 +202,26 @@ vm_area::remove(uintptr_t offset, size_t count, state expected)
void
vm_area::map(uintptr_t offset, size_t count, uintptr_t phys)
{
for (auto &it : m_procs) {
for (auto &it : m_spaces) {
uintptr_t addr = it.val + offset;
vm_space *space = it.key;
space->page_in(addr, count, phys);
}
space->page_in(addr, phys, count);
}
}
void
vm_area::unmap(uintptr_t offset, size_t count)
{
for (auto &it : m_procs) {
for (auto &it : m_spaces) {
uintptr_t addr = it.val + offset;
vm_space *space = it.key;
space->clear(addr, count);
}
}
void
vm_area::on_no_handles()
{
kobject::on_no_handles();
delete this;
}