Compare commits
5 Commits
cross-buil
...
dde48f287b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dde48f287b | ||
|
|
fc89e68155 | ||
|
|
289c2f3a3e | ||
|
|
1e5648c9a5 | ||
|
|
d031d7c513 |
@@ -41,6 +41,14 @@
|
|||||||
|
|
||||||
DEFINE_bool(debugprint_trap_log, false,
|
DEFINE_bool(debugprint_trap_log, false,
|
||||||
"Log debugprint traps to the active debugger", "CPU");
|
"Log debugprint traps to the active debugger", "CPU");
|
||||||
|
DEFINE_uint32(audit_jit_prolog_pc, 0,
|
||||||
|
"Guest function entry PC at which to emit one XELOGKERNEL line "
|
||||||
|
"per JIT-compiled entry. When non-zero, the x64 emitter inserts "
|
||||||
|
"a CallNative to AuditLogJitPrologArgs at the start of the JIT "
|
||||||
|
"body of the matching guest function. Dumps r3..r10, LR, and 64 "
|
||||||
|
"bytes at host(r3). Zero (default) disables the probe. Audit-059 "
|
||||||
|
"round 7+ JIT-prolog probe — generic PC-configurable variant.",
|
||||||
|
"Auditing");
|
||||||
DEFINE_bool(ignore_undefined_externs, true,
|
DEFINE_bool(ignore_undefined_externs, true,
|
||||||
"Don't exit when an undefined extern is called.", "CPU");
|
"Don't exit when an undefined extern is called.", "CPU");
|
||||||
DEFINE_bool(emit_source_annotations, false,
|
DEFINE_bool(emit_source_annotations, false,
|
||||||
@@ -270,6 +278,17 @@ bool X64Emitter::Emit(HIRBuilder* builder, EmitFunctionInfo& func_info) {
|
|||||||
count on no other code modifying it. mov(GetMembaseReg(),
|
count on no other code modifying it. mov(GetMembaseReg(),
|
||||||
qword[GetContextReg() + offsetof(ppc::PPCContext, virtual_membase)]);
|
qword[GetContextReg() + offsetof(ppc::PPCContext, virtual_membase)]);
|
||||||
*/
|
*/
|
||||||
|
// Audit-059: PC-configurable JIT-prolog probe. Runtime-gated on the cvar
|
||||||
|
// audit_jit_prolog_pc (uint32; 0 disables). When non-zero and the current
|
||||||
|
// guest function's entry PC matches, emit a single CallNative to
|
||||||
|
// AuditLogJitPrologArgs that dumps r3..r10, LR, and 64 bytes at host(r3).
|
||||||
|
// Emits *before* any body instruction runs, so r3..r10 / LR in PPCContext
|
||||||
|
// still reflect the caller's args (no LOAD/STORE_CONTEXT has executed yet).
|
||||||
|
if (cvars::audit_jit_prolog_pc != 0u &&
|
||||||
|
current_guest_function_ == cvars::audit_jit_prolog_pc) {
|
||||||
|
extern uint64_t AuditLogJitPrologArgs(void* raw_context, uint64_t arg0);
|
||||||
|
CallNative(AuditLogJitPrologArgs, 0);
|
||||||
|
}
|
||||||
// Body.
|
// Body.
|
||||||
auto block = builder->first_block();
|
auto block = builder->first_block();
|
||||||
synchronize_stack_on_next_instruction_ = false;
|
synchronize_stack_on_next_instruction_ = false;
|
||||||
@@ -438,6 +457,55 @@ uint64_t TrapDebugBreak(void* raw_context, uint64_t address) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Audit-059 JIT-prolog probe: dump r3..r10, LR, and 64 bytes at host(r3)
|
||||||
|
// when the guest hits the JIT-compiled entry of the function whose entry PC
|
||||||
|
// matches cvars::audit_jit_prolog_pc. Generic PC-configurable variant of the
|
||||||
|
// round-7 sub_824F7800 hook. The hook logs the current guest PC so multiple
|
||||||
|
// instrumentation campaigns can share log output.
|
||||||
|
uint64_t AuditLogJitPrologArgs(void* raw_context, uint64_t /*unused*/) {
|
||||||
|
auto* ctx = reinterpret_cast<ppc::PPCContext_s*>(raw_context);
|
||||||
|
uint32_t pc = static_cast<uint32_t>(cvars::audit_jit_prolog_pc);
|
||||||
|
uint32_t r3 = static_cast<uint32_t>(ctx->r[3]);
|
||||||
|
uint32_t r4 = static_cast<uint32_t>(ctx->r[4]);
|
||||||
|
uint32_t r5 = static_cast<uint32_t>(ctx->r[5]);
|
||||||
|
uint32_t r6 = static_cast<uint32_t>(ctx->r[6]);
|
||||||
|
uint32_t r7 = static_cast<uint32_t>(ctx->r[7]);
|
||||||
|
uint32_t r8 = static_cast<uint32_t>(ctx->r[8]);
|
||||||
|
uint32_t r9 = static_cast<uint32_t>(ctx->r[9]);
|
||||||
|
uint32_t r10 = static_cast<uint32_t>(ctx->r[10]);
|
||||||
|
uint32_t lr = static_cast<uint32_t>(ctx->lr);
|
||||||
|
|
||||||
|
uint32_t tid = ctx->thread_state ? ctx->thread_state->thread_id() : 0u;
|
||||||
|
|
||||||
|
XELOGKERNEL(
|
||||||
|
"AUDIT-HLC JitProlog pc={:08X} tid={:08X} r3={:08X} r4={:08X} r5={:08X} "
|
||||||
|
"r6={:08X} r7={:08X} r8={:08X} r9={:08X} r10={:08X} lr={:08X}",
|
||||||
|
pc, tid, r3, r4, r5, r6, r7, r8, r9, r10, lr);
|
||||||
|
|
||||||
|
// Dump 64 bytes at host(r3) if r3 looks like a plausible guest VA.
|
||||||
|
if (r3 >= 0x10000 && r3 < 0xE0000000) {
|
||||||
|
uint8_t* host = ctx->TranslateVirtual(r3);
|
||||||
|
if (host) {
|
||||||
|
for (uint32_t off = 0; off < 0x40; off += 16) {
|
||||||
|
uint32_t d0 = xe::load_and_swap<uint32_t>(host + off + 0);
|
||||||
|
uint32_t d1 = xe::load_and_swap<uint32_t>(host + off + 4);
|
||||||
|
uint32_t d2 = xe::load_and_swap<uint32_t>(host + off + 8);
|
||||||
|
uint32_t d3 = xe::load_and_swap<uint32_t>(host + off + 12);
|
||||||
|
XELOGKERNEL(
|
||||||
|
"AUDIT-HLC JitProlog pc={:08X} r3+{:02X}: {:08X} {:08X} {:08X} "
|
||||||
|
"{:08X}",
|
||||||
|
pc, off, d0, d1, d2, d3);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
XELOGKERNEL("AUDIT-HLC JitProlog pc={:08X} r3 translate failed", pc);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
XELOGKERNEL("AUDIT-HLC JitProlog pc={:08X} r3 out of VA range, skipping dump",
|
||||||
|
pc);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void X64Emitter::Trap(uint16_t trap_type) {
|
void X64Emitter::Trap(uint16_t trap_type) {
|
||||||
switch (trap_type) {
|
switch (trap_type) {
|
||||||
case 20:
|
case 20:
|
||||||
|
|||||||
@@ -14,3 +14,7 @@ DEFINE_bool(headless, false,
|
|||||||
"UI");
|
"UI");
|
||||||
DEFINE_bool(log_high_frequency_kernel_calls, false,
|
DEFINE_bool(log_high_frequency_kernel_calls, false,
|
||||||
"Log kernel calls with the kHighFrequency tag.", "Logging");
|
"Log kernel calls with the kHighFrequency tag.", "Logging");
|
||||||
|
DEFINE_bool(audit_handle_lifecycle, false,
|
||||||
|
"Emit XELOGKERNEL on Event/Semaphore/Wait lifecycle "
|
||||||
|
"(create/set/wait/complete). Audit oracle probe — off by default.",
|
||||||
|
"Auditing");
|
||||||
|
|||||||
@@ -13,5 +13,6 @@
|
|||||||
|
|
||||||
DECLARE_bool(headless);
|
DECLARE_bool(headless);
|
||||||
DECLARE_bool(log_high_frequency_kernel_calls);
|
DECLARE_bool(log_high_frequency_kernel_calls);
|
||||||
|
DECLARE_bool(audit_handle_lifecycle);
|
||||||
|
|
||||||
#endif // XENIA_KERNEL_KERNEL_FLAGS_H_
|
#endif // XENIA_KERNEL_KERNEL_FLAGS_H_
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/emulator.h"
|
#include "xenia/emulator.h"
|
||||||
#include "xenia/hid/input_system.h"
|
#include "xenia/hid/input_system.h"
|
||||||
|
#include "xenia/kernel/kernel_flags.h"
|
||||||
#include "xenia/kernel/user_module.h"
|
#include "xenia/kernel/user_module.h"
|
||||||
#include "xenia/kernel/util/shim_utils.h"
|
#include "xenia/kernel/util/shim_utils.h"
|
||||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_memory.h"
|
#include "xenia/kernel/xboxkrnl/xboxkrnl_memory.h"
|
||||||
@@ -1373,6 +1374,13 @@ void KernelState::EmulateCPInterruptDPC(uint32_t interrupt_callback,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cvars::audit_handle_lifecycle) {
|
||||||
|
XELOGKERNEL(
|
||||||
|
"AUDIT-HLC EmulateCPInterruptDPC callback={:08X} data={:08X} source={} "
|
||||||
|
"cpu={}",
|
||||||
|
interrupt_callback, interrupt_callback_data, source, cpu);
|
||||||
|
}
|
||||||
|
|
||||||
auto thread = kernel::XThread::GetCurrentThread();
|
auto thread = kernel::XThread::GetCurrentThread();
|
||||||
assert_not_null(thread);
|
assert_not_null(thread);
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_threading.h"
|
#include "xenia/kernel/xboxkrnl/xboxkrnl_threading.h"
|
||||||
|
#include <atomic>
|
||||||
#include "xenia/base/atomic.h"
|
#include "xenia/base/atomic.h"
|
||||||
#include "xenia/base/clock.h"
|
#include "xenia/base/clock.h"
|
||||||
#include "xenia/base/platform.h"
|
#include "xenia/base/platform.h"
|
||||||
#include "xenia/cpu/processor.h"
|
#include "xenia/cpu/processor.h"
|
||||||
|
#include "xenia/kernel/kernel_flags.h"
|
||||||
#include "xenia/kernel/util/shim_utils.h"
|
#include "xenia/kernel/util/shim_utils.h"
|
||||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
||||||
#include "xenia/kernel/xsemaphore.h"
|
#include "xenia/kernel/xsemaphore.h"
|
||||||
@@ -22,6 +24,9 @@ namespace xe {
|
|||||||
namespace kernel {
|
namespace kernel {
|
||||||
namespace xboxkrnl {
|
namespace xboxkrnl {
|
||||||
|
|
||||||
|
// AUDIT-HLC: one-shot guard for silph::WorkerCtx context dump (round5).
|
||||||
|
static std::atomic<bool> g_audit_silph_ctx_dumped{false};
|
||||||
|
|
||||||
// r13 + 0x100: pointer to thread local state
|
// r13 + 0x100: pointer to thread local state
|
||||||
// Thread local state:
|
// Thread local state:
|
||||||
// 0x058: kernel time
|
// 0x058: kernel time
|
||||||
@@ -547,6 +552,48 @@ uint32_t xeKeSetEvent(X_KEVENT* event_ptr, uint32_t increment, uint32_t wait) {
|
|||||||
|
|
||||||
dword_result_t KeSetEvent_entry(pointer_t<X_KEVENT> event_ptr,
|
dword_result_t KeSetEvent_entry(pointer_t<X_KEVENT> event_ptr,
|
||||||
dword_t increment, dword_t wait) {
|
dword_t increment, dword_t wait) {
|
||||||
|
if (cvars::audit_handle_lifecycle) {
|
||||||
|
uint32_t lr = static_cast<uint32_t>(cpu::ThreadState::Get()->context()->lr);
|
||||||
|
XELOGKERNEL("AUDIT-HLC KeSetEvent guest_ptr={:08X} lr={:08X}",
|
||||||
|
uint32_t(event_ptr.guest_address()), lr);
|
||||||
|
// AUDIT-HLC round5: one-shot hexdump of the silph::WorkerCtx context when
|
||||||
|
// KeSetEvent first fires into the silph UI PKEVENT cluster
|
||||||
|
// (0xBCE25214/24/34/44 in current builds; widened a bit for allocator
|
||||||
|
// drift). Used by iterate 2.BF context-replication.
|
||||||
|
uint32_t ev_addr = uint32_t(event_ptr.guest_address());
|
||||||
|
if (ev_addr >= 0xBCE25200 && ev_addr < 0xBCE25300) {
|
||||||
|
if (!g_audit_silph_ctx_dumped.exchange(true)) {
|
||||||
|
// Events live at ctx+0x54, +0x64, +0x74, +0x84 (16-byte stride).
|
||||||
|
// Compute ctx_base assuming the canonical layout (lowest event at
|
||||||
|
// ctx+0x54 → ctx_base = 0xBCE251C0 when ev_addr == 0xBCE25214).
|
||||||
|
uint32_t event_offset_in_ctx = ev_addr - 0xBCE251C0;
|
||||||
|
uint32_t ctx_base = ev_addr - event_offset_in_ctx;
|
||||||
|
auto* ctx = cpu::ThreadState::Get()->context();
|
||||||
|
uint8_t* host = ctx->TranslateVirtual(ctx_base);
|
||||||
|
XELOGKERNEL(
|
||||||
|
"AUDIT-HLC silph_ctx_dump ctx_base={:08X} (event {:08X} at +{:02X})",
|
||||||
|
ctx_base, ev_addr, event_offset_in_ctx);
|
||||||
|
for (uint32_t off = 0; off < 0x300; off += 16) {
|
||||||
|
uint32_t d0 = xe::load_and_swap<uint32_t>(host + off + 0);
|
||||||
|
uint32_t d1 = xe::load_and_swap<uint32_t>(host + off + 4);
|
||||||
|
uint32_t d2 = xe::load_and_swap<uint32_t>(host + off + 8);
|
||||||
|
uint32_t d3 = xe::load_and_swap<uint32_t>(host + off + 12);
|
||||||
|
XELOGKERNEL("AUDIT-HLC DUMP {:08X}: {:08X} {:08X} {:08X} {:08X}",
|
||||||
|
ctx_base + off, d0, d1, d2, d3);
|
||||||
|
}
|
||||||
|
XELOGKERNEL("AUDIT-HLC silph_ctx event-slots:");
|
||||||
|
for (uint32_t i = 0; i < 8; ++i) {
|
||||||
|
uint32_t ev_off = 0x54 + i * 0x10;
|
||||||
|
uint32_t hdr = xe::load_and_swap<uint32_t>(host + ev_off + 0);
|
||||||
|
uint32_t state = xe::load_and_swap<uint32_t>(host + ev_off + 4);
|
||||||
|
XELOGKERNEL(
|
||||||
|
"AUDIT-HLC slot[{}] off=+{:02X} addr={:08X} hdr={:08X} "
|
||||||
|
"state={:08X}",
|
||||||
|
i, ev_off, ctx_base + ev_off, hdr, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return xeKeSetEvent(event_ptr, increment, wait);
|
return xeKeSetEvent(event_ptr, increment, wait);
|
||||||
}
|
}
|
||||||
DECLARE_XBOXKRNL_EXPORT2(KeSetEvent, kThreading, kImplemented, kHighFrequency);
|
DECLARE_XBOXKRNL_EXPORT2(KeSetEvent, kThreading, kImplemented, kHighFrequency);
|
||||||
@@ -604,6 +651,14 @@ dword_result_t NtCreateEvent_entry(
|
|||||||
if (handle_ptr) {
|
if (handle_ptr) {
|
||||||
*handle_ptr = ev->handle();
|
*handle_ptr = ev->handle();
|
||||||
}
|
}
|
||||||
|
if (cvars::audit_handle_lifecycle) {
|
||||||
|
uint32_t lr = static_cast<uint32_t>(cpu::ThreadState::Get()->context()->lr);
|
||||||
|
XELOGKERNEL(
|
||||||
|
"AUDIT-HLC NtCreateEvent handle={:08X} type={} initial_state={} "
|
||||||
|
"lr={:08X}",
|
||||||
|
handle_ptr ? uint32_t(*handle_ptr) : 0u, uint32_t(event_type),
|
||||||
|
uint32_t(initial_state), lr);
|
||||||
|
}
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
DECLARE_XBOXKRNL_EXPORT1(NtCreateEvent, kThreading, kImplemented);
|
DECLARE_XBOXKRNL_EXPORT1(NtCreateEvent, kThreading, kImplemented);
|
||||||
@@ -629,6 +684,11 @@ uint32_t xeNtSetEvent(uint32_t handle, xe::be<uint32_t>* previous_state_ptr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dword_result_t NtSetEvent_entry(dword_t handle, lpdword_t previous_state_ptr) {
|
dword_result_t NtSetEvent_entry(dword_t handle, lpdword_t previous_state_ptr) {
|
||||||
|
if (cvars::audit_handle_lifecycle) {
|
||||||
|
uint32_t lr = static_cast<uint32_t>(cpu::ThreadState::Get()->context()->lr);
|
||||||
|
XELOGKERNEL("AUDIT-HLC NtSetEvent handle={:08X} lr={:08X}",
|
||||||
|
uint32_t(handle), lr);
|
||||||
|
}
|
||||||
return xeNtSetEvent(handle, previous_state_ptr);
|
return xeNtSetEvent(handle, previous_state_ptr);
|
||||||
}
|
}
|
||||||
DECLARE_XBOXKRNL_EXPORT2(NtSetEvent, kThreading, kImplemented, kHighFrequency);
|
DECLARE_XBOXKRNL_EXPORT2(NtSetEvent, kThreading, kImplemented, kHighFrequency);
|
||||||
@@ -1003,9 +1063,43 @@ dword_result_t NtWaitForSingleObjectEx_entry(dword_t object_handle,
|
|||||||
dword_t wait_mode,
|
dword_t wait_mode,
|
||||||
dword_t alertable,
|
dword_t alertable,
|
||||||
lpqword_t timeout_ptr) {
|
lpqword_t timeout_ptr) {
|
||||||
|
uint32_t lr_enter = 0;
|
||||||
|
uint32_t guest_lr = 0;
|
||||||
|
if (cvars::audit_handle_lifecycle) {
|
||||||
|
auto* ctx = cpu::ThreadState::Get()->context();
|
||||||
|
lr_enter = static_cast<uint32_t>(ctx->lr);
|
||||||
|
// Walk one PPC stack frame up from the wait wrapper's frame to recover
|
||||||
|
// the GUEST caller's LR (lr_enter is just the kernel-internal wait
|
||||||
|
// wrapper's return address; we want the function that called *it*).
|
||||||
|
// Xbox 360 PPC convention: prologue stores caller's LR at [old_sp - 8]
|
||||||
|
// *before* bumping r1 to the new frame, so from any frame, the LR saved
|
||||||
|
// by that frame's prologue lives at (back_chain - 8). See xenia-rs
|
||||||
|
// walk_guest_back_chain in crates/xenia-kernel/src/state.rs.
|
||||||
|
uint32_t sp = static_cast<uint32_t>(ctx->r[1]);
|
||||||
|
uint32_t back1 = xe::load_and_swap<uint32_t>(ctx->TranslateVirtual(sp));
|
||||||
|
if (back1) {
|
||||||
|
uint32_t back2 =
|
||||||
|
xe::load_and_swap<uint32_t>(ctx->TranslateVirtual(back1));
|
||||||
|
if (back2) {
|
||||||
|
guest_lr = xe::load_and_swap<uint32_t>(
|
||||||
|
ctx->TranslateVirtual(back2 - 8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
XELOGKERNEL(
|
||||||
|
"AUDIT-HLC NtWaitForSingleObjectEx handle={:08X} alertable={} "
|
||||||
|
"lr={:08X} guest_lr={:08X}",
|
||||||
|
uint32_t(object_handle), uint32_t(alertable), lr_enter, guest_lr);
|
||||||
|
}
|
||||||
uint64_t timeout = timeout_ptr ? static_cast<uint64_t>(*timeout_ptr) : 0u;
|
uint64_t timeout = timeout_ptr ? static_cast<uint64_t>(*timeout_ptr) : 0u;
|
||||||
return NtWaitForSingleObjectEx(object_handle, wait_mode, alertable,
|
uint32_t result = NtWaitForSingleObjectEx(object_handle, wait_mode, alertable,
|
||||||
timeout_ptr ? &timeout : nullptr);
|
timeout_ptr ? &timeout : nullptr);
|
||||||
|
if (cvars::audit_handle_lifecycle) {
|
||||||
|
XELOGKERNEL(
|
||||||
|
"AUDIT-HLC NtWaitForSingleObjectEx_done handle={:08X} result={:08X} "
|
||||||
|
"lr={:08X} guest_lr={:08X}",
|
||||||
|
uint32_t(object_handle), result, lr_enter, guest_lr);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
DECLARE_XBOXKRNL_EXPORT3(NtWaitForSingleObjectEx, kThreading, kImplemented,
|
DECLARE_XBOXKRNL_EXPORT3(NtWaitForSingleObjectEx, kThreading, kImplemented,
|
||||||
kBlocking, kHighFrequency);
|
kBlocking, kHighFrequency);
|
||||||
|
|||||||
Reference in New Issue
Block a user