[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.
This commit is contained in:
Justin C. Miller
2021-09-12 16:15:23 -07:00
parent 7d8535af22
commit 6317e3ad00
2 changed files with 52 additions and 61 deletions

View File

@@ -1,77 +1,61 @@
#include "kutil/assert.h" #include "kutil/assert.h"
#include "kutil/memory.h" #include "kutil/memory.h"
#include "frame_allocator.h" #include "frame_allocator.h"
#include "kernel_memory.h"
#include "page_tree.h" #include "page_tree.h"
// Page tree levels map the following parts of a pagewise offset: // Page tree levels map the following parts of a pagewise offset. Note the xxx
// (Note that a level 0's entries are physical page addrs, the rest // 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) // map other page_tree nodes)
// //
// Level 0: 0000000003f 64 pages / 256 KiB // Level 0: 0000 0000 0003 fxxx 64 pages / 256 KiB
// Level 1: 00000000fc0 4K pages / 16 MiB // Level 1: 0000 0000 00fc 0xxx 4K pages / 16 MiB -- 24-bit addressing
// Level 2: 0000003f000 256K pages / 1 GiB // Level 2: 0000 0000 3f00 0xxx 256K pages / 1 GiB
// Level 3: 00000fc0000 16M pages / 64 GiB // Level 3: 0000 000f c000 0xxx 16M pages / 64 GiB -- 36-bit addressing
// Level 4: 0003f000000 1G pages / 4 TiB // Level 4: 0000 03f0 0000 0xxx 1G pages / 4 TiB
// Level 5: 00fc0000000 64G pages / 256 TiB // Level 5: 0000 fc00 0000 0xxx 64G pages / 256 TiB -- 48-bit addressing
// Level 6: 3f000000000 4T pages / 16 PiB -- Not supported until 5-level paging //
// 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 max_level = 5;
static constexpr unsigned bits_per_level = 6; static constexpr unsigned bits_per_level = 6;
inline uint64_t to_word(uint64_t base, uint64_t level, uint64_t flags = 0) { inline int level_shift(uint8_t level) { return level * bits_per_level + memory::frame_bits; }
// Clear out the non-appropriate bits for this level inline uint64_t level_mask(uint8_t level) { return ~0x3full << level_shift(level); }
base &= (~0x3full << (level*bits_per_level)); inline int index_for(uint64_t off, uint8_t level) { return (off >> level_shift(level)) & 0x3full; }
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;
}
page_tree::page_tree(uint64_t base, uint8_t level) : 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)); 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 bool
page_tree::find(const page_tree *root, uint64_t offset, uintptr_t &page) 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; page_tree const *node = root;
while (node) { while (node) {
uint8_t level = to_level(node->m_base); uint8_t level = node->m_level;
uint8_t index = 0; uint8_t index = 0;
if (!contains(page_off, node->m_base, index)) if (!node->contains(offset, index))
return false; return false;
if (!level) { if (!level) {
uintptr_t entry = node->m_entries[index].entry; uintptr_t entry = node->m_entries[index].entry;
page = entry & ~1ull; // bit 0 marks 'present' page = entry & ~0xfffull;
return (entry & 1); return (entry & 1); // bit 0 marks 'present'
} }
node = node->m_entries[index].child; node = node->m_entries[index].child;
@@ -83,13 +67,12 @@ page_tree::find(const page_tree *root, uint64_t offset, uintptr_t &page)
bool bool
page_tree::find_or_add(page_tree * &root, uint64_t offset, uintptr_t &page) 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; page_tree *level0 = nullptr;
if (!root) { if (!root) {
// There's no root yet, just make a level0 and make it // There's no root yet, just make a level0 and make it
// the root. // the root.
level0 = new page_tree(page_off, 0); level0 = new page_tree(offset, 0);
root = level0; root = level0;
} else { } else {
// Find or insert an existing level0 // 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; uint8_t parent_level = max_level + 1;
while (node) { while (node) {
uint8_t level = to_level(node->m_base); uint8_t level = node->m_level;
uint8_t index = 0; 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 // We found a valid parent but the slot where this node should
// go contains another node. Insert an intermediate parent of // go contains another node. Insert an intermediate parent of
// this node and a new level0 into the parent. // 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; uint8_t lcl = parent_level;
while (index_for(page_off, lcl) == index_for(other, lcl)) while (index_for(offset, lcl) == index_for(other, lcl)) --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; inter->m_entries[index_for(other, lcl)].child = node;
*parent = inter; *parent = inter;
level0 = new page_tree(page_off, 0); level0 = new page_tree(offset, 0);
inter->m_entries[index_for(page_off, lcl)].child = level0; inter->m_entries[index_for(offset, lcl)].child = level0;
break; break;
} }
@@ -132,13 +114,13 @@ page_tree::find_or_add(page_tree * &root, uint64_t offset, uintptr_t &page)
if (!node) { if (!node) {
// We found a parent with an empty spot where this node should // We found a parent with an empty spot where this node should
// be. Insert a new level0 there. // be. Insert a new level0 there.
level0 = new page_tree(page_off, 0); level0 = new page_tree(offset, 0);
*parent = level0; *parent = level0;
} }
} }
kassert(level0, "Got through find_or_add without a 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; uint64_t &ent = level0->m_entries[index].entry;
if (!(ent & 1)) { if (!(ent & 1)) {
// No entry for this page exists, so make one // No entry for this page exists, so make one

View File

@@ -26,10 +26,19 @@ public:
private: private:
page_tree(uint64_t base, uint8_t level); page_tree(uint64_t base, uint8_t level);
/// Stores the page offset of the start of this node's pages in bits 0:41 /// Check if this node should contain the given virtual address
/// and the depth of tree this node represents in bits 42:44 (0-7) /// \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; 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. /// For a level 0 node, the entries area all physical page addresses.
/// Other nodes contain pointers to child tree nodes. /// Other nodes contain pointers to child tree nodes.
union { union {