Implement standalone OVL (overlay) loading mode

* Allow to load OVL alone.
* Add error checks in ppu_load_exec(), do not crash on error.
* Fix crash on exit from standalone PRX mode, allow kernel explorer to work with it as well for the added OVL mode.
This commit is contained in:
Eladash 2021-01-30 16:25:21 +02:00 committed by Ivan
parent 9077563dac
commit 82c86ed2f7
4 changed files with 113 additions and 27 deletions

View File

@ -431,6 +431,11 @@ void cpu_thread::operator()()
while (!g_fxo->get<cpu_profiler>())
{
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);
}

View File

@ -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<ppu_module>()->segs.empty())
{
ppu_thread_params p
{
.stack_addr = vm::cast(vm::alloc(0x100000, vm::stack, 4096)),
.stack_size = 0x100000,
};
auto ppu = idm::make_ptr<named_thread<ppu_thread>>("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<lv2_memory_container>(4096);
}
}
std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::string& path)
{
// Create new PRX object
@ -1080,18 +1101,7 @@ std::shared_ptr<lv2_prx> 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<ppu_module>()->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<named_thread<ppu_thread>>("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<u32>(prog.p_vaddr), static_cast<u32>(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<ppu_module>();
@ -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<u32>(prog.p_filesz);
tls_vsize = ::narrow<u32>(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<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_exec_object& elf, const std::string& path)
@ -1875,5 +1936,8 @@ std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_ex
ovlm->path = path;
idm::import_existing<lv2_obj, lv2_overlay>(ovlm);
try_spawn_ppu_if_exclusive_program(*ovlm);
return {std::move(ovlm), {}};
}

View File

@ -2089,7 +2089,7 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<lv2_
std::vector<std::pair<std::string, u64>> 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())

View File

@ -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<u64> 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<std::string>& dir_queue, std::vector<lv2_prx*>* 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<lv2_prx> ppu_load_prx(const ppu_prx_object&, const std::string&);
extern std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_exec_object&, const std::string& path);
fs::file g_tty;
atomic_t<s64> 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_module>();
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)
{