/* DragonV2 - A collection of useful functionality Copyright (C) 2026 OmniaX-Dev This file is part of DragonV2. DragonV2 is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. DragonV2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with DragonV2. If not, see . */ #pragma once #include "BUS.hpp" namespace dragon { namespace hw { class MMU : public MemoryDevice { public: MMU(BUS& bus, const MMUAccessMode* mode_ptr); PhysicalAddress translate(VirtualAddress va, MMUAccessType type, MMUAccessMode mode) const; void enable(bool value = true); void setPtb(PhysicalAddress ptb); void invalidateAll(void); void invalidatePage(VirtualAddress va); bool guest_cas_u32(PhysicalAddress addr, u32 expected, u32& actual, u32 desired) override; // ---- Aligned loads ---- u64 load_u64(PhysicalAddress addr) const override; u32 load_u32(PhysicalAddress addr) const override; u16 load_u16(PhysicalAddress addr) const override; u8 load_u8 (PhysicalAddress addr) const override; // ---- Aligned stores ---- bool store_u64(PhysicalAddress addr, u64 value) override; bool store_u32(PhysicalAddress addr, u32 value) override; bool store_u16(PhysicalAddress addr, u16 value) override; bool store_u8 (PhysicalAddress addr, u8 value) override; inline void setCurrentAsid(u8 asid) { m_currentAsid = asid; } inline u8 getCurrentAsid(void) const { return m_currentAsid; } inline void disable(void) { enable(false); } inline bool isEnabled(void) const { return m_enabled; } inline PhysicalAddress getPtb(void) const { return m_ptb; } // Convenience: translate using current mode inline PhysicalAddress translate(VirtualAddress va, MMUAccessType type) const { return translate(va, type, *m_modePtr); } u32 fetch_u32(VirtualAddress va) const; private: ExceptionCause get_exception_cause(MMUFaultKind kind, MMUAccessType type) const; private: bool m_enabled { false }; PhysicalAddress m_ptb { 0 }; u8 m_currentAsid { 0 }; //TODO: BUS& m_bus; const MMUAccessMode* m_modePtr; private: // Page Table Entry bit layout (32 bits): // // 31 12 11 .. 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 // ┌─────────────────────────────┬─────────┬───┬───┬───┬───┬───┬───┬───┐ // │ physical page number (20) │ reserved│ G │ U │ X │ W │ R │ A │ P │ // └─────────────────────────────┴─────────┴───┴───┴───┴───┴───┴───┴───┘ // // P = Present : entry is valid; if 0, access faults // A = Accessed : set by hardware on any access to this page // R = Readable : load access permitted // W = Writable : store access permitted // X = Executable : instruction fetch permitted // U = User-accessible: accessible from user mode (else supervisor-only) // G = Global : translation valid in all address spaces (kernel mappings) inline static constexpr u32 PTE_PRESENT = 1u << 0; // 0x001 inline static constexpr u32 PTE_ACCESSED = 1u << 1; // 0x002 inline static constexpr u32 PTE_READ = 1u << 2; // 0x004 inline static constexpr u32 PTE_WRITE = 1u << 3; // 0x008 inline static constexpr u32 PTE_EXEC = 1u << 4; // 0x010 inline static constexpr u32 PTE_USER = 1u << 5; // 0x020 inline static constexpr u32 PTE_GLOBAL = 1u << 6; // 0x040 inline static constexpr u32 PTE_DIRTY = 1u << 7; // 0x080 // Bits 7..11 reserved for future use (Dirty bit, cache attributes, etc.) inline static constexpr u32 PTE_PERM_MASK = PTE_READ | PTE_WRITE | PTE_EXEC | PTE_USER | PTE_GLOBAL; inline static constexpr u32 PTE_FLAGS_MASK = 0xFFFu; // bottom 12 bits = flags inline static constexpr u32 PTE_PHYS_MASK = 0xFFFFF000u; // top 20 bits = phys page number // Helper: extract the physical page-aligned address from a PTE inline static constexpr u32 pte_phys_addr(u32 pte) { return pte & PTE_PHYS_MASK; } // Helper: check a single bit inline static constexpr bool pte_has(u32 pte, u32 flag) { return (pte & flag) != 0; } }; } }