diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index 2607a428cd..4f44b56307 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -3,6 +3,7 @@ #include "Utilities/bin_patch.h" #include "Utilities/StrUtil.h" +#include "Utilities/address_range.h" #include "Crypto/sha1.h" #include "Crypto/unself.h" #include "Loader/ELF.h" @@ -30,7 +31,7 @@ extern std::string ppu_get_function_name(const std::string& _module, u32 fnid); extern std::string ppu_get_variable_name(const std::string& _module, u32 vnid); extern void ppu_register_range(u32 addr, u32 size); extern void ppu_register_function_at(u32 addr, u32 size, ppu_function_t ptr); -extern void ppu_initialize(const ppu_module& info); +extern bool ppu_initialize(const ppu_module& info, bool = false); extern void ppu_initialize(); extern void sys_initialize_tls(ppu_thread&, u64, u32, u32, u32); @@ -1648,10 +1649,8 @@ void ppu_load_exec(const ppu_exec_object& elf) } } -std::shared_ptr ppu_load_overlay(const ppu_exec_object& elf, const std::string& path) +std::pair, CellError> ppu_load_overlay(const ppu_exec_object& elf, const std::string& path) { - const auto ovlm = idm::make_ptr(); - // Access linkage information object const auto link = g_fxo->get(); @@ -1659,6 +1658,25 @@ std::shared_ptr ppu_load_overlay(const ppu_exec_object& elf, const sha1_context sha; sha1_starts(&sha); + // Check if it is an overlay executable first + for (const auto& prog : elf.progs) + { + if (prog.p_type == 0x1u /* LOAD */ && prog.p_memsz) + { + using addr_range = utils::address_range; + + const addr_range r = addr_range::start_length(::narrow(prog.p_vaddr), ::narrow(prog.p_memsz)); + + if (!r.valid() || !r.inside(addr_range::start_length(0x30000000, 0x10000000))) + { + // TODO: Check error and if there's a better way to error check + return {nullptr, CELL_ENOEXEC}; + } + } + } + + const auto ovlm = std::make_shared(); + // Allocate memory at fixed positions for (const auto& prog : elf.progs) { @@ -1682,7 +1700,18 @@ std::shared_ptr ppu_load_overlay(const ppu_exec_object& elf, const fmt::throw_exception("Invalid binary size (0x%llx, memsz=0x%x)", prog.bin.size(), size); if (!vm::get(vm::any, 0x30000000)->falloc(addr, size)) - fmt::throw_exception("vm::falloc() failed (addr=0x%x, memsz=0x%x)", addr, size); + { + ppu_loader.error("ppu_load_overlay(): vm::falloc() failed (addr=0x%x, memsz=0x%x)", addr, size); + + // Revert previous allocations + for (const auto& seg : ovlm->segs) + { + ensure(vm::dealloc(seg.addr)); + } + + // TODO: Check error code, maybe disallow more than one overlay instance completely + return {nullptr, CELL_EBUSY}; + } // Copy segment data, hash it std::memcpy(vm::base(addr), prog.bin.data(), prog.bin.size()); @@ -1845,5 +1874,6 @@ std::shared_ptr ppu_load_overlay(const ppu_exec_object& elf, const ovlm->name = path.substr(path.find_last_of('/') + 1); ovlm->path = path; - return ovlm; + idm::import_existing(ovlm); + return {std::move(ovlm), {}}; } diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 0f914b40db..3fcd0dc2f6 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -18,6 +18,8 @@ #include "SPURecompiler.h" #include "lv2/sys_sync.h" #include "lv2/sys_prx.h" +#include "lv2/sys_overlay.h" +#include "lv2/sys_process.h" #include "lv2/sys_memory.h" #include "Emu/GDB.h" @@ -117,8 +119,9 @@ const ppu_decoder g_ppu_itype; extern void ppu_initialize(); extern void ppu_finalize(const ppu_module& info); -extern void ppu_initialize(const ppu_module& info); +extern bool ppu_initialize(const ppu_module& info, bool = false); static void ppu_initialize2(class jit_compiler& jit, const ppu_module& module_part, const std::string& cache_path, const std::string& obj_name); +extern std::pair, CellError> ppu_load_overlay(const ppu_exec_object&, const std::string& path); extern void ppu_unload_prx(const lv2_prx&); extern std::shared_ptr ppu_load_prx(const ppu_prx_object&, const std::string&); extern void ppu_execute_syscall(ppu_thread& ppu, u64 code); @@ -788,7 +791,7 @@ void ppu_thread::cpu_task() } case ppu_cmd::initialize: { - cmd_pop(), ppu_initialize(); + cmd_pop(), ppu_initialize(), spu_cache::initialize(); break; } case ppu_cmd::sleep: @@ -2064,19 +2067,27 @@ extern void ppu_finalize(const ppu_module& info) #endif } -extern void ppu_precompile(std::vector& dir_queue) +extern void ppu_precompile(std::vector& dir_queue, std::vector* loaded_prx) { + // Remove duplicates + std::sort(dir_queue.begin(), dir_queue.end()); + dir_queue.erase(std::unique(dir_queue.begin(), dir_queue.end()), dir_queue.end()); + + const std::string firmware_sprx_path = vfs::get("/dev_flash/sys/external/"); + + // Map fixed address executables area, fake overlay support + const bool had_ovl = !vm::map(0x3000'0000, 0x1000'0000, 0x200).operator bool(); + const u32 ppc_seg = std::exchange(g_ps3_process_info.ppc_seg, 0x3); + std::vector> file_queue; file_queue.reserve(2000); - // Initialize progress dialog - g_progr = "Scanning directories for SPRX libraries..."; - // Find all .sprx files recursively (TODO: process .mself files) for (usz i = 0; i < dir_queue.size(); i++) { if (Emu.IsStopped()) { + file_queue.clear(); break; } @@ -2086,6 +2097,7 @@ extern void ppu_precompile(std::vector& dir_queue) { if (Emu.IsStopped()) { + file_queue.clear(); break; } @@ -2099,17 +2111,72 @@ extern void ppu_precompile(std::vector& dir_queue) continue; } + std::string upper = fmt::to_upper(entry.name); + // Check .sprx filename - if (fmt::to_upper(entry.name).ends_with(".SPRX")) + if (upper.ends_with(".SPRX")) { + // Skip already loaded modules or HLEd ones + if (dir_queue[i] == firmware_sprx_path) + { + bool ignore = false; + + if (loaded_prx) + { + for (auto* obj : *loaded_prx) + { + if (obj->name == entry.name) + { + ignore = true; + break; + } + } + + if (ignore) + { + continue; + } + } + + if (g_cfg.core.libraries_control.get_set().count(entry.name + ":lle")) + { + // Force LLE + ignore = false; + } + else if (g_cfg.core.libraries_control.get_set().count(entry.name + ":hle")) + { + // Force HLE + ignore = true; + } + else + { + extern const std::map g_prx_list; + + // Use list + ignore = g_prx_list.count(entry.name) && g_prx_list.at(entry.name) != 0; + } + + if (ignore) + { + continue; + } + } + // Get full path file_queue.emplace_back(dir_queue[i] + entry.name, 0); - g_progr_ftotal++; + continue; + } + + // Check .self filename + if (upper.ends_with(".SELF")) + { + // Get full path + file_queue.emplace_back(dir_queue[i] + entry.name, 0); continue; } // Check .mself filename - if (fmt::to_upper(entry.name).ends_with(".MSELF")) + if (upper.ends_with(".MSELF")) { if (fs::file mself{dir_queue[i] + entry.name}) { @@ -2125,11 +2192,20 @@ extern void ppu_precompile(std::vector& dir_queue) { std::string name = rec.name; - if (fmt::to_upper(name).ends_with(".SPRX")) + upper = fmt::to_upper(name); + + if (upper.ends_with(".SPRX")) { // .sprx inside .mself found file_queue.emplace_back(dir_queue[i] + entry.name, rec.off); - g_progr_ftotal++; + continue; + } + + if (upper.ends_with(".SELF")) + { + // .self inside .mself found + file_queue.emplace_back(dir_queue[i] + entry.name, rec.off); + continue; } } else @@ -2146,22 +2222,35 @@ extern void ppu_precompile(std::vector& dir_queue) g_progr = "Compiling PPU modules"; + g_progr_ftotal += file_queue.size(); + atomic_t fnext = 0; - shared_mutex sprx_mtx; + shared_mutex sprx_mtx, ovl_mtx; - named_thread_group workers("SPRX Worker ", utils::get_thread_count(), [&] + named_thread_group workers("SPRX Worker ", std::min(utils::get_thread_count(), ::size32(file_queue)), [&] { - for (usz func_i = fnext++; func_i < file_queue.size(); func_i = fnext++) + for (usz func_i = fnext++; func_i < file_queue.size(); func_i = fnext++, g_progr_fdone++) { - std::string path = std::as_const(file_queue)[func_i].first; + if (Emu.IsStopped()) + { + continue; + } - ppu_log.notice("Trying to load SPRX: %s", path); + auto [path, offset] = std::as_const(file_queue)[func_i]; - // Load MSELF or SPRX + ppu_log.notice("Trying to load: %s", path); + + // Load MSELF, SPRX or SELF fs::file src{path}; - if (u64 off = file_queue[func_i].second) + if (!src) + { + ppu_log.error("Failed to open '%s' (%s)", path, fs::g_tls_error); + continue; + } + + if (u64 off = offset) { // Adjust offset for MSELF src.reset(std::make_unique(std::move(src), off)); @@ -2173,9 +2262,15 @@ extern void ppu_precompile(std::vector& dir_queue) // Some files may fail to decrypt due to the lack of klic src = decrypt_self(std::move(src)); - const ppu_prx_object obj = src; + if (!src) + { + ppu_log.error("Failed to decrypt '%s'", path); + continue; + } - if (obj == elf_error::ok) + elf_error prx_err{}, ovl_err{}; + + if (const ppu_prx_object obj = src; (prx_err = obj, obj == elf_error::ok)) { std::unique_lock lock(sprx_mtx); @@ -2188,19 +2283,64 @@ extern void ppu_precompile(std::vector& dir_queue) ppu_unload_prx(*prx); lock.unlock(); ppu_finalize(*prx); - g_progr_fdone++; + continue; + } + + // Log error + prx_err = elf_error::header_type; + } + + if (const ppu_exec_object obj = src; (ovl_err = obj, obj == elf_error::ok)) + { + while (ovl_err == elf_error::ok) + { + // Only one thread compiles OVL atm, other can compile PRX cuncurrently + std::unique_lock lock(ovl_mtx); + + auto [ovlm, error] = ppu_load_overlay(obj, path); + + if (error) + { + // Abort + ovl_err = elf_error::header_type; + break; + } + + ppu_initialize(*ovlm); + + for (auto& seg : ovlm->segs) + { + vm::dealloc(seg.addr); + } + + lock.unlock(); + idm::remove(idm::last_id()); + ppu_finalize(*ovlm); + break; + } + + if (ovl_err == elf_error::ok) + { continue; } } - ppu_log.error("Failed to load SPRX '%s' (%s)", path, obj.get_error()); - g_progr_fdone++; + ppu_log.error("Failed to precompile '%s' (prx: %s, ovl: %s)", path, prx_err, ovl_err); continue; } }); // Join every thread workers.join(); + + // Revert changes + + if (!had_ovl) + { + vm::unmap(0x3000'0000); + } + + g_ps3_process_info.ppc_seg = ppc_seg; } extern void ppu_initialize() @@ -2216,11 +2356,13 @@ extern void ppu_initialize() { return; } + + bool compile_main = false; - // Initialize main module + // Check main module cache if (!_main->segs.empty()) { - ppu_initialize(*_main); + compile_main = ppu_initialize(*_main, true); } std::vector prx_list; @@ -2230,20 +2372,68 @@ extern void ppu_initialize() prx_list.emplace_back(&prx); }); + // If empty we have no indication for cache state, check everything + bool compile_fw = prx_list.empty(); + + // Check preloaded libraries cache + for (auto ptr : prx_list) + { + compile_fw |= ppu_initialize(*ptr, true); + } + + std::vector dir_queue; + + if (compile_fw) + { + const std::string firmware_sprx_path = vfs::get("/dev_flash/sys/external/"); + dir_queue.emplace_back(firmware_sprx_path); + } + + // Avoid compilation if main's cache exists or it is a standalone SELF with no PARAM.SFO + if (compile_main && !Emu.GetTitleID().empty()) + { + dir_queue.emplace_back(vfs::get(Emu.GetDir()) + '/'); + + if (const std::string dev_bdvd = vfs::get("/dev_bdvd/PS3_GAME"); !dev_bdvd.empty()) + { + dir_queue.emplace_back(dev_bdvd + '/'); + } + } + + ppu_precompile(dir_queue, &prx_list); + + if (Emu.IsStopped()) + { + return; + } + + // Initialize main module cache + if (!_main->segs.empty()) + { + ppu_initialize(*_main); + } + // Initialize preloaded libraries for (auto ptr : prx_list) { + if (Emu.IsStopped()) + { + return; + } + ppu_initialize(*ptr); } - - // Initialize SPU cache - spu_cache::initialize(); } -extern void ppu_initialize(const ppu_module& info) +bool ppu_initialize(const ppu_module& info, bool check_only) { if (g_cfg.core.ppu_decoder != ppu_decoder_type::llvm) { + if (check_only) + { + return false; + } + // Temporarily s_ppu_toc = g_fxo->get>(); @@ -2261,7 +2451,7 @@ extern void ppu_initialize(const ppu_module& info) } } - return; + return false; } // Link table @@ -2375,6 +2565,8 @@ extern void ppu_initialize(const ppu_module& info) // Sync variable to acquire workloads atomic_t work_cv = 0; + bool compiled_new = false; + while (jit_mod.vars.empty() && fpos < info.funcs.size()) { // Initialize compiler instance @@ -2581,7 +2773,7 @@ extern void ppu_initialize(const ppu_module& info) // Check object file if (jit_compiler::check(cache_path + obj_name)) { - if (!jit) + if (!jit && !check_only) { ppu_log.success("LLVM: Module exists: %s", obj_name); continue; @@ -2590,6 +2782,14 @@ extern void ppu_initialize(const ppu_module& info) continue; } + // Remember, used in ppu_initialize(void) + compiled_new = true; + + if (check_only) + { + return true; + } + // Adjust information (is_compiled) link_workload.back().second = true; @@ -2651,7 +2851,7 @@ extern void ppu_initialize(const ppu_module& info) if (Emu.IsStopped() || !get_current_cpu_thread()) { - return; + return compiled_new; } for (auto [obj_name, is_compiled] : link_workload) @@ -2672,7 +2872,7 @@ extern void ppu_initialize(const ppu_module& info) if (Emu.IsStopped() || !get_current_cpu_thread()) { - return; + return compiled_new; } // Jit can be null if the loop doesn't ever enter. @@ -2742,6 +2942,8 @@ extern void ppu_initialize(const ppu_module& info) } } } + + return compiled_new; #else fmt::throw_exception("LLVM is not available in this build."); #endif diff --git a/rpcs3/Emu/Cell/lv2/sys_overlay.cpp b/rpcs3/Emu/Cell/lv2/sys_overlay.cpp index cf7e6e3535..3c7609dc9f 100644 --- a/rpcs3/Emu/Cell/lv2/sys_overlay.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_overlay.cpp @@ -12,9 +12,9 @@ #include "sys_overlay.h" #include "sys_fs.h" -extern std::shared_ptr ppu_load_overlay(const ppu_exec_object&, const std::string& path); +extern std::pair, CellError> ppu_load_overlay(const ppu_exec_object&, const std::string& path); -extern void ppu_initialize(const ppu_module&); +extern bool ppu_initialize(const ppu_module&, bool = false); extern void ppu_finalize(const ppu_module&); LOG_CHANNEL(sys_overlay); @@ -42,7 +42,12 @@ static error_code overlay_load_module(vm::ptr ovlmid, const std::string& vp return {CELL_ENOEXEC, obj.operator elf_error()}; } - const auto ovlm = ppu_load_overlay(obj, vfs::get(vpath)); + const auto [ovlm, error] = ppu_load_overlay(obj, vfs::get(vpath)); + + if (error) + { + return error; + } ppu_initialize(*ovlm); diff --git a/rpcs3/Emu/Cell/lv2/sys_prx.cpp b/rpcs3/Emu/Cell/lv2/sys_prx.cpp index 97954a6d0e..1f74cdaab0 100644 --- a/rpcs3/Emu/Cell/lv2/sys_prx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_prx.cpp @@ -17,7 +17,7 @@ extern std::shared_ptr ppu_load_prx(const ppu_prx_object&, const std::string&); extern void ppu_unload_prx(const lv2_prx& prx); -extern void ppu_initialize(const ppu_module&); +extern bool ppu_initialize(const ppu_module&, bool = false); extern void ppu_finalize(const ppu_module&); LOG_CHANNEL(sys_prx); diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index bcbd9b375c..213dd2b274 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -66,8 +66,8 @@ atomic_t g_watchdog_hold_ctr{0}; extern void ppu_load_exec(const ppu_exec_object&); extern void spu_load_exec(const spu_exec_object&); -extern void ppu_precompile(std::vector& dir_queue); -extern void ppu_initialize(const ppu_module&); +extern void ppu_precompile(std::vector& dir_queue, std::vector* loaded_prx); +extern bool ppu_initialize(const ppu_module&, bool = false); extern void ppu_finalize(const ppu_module&); extern void ppu_unload_prx(const lv2_prx&); extern std::shared_ptr ppu_load_prx(const ppu_prx_object&, const std::string&); @@ -1150,7 +1150,12 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool ensure(vm::falloc(0x10000, 0xf0000, vm::main)); } - ppu_precompile(dir_queue); + if (Emu.IsStopped()) + { + return; + } + + ppu_precompile(dir_queue, nullptr); // Exit "process" Emu.CallAfter([]