First pass at vm_space (address_manager replacement)

This commit is contained in:
Justin C. Miller
2019-04-17 19:16:54 -07:00
parent 910b5116f4
commit 806bfd1fbf
5 changed files with 391 additions and 0 deletions

View File

@@ -106,6 +106,7 @@ modules:
- src/libraries/kutil/logger.cpp
- src/libraries/kutil/memory.cpp
- src/libraries/kutil/printf.c
- src/libraries/kutil/vm_space.cpp
makerd:
kind: exe

View File

@@ -3,6 +3,7 @@
#include "initrd/initrd.h"
#include "kutil/assert.h"
#include "kutil/vm_space.h"
#include "apic.h"
#include "block_device.h"
#include "console.h"
@@ -12,6 +13,7 @@
#include "interrupts.h"
#include "io.h"
#include "kernel_args.h"
#include "kernel_memory.h"
#include "log.h"
#include "page_manager.h"
#include "scheduler.h"
@@ -71,6 +73,12 @@ kernel_main(kernel_args *header)
log::debug(logs::boot, "ACPI root table is at: %016lx", header->acpi_table);
log::debug(logs::boot, "Runtime service is at: %016lx", header->runtime);
kutil::vm_space k_space(
memory::kernel_offset,
memory::page_offset - memory::kernel_offset,
heap);
k_space.reserve(0xffffff0000100000, 0x100000);
initrd::disk ird(header->initrd, heap);
log::info(logs::boot, "initrd loaded with %d files.", ird.files().count());
for (auto &f : ird.files())

View File

@@ -0,0 +1,172 @@
#pragma once
/// \file avl_tree.h
/// Templated container class for an AVL tree
#include <stdint.h>
#include "kutil/assert.h"
namespace kutil {
template <typename T> class avl_tree;
/// A node in a `avl_tree<T>`
template <typename T>
class avl_node :
public T
{
public:
using item_type = T;
using node_type = avl_node<T>;
/// Dereference operator. Helper to cast this node to the contained type.
/// \returns A pointer to the node, cast to T*.
inline item_type & operator*() { return *this; }
/// Dereference operator. Helper to cast this node to the contained type.
/// \returns A pointer to the node, cast to T*.
inline const item_type & operator*() const { return *this; }
/// Cast operator. Helper to cast this node to the contained type.
/// \returns A reference to the node, cast to T&.
inline operator item_type& () { return *this; }
/// Cast operator. Helper to cast this node to the contained type.
/// \returns A reference to the node, cast to const T&.
inline operator const item_type& () { return *this; }
/// Helper to cast this node to the contained type.
/// \returns A reference to the node, cast to const T&.
inline const item_type& item() const { return *this; }
/// Helper to cast this node to the contained type.
/// \returns A reference to the node, cast to T&.
inline item_type& item() { return *this; }
/// Accessor for the left child.
/// \returns A pointer to the left child, or nullptr.
inline node_type * left() { return m_left; }
/// Accessor for the left child.
/// \returns A pointer to the left child, or nullptr.
inline const node_type * left() const { return m_left; }
/// Accessor for the right child.
/// \returns A pointer to the right child, or nullptr.
inline node_type * right() { return m_right; }
/// Accessor for the right child.
/// \returns A pointer to the right child, or nullptr.
inline const node_type * right() const { return m_right; }
private:
friend class avl_tree<T>;
inline static int height(node_type *l) { return (l ? l->m_height : 0); }
// Update this node's height and return its new balance factor
inline int update_height()
{
int left = height(m_left);
int right = height(m_right);
m_height = std::max(left, right) + 1;
return left - right;
}
int bias(node_type *addend)
{
const item_type &this_item = *this;
const item_type &that_item = *addend;
if (that_item < this_item)
return -1;
else if (that_item > this_item)
return 1;
kassert(false, "Equal items not allowed in AVL tree");
return 0;
}
static node_type * rotate_right(node_type *existing)
{
node_type *root = existing->m_left;
node_type *left = root->m_right;
root->m_right = existing;
existing->m_left = left;
existing->update_height();
root->update_height();
return root;
}
static node_type * rotate_left(node_type *existing)
{
node_type *root = existing->m_right;
node_type *right = root->m_left;
root->m_left = existing;
existing->m_right = right;
existing->update_height();
root->update_height();
return root;
}
static node_type * insert(node_type *existing, node_type *addend)
{
if (existing == nullptr)
return addend;
if (existing->compare(addend) < 0)
existing->m_left = insert(existing->m_left, addend);
else
existing->m_right = insert(existing->m_right, addend);
int balance = existing->update_height();
if (balance > 1) {
// Left-heavy
if (existing->m_left->compare(addend) < 0) {
// Left Left
return rotate_right(existing);
} else {
// Left Right
existing->m_left = rotate_left(existing->m_left);
return rotate_right(existing);
}
} else if (balance < -1) {
// Right-heavy
if (existing->m_right->compare(addend) > 0) {
// Right Right
return rotate_left(existing);
} else {
// Right Left
existing->m_right = rotate_right(existing->m_right);
return rotate_left(existing);
}
}
return existing;
}
int m_height;
node_type *m_left;
node_type *m_right;
};
template <typename T>
class avl_tree
{
public:
using item_type = T;
using node_type = avl_node<T>;
inline void insert(node_type *addend) { m_root = node_type::insert(m_root, addend); }
inline node_type * root() { return m_root; }
private:
node_type *m_root;
};
} // namespace kutil

