[kernel] Begin replacing page_manager with vm_space

This is the first commit of several reworking the VM system. The main
focus is replacing page_manager's global functionality with objects
representing individual VM spaces. The main changes in this commit were:

- Adding the (as yet unused) vm_area object, which will be the main
  point of control for programs to allocate or share memory.
- Replace the old vm_space with a new one based on state in its page
  tables. They will also be containers for vm_areas.
- vm_space takes over from page_manager as the page fault handler
- Commented out the page walking in memory_bootstrap; I'll probably need
  to recreate this functionality, but it was broken as it was.
- Split out the page_table.h implementations from page_manager.cpp into
  the new page_table.cpp, updated it, and added page_table::iterator as
  well.
This commit is contained in:
2020-09-17 00:48:17 -07:00
parent ca7f78565d
commit 9aa08a70cf
16 changed files with 1004 additions and 401 deletions

View File

@@ -1,73 +1,93 @@
#pragma once
/// \file vm_range.h
/// \file vm_space.h
/// Structure for tracking a range of virtual memory addresses
#include <stdint.h>
#include "kutil/avl_tree.h"
#include "kutil/enum_bitfields.h"
#include "kutil/vector.h"
enum class vm_state : uint8_t {
unknown,
none,
reserved,
committed
};
struct vm_range
{
uintptr_t address;
size_t size;
vm_state state;
inline uintptr_t end() const { return address + size; }
inline int64_t compare(const vm_range *other) const {
if (address > other->address) return -1;
else if (address < other->address) return 1;
else return 0;
}
};
struct page_table;
class process;
class vm_area;
/// Tracks a region of virtual memory address space
class vm_space
{
public:
/// Default constructor. Define an empty range.
vm_space();
/// Constructor.
/// \arg pml4 The pml4 for this address space
/// \arg kernel True if this is the kernel address space
vm_space(page_table *pml4, bool kernel = false);
/// Constructor. Define a range of managed VM space.
/// \arg start Starting address of the managed space
/// \arg size Size of the managed space, in bytes
vm_space(uintptr_t start, size_t size);
~vm_space();
/// Reserve a section of address space.
/// \arg start Starting address of reservaion, or 0 for any address
/// \arg size Size of reservation in bytes
/// \returns The address of the reservation, or 0 on failure
uintptr_t reserve(uintptr_t start, size_t size);
/// Add a virtual memorty area to this address space
/// \arg base The starting address of the area
/// \arg area The area to add
/// \returns True if the add succeeded
bool add(uintptr_t base, vm_area *area);
/// Unreserve (and uncommit, if committed) a section of address space.
/// \arg start Starting address of reservaion
/// \arg size Size of reservation in bytes
void unreserve(uintptr_t start, size_t size);
/// Remove a virtual memory area from this address space
/// \arg area The area to remove
/// \returns True if the area was removed
bool remove(vm_area *area);
/// Mark a section of address space as committed.
/// \arg start Starting address of reservaion, or 0 for any address
/// \arg size Size of reservation in bytes
/// \returns The address of the reservation, or 0 on failure
uintptr_t commit(uintptr_t start, size_t size);
/// Check the state of the given address.
/// Get the virtual memory area corresponding to an address
/// \arg addr The address to check
/// \returns The state of the memory if known, or 'unknown'
vm_state get(uintptr_t addr);
/// \arg base [out] if not null, receives the base address of the area
/// \returns The vm_area, or nullptr if not found
vm_area * get(uintptr_t addr, uintptr_t *base = nullptr);
/// Check if this is the kernel space
inline bool is_kernel() const { return m_kernel; }
/// Get the kernel virtual memory space
static vm_space & kernel_space();
/// Add page mappings into this space's page tables
/// \arg addr The virtual address to map at
/// \arg count The number of pages
/// \arg phys The physical address of the first page
void page_in(uintptr_t addr, size_t count, uintptr_t phys);
/// Remove page mappings from this space's page tables
/// \arg addr The virtual address to unmap
/// \arg count The number of pages
void page_out(uintptr_t addr, size_t count);
/// Mark whether allocation is allowed or not in a range of
/// virtual memory.
/// \arg start The starting virtual address of the area
/// \arg length The length in bytes of the area
/// \arg allow True if allocation should be allowed
void allow(uintptr_t start, size_t length, bool allow);
enum class fault_type : uint8_t {
none = 0x00,
present = 0x01,
write = 0x02,
user = 0x04,
reserved = 0x08,
fetch = 0x10
};
/// Handle a page fault.
/// \arg addr Address which caused the fault
/// \arg ft Flags from the interrupt about the kind of fault
/// \returns True if the fault was successfully handled
bool handle_fault(uintptr_t addr, fault_type fault);
private:
using node_type = kutil::avl_node<vm_range>;
using tree_type = kutil::avl_tree<vm_range>;
bool m_kernel;
page_table *m_pml4;
node_type * split_out(node_type* node, uintptr_t start, size_t size, vm_state state);
node_type * consolidate(node_type* needle);
node_type * find_empty(node_type* node, size_t size, vm_state state);
tree_type m_ranges;
struct area {
uintptr_t base;
vm_area *area;
int compare(const struct area &o) const;
bool operator==(const struct area &o) const;
};
kutil::vector<area> m_areas;
};
IS_BITFIELD(vm_space::fault_type);