diff --git a/rpcs3/Emu/CPU/CPUThread.cpp b/rpcs3/Emu/CPU/CPUThread.cpp index 74475d0459..8ed5c2d234 100644 --- a/rpcs3/Emu/CPU/CPUThread.cpp +++ b/rpcs3/Emu/CPU/CPUThread.cpp @@ -431,6 +431,11 @@ void cpu_thread::operator()() while (!g_fxo->get()) { + if (Emu.IsStopped()) + { + return; + } + // Can we have a little race, right? First thread is started concurrently with g_fxo->init() std::this_thread::sleep_for(1ms); } diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index 4f44b56307..de5ed47364 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -786,6 +786,27 @@ static void ppu_check_patch_spu_images(const ppu_segment& seg) } } +void try_spawn_ppu_if_exclusive_program(const ppu_module& m) +{ + // If only PRX/OVL has been loaded at Emu.BootGame(), launch a single PPU thread so its memory can be viewed + if (Emu.IsReady() && g_fxo->get()->segs.empty()) + { + ppu_thread_params p + { + .stack_addr = vm::cast(vm::alloc(0x100000, vm::stack, 4096)), + .stack_size = 0x100000, + }; + + auto ppu = idm::make_ptr>("PPU[0x1000000] Thread (test_thread)", p, "test_thread", 0); + + ppu->cmd_push({ppu_cmd::initialize, 0}); + ppu->cia = m.funcs[0].addr; + + // For kernel explorer + g_fxo->init(4096); + } +} + std::shared_ptr ppu_load_prx(const ppu_prx_object& elf, const std::string& path) { // Create new PRX object @@ -1080,18 +1101,7 @@ std::shared_ptr ppu_load_prx(const ppu_prx_object& elf, const std::stri ppu_loader.success("PRX library hash: %s (<- %u)", hash, applied); - if (Emu.IsReady() && g_fxo->get()->segs.empty()) - { - // Special loading mode - ppu_thread_params p{ - .stack_addr = vm::cast(vm::alloc(0x100000, vm::stack, 4096)), - .stack_size = 0x100000, - }; - - auto ppu = idm::make_ptr>("PPU[0x1000000] Thread (test_thread)", p, "test_thread", 0); - - ppu->cmd_push({ppu_cmd::initialize, 0}); - } + try_spawn_ppu_if_exclusive_program(*prx); return prx; } @@ -1129,8 +1139,24 @@ void ppu_unload_prx(const lv2_prx& prx) } } -void ppu_load_exec(const ppu_exec_object& elf) +bool ppu_load_exec(const ppu_exec_object& elf) { + // Check if it is a standalone 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(static_cast(prog.p_vaddr), static_cast(prog.p_memsz)); + + if ((prog.p_vaddr | prog.p_memsz) > UINT32_MAX || !r.valid() || !r.inside(addr_range::start_length(0x00000000, 0x30000000))) + { + return false; + } + } + } + // Set for delayed initialization in ppu_initialize() const auto _main = g_fxo->get(); @@ -1153,6 +1179,27 @@ void ppu_load_exec(const ppu_exec_object& elf) sha1_context sha; sha1_starts(&sha); + struct on_fatal_error + { + ppu_module* _main; + bool errored = true; + + ~on_fatal_error() + { + if (!errored) + { + return; + } + + // Revert previous allocations on an error + for (const auto& seg : _main->segs) + { + vm::dealloc(seg.addr); + } + } + + } error_handler{_main}; + // Allocate memory at fixed positions for (const auto& prog : elf.progs) { @@ -1173,7 +1220,10 @@ void ppu_load_exec(const ppu_exec_object& elf) if (type == 0x1 /* LOAD */ && prog.p_memsz) { if (prog.bin.size() > size || prog.bin.size() != prog.p_filesz) - fmt::throw_exception("Invalid binary size (0x%llx, memsz=0x%x)", prog.bin.size(), size); + { + ppu_loader.fatal("ppu_load_exec(): Invalid binary size (0x%llx, memsz=0x%x)", prog.bin.size(), size); + return false; + } if (!vm::falloc(addr, size, vm::main)) { @@ -1181,7 +1231,8 @@ void ppu_load_exec(const ppu_exec_object& elf) if (!vm::falloc(addr, size)) { - fmt::throw_exception("vm::falloc() failed (addr=0x%x, memsz=0x%x)", addr, size); + ppu_loader.fatal("ppu_load_exec(): vm::falloc() failed (addr=0x%x, memsz=0x%x)", addr, size); + return false; } } @@ -1299,11 +1350,17 @@ void ppu_load_exec(const ppu_exec_object& elf) case 0x00000007: // TLS { + ppu_loader.notice("TLS info segment found: tls-image=*0x%x, image-size=0x%x, tls-size=0x%x", prog.p_vaddr, prog.p_filesz, prog.p_memsz); + + if ((prog.p_vaddr | prog.p_filesz | prog.p_memsz) > UINT32_MAX) + { + ppu_loader.fatal("ppu_load_exec(): TLS segment is invalid!"); + return false; + } + tls_vaddr = vm::cast(prog.p_vaddr); tls_fsize = ::narrow(prog.p_filesz); tls_vsize = ::narrow(prog.p_memsz); - - ppu_loader.notice("TLS info segment found: tls-image=*0x%x, image-size=0x%x, tls-size=0x%x", tls_vaddr, tls_fsize, tls_vsize); break; } @@ -1388,7 +1445,8 @@ void ppu_load_exec(const ppu_exec_object& elf) if (proc_prx_param.magic != 0x1b434cecu) { - fmt::throw_exception("Bad magic! (0x%x)", proc_prx_param.magic); + ppu_loader.fatal("ppu_load_exec(): Bad magic! (0x%x)", proc_prx_param.magic); + return false; } ppu_load_exports(link, proc_prx_param.libent_start, proc_prx_param.libent_end); @@ -1647,6 +1705,9 @@ void ppu_load_exec(const ppu_exec_object& elf) ensure(vm::page_protect(addr, utils::align(size, 0x1000), 0, 0, vm::page_writable)); } } + + error_handler.errored = false; + return true; } std::pair, CellError> ppu_load_overlay(const ppu_exec_object& elf, const std::string& path) @@ -1875,5 +1936,8 @@ std::pair, CellError> ppu_load_overlay(const ppu_ex ovlm->path = path; idm::import_existing(ovlm); + + try_spawn_ppu_if_exclusive_program(*ovlm); + return {std::move(ovlm), {}}; } diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index d11802c0c2..bc4694e52b 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -2089,7 +2089,7 @@ extern void ppu_precompile(std::vector& dir_queue, std::vector> file_queue; file_queue.reserve(2000); - // Find all .sprx files recursively (TODO: process .mself files) + // Find all .sprx files recursively for (usz i = 0; i < dir_queue.size(); i++) { if (Emu.IsStopped()) diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 38d882c8f3..3c9d9e6964 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -18,6 +18,7 @@ #include "Emu/Cell/lv2/sys_memory.h" #include "Emu/Cell/lv2/sys_sync.h" #include "Emu/Cell/lv2/sys_prx.h" +#include "Emu/Cell/lv2/sys_overlay.h" #include "Emu/Cell/lv2/sys_rsx.h" #include "Emu/Cell/Modules/cellMsgDialog.h" @@ -64,13 +65,14 @@ std::string g_cfg_defaults; atomic_t g_watchdog_hold_ctr{0}; -extern void ppu_load_exec(const ppu_exec_object&); +extern bool ppu_load_exec(const ppu_exec_object&); extern void spu_load_exec(const spu_exec_object&); 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&); +extern std::pair, CellError> ppu_load_overlay(const ppu_exec_object&, const std::string& path); fs::file g_tty; atomic_t g_tty_size{0}; @@ -1570,15 +1572,30 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool g_fxo->init(); - ppu_load_exec(ppu_exec); + if (ppu_load_exec(ppu_exec)) + { + ConfigurePPUCache(); - ConfigurePPUCache(); + g_fxo->init(); + Emu.GetCallbacks().init_gs_render(); + Emu.GetCallbacks().init_pad_handler(m_title_id); + Emu.GetCallbacks().init_kb_handler(); + Emu.GetCallbacks().init_mouse_handler(); + } + // Overlay (OVL) executable (only load it) + else if (vm::map(0x3000'0000, 0x1000'0000, 0x200); !ppu_load_overlay(ppu_exec, m_path).first) + { + ppu_exec = fs::file{}; + } - g_fxo->init(); - Emu.GetCallbacks().init_gs_render(); - Emu.GetCallbacks().init_pad_handler(m_title_id); - Emu.GetCallbacks().init_kb_handler(); - Emu.GetCallbacks().init_mouse_handler(); + if (ppu_exec != elf_error::ok) + { + Stop(); + + sys_log.error("Invalid or unsupported PPU executable format: %s", elf_path); + + return game_boot_result::invalid_file_or_folder; + } } else if (ppu_prx.open(elf_file) == elf_error::ok) {