From 68f0393cf37b2e32df7c8eeb92855a10dafc840d Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Sat, 4 Feb 2017 19:30:21 +0300 Subject: [PATCH] sys_interrupt... --- rpcs3/Emu/Cell/PPUFunction.h | 7 +- rpcs3/Emu/Cell/SPUThread.cpp | 7 +- rpcs3/Emu/Cell/lv2/lv2.cpp | 3 + rpcs3/Emu/Cell/lv2/sys_interrupt.cpp | 143 +++++++++++++-------------- rpcs3/Emu/Cell/lv2/sys_interrupt.h | 27 +++-- rpcs3/Emu/Cell/lv2/sys_spu.cpp | 32 +++++- 6 files changed, 116 insertions(+), 103 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUFunction.h b/rpcs3/Emu/Cell/PPUFunction.h index 2c3018eddc..cd7ac19451 100644 --- a/rpcs3/Emu/Cell/PPUFunction.h +++ b/rpcs3/Emu/Cell/PPUFunction.h @@ -5,12 +5,7 @@ using ppu_function_t = void(*)(ppu_thread&); // BIND_FUNC macro "converts" any appropriate HLE function to ppu_function_t, binding it to PPU thread context. -// If function already has type ppu_function_t, it's handled specially and classified as "low-level HLE function". -// 1) Low-level functions are bound directly so they don't save their name to ppu.last_function variable. -// 2) Low-level functions don't install thread_guar, so they are very limited, and may be dangerous. -// If you don't need "low-level function", be sure it's either `void()` or `void(ppu_thread& ppu, ppu_thread&)` for example. -#define BIND_FUNC(func) (std::is_same::value ? reinterpret_cast(func) : static_cast([](ppu_thread& ppu){\ - const thread_guard guard(ppu);\ +#define BIND_FUNC(func) (static_cast([](ppu_thread& ppu) {\ const auto old_f = ppu.last_function;\ ppu.last_function = #func;\ ppu_func_detail::do_call(ppu, func);\ diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 12ba81c064..ff873f6cac 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -54,9 +54,12 @@ void spu_int_ctrl_t::set(u64 ints) { LV2_LOCK; - if (tag && tag->handler) + if (tag) { - tag->handler->exec(); + if (auto handler = tag->handler.lock()) + { + handler->exec(); + } } } } diff --git a/rpcs3/Emu/Cell/lv2/lv2.cpp b/rpcs3/Emu/Cell/lv2/lv2.cpp index 70f50c3dfd..f017dc69fa 100644 --- a/rpcs3/Emu/Cell/lv2/lv2.cpp +++ b/rpcs3/Emu/Cell/lv2/lv2.cpp @@ -973,6 +973,9 @@ void fmt_class_string::format(std::string& out, u64 arg) STR_CASE(CELL_EOVERFLOW); STR_CASE(CELL_ENOTMOUNTED); STR_CASE(CELL_ENOTSDATA); + STR_CASE(CELL_ESDKVER); + STR_CASE(CELL_ENOLICDISC); + STR_CASE(CELL_ENOLICENT); } return unknown; diff --git a/rpcs3/Emu/Cell/lv2/sys_interrupt.cpp b/rpcs3/Emu/Cell/lv2/sys_interrupt.cpp index bac4b7fa47..a49130a31d 100644 --- a/rpcs3/Emu/Cell/lv2/sys_interrupt.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_interrupt.cpp @@ -12,14 +12,6 @@ namespace vm { using namespace ps3; } logs::channel sys_interrupt("sys_interrupt", logs::level::notice); -lv2_int_serv::lv2_int_serv(const std::shared_ptr& thread, u64 arg1, u64 arg2) - : thread(thread) - , arg1(arg1) - , arg2(arg2) - , id(idm::last_id()) -{ -} - void lv2_int_serv::exec() { thread->cmd_list @@ -31,7 +23,7 @@ void lv2_int_serv::exec() thread->notify(); } -void lv2_int_serv::join(ppu_thread& ppu, lv2_lock_t lv2_lock) +void lv2_int_serv::join() { // Enqueue _sys_ppu_thread_exit call thread->cmd_list @@ -42,93 +34,98 @@ void lv2_int_serv::join(ppu_thread& ppu, lv2_lock_t lv2_lock) }); thread->notify(); - - // Join thread (TODO) - while (!test(thread->state & cpu_flag::exit)) - { - CHECK_EMU_STATUS; - - LV2_UNLOCK, thread_ctrl::wait_for(1000); - } - - // Cleanup - idm::remove(id); + thread->join(); } -s32 sys_interrupt_tag_destroy(u32 intrtag) +error_code sys_interrupt_tag_destroy(u32 intrtag) { sys_interrupt.warning("sys_interrupt_tag_destroy(intrtag=0x%x)", intrtag); - LV2_LOCK; + const auto tag = idm::withdraw(intrtag, [](lv2_int_tag& tag) -> CellError + { + if (!tag.handler.expired()) + { + return CELL_EBUSY; + } - const auto tag = idm::get(intrtag); + return {}; + }); if (!tag) { return CELL_ESRCH; } - if (tag->handler) + if (tag.ret) { - return CELL_EBUSY; + return tag.ret; } - idm::remove(intrtag); - return CELL_OK; } -s32 _sys_interrupt_thread_establish(vm::ptr ih, u32 intrtag, u32 intrthread, u64 arg1, u64 arg2) +error_code _sys_interrupt_thread_establish(vm::ptr ih, u32 intrtag, u32 intrthread, u64 arg1, u64 arg2) { sys_interrupt.warning("_sys_interrupt_thread_establish(ih=*0x%x, intrtag=0x%x, intrthread=0x%x, arg1=0x%llx, arg2=0x%llx)", ih, intrtag, intrthread, arg1, arg2); - LV2_LOCK; + CellError error = CELL_EAGAIN; - // Get interrupt tag - const auto tag = idm::get(intrtag); - - if (!tag) + const u32 id = idm::import([&]() { - return CELL_ESRCH; + std::shared_ptr result; + + // Get interrupt tag + const auto tag = idm::check_unlocked(intrtag); + + if (!tag) + { + error = CELL_ESRCH; + return result; + } + + // Get interrupt thread + const auto it = idm::get_unlocked(intrthread); + + if (!it) + { + error = CELL_ESRCH; + return result; + } + + // If interrupt thread is running, it's already established on another interrupt tag + if (!test(it->state & cpu_flag::stop)) + { + error = CELL_EAGAIN; + return result; + } + + // It's unclear if multiple handlers can be established on single interrupt tag + if (!tag->handler.expired()) + { + error = CELL_ESTAT; + return result; + } + + result = std::make_shared(it, arg1, arg2); + tag->handler = result; + it->run(); + return result; + }); + + if (id) + { + *ih = id; + return CELL_OK; } - // Get interrupt thread - const auto it = idm::get(intrthread); - - if (!it) - { - return CELL_ESRCH; - } - - // If interrupt thread is running, it's already established on another interrupt tag - if (!test(it->state & cpu_flag::stop)) - { - return CELL_EAGAIN; - } - - // It's unclear if multiple handlers can be established on single interrupt tag - if (tag->handler) - { - sys_interrupt.error("_sys_interrupt_thread_establish(): handler service already exists (intrtag=0x%x) -> CELL_ESTAT", intrtag); - return CELL_ESTAT; - } - - tag->handler = idm::make_ptr(it, arg1, arg2); - - it->run(); - - *ih = tag->handler->id; - - return CELL_OK; + return error; } -s32 _sys_interrupt_thread_disestablish(ppu_thread& ppu, u32 ih, vm::ptr r13) +error_code _sys_interrupt_thread_disestablish(ppu_thread& ppu, u32 ih, vm::ptr r13) { sys_interrupt.warning("_sys_interrupt_thread_disestablish(ih=0x%x, r13=*0x%x)", ih, r13); - LV2_LOCK; - - const auto handler = idm::get(ih); + const auto handler = idm::withdraw(ih); if (!handler) { @@ -136,7 +133,7 @@ s32 _sys_interrupt_thread_disestablish(ppu_thread& ppu, u32 ih, vm::ptr r13 } // Wait for sys_interrupt_thread_eoi() and destroy interrupt thread - handler->join(ppu, lv2_lock); + handler->join(); // Save TLS base *r13 = handler->thread->gpr[13]; @@ -144,23 +141,15 @@ s32 _sys_interrupt_thread_disestablish(ppu_thread& ppu, u32 ih, vm::ptr r13 return CELL_OK; } -void sys_interrupt_thread_eoi(ppu_thread& ppu) // Low-level PPU function example +void sys_interrupt_thread_eoi(ppu_thread& ppu) { - // Low-level function body must guard all C++-ish calls and all objects with non-trivial destructors - thread_guard{ppu}, sys_interrupt.trace("sys_interrupt_thread_eoi()"); + sys_interrupt.trace("sys_interrupt_thread_eoi()"); ppu.state += cpu_flag::ret; // Throw if this syscall was not called directly by the SC instruction (hack) if (ppu.lr == 0 || ppu.gpr[11] != 88) { - // Low-level function must disable interrupts before throwing (not related to sys_interrupt_*, it's rather coincidence) - ppu.get()->interrupt_disable(); throw cpu_flag::ret; } } - -lv2_int_tag::lv2_int_tag() - : id(idm::last_id()) -{ -} diff --git a/rpcs3/Emu/Cell/lv2/sys_interrupt.h b/rpcs3/Emu/Cell/lv2/sys_interrupt.h index 4463b995c1..637ffd17e8 100644 --- a/rpcs3/Emu/Cell/lv2/sys_interrupt.h +++ b/rpcs3/Emu/Cell/lv2/sys_interrupt.h @@ -8,11 +8,7 @@ struct lv2_int_tag final : lv2_obj { static const u32 id_base = 0x0a000000; - const u32 id; - - std::shared_ptr handler; - - lv2_int_tag(); + std::weak_ptr handler; }; struct lv2_int_serv final : lv2_obj @@ -20,20 +16,23 @@ struct lv2_int_serv final : lv2_obj static const u32 id_base = 0x0b000000; const std::shared_ptr thread; - - const u32 id; - const u64 arg1; const u64 arg2; - lv2_int_serv(const std::shared_ptr& thread, u64 arg1, u64 arg2); + lv2_int_serv(const std::shared_ptr& thread, u64 arg1, u64 arg2) + : thread(thread) + , arg1(arg1) + , arg2(arg2) + { + } void exec(); - void join(ppu_thread& ppu, lv2_lock_t); + void join(); }; -// SysCalls -s32 sys_interrupt_tag_destroy(u32 intrtag); -s32 _sys_interrupt_thread_establish(vm::ps3::ptr ih, u32 intrtag, u32 intrthread, u64 arg1, u64 arg2); -s32 _sys_interrupt_thread_disestablish(ppu_thread& ppu, u32 ih, vm::ps3::ptr r13); +// Syscalls + +error_code sys_interrupt_tag_destroy(u32 intrtag); +error_code _sys_interrupt_thread_establish(vm::ps3::ptr ih, u32 intrtag, u32 intrthread, u64 arg1, u64 arg2); +error_code _sys_interrupt_thread_disestablish(ppu_thread& ppu, u32 ih, vm::ps3::ptr r13); void sys_interrupt_thread_eoi(ppu_thread& ppu); diff --git a/rpcs3/Emu/Cell/lv2/sys_spu.cpp b/rpcs3/Emu/Cell/lv2/sys_spu.cpp index d8fecaeaf8..18ba7dfd90 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_spu.cpp @@ -1173,20 +1173,44 @@ s32 sys_raw_spu_destroy(ppu_thread& ppu, u32 id) // Stop thread thread->state += cpu_flag::stop; + // Kernel objects which must be removed + std::unordered_map> to_remove; + // Clear interrupt handlers for (auto& intr : thread->int_ctrl) { if (intr.tag) { - if (intr.tag->handler) + if (auto handler = intr.tag->handler.lock()) { - intr.tag->handler->join(ppu, lv2_lock); + LV2_UNLOCK, handler->join(); + to_remove.emplace(handler.get(), 0); } - idm::remove(intr.tag->id); + to_remove.emplace(intr.tag.get(), 0); } } + // Scan all kernel objects to determine IDs + idm::select([&](u32 id, lv2_obj& obj) + { + const auto found = to_remove.find(&obj); + + if (found != to_remove.end()) + { + found->second = id; + } + }); + + // Remove IDs + for (auto&& pair : to_remove) + { + if (pair.second >> 24 == 0xa) + idm::remove(pair.second); + if (pair.second >> 24 == 0xb) + idm::remove(pair.second); + } + idm::remove(thread->id); return CELL_OK; @@ -1219,7 +1243,7 @@ s32 sys_raw_spu_create_interrupt_tag(u32 id, u32 class_id, u32 hwthread, vm::ptr int_ctrl.tag = idm::make_ptr(); - *intrtag = int_ctrl.tag->id; + *intrtag = idm::last_id(); return CELL_OK; }