PPU Debugger/Memory: STDCX/STWCX breakpoints, make vm::_ref const

This commit is contained in:
elad335 2025-04-19 15:01:00 +03:00
parent 564c903fbd
commit b34a08fc99
14 changed files with 61 additions and 91 deletions

View file

@ -446,7 +446,7 @@ error_code _cellGcmInitBody(ppu_thread& ppu, vm::pptr<CellGcmContextData> contex
gcm_cfg.zculls_addr = vm::alloc(sizeof(CellGcmZcullInfo) * 8, vm::main); gcm_cfg.zculls_addr = vm::alloc(sizeof(CellGcmZcullInfo) * 8, vm::main);
gcm_cfg.tiles_addr = vm::alloc(sizeof(CellGcmTileInfo) * 15, vm::main); gcm_cfg.tiles_addr = vm::alloc(sizeof(CellGcmTileInfo) * 15, vm::main);
vm::_ref<CellGcmContextData>(gcm_cfg.gcm_info.context_addr) = gcm_cfg.current_context; vm::write<CellGcmContextData>(gcm_cfg.gcm_info.context_addr, gcm_cfg.current_context);
context->set(gcm_cfg.gcm_info.context_addr); context->set(gcm_cfg.gcm_info.context_addr);
// 0x40 is to offset CellGcmControl from RsxDmaControl // 0x40 is to offset CellGcmControl from RsxDmaControl
@ -590,7 +590,7 @@ ret_type gcmSetPrepareFlip(ppu_thread& ppu, vm::ptr<CellGcmContextData> ctxt, u3
if (!old_api && ctxt.addr() == gcm_cfg.gcm_info.context_addr) if (!old_api && ctxt.addr() == gcm_cfg.gcm_info.context_addr)
{ {
vm::_ref<CellGcmControl>(gcm_cfg.gcm_info.control_addr).put += cmd_size; vm::_ptr<CellGcmControl>(gcm_cfg.gcm_info.control_addr)->put += cmd_size;
} }
return static_cast<ret_type>(not_an_error(id)); return static_cast<ret_type>(not_an_error(id));
@ -1463,7 +1463,7 @@ s32 cellGcmCallback(ppu_thread& ppu, vm::ptr<CellGcmContextData> context, u32 co
auto& gcm_cfg = g_fxo->get<gcm_config>(); auto& gcm_cfg = g_fxo->get<gcm_config>();
auto& ctrl = vm::_ref<CellGcmControl>(gcm_cfg.gcm_info.control_addr); auto& ctrl = *vm::_ptr<CellGcmControl>(gcm_cfg.gcm_info.control_addr);
// Flush command buffer (ie allow RSX to read up to context->current) // Flush command buffer (ie allow RSX to read up to context->current)
ctrl.put.exchange(getOffsetFromAddress(context->current.addr())); ctrl.put.exchange(getOffsetFromAddress(context->current.addr()));

View file

@ -4134,7 +4134,7 @@ s32 _spurs::create_task(vm::ptr<CellSpursTaskset> taskset, vm::ptr<u32> task_id,
u32 tmp_task_id; u32 tmp_task_id;
vm::light_op(vm::_ref<atomic_be_t<v128>>(taskset.ptr(&CellSpursTaskset::enabled).addr()), [&](atomic_be_t<v128>& ptr) vm::light_op(*vm::_ptr<atomic_be_t<v128>>(taskset.ptr(&CellSpursTaskset::enabled).addr()), [&](atomic_be_t<v128>& ptr)
{ {
// NOTE: Realfw processes this using 4 32-bits atomic loops // NOTE: Realfw processes this using 4 32-bits atomic loops
// But here its processed within a single 128-bit atomic op // But here its processed within a single 128-bit atomic op

View file

@ -1594,12 +1594,12 @@ s32 spursTasksetProcessRequest(spu_thread& spu, s32 request, u32* taskId, u32* i
spursHalt(spu); spursHalt(spu);
} }
vm::_ref<v128>(ctxt->taskset.addr() + ::offset32(&CellSpursTaskset::waiting)) = waiting; // vm::_ref<v128>(ctxt->taskset.addr() + ::offset32(&CellSpursTaskset::waiting)) = waiting;
vm::_ref<v128>(ctxt->taskset.addr() + ::offset32(&CellSpursTaskset::running)) = running; // vm::_ref<v128>(ctxt->taskset.addr() + ::offset32(&CellSpursTaskset::running)) = running;
vm::_ref<v128>(ctxt->taskset.addr() + ::offset32(&CellSpursTaskset::ready)) = ready; // vm::_ref<v128>(ctxt->taskset.addr() + ::offset32(&CellSpursTaskset::ready)) = ready;
vm::_ref<v128>(ctxt->taskset.addr() + ::offset32(&CellSpursTaskset::pending_ready)) = v128{}; // vm::_ref<v128>(ctxt->taskset.addr() + ::offset32(&CellSpursTaskset::pending_ready)) = v128{};
vm::_ref<v128>(ctxt->taskset.addr() + ::offset32(&CellSpursTaskset::enabled)) = enabled; // vm::_ref<v128>(ctxt->taskset.addr() + ::offset32(&CellSpursTaskset::enabled)) = enabled;
vm::_ref<v128>(ctxt->taskset.addr() + ::offset32(&CellSpursTaskset::signalled)) = signalled; // vm::_ref<v128>(ctxt->taskset.addr() + ::offset32(&CellSpursTaskset::signalled)) = signalled;
std::memcpy(spu._ptr<void>(0x2700), spu._ptr<void>(0x100), 128); // Copy data std::memcpy(spu._ptr<void>(0x2700), spu._ptr<void>(0x100), 128); // Copy data
}//); }//);

View file

@ -3367,7 +3367,10 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value)
fmt::throw_exception("PPU %s: Unaligned address: 0x%08x", sizeof(T) == 4 ? "STWCX" : "STDCX", addr); fmt::throw_exception("PPU %s: Unaligned address: 0x%08x", sizeof(T) == 4 ? "STWCX" : "STDCX", addr);
} }
auto& data = vm::_ref<atomic_be_t<u64>>(addr & -8); // Notify breakpoint handler
vm::write<void>(addr, T{0}, &ppu);
auto& data = const_cast<atomic_be_t<u64>&>(vm::_ref<atomic_be_t<u64>>(addr & -8));
auto& res = vm::reservation_acquire(addr); auto& res = vm::reservation_acquire(addr);
const u64 rtime = ppu.rtime; const u64 rtime = ppu.rtime;

View file

@ -3833,14 +3833,14 @@ bool spu_thread::do_putllc(const spu_mfc_cmd& args)
{ {
if (addr - spurs_addr <= 0x80) if (addr - spurs_addr <= 0x80)
{ {
mov_rdata(vm::_ref<spu_rdata_t>(addr), to_write); mov_rdata(*vm::_ptr<spu_rdata_t>(addr), to_write);
res += 64; res += 64;
return true; return true;
} }
} }
else if (!g_use_rtm) else if (!g_use_rtm)
{ {
vm::_ref<atomic_t<u32>>(addr) += 0; *vm::_ptr<atomic_t<u32>>(addr) += 0;
} }
if (g_use_rtm) [[likely]] if (g_use_rtm) [[likely]]

View file

@ -181,7 +181,7 @@ error_code sys_rsx_memory_allocate(cpu_thread& cpu, vm::ptr<u32> mem_handle, vm:
if (u32 addr = rsx::get_current_renderer()->driver_info) if (u32 addr = rsx::get_current_renderer()->driver_info)
{ {
vm::_ref<RsxDriverInfo>(addr).memory_size = size; vm::_ptr<RsxDriverInfo>(addr)->memory_size = size;
} }
*mem_addr = rsx::constants::local_mem_base; *mem_addr = rsx::constants::local_mem_base;
@ -265,7 +265,7 @@ error_code sys_rsx_context_allocate(cpu_thread& cpu, vm::ptr<u32> context_id, vm
*lpar_driver_info = dma_address + 0x100000; *lpar_driver_info = dma_address + 0x100000;
*lpar_reports = dma_address + 0x200000; *lpar_reports = dma_address + 0x200000;
auto &reports = vm::_ref<RsxReports>(vm::cast(*lpar_reports)); auto &reports = *vm::_ptr<RsxReports>(vm::cast(*lpar_reports));
std::memset(&reports, 0, sizeof(RsxReports)); std::memset(&reports, 0, sizeof(RsxReports));
for (usz i = 0; i < std::size(reports.notify); ++i) for (usz i = 0; i < std::size(reports.notify); ++i)
@ -273,10 +273,10 @@ error_code sys_rsx_context_allocate(cpu_thread& cpu, vm::ptr<u32> context_id, vm
for (usz i = 0; i < std::size(reports.semaphore); i += 4) for (usz i = 0; i < std::size(reports.semaphore); i += 4)
{ {
reports.semaphore[i + 0].val.raw() = 0x1337C0D3; reports.semaphore[i + 0] = 0x1337C0D3;
reports.semaphore[i + 1].val.raw() = 0x1337BABE; reports.semaphore[i + 1] = 0x1337BABE;
reports.semaphore[i + 2].val.raw() = 0x1337BEEF; reports.semaphore[i + 2] = 0x1337BEEF;
reports.semaphore[i + 3].val.raw() = 0x1337F001; reports.semaphore[i + 3] = 0x1337F001;
} }
for (usz i = 0; i < std::size(reports.report); ++i) for (usz i = 0; i < std::size(reports.report); ++i)
@ -286,7 +286,7 @@ error_code sys_rsx_context_allocate(cpu_thread& cpu, vm::ptr<u32> context_id, vm
reports.report[i].pad = -1; reports.report[i].pad = -1;
} }
auto &driverInfo = vm::_ref<RsxDriverInfo>(vm::cast(*lpar_driver_info)); auto &driverInfo = *vm::_ptr<RsxDriverInfo>(vm::cast(*lpar_driver_info));
std::memset(&driverInfo, 0, sizeof(RsxDriverInfo)); std::memset(&driverInfo, 0, sizeof(RsxDriverInfo));
@ -303,7 +303,7 @@ error_code sys_rsx_context_allocate(cpu_thread& cpu, vm::ptr<u32> context_id, vm
render->driver_info = vm::cast(*lpar_driver_info); render->driver_info = vm::cast(*lpar_driver_info);
auto &dmaControl = vm::_ref<RsxDmaControl>(vm::cast(*lpar_dma_control)); auto &dmaControl = *vm::_ptr<RsxDmaControl>(vm::cast(*lpar_dma_control));
dmaControl.get = 0; dmaControl.get = 0;
dmaControl.put = 0; dmaControl.put = 0;
dmaControl.ref = 0; // Set later to -1 by cellGcmSys dmaControl.ref = 0; // Set later to -1 by cellGcmSys
@ -527,7 +527,7 @@ error_code sys_rsx_context_attribute(u32 context_id, u32 package_id, u64 a3, u64
return { CELL_EINVAL, "context_id is 0x%x", context_id }; return { CELL_EINVAL, "context_id is 0x%x", context_id };
} }
auto &driverInfo = vm::_ref<RsxDriverInfo>(render->driver_info); auto &driverInfo = *vm::_ptr<RsxDriverInfo>(render->driver_info);
switch (package_id) switch (package_id)
{ {
case 0x001: // FIFO case 0x001: // FIFO
@ -862,7 +862,7 @@ error_code sys_rsx_context_attribute(u32 context_id, u32 package_id, u64 a3, u64
// seems gcmSysWaitLabel uses this offset, so lets set it to 0 every flip // seems gcmSysWaitLabel uses this offset, so lets set it to 0 every flip
// NOTE: Realhw resets 16 bytes of this semaphore for some reason // NOTE: Realhw resets 16 bytes of this semaphore for some reason
vm::_ref<atomic_t<u128>>(render->label_addr + 0x10).store(u128{}); vm::_ptr<atomic_t<u128>>(render->label_addr + 0x10)->store(u128{});
render->send_event(0, SYS_RSX_EVENT_FLIP_BASE << 1, 0); render->send_event(0, SYS_RSX_EVENT_FLIP_BASE << 1, 0);
break; break;

View file

@ -87,10 +87,7 @@ struct RsxDmaControl
be_t<u32> unk1; be_t<u32> unk1;
}; };
struct RsxSemaphore using RsxSemaphore = be_t<u32>;
{
atomic_be_t<u32> val;
};
struct alignas(16) RsxNotify struct alignas(16) RsxNotify
{ {

View file

@ -553,7 +553,7 @@ error_code sys_ss_individual_info_manager(u64 pkg_id, u64 a2, vm::ptr<u64> out_s
case 0x17002: case 0x17002:
{ {
// TODO // TODO
vm::_ref<u64>(a5) = a4; // Write back size of buffer vm::write<u64>(a5, a4); // Write back size of buffer
break; break;
} }
// Get EID size // Get EID size

View file

@ -8,14 +8,14 @@
#include "util/to_endian.hpp" #include "util/to_endian.hpp"
class ppu_thread;
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS #ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
#include "rpcs3qt/breakpoint_handler.h" #include "rpcs3qt/breakpoint_handler.h"
#include "util/logs.hpp" #include "util/logs.hpp"
LOG_CHANNEL(debugbp_log, "DebugBP"); LOG_CHANNEL(debugbp_log, "DebugBP");
class ppu_thread;
void ppubreak(ppu_thread& ppu); void ppubreak(ppu_thread& ppu);
#endif #endif
@ -282,9 +282,10 @@ namespace vm
} }
// Convert specified PS3 address to a reference of specified (possibly converted to BE) type // Convert specified PS3 address to a reference of specified (possibly converted to BE) type
template <typename T, typename U> inline to_be_t<T>& _ref(const U& addr) // Const lvalue: prevent abused writes
template <typename T, typename U> inline const to_be_t<T>& _ref(const U& addr)
{ {
return *static_cast<to_be_t<T>*>(base(addr)); return *static_cast<const to_be_t<T>*>(base(addr));
} }
// Access memory bypassing memory protection // Access memory bypassing memory protection
@ -300,42 +301,43 @@ namespace vm
} }
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS #ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
inline void write16(u32 addr, be_t<u16> value, ppu_thread* ppu = nullptr) template <typename T, typename U = T>
inline void write(u32 addr, U value, ppu_thread* ppu = nullptr)
#else #else
inline void write16(u32 addr, be_t<u16> value) template <typename T, typename U = T>
inline void write(u32 addr, U value, ppu_thread* = nullptr)
#endif #endif
{ {
_ref<u16>(addr) = value; using dest_t = std::conditional_t<std::is_void_v<T>, U, T>;
if constexpr (!std::is_void_v<T>)
{
*_ptr<dest_t>(addr) = value;
}
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS #ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
if (ppu && g_breakpoint_handler.HasBreakpoint(addr, breakpoint_types::bp_write)) if (ppu && g_breakpoint_handler.HasBreakpoint(addr, breakpoint_types::bp_write))
{ {
debugbp_log.success("BPMW: breakpoint writing(16) 0x%x at 0x%x", value, addr); debugbp_log.success("BPMW: breakpoint writing(%d) 0x%x at 0x%x",
sizeof(dest_t) * CHAR_BIT, value, addr);
ppubreak(*ppu); ppubreak(*ppu);
} }
#endif #endif
} }
inline void write16(u32 addr, be_t<u16> value, ppu_thread* ppu = nullptr)
{
write<be_t<u16>>(addr, value, ppu);
}
inline const be_t<u32>& read32(u32 addr) inline const be_t<u32>& read32(u32 addr)
{ {
return _ref<u32>(addr); return _ref<u32>(addr);
} }
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
inline void write32(u32 addr, be_t<u32> value, ppu_thread* ppu = nullptr) inline void write32(u32 addr, be_t<u32> value, ppu_thread* ppu = nullptr)
#else
inline void write32(u32 addr, be_t<u32> value)
#endif
{ {
_ref<u32>(addr) = value; write<be_t<u32>>(addr, value, ppu);
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
if (ppu && g_breakpoint_handler.HasBreakpoint(addr, breakpoint_types::bp_write))
{
debugbp_log.success("BPMW: breakpoint writing(32) 0x%x at 0x%x", value, addr);
ppubreak(*ppu);
}
#endif
} }
inline const be_t<u64>& read64(u32 addr) inline const be_t<u64>& read64(u32 addr)
@ -343,41 +345,9 @@ namespace vm
return _ref<u64>(addr); return _ref<u64>(addr);
} }
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
inline void write64(u32 addr, be_t<u64> value, ppu_thread* ppu = nullptr) inline void write64(u32 addr, be_t<u64> value, ppu_thread* ppu = nullptr)
#else
inline void write64(u32 addr, be_t<u64> value)
#endif
{ {
_ref<u64>(addr) = value; write<be_t<u64>>(addr, value, ppu);
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
if (ppu && g_breakpoint_handler.HasBreakpoint(addr, breakpoint_types::bp_write))
{
debugbp_log.success("BPMW: breakpoint writing(64) 0x%x at 0x%x", value, addr);
ppubreak(*ppu);
}
#endif
}
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
template <typename T, typename U = T>
inline void write(u32 addr, U value, ppu_thread* ppu = nullptr)
#else
template <typename T, typename U = T>
inline void write(u32 addr, U value)
#endif
{
_ref<T>(addr) = static_cast<T>(value);
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
if (ppu && g_breakpoint_handler.HasBreakpoint(addr, breakpoint_types::bp_write))
{
debugbp_log.success("BPMW: breakpoint writing(%d) 0x%x at 0x%x",
sizeof(T) * CHAR_BIT, value, addr);
ppubreak(*ppu);
}
#endif
} }
void init(); void init();

View file

@ -15,7 +15,7 @@ namespace rsx
RSX(ctx)->sync(); RSX(ctx)->sync();
// Write ref+get (get will be written again with the same value at command end) // Write ref+get (get will be written again with the same value at command end)
auto& dma = vm::_ref<RsxDmaControl>(RSX(ctx)->dma_address); auto& dma = *vm::_ptr<RsxDmaControl>(RSX(ctx)->dma_address);
dma.get.release(RSX(ctx)->fifo_ctrl->get_pos()); dma.get.release(RSX(ctx)->fifo_ctrl->get_pos());
dma.ref.store(arg); dma.ref.store(arg);
} }
@ -28,7 +28,7 @@ namespace rsx
// Syncronization point, may be associated with memory changes without actually changing addresses // Syncronization point, may be associated with memory changes without actually changing addresses
RSX(ctx)->m_graphics_state |= rsx::pipeline_state::fragment_program_needs_rehash; RSX(ctx)->m_graphics_state |= rsx::pipeline_state::fragment_program_needs_rehash;
const auto& sema = vm::_ref<RsxSemaphore>(addr).val; const auto& sema = vm::_ref<RsxSemaphore>(addr);
if (sema == arg) if (sema == arg)
{ {

View file

@ -566,7 +566,7 @@ namespace rsx
default: default:
rsx_log.error("NV4097_GET_REPORT: Bad type %d", type); rsx_log.error("NV4097_GET_REPORT: Bad type %d", type);
vm::_ref<atomic_t<CellGcmReportData>>(address_ptr).atomic_op([&](CellGcmReportData& data) vm::_ptr<atomic_t<CellGcmReportData>>(address_ptr)->atomic_op([&](CellGcmReportData& data)
{ {
data.timer = RSX(ctx)->timestamp(); data.timer = RSX(ctx)->timestamp();
data.padding = 0; data.padding = 0;
@ -651,7 +651,7 @@ namespace rsx
ensure(addr != umax); ensure(addr != umax);
vm::_ref<atomic_t<RsxNotify>>(addr).store( vm::_ptr<atomic_t<RsxNotify>>(addr)->store(
{ {
RSX(ctx)->timestamp(), RSX(ctx)->timestamp(),
0 0

View file

@ -21,7 +21,7 @@ namespace rsx
// First, queue the GPU work. If it flushes the queue for us, the following routines will be faster. // First, queue the GPU work. If it flushes the queue for us, the following routines will be faster.
const bool handled = RSX(ctx)->get_backend_config().supports_host_gpu_labels && RSX(ctx)->release_GCM_label(address, data); const bool handled = RSX(ctx)->get_backend_config().supports_host_gpu_labels && RSX(ctx)->release_GCM_label(address, data);
if (vm::_ref<RsxSemaphore>(address).val == data) if (vm::_ref<RsxSemaphore>(address) == data)
{ {
// It's a no-op to write the same value (although there is a delay in real-hw so it's more accurate to allow GPU label in this case) // It's a no-op to write the same value (although there is a delay in real-hw so it's more accurate to allow GPU label in this case)
return; return;
@ -57,7 +57,7 @@ namespace rsx
} }
} }
vm::_ref<RsxSemaphore>(address).val = data; vm::write<atomic_t<RsxSemaphore>>(address, data);
} }
} }
} }

View file

@ -1212,7 +1212,7 @@ namespace rsx
if (const u64 get_put = new_get_put.exchange(u64{umax}); if (const u64 get_put = new_get_put.exchange(u64{umax});
get_put != umax) get_put != umax)
{ {
vm::_ref<atomic_be_t<u64>>(dma_address + ::offset32(&RsxDmaControl::put)).release(get_put); vm::_ptr<atomic_be_t<u64>>(dma_address + ::offset32(&RsxDmaControl::put))->release(get_put);
fifo_ctrl->set_get(static_cast<u32>(get_put)); fifo_ctrl->set_get(static_cast<u32>(get_put));
fifo_ctrl->abort(); fifo_ctrl->abort();
fifo_ret_addr = RSX_CALL_STACK_EMPTY; fifo_ret_addr = RSX_CALL_STACK_EMPTY;
@ -2457,7 +2457,7 @@ namespace rsx
} }
rsx::reservation_lock<true> lock(sink, 16); rsx::reservation_lock<true> lock(sink, 16);
vm::_ref<atomic_t<CellGcmReportData>>(sink).store({timestamp(), value, 0}); vm::_ptr<atomic_t<CellGcmReportData>>(sink)->store({timestamp(), value, 0});
} }
u32 thread::copy_zcull_stats(u32 memory_range_start, u32 memory_range, u32 destination) u32 thread::copy_zcull_stats(u32 memory_range_start, u32 memory_range, u32 destination)

View file

@ -62,7 +62,7 @@ namespace rsx
RSX(ctx)->reset(); RSX(ctx)->reset();
RSX(ctx)->on_frame_end(arg); RSX(ctx)->on_frame_end(arg);
RSX(ctx)->request_emu_flip(arg); RSX(ctx)->request_emu_flip(arg);
vm::_ref<atomic_t<u128>>(RSX(ctx)->label_addr + 0x10).store(u128{}); vm::_ptr<atomic_t<u128>>(RSX(ctx)->label_addr + 0x10)->store(u128{});
} }
void user_command(context* ctx, u32, u32 arg) void user_command(context* ctx, u32, u32 arg)