[kernel] Spit out vm_area types
The vm_space allow() functionality was a bit janky; using VMAs for all regions would be a lot cleaner. To that end, this change: - Adds a "static array" ctor to kutil::vector for setting the kernel address space's VMA list. This way a kernel heap VMA can be created without the heap already existing. - Splits vm_area into different subclasses depending on desired behavior - Splits out the concept of vm_mapper which maps vm_areas to vm_spaces, so that some kinds of VMA can be inherently single-space - Implements VMA resizing so that userspace can grow allocations. - Obsolete page_table_indices is removed Also, the following bugs were fixed: - kutil::map iterators on empty maps no longer break - memory::page_count was doing page-align, not page-count See: Github bug #242 See: [frobozz blog post](https://jsix.dev/posts/frobozz/) Tags:
This commit is contained in:
@@ -22,6 +22,7 @@ process::process() :
|
||||
s_processes.append(this);
|
||||
}
|
||||
|
||||
// The "kernel process"-only constructor
|
||||
process::process(page_table *kpml4) :
|
||||
kobject {kobject::type::process},
|
||||
m_space {kpml4},
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include "frame_allocator.h"
|
||||
#include "kernel_memory.h"
|
||||
#include "objects/vm_area.h"
|
||||
#include "vm_space.h"
|
||||
@@ -6,64 +5,55 @@
|
||||
using memory::frame_size;
|
||||
|
||||
vm_area::vm_area(size_t size, vm_flags flags) :
|
||||
m_size(size),
|
||||
m_flags(flags),
|
||||
kobject(kobject::type::vma)
|
||||
m_size {size},
|
||||
m_flags {flags},
|
||||
kobject {kobject::type::vma}
|
||||
{
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
vm_area::~vm_area() {}
|
||||
|
||||
size_t
|
||||
vm_area::resize(size_t size)
|
||||
{
|
||||
if (mapper().can_resize(size))
|
||||
m_size = size;
|
||||
return m_size;
|
||||
}
|
||||
|
||||
void
|
||||
vm_area::add_to(vm_space *space, uintptr_t base)
|
||||
vm_area::commit(uintptr_t phys, uintptr_t offset, size_t count)
|
||||
{
|
||||
kassert(space, "null vm_space passed to vm_area::add_to");
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
m_spaces.insert(space, base);
|
||||
space->add(base, this);
|
||||
mapper().map(offset, count, phys);
|
||||
}
|
||||
|
||||
void
|
||||
vm_area::remove_from(vm_space *space)
|
||||
vm_area::uncommit(uintptr_t offset, size_t count)
|
||||
{
|
||||
mapper().unmap(offset, count);
|
||||
}
|
||||
|
||||
void
|
||||
vm_area::on_no_handles()
|
||||
{
|
||||
kobject::on_no_handles();
|
||||
delete this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
vm_area_shared::vm_area_shared(size_t size, vm_flags flags) :
|
||||
m_mapper {*this},
|
||||
vm_area {size, flags}
|
||||
{
|
||||
}
|
||||
|
||||
vm_area_shared::~vm_area_shared()
|
||||
{
|
||||
size_t pages = memory::page_count(m_size);
|
||||
uintptr_t *base = m_spaces.find(space);
|
||||
if (space && base) {
|
||||
space->clear(*base, pages);
|
||||
m_spaces.erase(space);
|
||||
}
|
||||
space->remove(this);
|
||||
}
|
||||
|
||||
size_t
|
||||
vm_area::overlaps(uintptr_t offset, size_t pages, size_t *count)
|
||||
vm_area_shared::overlaps(uintptr_t offset, size_t pages, size_t *count)
|
||||
{
|
||||
size_t first = 0;
|
||||
size_t n = 0;
|
||||
@@ -84,14 +74,16 @@ vm_area::overlaps(uintptr_t offset, size_t pages, size_t *count)
|
||||
return first;
|
||||
}
|
||||
|
||||
bool
|
||||
vm_area::commit(uintptr_t phys, uintptr_t offset, size_t count)
|
||||
void
|
||||
vm_area_shared::commit(uintptr_t phys, uintptr_t offset, size_t count)
|
||||
{
|
||||
size_t n = 0;
|
||||
size_t o = overlaps(offset, count, &n);
|
||||
|
||||
// Mapping overlaps not allowed
|
||||
if (n) return false;
|
||||
if (n) return;
|
||||
|
||||
m_mapper.map(offset, count, phys);
|
||||
|
||||
o = m_mappings.sorted_insert({
|
||||
.offset = offset,
|
||||
@@ -99,7 +91,6 @@ vm_area::commit(uintptr_t phys, uintptr_t offset, size_t count)
|
||||
.phys = phys});
|
||||
n = 1;
|
||||
|
||||
map(offset, count, phys);
|
||||
|
||||
// Try to expand to abutting similar areas
|
||||
if (o > 0 &&
|
||||
@@ -134,15 +125,17 @@ vm_area::commit(uintptr_t phys, uintptr_t offset, size_t count)
|
||||
if (n > 1)
|
||||
m_mappings.remove_at(o+1, n-1);
|
||||
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
bool
|
||||
vm_area::uncommit(uintptr_t offset, size_t count)
|
||||
void
|
||||
vm_area_shared::uncommit(uintptr_t offset, size_t count)
|
||||
{
|
||||
size_t n = 0;
|
||||
size_t o = overlaps(offset, count, &n);
|
||||
if (!n) return true;
|
||||
if (!n) return;
|
||||
|
||||
m_mapper.unmap(offset, count);
|
||||
|
||||
mapping *first = &m_mappings[o];
|
||||
mapping *last = &m_mappings[o+n-1];
|
||||
@@ -169,7 +162,7 @@ vm_area::uncommit(uintptr_t offset, size_t count)
|
||||
size_t remove_pages = first->count;
|
||||
first->count = leading / frame_size;
|
||||
remove_pages -= first->count;
|
||||
unmap(first->end(), remove_pages);
|
||||
m_mapper.unmap(first->end(), remove_pages);
|
||||
}
|
||||
|
||||
if (trailing) {
|
||||
@@ -178,7 +171,7 @@ vm_area::uncommit(uintptr_t offset, size_t count)
|
||||
last->offset = end;
|
||||
last->count = trailing / frame_size;
|
||||
remove_pages -= last->count;
|
||||
unmap(remove_off, remove_pages);
|
||||
m_mapper.unmap(remove_off, remove_pages);
|
||||
last->phys += remove_pages * frame_size;
|
||||
}
|
||||
|
||||
@@ -189,39 +182,32 @@ vm_area::uncommit(uintptr_t offset, size_t count)
|
||||
if (offset <= m.offset && end >= m.end()) {
|
||||
if (!delete_count) delete_start = i;
|
||||
++delete_count;
|
||||
unmap(m.offset, m.count);
|
||||
m_mapper.unmap(m.offset, m.count);
|
||||
}
|
||||
}
|
||||
|
||||
if (delete_count)
|
||||
m_mappings.remove_at(delete_start, delete_count);
|
||||
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
vm_area_open::vm_area_open(size_t size, vm_space &space, vm_flags flags) :
|
||||
m_mapper(*this, space),
|
||||
vm_area(size, flags)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
vm_area::map(uintptr_t offset, size_t count, uintptr_t phys)
|
||||
vm_area_open::commit(uintptr_t phys, uintptr_t offset, size_t count)
|
||||
{
|
||||
for (auto &it : m_spaces) {
|
||||
uintptr_t addr = it.val + offset;
|
||||
vm_space *space = it.key;
|
||||
space->page_in(addr, phys, count);
|
||||
}
|
||||
m_mapper.map(offset, count, phys);
|
||||
}
|
||||
|
||||
void
|
||||
vm_area::unmap(uintptr_t offset, size_t count)
|
||||
vm_area_open::uncommit(uintptr_t offset, size_t count)
|
||||
{
|
||||
for (auto &it : m_spaces) {
|
||||
uintptr_t addr = it.val + offset;
|
||||
vm_space *space = it.key;
|
||||
space->clear(addr, count);
|
||||
}
|
||||
m_mapper.unmap(offset, count);
|
||||
}
|
||||
|
||||
void
|
||||
vm_area::on_no_handles()
|
||||
{
|
||||
kobject::on_no_handles();
|
||||
delete this;
|
||||
}
|
||||
|
||||
@@ -2,12 +2,16 @@
|
||||
/// \file vm_area.h
|
||||
/// Definition of VMA objects and related functions
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "j6/signals.h"
|
||||
#include "kutil/enum_bitfields.h"
|
||||
#include "kutil/map.h"
|
||||
#include "kutil/vector.h"
|
||||
|
||||
#include "kernel_memory.h"
|
||||
#include "objects/kobject.h"
|
||||
#include "vm_mapper.h"
|
||||
|
||||
class vm_space;
|
||||
|
||||
@@ -15,14 +19,17 @@ enum class vm_flags : uint32_t
|
||||
{
|
||||
none = 0x00000000,
|
||||
|
||||
zero = 0x00000001,
|
||||
contiguous = 0x00000002,
|
||||
write = 0x00000001,
|
||||
exec = 0x00000002,
|
||||
|
||||
zero = 0x00000010,
|
||||
contiguous = 0x00000020,
|
||||
|
||||
large_pages = 0x00000100,
|
||||
huge_pages = 0x00000200
|
||||
};
|
||||
huge_pages = 0x00000200,
|
||||
|
||||
IS_BITFIELD(vm_flags);
|
||||
user_mask = 0x0000ffff ///< flags allowed via syscall
|
||||
};
|
||||
|
||||
|
||||
/// Virtual memory areas allow control over memory allocation
|
||||
@@ -30,16 +37,20 @@ class vm_area :
|
||||
public kobject
|
||||
{
|
||||
public:
|
||||
static constexpr kobject::type type = kobject::type::vma;
|
||||
|
||||
/// Constructor.
|
||||
/// \arg size Initial virtual size of the memory area
|
||||
/// \arg flags Flags for this memory area
|
||||
vm_area(size_t size, vm_flags flags = vm_flags::none);
|
||||
|
||||
virtual ~vm_area();
|
||||
|
||||
static constexpr kobject::type type = kobject::type::vma;
|
||||
|
||||
/// Get the current virtual size of the memory area
|
||||
size_t size() const { return m_size; }
|
||||
inline size_t size() const { return m_size; }
|
||||
|
||||
/// Get the flags set for this area
|
||||
inline vm_flags flags() const { return m_flags; }
|
||||
|
||||
/// Change the virtual size of the memory area. This may cause
|
||||
/// deallocation if the new size is smaller than the current size.
|
||||
@@ -49,37 +60,52 @@ public:
|
||||
/// \returns The new virtual size
|
||||
size_t resize(size_t size);
|
||||
|
||||
/// Add this virtual area to a process' virtual address space. If
|
||||
/// the given base address is zero, a base address will be chosen
|
||||
/// automatically.
|
||||
/// \arg s The target address space
|
||||
/// \arg base The base address this area will be mapped to
|
||||
void add_to(vm_space *s, uintptr_t base);
|
||||
/// Get the mapper object that maps this area to address spaces
|
||||
virtual vm_mapper & mapper() = 0;
|
||||
virtual const vm_mapper & mapper() const = 0;
|
||||
|
||||
/// Remove this virtual area from a process' virtual address space.
|
||||
/// \arg s The target address space
|
||||
void remove_from(vm_space *s);
|
||||
/// Check whether allocation at the given offset is allowed
|
||||
virtual bool allowed(uintptr_t offset) const { return true; }
|
||||
|
||||
/// Commit contiguous physical pages to this area
|
||||
/// \arg phys The physical address of the first page
|
||||
/// \arg offset The offset from the start of this area these pages represent
|
||||
/// \arg count The number of pages
|
||||
/// \returns True if successful
|
||||
bool commit(uintptr_t phys, uintptr_t offset, size_t count);
|
||||
virtual void commit(uintptr_t phys, uintptr_t offset, size_t count);
|
||||
|
||||
/// Uncommit physical pages from this area
|
||||
/// \arg offset The offset from the start of this area these pages represent
|
||||
/// \arg count The number of pages
|
||||
/// \returns True if successful
|
||||
bool uncommit(uintptr_t offset, size_t count);
|
||||
|
||||
/// Get the flags set for this area
|
||||
vm_flags flags() const { return m_flags; }
|
||||
virtual void uncommit(uintptr_t offset, size_t count);
|
||||
|
||||
protected:
|
||||
virtual void on_no_handles() override;
|
||||
|
||||
size_t m_size;
|
||||
vm_flags m_flags;
|
||||
};
|
||||
|
||||
|
||||
/// The standard, sharable, user-controllable VMA type
|
||||
class vm_area_shared :
|
||||
public vm_area
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg size Initial virtual size of the memory area
|
||||
/// \arg flags Flags for this memory area
|
||||
vm_area_shared(size_t size, vm_flags flags = vm_flags::none);
|
||||
virtual ~vm_area_shared();
|
||||
|
||||
virtual vm_mapper & mapper() override { return m_mapper; }
|
||||
virtual const vm_mapper & mapper() const override { return m_mapper; }
|
||||
|
||||
virtual void commit(uintptr_t phys, uintptr_t offset, size_t count) override;
|
||||
virtual void uncommit(uintptr_t offset, size_t count) override;
|
||||
|
||||
private:
|
||||
vm_mapper_multi m_mapper;
|
||||
|
||||
struct mapping {
|
||||
uintptr_t offset;
|
||||
size_t count;
|
||||
@@ -95,11 +121,72 @@ private:
|
||||
|
||||
size_t overlaps(uintptr_t offset, size_t pages, size_t *count);
|
||||
|
||||
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_spaces;
|
||||
kutil::vector<mapping> m_mappings;
|
||||
};
|
||||
|
||||
/// Area split into standard-sized segments
|
||||
class vm_area_buffers :
|
||||
public vm_area
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg size Initial virtual size of the memory area
|
||||
/// \arg space The address space this area belongs to
|
||||
/// \arg flags Flags for this memory area
|
||||
vm_area_buffers(size_t size, vm_space &space, vm_flags flags);
|
||||
|
||||
virtual vm_mapper & mapper() override { return m_mapper; }
|
||||
virtual const vm_mapper & mapper() const override { return m_mapper; }
|
||||
|
||||
virtual bool allowed(uintptr_t offset) const override;
|
||||
virtual void commit(uintptr_t phys, uintptr_t offset, size_t count) override;
|
||||
virtual void uncommit(uintptr_t offset, size_t count) override;
|
||||
|
||||
private:
|
||||
vm_mapper_single m_mapper;
|
||||
};
|
||||
|
||||
|
||||
/// Area backed by an external source (like a loaded program)
|
||||
class vm_area_backed :
|
||||
public vm_area
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg size Initial virtual size of the memory area
|
||||
/// \arg flags Flags for this memory area
|
||||
vm_area_backed(size_t size, vm_flags flags);
|
||||
|
||||
virtual vm_mapper & mapper() override { return m_mapper; }
|
||||
virtual const vm_mapper & mapper() const override { return m_mapper; }
|
||||
|
||||
virtual void commit(uintptr_t phys, uintptr_t offset, size_t count) override;
|
||||
virtual void uncommit(uintptr_t offset, size_t count) override;
|
||||
|
||||
private:
|
||||
vm_mapper_multi m_mapper;
|
||||
};
|
||||
|
||||
|
||||
/// Area that allows open allocation (eg, kernel heap)
|
||||
class vm_area_open :
|
||||
public vm_area
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg size Initial virtual size of the memory area
|
||||
/// \arg space The address space this area belongs to
|
||||
/// \arg flags Flags for this memory area
|
||||
vm_area_open(size_t size, vm_space &space, vm_flags flags);
|
||||
|
||||
virtual vm_mapper & mapper() override { return m_mapper; }
|
||||
virtual const vm_mapper & mapper() const override { return m_mapper; }
|
||||
|
||||
virtual void commit(uintptr_t phys, uintptr_t offset, size_t count) override;
|
||||
virtual void uncommit(uintptr_t offset, size_t count) override;
|
||||
|
||||
private:
|
||||
vm_mapper_single m_mapper;
|
||||
};
|
||||
|
||||
IS_BITFIELD(vm_flags);
|
||||
|
||||
Reference in New Issue
Block a user