diff --git a/src/backend/MemoryRegions.hpp b/src/backend/MemoryRegions.hpp index 6be034bb..25f09e07 100644 --- a/src/backend/MemoryRegions.hpp +++ b/src/backend/MemoryRegions.hpp @@ -58,6 +58,38 @@ #define PIF_ROM_REGION PIF_ROM_REGION_START ... PIF_ROM_REGION_END #define PIF_RAM_REGION PIF_RAM_REGION_START ... PIF_RAM_REGION_END +#define START_VREGION_KUSEG 0x00000000 +#define START_VREGION_KSEG0 0x80000000 +#define START_VREGION_KSEG1 0xA0000000 +#define START_VREGION_KSSEG 0xC0000000 +#define START_VREGION_KSEG3 0xE0000000 + +#define END_VREGION_KUSEG 0x7FFFFFFF +#define END_VREGION_KSEG0 0x9FFFFFFF +#define END_VREGION_KSEG1 0xBFFFFFFF +#define END_VREGION_KSSEG 0xDFFFFFFF +#define END_VREGION_KSEG3 0xFFFFFFFF + +#define VREGION_KUSEG START_VREGION_KUSEG ... END_VREGION_KUSEG +#define VREGION_KSEG0 START_VREGION_KSEG0 ... END_VREGION_KSEG0 +#define VREGION_KSEG1 START_VREGION_KSEG1 ... END_VREGION_KSEG1 +#define VREGION_KSSEG START_VREGION_KSSEG ... END_VREGION_KSSEG +#define VREGION_KSEG3 START_VREGION_KSEG3 ... END_VREGION_KSEG3 + +#define DIRECT_MAP_MASK 0x1FFFFFFF + +#define VREGION_XKUSEG 0x0000000000000000 ... 0x000000FFFFFFFFFF +#define VREGION_XBAD1 0x0000010000000000 ... 0x3FFFFFFFFFFFFFFF +#define VREGION_XKSSEG 0x4000000000000000 ... 0x400000FFFFFFFFFF +#define VREGION_XBAD2 0x4000010000000000 ... 0x7FFFFFFFFFFFFFFF +#define VREGION_XKPHYS 0x8000000000000000 ... 0xBFFFFFFFFFFFFFFF +#define VREGION_XKSEG 0xC000000000000000 ... 0xC00000FF7FFFFFFF +#define VREGION_XBAD3 0xC00000FF80000000 ... 0xFFFFFFFF7FFFFFFF +#define VREGION_CKSEG0 0xFFFFFFFF80000000 ... 0xFFFFFFFF9FFFFFFF +#define VREGION_CKSEG1 0xFFFFFFFFA0000000 ... 0xFFFFFFFFBFFFFFFF +#define VREGION_CKSSEG 0xFFFFFFFFC0000000 ... 0xFFFFFFFFDFFFFFFF +#define VREGION_CKSEG3 0xFFFFFFFFE0000000 ... 0xFFFFFFFFFFFFFFFF + constexpr u64 operator""_kb(unsigned long long int x) { return 1024ULL * x; } diff --git a/src/backend/core/registers/Cop0.cpp b/src/backend/core/registers/Cop0.cpp index f8320610..6c12d01d 100644 --- a/src/backend/core/registers/Cop0.cpp +++ b/src/backend/core/registers/Cop0.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -107,6 +106,7 @@ void Cop0::SetReg32(u8 addr, u32 value) { case COP0_REG_STATUS: status.raw &= ~STATUS_MASK; status.raw |= (value & STATUS_MASK); + Update(); break; case COP0_REG_CAUSE: { Cop0Cause tmp{}; @@ -294,6 +294,8 @@ void FireException(Registers& regs, ExceptionCode code, int cop, bool useOldPC) default: Util::panic("Unhandled exception! {}", static_cast(code)); } } + + regs.cop0.Update(); } void HandleTLBException(Registers& regs, u64 vaddr) { @@ -362,4 +364,116 @@ void Cop0::decodeInterp(Registers& regs, u32 instr) { default: Util::panic("Unimplemented COP0 instruction {} {}", mask_cop >> 4, mask_cop & 7); } } + +bool MapVAddr(Registers& regs, TLBAccessType accessType, u64 vaddr, u32& paddr) { + if(regs.cop0.is_64bit_addressing) [[unlikely]] { + if (regs.cop0.kernel_mode) [[likely]] { + return MapVAddr64(regs, accessType, vaddr, paddr); + } else if (regs.cop0.user_mode) { + return UserMapVAddr64(regs, accessType, vaddr, paddr); + } else if (regs.cop0.supervisor_mode) { + Util::panic("Supervisor mode memory access, 64 bit mode"); + } else { + Util::panic("Unknown mode! This should never happen!"); + } + } else { + if (regs.cop0.kernel_mode) [[likely]] { + return MapVAddr32(regs, accessType, vaddr, paddr); + } else if (regs.cop0.user_mode) { + return UserMapVAddr32(regs, accessType, vaddr, paddr); + } else if (regs.cop0.supervisor_mode) { + Util::panic("Supervisor mode memory access, 32 bit mode"); + } else { + Util::panic("Unknown mode! This should never happen!"); + } + } +} + +bool UserMapVAddr32(Registers& regs, TLBAccessType accessType, u64 vaddr, u32& paddr) { + switch (vaddr) { + case VREGION_KUSEG: + return ProbeTLB(regs, accessType, s64(s32(vaddr)), paddr, nullptr); + default: + regs.cop0.tlbError = DISALLOWED_ADDRESS; + return false; + } +} + +bool MapVAddr32(Registers& regs, TLBAccessType accessType, u64 vaddr, u32& paddr) { + switch((u32(vaddr) >> 29) & 7) { + case 0 ... 3: case 7: + return ProbeTLB(regs, accessType, s64(s32(vaddr)), paddr, nullptr); + case 4 ... 5: + paddr = vaddr & 0x1FFFFFFF; + return true; + case 6: Util::panic("Unimplemented virtual mapping in KSSEG! ({:08X})", vaddr); + default: + Util::panic("Should never end up in default case in map_vaddr! ({:08X})", vaddr); + } + + return false; +} + +bool UserMapVAddr64(Registers& regs, TLBAccessType accessType, u64 vaddr, u32& paddr) { + switch (vaddr) { + case VREGION_XKUSEG: + return ProbeTLB(regs, accessType, vaddr, paddr, nullptr); + default: + regs.cop0.tlbError = DISALLOWED_ADDRESS; + return false; + } +} + +bool MapVAddr64(Registers& regs, TLBAccessType accessType, u64 vaddr, u32& paddr) { + switch (vaddr) { + case VREGION_XKUSEG: case VREGION_XKSSEG: + return ProbeTLB(regs, accessType, vaddr, paddr, nullptr); + case VREGION_XKPHYS: { + if (!regs.cop0.kernel_mode) { + Util::panic("Access to XKPHYS address 0x{:016X} when outside kernel mode!", vaddr); + } + u8 high_two_bits = (vaddr >> 62) & 0b11; + if (high_two_bits != 0b10) { + Util::panic("Access to XKPHYS address 0x{:016X} with high two bits != 0b10!", vaddr); + } + u8 subsegment = (vaddr >> 59) & 0b11; + bool cached = subsegment != 2; // do something with this eventually + // If any bits in the range of 58:32 are set, the address is invalid. + bool valid = (vaddr & 0x07FFFFFF00000000) == 0; + if (!valid) { + regs.cop0.tlbError = DISALLOWED_ADDRESS; + return false; + } + paddr = vaddr & 0xFFFFFFFF; + return true; + } + case VREGION_XKSEG: + return ProbeTLB(regs, accessType, vaddr, paddr, nullptr); + case VREGION_CKSEG0: + // Identical to kseg0 in 32 bit mode. + // Unmapped translation. Subtract the base address of the space to get the physical address. + paddr = vaddr - START_VREGION_KSEG0; // Implies cutting off the high 32 bits + Util::trace("CKSEG0: Translated 0x{:016X} to 0x{:08X}", vaddr, paddr); + return true; + case VREGION_CKSEG1: + // Identical to kseg1 in 32 bit mode. + // Unmapped translation. Subtract the base address of the space to get the physical address. + paddr = vaddr - START_VREGION_KSEG1; // Implies cutting off the high 32 bits + Util::trace("KSEG1: Translated 0x{:016X} to 0x{:08X}", vaddr, paddr); + return true; + case VREGION_CKSSEG: + Util::panic("Resolving virtual address 0x{:016X} (VREGION_CKSSEG) in 64 bit mode", vaddr); + case VREGION_CKSEG3: + return ProbeTLB(regs, accessType, vaddr, paddr, nullptr); + case VREGION_XBAD1: + case VREGION_XBAD2: + case VREGION_XBAD3: + regs.cop0.tlbError = DISALLOWED_ADDRESS; + return false; + default: + Util::panic("Resolving virtual address 0x{:016X} in 64 bit mode", vaddr); + } + + return false; +} } \ No newline at end of file diff --git a/src/backend/core/registers/Cop0.hpp b/src/backend/core/registers/Cop0.hpp index 117f68cc..aeea19e0 100644 --- a/src/backend/core/registers/Cop0.hpp +++ b/src/backend/core/registers/Cop0.hpp @@ -221,10 +221,10 @@ struct Cop0 { void Reset(); - bool kernel_mode{}; - bool supervisor_mode{}; - bool user_mode{}; - bool is_64bit_addressing{}; + bool kernel_mode{true}; + bool supervisor_mode{false}; + bool user_mode{false}; + bool is_64bit_addressing{false}; bool llbit{}; PageMask pageMask{}; @@ -263,6 +263,18 @@ struct Cop0 { val = (val % upper) + lower; return val; } + + FORCE_INLINE void Update() { + bool exception = status.exl || status.erl; + + kernel_mode = exception || status.ksu == 0; + supervisor_mode = !exception && status.ksu == 1; + user_mode = !exception && status.ksu == 2; + is_64bit_addressing = + (kernel_mode && status.kx) + || (supervisor_mode && status.sx) + || (user_mode && status.ux); + } private: FORCE_INLINE u32 GetWired() { return wired & 0x3F; } FORCE_INLINE u32 GetCount() { return u32(u64(count >> 1)); } @@ -289,20 +301,11 @@ enum TLBAccessType { bool ProbeTLB(Registers& regs, TLBAccessType access_type, u64 vaddr, u32& paddr, int* match); -static FORCE_INLINE bool MapVAddr(Registers& regs, TLBAccessType accessType, u64 vaddr, u32& paddr) { - switch(u32(vaddr) >> 29) { - case 0 ... 3: case 7: - return ProbeTLB(regs, accessType, vaddr, paddr, nullptr); - case 4 ... 5: - paddr = vaddr & 0x1FFFFFFF; - return true; - case 6: Util::panic("Unimplemented virtual mapping in KSSEG! ({:08X})", vaddr); - default: - Util::panic("Should never end up in default case in map_vaddr! ({:08X})", vaddr); - } - - return false; -} +bool MapVAddr(Registers& regs, TLBAccessType accessType, u64 vaddr, u32& paddr); +bool UserMapVAddr32(Registers& regs, TLBAccessType accessType, u64 vaddr, u32& paddr); +bool MapVAddr32(Registers& regs, TLBAccessType accessType, u64 vaddr, u32& paddr); +bool UserMapVAddr64(Registers& regs, TLBAccessType accessType, u64 vaddr, u32& paddr); +bool MapVAddr64(Registers& regs, TLBAccessType accessType, u64 vaddr, u32& paddr); TLBEntry* TLBTryMatch(Registers& regs, u64 vaddr, int* match); void HandleTLBException(Registers& regs, u64 vaddr);