diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index 4aa5146b09..5b1fb69ed8 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -153,6 +153,7 @@ struct ppu_linkage_info // Module map std::map modules{}; + std::map>, std::less<>> lib_lock; shared_mutex mutex; }; @@ -628,8 +629,43 @@ extern u32 ppu_get_exported_func_addr(u32 fnid, const std::string& module_name) return g_fxo->get().modules[module_name].functions[fnid].export_addr; } +extern bool ppu_register_library_lock(std::string_view libname, bool lock_lib) +{ + auto link = g_fxo->try_get(); + + if (!link || libname.empty()) + { + return false; + } + + reader_lock lock(link->mutex); + + if (auto it = std::as_const(link->lib_lock).find(libname); it != link->lib_lock.cend() && it->second) + { + return lock_lib ? !it->second->test_and_set() : it->second->test_and_reset(); + } + + if (!lock_lib) + { + // If lock hasn't been installed it wasn't locked in the first place + return false; + } + + lock.upgrade(); + + auto& lib_lock = link->lib_lock.emplace(std::string{libname}, nullptr).first->second; + + if (!lib_lock) + { + lib_lock = std::make_shared>(true); + return true; + } + + return !lib_lock->test_and_set(); +} + // Load and register exports; return special exports found (nameless module) -static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 exports_end) +static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 exports_end, bool for_observing_callbacks = false) { std::unordered_map result; @@ -663,6 +699,12 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo continue; } + if (for_observing_callbacks) + { + addr += lib.size ? lib.size : sizeof(ppu_prx_module_info); + continue; + } + const std::string module_name(lib.name.get_ptr()); ppu_loader.notice("** Exported module '%s' (0x%x, 0x%x, 0x%x, 0x%x)", module_name, lib.vnids, lib.vstubs, lib.unk4, lib.unk5); @@ -873,6 +915,12 @@ void ppu_manual_load_imports_exports(u32 imports_start, u32 imports_size, u32 ex auto& link = g_fxo->get(); ppu_load_exports(&link, exports_start, exports_start + exports_size); + + if (!imports_size) + { + return; + } + ppu_load_imports(_main.relocs, &link, imports_start, imports_start + imports_size); } @@ -1419,10 +1467,13 @@ std::shared_ptr ppu_load_prx(const ppu_prx_object& elf, const std::stri prx->module_info_version[0] = lib_info->version[0]; prx->module_info_version[1] = lib_info->version[1]; prx->module_info_attributes = lib_info->attributes; + + prx->exports_start = lib_info->exports_start; + prx->exports_end = lib_info->exports_end; ppu_loader.warning("Library %s (rtoc=0x%x):", lib_name, lib_info->toc); - prx->specials = ppu_load_exports(&link, lib_info->exports_start, lib_info->exports_end); + prx->specials = ppu_load_exports(&link, prx->exports_start, prx->exports_end, true); prx->imports = ppu_load_imports(prx->relocs, &link, lib_info->imports_start, lib_info->imports_end); std::stable_sort(prx->relocs.begin(), prx->relocs.end()); toc = lib_info->toc; @@ -2055,6 +2106,8 @@ bool ppu_load_exec(const ppu_exec_object& elf, utils::serial* ar) ppu_loader.warning("Loading library: %s", name); auto prx = ppu_load_prx(obj, lle_dir + name, 0, nullptr); + prx->state = PRX_STATE_STARTED; + prx->load_exports(); if (prx->funcs.empty()) { diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index c0c1ca6e92..4799f2e81d 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -3192,6 +3192,13 @@ extern void ppu_initialize() if (ptr->path.starts_with(firmware_sprx_path)) { compile_fw |= ppu_initialize(*ptr, true); + + // Fixup for compatibility with old savestates + if (Emu.DeserialManager() && ptr->name == "liblv2.sprx") + { + static_cast(ptr)->state = PRX_STATE_STARTED; + static_cast(ptr)->load_exports(); + } } } } diff --git a/rpcs3/Emu/Cell/lv2/sys_prx.cpp b/rpcs3/Emu/Cell/lv2/sys_prx.cpp index e31393acb3..a8539f1332 100644 --- a/rpcs3/Emu/Cell/lv2/sys_prx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_prx.cpp @@ -170,6 +170,8 @@ extern const std::map g_prx_list { "libwmadec.sprx", 0 }, }; +bool ppu_register_library_lock(std::string_view libname, bool lock_lib); + static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr /*pOpt*/, fs::file src = {}, s64 file_offset = 0) { if (flags != 0) @@ -191,16 +193,6 @@ static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr([&](u32, lv2_prx& prx) - { - return prx.path == path && prx.offset == file_offset; - }); - - if (existing) - { - return CELL_PRX_ERROR_LIBRARY_FOUND; - } - bool ignore = false; constexpr std::string_view firmware_sprx_dir = "/dev_flash/sys/external/"; @@ -295,6 +287,8 @@ fs::file make_file_view(fs::file&& _file, u64 offset); std::shared_ptr lv2_prx::load(utils::serial& ar) { + [[maybe_unused]] const s32 version = GET_SERIALIZATION_VERSION(lv2_prx_overlay); + const std::string path = vfs::get(ar.operator std::string()); const s64 offset = ar; const u32 state = ar; @@ -320,6 +314,18 @@ std::shared_ptr lv2_prx::load(utils::serial& ar) u128 klic = g_fxo->get().last_key(); file = make_file_view(std::move(file), offset); prx = ppu_load_prx(ppu_prx_object{ decrypt_self(std::move(file), reinterpret_cast(&klic)) }, path, 0, &ar); + + if (version >= 2 && state == PRX_STATE_STARTED) + { + ensure(ppu_register_library_lock(prx->module_info_name, true)); + prx->load_exports(); + } + + if (version == 1) + { + prx->load_exports(); + } + ensure(prx); } else @@ -498,9 +504,35 @@ error_code _sys_prx_start_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptrmutex); + + if (prx->state != PRX_STATE_INITIALIZED) + { + if (prx->state == PRX_STATE_DESTROYED) + { + return CELL_ESRCH; + } + + return CELL_PRX_ERROR_ERROR; + } + + if (prx->exports_end > prx->exports_start && !ppu_register_library_lock(prx->module_info_name, true)) + { + return {CELL_PRX_ERROR_LIBRARY_FOUND, +prx->module_info_name}; + } + + prx->load_exports(); + if (!prx->state.compare_and_swap_test(PRX_STATE_INITIALIZED, PRX_STATE_STARTING)) { // The only error code here + ensure(prx->exports_end <= prx->exports_start || ppu_register_library_lock(prx->module_info_name, false)); + + if (prx->state == PRX_STATE_DESTROYED) + { + return CELL_ESRCH; + } + return CELL_PRX_ERROR_ERROR; } @@ -590,6 +622,7 @@ error_code _sys_prx_stop_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptrmutex); + ensure(prx->exports_end <= prx->exports_start || (prx->state == PRX_STATE_STOPPING && ppu_register_library_lock(prx->module_info_name, false))); ensure(prx->state.compare_and_swap_test(PRX_STATE_STOPPING, PRX_STATE_STOPPED)); return CELL_OK; } @@ -628,6 +663,7 @@ error_code _sys_prx_stop_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptr(id, [](lv2_prx& prx) -> CellPrxError { - switch (prx.state) + switch (prx.state.fetch_op([](u32& value) + { + if (value == PRX_STATE_INITIALIZED || value == PRX_STATE_STOPPED) + { + value = PRX_STATE_DESTROYED; + return true; + } + + return false; + }).first) { case PRX_STATE_INITIALIZED: case PRX_STATE_STOPPED: @@ -683,6 +728,8 @@ error_code _sys_prx_unload_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptrname); + prx->mutex.lock_unlock(); + ppu_unload_prx(*prx); ppu_finalize(*prx); @@ -694,6 +741,17 @@ error_code _sys_prx_unload_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptr name, vm::ptr opt) { ppu.state += cpu_flag::wait; diff --git a/rpcs3/Emu/Cell/lv2/sys_prx.h b/rpcs3/Emu/Cell/lv2/sys_prx.h index 2df2328f62..b152312788 100644 --- a/rpcs3/Emu/Cell/lv2/sys_prx.h +++ b/rpcs3/Emu/Cell/lv2/sys_prx.h @@ -169,6 +169,7 @@ enum : u32 PRX_STATE_STARTED, PRX_STATE_STOPPING, // In-between state between started and stopped (internal) PRX_STATE_STOPPED, // Last state, the module cannot be restarted + PRX_STATE_DESTROYED, // Last state, the module cannot be restarted }; struct lv2_prx final : lv2_obj, ppu_module @@ -176,6 +177,7 @@ struct lv2_prx final : lv2_obj, ppu_module static const u32 id_base = 0x23000000; atomic_t state = PRX_STATE_INITIALIZED; + shared_mutex mutex; std::unordered_map specials; std::unordered_map imports; @@ -190,6 +192,11 @@ struct lv2_prx final : lv2_obj, ppu_module u8 module_info_version[2]; be_t module_info_attributes; + u32 exports_start = umax; + u32 exports_end = 0; + + void load_exports(); // (Re)load exports + lv2_prx() noexcept = default; lv2_prx(utils::serial&) {} static std::shared_ptr load(utils::serial&); diff --git a/rpcs3/Emu/savestate_utils.cpp b/rpcs3/Emu/savestate_utils.cpp index 539a445d4d..12c348cb6b 100644 --- a/rpcs3/Emu/savestate_utils.cpp +++ b/rpcs3/Emu/savestate_utils.cpp @@ -42,7 +42,7 @@ SERIALIZATION_VER(lv2_sync, 3, 1) SERIALIZATION_VER(lv2_vm, 4, 1) SERIALIZATION_VER(lv2_net, 5, 1) SERIALIZATION_VER(lv2_fs, 6, 1) -SERIALIZATION_VER(lv2_prx_overlay, 7, 1) +SERIALIZATION_VER(lv2_prx_overlay, 7, 1, 2/*PRX dynamic exports*/) SERIALIZATION_VER(lv2_memory, 8, 1) SERIALIZATION_VER(lv2_config, 9, 1)