From 6317e3ad00b392c33ab46cbfcfcd6d762275cde6 Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Sun, 12 Sep 2021 16:15:23 -0700 Subject: [PATCH] [kernel] Simplify page_tree code The page_tree struct was doing a lot of bit manipulation to keep its base, level, and flags in a single uint64_t. But since this is such a large structure anyway, another word doesn't change it much and greatly simplifies both the code and reasoning about it. --- src/kernel/page_tree.cpp | 100 ++++++++++++++++----------------------- src/kernel/page_tree.h | 13 ++++- 2 files changed, 52 insertions(+), 61 deletions(-) diff --git a/src/kernel/page_tree.cpp b/src/kernel/page_tree.cpp index cfe1964..d1b492c 100644 --- a/src/kernel/page_tree.cpp +++ b/src/kernel/page_tree.cpp @@ -1,77 +1,61 @@ #include "kutil/assert.h" #include "kutil/memory.h" #include "frame_allocator.h" +#include "kernel_memory.h" #include "page_tree.h" -// Page tree levels map the following parts of a pagewise offset: -// (Note that a level 0's entries are physical page addrs, the rest +// Page tree levels map the following parts of a pagewise offset. Note the xxx +// are not part of the offset but represent the bits added for the actual virtual +// address. (Also note that level 0's entries are physical page addrs, the rest // map other page_tree nodes) // -// Level 0: 0000000003f 64 pages / 256 KiB -// Level 1: 00000000fc0 4K pages / 16 MiB -// Level 2: 0000003f000 256K pages / 1 GiB -// Level 3: 00000fc0000 16M pages / 64 GiB -// Level 4: 0003f000000 1G pages / 4 TiB -// Level 5: 00fc0000000 64G pages / 256 TiB -// Level 6: 3f000000000 4T pages / 16 PiB -- Not supported until 5-level paging +// Level 0: 0000 0000 0003 fxxx 64 pages / 256 KiB +// Level 1: 0000 0000 00fc 0xxx 4K pages / 16 MiB -- 24-bit addressing +// Level 2: 0000 0000 3f00 0xxx 256K pages / 1 GiB +// Level 3: 0000 000f c000 0xxx 16M pages / 64 GiB -- 36-bit addressing +// Level 4: 0000 03f0 0000 0xxx 1G pages / 4 TiB +// Level 5: 0000 fc00 0000 0xxx 64G pages / 256 TiB -- 48-bit addressing +// +// Not supported until 5-level paging: +// Level 6: 003f 0000 0000 0xxx 4T pages / 16 PiB -- 54-bit addressing +// Level 7: 0fc0 0000 0000 0xxx 256T pages / 1 EiB -- 60-bit addressing + +static_assert(sizeof(page_tree) == 66 * sizeof(uintptr_t)); static constexpr unsigned max_level = 5; static constexpr unsigned bits_per_level = 6; -inline uint64_t to_word(uint64_t base, uint64_t level, uint64_t flags = 0) { - // Clear out the non-appropriate bits for this level - base &= (~0x3full << (level*bits_per_level)); - - return - (base & 0x3ffffffffff) | - ((level & 0x7) << 42) | - ((flags & 0x7ffff) << 45); -} - -inline uint64_t to_base(uint64_t word) { - return word & 0x3ffffffffff; -} - -inline uint64_t to_level(uint64_t word) { - return (word >> 42) & 0x3f; -} - -inline uint64_t to_flags(uint64_t word) { - return (word >> 45); -} - -inline bool contains(uint64_t page_off, uint64_t word, uint8_t &index) { - uint64_t base = to_base(word); - uint64_t bits = to_level(word) * bits_per_level; - index = (page_off >> bits) & 0x3f; - return (page_off & (~0x3full << bits)) == base; -} - -inline uint64_t index_for(uint64_t page_off, uint8_t level) { - return (page_off >> (level*bits_per_level)) & 0x3f; -} +inline int level_shift(uint8_t level) { return level * bits_per_level + memory::frame_bits; } +inline uint64_t level_mask(uint8_t level) { return ~0x3full << level_shift(level); } +inline int index_for(uint64_t off, uint8_t level) { return (off >> level_shift(level)) & 0x3full; } page_tree::page_tree(uint64_t base, uint8_t level) : - m_base {to_word(base, level)} + m_base {base & level_mask(level)}, + m_level {level} { kutil::memset(m_entries, 0, sizeof(m_entries)); } +bool +page_tree::contains(uint64_t offset, uint8_t &index) const +{ + return (offset & level_mask(m_level)) == m_base; +} + bool page_tree::find(const page_tree *root, uint64_t offset, uintptr_t &page) { - uint64_t page_off = offset >> 12; // change to pagewise offset page_tree const *node = root; while (node) { - uint8_t level = to_level(node->m_base); + uint8_t level = node->m_level; uint8_t index = 0; - if (!contains(page_off, node->m_base, index)) + if (!node->contains(offset, index)) return false; if (!level) { uintptr_t entry = node->m_entries[index].entry; - page = entry & ~1ull; // bit 0 marks 'present' - return (entry & 1); + page = entry & ~0xfffull; + return (entry & 1); // bit 0 marks 'present' } node = node->m_entries[index].child; @@ -83,13 +67,12 @@ page_tree::find(const page_tree *root, uint64_t offset, uintptr_t &page) bool page_tree::find_or_add(page_tree * &root, uint64_t offset, uintptr_t &page) { - uint64_t page_off = offset >> 12; // change to pagewise offset page_tree *level0 = nullptr; if (!root) { // There's no root yet, just make a level0 and make it // the root. - level0 = new page_tree(page_off, 0); + level0 = new page_tree(offset, 0); root = level0; } else { // Find or insert an existing level0 @@ -98,23 +81,22 @@ page_tree::find_or_add(page_tree * &root, uint64_t offset, uintptr_t &page) uint8_t parent_level = max_level + 1; while (node) { - uint8_t level = to_level(node->m_base); + uint8_t level = node->m_level; uint8_t index = 0; - if (!contains(page_off, node->m_base, index)) { + if (!node->contains(offset, index)) { // We found a valid parent but the slot where this node should // go contains another node. Insert an intermediate parent of // this node and a new level0 into the parent. - uint64_t other = to_base(node->m_base); + uint64_t other = node->m_base; uint8_t lcl = parent_level; - while (index_for(page_off, lcl) == index_for(other, lcl)) - --lcl; + while (index_for(offset, lcl) == index_for(other, lcl)) --lcl; - page_tree *inter = new page_tree(page_off, lcl); + page_tree *inter = new page_tree(offset, lcl); inter->m_entries[index_for(other, lcl)].child = node; *parent = inter; - level0 = new page_tree(page_off, 0); - inter->m_entries[index_for(page_off, lcl)].child = level0; + level0 = new page_tree(offset, 0); + inter->m_entries[index_for(offset, lcl)].child = level0; break; } @@ -132,13 +114,13 @@ page_tree::find_or_add(page_tree * &root, uint64_t offset, uintptr_t &page) if (!node) { // We found a parent with an empty spot where this node should // be. Insert a new level0 there. - level0 = new page_tree(page_off, 0); + level0 = new page_tree(offset, 0); *parent = level0; } } kassert(level0, "Got through find_or_add without a level0"); - uint8_t index = index_for(page_off, 0); + uint8_t index = index_for(offset, 0); uint64_t &ent = level0->m_entries[index].entry; if (!(ent & 1)) { // No entry for this page exists, so make one diff --git a/src/kernel/page_tree.h b/src/kernel/page_tree.h index 98e0a08..e7a5df6 100644 --- a/src/kernel/page_tree.h +++ b/src/kernel/page_tree.h @@ -26,10 +26,19 @@ public: private: page_tree(uint64_t base, uint8_t level); - /// Stores the page offset of the start of this node's pages in bits 0:41 - /// and the depth of tree this node represents in bits 42:44 (0-7) + /// Check if this node should contain the given virtual address + /// \arg offset The offset into the VMA, in bytes + /// \arg index [out] If found, what entry index should contain addr + /// \returns True if the address is contained + bool contains(uintptr_t offset, uint8_t &index) const; + + /// Stores the page offset of the start of this node's pages virtual addresses uint64_t m_base; + /// Level of this node: 0 maps actual physical pages. Other levels N point to + /// nodes of level N-1. + uint8_t m_level; + /// For a level 0 node, the entries area all physical page addresses. /// Other nodes contain pointers to child tree nodes. union {