mirror of
https://github.com/RPCSX/rpcsx.git
synced 2026-04-05 22:47:03 +00:00
Savestates Support For PS3 Emulation (#10478)
This commit is contained in:
parent
969b9eb89d
commit
fcd297ffb2
154 changed files with 4948 additions and 635 deletions
|
|
@ -4,6 +4,7 @@
|
|||
#include "Utilities/bin_patch.h"
|
||||
#include "Utilities/StrUtil.h"
|
||||
#include "Utilities/address_range.h"
|
||||
#include "util/serialization.hpp"
|
||||
#include "Crypto/sha1.h"
|
||||
#include "Crypto/unself.h"
|
||||
#include "Loader/ELF.h"
|
||||
|
|
@ -152,7 +153,7 @@ struct ppu_linkage_info
|
|||
};
|
||||
|
||||
// Initialize static modules.
|
||||
static void ppu_initialize_modules(ppu_linkage_info* link)
|
||||
static void ppu_initialize_modules(ppu_linkage_info* link, utils::serial* ar = nullptr)
|
||||
{
|
||||
if (!link->modules.empty())
|
||||
{
|
||||
|
|
@ -280,7 +281,10 @@ static void ppu_initialize_modules(ppu_linkage_info* link)
|
|||
u32& hle_funcs_addr = g_fxo->get<ppu_function_manager>().addr;
|
||||
|
||||
// Allocate memory for the array (must be called after fixed allocations)
|
||||
hle_funcs_addr = vm::alloc(::size32(hle_funcs) * 8, vm::main);
|
||||
if (!hle_funcs_addr)
|
||||
hle_funcs_addr = vm::alloc(::size32(hle_funcs) * 8, vm::main);
|
||||
else
|
||||
vm::page_protect(hle_funcs_addr, utils::align(::size32(hle_funcs) * 8, 0x1000), 0, vm::page_writable);
|
||||
|
||||
// Initialize as PPU executable code
|
||||
ppu_register_range(hle_funcs_addr, ::size32(hle_funcs) * 8);
|
||||
|
|
@ -319,6 +323,71 @@ static void ppu_initialize_modules(ppu_linkage_info* link)
|
|||
ppu_loader.trace("Registered static module: %s", _module->name);
|
||||
}
|
||||
|
||||
struct hle_vars_save
|
||||
{
|
||||
hle_vars_save() = default;
|
||||
|
||||
hle_vars_save(const hle_vars_save&) = delete;
|
||||
|
||||
hle_vars_save& operator =(const hle_vars_save&) = delete;
|
||||
|
||||
hle_vars_save(utils::serial& ar)
|
||||
{
|
||||
auto& manager = ppu_module_manager::get();
|
||||
|
||||
while (true)
|
||||
{
|
||||
const std::string name = ar.operator std::string();
|
||||
|
||||
if (name.empty())
|
||||
{
|
||||
// Null termination
|
||||
break;
|
||||
}
|
||||
|
||||
const auto _module = manager.at(name);
|
||||
|
||||
auto& variable = _module->variables;
|
||||
|
||||
for (u32 i = 0, end = ar.operator usz(); i < end; i++)
|
||||
{
|
||||
auto* ptr = &variable.at(ar.operator u32());
|
||||
ptr->addr = ar.operator u32();
|
||||
ensure(!!ptr->var);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void save(utils::serial& ar)
|
||||
{
|
||||
for (auto& pair : ppu_module_manager::get())
|
||||
{
|
||||
const auto _module = pair.second;
|
||||
|
||||
ar(_module->name);
|
||||
|
||||
ar(_module->variables.size());
|
||||
|
||||
for (auto& variable : _module->variables)
|
||||
{
|
||||
ar(variable.first, variable.second.addr);
|
||||
}
|
||||
}
|
||||
|
||||
// Null terminator
|
||||
ar(std::string{});
|
||||
}
|
||||
};
|
||||
|
||||
if (ar)
|
||||
{
|
||||
g_fxo->init<hle_vars_save>(*ar);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_fxo->init<hle_vars_save>();
|
||||
}
|
||||
|
||||
for (auto& pair : ppu_module_manager::get())
|
||||
{
|
||||
const auto _module = pair.second;
|
||||
|
|
@ -345,7 +414,11 @@ static void ppu_initialize_modules(ppu_linkage_info* link)
|
|||
ppu_loader.trace("** &0x%08X: %s (size=0x%x, align=0x%x)", variable.first, variable.second.name, variable.second.size, variable.second.align);
|
||||
|
||||
// Allocate HLE variable
|
||||
if (variable.second.size >= 0x10000 || variable.second.align >= 0x10000)
|
||||
if (ar)
|
||||
{
|
||||
// Already loaded
|
||||
}
|
||||
else if (variable.second.size >= 0x10000 || variable.second.align >= 0x10000)
|
||||
{
|
||||
variable.second.addr = vm::alloc(variable.second.size, vm::main, std::max<u32>(variable.second.align, 0x10000));
|
||||
}
|
||||
|
|
@ -790,6 +863,49 @@ void ppu_manual_load_imports_exports(u32 imports_start, u32 imports_size, u32 ex
|
|||
ppu_load_imports(_main.relocs, &link, imports_start, imports_start + imports_size);
|
||||
}
|
||||
|
||||
// For savestates
|
||||
extern bool is_memory_read_only_of_executable(u32 addr)
|
||||
{
|
||||
if (g_cfg.savestate.state_inspection_mode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto _main = g_fxo->try_get<ppu_module>();
|
||||
ensure(_main);
|
||||
|
||||
for (const auto& seg : _main->segs)
|
||||
{
|
||||
if (!seg.addr || (seg.flags & 0x2) /* W */)
|
||||
continue;
|
||||
|
||||
if (addr >= seg.addr && addr < (seg.addr + seg.size))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void init_ppu_functions(utils::serial* ar, bool full = false)
|
||||
{
|
||||
g_fxo->need<ppu_linkage_info>();
|
||||
|
||||
if (ar)
|
||||
{
|
||||
ensure(vm::check_addr(g_fxo->init<ppu_function_manager>(*ar)->addr));
|
||||
}
|
||||
else
|
||||
g_fxo->init<ppu_function_manager>();
|
||||
|
||||
if (full)
|
||||
{
|
||||
ensure(ar);
|
||||
|
||||
// Initialize HLE modules
|
||||
ppu_initialize_modules(&g_fxo->get<ppu_linkage_info>(), ar);
|
||||
}
|
||||
}
|
||||
|
||||
static void ppu_check_patch_spu_images(const ppu_segment& seg)
|
||||
{
|
||||
const std::string_view seg_view{vm::get_super_ptr<char>(seg.addr), seg.size};
|
||||
|
|
@ -894,7 +1010,7 @@ 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())
|
||||
if (Emu.IsReady() && g_fxo->get<ppu_module>().segs.empty() && !Emu.DeserialManager())
|
||||
{
|
||||
ppu_thread_params p
|
||||
{
|
||||
|
|
@ -911,7 +1027,7 @@ void try_spawn_ppu_if_exclusive_program(const ppu_module& m)
|
|||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::string& path, s64 file_offset)
|
||||
std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::string& path, s64 file_offset, utils::serial* ar)
|
||||
{
|
||||
if (elf != elf_error::ok)
|
||||
{
|
||||
|
|
@ -919,7 +1035,7 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
|
|||
}
|
||||
|
||||
// Create new PRX object
|
||||
const auto prx = idm::make_ptr<lv2_obj, lv2_prx>();
|
||||
const auto prx = !ar ? idm::make_ptr<lv2_obj, lv2_prx>() : std::make_shared<lv2_prx>();
|
||||
|
||||
// Access linkage information object
|
||||
auto& link = g_fxo->get<ppu_linkage_info>();
|
||||
|
|
@ -957,15 +1073,16 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
|
|||
//const u32 init_addr = ::narrow<u32>(prog.p_vaddr);
|
||||
|
||||
// Alloc segment memory
|
||||
const u32 addr = vm::alloc(mem_size, vm::main);
|
||||
// Or use saved address
|
||||
const u32 addr = !ar ? vm::alloc(mem_size, vm::main) : ar->operator u32();
|
||||
|
||||
if (!addr)
|
||||
if (!vm::check_addr(addr))
|
||||
{
|
||||
fmt::throw_exception("vm::alloc() failed (size=0x%x)", mem_size);
|
||||
}
|
||||
|
||||
// Copy segment data
|
||||
std::memcpy(vm::base(addr), prog.bin.data(), file_size);
|
||||
if (!ar) std::memcpy(vm::base(addr), prog.bin.data(), file_size);
|
||||
ppu_loader.warning("**** Loaded to 0x%x...0x%x (size=0x%x)", addr, addr + mem_size - 1, mem_size);
|
||||
|
||||
// Hash segment
|
||||
|
|
@ -1068,6 +1185,11 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
|
|||
const u64 rdata = _rel.data = data_base + rel.ptr.addr();
|
||||
prx->relocs.emplace_back(_rel);
|
||||
|
||||
if (ar)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
switch (rtype)
|
||||
{
|
||||
case 1: // R_PPC64_ADDR32
|
||||
|
|
@ -1293,7 +1415,7 @@ void ppu_unload_prx(const lv2_prx& prx)
|
|||
}
|
||||
}
|
||||
|
||||
bool ppu_load_exec(const ppu_exec_object& elf)
|
||||
bool ppu_load_exec(const ppu_exec_object& elf, utils::serial* ar)
|
||||
{
|
||||
if (elf != elf_error::ok)
|
||||
{
|
||||
|
|
@ -1316,8 +1438,7 @@ bool ppu_load_exec(const ppu_exec_object& elf)
|
|||
}
|
||||
}
|
||||
|
||||
g_fxo->need<ppu_linkage_info>();
|
||||
g_fxo->need<ppu_function_manager>();
|
||||
init_ppu_functions(ar, false);
|
||||
|
||||
// Set for delayed initialization in ppu_initialize()
|
||||
auto& _main = g_fxo->get<ppu_module>();
|
||||
|
|
@ -1390,7 +1511,17 @@ bool ppu_load_exec(const ppu_exec_object& elf)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!vm::falloc(addr, size, vm::main))
|
||||
const bool already_loaded = ar && (_seg.flags & 0x2);
|
||||
|
||||
if (already_loaded)
|
||||
{
|
||||
if (!vm::check_addr(addr, vm::page_readable, size))
|
||||
{
|
||||
ppu_loader.fatal("ppu_load_exec(): Archived PPU executable memory has not been found! (addr=0x%x, memsz=0x%x)", addr, size);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!vm::falloc(addr, size, vm::main))
|
||||
{
|
||||
ppu_loader.error("vm::falloc(vm::main) failed (addr=0x%x, memsz=0x%x)", addr, size); // TODO
|
||||
|
||||
|
|
@ -1402,7 +1533,18 @@ bool ppu_load_exec(const ppu_exec_object& elf)
|
|||
}
|
||||
|
||||
// Copy segment data, hash it
|
||||
std::memcpy(vm::base(addr), prog.bin.data(), prog.bin.size());
|
||||
if (!already_loaded)
|
||||
{
|
||||
std::memcpy(vm::base(addr), prog.bin.data(), prog.bin.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
// For backwards compatibility: already loaded memory will always be writable
|
||||
const u32 size0 = utils::align(size + addr % 0x10000, 0x10000);
|
||||
const u32 addr0 = addr & -0x10000;
|
||||
vm::page_protect(addr0, size0, 0, vm::page_writable | vm::page_readable, vm::page_executable);
|
||||
}
|
||||
|
||||
sha1_update(&sha, reinterpret_cast<const uchar*>(&prog.p_vaddr), sizeof(prog.p_vaddr));
|
||||
sha1_update(&sha, reinterpret_cast<const uchar*>(&prog.p_memsz), sizeof(prog.p_memsz));
|
||||
sha1_update(&sha, prog.bin.data(), prog.bin.size());
|
||||
|
|
@ -1476,7 +1618,7 @@ bool ppu_load_exec(const ppu_exec_object& elf)
|
|||
}
|
||||
|
||||
// Initialize HLE modules
|
||||
ppu_initialize_modules(&link);
|
||||
ppu_initialize_modules(&link, ar);
|
||||
|
||||
// Embedded SPU elf patching
|
||||
for (const auto& seg : _main.segs)
|
||||
|
|
@ -1641,6 +1783,46 @@ bool ppu_load_exec(const ppu_exec_object& elf)
|
|||
}
|
||||
}
|
||||
|
||||
// Initialize memory stats (according to sdk version)
|
||||
u32 mem_size;
|
||||
if (g_ps3_process_info.get_cellos_appname() == "vsh.self"sv)
|
||||
{
|
||||
// Because vsh.self comes before any generic application, more memory is available to it
|
||||
mem_size = 0xF000000;
|
||||
}
|
||||
else if (sdk_version > 0x0021FFFF)
|
||||
{
|
||||
mem_size = 0xD500000;
|
||||
}
|
||||
else if (sdk_version > 0x00192FFF)
|
||||
{
|
||||
mem_size = 0xD300000;
|
||||
}
|
||||
else if (sdk_version > 0x0018FFFF)
|
||||
{
|
||||
mem_size = 0xD100000;
|
||||
}
|
||||
else if (sdk_version > 0x0017FFFF)
|
||||
{
|
||||
mem_size = 0xD000000;
|
||||
}
|
||||
else if (sdk_version > 0x00154FFF)
|
||||
{
|
||||
mem_size = 0xCC00000;
|
||||
}
|
||||
else
|
||||
{
|
||||
mem_size = 0xC800000;
|
||||
}
|
||||
|
||||
if (g_cfg.core.debug_console_mode)
|
||||
{
|
||||
// TODO: Check for all sdk versions
|
||||
mem_size += 0xC000000;
|
||||
}
|
||||
|
||||
if (!ar) g_fxo->init<lv2_memory_container>(mem_size);
|
||||
|
||||
// Initialize process
|
||||
std::vector<std::shared_ptr<lv2_prx>> loaded_modules;
|
||||
|
||||
|
|
@ -1658,9 +1840,9 @@ bool ppu_load_exec(const ppu_exec_object& elf)
|
|||
load_libs.emplace("libsysmodule.sprx");
|
||||
}
|
||||
|
||||
if (g_ps3_process_info.get_cellos_appname() == "vsh.self"sv)
|
||||
if (ar || g_ps3_process_info.get_cellos_appname() == "vsh.self"sv)
|
||||
{
|
||||
// Cannot be used with vsh.self (it self-manages itself)
|
||||
// Cannot be used with vsh.self or savestates (they self-manage itself)
|
||||
load_libs.clear();
|
||||
}
|
||||
|
||||
|
|
@ -1676,6 +1858,25 @@ bool ppu_load_exec(const ppu_exec_object& elf)
|
|||
// Program entry
|
||||
u32 entry = 0;
|
||||
|
||||
// Set path (TODO)
|
||||
_main.name.clear();
|
||||
_main.path = vfs::get(Emu.argv[0]);
|
||||
|
||||
// Analyse executable (TODO)
|
||||
_main.analyse(0, static_cast<u32>(elf.header.e_entry), end, applied);
|
||||
|
||||
// Validate analyser results (not required)
|
||||
_main.validate(0);
|
||||
|
||||
// Set SDK version
|
||||
g_ps3_process_info.sdk_ver = sdk_version;
|
||||
|
||||
// Set ppc fixed allocations segment permission
|
||||
g_ps3_process_info.ppc_seg = ppc_seg;
|
||||
|
||||
void init_fxo_for_exec(utils::serial* ar, bool full);
|
||||
init_fxo_for_exec(ar, false);
|
||||
|
||||
if (!load_libs.empty())
|
||||
{
|
||||
for (const auto& name : load_libs)
|
||||
|
|
@ -1686,7 +1887,7 @@ bool ppu_load_exec(const ppu_exec_object& elf)
|
|||
{
|
||||
ppu_loader.warning("Loading library: %s", name);
|
||||
|
||||
auto prx = ppu_load_prx(obj, lle_dir + name, 0);
|
||||
auto prx = ppu_load_prx(obj, lle_dir + name, 0, nullptr);
|
||||
|
||||
if (prx->funcs.empty())
|
||||
{
|
||||
|
|
@ -1715,21 +1916,11 @@ bool ppu_load_exec(const ppu_exec_object& elf)
|
|||
}
|
||||
}
|
||||
|
||||
// Set path (TODO)
|
||||
_main.name.clear();
|
||||
_main.path = vfs::get(Emu.argv[0]);
|
||||
|
||||
// Analyse executable (TODO)
|
||||
_main.analyse(0, static_cast<u32>(elf.header.e_entry), end, applied);
|
||||
|
||||
// Validate analyser results (not required)
|
||||
_main.validate(0);
|
||||
|
||||
// Set SDK version
|
||||
g_ps3_process_info.sdk_ver = sdk_version;
|
||||
|
||||
// Set ppc fixed allocations segment permission
|
||||
g_ps3_process_info.ppc_seg = ppc_seg;
|
||||
if (ar)
|
||||
{
|
||||
error_handler.errored = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ppc_seg != 0x0)
|
||||
{
|
||||
|
|
@ -1804,44 +1995,6 @@ bool ppu_load_exec(const ppu_exec_object& elf)
|
|||
ppu->gpr[1] -= Emu.data.size();
|
||||
}
|
||||
|
||||
// Initialize memory stats (according to sdk version)
|
||||
u32 mem_size;
|
||||
if (g_ps3_process_info.get_cellos_appname() == "vsh.self"sv)
|
||||
{
|
||||
// Because vsh.self comes before any generic application, more memory is available to it
|
||||
mem_size = 0xF000000;
|
||||
}
|
||||
else if (sdk_version > 0x0021FFFF)
|
||||
{
|
||||
mem_size = 0xD500000;
|
||||
}
|
||||
else if (sdk_version > 0x00192FFF)
|
||||
{
|
||||
mem_size = 0xD300000;
|
||||
}
|
||||
else if (sdk_version > 0x0018FFFF)
|
||||
{
|
||||
mem_size = 0xD100000;
|
||||
}
|
||||
else if (sdk_version > 0x0017FFFF)
|
||||
{
|
||||
mem_size = 0xD000000;
|
||||
}
|
||||
else if (sdk_version > 0x00154FFF)
|
||||
{
|
||||
mem_size = 0xCC00000;
|
||||
}
|
||||
else
|
||||
{
|
||||
mem_size = 0xC800000;
|
||||
}
|
||||
|
||||
if (g_cfg.core.debug_console_mode)
|
||||
{
|
||||
// TODO: Check for all sdk versions
|
||||
mem_size += 0xC000000;
|
||||
}
|
||||
|
||||
if (Emu.init_mem_containers)
|
||||
{
|
||||
// Refer to sys_process_exit2 for explanation
|
||||
|
|
@ -1913,7 +2066,7 @@ bool ppu_load_exec(const ppu_exec_object& elf)
|
|||
return true;
|
||||
}
|
||||
|
||||
std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_exec_object& elf, const std::string& path, s64 file_offset)
|
||||
std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_exec_object& elf, const std::string& path, s64 file_offset, utils::serial* ar)
|
||||
{
|
||||
if (elf != elf_error::ok)
|
||||
{
|
||||
|
|
@ -1975,7 +2128,17 @@ std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_ex
|
|||
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);
|
||||
|
||||
if (!vm::get(vm::any, 0x30000000)->falloc(addr, size))
|
||||
const bool already_loaded = ar /*&& !!(_seg.flags & 0x2)*/;
|
||||
|
||||
if (already_loaded)
|
||||
{
|
||||
if (!vm::check_addr(addr, vm::page_readable, size))
|
||||
{
|
||||
ppu_loader.fatal("ppu_load_overlay(): Archived PPU overlay memory has not been found! (addr=0x%x, memsz=0x%x)", addr, size);
|
||||
return {nullptr, CELL_EABORT};
|
||||
}
|
||||
}
|
||||
else if (!vm::get(vm::any, 0x30000000)->falloc(addr, size))
|
||||
{
|
||||
ppu_loader.error("ppu_load_overlay(): vm::falloc() failed (addr=0x%x, memsz=0x%x)", addr, size);
|
||||
|
||||
|
|
@ -1990,7 +2153,7 @@ std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_ex
|
|||
}
|
||||
|
||||
// Copy segment data, hash it
|
||||
std::memcpy(vm::base(addr), prog.bin.data(), prog.bin.size());
|
||||
if (!already_loaded) std::memcpy(vm::base(addr), prog.bin.data(), prog.bin.size());
|
||||
sha1_update(&sha, reinterpret_cast<const uchar*>(&prog.p_vaddr), sizeof(prog.p_vaddr));
|
||||
sha1_update(&sha, reinterpret_cast<const uchar*>(&prog.p_memsz), sizeof(prog.p_memsz));
|
||||
sha1_update(&sha, prog.bin.data(), prog.bin.size());
|
||||
|
|
@ -2158,9 +2321,11 @@ std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_ex
|
|||
// Validate analyser results (not required)
|
||||
ovlm->validate(0);
|
||||
|
||||
idm::import_existing<lv2_obj, lv2_overlay>(ovlm);
|
||||
|
||||
try_spawn_ppu_if_exclusive_program(*ovlm);
|
||||
if (!ar)
|
||||
{
|
||||
idm::import_existing<lv2_obj, lv2_overlay>(ovlm);
|
||||
try_spawn_ppu_if_exclusive_program(*ovlm);
|
||||
}
|
||||
|
||||
return {std::move(ovlm), {}};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue