PPU LLVM: Precompile all executable (PRX, MSELF, overlay) code at startup (#9680)

* Precompile LLVM cache at startup of games, like the GUI "Create PPU Cache" option.
* Allow OVL (overlay) precompilation as well (used by certain games).

Co-authored-by: Megamouse <studienricky89@googlemail.com>
Co-authored-by: Nekotekina <nekotekina@gmail.com>
This commit is contained in:
Eladash 2021-01-30 15:08:22 +02:00 committed by GitHub
parent bb2cc196a6
commit e3b3b0cda7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 288 additions and 46 deletions

View file

@ -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<lv2_overlay> ppu_load_overlay(const ppu_exec_object& elf, const std::string& path)
std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_exec_object& elf, const std::string& path)
{
const auto ovlm = idm::make_ptr<lv2_obj, lv2_overlay>();
// Access linkage information object
const auto link = g_fxo->get<ppu_linkage_info>();
@ -1659,6 +1658,25 @@ std::shared_ptr<lv2_overlay> 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<u32>(prog.p_vaddr), ::narrow<u32>(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<lv2_overlay>();
// Allocate memory at fixed positions
for (const auto& prog : elf.progs)
{
@ -1682,7 +1700,18 @@ std::shared_ptr<lv2_overlay> 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<lv2_overlay> 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<lv2_obj, lv2_overlay>(ovlm);
return {std::move(ovlm), {}};
}