Experimental RawSPU MMIO implementation

This commit is contained in:
Nekotekina 2015-01-04 15:35:47 +03:00
parent 3d2aa622f1
commit 896097da0d
4 changed files with 194 additions and 28 deletions

View file

@ -254,22 +254,195 @@ void CPUThread::ExecOnce()
SendDbgCommand(DID_PAUSED_THREAD, this); SendDbgCommand(DID_PAUSED_THREAD, this);
} }
enum x64_reg_t : u32
{
X64R_EAX,
X64R_ECX,
X64R_EDX,
X64R_EBX,
X64R_ESP,
X64R_EBP,
X64R_ESI,
X64R_EDI,
X64R_R8D,
X64R_R9D,
X64R_R10D,
X64R_R11D,
X64R_R12D,
X64R_R13D,
X64R_R14D,
X64R_R15D,
X64R32 = X64R_EAX,
X64_IMM8,
X64_IMM16,
X64_IMM32,
X64_IMM64,
};
enum x64_op_t : u32
{
X64OP_LOAD,
X64OP_STORE,
};
void decode_x64_reg_op(const u8* code, x64_op_t& decoded_op, x64_reg_t& decoded_reg, size_t& decoded_size)
{
decoded_size = 0;
u8 reg = 0;
if ((*code & 0xf0) == 0x40) // check REX prefix
{
if (*code & 0x80) // check REX.W bit
{
throw fmt::Format("decode_x64_reg_op(%.16llXh): REX.W bit found", code - decoded_size);
}
if (*code & 0x04) // check REX.R bit
{
reg = 8;
}
code++;
decoded_size++;
}
if (*code == 0x66)
{
throw fmt::Format("decode_x64_reg_op(%.16llXh): 0x66 prefix found", code - decoded_size);
code++;
decoded_size++;
}
auto get_modRM_r32 = [](const u8* code, const u8 reg_base) -> x64_reg_t
{
return (x64_reg_t)((((*code & 0x38) >> 3) | reg_base) + X64R32);
};
auto get_modRM_size = [](const u8* code) -> size_t
{
switch (*code >> 6) // check Mod
{
case 0: return (*code & 0x07) == 4 ? 2 : 1; // check SIB
case 1: return (*code & 0x07) == 4 ? 3 : 2; // check SIB (disp8)
case 2: return (*code & 0x07) == 4 ? 6 : 5; // check SIB (disp32)
default: return 1;
}
};
decoded_size++;
switch (const u8 op1 = *code++)
{
case 0x89: // MOV r/m32, r32
{
decoded_op = X64OP_STORE;
decoded_reg = get_modRM_r32(code, reg);
decoded_size += get_modRM_size(code);
return;
}
case 0x8b: // MOV r32, r/m32
{
decoded_op = X64OP_LOAD;
decoded_reg = get_modRM_r32(code, reg);
decoded_size += get_modRM_size(code);
return;
}
case 0xc7:
{
if (get_modRM_r32(code, 0) == X64R_EAX) // MOV r/m32, imm32
{
decoded_op = X64OP_STORE;
decoded_reg = X64_IMM32;
decoded_size = get_modRM_size(code) + 4;
return;
}
}
default:
{
throw fmt::Format("decode_x64_reg_op(%.16llX): unsupported opcode found (0x%.2X, 0x%.2X, 0x%.2X)", code - decoded_size, op1, code[0], code[1]);
}
}
}
#ifdef _WIN32 #ifdef _WIN32
void _se_translator(unsigned int u, EXCEPTION_POINTERS* pExp) void _se_translator(unsigned int u, EXCEPTION_POINTERS* pExp)
{ {
const u64 addr = (u64)pExp->ExceptionRecord->ExceptionInformation[1] - (u64)Memory.GetBaseAddr(); const u64 addr64 = (u64)pExp->ExceptionRecord->ExceptionInformation[1] - (u64)Memory.GetBaseAddr();
const bool is_writing = pExp->ExceptionRecord->ExceptionInformation[0] != 0;
CPUThread* t = GetCurrentCPUThread(); CPUThread* t = GetCurrentCPUThread();
if (u == EXCEPTION_ACCESS_VIOLATION && addr < 0x100000000 && t) if (u == EXCEPTION_ACCESS_VIOLATION && addr64 < 0x100000000 && t)
{ {
// TODO: allow recovering from a page fault const u32 addr = (u32)addr64;
throw fmt::Format("Access violation: addr = 0x%x (is_alive=%d, last_syscall=0x%llx (%s))", if (addr >= RAW_SPU_BASE_ADDR && (addr % RAW_SPU_OFFSET) >= RAW_SPU_PROB_OFFSET) // RawSPU MMIO registers
(u32)addr, t->IsAlive() ? 1 : 0, t->m_last_syscall, SysCalls::GetHLEFuncName((u32)t->m_last_syscall).c_str()); {
} // one x64 instruction is manually decoded and interpreted
else x64_op_t op;
{ x64_reg_t reg;
// some fatal error (should crash) size_t size;
return; decode_x64_reg_op((const u8*)pExp->ContextRecord->Rip, op, reg, size);
// get x64 reg value (for store operations)
u64 reg_value;
if (reg - X64R32 < 16)
{
// load the value from x64 register
reg_value = (u32)(&pExp->ContextRecord->Rax)[reg - X64R32];
}
else if (reg == X64_IMM32)
{
// load the immediate value (assuming it's at the end of the instruction)
reg_value = *(u32*)(pExp->ContextRecord->Rip + size - 4);
}
else
{
assert(!"Invalid x64_reg_t value");
}
bool save_reg = false;
switch (op)
{
case X64OP_LOAD:
{
assert(!is_writing);
reg_value = re32(Memory.ReadMMIO32(addr));
save_reg = true;
break;
}
case X64OP_STORE:
{
assert(is_writing);
Memory.WriteMMIO32(addr, re32((u32)reg_value));
break;
}
default: assert(!"Invalid x64_op_t value");
}
// save x64 reg value (for load operations)
if (save_reg)
{
if (reg - X64R32 < 16)
{
// store the value into x64 register
(&pExp->ContextRecord->Rax)[reg - X64R32] = (u32)reg_value;
}
else
{
assert(!"Invalid x64_reg_t value (saving)");
}
}
// skip decoded instruction
pExp->ContextRecord->Rip += size;
// restore context (further code shouldn't be reached)
RtlRestoreContext(pExp->ContextRecord, pExp->ExceptionRecord);
// it's dangerous because destructors won't be executed
}
// TODO: allow recovering from a page fault as a feature of PS3 virtual memory
throw fmt::Format("Access violation %s location 0x%x (is_alive=%d, last_syscall=0x%llx (%s))",
is_writing ? "writing" : "reading", (u32)addr, t->IsAlive() ? 1 : 0, t->m_last_syscall, SysCalls::GetHLEFuncName((u32)t->m_last_syscall).c_str());
} }
// else some fatal error (should crash)
} }
#else #else
// TODO: linux version // TODO: linux version

