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

@ -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), {}};
}