View File

@@ -0,0 +1,74 @@
#pragma once
/// \file vm_range.h
/// Structure for tracking a range of virtual memory addresses
#include <stdint.h>
#include "kutil/allocator.h"
#include "kutil/avl_tree.h"
#include "kutil/slab_allocator.h"
namespace kutil {
enum class vm_state : uint8_t {
unknown,
none,
reserved,
committed,
mapped
};
struct vm_range
{
uintptr_t address;
size_t size;
vm_state state;
inline uintptr_t end() const { return address + size; }
inline int compare(const vm_range *other) const {
return other->address - address;
}
};
class vm_space
{
public:
vm_space(uintptr_t start, size_t size, kutil::allocator &alloc);
/// 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);
/// 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);
/// Mark a section of address space as committed.
/// \arg start Starting address of reservaion
/// \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);
/// Mark a section of address space as uncommitted, but still reserved.
/// \arg start Starting address of reservaion
/// \arg size Size of reservation in bytes
void uncommit(uintptr_t start, size_t size);
/// Check the state of the given address.
/// \arg addr The address to check
/// \returns The state of the memory if known, or 'unknown'
vm_state get(uintptr_t addr);
private:
using node_type = kutil::avl_node<vm_range>;
using tree_type = kutil::avl_tree<vm_range>;
node_type * split_out(node_type* node, uintptr_t start, size_t size, vm_state state);
slab_allocator<node_type> m_alloc;
tree_type m_ranges;
};
} // namespace kutil

View File

@@ -0,0 +1,136 @@
#include <algorithm>
#include "kutil/logger.h"
#include "kutil/vm_space.h"
namespace kutil {
using node_type = kutil::avl_node<vm_range>;
vm_space::vm_space(uintptr_t start, size_t size, allocator &alloc) :
m_alloc(alloc)
{
node_type *node = m_alloc.pop();
node->address = start;
node->size = size;
node->state = vm_state::none;
m_ranges.insert(node);
log::info(logs::vmem, "Creating address space from %016llx-%016llx",
start, start+size);
}
inline static bool
overlaps(node_type *node, uintptr_t start, size_t size)
{
return start <= node->end() &&
(start + size) >= node->address;
}
static node_type *
find_overlapping(node_type *from, uintptr_t start, size_t size)
{
while (from) {
if (overlaps(from, start, size))
return from;
from = start < from->address ?
from->left() :
from->right();
}
return nullptr;
}
node_type *
vm_space::split_out(node_type *node, uintptr_t start, size_t size, vm_state state)
{
// No cross-boundary splits allowed for now
bool contained =
start >= node->address &&
start+size <= node->end();
kassert(contained,
"Tried to split an address range across existing boundaries");
if (!contained)
return nullptr;
vm_state old_state = node->state;
if (state == old_state)
return node;
log::debug(logs::vmem, "Splitting out region %016llx-%016llx[%d] from %016llx-%016llx[%d]",
start, start+size, state, node->address, node->end(), old_state);
if (node->address < start) {
// Split off rest into new node
size_t leading = start - node->address;
node_type *next = m_alloc.pop();
next->state = state;
next->address = start;
next->size = node->size - leading;
node->size = leading;
m_ranges.insert(next);
node = next;
} else {
// TODO: check if this can merge with prev node
}
if (node->end() > start + size) {
// Split off remaining into new node
size_t trailing = node->size - size;
node->size -= trailing;
node_type *next = m_alloc.pop();
next->state = old_state;
next->address = node->end();
next->size = trailing;
m_ranges.insert(next);
} else {
// TODO: check if this can merge with next node
}
return node;
}
uintptr_t
vm_space::reserve(uintptr_t start, size_t size)
{
log::debug(logs::vmem, "Reserving region %016llx-%016llx", start, start+size);
node_type *node = find_overlapping(m_ranges.root(), start, size);
if (!node || node->state != vm_state::none)
return 0;
node = split_out(node, start, size, vm_state::reserved);
return node ? start : 0;
}
void
vm_space::unreserve(uintptr_t start, size_t size)
{
}
uintptr_t
vm_space::commit(uintptr_t start, size_t size)
{
return 0;
}
void
vm_space::uncommit(uintptr_t start, size_t size)
{
}
vm_state
vm_space::get(uintptr_t addr)
{
node_type *node = find_overlapping(m_ranges.root(), addr, 0);
return node ? node->state : vm_state::unknown;
}
} // namespace kutil