Started work on MMU class
This commit is contained in:
parent
8b94e8f55c
commit
7c0f11b34b
11 changed files with 535 additions and 63 deletions
|
|
@ -33,12 +33,14 @@ list(APPEND RUNTIME_SOURCE_FILES
|
||||||
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/hardware/CPU.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/hardware/CPU.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/hardware/RAM.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/hardware/RAM.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/hardware/BUS.cpp
|
||||||
)
|
)
|
||||||
list(APPEND DEBUGGER_SOURCE_FILES
|
list(APPEND DEBUGGER_SOURCE_FILES
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/debugger/debugger_main.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/debugger/debugger_main.cpp
|
||||||
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/hardware/CPU.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/hardware/CPU.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/hardware/RAM.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/hardware/RAM.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/hardware/BUS.cpp
|
||||||
)
|
)
|
||||||
list(APPEND ASSEMBLER_SOURCE_FILES
|
list(APPEND ASSEMBLER_SOURCE_FILES
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/assembler/assembler_main.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/assembler/assembler_main.cpp
|
||||||
|
|
@ -49,6 +51,7 @@ list(APPEND TOOLS_SOURCE_FILES
|
||||||
list(APPEND TEST_SOURCE_FILES
|
list(APPEND TEST_SOURCE_FILES
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/RAM_test.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/RAM_test.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/hardware/RAM.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/hardware/RAM.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/hardware/BUS.cpp
|
||||||
)
|
)
|
||||||
#-----------------------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
9
|
10
|
||||||
|
|
|
||||||
35
src/common/Data.hpp
Normal file
35
src/common/Data.hpp
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ostd/data/Types.hpp>
|
||||||
|
#include <ostd/string/String.hpp>
|
||||||
|
|
||||||
|
namespace dragon
|
||||||
|
{
|
||||||
|
// ------ Contextual Aliases ------
|
||||||
|
using AddressType = u32;
|
||||||
|
using VirtualAddress = AddressType;
|
||||||
|
using PhysycalAddress = AddressType;
|
||||||
|
// --------------------------------
|
||||||
|
|
||||||
|
enum class MMUAccessType { Read, Write, Execute };
|
||||||
|
}
|
||||||
148
src/hardware/BUS.cpp
Normal file
148
src/hardware/BUS.cpp
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
/*
|
||||||
|
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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "BUS.hpp"
|
||||||
|
|
||||||
|
namespace dragon
|
||||||
|
{
|
||||||
|
AtomicStorageMemoryDevice::AtomicStorageMemoryDevice(u64 size)
|
||||||
|
{
|
||||||
|
if (size == 0) // Special case, for classes like BUS, that are MemoryDevices but don't own any data
|
||||||
|
{
|
||||||
|
m_wordCount = 0;
|
||||||
|
m_size = 0;
|
||||||
|
m_words = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_wordCount = (size + 7) / 8; // Round up to multiple of 8 bytes for the word-granular backing
|
||||||
|
m_size = size;
|
||||||
|
m_words = new std::atomic<u64>[m_wordCount];
|
||||||
|
for (u64 i = 0; i < m_wordCount; ++i)
|
||||||
|
m_words[i].store(0, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
AtomicStorageMemoryDevice::~AtomicStorageMemoryDevice(void)
|
||||||
|
{
|
||||||
|
delete[] m_words;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MemoryDevice::guest_cas_u32(AddressType addr, u32 expected, u32& actual, u32 desired)
|
||||||
|
{
|
||||||
|
fault(addr, 4, "CAS not supported on this device");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace hw
|
||||||
|
{
|
||||||
|
bool BUS::guest_cas_u32(PhysycalAddress addr, u32 expected, u32& actual, u32 desired)
|
||||||
|
{
|
||||||
|
auto region = findRegion(addr);
|
||||||
|
if (!region) { /* fault */ return false; }
|
||||||
|
AddressType finalAddr = (region->remap ? addr - region->startAddress : addr);
|
||||||
|
return region->device->guest_cas_u32(finalAddr, expected, actual, desired);
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 BUS::load_u8(PhysycalAddress addr) const
|
||||||
|
{
|
||||||
|
auto region = findRegion(addr);
|
||||||
|
if (region == nullptr) return 0x00; //TODO: Error
|
||||||
|
AddressType finalAddr = (region->remap ? addr - region->startAddress : addr);
|
||||||
|
return region->device->load_u8(finalAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 BUS::load_u16(PhysycalAddress addr) const
|
||||||
|
{
|
||||||
|
auto region = findRegion(addr);
|
||||||
|
if (region == nullptr) return 0x0000; //TODO: Error
|
||||||
|
AddressType finalAddr = (region->remap ? addr - region->startAddress : addr);
|
||||||
|
return region->device->load_u16(finalAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 BUS::load_u32(PhysycalAddress addr) const
|
||||||
|
{
|
||||||
|
auto region = findRegion(addr);
|
||||||
|
if (region == nullptr) return 0x0000'0000; //TODO: Error
|
||||||
|
AddressType finalAddr = (region->remap ? addr - region->startAddress : addr);
|
||||||
|
return region->device->load_u32(finalAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 BUS::load_u64(PhysycalAddress addr) const
|
||||||
|
{
|
||||||
|
auto region = findRegion(addr);
|
||||||
|
if (region == nullptr) return 0x0000'0000'0000'0000; //TODO: Error
|
||||||
|
AddressType finalAddr = (region->remap ? addr - region->startAddress : addr);
|
||||||
|
return region->device->load_u64(finalAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BUS::store_u8(PhysycalAddress addr, u8 value)
|
||||||
|
{
|
||||||
|
auto region = findRegion(addr);
|
||||||
|
if (region == nullptr) return false;
|
||||||
|
AddressType finalAddr = (region->remap ? addr - region->startAddress : addr);
|
||||||
|
return region->device->store_u8(finalAddr, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BUS::store_u16(PhysycalAddress addr, u16 value)
|
||||||
|
{
|
||||||
|
auto region = findRegion(addr);
|
||||||
|
if (region == nullptr) return false;
|
||||||
|
AddressType finalAddr = (region->remap ? addr - region->startAddress : addr);
|
||||||
|
return region->device->store_u16(finalAddr, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BUS::store_u32(PhysycalAddress addr, u32 value)
|
||||||
|
{
|
||||||
|
auto region = findRegion(addr);
|
||||||
|
if (region == nullptr) return false;
|
||||||
|
AddressType finalAddr = (region->remap ? addr - region->startAddress : addr);
|
||||||
|
return region->device->store_u32(finalAddr, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BUS::store_u64(PhysycalAddress addr, u64 value)
|
||||||
|
{
|
||||||
|
auto region = findRegion(addr);
|
||||||
|
if (region == nullptr) return false;
|
||||||
|
AddressType finalAddr = (region->remap ? addr - region->startAddress : addr);
|
||||||
|
return region->device->store_u64(finalAddr, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BUS::mapDevice(MemoryDevice& device, PhysycalAddress startAddr, PhysycalAddress endAddr, bool remap, ostd::String name)
|
||||||
|
{
|
||||||
|
m_regions.push_back({ &device, startAddr, endAddr, remap, name });
|
||||||
|
}
|
||||||
|
|
||||||
|
ostd::String BUS::getMemoryRegionName(PhysycalAddress address)
|
||||||
|
{
|
||||||
|
tMemoryRegion* region = findRegion(address);
|
||||||
|
if (region == nullptr) return "Invalid";
|
||||||
|
return region->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BUS::fault(PhysycalAddress addr, u32 size, const String& msg) const
|
||||||
|
{
|
||||||
|
std::cout << msg << ostd::String::getHexStr(addr, true, 4) << "\n";
|
||||||
|
/* TODO: This function basically only has to deal with the "region not mapped" problem. */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
144
src/hardware/BUS.hpp
Normal file
144
src/hardware/BUS.hpp
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
/*
|
||||||
|
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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ostd/data/BaseObject.hpp>
|
||||||
|
#include <atomic>
|
||||||
|
#include "../common/Data.hpp"
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace dragon
|
||||||
|
{
|
||||||
|
class MemoryDevice : public ostd::BaseObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MemoryDevice(void) = default;
|
||||||
|
MemoryDevice(const MemoryDevice&) = delete;
|
||||||
|
MemoryDevice& operator=(const MemoryDevice&) = delete;
|
||||||
|
MemoryDevice(MemoryDevice&&) = delete;
|
||||||
|
MemoryDevice& operator=(MemoryDevice&&) = delete;
|
||||||
|
virtual ~MemoryDevice() = default;
|
||||||
|
|
||||||
|
virtual bool guest_cas_u32(AddressType addr, u32 expected, u32& actual, u32 desired);
|
||||||
|
|
||||||
|
// ---- Aligned loads ----
|
||||||
|
virtual u64 load_u64(AddressType addr) const = 0;
|
||||||
|
virtual u32 load_u32(AddressType addr) const = 0;
|
||||||
|
virtual u16 load_u16(u32 addr) const = 0;
|
||||||
|
virtual u8 load_u8 (u32 addr) const = 0;
|
||||||
|
|
||||||
|
inline i64 load_i64(AddressType addr) const { return cast<i64>(load_u64(addr)); }
|
||||||
|
inline i32 load_i32(AddressType addr) const { return cast<i32>(load_u32(addr)); }
|
||||||
|
inline i16 load_i16(AddressType addr) const { return cast<i16>(load_u16(addr)); }
|
||||||
|
inline i8 load_i8 (AddressType addr) const { return cast<i8 >(load_u8 (addr)); }
|
||||||
|
|
||||||
|
// ---- Aligned stores ----
|
||||||
|
virtual bool store_u64(AddressType addr, u64 value) = 0;
|
||||||
|
virtual bool store_u32(AddressType addr, u32 value) = 0;
|
||||||
|
virtual bool store_u16(AddressType addr, u16 value) = 0;
|
||||||
|
virtual bool store_u8 (AddressType addr, u8 value) = 0;
|
||||||
|
|
||||||
|
inline bool store_i64(AddressType addr, u64 value) { return store_u64(addr, cast<i64>(value)); }
|
||||||
|
inline bool store_i32(AddressType addr, u32 value) { return store_u32(addr, cast<i32>(value)); }
|
||||||
|
inline bool store_i16(AddressType addr, u16 value) { return store_u16(addr, cast<i16>(value)); }
|
||||||
|
inline bool store_i8 (AddressType addr, u8 value) { return store_u8 (addr, cast<i8 >(value)); }
|
||||||
|
|
||||||
|
[[noreturn]] virtual void fault(AddressType addr, u32 size, const String& msg = "") const = 0; // Message can be useful for debugging
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class AtomicStorageMemoryDevice : public MemoryDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit AtomicStorageMemoryDevice(u64 size);
|
||||||
|
virtual ~AtomicStorageMemoryDevice(void);
|
||||||
|
AtomicStorageMemoryDevice(const AtomicStorageMemoryDevice&) = delete;
|
||||||
|
AtomicStorageMemoryDevice& operator=(const AtomicStorageMemoryDevice&) = delete;
|
||||||
|
AtomicStorageMemoryDevice(AtomicStorageMemoryDevice&&) = delete;
|
||||||
|
AtomicStorageMemoryDevice& operator=(AtomicStorageMemoryDevice&&) = delete;
|
||||||
|
|
||||||
|
// ---- Helpers ----
|
||||||
|
inline u64 getWordCount(void) const { return m_wordCount; }
|
||||||
|
inline u64 getSize(void) const { return m_size; }
|
||||||
|
inline virtual std::span<std::atomic<u64>> getRawData(void) { return { m_words, m_size }; }
|
||||||
|
inline virtual std::span<const std::atomic<u64>> getRawData(void) const { return { m_words, m_size }; }
|
||||||
|
// Both getRawData() can be overridden to return empty spans for devices where internal memory shouldn't be exposed
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::atomic<u64>* m_words;
|
||||||
|
u64 m_wordCount;
|
||||||
|
u64 m_size;
|
||||||
|
};
|
||||||
|
namespace hw
|
||||||
|
{
|
||||||
|
class BUS : public MemoryDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
private: struct tMemoryRegion
|
||||||
|
{
|
||||||
|
MemoryDevice* device { nullptr };
|
||||||
|
PhysycalAddress startAddress { 0x0000'0000 };
|
||||||
|
PhysycalAddress endAddress { 0x0000'0000 };
|
||||||
|
bool remap { false };
|
||||||
|
ostd::String name { "" };
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
BUS(void) = default;
|
||||||
|
|
||||||
|
bool guest_cas_u32(PhysycalAddress addr, u32 expected, u32& actual, u32 desired) override;
|
||||||
|
|
||||||
|
// ---- Aligned loads ----
|
||||||
|
u64 load_u64(PhysycalAddress addr) const override;
|
||||||
|
u32 load_u32(PhysycalAddress addr) const override;
|
||||||
|
u16 load_u16(PhysycalAddress addr) const override;
|
||||||
|
u8 load_u8 (PhysycalAddress addr) const override;
|
||||||
|
|
||||||
|
// ---- Aligned stores ----
|
||||||
|
bool store_u64(PhysycalAddress addr, u64 value) override;
|
||||||
|
bool store_u32(PhysycalAddress addr, u32 value) override;
|
||||||
|
bool store_u16(PhysycalAddress addr, u16 value) override;
|
||||||
|
bool store_u8 (PhysycalAddress addr, u8 value) override;
|
||||||
|
|
||||||
|
void mapDevice(MemoryDevice& device, PhysycalAddress startAddr, PhysycalAddress endAddr, bool remap = false, ostd::String name = "");
|
||||||
|
ostd::String getMemoryRegionName(PhysycalAddress address);
|
||||||
|
|
||||||
|
[[noreturn]] void fault(PhysycalAddress addr, u32 size, const String& msg) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename Self>
|
||||||
|
auto findRegion(this Self&& self, PhysycalAddress address)
|
||||||
|
{
|
||||||
|
for (auto& region : self.m_regions)
|
||||||
|
{ // works whether self is const or not
|
||||||
|
if (address >= region.startAddress && address <= region.endAddress)
|
||||||
|
return ®ion;
|
||||||
|
}
|
||||||
|
self.fault(address, 0, "Memory device not found: ");
|
||||||
|
return decltype(&self.m_regions[0]){nullptr};
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<tMemoryRegion> m_regions;
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,10 +20,16 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ostd/data/Types.hpp>
|
#include "../common/Data.hpp"
|
||||||
|
|
||||||
namespace dragon
|
namespace dragon
|
||||||
{
|
{
|
||||||
|
struct GuestException
|
||||||
|
{
|
||||||
|
// ExceptionCause cause;
|
||||||
|
VirtualAddress fault_addr; // VA for translation faults, PA for bus faults, 0 otherwise
|
||||||
|
u32 faulting_pc; // set by the CPU before fault propagation, not by the throwsite
|
||||||
|
};
|
||||||
namespace hw
|
namespace hw
|
||||||
{
|
{
|
||||||
class CPU
|
class CPU
|
||||||
|
|
|
||||||
68
src/hardware/MMU.cpp
Normal file
68
src/hardware/MMU.cpp
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "MMU.hpp"
|
||||||
|
|
||||||
|
namespace dragon
|
||||||
|
{
|
||||||
|
namespace hw
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// PhysycalAddress MMU::translate(VirtualAddress va, AccessType type, Mode mode)
|
||||||
|
// {
|
||||||
|
// if (!m_enabled) return va; // identity map when disabled
|
||||||
|
|
||||||
|
// u32 l1_idx = (va >> 22) & 0x3FF;
|
||||||
|
// u32 l2_idx = (va >> 12) & 0x3FF;
|
||||||
|
// u32 offset = va & 0xFFF;
|
||||||
|
|
||||||
|
// // Walk level 1
|
||||||
|
// u32 l1_pte_addr = m_ptb + l1_idx * 4;
|
||||||
|
// u32 l1_pte = ram.load_u32(l1_pte_addr);
|
||||||
|
// if (!(l1_pte & PTE_PRESENT))
|
||||||
|
// throw GuestException{cause_for(type, PageFault), va};
|
||||||
|
|
||||||
|
// // Walk level 2
|
||||||
|
// u32 l2_table_phys = (l1_pte >> 12) << 12; // physical address of L2 table
|
||||||
|
// u32 l2_pte_addr = l2_table_phys + l2_idx * 4;
|
||||||
|
// u32 l2_pte = ram.load_u32(l2_pte_addr);
|
||||||
|
// if (!(l2_pte & PTE_PRESENT))
|
||||||
|
// throw GuestException{cause_for(type, PageFault), va};
|
||||||
|
|
||||||
|
// // Permission check
|
||||||
|
// if (mode == Mode::User && !(l2_pte & PTE_USER))
|
||||||
|
// throw GuestException{cause_for(type, PrivPageFault), va};
|
||||||
|
// if (type == AccessType::Read && !(l2_pte & PTE_READ)) throw GuestException{...};
|
||||||
|
// if (type == AccessType::Write && !(l2_pte & PTE_WRITE)) throw GuestException{...};
|
||||||
|
// if (type == AccessType::Execute && !(l2_pte & PTE_EXEC)) throw GuestException{...};
|
||||||
|
|
||||||
|
// // Set accessed bit (atomic so we don't race with other cores walking the same PTE)
|
||||||
|
// if (!(l2_pte & PTE_ACCESSED)) {
|
||||||
|
// // CAS to set the bit; ignore failure (means someone else set it)
|
||||||
|
// ram.guest_cas_u32(l2_pte_addr, l2_pte, l2_pte | PTE_ACCESSED);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// u32 phys_page = (l2_pte >> 12) << 12;
|
||||||
|
// return phys_page | offset;
|
||||||
|
// }
|
||||||
37
src/hardware/MMU.hpp
Normal file
37
src/hardware/MMU.hpp
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "BUS.hpp"
|
||||||
|
|
||||||
|
namespace dragon
|
||||||
|
{
|
||||||
|
namespace hw
|
||||||
|
{
|
||||||
|
class MMU : public MemoryDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit MMU(void);
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -26,35 +26,55 @@ namespace dragon
|
||||||
{
|
{
|
||||||
namespace hw
|
namespace hw
|
||||||
{
|
{
|
||||||
RAM::RAM(u64 size)
|
bool RAM::guest_cas_u32(PhysycalAddress addr, u32 expected, u32& actual, u32 desired)
|
||||||
{
|
{
|
||||||
m_wordCount = (size + 7) / 8; // Round up to multiple of 8 bytes for the word-granular backing
|
if (addr + 4 > m_size || (addr & 3))
|
||||||
m_size = size;
|
{
|
||||||
m_words = new std::atomic<u64>[m_wordCount];
|
fault(addr, 4, "Misaligned/OOB CAS");
|
||||||
for (u64 i = 0; i < m_wordCount; ++i)
|
return false;
|
||||||
m_words[i].store(0, std::memory_order_relaxed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RAM::~RAM(void)
|
u32 shift = (addr & 4) * 8;
|
||||||
|
u64 mask = u64(0xFFFFFFFFu) << shift;
|
||||||
|
std::atomic<u64>& word = m_words[addr >> 3];
|
||||||
|
|
||||||
|
u64 word_expected = word.load(std::memory_order_seq_cst);
|
||||||
|
while (true)
|
||||||
{
|
{
|
||||||
delete[] m_words;
|
u32 current_in_slot = static_cast<u32>(word_expected >> shift);
|
||||||
|
if (current_in_slot != expected)
|
||||||
|
{
|
||||||
|
// CAS fails: report what we saw, don't modify
|
||||||
|
actual = current_in_slot;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// CAS could succeed: try to update
|
||||||
|
u64 word_desired = (word_expected & ~mask) | (u64(desired) << shift);
|
||||||
|
if (word.compare_exchange_weak(word_expected, word_desired, std::memory_order_seq_cst, std::memory_order_seq_cst))
|
||||||
|
{
|
||||||
|
actual = expected; // tell caller we succeeded
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// CAS spuriously failed or someone else changed the word; word_expected
|
||||||
|
// is updated. Loop and re-check.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 RAM::load_u64(u32 addr) const
|
u64 RAM::load_u64(PhysycalAddress addr) const
|
||||||
{
|
{
|
||||||
if (addr + 8 > m_size || (addr & 7))
|
if (addr + 8 > m_size || (addr & 7))
|
||||||
{
|
{
|
||||||
fault(addr, 8);
|
fault(addr, 8, "Memory Fault: ");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return m_words[addr >> 3].load(std::memory_order_seq_cst);
|
return m_words[addr >> 3].load(std::memory_order_seq_cst);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 RAM::load_u32(u32 addr) const
|
u32 RAM::load_u32(PhysycalAddress addr) const
|
||||||
{
|
{
|
||||||
if (addr + 4 > m_size || (addr & 3))
|
if (addr + 4 > m_size || (addr & 3))
|
||||||
{
|
{
|
||||||
fault(addr, 4);
|
fault(addr, 4, "Memory Fault: ");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
u64 word = m_words[addr >> 3].load(std::memory_order_seq_cst);
|
u64 word = m_words[addr >> 3].load(std::memory_order_seq_cst);
|
||||||
|
|
@ -62,11 +82,11 @@ namespace dragon
|
||||||
return static_cast<u32>(word >> shift);
|
return static_cast<u32>(word >> shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 RAM::load_u16(u32 addr) const
|
u16 RAM::load_u16(PhysycalAddress addr) const
|
||||||
{
|
{
|
||||||
if (addr + 2 > m_size || (addr & 1))
|
if (addr + 2 > m_size || (addr & 1))
|
||||||
{
|
{
|
||||||
fault(addr, 2);
|
fault(addr, 2, "Memory Fault: ");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
u64 word = m_words[addr >> 3].load(std::memory_order_seq_cst);
|
u64 word = m_words[addr >> 3].load(std::memory_order_seq_cst);
|
||||||
|
|
@ -74,11 +94,11 @@ namespace dragon
|
||||||
return static_cast<u16>(word >> shift);
|
return static_cast<u16>(word >> shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 RAM::load_u8(u32 addr) const
|
u8 RAM::load_u8(PhysycalAddress addr) const
|
||||||
{
|
{
|
||||||
if (addr >= m_size)
|
if (addr >= m_size)
|
||||||
{
|
{
|
||||||
fault(addr, 1);
|
fault(addr, 1, "Memory Fault: ");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
u64 word = m_words[addr >> 3].load(std::memory_order_seq_cst);
|
u64 word = m_words[addr >> 3].load(std::memory_order_seq_cst);
|
||||||
|
|
@ -86,22 +106,23 @@ namespace dragon
|
||||||
return static_cast<u8>(word >> shift);
|
return static_cast<u8>(word >> shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RAM::store_u64(u32 addr, u64 value)
|
bool RAM::store_u64(PhysycalAddress addr, u64 value)
|
||||||
{
|
{
|
||||||
if (addr + 8 > m_size || (addr & 7))
|
if (addr + 8 > m_size || (addr & 7))
|
||||||
{
|
{
|
||||||
fault(addr, 8);
|
fault(addr, 8, "Memory Fault: ");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
m_words[addr >> 3].store(value, std::memory_order_seq_cst);
|
m_words[addr >> 3].store(value, std::memory_order_seq_cst);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RAM::store_u32(u32 addr, u32 value)
|
bool RAM::store_u32(PhysycalAddress addr, u32 value)
|
||||||
{
|
{
|
||||||
if (addr + 4 > m_size || (addr & 3))
|
if (addr + 4 > m_size || (addr & 3))
|
||||||
{
|
{
|
||||||
fault(addr, 4);
|
fault(addr, 4, "Memory Fault: ");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
u32 shift = (addr & 4) * 8;
|
u32 shift = (addr & 4) * 8;
|
||||||
u64 mask = u64(0xFFFFFFFFu) << shift;
|
u64 mask = u64(0xFFFFFFFFu) << shift;
|
||||||
|
|
@ -112,14 +133,15 @@ namespace dragon
|
||||||
{
|
{
|
||||||
desired = (expected & ~mask) | (u64(value) << shift);
|
desired = (expected & ~mask) | (u64(value) << shift);
|
||||||
} while (!word.compare_exchange_weak(expected, desired, std::memory_order_seq_cst, std::memory_order_relaxed));
|
} while (!word.compare_exchange_weak(expected, desired, std::memory_order_seq_cst, std::memory_order_relaxed));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RAM::store_u16(u32 addr, u16 value)
|
bool RAM::store_u16(PhysycalAddress addr, u16 value)
|
||||||
{
|
{
|
||||||
if (addr + 2 > m_size || (addr & 1))
|
if (addr + 2 > m_size || (addr & 1))
|
||||||
{
|
{
|
||||||
fault(addr, 2);
|
fault(addr, 2, "Memory Fault: ");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
u32 shift = (addr & 6) * 8;
|
u32 shift = (addr & 6) * 8;
|
||||||
u64 mask = u64(0xFFFFu) << shift;
|
u64 mask = u64(0xFFFFu) << shift;
|
||||||
|
|
@ -130,14 +152,15 @@ namespace dragon
|
||||||
{
|
{
|
||||||
desired = (expected & ~mask) | (u64(value) << shift);
|
desired = (expected & ~mask) | (u64(value) << shift);
|
||||||
} while (!word.compare_exchange_weak(expected, desired, std::memory_order_seq_cst, std::memory_order_relaxed));
|
} while (!word.compare_exchange_weak(expected, desired, std::memory_order_seq_cst, std::memory_order_relaxed));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RAM::store_u8(u32 addr, u8 value)
|
bool RAM::store_u8(PhysycalAddress addr, u8 value)
|
||||||
{
|
{
|
||||||
if (addr >= m_size)
|
if (addr >= m_size)
|
||||||
{
|
{
|
||||||
fault(addr, 1);
|
fault(addr, 1, "Memory Fault: ");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
u32 shift = (addr & 7) * 8;
|
u32 shift = (addr & 7) * 8;
|
||||||
u64 mask = u64(0xFFu) << shift;
|
u64 mask = u64(0xFFu) << shift;
|
||||||
|
|
@ -148,11 +171,12 @@ namespace dragon
|
||||||
{
|
{
|
||||||
desired = (expected & ~mask) | (u64(value) << shift);
|
desired = (expected & ~mask) | (u64(value) << shift);
|
||||||
} while (!word.compare_exchange_weak(expected, desired, std::memory_order_seq_cst, std::memory_order_relaxed));
|
} while (!word.compare_exchange_weak(expected, desired, std::memory_order_seq_cst, std::memory_order_relaxed));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RAM::fault(u32 addr, u32 size) const
|
void RAM::fault(PhysycalAddress addr, u32 size, const String& msg) const
|
||||||
{
|
{
|
||||||
std::cout << "Memory Fault: " << ostd::String::getHexStr(addr, true, 4) << "\n";
|
std::cout << msg << ostd::String::getHexStr(addr, true, 4) << "\n";
|
||||||
/* TODO: raise guest trap */
|
/* TODO: raise guest trap */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,46 +21,33 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ostd/data/Types.hpp>
|
#include <ostd/data/Types.hpp>
|
||||||
#include <atomic>
|
|
||||||
|
#include "BUS.hpp"
|
||||||
|
|
||||||
namespace dragon
|
namespace dragon
|
||||||
{
|
{
|
||||||
namespace hw
|
namespace hw
|
||||||
{
|
{
|
||||||
class RAM
|
class RAM : public AtomicStorageMemoryDevice
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit RAM(u64 size);
|
explicit RAM(u64 size) : AtomicStorageMemoryDevice(size) { }
|
||||||
~RAM(void);
|
|
||||||
RAM(const RAM&) = delete;
|
bool guest_cas_u32(PhysycalAddress addr, u32 expected, u32& actual, u32 desired) override;
|
||||||
RAM& operator=(const RAM&) = delete;
|
|
||||||
|
|
||||||
// ---- Aligned loads ----
|
// ---- Aligned loads ----
|
||||||
u64 load_u64(u32 addr) const;
|
u64 load_u64(PhysycalAddress addr) const override;
|
||||||
u32 load_u32(u32 addr) const;
|
u32 load_u32(PhysycalAddress addr) const override;
|
||||||
u16 load_u16(u32 addr) const;
|
u16 load_u16(PhysycalAddress addr) const override;
|
||||||
u8 load_u8 (u32 addr) const;
|
u8 load_u8 (PhysycalAddress addr) const override;
|
||||||
inline i64 load_i64(u32 addr) const { return cast<i64>(load_u64(addr)); }
|
|
||||||
inline i32 load_i32(u32 addr) const { return cast<i32>(load_u32(addr)); }
|
|
||||||
inline i16 load_i16(u32 addr) const { return cast<i16>(load_u16(addr)); }
|
|
||||||
inline i8 load_i8 (u32 addr) const { return cast<i8 >(load_u8 (addr)); }
|
|
||||||
|
|
||||||
// ---- Aligned stores ----
|
// ---- Aligned stores ----
|
||||||
void store_u64(u32 addr, u64 value);
|
bool store_u64(PhysycalAddress addr, u64 value) override;
|
||||||
void store_u32(u32 addr, u32 value);
|
bool store_u32(PhysycalAddress addr, u32 value) override;
|
||||||
void store_u16(u32 addr, u16 value);
|
bool store_u16(PhysycalAddress addr, u16 value) override;
|
||||||
void store_u8 (u32 addr, u8 value);
|
bool store_u8 (PhysycalAddress addr, u8 value) override;
|
||||||
inline void store_i64(u32 addr, u64 value) { store_u64(addr, cast<i64>(value)); }
|
|
||||||
inline void store_i32(u32 addr, u32 value) { store_u32(addr, cast<i32>(value)); }
|
|
||||||
inline void store_i16(u32 addr, u16 value) { store_u16(addr, cast<i16>(value)); }
|
|
||||||
inline void store_i8 (u32 addr, u8 value) { store_u8 (addr, cast<i8 >(value)); }
|
|
||||||
|
|
||||||
void fault(u32 addr, u32 size) const;
|
[[noreturn]] void fault(PhysycalAddress addr, u32 size, const String& msg = "") const override;
|
||||||
|
|
||||||
private:
|
|
||||||
std::atomic<u64>* m_words;
|
|
||||||
u64 m_wordCount;
|
|
||||||
u64 m_size;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,23 @@
|
||||||
|
/*
|
||||||
|
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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <ostd/ostd.hpp>
|
#include <ostd/ostd.hpp>
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue