Some checks failed
Orchestrator / Commit Message Validation (push) Has been skipped
Orchestrator / Lint (push) Failing after 2m0s
Orchestrator / Windows (x86-64) (push) Has been skipped
Orchestrator / Linux (x86-64) (push) Has been skipped
Orchestrator / Create Release (push) Has been skipped
Working-tree snapshot taken 2026-07-28 so this work is not lost. To be clear about provenance: NONE of this was written in the session that committed it — it is pre-existing uncommitted work on phase-a-tracing, last committed 2026-07-09, from the xenia-rs decoder/deadlock investigation. It is recorded verbatim, not reviewed and not tested. Contents: 24 tracked modifications (ppc_emit_memory, xex_module, object_table, shim_utils, xboxkrnl threading/rtl, xevent/xobject/xthread, memory) plus the untracked probe sources audit_68_host_mem_watch, audit_69_event_signal_watch, audit_70_semaphore_release_watch, phase_b_snapshot, and cmake/toolchains. Deliberately excluded: build-cross/ (51 GB of build output) and vkd3d-proton.cache. Note third_party/snappy is a submodule pointer change whose target may not be pushed anywhere, so this branch is a preservation record rather than a guaranteed-buildable tree. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
96 lines
4.3 KiB
C++
96 lines
4.3 KiB
C++
/**
|
|
******************************************************************************
|
|
* Xenia : Xbox 360 Emulator Research Project *
|
|
******************************************************************************
|
|
* AUDIT-068: host-side memory-write watch — forward declarations only.
|
|
*
|
|
* Declarations here are intentionally minimal so that xenia/base/memory.h can
|
|
* include this without pulling in xenia/memory.h (which would create a
|
|
* circular dependency: xenia-base → xenia-core → xenia-base). The full
|
|
* definitions live in xenia/audit_68_host_mem_watch.{h,cc} (xenia-core).
|
|
*
|
|
* Hot path: callers (the integer specializations of xe::store_and_swap<T>)
|
|
* load the atomic flag once. When it is 0 (default), no further work is done
|
|
* — a single relaxed atomic load and a predictable branch.
|
|
******************************************************************************
|
|
*/
|
|
|
|
#ifndef XENIA_BASE_AUDIT_68_HOST_MEM_WATCH_FWD_H_
|
|
#define XENIA_BASE_AUDIT_68_HOST_MEM_WATCH_FWD_H_
|
|
|
|
#include <atomic>
|
|
#include <cstdint>
|
|
|
|
namespace xe {
|
|
namespace audit_68 {
|
|
|
|
// 0 = inactive (default). Non-zero = the cvars have been parsed and at least
|
|
// one watch is configured. Set lazily by check_host_write_slowpath() on first
|
|
// call after cvar parsing. Loaded relaxed on the hot path.
|
|
//
|
|
// Implementation lives in xenia-base (audit_68_host_mem_watch_base.cc) so
|
|
// that callers in xenia-base/xenia-cpu/xenia-kernel can resolve the symbol
|
|
// without depending on xenia-core link order.
|
|
extern std::atomic<uint32_t> g_active;
|
|
|
|
// Host-pointer → guest-VA translation thunk. xenia/memory.cc::Memory::Memory()
|
|
// registers a function pointer here that wraps Memory::HostToGuestVirtual.
|
|
// Until set, the slow path falls back to logging the raw host pointer.
|
|
using HostToGuestThunk = uint32_t (*)(const void*);
|
|
extern HostToGuestThunk g_host_to_guest_thunk;
|
|
|
|
// AUDIT-068 Session 3 — read-mode probe support.
|
|
//
|
|
// Guest-VA → host-pointer translation thunk (wraps Memory::TranslateVirtual).
|
|
// Used by the read-probe poll thread to sample bytes at configured guest VAs.
|
|
// May return non-null even for unmapped/uncommitted VAs (the underlying
|
|
// translation is arithmetic — virtual_membase_ + va) — callers MUST consult
|
|
// the QueryProtect thunk before dereferencing.
|
|
using GuestToHostThunk = const void* (*)(uint32_t);
|
|
extern GuestToHostThunk g_guest_to_host_thunk;
|
|
|
|
// Returns true iff the page containing `guest_va` is committed and readable;
|
|
// out_protect receives the raw page protect bits (kProtectRead, etc.). Wraps
|
|
// Memory::LookupHeap() + BaseHeap::QueryProtect(). Used as a guard before the
|
|
// read-probe samples bytes (early-boot heap-not-yet-mapped path must NOT
|
|
// crash).
|
|
using QueryProtectThunk = bool (*)(uint32_t, uint32_t* /*out_protect*/);
|
|
extern QueryProtectThunk g_query_protect_thunk;
|
|
|
|
// Slow path. Only invoked when g_active is non-zero. Implementation in
|
|
// xenia/base/audit_68_host_mem_watch_base.cc (xenia-base).
|
|
//
|
|
// host_ptr: the host pointer being written (from store_and_swap's `mem`).
|
|
// value: the value being stored (zero-extended to u64).
|
|
// size: 1, 2, 4 or 8.
|
|
// tag: caller-provided tag string (e.g. "store_and_swap<u32>"). Logged
|
|
// verbatim, no formatting. Must be a static string (lifetime
|
|
// beyond this call).
|
|
void check_host_write_slowpath(const void* host_ptr, uint64_t value,
|
|
uint8_t size, const char* tag);
|
|
|
|
// Same as above, but with a known guest VA (for callers like Memory::Zero/
|
|
// Fill/Copy that have the VA but not a single host pointer).
|
|
void check_guest_va_slowpath(uint32_t guest_va, uint64_t value, uint8_t size,
|
|
const char* tag);
|
|
|
|
// Inline hot-path wrappers. Single relaxed atomic load + branch when inactive.
|
|
inline void check_host_write(const void* host_ptr, uint64_t value, uint8_t size,
|
|
const char* tag) {
|
|
if (g_active.load(std::memory_order_relaxed) != 0) [[unlikely]] {
|
|
check_host_write_slowpath(host_ptr, value, size, tag);
|
|
}
|
|
}
|
|
|
|
inline void check_guest_va(uint32_t guest_va, uint64_t value, uint8_t size,
|
|
const char* tag) {
|
|
if (g_active.load(std::memory_order_relaxed) != 0) [[unlikely]] {
|
|
check_guest_va_slowpath(guest_va, value, size, tag);
|
|
}
|
|
}
|
|
|
|
} // namespace audit_68
|
|
} // namespace xe
|
|
|
|
#endif // XENIA_BASE_AUDIT_68_HOST_MEM_WATCH_FWD_H_
|