RawSPU: Implement 16-bit and 8-bit read MMIO

This commit is contained in:
Elad 2026-05-20 17:26:28 +03:00
parent 6a2ad0a0aa
commit d7da6a713b
2 changed files with 68 additions and 12 deletions

View File

@ -1384,11 +1384,7 @@ bool handle_access_violation(u32 addr, bool is_writing, bool is_exec, ucontext_t
return false; return false;
} }
if (a_size != 4) bool handled = true;
{
// Might be unimplemented, such as writing MFC proxy EAL+EAH using 64-bit store
break;
}
switch (op) switch (op)
{ {
@ -1398,14 +1394,37 @@ bool handle_access_violation(u32 addr, bool is_writing, bool is_exec, ucontext_t
case X64OP_LOAD_TEST: case X64OP_LOAD_TEST:
{ {
u32 value; u32 value;
if (is_writing || !thread->read_reg(addr, value)) const u32 addr_aligned = addr & -4;
if (addr % 4 + a_size > 4)
{
handled = false;
break;
}
if (is_writing || !thread->read_reg(addr_aligned, value))
{ {
return false; return false;
} }
// Adjust value for 8-bit and 16-bit reads
value >>= ((4 - a_size) * 8) - ((addr % 4) * 8);
value &= a_size == 4 ? u32{umax} : ((1u << (a_size * 8)) - 1);
if (op != X64OP_LOAD_BE) if (op != X64OP_LOAD_BE)
{ {
value = stx::se_storage<u32>::swap(value); if (a_size == 4)
{
value = stx::se_storage<u32>::swap(value);
}
else if (a_size == 2)
{
value = stx::se_storage<u16>::swap(value);
}
else
{
ensure(a_size == 1);
}
} }
if (op == X64OP_LOAD_CMP) if (op == X64OP_LOAD_CMP)
@ -1440,12 +1459,35 @@ bool handle_access_violation(u32 addr, bool is_writing, bool is_exec, ucontext_t
case X64OP_BEXTR: case X64OP_BEXTR:
{ {
u32 value; u32 value;
if (is_writing || !thread->read_reg(addr, value)) const u32 addr_aligned = addr & -4;
if (addr % 4 + a_size > 4)
{
handled = false;
break;
}
if (is_writing || !thread->read_reg(addr_aligned, value))
{ {
return false; return false;
} }
value = stx::se_storage<u32>::swap(value); // Adjust value for 8-bit and 16-bit reads
value >>= ((4 - a_size) * 8) - ((addr % 4) * 8);
value &= a_size == 4 ? u32{umax} : ((1u << (a_size * 8)) - 1);
if (a_size == 4)
{
value = stx::se_storage<u32>::swap(value);
}
else if (a_size == 2)
{
value = stx::se_storage<u16>::swap(value);
}
else
{
ensure(a_size == 1);
}
u64 ctrl; u64 ctrl;
if (!get_x64_reg_value(context, s_tls_reg3, d_size, i_size, ctrl)) if (!get_x64_reg_value(context, s_tls_reg3, d_size, i_size, ctrl))
@ -1471,6 +1513,13 @@ bool handle_access_violation(u32 addr, bool is_writing, bool is_exec, ucontext_t
case X64OP_STORE: case X64OP_STORE:
case X64OP_STORE_BE: case X64OP_STORE_BE:
{ {
if (a_size != 4)
{
// Might be unimplemented, such as writing MFC proxy EAL+EAH using 64-bit store
handled = false;
break;
}
u64 reg_value; u64 reg_value;
if (!is_writing || !get_x64_reg_value(context, reg, d_size, i_size, reg_value)) if (!is_writing || !get_x64_reg_value(context, reg, d_size, i_size, reg_value))
{ {
@ -1489,12 +1538,19 @@ bool handle_access_violation(u32 addr, bool is_writing, bool is_exec, ucontext_t
case X64OP_STOS: case X64OP_STOS:
default: default:
{ {
sig_log.error("Invalid or unsupported operation (op=%d, reg=%d, d_size=%lld, i_size=%lld)", +op, +reg, d_size, i_size); sig_log.error("Invalid or unsupported operation (op=%d, addr=0x%x, reg=%d, d_size=%lld, i_size=%lld, a_size=%d)", +op, addr, +reg, d_size, i_size, a_size);
report_opcode(); report_opcode();
return false; return false;
} }
} }
if (!handled)
{
sig_log.error("Invalid or unsupported operation (op=%d, addr=0x%x, reg=%d, d_size=%lld, i_size=%lld, a_size=%d)", +op, addr, +reg, d_size, i_size, a_size);
report_opcode();
break;
}
// skip processed instruction // skip processed instruction
RIP(context) += i_size; RIP(context) += i_size;
g_tls_fault_spu++; g_tls_fault_spu++;

View File

@ -6430,8 +6430,6 @@ extern void resume_spu_thread_group_from_waiting(spu_thread& spu, std::array<sha
bool spu_thread::stop_and_signal(u32 code) bool spu_thread::stop_and_signal(u32 code)
{ {
spu_log.trace("stop_and_signal(code=0x%x)", code);
auto set_status_npc = [&]() auto set_status_npc = [&]()
{ {
status_npc.atomic_op([&](status_npc_sync_var& state) status_npc.atomic_op([&](status_npc_sync_var& state)
@ -6445,6 +6443,8 @@ bool spu_thread::stop_and_signal(u32 code)
if (get_type() >= spu_type::raw) if (get_type() >= spu_type::raw)
{ {
spu_log.warning("stop_and_signal(code=0x%x)", code);
// Save next PC and current SPU Interrupt Status // Save next PC and current SPU Interrupt Status
state += cpu_flag::stop + cpu_flag::wait + cpu_flag::ret; state += cpu_flag::stop + cpu_flag::wait + cpu_flag::ret;
set_status_npc(); set_status_npc();