From 806bfd1fbff03a107fea26f55bb2e71d3ec25927 Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Wed, 17 Apr 2019 19:16:54 -0700 Subject: [PATCH] First pass at vm_space (address_manager replacement) --- modules.yaml | 1 + src/kernel/main.cpp | 8 + src/libraries/kutil/include/kutil/avl_tree.h | 172 +++++++++++++++++++ src/libraries/kutil/include/kutil/vm_space.h | 74 ++++++++ src/libraries/kutil/vm_space.cpp | 136 +++++++++++++++ 5 files changed, 391 insertions(+) create mode 100644 src/libraries/kutil/include/kutil/avl_tree.h create mode 100644 src/libraries/kutil/include/kutil/vm_space.h create mode 100644 src/libraries/kutil/vm_space.cpp diff --git a/modules.yaml b/modules.yaml index fe36c61..590e897 100644 --- a/modules.yaml +++ b/modules.yaml @@ -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 diff --git a/src/kernel/main.cpp b/src/kernel/main.cpp index 4d6c5e5..4c15098 100644 --- a/src/kernel/main.cpp +++ b/src/kernel/main.cpp @@ -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()) diff --git a/src/libraries/kutil/include/kutil/avl_tree.h b/src/libraries/kutil/include/kutil/avl_tree.h new file mode 100644 index 0000000..bc7574e --- /dev/null +++ b/src/libraries/kutil/include/kutil/avl_tree.h @@ -0,0 +1,172 @@ +#pragma once +/// \file avl_tree.h +/// Templated container class for an AVL tree + +#include +#include "kutil/assert.h" + +namespace kutil { + +template class avl_tree; + + +/// A node in a `avl_tree` +template +class avl_node : + public T +{ +public: + using item_type = T; + using node_type = avl_node; + + /// 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; + + 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 +class avl_tree +{ +public: + using item_type = T; + using node_type = avl_node; + + 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 diff --git a/src/libraries/kutil/include/kutil/vm_space.h b/src/libraries/kutil/include/kutil/vm_space.h new file mode 100644 index 0000000..482285c --- /dev/null +++ b/src/libraries/kutil/include/kutil/vm_space.h @@ -0,0 +1,74 @@ +#pragma once +/// \file vm_range.h +/// Structure for tracking a range of virtual memory addresses + +#include +#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; + using tree_type = kutil::avl_tree; + + node_type * split_out(node_type* node, uintptr_t start, size_t size, vm_state state); + + slab_allocator m_alloc; + tree_type m_ranges; +}; + +} // namespace kutil diff --git a/src/libraries/kutil/vm_space.cpp b/src/libraries/kutil/vm_space.cpp new file mode 100644 index 0000000..1fd3dfc --- /dev/null +++ b/src/libraries/kutil/vm_space.cpp @@ -0,0 +1,136 @@ +#include +#include "kutil/logger.h" +#include "kutil/vm_space.h" + +namespace kutil { + +using node_type = kutil::avl_node; + + +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