diff --git a/Core/Debugger/WebSocket/MemorySubscriber.cpp b/Core/Debugger/WebSocket/MemorySubscriber.cpp index cbbca543ac..f3cd3ac2bf 100644 --- a/Core/Debugger/WebSocket/MemorySubscriber.cpp +++ b/Core/Debugger/WebSocket/MemorySubscriber.cpp @@ -124,7 +124,7 @@ void WebSocketMemoryReadU8(DebuggerRequest &req) { Core_RunOnCPUThread([&] { AutoDisabledReplacements memLock = LockMemory(true); JsonWriter &json = req.Respond(); - json.writeUint("value", Memory::Read_U8(addr)); + json.writeUint("value", Memory::ReadUnchecked_U8(addr)); }); } @@ -145,7 +145,7 @@ void WebSocketMemoryReadU16(DebuggerRequest &req) { return req.Fail("CPU not started"); // This only depends on addr, not on anything CPU-thread-owned, so fail fast here rather than // making a round trip through the queue for a request we already know is invalid. - if (!Memory::IsValidAddress(addr)) + if (!Memory::IsValidRange(addr, 2)) return req.Fail("Invalid address"); // Route the actual memory read to the CPU thread instead of poking at it directly @@ -153,7 +153,7 @@ void WebSocketMemoryReadU16(DebuggerRequest &req) { Core_RunOnCPUThread([&] { AutoDisabledReplacements memLock = LockMemory(true); JsonWriter &json = req.Respond(); - json.writeUint("value", Memory::Read_U16(addr)); + json.writeUint("value", Memory::ReadUnchecked_U16(addr)); }); } @@ -174,7 +174,7 @@ void WebSocketMemoryReadU32(DebuggerRequest &req) { return req.Fail("CPU not started"); // This only depends on addr, not on anything CPU-thread-owned, so fail fast here rather than // making a round trip through the queue for a request we already know is invalid. - if (!Memory::IsValidAddress(addr)) + if (!Memory::IsValidRange(addr, 4)) return req.Fail("Invalid address"); // Route the actual memory read to the CPU thread instead of poking at it directly diff --git a/Core/MIPS/MIPSInt.cpp b/Core/MIPS/MIPSInt.cpp index 1a74bd8d69..9ca2a1be4b 100644 --- a/Core/MIPS/MIPSInt.cpp +++ b/Core/MIPS/MIPSInt.cpp @@ -293,8 +293,7 @@ namespace MIPSInt void Int_JumpRegType(MIPSOpcode op) { - if (mipsr4k.inDelaySlot) - { + if (mipsr4k.inDelaySlot) { // There's one of these in Star Soldier at 0881808c, which seems benign. ERROR_LOG(Log::CPU, "Jump in delay slot :("); } @@ -318,8 +317,7 @@ namespace MIPSInt } } - void Int_IType(MIPSOpcode op) - { + void Int_IType(MIPSOpcode op) { u32 uimm = op & 0xFFFF; u32 suimm = SignExtend16ToU32(op); s32 simm = SignExtend16ToS32(op); @@ -349,9 +347,8 @@ namespace MIPSInt PC += 4; } - void Int_StoreSync(MIPSOpcode op) - { - int imm = (signed short)(op&0xFFFF); + void Int_StoreSync(MIPSOpcode op) { + int imm = (signed short)(op & 0xFFFF); int rt = _RT; int rs = _RS; u32 addr = R(rs) + imm; @@ -360,12 +357,20 @@ namespace MIPSInt { case 48: // ll if (rt != 0) { + if (!Memory::IsValid4AlignedAddress(addr)) { + Core_MemoryException(addr, 4, PC, MemoryExceptionType::READ_WORD, "ll"); + return; + } R(rt) = Memory::Read_U32(addr); } currentMIPS->llBit = 1; break; case 56: // sc if (currentMIPS->llBit) { + if (!Memory::IsValid4AlignedAddress(addr)) { + Core_MemoryException(addr, 4, PC, MemoryExceptionType::WRITE_WORD, "sc"); + return; + } Memory::Write_U32(R(rt), addr); if (rt != 0) { R(rt) = 1; @@ -382,21 +387,18 @@ namespace MIPSInt } - void Int_RType3(MIPSOpcode op) - { + void Int_RType3(MIPSOpcode op) { int rt = _RT; int rs = _RS; int rd = _RD; // Don't change $zr. - if (rd == 0) - { + if (rd == 0) { PC += 4; return; } - switch (op & 63) - { + switch (op & 63) { case 10: if (R(rt) == 0) R(rd) = R(rs); break; //movz case 11: if (R(rt) != 0) R(rd) = R(rs); break; //movn case 32: R(rd) = R(rs) + R(rt); break; //add (exception on overflow) @@ -419,12 +421,11 @@ namespace MIPSInt } - void Int_ITypeMem(MIPSOpcode op) - { + void Int_ITypeMem(MIPSOpcode op) { int imm = (signed short)(op&0xFFFF); int rt = _RT; int rs = _RS; - u32 addr = R(rs) + imm; + const u32 addr = R(rs) + imm; if (((op >> 29) & 1) == 0 && rt == 0) { // Don't load anything into $zr @@ -432,23 +433,75 @@ namespace MIPSInt return; } - switch (op >> 26) - { - case 32: R(rt) = SignExtend8ToU32(Memory::Read_U8(addr)); break; //lb - case 33: R(rt) = SignExtend16ToU32(Memory::Read_U16(addr)); break; //lh - case 35: R(rt) = Memory::Read_U32(addr); break; //lw - case 36: R(rt) = Memory::Read_U8 (addr); break; //lbu - case 37: R(rt) = Memory::Read_U16(addr); break; //lhu - case 40: Memory::Write_U8(R(rt), addr); break; //sb - case 41: Memory::Write_U16(R(rt), addr); break; //sh - case 43: Memory::Write_U32(R(rt), addr); break; //sw + switch (op >> 26) { + case 32: + if (!Memory::IsValidAddress(addr)) { + Core_MemoryException(addr, 1, PC, MemoryExceptionType::READ_WORD, "lb"); + return; + } + R(rt) = SignExtend8ToU32(Memory::ReadUnchecked_U8(addr)); + break; //lb + case 33: + if (!Memory::IsValid2AlignedAddress(addr)) { + Core_MemoryException(addr, 2, PC, MemoryExceptionType::READ_WORD, "lh"); + return; + } + R(rt) = SignExtend16ToU32(Memory::ReadUnchecked_U16(addr)); + break; //lh + case 35: + if (!Memory::IsValid4AlignedAddress(addr)) { + Core_MemoryException(addr, 4, PC, MemoryExceptionType::READ_WORD, "lw"); + return; + } + R(rt) = Memory::ReadUnchecked_U32(addr); + break; //lw + case 36: + if (!Memory::IsValidAddress(addr)) { + Core_MemoryException(addr, 1, PC, MemoryExceptionType::READ_WORD, "lbu"); + return; + } + R(rt) = Memory::Read_U8 (addr); + break; //lbu + case 37: + if (!Memory::IsValid2AlignedAddress(addr)) { + Core_MemoryException(addr, 2, PC, MemoryExceptionType::READ_WORD, "lhu"); + return; + } + R(rt) = Memory::Read_U16(addr); + break; //lhu + case 40: + if (!Memory::IsValidAddress(addr)) { + Core_MemoryException(addr, 1, PC, MemoryExceptionType::WRITE_WORD, "sb"); + return; + } + Memory::Write_U8(R(rt), addr); + break; //sb + case 41: + if (!Memory::IsValid2AlignedAddress(addr)) { + Core_MemoryException(addr, 2, PC, MemoryExceptionType::WRITE_WORD, "sh"); + return; + } + Memory::Write_U16(R(rt), addr); + break; //sh + case 43: + if (!Memory::IsValid4AlignedAddress(addr)) { + Core_MemoryException(addr, 4, PC, MemoryExceptionType::WRITE_WORD, "sw"); + return; + } + Memory::Write_U32(R(rt), addr); + break; //sw // When there's an LWL and an LWR together, we should be able to peephole optimize that // into a single non-alignment-checking LW. case 34: //lwl { + // Not checking for alignment here - the actual read will be aligned. + if (!Memory::IsValidAddress(addr)) { + Core_MemoryException(addr, 4, PC, MemoryExceptionType::READ_WORD, "lwl"); + return; + } u32 shift = (addr & 3) * 8; - u32 mem = Memory::Read_U32(addr & 0xfffffffc); + u32 mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc); u32 result = ( u32(R(rt)) & (0x00ffffff >> shift) ) | ( mem << (24 - shift) ); R(rt) = result; } @@ -456,8 +509,13 @@ namespace MIPSInt case 38: //lwr { + // Not checking for alignment here - the actual read will be aligned. + if (!Memory::IsValidAddress(addr)) { + Core_MemoryException(addr, 4, PC, MemoryExceptionType::READ_WORD, "lwr"); + return; + } u32 shift = (addr & 3) * 8; - u32 mem = Memory::Read_U32(addr & 0xfffffffc); + u32 mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc); u32 regval = R(rt); u32 result = ( regval & (0xffffff00 << (24 - shift)) ) | ( mem >> shift ); R(rt) = result; @@ -466,19 +524,29 @@ namespace MIPSInt case 42: //swl { + // Not checking for alignment here - the actual read/write will be aligned. + if (!Memory::IsValidAddress(addr)) { + Core_MemoryException(addr, 4, PC, MemoryExceptionType::WRITE_WORD, "swl"); + return; + } u32 shift = (addr & 3) * 8; - u32 mem = Memory::Read_U32(addr & 0xfffffffc); + u32 mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc); u32 result = ( ( u32(R(rt)) >> (24 - shift) ) ) | ( mem & (0xffffff00 << shift) ); - Memory::Write_U32(result, (addr & 0xfffffffc)); + Memory::WriteUnchecked_U32(result, (addr & 0xfffffffc)); } break; case 46: //swr { + // Not checking for alignment here - the actual read/write will be aligned. + if (!Memory::IsValidAddress(addr)) { + Core_MemoryException(addr, 4, PC, MemoryExceptionType::WRITE_WORD, "swr"); + return; + } u32 shift = (addr & 3) << 3; - u32 mem = Memory::Read_U32(addr & 0xfffffffc); + u32 mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc); u32 result = ( ( u32(R(rt)) << shift ) | (mem & (0x00ffffff >> (24 - shift)) ) ); - Memory::Write_U32(result, (addr & 0xfffffffc)); + Memory::WriteUnchecked_U32(result, (addr & 0xfffffffc)); } break; @@ -489,17 +557,27 @@ namespace MIPSInt PC += 4; } - void Int_FPULS(MIPSOpcode op) - { - s32 offset = (s16)(op&0xFFFF); + void Int_FPULS(MIPSOpcode op) { + s32 offset = (s16)(op & 0xFFFF); int ft = _FT; int rs = _RS; u32 addr = R(rs) + offset; - switch(op >> 26) - { - case 49: FI(ft) = Memory::Read_U32(addr); break; //lwc1 - case 57: Memory::Write_U32(FI(ft), addr); break; //swc1 + switch (op >> 26) { + case 49: + if (!Memory::IsValid4AlignedAddress(addr)) { + Core_MemoryException(addr, 4, PC, MemoryExceptionType::READ_WORD, "lwc1"); + return; + } + FI(ft) = Memory::Read_U32(addr); + break; //lwc1 + case 57: + if (!Memory::IsValid4AlignedAddress(addr)) { + Core_MemoryException(addr, 4, PC, MemoryExceptionType::WRITE_WORD, "swc1"); + return; + } + Memory::Write_U32(FI(ft), addr); + break; //swc1 default: _dbg_assert_msg_(false,"Trying to interpret FPULS instruction that can't be interpreted"); break; diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index f0ae68a799..69cd0bfcd2 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -200,8 +200,7 @@ namespace MIPSInt PC += 4; } - void Int_SVQ(MIPSOpcode op) - { + void Int_SVQ(MIPSOpcode op) { int imm = SignExtend16ToS32(op & 0xFFFC); int rs = _RS; int vt = (((op >> 16) & 0x1f)) | ((op&1) << 5); @@ -210,31 +209,29 @@ namespace MIPSInt float *f; const float *cf; - switch (op >> 26) - { + switch (op >> 26) { case 53: //lvl.q/lvr.q { - if (addr & 0x3) - { - _dbg_assert_msg_( 0, "Misaligned lvX.q at %08x (pc = %08x)", addr, PC); - } float d[4]; ReadVector(d, V_Quad, vt); int offset = (addr >> 2) & 3; - if ((op & 2) == 0) - { + if ((op & 2) == 0) { + if (!Memory::IsValid4AlignedAddress(addr)) { + Core_MemoryException(addr, 16, PC, MemoryExceptionType::READ_WORD, "lvl.q"); + return; + } // It's an LVL - for (int i = 0; i < offset + 1; i++) - { - d[3 - i] = Memory::Read_Float(addr - 4 * i); + for (int i = 0; i < offset + 1; i++) { + d[3 - i] = Memory::ReadUnchecked_Float(addr - 4 * i); + } + } else { + if (!Memory::IsValid4AlignedAddress(addr)) { + Core_MemoryException(addr, 16, PC, MemoryExceptionType::READ_WORD, "lvr.q"); + return; } - } - else - { // It's an LVR - for (int i = 0; i < (3 - offset) + 1; i++) - { - d[i] = Memory::Read_Float(addr + 4 * i); + for (int i = 0; i < (3 - offset) + 1; i++) { + d[i] = Memory::ReadUnchecked_Float(addr + 4 * i); } } WriteVector(d, V_Quad, vt); @@ -242,21 +239,21 @@ namespace MIPSInt break; case 54: //lv.q - if (addr & 0xF) - { - _dbg_assert_msg_( 0, "Misaligned lv.q at %08x (pc = %08x)", addr, PC); + if ((addr & 0xF) || !Memory::IsValid4AlignedAddress(addr)) { + Core_MemoryException(addr, 16, PC, MemoryExceptionType::READ_WORD, "lv.q"); } + #ifndef COMMON_BIG_ENDIAN - cf = reinterpret_cast(Memory::GetPointerRange(addr, 16)); + cf = reinterpret_cast(Memory::GetPointerUnchecked(addr)); if (cf) WriteVector(cf, V_Quad, vt); #else float lvqd[4]; - lvqd[0] = Memory::Read_Float(addr); - lvqd[1] = Memory::Read_Float(addr + 4); - lvqd[2] = Memory::Read_Float(addr + 8); - lvqd[3] = Memory::Read_Float(addr + 12); + lvqd[0] = Memory::ReadUnchecked_Float(addr); + lvqd[1] = Memory::ReadUnchecked_Float(addr + 4); + lvqd[2] = Memory::ReadUnchecked_Float(addr + 8); + lvqd[3] = Memory::ReadUnchecked_Float(addr + 12); WriteVector(lvqd, V_Quad, vt); #endif @@ -264,49 +261,49 @@ namespace MIPSInt case 61: // svl.q/svr.q { - if (addr & 0x3) - { - _dbg_assert_msg_( 0, "Misaligned svX.q at %08x (pc = %08x)", addr, PC); - } float d[4]; ReadVector(d, V_Quad, vt); int offset = (addr >> 2) & 3; - if ((op&2) == 0) - { + if ((op & 2) == 0) { + if (!Memory::IsValid4AlignedAddress(addr)) { + Core_MemoryException(addr, 16, PC, MemoryExceptionType::WRITE_WORD, "svl.q"); + return; + } // It's an SVL for (int i = 0; i < offset + 1; i++) { - Memory::Write_Float(d[3 - i], addr - i * 4); + Memory::WriteUnchecked_Float(d[3 - i], addr - i * 4); + } + } else { + if (!Memory::IsValid4AlignedAddress(addr)) { + Core_MemoryException(addr, 16, PC, MemoryExceptionType::WRITE_WORD, "svr.q"); + return; } - } - else - { // It's an SVR - for (int i = 0; i < (3 - offset) + 1; i++) - { - Memory::Write_Float(d[i], addr + 4 * i); + for (int i = 0; i < (3 - offset) + 1; i++) { + Memory::WriteUnchecked_Float(d[i], addr + 4 * i); } } break; } case 62: //sv.q - if (addr & 0xF) - { - _dbg_assert_msg_( 0, "Misaligned sv.q at %08x (pc = %08x)", addr, PC); + if ((addr & 0xF) || !Memory::IsValid4AlignedAddress(addr)) { + Core_MemoryException(addr, 16, PC, MemoryExceptionType::WRITE_WORD, "sv.q"); } #ifndef COMMON_BIG_ENDIAN - f = reinterpret_cast(Memory::GetPointerWriteRange(addr, 16)); - if (f) + f = reinterpret_cast(Memory::GetPointerWriteUnchecked(addr)); + if (f) { ReadVector(f, V_Quad, vt); + } #else float svqd[4]; ReadVector(svqd, V_Quad, vt); - Memory::Write_Float(svqd[0], addr); - Memory::Write_Float(svqd[1], addr + 4); - Memory::Write_Float(svqd[2], addr + 8); - Memory::Write_Float(svqd[3], addr + 12); + Memory::WriteUnchecked_Float(svqd[0], addr); + Memory::WriteUnchecked_Float(svqd[1], addr + 4); + Memory::WriteUnchecked_Float(svqd[2], addr + 8); + Memory::WriteUnchecked_Float(svqd[3], addr + 12); #endif break; diff --git a/Core/MemMap.h b/Core/MemMap.h index 8919ff08a0..623f9a1b1c 100644 --- a/Core/MemMap.h +++ b/Core/MemMap.h @@ -238,14 +238,6 @@ inline void WriteUnchecked_U8(u8 data, u32 address) { #endif } -inline float Read_Float(u32 address) -{ - u32 ifloat = Read_U32(address); - float f; - memcpy(&f, &ifloat, sizeof(float)); - return f; -} - // used by JIT. Return zero-extended 32bit values u32 Read_U8_ZX(const u32 address); u32 Read_U16_ZX(const u32 address); @@ -325,6 +317,20 @@ inline bool IsValidAddress(const u32 address) { } } +inline bool IsValid2AlignedAddress(const u32 address) { + if ((address & 0x3E000001) == 0x08000000) { + return true; + } else if ((address & 0x3F800001) == 0x04000000) { + return address < 0x80000000; // Let's disallow kernel-flagged VRAM. We don't have it mapped and I am not sure if it's accessible. + } else if ((address & 0xBFFFC001) == 0x00010000) { + return true; + } else if ((address & 0x3F000000) >= 0x08000000 && (address & 0x3F000000) < 0x08000000 + g_MemorySize) { + return (address & 1) == 0; + } else { + return false; + } +} + inline bool IsValid4AlignedAddress(const u32 address) { if ((address & 0x3E000003) == 0x08000000) { return true; diff --git a/UI/ImDebugger/ImMemView.cpp b/UI/ImDebugger/ImMemView.cpp index 12b3a22843..ce182b664d 100644 --- a/UI/ImDebugger/ImMemView.cpp +++ b/UI/ImDebugger/ImMemView.cpp @@ -516,7 +516,7 @@ void ImMemView::PopupMenu() { if (ImGui::MenuItem("Copy value (float32)")) { char temp[64]; - snprintf(temp, sizeof(temp), "%f", Memory::IsValidAddress(curAddress_) ? Memory::Read_Float(curAddress_) : NAN); + snprintf(temp, sizeof(temp), "%f", Memory::IsValid4AlignedAddress(curAddress_) ? Memory::ReadUnchecked_Float(curAddress_) : NAN); System_CopyStringToClipboard(temp); } /* diff --git a/Windows/Debugger/CtrlMemView.cpp b/Windows/Debugger/CtrlMemView.cpp index c2bce68e93..e249710f51 100644 --- a/Windows/Debugger/CtrlMemView.cpp +++ b/Windows/Debugger/CtrlMemView.cpp @@ -597,7 +597,7 @@ void CtrlMemView::onMouseUp(WPARAM wParam, LPARAM lParam, int button) { { auto memLock = Memory::Lock(); std::ostringstream stream; - stream << (Memory::IsValidAddress(curAddress_) ? Memory::Read_Float(curAddress_) : NAN); + stream << (Memory::IsValid4AlignedAddress(curAddress_) ? Memory::ReadUnchecked_Float(curAddress_) : NAN); auto temp_string = stream.str(); W32Util::CopyTextToClipboard(wnd, temp_string); }