[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:
@@ -32,6 +32,8 @@ process::process(page_table *kpml4) :
|
||||
|
||||
process::~process()
|
||||
{
|
||||
for (auto &it : m_handles)
|
||||
if (it.val) it.val->handle_release();
|
||||
s_processes.remove_swap(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -19,9 +19,7 @@ enum class vm_flags : uint32_t
|
||||
contiguous = 0x00000002,
|
||||
|
||||
large_pages = 0x00000100,
|
||||
huge_pages = 0x00000200,
|
||||
|
||||
offset_linear = 0x80000000
|
||||
huge_pages = 0x00000200
|
||||
};
|
||||
|
||||
IS_BITFIELD(vm_flags);
|
||||
@@ -55,14 +53,12 @@ public:
|
||||
/// the given base address is zero, a base address will be chosen
|
||||
/// automatically.
|
||||
/// \arg s The target address space
|
||||
/// \arg base [in] The desired base address [out] the actual base address
|
||||
/// \returns j6_status_ok on success
|
||||
j6_status_t add_to(vm_space *s, uintptr_t *base);
|
||||
/// \arg base The base address this area will be mapped to
|
||||
void add_to(vm_space *s, uintptr_t base);
|
||||
|
||||
/// Remove this virtual area from a process' virtual address space.
|
||||
/// \arg s The target address space
|
||||
/// \returns j6_status_ok on success
|
||||
j6_status_t remove_from(vm_space *s);
|
||||
void remove_from(vm_space *s);
|
||||
|
||||
/// Commit contiguous physical pages to this area
|
||||
/// \arg phys The physical address of the first page
|
||||
@@ -77,35 +73,17 @@ public:
|
||||
/// \returns True if successful
|
||||
bool uncommit(uintptr_t offset, size_t count);
|
||||
|
||||
/// Reserve a range of this area to never commit
|
||||
/// \arg offset The offset from the start of this area
|
||||
/// \arg count The number of pages
|
||||
/// \returns True if successful
|
||||
bool reserve(uintptr_t offset, size_t count);
|
||||
|
||||
/// Unreserve a range of this area to allow commits
|
||||
/// \arg offset The offset from the start of this area
|
||||
/// \arg count The number of pages
|
||||
/// \returns True if successful
|
||||
bool unreserve(uintptr_t offset, size_t count);
|
||||
|
||||
enum class state : uint8_t { none, reserved, mapped };
|
||||
|
||||
/// Get the physical page representing an offset in this area
|
||||
/// \arg offset The offset into the area
|
||||
/// \arg phys [out] The physical page address
|
||||
/// \returns State of the given address
|
||||
state get(uintptr_t offset, uintptr_t *phys);
|
||||
|
||||
/// Get the flags set for this area
|
||||
vm_flags flags() const { return m_flags; }
|
||||
|
||||
protected:
|
||||
virtual void on_no_handles() override;
|
||||
|
||||
private:
|
||||
struct mapping {
|
||||
uintptr_t offset;
|
||||
size_t count;
|
||||
uintptr_t phys;
|
||||
state state;
|
||||
|
||||
int compare(const struct mapping &o) const {
|
||||
return offset > o.offset ? 1 : offset < o.offset ? -1 : 0;
|
||||
@@ -116,14 +94,12 @@ private:
|
||||
};
|
||||
|
||||
size_t overlaps(uintptr_t offset, size_t pages, size_t *count);
|
||||
bool add(uintptr_t offset, size_t count, state desired, uintptr_t phys);
|
||||
bool remove(uintptr_t offset, size_t count, state expected);
|
||||
|
||||
void map(uintptr_t offset, size_t count, uintptr_t phys);
|
||||
void unmap(uintptr_t offset, size_t count);
|
||||
|
||||
size_t m_size;
|
||||
vm_flags m_flags;
|
||||
kutil::map<vm_space*, uintptr_t> m_procs;
|
||||
kutil::map<vm_space*, uintptr_t> m_spaces;
|
||||
kutil::vector<mapping> m_mappings;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user