From 74f344378eccc0ed269734b0e16d792aa806f3a9 Mon Sep 17 00:00:00 2001 From: eladash Date: Fri, 8 Feb 2019 19:23:49 +0200 Subject: [PATCH 1/3] Make sys_spu_thread_group_join return once per termination --- rpcs3/Emu/Cell/SPUThread.cpp | 7 ++++++- rpcs3/Emu/Cell/lv2/sys_spu.cpp | 35 +++++++++------------------------- rpcs3/Emu/Cell/lv2/sys_spu.h | 9 +-------- 3 files changed, 16 insertions(+), 35 deletions(-) diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 2a6864f8cc94..15bbe78db908 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -521,6 +521,11 @@ void spu_thread::cpu_stop() group->stop_count++; group->run_state = SPU_THREAD_GROUP_STATUS_INITIALIZED; + if (!group->join_state) + { + group->join_state = SYS_SPU_THREAD_GROUP_JOIN_ALL_THREADS_EXIT; + } + if (const auto ppu = std::exchange(group->waiter, nullptr)) { // Send exit status directly to the joining thread @@ -2436,7 +2441,7 @@ bool spu_thread::stop_and_signal(u32 code) } group->exit_status = value; - group->join_state |= SPU_TGJSF_GROUP_EXIT; + group->join_state = SYS_SPU_THREAD_GROUP_JOIN_GROUP_EXIT; state += cpu_flag::stop; return true; diff --git a/rpcs3/Emu/Cell/lv2/sys_spu.cpp b/rpcs3/Emu/Cell/lv2/sys_spu.cpp index 1c84648034c4..d1361437be5f 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_spu.cpp @@ -607,7 +607,7 @@ error_code sys_spu_thread_group_terminate(u32 id, s32 value) } group->exit_status = value; - group->join_state |= SPU_TGJSF_TERMINATED; + group->join_state = SYS_SPU_THREAD_GROUP_JOIN_TERMINATED; // Wait until the threads are actually stopped const u64 last_stop = group->stop_count - !group->running; @@ -648,11 +648,12 @@ error_code sys_spu_thread_group_join(ppu_thread& ppu, u32 id, vm::ptr cause return CELL_EBUSY; } - if (group->run_state == SPU_THREAD_GROUP_STATUS_INITIALIZED) + if (group->join_state) { - // Already terminated + // Already signaled ppu.gpr[4] = group->join_state; ppu.gpr[5] = group->exit_status; + group->join_state.release(0); break; } else @@ -661,11 +662,9 @@ error_code sys_spu_thread_group_join(ppu_thread& ppu, u32 id, vm::ptr cause group->waiter = &ppu; } - const u64 last_stop = group->stop_count - !group->running; - lv2_obj::sleep(ppu); - while (group->stop_count == last_stop) + while (!group->join_state) { if (ppu.is_stopped()) { @@ -674,6 +673,8 @@ error_code sys_spu_thread_group_join(ppu_thread& ppu, u32 id, vm::ptr cause group->cond.wait(lock); } + + group->join_state.release(0); } while (0); @@ -682,27 +683,9 @@ error_code sys_spu_thread_group_join(ppu_thread& ppu, u32 id, vm::ptr cause return 0; } - switch (ppu.gpr[4] & (SPU_TGJSF_GROUP_EXIT | SPU_TGJSF_TERMINATED)) - { - case 0: - { - if (cause) *cause = SYS_SPU_THREAD_GROUP_JOIN_ALL_THREADS_EXIT; - break; - } - case SPU_TGJSF_GROUP_EXIT: - { - if (cause) *cause = SYS_SPU_THREAD_GROUP_JOIN_GROUP_EXIT; - break; - } - case SPU_TGJSF_TERMINATED: + if (cause) { - if (cause) *cause = SYS_SPU_THREAD_GROUP_JOIN_TERMINATED; - break; - } - default: - { - fmt::throw_exception("Unexpected join_state" HERE); - } + *cause = static_cast(ppu.gpr[4]); } if (status) diff --git a/rpcs3/Emu/Cell/lv2/sys_spu.h b/rpcs3/Emu/Cell/lv2/sys_spu.h index 8a90656af022..adfdabba62ad 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.h +++ b/rpcs3/Emu/Cell/lv2/sys_spu.h @@ -207,13 +207,6 @@ enum : u32 SYS_SPU_IMAGE_DIRECT = 1, }; -// SPU Thread Group Join State Flag -enum : u32 -{ - SPU_TGJSF_TERMINATED = (1 << 1), // set if SPU Thread Group is terminated by sys_spu_thread_group_terminate - SPU_TGJSF_GROUP_EXIT = (1 << 2), // set if SPU Thread Group is terminated by sys_spu_thread_group_exit -}; - struct lv2_spu_group { static const u32 id_base = 1; // Wrong? @@ -232,7 +225,7 @@ struct lv2_spu_group atomic_t prio; // SPU Thread Group Priority atomic_t run_state; // SPU Thread Group State atomic_t exit_status; // SPU Thread Group Exit Status - atomic_t join_state; // flags used to detect exit cause + atomic_t join_state; // flags used to detect exit cause and signal atomic_t running; // Number of running threads cond_variable cond; // used to signal waiting PPU thread atomic_t stop_count; From fd67efff3e975d050ddef3f14f45f62a89e58137 Mon Sep 17 00:00:00 2001 From: eladash Date: Sat, 9 Feb 2019 11:34:36 +0200 Subject: [PATCH 2/3] Make more use of the new atomic_t<>::release --- rpcs3/Emu/Cell/PPUThread.cpp | 14 ++++---- rpcs3/Emu/Cell/SPUThread.cpp | 54 ++++++++++++++++-------------- rpcs3/Emu/Cell/SPUThread.h | 8 ++--- rpcs3/Emu/Cell/lv2/sys_lwcond.cpp | 2 +- rpcs3/Emu/Cell/lv2/sys_lwmutex.cpp | 2 +- 5 files changed, 42 insertions(+), 38 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 87a27546e4f8..843f819e0184 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -1092,17 +1092,18 @@ extern bool ppu_stwcx(ppu_thread& ppu, u32 addr, u32 reg_value) vm::passive_unlock(ppu); auto& res = vm::reservation_lock(addr, sizeof(u32)); + const u64 old_time = res.load() & ~1ull; - const bool result = ppu.rtime == (res & ~1ull) && data.compare_and_swap_test(old_data, reg_value); + const bool result = ppu.rtime == old_time && data.compare_and_swap_test(old_data, reg_value); if (result) { - res++; + res.release(old_time + 2); vm::reservation_notifier(addr, sizeof(u32)).notify_all(); } else { - res &= ~1ull; + res.release(old_time); } vm::passive_lock(ppu); @@ -1185,17 +1186,18 @@ extern bool ppu_stdcx(ppu_thread& ppu, u32 addr, u64 reg_value) vm::passive_unlock(ppu); auto& res = vm::reservation_lock(addr, sizeof(u64)); + const u64 old_time = res.load() & ~1ull; - const bool result = ppu.rtime == (res & ~1ull) && data.compare_and_swap_test(old_data, reg_value); + const bool result = ppu.rtime == old_time && data.compare_and_swap_test(old_data, reg_value); if (result) { - res++; + res.release(old_time + 2); vm::reservation_notifier(addr, sizeof(u64)).notify_all(); } else { - res &= ~1ull; + res.release(old_time); } vm::passive_lock(ppu); diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 15bbe78db908..96973c6c1786 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -471,20 +471,20 @@ void spu_thread::cpu_init() ch_tag_upd = 0; ch_tag_mask = 0; mfc_prxy_mask = 0; - ch_tag_stat.data.store({}); + ch_tag_stat.data.release({}); ch_stall_mask = 0; - ch_stall_stat.data.store({}); - ch_atomic_stat.data.store({}); + ch_stall_stat.data.release({}); + ch_atomic_stat.data.release({}); ch_in_mbox.clear(); - ch_out_mbox.data.store({}); - ch_out_intr_mbox.data.store({}); + ch_out_mbox.data.release({}); + ch_out_intr_mbox.data.release({}); snr_config = 0; - ch_snr1.data.store({}); - ch_snr2.data.store({}); + ch_snr1.data.release({}); + ch_snr2.data.release({}); ch_event_mask = 0; ch_event_stat = 0; @@ -494,9 +494,9 @@ void spu_thread::cpu_init() ch_dec_start_timestamp = get_timebased_time(); // ??? ch_dec_value = 0; - run_ctrl = 0; - status = 0; - npc = 0; + run_ctrl.release(0); + status.release(0); + npc.release(0); int_ctrl[0].clear(); int_ctrl[1].clear(); @@ -810,28 +810,28 @@ void spu_thread::do_dma_transfer(const spu_mfc_cmd& args) { auto& res = vm::reservation_lock(eal, 1); *reinterpret_cast(dst) = *reinterpret_cast(src); - res++; + res.release(res.load() + 1); break; } case 2: { auto& res = vm::reservation_lock(eal, 2); *reinterpret_cast(dst) = *reinterpret_cast(src); - res++; + res.release(res.load() + 1); break; } case 4: { auto& res = vm::reservation_lock(eal, 4); *reinterpret_cast(dst) = *reinterpret_cast(src); - res++; + res.release(res.load() + 1); break; } case 8: { auto& res = vm::reservation_lock(eal, 8); *reinterpret_cast(dst) = *reinterpret_cast(src); - res++; + res.release(res.load() + 1); break; } default: @@ -850,7 +850,7 @@ void spu_thread::do_dma_transfer(const spu_mfc_cmd& args) size -= 16; } - res++; + res.release(res.load() + 1); break; } @@ -874,7 +874,7 @@ void spu_thread::do_dma_transfer(const spu_mfc_cmd& args) size -= 16; } - *lock = 0; + lock->release(0); break; } } @@ -1086,12 +1086,12 @@ void spu_thread::do_putlluc(const spu_mfc_cmd& args) // TODO: vm::check_addr vm::writer_lock lock(addr); mov_rdata(data.data(), to_write.data()); - res++; + res.release(res.load() + 1); } else { mov_rdata(data.data(), to_write.data()); - res++; + res.release(res.load() + 1); } } @@ -1281,6 +1281,7 @@ bool spu_thread::process_mfc_cmd() else { auto& res = vm::reservation_lock(addr, 128); + const u64 old_time = res.load() & ~1ull; if (g_cfg.core.spu_accurate_getllar) { @@ -1290,15 +1291,15 @@ bool spu_thread::process_mfc_cmd() // TODO: vm::check_addr vm::writer_lock lock(addr); - ntime = res & ~1ull; + ntime = old_time; mov_rdata(dst.data(), data.data()); - res &= ~1ull; + res.release(old_time); } else { - ntime = res & ~1ull; + ntime = old_time; mov_rdata(dst.data(), data.data()); - res &= ~1ull; + res.release(old_time); } } @@ -1355,8 +1356,9 @@ bool spu_thread::process_mfc_cmd() else if (auto& data = vm::_ref(addr); rdata == data) { auto& res = vm::reservation_lock(raddr, 128); + const u64 old_time = res.load() & ~1ull; - if (rtime == (res & ~1ull)) + if (rtime == old_time) { *reinterpret_cast*>(&data) += 0; @@ -1367,17 +1369,17 @@ bool spu_thread::process_mfc_cmd() if (rdata == data) { mov_rdata(data.data(), to_write.data()); - res++; + res.release(old_time + 2); result = 1; } else { - res &= ~1ull; + res.release(old_time); } } else { - res &= ~1ull; + res.release(old_time); } } } diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 21bd74d22c5e..55181a622dd3 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -276,8 +276,8 @@ struct spu_channel_4_t public: void clear() { - values.store({}); - value3 = 0; + values.release({}); + value3.release(0); } // push unconditionally (overwriting latest value), returns true if needs signaling @@ -364,8 +364,8 @@ struct spu_int_ctrl_t void clear() { - mask = 0; - stat = 0; + mask.release(0); + stat.release(0); tag = nullptr; } }; diff --git a/rpcs3/Emu/Cell/lv2/sys_lwcond.cpp b/rpcs3/Emu/Cell/lv2/sys_lwcond.cpp index f5645fb371fe..92537e81d91d 100644 --- a/rpcs3/Emu/Cell/lv2/sys_lwcond.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_lwcond.cpp @@ -266,7 +266,7 @@ error_code _sys_lwcond_queue_wait(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id return cpu; } - mutex->signaled = 1; + mutex->signaled.release(1); return nullptr; }); diff --git a/rpcs3/Emu/Cell/lv2/sys_lwmutex.cpp b/rpcs3/Emu/Cell/lv2/sys_lwmutex.cpp index 7ebaffc224d9..8e1ee29c5c08 100644 --- a/rpcs3/Emu/Cell/lv2/sys_lwmutex.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_lwmutex.cpp @@ -175,7 +175,7 @@ error_code _sys_lwmutex_unlock(ppu_thread& ppu, u32 lwmutex_id) return cpu; } - mutex.signaled = 1; + mutex.signaled.release(1); return nullptr; }); From f0b2300aaab7bcaaf1a0e75b117e8577a37b30a1 Mon Sep 17 00:00:00 2001 From: eladash Date: Sat, 9 Feb 2019 18:58:54 +0200 Subject: [PATCH 3/3] Add EFAULT checks to spu_thread_group_join, ppu_thread_join Order of checks is based on firmware --- rpcs3/Emu/Cell/Modules/cellSpurs.cpp | 2 +- rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp | 18 ++++++++++-------- rpcs3/Emu/Cell/lv2/sys_spu.cpp | 11 +++++++---- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellSpurs.cpp b/rpcs3/Emu/Cell/Modules/cellSpurs.cpp index a7245ec9090d..3d13726aa450 100644 --- a/rpcs3/Emu/Cell/Modules/cellSpurs.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSpurs.cpp @@ -574,7 +574,7 @@ void _spurs::handler_entry(ppu_thread& ppu, vm::ptr spurs) CHECK_SUCCESS(sys_spu_thread_group_start(ppu, spurs->spuTG)); - if (s32 rc = sys_spu_thread_group_join(ppu, spurs->spuTG, vm::null, vm::null)) + if (s32 rc = sys_spu_thread_group_join(ppu, spurs->spuTG, vm::var{}, vm::var{})) { if (rc == CELL_ESTAT) { diff --git a/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp b/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp index da1fbcba4439..fadc02d7cbd1 100644 --- a/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp @@ -120,19 +120,21 @@ error_code sys_ppu_thread_join(ppu_thread& ppu, u32 thread_id, vm::ptr vptr // Wait for cleanup (*thread.ptr)(); - // Get the exit status from the register - if (vptr) + if (ppu.test_stopped()) { - if (ppu.test_stopped()) - { - return 0; - } - - *vptr = thread->gpr[3]; + return 0; } // Cleanup idm::remove>(thread->id); + + if (!vptr) + { + return CELL_EFAULT; + } + + // Get the exit status from the register + *vptr = thread->gpr[3]; return CELL_OK; } diff --git a/rpcs3/Emu/Cell/lv2/sys_spu.cpp b/rpcs3/Emu/Cell/lv2/sys_spu.cpp index d1361437be5f..ada4b421ee6e 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_spu.cpp @@ -683,16 +683,19 @@ error_code sys_spu_thread_group_join(ppu_thread& ppu, u32 id, vm::ptr cause return 0; } - if (cause) + if (!cause) { - *cause = static_cast(ppu.gpr[4]); + return CELL_EFAULT; } - if (status) + *cause = static_cast(ppu.gpr[4]); + + if (!status) { - *status = static_cast(ppu.gpr[5]); + return CELL_EFAULT; } + *status = static_cast(ppu.gpr[5]); return CELL_OK; }