Detect MMIO writes via eieio

This commit is contained in:
Sajid 2024-11-24 15:36:50 +06:00
parent 4357a55eff
commit e5317bda48
3 changed files with 43 additions and 17 deletions

View File

@ -158,6 +158,7 @@ bool Recompiler::Recompile(
const Function& fn,
uint32_t base,
const ppc_insn& insn,
const uint32_t* data,
std::unordered_map<uint32_t, RecompilerSwitchTable>::iterator& switchTable,
RecompilerLocalVariables& localVariables,
CSRState& csrState)
@ -262,6 +263,12 @@ bool Recompiler::Recompile(
return "ea";
};
// TODO (Sajid): Check for out of bounds access
auto mmioStore = [&]() -> bool
{
return *(data + 1) == c_eieio;
};
auto printFunctionCall = [&](uint32_t address)
{
if (address == config.longJmpAddress)
@ -1362,7 +1369,7 @@ bool Recompiler::Recompile(
break;
case PPC_INST_STB:
print("\tPPC_STORE_U8(");
print("{}", mmioStore() ? "\tPPC_MM_STORE_U8(" : "\tPPC_STORE_U8(");
if (insn.operands[2] != 0)
print("{}.u32 + ", r(insn.operands[2]));
println("{}, {}.u8);", int32_t(insn.operands[1]), r(insn.operands[0]));
@ -1375,14 +1382,14 @@ bool Recompiler::Recompile(
break;
case PPC_INST_STBX:
print("\tPPC_STORE_U8(");
print("{}", mmioStore() ? "\tPPC_MM_STORE_U8(" : "\tPPC_STORE_U8(");
if (insn.operands[1] != 0)
print("{}.u32 + ", r(insn.operands[1]));
println("{}.u32, {}.u8);", r(insn.operands[2]), r(insn.operands[0]));
break;
case PPC_INST_STD:
print("\tPPC_STORE_U64(");
print("{}", mmioStore() ? "\tPPC_MM_STORE_U64(" : "\tPPC_STORE_U64(");
if (insn.operands[2] != 0)
print("{}.u32 + ", r(insn.operands[2]));
println("{}, {}.u64);", int32_t(insn.operands[1]), r(insn.operands[0]));
@ -1405,7 +1412,7 @@ bool Recompiler::Recompile(
break;
case PPC_INST_STDX:
print("\tPPC_STORE_U64(");
print("{}", mmioStore() ? "\tPPC_MM_STORE_U64(" : "\tPPC_STORE_U64(");
if (insn.operands[1] != 0)
print("{}.u32 + ", r(insn.operands[1]));
println("{}.u32, {}.u64);", r(insn.operands[2]), r(insn.operands[0]));
@ -1413,7 +1420,7 @@ bool Recompiler::Recompile(
case PPC_INST_STFD:
printSetFlushMode(false);
print("\tPPC_STORE_U64(");
print("{}", mmioStore() ? "\tPPC_MM_STORE_U64(" : "\tPPC_STORE_U64(");
if (insn.operands[2] != 0)
print("{}.u32 + ", r(insn.operands[2]));
println("{}, {}.u64);", int32_t(insn.operands[1]), f(insn.operands[0]));
@ -1421,7 +1428,7 @@ bool Recompiler::Recompile(
case PPC_INST_STFDX:
printSetFlushMode(false);
print("\tPPC_STORE_U64(");
print("{}", mmioStore() ? "\tPPC_MM_STORE_U64(" : "\tPPC_STORE_U64(");
if (insn.operands[1] != 0)
print("{}.u32 + ", r(insn.operands[1]));
println("{}.u32, {}.u64);", r(insn.operands[2]), f(insn.operands[0]));
@ -1429,7 +1436,7 @@ bool Recompiler::Recompile(
case PPC_INST_STFIWX:
printSetFlushMode(false);
print("\tPPC_STORE_U32(");
print("{}", mmioStore() ? "\tPPC_MM_STORE_U32(" : "\tPPC_STORE_U32(");
if (insn.operands[1] != 0)
print("{}.u32 + ", r(insn.operands[1]));
println("{}.u32, {}.u32);", r(insn.operands[2]), f(insn.operands[0]));
@ -1438,7 +1445,7 @@ bool Recompiler::Recompile(
case PPC_INST_STFS:
printSetFlushMode(false);
println("\t{}.f32 = float({}.f64);", temp(), f(insn.operands[0]));
print("\tPPC_STORE_U32(");
print("{}", mmioStore() ? "\tPPC_MM_STORE_U32(" : "\tPPC_STORE_U32(");
if (insn.operands[2] != 0)
print("{}.u32 + ", r(insn.operands[2]));
println("{}, {}.u32);", int32_t(insn.operands[1]), temp());
@ -1447,28 +1454,28 @@ bool Recompiler::Recompile(
case PPC_INST_STFSX:
printSetFlushMode(false);
println("\t{}.f32 = float({}.f64);", temp(), f(insn.operands[0]));
print("\tPPC_STORE_U32(");
print("{}", mmioStore() ? "\tPPC_MM_STORE_U32(" : "\tPPC_STORE_U32(");
if (insn.operands[1] != 0)
print("{}.u32 + ", r(insn.operands[1]));
println("{}.u32, {}.u32);", r(insn.operands[2]), temp());
break;
case PPC_INST_STH:
print("\tPPC_STORE_U16(");
print("{}", mmioStore() ? "\tPPC_MM_STORE_U16(" : "\tPPC_STORE_U16(");
if (insn.operands[2] != 0)
print("{}.u32 + ", r(insn.operands[2]));
println("{}, {}.u16);", int32_t(insn.operands[1]), r(insn.operands[0]));
break;
case PPC_INST_STHBRX:
print("\tPPC_STORE_U16(");
print("{}", mmioStore() ? "\tPPC_MM_STORE_U16(" : "\tPPC_STORE_U16(");
if (insn.operands[1] != 0)
print("{}.u32 + ", r(insn.operands[1]));
println("{}.u32, __builtin_bswap16({}.u16));", r(insn.operands[2]), r(insn.operands[0]));
break;
case PPC_INST_STHX:
print("\tPPC_STORE_U16(");
print("{}", mmioStore() ? "\tPPC_MM_STORE_U16(" : "\tPPC_STORE_U16(");
if (insn.operands[1] != 0)
print("{}.u32 + ", r(insn.operands[1]));
println("{}.u32, {}.u16);", r(insn.operands[2]), r(insn.operands[0]));
@ -1530,14 +1537,14 @@ bool Recompiler::Recompile(
break;
case PPC_INST_STW:
print("\tPPC_STORE_U32(");
print("{}", mmioStore() ? "\tPPC_MM_STORE_U32(" : "\tPPC_STORE_U32(");
if (insn.operands[2] != 0)
print("{}.u32 + ", r(insn.operands[2]));
println("{}, {}.u32);", int32_t(insn.operands[1]), r(insn.operands[0]));
break;
case PPC_INST_STWBRX:
print("\tPPC_STORE_U32(");
print("{}", mmioStore() ? "\tPPC_MM_STORE_U32(" : "\tPPC_STORE_U32(");
if (insn.operands[1] != 0)
print("{}.u32 + ", r(insn.operands[1]));
println("{}.u32, __builtin_bswap32({}.u32));", r(insn.operands[2]), r(insn.operands[0]));
@ -1566,7 +1573,7 @@ bool Recompiler::Recompile(
break;
case PPC_INST_STWX:
print("\tPPC_STORE_U32(");
print("{}", mmioStore() ? "\tPPC_MM_STORE_U32(" : "\tPPC_STORE_U32(");
if (insn.operands[1] != 0)
print("{}.u32 + ", r(insn.operands[1]));
println("{}.u32, {}.u32);", r(insn.operands[2]), r(insn.operands[0]));
@ -2287,7 +2294,7 @@ bool Recompiler::Recompile(const Function& fn)
if (insn.opcode->id == PPC_INST_BCTR && (*(data - 1) == 0x07008038 || *(data - 1) == 0x00000060) && switchTable == config.switchTables.end())
std::println("Found a switch jump table at {:X} with no switch table entry present", base);
if (!Recompile(fn, base, insn, switchTable, localVariables, csrState))
if (!Recompile(fn, base, insn, data, switchTable, localVariables, csrState))
{
std::println("Unrecognized instruction at 0x{:X}: {}", base, insn.opcode->name);
allRecompiled = false;

View File

@ -27,6 +27,8 @@ enum class CSRState
struct Recompiler
{
// Enforce In-order Execution of I/O constant for quick comparison
static constexpr uint32_t c_eieio = 0xAC06007C;
Image image;
std::vector<Function> functions;
std::string out;
@ -54,7 +56,8 @@ struct Recompiler
bool Recompile(
const Function& fn,
uint32_t base,
const ppc_insn& insn,
const ppc_insn& insn,
const uint32_t* data,
std::unordered_map<uint32_t, RecompilerSwitchTable>::iterator& switchTable,
RecompilerLocalVariables& localVariables,
CSRState& csrState);

View File

@ -29,11 +29,27 @@
#define PPC_LOAD_U32(x) __builtin_bswap32(*(volatile uint32_t*)(base + (x)))
#define PPC_LOAD_U64(x) __builtin_bswap64(*(volatile uint64_t*)(base + (x)))
// TODO: Implement.
// These are currently unused. However, MMIO loads could possibly be handled statically with some profiling and a fallback.
// The fallback would be a runtime exception handler which will intercept reads from MMIO regions
// and log the PC for compiling to static code later.
#define PPC_MM_LOAD_U8(x) PPC_LOAD_U8 (x)
#define PPC_MM_LOAD_U16(x) PPC_LOAD_U16(x)
#define PPC_MM_LOAD_U32(x) PPC_LOAD_U32(x)
#define PPC_MM_LOAD_U64(x) PPC_LOAD_U64(x)
#define PPC_STORE_U8(x, y) *(volatile uint8_t*)(base + (x)) = (y)
#define PPC_STORE_U16(x, y) *(volatile uint16_t*)(base + (x)) = __builtin_bswap16(y)
#define PPC_STORE_U32(x, y) *(volatile uint32_t*)(base + (x)) = __builtin_bswap32(y)
#define PPC_STORE_U64(x, y) *(volatile uint64_t*)(base + (x)) = __builtin_bswap64(y)
// MMIO Store handling is completely reliant on being preeceded by eieio.
// TODO: Verify if that's always the case.
#define PPC_MM_STORE_U8(x, y) PPC_STORE_U8 (x, y)
#define PPC_MM_STORE_U16(x, y) PPC_STORE_U16(x, y)
#define PPC_MM_STORE_U32(x, y) PPC_STORE_U32(x, y)
#define PPC_MM_STORE_U64(x, y) PPC_STORE_U64(x, y)
#define PPC_CALL_FUNC(x) x(ctx, base)
#define PPC_CALL_INDIRECT_FUNC(x) (*(PPCFunc**)(ctx.fn + uint64_t(x) * 2))(ctx, base)