[kernel] Move page mapping into vm_space

vm_space no longer relies on page_manager to map pages during a page
fault. Other changes that come with this commit:

- C++ standard has been changed to C++17
- enum bitfield operators became constexpr
- enum bifrield operators can take a mix of ints and enum arguments
- added page table flags enum instead of relying on ints
- remove page_table::unmap_table and page_table::unmap_pages
This commit is contained in:
2020-09-20 10:52:57 -07:00
parent deb2fa0a09
commit 1b392a0551
7 changed files with 107 additions and 143 deletions

View File

@@ -76,84 +76,6 @@ page_manager::map_pages(uintptr_t address, size_t count, bool user, page_table *
return ret;
}
void
page_manager::unmap_table(page_table *table, page_table::level lvl, bool free, page_table_indices index)
{
const int max =
lvl == page_table::level::pml4
? pml4e_kernel
: table_entries;
uintptr_t free_start = 0;
uintptr_t free_start_virt = 0;
uintptr_t free_count = 0;
size_t size =
lvl == page_table::level::pdp ? (1<<30) :
lvl == page_table::level::pd ? (1<<21) :
lvl == page_table::level::pt ? (1<<12) :
0;
for (int i = 0; i < max; ++i) {
if (!table->is_present(i)) continue;
index[lvl] = i;
bool is_page =
lvl == page_table::level::pt ||
table->is_large_page(lvl, i);
if (is_page) {
uintptr_t frame = table->entries[i] & ~0xfffull;
if (!free_count || frame != free_start + free_count * size) {
if (free_count && free) {
log::debug(logs::paging,
" freeing v:%016lx-%016lx p:%016lx-%016lx",
free_start_virt, free_start_virt + free_count * frame_size,
free_start, free_start + free_count * frame_size);
m_frames.free(free_start, (free_count * size) / frame_size);
free_count = 0;
}
if (!free_count) {
free_start = frame;
free_start_virt = index.addr();
}
}
free_count += 1;
} else {
page_table *next = table->get(i);
unmap_table(next, page_table::deeper(lvl), free, index);
}
}
if (free_count && free) {
log::debug(logs::paging,
" freeing v:%016lx-%016lx p:%016lx-%016lx",
free_start_virt, free_start_virt + free_count * frame_size,
free_start, free_start + free_count * frame_size);
m_frames.free(free_start, (free_count * size) / frame_size);
}
page_table::free_table_page(table);
log::debug(logs::paging, "Unmapped%s lv %d table at %016lx",
free ? " (and freed)" : "", lvl, table);
}
void
page_manager::unmap_pages(void* address, size_t count, page_table *pml4)
{
if (!pml4)
pml4 = get_pml4();
uintptr_t iaddr = reinterpret_cast<uintptr_t>(address);
page_out(pml4, iaddr, count, true);
}
void
page_manager::check_needs_page(page_table *table, unsigned index, bool user)
{

View File

@@ -57,12 +57,6 @@ public:
/// \returns A pointer to the start of the mapped region
void * map_pages(uintptr_t address, size_t count, bool user = false, page_table *pml4 = nullptr);
/// Unmap and free existing pages from memory.
/// \arg address The virtual address of the memory to unmap
/// \arg count The number of pages to unmap
/// \arg pml4 The pml4 to unmap from - null for the current one
void unmap_pages(void *address, size_t count, page_table *pml4 = nullptr);
/// Dump the given or current PML4 to the console
/// \arg pml4 The page table to use, null for the current one
/// \arg recurse Whether to print sub-tables
@@ -110,10 +104,6 @@ private:
size_t count,
bool free = false);
/// Low-level routine for unmapping an entire table of memory at once
void unmap_table(page_table *table, page_table::level lvl, bool free,
page_table_indices index = {});
page_table *m_kernel_pml4; ///< The PML4 of just kernel pages
frame_allocator &m_frames;

View File

@@ -14,17 +14,10 @@ free_page_header * page_table::s_page_cache = nullptr;
size_t page_table::s_cache_count = 0;
constexpr size_t page_table::entry_sizes[4];
// Flags: 0 0 0 0 0 0 0 0 0 0 1 1 = 0x0003
// IGNORED | | | | | | | +- Present
// | | | | | | +--- Writeable
// | | | | | +----- Usermode access (Supervisor only)
// | | | | +------- PWT (determining memory type for pdpt)
// | | | +---------- PCD (determining memory type for pdpt)
// | | +------------ Accessed flag (not accessed yet)
// | +-------------- Ignored
// +---------------- Reserved 0 (Table pointer, not page)
/// Page table entry flags for entries pointing at another table
constexpr uint16_t table_flags = 0x003;
constexpr page_table::flag table_flags =
page_table::flag::present |
page_table::flag::write;
page_table::iterator::iterator(uintptr_t virt, page_table *pml4) :
@@ -114,7 +107,7 @@ page_table::iterator::allowed() const
{
level d = depth();
while (true) {
if (entry(d) & flag_allowed) return true;
if (entry(d) & flag::allowed) return true;
else if (d == level::pml4) return false;
--d;
}
@@ -126,8 +119,8 @@ page_table::iterator::allow(level at, bool allowed)
for (level l = level::pdp; l <= at; ++l)
ensure_table(l);
if (allowed) entry(at) |= flag_allowed;
else entry(at) &= ~flag_allowed;
if (allowed) entry(at) |= flag::allowed;
else entry(at) &= ~flag::allowed;
}
bool
@@ -169,8 +162,9 @@ page_table::iterator::ensure_table(level l)
kassert(n, "Failed to allocate a page table");
uint64_t &parent = entry(l - 1);
uint64_t flags = table_flags |
(parent & flag_allowed) ? flag_allowed : 0;
flag flags = table_flags | (parent & flag::allowed);
if (m_index[0] < memory::pml4e_kernel)
flags |= flag::user;
m_table[unsigned(l)] = reinterpret_cast<page_table*>(phys | page_offset);
parent = (reinterpret_cast<uintptr_t>(phys) & ~0xfffull) | flags;
@@ -193,7 +187,7 @@ page_table::get(int i, uint16_t *flags) const
void
page_table::set(int i, page_table *p, uint16_t flags)
{
if (entries[i] & flag_allowed) flags |= flag_allowed;
if (entries[i] & flag::allowed) flags |= flag::allowed;
entries[i] =
(reinterpret_cast<uint64_t>(p) - page_offset) |
(flags & 0xfff);

View File

@@ -3,6 +3,7 @@
/// Helper structures for dealing with page tables.
#include <stdint.h>
#include "kutil/enum_bitfields.h"
#include "kernel_memory.h"
struct free_page_header;
@@ -14,6 +15,26 @@ struct page_table
/// Enum representing the table levels in 4-level paging
enum class level : unsigned { pml4, pdp, pd, pt, page };
/// Page entry flags
enum class flag : uint64_t
{
none = 0x0000,
present = 0x0001, /// Entry is present in the table
write = 0x0002, /// Section may be written
user = 0x0004, /// User-accessible
mtrr0 = 0x0008, /// MTRR selector bit 0
mtrr1 = 0x0010, /// MTRR selector bit 1
accessed = 0x0020, /// Entry has been accessed
dirty = 0x0040, /// Page has been written to
page = 0x0080, /// Entry is a large page
pte_mtrr2 = 0x0080, /// MTRR selector bit 2 on PT entries
global = 0x0100, /// Entry is not PCID-specific
mtrr2 = 0x1000, /// MTRR selector bit 2 on PD and PDP entries
// jsix-defined
allowed = 0x0800 /// Allocation here is allowed
};
/// Helper for getting the next level value
inline static level deeper(level l) {
return static_cast<level>(static_cast<unsigned>(l) + 1);
@@ -25,9 +46,6 @@ struct page_table
0x200000, // PD entry: 2 MiB
0x1000}; // PT entry: 4 KiB
/// Flag marking unused space as allowed for allocation
static constexpr uint64_t flag_allowed = (1ull << 11);
/// Iterator over page table entries.
class iterator
{
@@ -197,3 +215,5 @@ inline bool operator<(page_table::level a, page_table::level b) {
inline page_table::level& operator++(page_table::level& l) { l = l + 1; return l; }
inline page_table::level& operator--(page_table::level& l) { l = l - 1; return l; }
IS_BITFIELD(page_table::flag);

View File

@@ -1,3 +1,4 @@
#include "frame_allocator.h"
#include "log.h"
#include "objects/process.h"
#include "objects/thread.h"
@@ -5,6 +6,8 @@
#include "page_manager.h"
#include "vm_space.h"
extern frame_allocator &g_frame_allocator;
int
vm_space::area::compare(const vm_space::area &o) const
{
@@ -152,17 +155,26 @@ vm_space::handle_fault(uintptr_t addr, fault_type fault)
page_table::iterator it {addr, m_pml4};
// TODO: Handle more fult types
if (fault && fault_type::present)
return false;
if (!it.allowed())
return false;
// TODO: pull this out of PM
page_manager::get()->map_pages(page, 1, m_pml4);
uintptr_t phys = 0;
size_t n = g_frame_allocator.allocate(1, &phys);
kassert(n, "Failed to allocate a new page during page fault");
/* TODO: Tell the VMA if there is one
uintptr_t base = 0;
vm_area *area = get(addr, &base);
*/
page_table::flag flags =
page_table::flag::present |
page_table::flag::write |
page_table::flag::allowed |
(is_kernel()
? page_table::flag::global
: page_table::flag::user);
it.entry(page_table::level::pt) = phys | flags;
return true;
}

View File

@@ -8,104 +8,130 @@ struct is_enum_bitfield { static constexpr bool value = false; };
#define IS_BITFIELD(name) \
template<> struct ::is_enum_bitfield<name> {static constexpr bool value=true;}
template <typename E>
struct enum_or_int {
static constexpr bool value =
std::disjunction< is_enum_bitfield<E>, std::is_integral<E> >::value;
};
template <typename E, typename F>
typename std::enable_if<is_enum_bitfield<E>::value,E>::type
struct both_enum_or_int {
static constexpr bool value =
std::conjunction< enum_or_int<E>, enum_or_int<F> >::value;
};
template <typename E>
struct integral { using type = typename std::underlying_type<E>::type; };
template <> struct integral<char> { using type = char; };
template <> struct integral<unsigned char> { using type = unsigned char; };
template <> struct integral<short> { using type = short; };
template <> struct integral<unsigned short> { using type = unsigned short; };
template <> struct integral<int> { using type = int; };
template <> struct integral<unsigned int> { using type = unsigned int; };
template <> struct integral<long> { using type = long; };
template <> struct integral<unsigned long> { using type = unsigned long; };
template <> struct integral<long long> { using type = long long; };
template <> struct integral<unsigned long long> { using type = unsigned long long; };
template <typename E, typename F>
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type
operator & (E lhs, F rhs)
{
return static_cast<E> (
static_cast<typename std::underlying_type<E>::type>(lhs) &
static_cast<typename std::underlying_type<E>::type>(rhs));
static_cast<typename integral<E>::type>(lhs) &
static_cast<typename integral<F>::type>(rhs));
}
template <typename E, typename F>
typename std::enable_if<is_enum_bitfield<E>::value,E>::type
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type
operator | (E lhs, F rhs)
{
return static_cast<E> (
static_cast<typename std::underlying_type<E>::type>(lhs) |
static_cast<typename std::underlying_type<E>::type>(rhs));
static_cast<typename integral<E>::type>(lhs) |
static_cast<typename integral<F>::type>(rhs));
}
template <typename E, typename F>
typename std::enable_if<is_enum_bitfield<E>::value,E>::type
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type
operator ^ (E lhs, F rhs)
{
return static_cast<E> (
static_cast<typename std::underlying_type<E>::type>(lhs) ^
static_cast<typename std::underlying_type<E>::type>(rhs));
static_cast<typename integral<E>::type>(lhs) ^
static_cast<typename integral<F>::type>(rhs));
}
template <typename E>
typename std::enable_if<is_enum_bitfield<E>::value,E>::type
constexpr typename std::enable_if<is_enum_bitfield<E>::value,E>::type
operator ~ (E rhs)
{
return static_cast<E>(~static_cast<typename std::underlying_type<E>::type>(rhs));
}
template <typename E, typename F>
typename std::enable_if<is_enum_bitfield<E>::value,E>::type&
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type&
operator |= (E &lhs, F rhs)
{
lhs = static_cast<E>(
static_cast<typename std::underlying_type<E>::type>(lhs) |
static_cast<typename std::underlying_type<E>::type>(rhs));
static_cast<typename integral<E>::type>(lhs) |
static_cast<typename integral<F>::type>(rhs));
return lhs;
}
template <typename E, typename F>
typename std::enable_if<is_enum_bitfield<E>::value,E>::type&
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type&
operator &= (E &lhs, F rhs)
{
lhs = static_cast<E>(
static_cast<typename std::underlying_type<E>::type>(lhs) &
static_cast<typename std::underlying_type<E>::type>(rhs));
static_cast<typename integral<E>::type>(lhs) &
static_cast<typename integral<F>::type>(rhs));
return lhs;
}
template <typename E, typename F>
typename std::enable_if<is_enum_bitfield<E>::value,E>::type&
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type&
operator ^= (E &lhs, F rhs)
{
lhs = static_cast<E>(
static_cast<typename std::underlying_type<E>::type>(lhs) ^
static_cast<typename std::underlying_type<E>::type>(rhs));
static_cast<typename integral<E>::type>(lhs) ^
static_cast<typename integral<F>::type>(rhs));
return lhs;
}
template <typename E, typename F>
typename std::enable_if<is_enum_bitfield<E>::value,E>::type&
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type&
operator -= (E &lhs, F rhs)
{
lhs = static_cast<E>(
static_cast<typename std::underlying_type<E>::type>(lhs) &
~static_cast<typename std::underlying_type<E>::type>(rhs));
static_cast<typename integral<E>::type>(lhs) &
~static_cast<typename integral<F>::type>(rhs));
return lhs;
}
template <typename E, typename F>
typename std::enable_if<is_enum_bitfield<E>::value,E>::type&
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type&
operator += (E &lhs, F rhs)
{
lhs = static_cast<E>(
static_cast<typename std::underlying_type<E>::type>(lhs) |
static_cast<typename std::underlying_type<E>::type>(rhs));
static_cast<typename integral<E>::type>(lhs) |
static_cast<typename integral<F>::type>(rhs));
return lhs;
}
template <typename E>
typename std::enable_if<is_enum_bitfield<E>::value,bool>::type
constexpr typename std::enable_if<is_enum_bitfield<E>::value,bool>::type
operator ! (E rhs)
{
return static_cast<typename std::underlying_type<E>::type>(rhs) == 0;
}
template <typename E>
typename std::enable_if<is_enum_bitfield<E>::value,bool>::type
constexpr typename std::enable_if<is_enum_bitfield<E>::value,bool>::type
bitfield_has(E set, E flag)
{
return (set & flag) == flag;
@@ -113,7 +139,7 @@ bitfield_has(E set, E flag)
// Overload the logical-and operator to be 'bitwise-and, bool-cast'
template <typename E>
typename std::enable_if<is_enum_bitfield<E>::value,bool>::type
constexpr typename std::enable_if<is_enum_bitfield<E>::value,bool>::type
operator && (E set, E flag)
{
return (set & flag) == flag;