diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index 60c5182a8e..f90accc485 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -530,10 +530,12 @@ bool get_x64_reg_value(x64_context* context, x64_reg_t reg, size_t d_size, size_ else if (reg - X64R_AL < 4 && d_size == 1) { out_value = (u8)(*X64REG(context, reg - X64R_AL)); + return true; } else if (reg - X64R_AH < 4 && d_size == 1) { out_value = (u8)(*X64REG(context, reg - X64R_AH) >> 8); + return true; } else if (reg == X64_IMM32) { @@ -549,6 +551,7 @@ bool get_x64_reg_value(x64_context* context, x64_reg_t reg, size_t d_size, size_ else if (reg == X64R_ECX) { out_value = (u32)RCX(context); + return true; } LOG_ERROR(GENERAL, "get_x64_reg_value(): invalid arguments (reg=%d, d_size=%lld, i_size=%lld)", reg, d_size, i_size); @@ -571,7 +574,7 @@ bool put_x64_reg_value(x64_context* context, x64_reg_t reg, size_t d_size, u64 v void fix_x64_reg_op(x64_context* context, x64_op_t& op, x64_reg_t& reg, size_t& d_size, size_t& i_size) { - if (op == X64OP_MOVS && reg != X64_NOT_SET) + if (op == X64OP_MOVS && reg != X64_NOT_SET) // get "full" access size from RCX register { u64 counter; if (!get_x64_reg_value(context, reg, 8, i_size, counter)) @@ -584,6 +587,8 @@ void fix_x64_reg_op(x64_context* context, x64_op_t& op, x64_reg_t& reg, size_t& } d_size *= counter; + reg = X64_NOT_SET; + return; } } @@ -600,6 +605,29 @@ bool handle_access_violation(const u32 addr, bool is_writing, x64_context* conte decode_x64_reg_op(code, op, reg, d_size, i_size); fix_x64_reg_op(context, op, reg, d_size, i_size); + if (d_size + addr >= 0x100000000ull) + { + LOG_ERROR(GENERAL, "Invalid d_size (0x%llx)", d_size); + return false; + } + + if (op == X64OP_CMPXCHG) + { + // detect whether this instruction can't actually modify memory to avoid breaking reservation; + // this may theoretically cause endless loop, but it shouldn't be a problem if only read_sync() generates such instruction + u64 cmp, exch; + if (!get_x64_reg_value(context, reg, d_size, i_size, cmp) || !get_x64_reg_value(context, X64R_RAX, d_size, i_size, exch)) + { + return false; + } + + if (cmp == exch) + { + // could also be emulated without attempt to write memory + is_writing = false; + } + } + // check if address is RawSPU MMIO register if (addr - RAW_SPU_BASE_ADDR < (6 * RAW_SPU_OFFSET) && (addr % RAW_SPU_OFFSET) >= RAW_SPU_PROB_OFFSET) { @@ -645,7 +673,7 @@ bool handle_access_violation(const u32 addr, bool is_writing, x64_context* conte } // check if fault is caused by reservation - if (vm::reservation_query(addr, is_writing)) + if (vm::reservation_query(addr, (u32)d_size, is_writing)) { return true; } diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 963aa5ebf9..b0785cfd16 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -203,7 +203,10 @@ void SPUThread::WriteSNR(bool number, u32 value) void SPUThread::ProcessCmd(u32 cmd, u32 tag, u32 lsa, u64 ea, u32 size) { - if (cmd & (MFC_BARRIER_MASK | MFC_FENCE_MASK)) _mm_mfence(); + if (cmd & (MFC_BARRIER_MASK | MFC_FENCE_MASK)) + { + _mm_mfence(); + } u32 eal = vm::cast(ea, "ea"); @@ -298,7 +301,7 @@ void SPUThread::ListCmd(u32 lsa, u64 ea, u16 tag, u16 size, u32 cmd, MFCReg& MFC if (Ini.HLELogging.GetValue() || rec->s.data()) { - LOG_NOTICE(Log::SPU, "*** list element(%d/%d): s = 0x%x, ts = 0x%x, low ea = 0x%x (lsa = 0x%x)", i, list_size, rec->s, rec->ts, rec->ea, lsa | (addr & 0xf)); + LOG_NOTICE(Log::SPU, "*** list element(%d/%d): s=0x%x, ts=0x%x, eal=0x%x (lsa=0x%x)", i, list_size, rec->s, rec->ts, rec->ea, lsa | (addr & 0xf)); } if (size) @@ -346,7 +349,7 @@ void SPUThread::EnqMfcCmd(MFCReg& MFCArgs) case MFC_PUTR_CMD: // ??? case MFC_GET_CMD: { - if (Ini.HLELogging.GetValue()) LOG_NOTICE(Log::SPU, "DMA %s%s%s%s: lsa = 0x%x, ea = 0x%llx, tag = 0x%x, size = 0x%x, cmd = 0x%x", + if (Ini.HLELogging.GetValue()) LOG_NOTICE(Log::SPU, "DMA %s%s%s%s: lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x, cmd=0x%x", (op & MFC_PUT_CMD ? "PUT" : "GET"), (op & MFC_RESULT_MASK ? "R" : ""), (op & MFC_BARRIER_MASK ? "B" : ""), @@ -362,7 +365,7 @@ void SPUThread::EnqMfcCmd(MFCReg& MFCArgs) case MFC_PUTRL_CMD: // ??? case MFC_GETL_CMD: { - if (Ini.HLELogging.GetValue()) LOG_NOTICE(Log::SPU, "DMA %s%s%s%s: lsa = 0x%x, list = 0x%llx, tag = 0x%x, size = 0x%x, cmd = 0x%x", + if (Ini.HLELogging.GetValue()) LOG_NOTICE(Log::SPU, "DMA %s%s%s%s: lsa=0x%x, list=0x%llx, tag=0x%x, size=0x%x, cmd=0x%x", (op & MFC_PUT_CMD ? "PUT" : "GET"), (op & MFC_RESULT_MASK ? "RL" : "L"), (op & MFC_BARRIER_MASK ? "B" : ""), @@ -378,39 +381,16 @@ void SPUThread::EnqMfcCmd(MFCReg& MFCArgs) case MFC_PUTLLUC_CMD: case MFC_PUTQLLUC_CMD: { - if (Ini.HLELogging.GetValue() || size != 128) LOG_NOTICE(Log::SPU, "DMA %s: lsa=0x%x, ea = 0x%llx, (tag) = 0x%x, (size) = 0x%x, cmd = 0x%x", + if (Ini.HLELogging.GetValue() || size != 128) LOG_NOTICE(Log::SPU, "DMA %s: lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x, cmd=0x%x", (op == MFC_GETLLAR_CMD ? "GETLLAR" : op == MFC_PUTLLC_CMD ? "PUTLLC" : op == MFC_PUTLLUC_CMD ? "PUTLLUC" : "PUTQLLUC"), lsa, ea, tag, size, cmd); - if ((u32)ea != ea) - { - LOG_ERROR(Log::SPU, "DMA %s: Invalid external address (0x%llx)", - (op == MFC_GETLLAR_CMD ? "GETLLAR" : - op == MFC_PUTLLC_CMD ? "PUTLLC" : - op == MFC_PUTLLUC_CMD ? "PUTLLUC" : "PUTQLLUC"), - ea); - Emu.Pause(); - return; - } - if (op == MFC_GETLLAR_CMD) // get reservation { - //std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack - vm::reservation_acquire(vm::get_ptr(ls_offset + lsa), vm::cast(ea), 128, [this]() { - //std::shared_ptr t = Emu.GetCPU().GetThread(tid); - - //if (t && (t->GetType() == CPU_THREAD_SPU || t->GetType() == CPU_THREAD_RAW_SPU)) - //{ - // SPUThread& spu = static_cast(*t); - - // spu.m_events |= SPU_EVENT_LR; // TODO: atomic op - // spu.Notify(); - //} - m_events |= SPU_EVENT_LR; // TODO: atomic op Notify(); }); @@ -448,16 +428,16 @@ void SPUThread::EnqMfcCmd(MFCReg& MFCArgs) } default: - LOG_ERROR(Log::SPU, "Unknown MFC cmd. (opcode=0x%x, cmd=0x%x, lsa = 0x%x, ea = 0x%llx, tag = 0x%x, size = 0x%x)", - op, cmd, lsa, ea, tag, size); + { + LOG_ERROR(Log::SPU, "Unknown MFC cmd (opcode=0x%x, cmd=0x%x, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x)", op, cmd, lsa, ea, tag, size); + Emu.Pause(); break; } + } } bool SPUThread::CheckEvents() { - // checks events: - return (m_events & m_event_mask) != 0; } diff --git a/rpcs3/Emu/Memory/Memory.cpp b/rpcs3/Emu/Memory/Memory.cpp index de4705a643..6db3ebe06e 100644 --- a/rpcs3/Emu/Memory/Memory.cpp +++ b/rpcs3/Emu/Memory/Memory.cpp @@ -297,7 +297,7 @@ bool DynamicMemoryBlockBase::AllocFixed(u32 addr, u32 size) for (u32 i = 0; i= m_allocated[i].addr && addr < m_allocated[i].addr + m_allocated[i].size) return false; + if (addr >= m_allocated[i].addr && addr <= m_allocated[i].addr + m_allocated[i].size - 1) return false; } AppendMem(addr, size); @@ -342,8 +342,8 @@ u32 DynamicMemoryBlockBase::AllocAlign(u32 size, u32 align) for (u32 i = 0; i= m_allocated[i].addr && addr < m_allocated[i].addr + m_allocated[i].size) || - (m_allocated[i].addr >= addr && m_allocated[i].addr < addr + exsize)) + if ((addr >= m_allocated[i].addr && addr <= m_allocated[i].addr + m_allocated[i].size - 1) || + (m_allocated[i].addr >= addr && m_allocated[i].addr <= addr + exsize - 1)) { is_good_addr = false; addr = m_allocated[i].addr + m_allocated[i].size; diff --git a/rpcs3/Emu/Memory/vm.cpp b/rpcs3/Emu/Memory/vm.cpp index 20b127ef19..8f50f1169a 100644 --- a/rpcs3/Emu/Memory/vm.cpp +++ b/rpcs3/Emu/Memory/vm.cpp @@ -271,7 +271,7 @@ namespace vm return true; } - bool reservation_query(u32 addr, bool is_writing) + bool reservation_query(u32 addr, u32 size, bool is_writing) { std::lock_guard lock(g_reservation_mutex); @@ -282,8 +282,30 @@ namespace vm if (is_writing) { - // break the reservation - _reservation_break(addr); + assert(size); + + if (addr + size - 1 >= g_reservation_addr && g_reservation_addr + g_reservation_size - 1 >= addr) + { + // break the reservation if writing access and reservation overlap + _reservation_break(addr); + } + else + { + // full-size check (isn't accurate enough) + if (!check_addr(addr, size)) + { + return false; + } + + // assume that the same memory page is accessed (isn't accurate enough) + if (g_reservation_addr >> 12 != addr >> 12) + { + return false; + } + + // write memory using "privileged" access to avoid breaking reservation + return false; + } } return true; diff --git a/rpcs3/Emu/Memory/vm.h b/rpcs3/Emu/Memory/vm.h index 5f1d5a4338..e21d484be4 100644 --- a/rpcs3/Emu/Memory/vm.h +++ b/rpcs3/Emu/Memory/vm.h @@ -39,7 +39,7 @@ namespace vm // attempt to atomically update reserved memory bool reservation_update(u32 addr, const void* data, u32 size); // for internal use - bool reservation_query(u32 addr, bool is_writing); + bool reservation_query(u32 addr, u32 size, bool is_writing); // for internal use void reservation_free(); // perform complete operation