View file

@ -170,28 +170,29 @@ void MemoryBase::Close()
MemoryBlocks.clear(); MemoryBlocks.clear();
} }
bool MemoryBase::WriteMMIO32(u32 addr, const u32 data) void MemoryBase::WriteMMIO32(u32 addr, const u32 data)
{ {
LV2_LOCK(0); LV2_LOCK(0);
if (RawSPUMem[(addr - RAW_SPU_BASE_ADDR) / RAW_SPU_OFFSET] && ((RawSPUThread*)RawSPUMem[(addr - RAW_SPU_BASE_ADDR) / RAW_SPU_OFFSET])->Write32(addr, data)) if (RawSPUMem[(addr - RAW_SPU_BASE_ADDR) / RAW_SPU_OFFSET] && ((RawSPUThread*)RawSPUMem[(addr - RAW_SPU_BASE_ADDR) / RAW_SPU_OFFSET])->Write32(addr, data))
{ {
return true; return;
} }
return false; throw fmt::Format("%s(addr=0x%x, data=0x%x) failed", __FUNCTION__, addr, data);
} }
bool MemoryBase::ReadMMIO32(u32 addr, u32& res) u32 MemoryBase::ReadMMIO32(u32 addr)
{ {
LV2_LOCK(0); LV2_LOCK(0);
u32 res;
if (RawSPUMem[(addr - RAW_SPU_BASE_ADDR) / RAW_SPU_OFFSET] && ((RawSPUThread*)RawSPUMem[(addr - RAW_SPU_BASE_ADDR) / RAW_SPU_OFFSET])->Read32(addr, &res)) if (RawSPUMem[(addr - RAW_SPU_BASE_ADDR) / RAW_SPU_OFFSET] && ((RawSPUThread*)RawSPUMem[(addr - RAW_SPU_BASE_ADDR) / RAW_SPU_OFFSET])->Read32(addr, &res))
{ {
return true; return res;
} }
return false; throw fmt::Format("%s(addr=0x%x) failed", __FUNCTION__, addr);
} }
bool MemoryBase::Map(const u64 addr, const u32 size) bool MemoryBase::Map(const u64 addr, const u32 size)

View file

@ -127,9 +127,9 @@ public:
void Close(); void Close();
__noinline bool WriteMMIO32(u32 addr, const u32 data); __noinline void WriteMMIO32(u32 addr, const u32 data);
__noinline bool ReadMMIO32(u32 addr, u32& res); __noinline u32 ReadMMIO32(u32 addr);
u32 GetUserMemTotalSize() u32 GetUserMemTotalSize()
{ {

View file

@ -64,20 +64,12 @@ namespace vm
static u32 read32(u32 addr) static u32 read32(u32 addr)
{ {
u32 res; return re32(*(u32*)((u8*)g_base_addr + addr));;
if (addr < RAW_SPU_BASE_ADDR || (addr % RAW_SPU_OFFSET) < RAW_SPU_PROB_OFFSET || !Memory.ReadMMIO32((u32)addr, res))
{
res = re32(*(u32*)((u8*)g_base_addr + addr));
}
return res;
} }
static void write32(u32 addr, be_t<u32> value) static void write32(u32 addr, be_t<u32> value)
{ {
if (addr < RAW_SPU_BASE_ADDR || (addr % RAW_SPU_OFFSET) < RAW_SPU_PROB_OFFSET || !Memory.WriteMMIO32((u32)addr, value)) *(be_t<u32>*)((u8*)g_base_addr + addr) = value;
{
*(be_t<u32>*)((u8*)g_base_addr + addr) = value;
}
} }
static u64 read64(u32 addr) static u64 read64(u32 addr)