Finished first draft of MMU class
This commit is contained in:
parent
7c0f11b34b
commit
dba1241f2d
10 changed files with 444 additions and 155 deletions
|
|
@ -1 +1 @@
|
||||||
10
|
12
|
||||||
|
|
|
||||||
|
|
@ -407,7 +407,14 @@ int main(void)
|
||||||
TEST(test_roundtrip_u64);
|
TEST(test_roundtrip_u64);
|
||||||
TEST(test_no_neighbour_corruption_singlethread);
|
TEST(test_no_neighbour_corruption_singlethread);
|
||||||
TEST(test_endianness_layout);
|
TEST(test_endianness_layout);
|
||||||
|
try
|
||||||
|
{
|
||||||
TEST(test_faults_dont_modify_memory);
|
TEST(test_faults_dont_modify_memory);
|
||||||
|
}
|
||||||
|
catch (dragon::GuestException& e)
|
||||||
|
{
|
||||||
|
std::cout << e.msg << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
std::cout << "\n--- multi-threaded atomicity ---\n";
|
std::cout << "\n--- multi-threaded atomicity ---\n";
|
||||||
TEST(test_concurrent_same_word_smoke);
|
TEST(test_concurrent_same_word_smoke);
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,66 @@ namespace dragon
|
||||||
// ------ Contextual Aliases ------
|
// ------ Contextual Aliases ------
|
||||||
using AddressType = u32;
|
using AddressType = u32;
|
||||||
using VirtualAddress = AddressType;
|
using VirtualAddress = AddressType;
|
||||||
using PhysycalAddress = AddressType;
|
using PhysicalAddress = AddressType;
|
||||||
// --------------------------------
|
// --------------------------------
|
||||||
|
|
||||||
enum class MMUAccessType { Read, Write, Execute };
|
enum class MMUAccessType : u8 { Read, Write, Execute };
|
||||||
|
enum class MMUAccessMode : u8 { Privileged, User };
|
||||||
|
enum class MMUFaultKind : u8 { PageFault, PrivPageFault, Bounds, Misalignment };
|
||||||
|
enum class ExceptionCause : u32
|
||||||
|
{
|
||||||
|
// === Instruction fetch faults ===
|
||||||
|
MisalignedFetch = 0x0000'0000, // PC not aligned to instruction boundary
|
||||||
|
InstructionBounds = 0x0000'0001, // PC outside any mapped region
|
||||||
|
InstructionPageFault = 0x0000'0002, // virt page not mapped (MMU on) or no X permission
|
||||||
|
InstructionPrivPageFault = 0x0000'0003, // privileged virt page not mapped (MMU on) or no X permission
|
||||||
|
UnknownInstruction = 0x0000'0004, // decoded opcode is not valid
|
||||||
|
|
||||||
|
// === Privilege ===
|
||||||
|
PrivilegeViolation = 0x0000'1000, // user mode executed a [PRIV] instruction
|
||||||
|
|
||||||
|
// === Memory access faults (loads) ===
|
||||||
|
MisalignedLoad = 0x0000'2000, // load address not naturally aligned
|
||||||
|
LoadBounds = 0x0000'2001, // load address outside mapped region
|
||||||
|
LoadPageFault = 0x0000'2002, // virt page not mapped or no R permission
|
||||||
|
LoadPrivPageFault = 0x0000'2003, // virt page not mapped or no R permission
|
||||||
|
|
||||||
|
// === Memory access faults (stores) ===
|
||||||
|
MisalignedStore = 0x0000'3000,
|
||||||
|
StoreBounds = 0x0000'3001,
|
||||||
|
StorePageFault = 0x0000'3002, // virt page not mapped or no W permission
|
||||||
|
StorePrivPageFault = 0x0000'3003, // virt page not mapped or no W permission
|
||||||
|
|
||||||
|
// === Atomic op faults ===
|
||||||
|
AtomicNotSupported = 0x0000'4000, // CAS/XADD/XCHG on a device that can't (ROM, MMIO)
|
||||||
|
|
||||||
|
// === Arithmetic ===
|
||||||
|
DivideByZero = 0x0000'5000,
|
||||||
|
|
||||||
|
// === MMIO ===
|
||||||
|
BusUnmapped = 0x0000'6000,
|
||||||
|
ReadOnlyViolation = 0x0000'6001,
|
||||||
|
|
||||||
|
// === Deliberate guest-initiated traps ===
|
||||||
|
Syscall = 0x0000'7000, // syscall instruction
|
||||||
|
SoftwareInt = 0x0000'7001, // int N instruction
|
||||||
|
TrapInstruction = 0x0000'7002, // explicit trap instr
|
||||||
|
Breakpoint = 0x0000'7003, // brk for debugger
|
||||||
|
|
||||||
|
// === Asynchronous (external) ===
|
||||||
|
HardwareInterrupt = 0x0000'F000, // generic external IRQ; details in INT controller
|
||||||
|
|
||||||
|
// === Catastrophic ===
|
||||||
|
DoubleFault = 0x000F'FFFF, // exception while handling exception
|
||||||
|
|
||||||
|
// Just in case
|
||||||
|
NoFault = 0xFFFF'FFFF,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GuestException
|
||||||
|
{
|
||||||
|
ExceptionCause cause;
|
||||||
|
VirtualAddress fault_addr; // VA for translation faults, PA for bus faults, 0 otherwise
|
||||||
|
String msg; // For debug
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ namespace dragon
|
||||||
|
|
||||||
bool MemoryDevice::guest_cas_u32(AddressType addr, u32 expected, u32& actual, u32 desired)
|
bool MemoryDevice::guest_cas_u32(AddressType addr, u32 expected, u32& actual, u32 desired)
|
||||||
{
|
{
|
||||||
fault(addr, 4, "CAS not supported on this device");
|
fault(addr, ExceptionCause::AtomicNotSupported, "MemoryDevice::guest_cas_u32");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,7 +55,7 @@ namespace dragon
|
||||||
|
|
||||||
namespace hw
|
namespace hw
|
||||||
{
|
{
|
||||||
bool BUS::guest_cas_u32(PhysycalAddress addr, u32 expected, u32& actual, u32 desired)
|
bool BUS::guest_cas_u32(PhysicalAddress addr, u32 expected, u32& actual, u32 desired)
|
||||||
{
|
{
|
||||||
auto region = findRegion(addr);
|
auto region = findRegion(addr);
|
||||||
if (!region) { /* fault */ return false; }
|
if (!region) { /* fault */ return false; }
|
||||||
|
|
@ -63,7 +63,7 @@ namespace dragon
|
||||||
return region->device->guest_cas_u32(finalAddr, expected, actual, desired);
|
return region->device->guest_cas_u32(finalAddr, expected, actual, desired);
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 BUS::load_u8(PhysycalAddress addr) const
|
u8 BUS::load_u8(PhysicalAddress addr) const
|
||||||
{
|
{
|
||||||
auto region = findRegion(addr);
|
auto region = findRegion(addr);
|
||||||
if (region == nullptr) return 0x00; //TODO: Error
|
if (region == nullptr) return 0x00; //TODO: Error
|
||||||
|
|
@ -71,7 +71,7 @@ namespace dragon
|
||||||
return region->device->load_u8(finalAddr);
|
return region->device->load_u8(finalAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 BUS::load_u16(PhysycalAddress addr) const
|
u16 BUS::load_u16(PhysicalAddress addr) const
|
||||||
{
|
{
|
||||||
auto region = findRegion(addr);
|
auto region = findRegion(addr);
|
||||||
if (region == nullptr) return 0x0000; //TODO: Error
|
if (region == nullptr) return 0x0000; //TODO: Error
|
||||||
|
|
@ -79,7 +79,7 @@ namespace dragon
|
||||||
return region->device->load_u16(finalAddr);
|
return region->device->load_u16(finalAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 BUS::load_u32(PhysycalAddress addr) const
|
u32 BUS::load_u32(PhysicalAddress addr) const
|
||||||
{
|
{
|
||||||
auto region = findRegion(addr);
|
auto region = findRegion(addr);
|
||||||
if (region == nullptr) return 0x0000'0000; //TODO: Error
|
if (region == nullptr) return 0x0000'0000; //TODO: Error
|
||||||
|
|
@ -87,7 +87,7 @@ namespace dragon
|
||||||
return region->device->load_u32(finalAddr);
|
return region->device->load_u32(finalAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 BUS::load_u64(PhysycalAddress addr) const
|
u64 BUS::load_u64(PhysicalAddress addr) const
|
||||||
{
|
{
|
||||||
auto region = findRegion(addr);
|
auto region = findRegion(addr);
|
||||||
if (region == nullptr) return 0x0000'0000'0000'0000; //TODO: Error
|
if (region == nullptr) return 0x0000'0000'0000'0000; //TODO: Error
|
||||||
|
|
@ -95,7 +95,7 @@ namespace dragon
|
||||||
return region->device->load_u64(finalAddr);
|
return region->device->load_u64(finalAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BUS::store_u8(PhysycalAddress addr, u8 value)
|
bool BUS::store_u8(PhysicalAddress addr, u8 value)
|
||||||
{
|
{
|
||||||
auto region = findRegion(addr);
|
auto region = findRegion(addr);
|
||||||
if (region == nullptr) return false;
|
if (region == nullptr) return false;
|
||||||
|
|
@ -103,7 +103,7 @@ namespace dragon
|
||||||
return region->device->store_u8(finalAddr, value);
|
return region->device->store_u8(finalAddr, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BUS::store_u16(PhysycalAddress addr, u16 value)
|
bool BUS::store_u16(PhysicalAddress addr, u16 value)
|
||||||
{
|
{
|
||||||
auto region = findRegion(addr);
|
auto region = findRegion(addr);
|
||||||
if (region == nullptr) return false;
|
if (region == nullptr) return false;
|
||||||
|
|
@ -111,7 +111,7 @@ namespace dragon
|
||||||
return region->device->store_u16(finalAddr, value);
|
return region->device->store_u16(finalAddr, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BUS::store_u32(PhysycalAddress addr, u32 value)
|
bool BUS::store_u32(PhysicalAddress addr, u32 value)
|
||||||
{
|
{
|
||||||
auto region = findRegion(addr);
|
auto region = findRegion(addr);
|
||||||
if (region == nullptr) return false;
|
if (region == nullptr) return false;
|
||||||
|
|
@ -119,7 +119,7 @@ namespace dragon
|
||||||
return region->device->store_u32(finalAddr, value);
|
return region->device->store_u32(finalAddr, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BUS::store_u64(PhysycalAddress addr, u64 value)
|
bool BUS::store_u64(PhysicalAddress addr, u64 value)
|
||||||
{
|
{
|
||||||
auto region = findRegion(addr);
|
auto region = findRegion(addr);
|
||||||
if (region == nullptr) return false;
|
if (region == nullptr) return false;
|
||||||
|
|
@ -127,22 +127,16 @@ namespace dragon
|
||||||
return region->device->store_u64(finalAddr, value);
|
return region->device->store_u64(finalAddr, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BUS::mapDevice(MemoryDevice& device, PhysycalAddress startAddr, PhysycalAddress endAddr, bool remap, ostd::String name)
|
void BUS::mapDevice(MemoryDevice& device, PhysicalAddress startAddr, PhysicalAddress endAddr, bool remap, ostd::String name)
|
||||||
{
|
{
|
||||||
m_regions.push_back({ &device, startAddr, endAddr, remap, name });
|
m_regions.push_back({ &device, startAddr, endAddr, remap, name });
|
||||||
}
|
}
|
||||||
|
|
||||||
ostd::String BUS::getMemoryRegionName(PhysycalAddress address)
|
ostd::String BUS::getMemoryRegionName(PhysicalAddress address)
|
||||||
{
|
{
|
||||||
tMemoryRegion* region = findRegion(address);
|
tMemoryRegion* region = findRegion(address);
|
||||||
if (region == nullptr) return "Invalid";
|
if (region == nullptr) return "Invalid";
|
||||||
return region->name;
|
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. */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,8 @@ namespace dragon
|
||||||
// ---- Aligned loads ----
|
// ---- Aligned loads ----
|
||||||
virtual u64 load_u64(AddressType addr) const = 0;
|
virtual u64 load_u64(AddressType addr) const = 0;
|
||||||
virtual u32 load_u32(AddressType addr) const = 0;
|
virtual u32 load_u32(AddressType addr) const = 0;
|
||||||
virtual u16 load_u16(u32 addr) const = 0;
|
virtual u16 load_u16(AddressType addr) const = 0;
|
||||||
virtual u8 load_u8 (u32 addr) const = 0;
|
virtual u8 load_u8 (AddressType addr) const = 0;
|
||||||
|
|
||||||
inline i64 load_i64(AddressType addr) const { return cast<i64>(load_u64(addr)); }
|
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 i32 load_i32(AddressType addr) const { return cast<i32>(load_u32(addr)); }
|
||||||
|
|
@ -60,8 +60,7 @@ namespace dragon
|
||||||
inline bool store_i16(AddressType addr, u16 value) { return store_u16(addr, cast<i16>(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)); }
|
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
|
[[noreturn]] virtual void fault(u32 addr, ExceptionCause cause, const String& msg = "") const { throw GuestException{ cause, addr, msg }; }; // Message can be useful for debugging
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class AtomicStorageMemoryDevice : public MemoryDevice
|
class AtomicStorageMemoryDevice : public MemoryDevice
|
||||||
|
|
@ -94,8 +93,8 @@ namespace dragon
|
||||||
private: struct tMemoryRegion
|
private: struct tMemoryRegion
|
||||||
{
|
{
|
||||||
MemoryDevice* device { nullptr };
|
MemoryDevice* device { nullptr };
|
||||||
PhysycalAddress startAddress { 0x0000'0000 };
|
PhysicalAddress startAddress { 0x0000'0000 };
|
||||||
PhysycalAddress endAddress { 0x0000'0000 };
|
PhysicalAddress endAddress { 0x0000'0000 };
|
||||||
bool remap { false };
|
bool remap { false };
|
||||||
ostd::String name { "" };
|
ostd::String name { "" };
|
||||||
};
|
};
|
||||||
|
|
@ -103,35 +102,33 @@ namespace dragon
|
||||||
public:
|
public:
|
||||||
BUS(void) = default;
|
BUS(void) = default;
|
||||||
|
|
||||||
bool guest_cas_u32(PhysycalAddress addr, u32 expected, u32& actual, u32 desired) override;
|
bool guest_cas_u32(PhysicalAddress addr, u32 expected, u32& actual, u32 desired) override;
|
||||||
|
|
||||||
// ---- Aligned loads ----
|
// ---- Aligned loads ----
|
||||||
u64 load_u64(PhysycalAddress addr) const override;
|
u64 load_u64(PhysicalAddress addr) const override;
|
||||||
u32 load_u32(PhysycalAddress addr) const override;
|
u32 load_u32(PhysicalAddress addr) const override;
|
||||||
u16 load_u16(PhysycalAddress addr) const override;
|
u16 load_u16(PhysicalAddress addr) const override;
|
||||||
u8 load_u8 (PhysycalAddress addr) const override;
|
u8 load_u8 (PhysicalAddress addr) const override;
|
||||||
|
|
||||||
// ---- Aligned stores ----
|
// ---- Aligned stores ----
|
||||||
bool store_u64(PhysycalAddress addr, u64 value) override;
|
bool store_u64(PhysicalAddress addr, u64 value) override;
|
||||||
bool store_u32(PhysycalAddress addr, u32 value) override;
|
bool store_u32(PhysicalAddress addr, u32 value) override;
|
||||||
bool store_u16(PhysycalAddress addr, u16 value) override;
|
bool store_u16(PhysicalAddress addr, u16 value) override;
|
||||||
bool store_u8 (PhysycalAddress addr, u8 value) override;
|
bool store_u8 (PhysicalAddress addr, u8 value) override;
|
||||||
|
|
||||||
void mapDevice(MemoryDevice& device, PhysycalAddress startAddr, PhysycalAddress endAddr, bool remap = false, ostd::String name = "");
|
void mapDevice(MemoryDevice& device, PhysicalAddress startAddr, PhysicalAddress endAddr, bool remap = false, ostd::String name = "");
|
||||||
ostd::String getMemoryRegionName(PhysycalAddress address);
|
ostd::String getMemoryRegionName(PhysicalAddress address);
|
||||||
|
|
||||||
[[noreturn]] void fault(PhysycalAddress addr, u32 size, const String& msg) const override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <typename Self>
|
template <typename Self>
|
||||||
auto findRegion(this Self&& self, PhysycalAddress address)
|
auto findRegion(this Self&& self, PhysicalAddress address)
|
||||||
{
|
{
|
||||||
for (auto& region : self.m_regions)
|
for (auto& region : self.m_regions)
|
||||||
{ // works whether self is const or not
|
{ // works whether self is const or not
|
||||||
if (address >= region.startAddress && address <= region.endAddress)
|
if (address >= region.startAddress && address <= region.endAddress)
|
||||||
return ®ion;
|
return ®ion;
|
||||||
}
|
}
|
||||||
self.fault(address, 0, "Memory device not found: ");
|
self.fault(address, ExceptionCause::BusUnmapped, "Memory device not found");
|
||||||
return decltype(&self.m_regions[0]){nullptr};
|
return decltype(&self.m_regions[0]){nullptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,16 +20,10 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../common/Data.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
|
||||||
|
|
|
||||||
|
|
@ -24,45 +24,223 @@ namespace dragon
|
||||||
{
|
{
|
||||||
namespace hw
|
namespace hw
|
||||||
{
|
{
|
||||||
|
MMU::MMU(BUS& bus, const MMUAccessMode* mode_ptr) : m_bus(bus), m_modePtr(mode_ptr)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PhysicalAddress MMU::translate(VirtualAddress va, MMUAccessType type, MMUAccessMode mode) const
|
||||||
|
{
|
||||||
|
// Phase 1: when MMU is off, identity-map. No translation, no checks.
|
||||||
|
// This is the state at boot and during early kernel bringup.
|
||||||
|
if (!m_enabled) return va;
|
||||||
|
|
||||||
|
// Decompose the virtual address into the three pieces.
|
||||||
|
// ┌──────────┬──────────┬──────────────────┐
|
||||||
|
// │ L1 idx │ L2 idx │ offset │
|
||||||
|
// │ (10 bits)│ (10 bits)│ (12 bits) │
|
||||||
|
// └──────────┴──────────┴──────────────────┘
|
||||||
|
const u32 l1_idx = (va >> 22) & 0x3FF;
|
||||||
|
const u32 l2_idx = (va >> 12) & 0x3FF;
|
||||||
|
const u32 offset = va & 0xFFF;
|
||||||
|
|
||||||
|
// ---- Walk Level 1 ----
|
||||||
|
// L1 table starts at m_ptb. Each entry is 4 bytes.
|
||||||
|
// Read the L1 PTE from physical memory via the bus.
|
||||||
|
const PhysicalAddress l1_pte_addr = m_ptb + l1_idx * 4;
|
||||||
|
const u32 l1_pte = m_bus.load_u32(l1_pte_addr);
|
||||||
|
|
||||||
|
// If the L1 entry isn't present, the L2 table doesn't exist for this region.
|
||||||
|
// The whole 4 MB chunk (covered by this L1 entry) is unmapped.
|
||||||
|
if (!pte_has(l1_pte, PTE_PRESENT))
|
||||||
|
fault(va, get_exception_cause(MMUFaultKind::PageFault, type), "MMU::translate L1 not present");
|
||||||
|
|
||||||
|
// ---- Walk Level 2 ----
|
||||||
|
// The L2 table's physical base address is in the upper 20 bits of the L1 PTE.
|
||||||
|
const PhysicalAddress l2_table_phys = pte_phys_addr(l1_pte);
|
||||||
|
const PhysicalAddress l2_pte_addr = l2_table_phys + l2_idx * 4;
|
||||||
|
const u32 l2_pte = m_bus.load_u32(l2_pte_addr);
|
||||||
|
|
||||||
|
// If the L2 entry isn't present, this specific 4 KB page isn't mapped.
|
||||||
|
if (!pte_has(l2_pte, PTE_PRESENT))
|
||||||
|
fault(va, get_exception_cause(MMUFaultKind::PageFault, type), "MMU::translate L2 not present");
|
||||||
|
|
||||||
|
// ---- Permission checks ----
|
||||||
|
// User mode can only access pages with the U bit set. Supervisor can access anything.
|
||||||
|
if (mode == MMUAccessMode::User && !pte_has(l2_pte, PTE_USER))
|
||||||
|
fault(va, get_exception_cause(MMUFaultKind::PrivPageFault, type), "MMU::translate user access to priv page");
|
||||||
|
|
||||||
|
// Per-access-type permission check. R/W/X bits.
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case MMUAccessType::Read:
|
||||||
|
if (!pte_has(l2_pte, PTE_READ))
|
||||||
|
fault(va, get_exception_cause(MMUFaultKind::PageFault, type), "MMU::translate no read perm");
|
||||||
|
break;
|
||||||
|
case MMUAccessType::Write:
|
||||||
|
if (!pte_has(l2_pte, PTE_WRITE))
|
||||||
|
fault(va, get_exception_cause(MMUFaultKind::PageFault, type), "MMU::translate no write perm");
|
||||||
|
break;
|
||||||
|
case MMUAccessType::Execute:
|
||||||
|
if (!pte_has(l2_pte, PTE_EXEC))
|
||||||
|
fault(va, get_exception_cause(MMUFaultKind::PageFault, type), "MMU::translate no exec perm");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Update Accessed bit (and Dirty bit on writes) ----
|
||||||
|
// The hardware sets these so the OS can track which pages have been used.
|
||||||
|
// Use CAS so we don't race with other cores walking the same PTE.
|
||||||
|
// If the CAS fails, someone else updated the PTE — that's fine, we don't retry
|
||||||
|
// because they will have set the same bits we wanted to set.
|
||||||
|
u32 new_flags = 0;
|
||||||
|
if (!pte_has(l2_pte, PTE_ACCESSED))
|
||||||
|
new_flags |= PTE_ACCESSED;
|
||||||
|
if (type == MMUAccessType::Write && !pte_has(l2_pte, PTE_DIRTY))
|
||||||
|
new_flags |= PTE_DIRTY;
|
||||||
|
|
||||||
|
if (new_flags != 0)
|
||||||
|
{
|
||||||
|
u32 actual; // out-param required by guest_cas_u32; we ignore its value
|
||||||
|
m_bus.guest_cas_u32(l2_pte_addr, l2_pte, actual, l2_pte | new_flags);
|
||||||
|
// Intentionally don't check the return value or retry. If another core
|
||||||
|
// raced us and set different bits, the next access will set them via
|
||||||
|
// its own translate call.
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Assemble final physical address ----
|
||||||
|
const PhysicalAddress phys_page = pte_phys_addr(l2_pte);
|
||||||
|
return phys_page | offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MMU::enable(bool value)
|
||||||
|
{
|
||||||
|
if (m_enabled == value) return;
|
||||||
|
m_enabled = value;
|
||||||
|
invalidateAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MMU::setPtb(PhysicalAddress ptb)
|
||||||
|
{
|
||||||
|
const PhysicalAddress aligned = ptb & PTE_PHYS_MASK;
|
||||||
|
if (m_ptb == aligned) return;
|
||||||
|
m_ptb = aligned;
|
||||||
|
invalidateAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MMU::invalidateAll(void)
|
||||||
|
{// TODO:
|
||||||
|
// No TLB yet. When implemented, this flushes every cached translation,
|
||||||
|
// including global entries.
|
||||||
|
}
|
||||||
|
|
||||||
|
void MMU::invalidatePage(VirtualAddress va)
|
||||||
|
{// TODO:
|
||||||
|
// No TLB yet. When implemented, this flushes any cached translation
|
||||||
|
// for the page containing va, regardless of G bit or ASID.
|
||||||
|
(void)va; // suppress unused-parameter warning for now
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MMU::guest_cas_u32(VirtualAddress va, u32 expected, u32& actual, u32 desired)
|
||||||
|
{
|
||||||
|
PhysicalAddress pa = translate(va, MMUAccessType::Write);
|
||||||
|
return m_bus.guest_cas_u32(pa, expected, actual, desired);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 MMU::load_u64(VirtualAddress va) const
|
||||||
|
{
|
||||||
|
PhysicalAddress pa = translate(va, MMUAccessType::Read);
|
||||||
|
return m_bus.load_u64(pa);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 MMU::load_u32(VirtualAddress va) const
|
||||||
|
{
|
||||||
|
PhysicalAddress pa = translate(va, MMUAccessType::Read);
|
||||||
|
return m_bus.load_u32(pa);
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 MMU::load_u16(VirtualAddress va) const
|
||||||
|
{
|
||||||
|
PhysicalAddress pa = translate(va, MMUAccessType::Read);
|
||||||
|
return m_bus.load_u16(pa);
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 MMU::load_u8(VirtualAddress va) const
|
||||||
|
{
|
||||||
|
PhysicalAddress pa = translate(va, MMUAccessType::Read);
|
||||||
|
return m_bus.load_u8(pa);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MMU::store_u64(VirtualAddress va, u64 value)
|
||||||
|
{
|
||||||
|
PhysicalAddress pa = translate(va, MMUAccessType::Write);
|
||||||
|
return m_bus.store_u64(pa, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MMU::store_u32(VirtualAddress va, u32 value)
|
||||||
|
{
|
||||||
|
PhysicalAddress pa = translate(va, MMUAccessType::Write);
|
||||||
|
return m_bus.store_u32(pa, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MMU::store_u16(VirtualAddress va, u16 value)
|
||||||
|
{
|
||||||
|
PhysicalAddress pa = translate(va, MMUAccessType::Write);
|
||||||
|
return m_bus.store_u16(pa, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MMU::store_u8(VirtualAddress va, u8 value)
|
||||||
|
{
|
||||||
|
PhysicalAddress pa = translate(va, MMUAccessType::Write);
|
||||||
|
return m_bus.store_u8(pa, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 MMU::fetch_u32(VirtualAddress va) const
|
||||||
|
{
|
||||||
|
PhysicalAddress pa = translate(va, MMUAccessType::Execute);
|
||||||
|
return m_bus.load_u32(pa);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExceptionCause MMU::get_exception_cause(MMUFaultKind kind, MMUAccessType type) const
|
||||||
|
{
|
||||||
|
using F = MMUFaultKind;
|
||||||
|
using T = MMUAccessType;
|
||||||
|
using E = ExceptionCause;
|
||||||
|
|
||||||
|
switch (kind) {
|
||||||
|
case F::PageFault:
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case T::Read: return E::LoadPageFault;
|
||||||
|
case T::Write: return E::StorePageFault;
|
||||||
|
case T::Execute: return E::InstructionPageFault;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case F::PrivPageFault:
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case T::Read: return E::LoadPrivPageFault;
|
||||||
|
case T::Write: return E::StorePrivPageFault;
|
||||||
|
case T::Execute: return E::InstructionPrivPageFault;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case F::Bounds:
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case T::Read: return E::LoadBounds;
|
||||||
|
case T::Write: return E::StoreBounds;
|
||||||
|
case T::Execute: return E::InstructionBounds;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case F::Misalignment:
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case T::Read: return E::MisalignedLoad;
|
||||||
|
case T::Write: return E::MisalignedStore;
|
||||||
|
case T::Execute: return E::MisalignedFetch;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return E::NoFault; // unreachable; satisfies compiler
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
// }
|
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,83 @@ namespace dragon
|
||||||
class MMU : public MemoryDevice
|
class MMU : public MemoryDevice
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit MMU(void);
|
explicit 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; }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,20 +19,39 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "RAM.hpp"
|
#include "RAM.hpp"
|
||||||
#include <iostream>
|
|
||||||
#include <ostd/string/String.hpp>
|
#include <ostd/string/String.hpp>
|
||||||
|
|
||||||
|
#define DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_R(size, msg) \
|
||||||
|
if (addr + size > m_size) \
|
||||||
|
{ \
|
||||||
|
fault(addr, ExceptionCause::LoadBounds, msg); \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
|
if (addr & (size - 1)) \
|
||||||
|
{ \
|
||||||
|
fault(addr, ExceptionCause::MisalignedLoad, msg); \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
|
|
||||||
|
#define DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_W(size, msg) \
|
||||||
|
if (addr + size > m_size) \
|
||||||
|
{ \
|
||||||
|
fault(addr, ExceptionCause::StoreBounds, msg); \
|
||||||
|
return false; \
|
||||||
|
} \
|
||||||
|
if (addr & (size - 1)) \
|
||||||
|
{ \
|
||||||
|
fault(addr, ExceptionCause::MisalignedStore, msg); \
|
||||||
|
return false; \
|
||||||
|
} \
|
||||||
|
|
||||||
namespace dragon
|
namespace dragon
|
||||||
{
|
{
|
||||||
namespace hw
|
namespace hw
|
||||||
{
|
{
|
||||||
bool RAM::guest_cas_u32(PhysycalAddress addr, u32 expected, u32& actual, u32 desired)
|
bool RAM::guest_cas_u32(PhysicalAddress addr, u32 expected, u32& actual, u32 desired)
|
||||||
{
|
{
|
||||||
if (addr + 4 > m_size || (addr & 3))
|
DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_R(4, "RAM::guest_cas_u32");
|
||||||
{
|
|
||||||
fault(addr, 4, "Misaligned/OOB CAS");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 shift = (addr & 4) * 8;
|
u32 shift = (addr & 4) * 8;
|
||||||
u64 mask = u64(0xFFFFFFFFu) << shift;
|
u64 mask = u64(0xFFFFFFFFu) << shift;
|
||||||
|
|
@ -60,45 +79,33 @@ namespace dragon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 RAM::load_u64(PhysycalAddress addr) const
|
u64 RAM::load_u64(PhysicalAddress addr) const
|
||||||
{
|
{
|
||||||
if (addr + 8 > m_size || (addr & 7))
|
DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_R(8, "RAM::load_u64");
|
||||||
{
|
|
||||||
fault(addr, 8, "Memory Fault: ");
|
|
||||||
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(PhysycalAddress addr) const
|
u32 RAM::load_u32(PhysicalAddress addr) const
|
||||||
{
|
{
|
||||||
if (addr + 4 > m_size || (addr & 3))
|
DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_R(4, "RAM::load_u32");
|
||||||
{
|
|
||||||
fault(addr, 4, "Memory Fault: ");
|
|
||||||
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);
|
||||||
u32 shift = (addr & 4) * 8; // 0 or 32
|
u32 shift = (addr & 4) * 8; // 0 or 32
|
||||||
return static_cast<u32>(word >> shift);
|
return static_cast<u32>(word >> shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 RAM::load_u16(PhysycalAddress addr) const
|
u16 RAM::load_u16(PhysicalAddress addr) const
|
||||||
{
|
{
|
||||||
if (addr + 2 > m_size || (addr & 1))
|
DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_R(2, "RAM::load_u16");
|
||||||
{
|
|
||||||
fault(addr, 2, "Memory Fault: ");
|
|
||||||
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);
|
||||||
u32 shift = (addr & 6) * 8; // 0, 16, 32, or 48
|
u32 shift = (addr & 6) * 8; // 0, 16, 32, or 48
|
||||||
return static_cast<u16>(word >> shift);
|
return static_cast<u16>(word >> shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 RAM::load_u8(PhysycalAddress addr) const
|
u8 RAM::load_u8(PhysicalAddress addr) const
|
||||||
{
|
{
|
||||||
if (addr >= m_size)
|
if (addr >= m_size)
|
||||||
{
|
{
|
||||||
fault(addr, 1, "Memory Fault: ");
|
fault(addr, ExceptionCause::LoadBounds, "RAM::load_u8");
|
||||||
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);
|
||||||
|
|
@ -106,24 +113,16 @@ namespace dragon
|
||||||
return static_cast<u8>(word >> shift);
|
return static_cast<u8>(word >> shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RAM::store_u64(PhysycalAddress addr, u64 value)
|
bool RAM::store_u64(PhysicalAddress addr, u64 value)
|
||||||
{
|
{
|
||||||
if (addr + 8 > m_size || (addr & 7))
|
DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_W(8, "RAM::store_u64");
|
||||||
{
|
|
||||||
fault(addr, 8, "Memory Fault: ");
|
|
||||||
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RAM::store_u32(PhysycalAddress addr, u32 value)
|
bool RAM::store_u32(PhysicalAddress addr, u32 value)
|
||||||
{
|
{
|
||||||
if (addr + 4 > m_size || (addr & 3))
|
DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_W(4, "RAM::store_u32");
|
||||||
{
|
|
||||||
fault(addr, 4, "Memory Fault: ");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
u32 shift = (addr & 4) * 8;
|
u32 shift = (addr & 4) * 8;
|
||||||
u64 mask = u64(0xFFFFFFFFu) << shift;
|
u64 mask = u64(0xFFFFFFFFu) << shift;
|
||||||
std::atomic<u64>& word = m_words[addr >> 3];
|
std::atomic<u64>& word = m_words[addr >> 3];
|
||||||
|
|
@ -136,13 +135,9 @@ namespace dragon
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RAM::store_u16(PhysycalAddress addr, u16 value)
|
bool RAM::store_u16(PhysicalAddress addr, u16 value)
|
||||||
{
|
{
|
||||||
if (addr + 2 > m_size || (addr & 1))
|
DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_W(2, "RAM::store_u16");
|
||||||
{
|
|
||||||
fault(addr, 2, "Memory Fault: ");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
u32 shift = (addr & 6) * 8;
|
u32 shift = (addr & 6) * 8;
|
||||||
u64 mask = u64(0xFFFFu) << shift;
|
u64 mask = u64(0xFFFFu) << shift;
|
||||||
std::atomic<u64>& word = m_words[addr >> 3];
|
std::atomic<u64>& word = m_words[addr >> 3];
|
||||||
|
|
@ -155,11 +150,11 @@ namespace dragon
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RAM::store_u8(PhysycalAddress addr, u8 value)
|
bool RAM::store_u8(PhysicalAddress addr, u8 value)
|
||||||
{
|
{
|
||||||
if (addr >= m_size)
|
if (addr >= m_size)
|
||||||
{
|
{
|
||||||
fault(addr, 1, "Memory Fault: ");
|
fault(addr, ExceptionCause::StoreBounds, "RAM::store_u8");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
u32 shift = (addr & 7) * 8;
|
u32 shift = (addr & 7) * 8;
|
||||||
|
|
@ -173,11 +168,5 @@ namespace dragon
|
||||||
} 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RAM::fault(PhysycalAddress addr, u32 size, const String& msg) const
|
|
||||||
{
|
|
||||||
std::cout << msg << ostd::String::getHexStr(addr, true, 4) << "\n";
|
|
||||||
/* TODO: raise guest trap */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,21 +33,19 @@ namespace dragon
|
||||||
public:
|
public:
|
||||||
explicit RAM(u64 size) : AtomicStorageMemoryDevice(size) { }
|
explicit RAM(u64 size) : AtomicStorageMemoryDevice(size) { }
|
||||||
|
|
||||||
bool guest_cas_u32(PhysycalAddress addr, u32 expected, u32& actual, u32 desired) override;
|
bool guest_cas_u32(PhysicalAddress addr, u32 expected, u32& actual, u32 desired) override;
|
||||||
|
|
||||||
// ---- Aligned loads ----
|
// ---- Aligned loads ----
|
||||||
u64 load_u64(PhysycalAddress addr) const override;
|
u64 load_u64(PhysicalAddress addr) const override;
|
||||||
u32 load_u32(PhysycalAddress addr) const override;
|
u32 load_u32(PhysicalAddress addr) const override;
|
||||||
u16 load_u16(PhysycalAddress addr) const override;
|
u16 load_u16(PhysicalAddress addr) const override;
|
||||||
u8 load_u8 (PhysycalAddress addr) const override;
|
u8 load_u8 (PhysicalAddress addr) const override;
|
||||||
|
|
||||||
// ---- Aligned stores ----
|
// ---- Aligned stores ----
|
||||||
bool store_u64(PhysycalAddress addr, u64 value) override;
|
bool store_u64(PhysicalAddress addr, u64 value) override;
|
||||||
bool store_u32(PhysycalAddress addr, u32 value) override;
|
bool store_u32(PhysicalAddress addr, u32 value) override;
|
||||||
bool store_u16(PhysycalAddress addr, u16 value) override;
|
bool store_u16(PhysicalAddress addr, u16 value) override;
|
||||||
bool store_u8 (PhysycalAddress addr, u8 value) override;
|
bool store_u8 (PhysicalAddress addr, u8 value) override;
|
||||||
|
|
||||||
[[noreturn]] void fault(PhysycalAddress addr, u32 size, const String& msg = "") const override;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue