Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reimplement/fix PRX patches #9746

Merged
merged 2 commits into from Feb 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
48 changes: 20 additions & 28 deletions Utilities/bin_patch.cpp
Expand Up @@ -505,39 +505,26 @@ void patch_engine::append_title_patches(const std::string& title_id)
load(m_map, get_patches_path() + title_id + "_patch.yml");
}

std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst)
{
return apply_patch<false>(name, dst, 0, 0);
}

std::basic_string<u32> patch_engine::apply_with_ls_check(const std::string& name, u8* dst, u32 filesz, u32 ls_addr)
{
return apply_patch<true>(name, dst, filesz, ls_addr);
}

template <bool CheckLS>
static std::basic_string<u32> apply_modification(const patch_engine::patch_info& patch, u8* dst, u32 filesz, u32 ls_addr)
static std::basic_string<u32> apply_modification(const patch_engine::patch_info& patch, u8* dst, u32 filesz, u32 min_addr)
{
std::basic_string<u32> applied;

for (const auto& p : patch.data_list)
{
u32 offset = p.offset;
u32 resval = 0;

if constexpr (CheckLS)
if (offset < min_addr || offset - min_addr >= filesz)
{
if (offset < ls_addr || offset >= (ls_addr + filesz))
{
// This patch is out of range for this segment
continue;
}

offset -= ls_addr;
// This patch is out of range for this segment
continue;
}

offset -= min_addr;

auto ptr = dst + offset;

u32 resval = -1;

switch (p.type)
{
case patch_type::invalid:
Expand Down Expand Up @@ -584,10 +571,7 @@ static std::basic_string<u32> apply_modification(const patch_engine::patch_info&
case patch_type::be32:
{
*reinterpret_cast<be_t<u32, 1>*>(ptr) = static_cast<u32>(p.value.long_value);

// Possibly an executable instruction
if constexpr (!CheckLS)
resval = offset;
if (offset % 4 == 0) resval = offset;
break;
}
case patch_type::bef32:
Expand All @@ -598,6 +582,14 @@ static std::basic_string<u32> apply_modification(const patch_engine::patch_info&
case patch_type::be64:
{
*reinterpret_cast<be_t<u64, 1>*>(ptr) = static_cast<u64>(p.value.long_value);

if (offset % 4)
{
break;
}

resval = offset;
applied.push_back((offset + 7) & -4); // Two 32-bit locations
break;
}
case patch_type::bef64:
Expand All @@ -607,14 +599,14 @@ static std::basic_string<u32> apply_modification(const patch_engine::patch_info&
}
}

// Possibly an executable instruction
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of aligning them down, seems better to not add unaligned patches to resval at all.

applied.push_back(resval);
}

return applied;
}

template <bool CheckLS>
std::basic_string<u32> patch_engine::apply_patch(const std::string& name, u8* dst, u32 filesz, u32 ls_addr)
std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst, u32 filesz, u32 min_addr)
{
if (m_map.find(name) == m_map.cend())
{
Expand Down Expand Up @@ -719,7 +711,7 @@ std::basic_string<u32> patch_engine::apply_patch(const std::string& name, u8* ds
m_applied_groups.insert(patch.patch_group);
}

auto applied = apply_modification<CheckLS>(patch, dst, filesz, ls_addr);
auto applied = apply_modification(patch, dst, filesz, min_addr);

applied_total += applied;

Expand Down
9 changes: 1 addition & 8 deletions Utilities/bin_patch.h
Expand Up @@ -129,16 +129,9 @@ class patch_engine
void append_title_patches(const std::string& title_id);

// Apply patch (returns the number of entries applied)
std::basic_string<u32> apply(const std::string& name, u8* dst);

// Apply patch with a check that the address exists in SPU local storage
std::basic_string<u32> apply_with_ls_check(const std::string& name, u8* dst, u32 filesz, u32 ls_addr);
std::basic_string<u32> apply(const std::string& name, u8* dst, u32 filesz = UINT32_MAX, u32 min_addr = 0);

private:
// Internal: Apply patch (returns the number of entries applied)
template <bool CheckLS>
std::basic_string<u32> apply_patch(const std::string& name, u8* dst, u32 filesz, u32 ls_addr);

// Database
patch_map m_map;

Expand Down
50 changes: 30 additions & 20 deletions rpcs3/Emu/Cell/PPUModule.cpp
Expand Up @@ -315,18 +315,18 @@ 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 >= 4096 || variable.second.align >= 4096)
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));
}
else
{
const u32 next = utils::align(alloc_addr, variable.second.align);
const u32 end = next + variable.second.size;
const u32 end = next + variable.second.size - 1;

if (!next || (end >> 12 != alloc_addr >> 12))
if (!next || (end >> 16 != alloc_addr >> 16))
{
alloc_addr = vm::alloc(4096, vm::main);
alloc_addr = vm::alloc(0x10000, vm::main);
}
else
{
Expand Down Expand Up @@ -773,12 +773,12 @@ static void ppu_check_patch_spu_images(const ppu_segment& seg)
for (const auto& prog : obj.progs)
{
// Apply the patch
applied += g_fxo->get<patch_engine>()->apply_with_ls_check(hash, (elf_header + prog.p_offset), prog.p_filesz, prog.p_vaddr);
applied += g_fxo->get<patch_engine>()->apply(hash, (elf_header + prog.p_offset), prog.p_filesz, prog.p_vaddr);

if (!Emu.GetTitleID().empty())
{
// Alternative patch
applied += g_fxo->get<patch_engine>()->apply_with_ls_check(Emu.GetTitleID() + '-' + hash, (elf_header + prog.p_offset), prog.p_filesz, prog.p_vaddr);
applied += g_fxo->get<patch_engine>()->apply(Emu.GetTitleID() + '-' + hash, (elf_header + prog.p_offset), prog.p_filesz, prog.p_vaddr);
}
}

Expand Down Expand Up @@ -1084,21 +1084,33 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
sha1_finish(&sha, prx->sha1);

// Format patch name
std::string hash("PRX-0000000000000000000000000000000000000000");
for (u32 i = 0; i < 20; i++)
{
constexpr auto pal = "0123456789abcdef";
hash[4 + i * 2] = pal[prx->sha1[i] >> 4];
hash[5 + i * 2] = pal[prx->sha1[i] & 15];
}
std::string hash = fmt::format("PRX-%s", fmt::base57(prx->sha1));

// Apply the patch
auto applied = g_fxo->get<patch_engine>()->apply(hash, vm::g_base_addr);
std::basic_string<u32> applied;

if (!Emu.GetTitleID().empty())
for (usz i = 0; i < prx->segs.size(); i++)
{
// Alternative patch
applied += g_fxo->get<patch_engine>()->apply(Emu.GetTitleID() + '-' + hash, vm::g_base_addr);
const auto& seg = prx->segs[i];

if (!seg.size) continue;

const std::string hash_seg = fmt::format("%s-%u", hash, i);

// Apply the patch
auto _applied = g_fxo->get<patch_engine>()->apply(hash_seg, vm::get_super_ptr(seg.addr), seg.size);

if (!Emu.GetTitleID().empty())
{
// Alternative patch
_applied += g_fxo->get<patch_engine>()->apply(Emu.GetTitleID() + '-' + hash_seg, vm::get_super_ptr(seg.addr), seg.size);
}

// Rebase patch offsets
std::for_each(_applied.begin(), _applied.end(), [&](u32& res) { if (res != umax) res += seg.addr; });

applied += _applied;

ppu_loader.success("PRX library hash: %s (<- %u)", hash_seg, _applied.size());
}

// Embedded SPU elf patching
Expand All @@ -1109,8 +1121,6 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri

prx->analyse(toc, 0, end, applied);

ppu_loader.success("PRX library hash: %s (<- %u)", hash, applied.size());

try_spawn_ppu_if_exclusive_program(*prx);

return prx;
Expand Down
9 changes: 5 additions & 4 deletions rpcs3/Emu/Cell/PPUThread.cpp
Expand Up @@ -385,15 +385,16 @@ extern void ppu_register_range(u32 addr, u32 size)
return;
}

size = utils::align(size + addr % 0x10000, 0x10000);
addr &= -0x10000;

// Register executable range at
utils::memory_commit(&ppu_ref(addr), size * 2, utils::protection::rw);
vm::page_protect(addr, utils::align(size, 0x10000), 0, vm::page_executable);
utils::memory_commit(&ppu_ref(addr), u64{size} * 2, utils::protection::rw);
vm::page_protect(addr, size, 0, vm::page_executable);

const u64 fallback = reinterpret_cast<uptr>(ppu_fallback);
const u64 seg_base = addr;

size &= ~3; // Loop assumes `size = n * 4`, enforce that by rounding down

while (size)
{
if (g_cfg.core.ppu_decoder == ppu_decoder_type::llvm)
Expand Down
30 changes: 22 additions & 8 deletions rpcs3/rpcs3qt/kernel_explorer.cpp
Expand Up @@ -290,6 +290,18 @@ void kernel_explorer::Update()

add_solid_node(m_tree, find_node(m_tree, additional_nodes::process_info), qstr(fmt::format("Process Info, Sdk Version: 0x%08x, PPC SEG: %#x, SFO Category: %s", g_ps3_process_info.sdk_ver, g_ps3_process_info.ppc_seg, Emu.GetCat())));

auto display_program_segments = [this](QTreeWidgetItem* tree, const ppu_module& m)
{
for (usz i = 0; i < m.segs.size(); i++)
{
const u32 addr = m.segs[i].addr;
const u32 size = m.segs[i].size;

add_leaf(tree, qstr(fmt::format("Segment %u: (0x%08x...0x%08x), Flags: 0x%x"
, i, addr, addr + std::max<u32>(size, 1) - 1, m.segs[i].flags)));
}
};

idm::select<lv2_obj>([&](u32 id, lv2_obj& obj)
{
auto node = find_node(m_tree, id >> 24);
Expand Down Expand Up @@ -366,16 +378,16 @@ void kernel_explorer::Update()
case SYS_PRX_OBJECT:
{
auto& prx = static_cast<lv2_prx&>(obj);
const u32 addr0 = !prx.segs.empty() ? prx.segs[0].addr : 0;

if (!addr0)
if (prx.segs.empty())
{
add_leaf(node, qstr(fmt::format("PRX 0x%08x: '%s' (HLE)", id, prx.name)));
break;
}

const u32 end0 = addr0 + prx.segs[0].size - 1;
add_leaf(node, qstr(fmt::format("PRX 0x%08x: '%s', Seg0: (0x%x...0x%x)", id, prx.name, addr0, end0)));
const QString text = qstr(fmt::format("PRX 0x%08x: '%s'", id, prx.name));
QTreeWidgetItem* prx_tree = add_solid_node(m_tree, node, text, text);
display_program_segments(prx_tree, prx);
break;
}
case SYS_SPUPORT_OBJECT:
Expand All @@ -386,7 +398,9 @@ void kernel_explorer::Update()
case SYS_OVERLAY_OBJECT:
{
auto& ovl = static_cast<lv2_overlay&>(obj);
add_leaf(node, qstr(fmt::format("OVL 0x%08x: '%s', Seg0: (0x%x...0x%x)", id, ovl.name, ovl.segs[0].addr, ovl.segs[0].addr + ovl.segs[0].size - 1)));
const QString text = qstr(fmt::format("OVL 0x%08x: '%s'", id, ovl.name));
QTreeWidgetItem* ovl_tree = add_solid_node(m_tree, node, text, text);
display_program_segments(ovl_tree, ovl);
break;
}
case SYS_LWMUTEX_OBJECT:
Expand Down Expand Up @@ -534,9 +548,9 @@ void kernel_explorer::Update()

if (pspurs && pspurs.try_read(spurs))
{
const QString branch_name = "SPURS";
const QString branch_name = tr("SPURS %1").arg(pspurs.addr());
const u32 wklEnabled = spurs.wklEnabled;
QTreeWidgetItem* spurs_tree = add_solid_node(m_tree, spu_tree, branch_name, branch_name + qstr(fmt::format(", Instance: *0x%x, LWMutex: 0x%x, LWCond: 0x%x, wklEnabled: 0x%x"
QTreeWidgetItem* spurs_tree = add_solid_node(m_tree, spu_tree, branch_name, qstr(fmt::format("SPURS, Instance: *0x%x, LWMutex: 0x%x, LWCond: 0x%x, wklEnabled: 0x%x"
, pspurs, spurs.mutex.sleep_queue, spurs.cond.lwcond_queue, wklEnabled)));

const u32 signal_mask = u32{spurs.wklSignal1} << 16 | spurs.wklSignal2;
Expand All @@ -561,7 +575,7 @@ void kernel_explorer::Update()
const u8 status = spurs.wklStatus(wid);
const auto has_signal = (signal_mask & (0x80000000u >> wid)) ? "Signalled"sv : "No Signal"sv;

QTreeWidgetItem* wkl_tree = add_solid_node(m_tree, spurs_tree, qstr(fmt::format("Work.%u", wid)), qstr(fmt::format("Work.%u, class: %s, %s, %s, Status: %#x, Event: %#x, %s, ReadyCnt: %u", wid, +name.nameClass, +name.nameInstance, state, status, evt, has_signal, ready_count)));
QTreeWidgetItem* wkl_tree = add_solid_node(m_tree, spurs_tree, branch_name + qstr(fmt::format(" Work.%u", wid)), qstr(fmt::format("Work.%u, class: %s, %s, %s, Status: %#x, Event: %#x, %s, ReadyCnt: %u", wid, +name.nameClass, +name.nameInstance, state, status, evt, has_signal, ready_count)));

auto contention = [&](u8 v)
{
Expand Down