diff --git a/scripts/templates/build.ninja.j2 b/scripts/templates/build.ninja.j2 index 2b48eb8..aae17e6 100644 --- a/scripts/templates/build.ninja.j2 +++ b/scripts/templates/build.ninja.j2 @@ -51,7 +51,7 @@ asflags = $ -I${srcroot}/src/include cflags = -std=c11 -cxxflags = -std=c++14 +cxxflags = -std=c++17 libs = rule c diff --git a/src/kernel/page_manager.cpp b/src/kernel/page_manager.cpp index f48df9e..36ace69 100644 --- a/src/kernel/page_manager.cpp +++ b/src/kernel/page_manager.cpp @@ -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(address); - - page_out(pml4, iaddr, count, true); -} - void page_manager::check_needs_page(page_table *table, unsigned index, bool user) { diff --git a/src/kernel/page_manager.h b/src/kernel/page_manager.h index ba37274..f1b15bb 100644 --- a/src/kernel/page_manager.h +++ b/src/kernel/page_manager.h @@ -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; diff --git a/src/kernel/page_table.cpp b/src/kernel/page_table.cpp index 5087e49..ae296e4 100644 --- a/src/kernel/page_table.cpp +++ b/src/kernel/page_table.cpp @@ -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(phys | page_offset); parent = (reinterpret_cast(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(p) - page_offset) | (flags & 0xfff); diff --git a/src/kernel/page_table.h b/src/kernel/page_table.h index 4477849..208c58f 100644 --- a/src/kernel/page_table.h +++ b/src/kernel/page_table.h @@ -3,6 +3,7 @@ /// Helper structures for dealing with page tables. #include +#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(static_cast(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); diff --git a/src/kernel/vm_space.cpp b/src/kernel/vm_space.cpp index 556d9d1..76d7d6e 100644 --- a/src/kernel/vm_space.cpp +++ b/src/kernel/vm_space.cpp @@ -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; } diff --git a/src/libraries/kutil/include/kutil/enum_bitfields.h b/src/libraries/kutil/include/kutil/enum_bitfields.h index 755e521..bbf334a 100644 --- a/src/libraries/kutil/include/kutil/enum_bitfields.h +++ b/src/libraries/kutil/include/kutil/enum_bitfields.h @@ -8,104 +8,130 @@ struct is_enum_bitfield { static constexpr bool value = false; }; #define IS_BITFIELD(name) \ template<> struct ::is_enum_bitfield {static constexpr bool value=true;} +template +struct enum_or_int { + static constexpr bool value = + std::disjunction< is_enum_bitfield, std::is_integral >::value; +}; + template -typename std::enable_if::value,E>::type +struct both_enum_or_int { + static constexpr bool value = + std::conjunction< enum_or_int, enum_or_int >::value; +}; + +template +struct integral { using type = typename std::underlying_type::type; }; + +template <> struct integral { using type = char; }; +template <> struct integral { using type = unsigned char; }; +template <> struct integral { using type = short; }; +template <> struct integral { using type = unsigned short; }; +template <> struct integral { using type = int; }; +template <> struct integral { using type = unsigned int; }; +template <> struct integral { using type = long; }; +template <> struct integral { using type = unsigned long; }; +template <> struct integral { using type = long long; }; +template <> struct integral { using type = unsigned long long; }; + +template +constexpr typename std::enable_if::value,E>::type operator & (E lhs, F rhs) { return static_cast ( - static_cast::type>(lhs) & - static_cast::type>(rhs)); + static_cast::type>(lhs) & + static_cast::type>(rhs)); } template -typename std::enable_if::value,E>::type +constexpr typename std::enable_if::value,E>::type operator | (E lhs, F rhs) { return static_cast ( - static_cast::type>(lhs) | - static_cast::type>(rhs)); + static_cast::type>(lhs) | + static_cast::type>(rhs)); } template -typename std::enable_if::value,E>::type +constexpr typename std::enable_if::value,E>::type operator ^ (E lhs, F rhs) { return static_cast ( - static_cast::type>(lhs) ^ - static_cast::type>(rhs)); + static_cast::type>(lhs) ^ + static_cast::type>(rhs)); } template -typename std::enable_if::value,E>::type +constexpr typename std::enable_if::value,E>::type operator ~ (E rhs) { return static_cast(~static_cast::type>(rhs)); } template -typename std::enable_if::value,E>::type& +constexpr typename std::enable_if::value,E>::type& operator |= (E &lhs, F rhs) { lhs = static_cast( - static_cast::type>(lhs) | - static_cast::type>(rhs)); + static_cast::type>(lhs) | + static_cast::type>(rhs)); return lhs; } template -typename std::enable_if::value,E>::type& +constexpr typename std::enable_if::value,E>::type& operator &= (E &lhs, F rhs) { lhs = static_cast( - static_cast::type>(lhs) & - static_cast::type>(rhs)); + static_cast::type>(lhs) & + static_cast::type>(rhs)); return lhs; } template -typename std::enable_if::value,E>::type& +constexpr typename std::enable_if::value,E>::type& operator ^= (E &lhs, F rhs) { lhs = static_cast( - static_cast::type>(lhs) ^ - static_cast::type>(rhs)); + static_cast::type>(lhs) ^ + static_cast::type>(rhs)); return lhs; } template -typename std::enable_if::value,E>::type& +constexpr typename std::enable_if::value,E>::type& operator -= (E &lhs, F rhs) { lhs = static_cast( - static_cast::type>(lhs) & - ~static_cast::type>(rhs)); + static_cast::type>(lhs) & + ~static_cast::type>(rhs)); return lhs; } template -typename std::enable_if::value,E>::type& +constexpr typename std::enable_if::value,E>::type& operator += (E &lhs, F rhs) { lhs = static_cast( - static_cast::type>(lhs) | - static_cast::type>(rhs)); + static_cast::type>(lhs) | + static_cast::type>(rhs)); return lhs; } template -typename std::enable_if::value,bool>::type +constexpr typename std::enable_if::value,bool>::type operator ! (E rhs) { return static_cast::type>(rhs) == 0; } template -typename std::enable_if::value,bool>::type +constexpr typename std::enable_if::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 std::enable_if::value,bool>::type +constexpr typename std::enable_if::value,bool>::type operator && (E set, E flag) { return (set & flag) == flag;