[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:
2020-09-26 21:47:15 -07:00
parent 0e0975e5f6
commit 13aee1755e
19 changed files with 533 additions and 190 deletions

View File

@@ -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);