rpcs3/rpcs3/Emu/SysCalls/Modules.cpp
2015-03-07 01:10:04 +03:00

602 lines
12 KiB
C++

#include "stdafx.h"
#include "Ini.h"
#include "Utilities/Log.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "Emu/SysCalls/Modules.h"
#include "Emu/SysCalls/SysCalls.h"
#include "Emu/SysCalls/CB_FUNC.h"
#include "Crypto/sha1.h"
#include "ModuleManager.h"
#include "Emu/Cell/PPUInstrTable.h"
std::vector<ModuleFunc> g_ppu_func_list;
std::vector<StaticFunc> g_ppu_func_subs;
u32 add_ppu_func(ModuleFunc func)
{
for (auto& f : g_ppu_func_list)
{
assert(f.id != func.id);
}
g_ppu_func_list.push_back(func);
return (u32)g_ppu_func_list.size() - 1;
}
u32 add_ppu_func_sub(StaticFunc func)
{
g_ppu_func_subs.push_back(func);
return func.index;
}
u32 add_ppu_func_sub(const char group[8], const SearchPatternEntry ops[], const size_t count, const char* name, Module* module, ppu_func_caller func)
{
char group_name[9] = {};
if (group)
{
strcpy_trunc(group_name, group);
}
StaticFunc sf;
sf.index = add_ppu_func(ModuleFunc(get_function_id(name), 0, module, func));
sf.name = name;
sf.group = *(u64*)group_name;
sf.found = 0;
for (u32 i = 0; i < count; i++)
{
SearchPatternEntry op;
op.type = ops[i].type;
op.data = re32(ops[i].data);
op.mask = re32(ops[i].mask);
op.num = ops[i].num;
assert(!op.mask || (op.data & ~op.mask) == 0);
sf.ops.push_back(op);
}
return add_ppu_func_sub(sf);
}
ModuleFunc* get_ppu_func_by_nid(u32 nid, u32* out_index)
{
for (auto& f : g_ppu_func_list)
{
if (f.id == nid)
{
if (out_index)
{
*out_index = (u32)(&f - g_ppu_func_list.data());
}
return &f;
}
}
return nullptr;
}
ModuleFunc* get_ppu_func_by_index(u32 index)
{
index &= ~EIF_FLAGS;
if (index >= g_ppu_func_list.size())
{
return nullptr;
}
return &g_ppu_func_list[index];
}
void execute_ppu_func_by_index(PPUThread& CPU, u32 index)
{
if (auto func = get_ppu_func_by_index(index))
{
auto old_last_syscall = CPU.m_last_syscall;
CPU.m_last_syscall = func->id;
if (index & EIF_SAVE_RTOC)
{
// save RTOC if necessary
vm::write64(vm::cast(CPU.GPR[1] + 0x28), CPU.GPR[2]);
}
if (func->lle_func && !(func->flags & MFF_FORCED_HLE))
{
// call LLE function if available
if (Ini.HLELogging.GetValue())
{
LOG_NOTICE(HLE, "LLE function called: %s", SysCalls::GetHLEFuncName(func->id));
}
func->lle_func(CPU);
if (Ini.HLELogging.GetValue())
{
LOG_NOTICE(HLE, "LLE function finished: %s -> 0x%llx", SysCalls::GetHLEFuncName(func->id), CPU.GPR[3]);
}
}
else if (func->func)
{
if (Ini.HLELogging.GetValue())
{
LOG_NOTICE(HLE, "HLE function called: %s", SysCalls::GetHLEFuncName(func->id));
}
func->func(CPU);
if (Ini.HLELogging.GetValue())
{
LOG_NOTICE(HLE, "HLE function finished: %s -> 0x%llx", SysCalls::GetHLEFuncName(func->id), CPU.GPR[3]);
}
}
else
{
LOG_ERROR(HLE, "Unimplemented function: %s -> CELL_OK", SysCalls::GetHLEFuncName(func->id));
CPU.GPR[3] = 0;
}
if (index & EIF_PERFORM_BLR)
{
// return if necessary
CPU.SetBranch(vm::cast(CPU.LR & ~3), true);
}
CPU.m_last_syscall = old_last_syscall;
}
else
{
throw "Invalid function index";
}
}
void clear_ppu_functions()
{
g_ppu_func_list.clear();
g_ppu_func_subs.clear();
}
u32 get_function_id(const char* name)
{
const char* suffix = "\x67\x59\x65\x99\x04\x25\x04\x90\x56\x64\x27\x49\x94\x89\x74\x1A"; // Symbol name suffix
u8 output[20];
// Compute SHA-1 hash
sha1_context ctx;
sha1_starts(&ctx);
sha1_update(&ctx, (const u8*)name, strlen(name));
sha1_update(&ctx, (const u8*)suffix, strlen(suffix));
sha1_finish(&ctx, output);
return (u32&)output[0];
}
void hook_ppu_func(vm::ptr<u32> base, u32 pos, u32 size)
{
using namespace PPU_instr;
for (auto& sub : g_ppu_func_subs)
{
bool found = true;
for (u32 k = pos, x = 0; x + 1 <= sub.ops.size(); k++, x++)
{
if (k >= size)
{
found = false;
break;
}
// skip NOP
if (base[k].data() == se32(0x60000000))
{
x--;
continue;
}
const u32 data = sub.ops[x].data;
const u32 mask = sub.ops[x].mask;
const bool match = (base[k].data() & mask) == data;
switch (sub.ops[x].type)
{
case SPET_MASKED_OPCODE:
{
// masked pattern
if (!match)
{
found = false;
}
break;
}
case SPET_OPTIONAL_MASKED_OPCODE:
{
// optional masked pattern
if (!match)
{
k--;
}
break;
}
case SPET_LABEL:
{
const auto addr = (base + k--).addr();
const auto lnum = data;
const auto label = sub.labels.find(lnum);
if (label == sub.labels.end()) // register the label
{
sub.labels[lnum] = addr;
}
else if (label->second != addr) // or check registered label
{
found = false;
}
break;
}
case SPET_BRANCH_TO_LABEL:
{
if (!match)
{
found = false;
break;
}
const auto addr = (base[k].data() & se32(2) ? 0 : (base + k).addr()) + ((s32)base[k] << cntlz32(mask) >> (cntlz32(mask) + 2));
const auto lnum = sub.ops[x].num;
const auto label = sub.labels.find(lnum);
if (label == sub.labels.end()) // register the label
{
sub.labels[lnum] = addr;
}
else if (label->second != addr) // or check registered label
{
found = false;
}
break;
}
//case SPET_BRANCH_TO_FUNC:
//{
// if (!match)
// {
// found = false;
// break;
// }
// const auto addr = (base[k].data() & se32(2) ? 0 : (base + k).addr()) + ((s32)base[k] << cntlz32(mask) >> (cntlz32(mask) + 2));
// const auto nid = sub.ops[x].num;
// // TODO: recursive call
//}
default:
{
LOG_ERROR(LOADER, "Unknown search pattern type (%d)", sub.ops[x].type);
assert(0);
return;
}
}
if (!found)
{
break;
}
}
if (found)
{
LOG_SUCCESS(LOADER, "Function '%s' hooked (addr=0x%x)", sub.name, (base + pos).addr());
sub.found++;
base[pos] = HACK(sub.index | EIF_PERFORM_BLR);
}
if (sub.labels.size())
{
sub.labels.clear();
}
}
}
void hook_ppu_funcs(vm::ptr<u32> base, u32 size)
{
using namespace PPU_instr;
if (!Ini.HLEHookStFunc.GetValue())
{
return;
}
// TODO: optimize search
for (u32 i = 0; i < size; i++)
{
// skip NOP
if (base[i].data() == se32(0x60000000))
{
continue;
}
hook_ppu_func(base, i, size);
}
// check function groups
for (u32 i = 0; i < g_ppu_func_subs.size(); i++)
{
if (g_ppu_func_subs[i].found) // start from some group
{
const u64 group = g_ppu_func_subs[i].group;
if (!group)
{
// skip if group not set
continue;
}
enum GroupSearchResult : u32
{
GSR_SUCCESS = 0, // every function from this group has been found once
GSR_MISSING = 1, // (error) some function not found
GSR_EXCESS = 2, // (error) some function found twice or more
};
u32 res = GSR_SUCCESS;
// analyse
for (u32 j = 0; j < g_ppu_func_subs.size(); j++) if (g_ppu_func_subs[j].group == group)
{
u32 count = g_ppu_func_subs[j].found;
if (count == 0) // not found
{
// check if this function has been found with different pattern
for (u32 k = 0; k < g_ppu_func_subs.size(); k++) if (g_ppu_func_subs[k].group == group)
{
if (k != j && g_ppu_func_subs[k].index == g_ppu_func_subs[j].index)
{
count += g_ppu_func_subs[k].found;
}
}
if (count == 0)
{
res |= GSR_MISSING;
LOG_ERROR(LOADER, "Function '%s' not found", g_ppu_func_subs[j].name);
}
else if (count > 1)
{
res |= GSR_EXCESS;
}
}
else if (count == 1) // found
{
// ensure that this function has NOT been found with different pattern
for (u32 k = 0; k < g_ppu_func_subs.size(); k++) if (g_ppu_func_subs[k].group == group)
{
if (k != j && g_ppu_func_subs[k].index == g_ppu_func_subs[j].index)
{
if (g_ppu_func_subs[k].found)
{
res |= GSR_EXCESS;
LOG_ERROR(LOADER, "Function '%s' hooked twice", g_ppu_func_subs[j].name);
}
}
}
}
else
{
res |= GSR_EXCESS;
LOG_ERROR(LOADER, "Function '%s' hooked twice", g_ppu_func_subs[j].name);
}
}
// clear data
for (u32 j = 0; j < g_ppu_func_subs.size(); j++)
{
if (g_ppu_func_subs[j].group == group) g_ppu_func_subs[j].found = 0;
}
char group_name[9] = {};
*(u64*)group_name = group;
if (res == GSR_SUCCESS)
{
LOG_SUCCESS(LOADER, "Function group [%s] successfully hooked", group_name);
}
else
{
LOG_ERROR(LOADER, "Function group [%s] failed:%s%s", group_name,
(res & GSR_MISSING ? " missing;" : ""),
(res & GSR_EXCESS ? " excess;" : ""));
}
}
}
}
bool patch_ppu_import(u32 addr, u32 index)
{
const auto data = vm::ptr<const u32>::make(addr);
using namespace PPU_instr;
// check different patterns:
if (vm::check_addr(addr, 32) &&
(data[0] & 0xffff0000) == LI_(r12, 0) &&
(data[1] & 0xffff0000) == ORIS(r12, r12, 0) &&
(data[2] & 0xffff0000) == LWZ(r12, r12, 0) &&
data[3] == STD(r2, r1, 0x28) &&
data[4] == LWZ(r0, r12, 0) &&
data[5] == LWZ(r2, r12, 4) &&
data[6] == MTCTR(r0) &&
data[7] == BCTR())
{
vm::write32(addr, HACK(index | EIF_SAVE_RTOC | EIF_PERFORM_BLR));
return true;
}
if (vm::check_addr(addr, 12) &&
(data[0] & 0xffff0000) == LI_(r0, 0) &&
(data[1] & 0xffff0000) == ORIS(r0, r0, 0) &&
(data[2] & 0xfc000003) == B(0, 0, 0))
{
const auto sub = vm::ptr<const u32>::make(addr + 8 + ((s32)data[2] << 6 >> 8 << 2));
if (vm::check_addr(sub.addr(), 60) &&
sub[0x0] == STDU(r1, r1, -0x80) &&
sub[0x1] == STD(r2, r1, 0x70) &&
sub[0x2] == MR(r2, r0) &&
sub[0x3] == MFLR(r0) &&
sub[0x4] == STD(r0, r1, 0x90) &&
sub[0x5] == LWZ(r2, r2, 0) &&
sub[0x6] == LWZ(r0, r2, 0) &&
sub[0x7] == LWZ(r2, r2, 4) &&
sub[0x8] == MTCTR(r0) &&
sub[0x9] == BCTRL() &&
sub[0xa] == LD(r2, r1, 0x70) &&
sub[0xb] == ADDI(r1, r1, 0x80) &&
sub[0xc] == LD(r0, r1, 0x10) &&
sub[0xd] == MTLR(r0) &&
sub[0xe] == BLR())
{
vm::write32(addr, HACK(index | EIF_PERFORM_BLR));
return true;
}
}
if (vm::check_addr(addr, 64) &&
data[0x0] == MFLR(r0) &&
data[0x1] == STD(r0, r1, 0x10) &&
data[0x2] == STDU(r1, r1, -0x80) &&
data[0x3] == STD(r2, r1, 0x70) &&
(data[0x4] & 0xffff0000) == LI_(r2, 0) &&
(data[0x5] & 0xffff0000) == ORIS(r2, r2, 0) &&
data[0x6] == LWZ(r2, r2, 0) &&
data[0x7] == LWZ(r0, r2, 0) &&
data[0x8] == LWZ(r2, r2, 4) &&
data[0x9] == MTCTR(r0) &&
data[0xa] == BCTRL() &&
data[0xb] == LD(r2, r1, 0x70) &&
data[0xc] == ADDI(r1, r1, 0x80) &&
data[0xd] == LD(r0, r1, 0x10) &&
data[0xe] == MTLR(r0) &&
data[0xf] == BLR())
{
vm::write32(addr, HACK(index | EIF_PERFORM_BLR));
return true;
}
if (vm::check_addr(addr, 64) &&
data[0x0] == MFLR(r0) &&
data[0x1] == STD(r0, r1, 0x10) &&
data[0x2] == STDU(r1, r1, -0x80) &&
data[0x3] == STD(r2, r1, 0x70) &&
(data[0x4] & 0xffff0000) == LIS(r12, 0) &&
(data[0x5] & 0xffff0000) == LWZ(r12, r12, 0) &&
data[0x6] == LWZ(r0, r12, 0) &&
data[0x7] == LWZ(r2, r12, 4) &&
data[0x8] == MTCTR(r0) &&
data[0x9] == BCTRL() &&
data[0xa] == LD(r2, r1, 0x70) &&
data[0xb] == ADDI(r1, r1, 0x80) &&
data[0xc] == LD(r0, r1, 0x10) &&
data[0xd] == MTLR(r0) &&
data[0xe] == BLR())
{
vm::write32(addr, HACK(index | EIF_PERFORM_BLR));
return true;
}
if (vm::check_addr(addr, 56) &&
(data[0x0] & 0xffff0000) == LI_(r12, 0) &&
(data[0x1] & 0xffff0000) == ORIS(r12, r12, 0) &&
(data[0x2] & 0xffff0000) == LWZ(r12, r12, 0) &&
data[0x3] == STD(r2, r1, 0x28) &&
data[0x4] == MFLR(r0) &&
data[0x5] == STD(r0, r1, 0x20) &&
data[0x6] == LWZ(r0, r12, 0) &&
data[0x7] == LWZ(r2, r12, 4) &&
data[0x8] == MTCTR(r0) &&
data[0x9] == BCTRL() &&
data[0xa] == LD(r0, r1, 0x20) &&
data[0xb] == MTLR(r0) &&
data[0xc] == LD(r2, r1, 0x28) &&
data[0xd] == BLR())
{
vm::write32(addr, HACK(index | EIF_PERFORM_BLR));
return true;
}
return false;
}
Module::Module(const char* name, void(*init)())
: m_is_loaded(false)
, m_name(name)
, m_init(init)
{
}
Module::~Module()
{
}
void Module::Init()
{
m_init();
}
void Module::Load()
{
if (IsLoaded())
{
return;
}
if (on_load)
{
on_load();
}
SetLoaded(true);
}
void Module::Unload()
{
if (!IsLoaded())
{
return;
}
if (on_unload)
{
on_unload();
}
SetLoaded(false);
}
void Module::SetLoaded(bool loaded)
{
m_is_loaded = loaded;
}
bool Module::IsLoaded() const
{
return m_is_loaded;
}
const std::string& Module::GetName() const
{
return m_name;
}
void Module::SetName(const std::string& name)
{
m_name = name;
}