Compare commits
3 Commits
5427f14f1b
...
audit-hand
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbffd8d20c | ||
|
|
17f4612b79 | ||
|
|
33dee75c03 |
@@ -58,6 +58,13 @@ DEFINE_uint32(audit_jit_prolog_mem_dump, 0,
|
|||||||
"and its vtable[0] (first virtual method = bctrl target at "
|
"and its vtable[0] (first virtual method = bctrl target at "
|
||||||
"sub_822F1AA8+0x90). Zero (default) disables.",
|
"sub_822F1AA8+0x90). Zero (default) disables.",
|
||||||
"Auditing");
|
"Auditing");
|
||||||
|
DEFINE_uint32(audit_jit_prolog_r3_bytes, 0x40,
|
||||||
|
"Audit-052 — number of bytes to dump from host(r3) on every "
|
||||||
|
"audit_jit_prolog_pc fire (capped at 256, 16-byte aligned). "
|
||||||
|
"Default 64 (existing behaviour). Set to 80 to capture the "
|
||||||
|
"audit-051 stack-local struct at sub_82452DC0's r31+96 "
|
||||||
|
"(probe sub_8245B000 entry where r3 IS the struct ptr).",
|
||||||
|
"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,
|
||||||
@@ -491,11 +498,16 @@ uint64_t AuditLogJitPrologArgs(void* raw_context, uint64_t /*unused*/) {
|
|||||||
"r6={:08X} r7={:08X} r8={:08X} r9={:08X} r10={:08X} lr={:08X}",
|
"r6={:08X} r7={:08X} r8={:08X} r9={:08X} r10={:08X} lr={:08X}",
|
||||||
pc, tid, r3, r4, r5, r6, r7, r8, r9, r10, lr);
|
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.
|
// Dump N bytes at host(r3) if r3 looks like a plausible guest VA.
|
||||||
|
// N comes from cvar `audit_jit_prolog_r3_bytes` (default 64 = existing
|
||||||
|
// behaviour). Round up to a 16-byte multiple; cap at 256.
|
||||||
|
uint32_t r3_dump_bytes = static_cast<uint32_t>(cvars::audit_jit_prolog_r3_bytes);
|
||||||
|
if (r3_dump_bytes > 256) r3_dump_bytes = 256;
|
||||||
|
r3_dump_bytes = (r3_dump_bytes + 15) & ~15u;
|
||||||
if (r3 >= 0x10000 && r3 < 0xE0000000) {
|
if (r3 >= 0x10000 && r3 < 0xE0000000) {
|
||||||
uint8_t* host = ctx->TranslateVirtual(r3);
|
uint8_t* host = ctx->TranslateVirtual(r3);
|
||||||
if (host) {
|
if (host) {
|
||||||
for (uint32_t off = 0; off < 0x40; off += 16) {
|
for (uint32_t off = 0; off < r3_dump_bytes; off += 16) {
|
||||||
uint32_t d0 = xe::load_and_swap<uint32_t>(host + off + 0);
|
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 d1 = xe::load_and_swap<uint32_t>(host + off + 4);
|
||||||
uint32_t d2 = xe::load_and_swap<uint32_t>(host + off + 8);
|
uint32_t d2 = xe::load_and_swap<uint32_t>(host + off + 8);
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
#include "xenia/base/clock.h"
|
#include "xenia/base/clock.h"
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/base/math.h"
|
#include "xenia/base/math.h"
|
||||||
|
#include "xenia/base/memory.h"
|
||||||
|
#include "xenia/base/string_util.h"
|
||||||
#include "xenia/base/profiling.h"
|
#include "xenia/base/profiling.h"
|
||||||
#include "xenia/base/threading.h"
|
#include "xenia/base/threading.h"
|
||||||
#include "xenia/config.h"
|
#include "xenia/config.h"
|
||||||
@@ -36,6 +38,32 @@ DEFINE_bool(
|
|||||||
"runtime spikes and freezes when playing the game not for the first time.",
|
"runtime spikes and freezes when playing the game not for the first time.",
|
||||||
"GPU");
|
"GPU");
|
||||||
|
|
||||||
|
// Audit memory-watch: poll-based value-change logger for arbitrary guest VAs.
|
||||||
|
// Reads the configured guest virtual address(es) once per vblank (per frame)
|
||||||
|
// from inside GraphicsSystem::MarkVblank() and emits one XELOGKERNEL
|
||||||
|
// "AUDIT-MEM-WATCH" line whenever the read value changes vs the previous
|
||||||
|
// frame. Greppable on the same log stream as AUDIT-HLC / AUDIT-MEM-READ.
|
||||||
|
// Default empty => disabled => zero overhead. Mechanism is poll/value-change
|
||||||
|
// (NOT a write-trap), so it captures WHEN a value changes (vblank index +
|
||||||
|
// emulator instruction count) but NOT the writer guest-PC. To find the writer
|
||||||
|
// PC, pair this with audit_jit_prolog_pc on the suspected writer function.
|
||||||
|
DEFINE_string(
|
||||||
|
audit_mem_watch_addr, "",
|
||||||
|
"Audit memory-watch — comma-separated list of guest virtual addresses "
|
||||||
|
"(hex, e.g. \"0x40d09a40\" or \"0x40d09a40,0x40929c00\") to poll once per "
|
||||||
|
"vblank. On every value change vs the previous frame, emit one "
|
||||||
|
"XELOGKERNEL AUDIT-MEM-WATCH line (watched VA, old value, new value, "
|
||||||
|
"vblank index, instruction count). Empty (default) disables the watch. "
|
||||||
|
"Poll-based value-change log: tells you WHEN a value changes, not the "
|
||||||
|
"writer PC.",
|
||||||
|
"Auditing");
|
||||||
|
DEFINE_uint32(
|
||||||
|
audit_mem_watch_size, 4,
|
||||||
|
"Audit memory-watch — number of bytes to read at each watched VA (1, 2, "
|
||||||
|
"4, or 8). Read big-endian (guest byte order) and compared as a 64-bit "
|
||||||
|
"value. Default 4.",
|
||||||
|
"Auditing");
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
|
|
||||||
@@ -333,12 +361,117 @@ void GraphicsSystem::DispatchInterruptCallback(uint32_t source, uint32_t cpu) {
|
|||||||
interrupt_callback_data_, source, cpu);
|
interrupt_callback_data_, source, cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Audit memory-watch poll. Called once per vblank from MarkVblank(). Parses
|
||||||
|
// the comma-separated VA list from cvars::audit_mem_watch_addr exactly once
|
||||||
|
// (cached), then reads each VA (big-endian, audit_mem_watch_size bytes) and
|
||||||
|
// logs an AUDIT-MEM-WATCH line on every value change vs the previous frame.
|
||||||
|
// Poll/value-change mechanism: captures WHEN, not the writer PC. Zero work
|
||||||
|
// when the cvar is empty.
|
||||||
|
static void AuditMemWatchPoll(Memory* memory, uint64_t vblank_index) {
|
||||||
|
struct WatchEntry {
|
||||||
|
uint32_t va;
|
||||||
|
uint64_t last_value;
|
||||||
|
bool have_last;
|
||||||
|
};
|
||||||
|
static std::vector<WatchEntry> entries;
|
||||||
|
static std::string parsed_spec;
|
||||||
|
static bool parse_failed = false;
|
||||||
|
|
||||||
|
const std::string& spec = cvars::audit_mem_watch_addr;
|
||||||
|
if (spec.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// (Re)parse only when the cvar string changes (set once at startup).
|
||||||
|
if (spec != parsed_spec) {
|
||||||
|
parsed_spec = spec;
|
||||||
|
entries.clear();
|
||||||
|
parse_failed = false;
|
||||||
|
size_t start = 0;
|
||||||
|
while (start <= spec.size()) {
|
||||||
|
size_t comma = spec.find(',', start);
|
||||||
|
std::string tok = spec.substr(
|
||||||
|
start, comma == std::string::npos ? std::string::npos : comma - start);
|
||||||
|
// Trim whitespace.
|
||||||
|
while (!tok.empty() && (tok.front() == ' ' || tok.front() == '\t'))
|
||||||
|
tok.erase(tok.begin());
|
||||||
|
while (!tok.empty() && (tok.back() == ' ' || tok.back() == '\t'))
|
||||||
|
tok.pop_back();
|
||||||
|
if (!tok.empty()) {
|
||||||
|
uint32_t va = 0;
|
||||||
|
try {
|
||||||
|
va = static_cast<uint32_t>(std::stoul(tok, nullptr, 16));
|
||||||
|
} catch (...) {
|
||||||
|
XELOGKERNEL("AUDIT-MEM-WATCH bad VA token '{}' in '{}'", tok, spec);
|
||||||
|
parse_failed = true;
|
||||||
|
va = 0;
|
||||||
|
}
|
||||||
|
if (va != 0) {
|
||||||
|
entries.push_back({va, 0, false});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (comma == std::string::npos) break;
|
||||||
|
start = comma + 1;
|
||||||
|
}
|
||||||
|
XELOGKERNEL("AUDIT-MEM-WATCH armed: {} address(es), size={} bytes",
|
||||||
|
entries.size(),
|
||||||
|
static_cast<uint32_t>(cvars::audit_mem_watch_size));
|
||||||
|
}
|
||||||
|
if (entries.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(void)parse_failed;
|
||||||
|
|
||||||
|
uint32_t size = static_cast<uint32_t>(cvars::audit_mem_watch_size);
|
||||||
|
for (auto& e : entries) {
|
||||||
|
if (e.va < 0x10000 || e.va >= 0xE0000000) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
uint8_t* host = memory->TranslateVirtual(e.va);
|
||||||
|
if (!host) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
uint64_t value = 0;
|
||||||
|
switch (size) {
|
||||||
|
case 1:
|
||||||
|
value = *host;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
value = xe::load_and_swap<uint16_t>(host);
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
value = xe::load_and_swap<uint64_t>(host);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
default:
|
||||||
|
value = xe::load_and_swap<uint32_t>(host);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!e.have_last) {
|
||||||
|
e.have_last = true;
|
||||||
|
e.last_value = value;
|
||||||
|
XELOGKERNEL(
|
||||||
|
"AUDIT-MEM-WATCH va={:08X} init={:016X} vblank={} (first read)",
|
||||||
|
e.va, value, vblank_index);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (value != e.last_value) {
|
||||||
|
XELOGKERNEL(
|
||||||
|
"AUDIT-MEM-WATCH va={:08X} old={:016X} new={:016X} vblank={}",
|
||||||
|
e.va, e.last_value, value, vblank_index);
|
||||||
|
e.last_value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GraphicsSystem::MarkVblank() {
|
void GraphicsSystem::MarkVblank() {
|
||||||
SCOPE_profile_cpu_f("gpu");
|
SCOPE_profile_cpu_f("gpu");
|
||||||
|
|
||||||
// Increment vblank counter (so the game sees us making progress).
|
// Increment vblank counter (so the game sees us making progress).
|
||||||
command_processor_->increment_counter();
|
command_processor_->increment_counter();
|
||||||
|
|
||||||
|
// Audit memory-watch (poll-based; no-op unless audit_mem_watch_addr set).
|
||||||
|
AuditMemWatchPoll(memory_, command_processor_->counter());
|
||||||
|
|
||||||
// TODO(benvanik): we shouldn't need to do the dispatch here, but there's
|
// TODO(benvanik): we shouldn't need to do the dispatch here, but there's
|
||||||
// something wrong and the CP will block waiting for code that
|
// something wrong and the CP will block waiting for code that
|
||||||
// needs to be run in the interrupt.
|
// needs to be run in the interrupt.
|
||||||
|
|||||||
@@ -18,3 +18,8 @@ DEFINE_bool(audit_handle_lifecycle, false,
|
|||||||
"Emit XELOGKERNEL on Event/Semaphore/Wait lifecycle "
|
"Emit XELOGKERNEL on Event/Semaphore/Wait lifecycle "
|
||||||
"(create/set/wait/complete). Audit oracle probe — off by default.",
|
"(create/set/wait/complete). Audit oracle probe — off by default.",
|
||||||
"Auditing");
|
"Auditing");
|
||||||
|
DEFINE_uint32(audit_track_event_handle, 0,
|
||||||
|
"If non-zero and AUDIT-HLC enabled, log underlying X_KEVENT "
|
||||||
|
"guest VA whenever NtCreateEvent returns this handle. "
|
||||||
|
"Audit round-24 oracle probe.",
|
||||||
|
"Auditing");
|
||||||
|
|||||||
@@ -14,5 +14,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);
|
DECLARE_bool(audit_handle_lifecycle);
|
||||||
|
DECLARE_uint32(audit_track_event_handle);
|
||||||
|
|
||||||
#endif // XENIA_KERNEL_KERNEL_FLAGS_H_
|
#endif // XENIA_KERNEL_KERNEL_FLAGS_H_
|
||||||
|
|||||||
@@ -1071,6 +1071,14 @@ void KernelState::CompleteOverlappedEx(uint32_t overlapped_ptr, X_RESULT result,
|
|||||||
auto ev = object_table()->LookupObject<XEvent>(event_handle);
|
auto ev = object_table()->LookupObject<XEvent>(event_handle);
|
||||||
assert_not_null(ev);
|
assert_not_null(ev);
|
||||||
if (ev) {
|
if (ev) {
|
||||||
|
if (cvars::audit_handle_lifecycle) {
|
||||||
|
uint32_t lr =
|
||||||
|
static_cast<uint32_t>(cpu::ThreadState::Get()->context()->lr);
|
||||||
|
XELOGKERNEL(
|
||||||
|
"AUDIT-HLC CompleteOverlappedEx_signal_event handle={:08X} "
|
||||||
|
"kevent_va={:08X} lr={:08X}",
|
||||||
|
uint32_t(event_handle), ev->guest_object(), lr);
|
||||||
|
}
|
||||||
ev->Set(0, false);
|
ev->Set(0, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
|
#include "xenia/cpu/thread_state.h"
|
||||||
#include "xenia/kernel/info/file.h"
|
#include "xenia/kernel/info/file.h"
|
||||||
|
#include "xenia/kernel/kernel_flags.h"
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.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"
|
||||||
@@ -208,6 +210,13 @@ dword_result_t NtReadFile_entry(dword_t file_handle, dword_t event_handle,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ev && signal_event) {
|
if (ev && signal_event) {
|
||||||
|
if (cvars::audit_handle_lifecycle) {
|
||||||
|
uint32_t lr = static_cast<uint32_t>(cpu::ThreadState::Get()->context()->lr);
|
||||||
|
XELOGKERNEL(
|
||||||
|
"AUDIT-HLC NtReadFile_signal_event handle={:08X} kevent_va={:08X} "
|
||||||
|
"lr={:08X}",
|
||||||
|
uint32_t(event_handle), ev->guest_object(), lr);
|
||||||
|
}
|
||||||
ev->Set(0, false);
|
ev->Set(0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,6 +303,13 @@ dword_result_t NtReadFileScatter_entry(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ev && signal_event) {
|
if (ev && signal_event) {
|
||||||
|
if (cvars::audit_handle_lifecycle) {
|
||||||
|
uint32_t lr = static_cast<uint32_t>(cpu::ThreadState::Get()->context()->lr);
|
||||||
|
XELOGKERNEL(
|
||||||
|
"AUDIT-HLC NtReadFileScatter_signal_event handle={:08X} "
|
||||||
|
"kevent_va={:08X} lr={:08X}",
|
||||||
|
uint32_t(event_handle), ev->guest_object(), lr);
|
||||||
|
}
|
||||||
ev->Set(0, false);
|
ev->Set(0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,6 +397,13 @@ dword_result_t NtWriteFile_entry(dword_t file_handle, dword_t event_handle,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ev && signal_event) {
|
if (ev && signal_event) {
|
||||||
|
if (cvars::audit_handle_lifecycle) {
|
||||||
|
uint32_t lr = static_cast<uint32_t>(cpu::ThreadState::Get()->context()->lr);
|
||||||
|
XELOGKERNEL(
|
||||||
|
"AUDIT-HLC NtWriteFile_signal_event handle={:08X} kevent_va={:08X} "
|
||||||
|
"lr={:08X}",
|
||||||
|
uint32_t(event_handle), ev->guest_object(), lr);
|
||||||
|
}
|
||||||
ev->Set(0, false);
|
ev->Set(0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_ob.h"
|
#include "xenia/kernel/xboxkrnl/xboxkrnl_ob.h"
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/cpu/processor.h"
|
#include "xenia/cpu/processor.h"
|
||||||
|
#include "xenia/cpu/thread_state.h"
|
||||||
|
#include "xenia/kernel/kernel_flags.h"
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
||||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_threading.h"
|
#include "xenia/kernel/xboxkrnl/xboxkrnl_threading.h"
|
||||||
@@ -398,6 +400,14 @@ dword_result_t NtDuplicateObject_entry(dword_t handle, lpdword_t new_handle_ptr,
|
|||||||
X_STATUS result =
|
X_STATUS result =
|
||||||
kernel_state()->object_table()->DuplicateHandle(handle, &new_handle);
|
kernel_state()->object_table()->DuplicateHandle(handle, &new_handle);
|
||||||
|
|
||||||
|
if (cvars::audit_handle_lifecycle) {
|
||||||
|
uint32_t lr = static_cast<uint32_t>(cpu::ThreadState::Get()->context()->lr);
|
||||||
|
XELOGKERNEL(
|
||||||
|
"AUDIT-HLC NtDuplicateObject src={:08X} dst={:08X} options={:08X} "
|
||||||
|
"lr={:08X}",
|
||||||
|
uint32_t(handle), uint32_t(new_handle), uint32_t(options), lr);
|
||||||
|
}
|
||||||
|
|
||||||
if (new_handle_ptr) {
|
if (new_handle_ptr) {
|
||||||
*new_handle_ptr = new_handle;
|
*new_handle_ptr = new_handle;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -677,11 +677,26 @@ dword_result_t NtCreateEvent_entry(
|
|||||||
}
|
}
|
||||||
if (cvars::audit_handle_lifecycle) {
|
if (cvars::audit_handle_lifecycle) {
|
||||||
uint32_t lr = static_cast<uint32_t>(cpu::ThreadState::Get()->context()->lr);
|
uint32_t lr = static_cast<uint32_t>(cpu::ThreadState::Get()->context()->lr);
|
||||||
|
uint32_t out_handle = handle_ptr ? uint32_t(*handle_ptr) : 0u;
|
||||||
XELOGKERNEL(
|
XELOGKERNEL(
|
||||||
"AUDIT-HLC NtCreateEvent handle={:08X} type={} initial_state={} "
|
"AUDIT-HLC NtCreateEvent handle={:08X} type={} initial_state={} "
|
||||||
"lr={:08X}",
|
"lr={:08X}",
|
||||||
handle_ptr ? uint32_t(*handle_ptr) : 0u, uint32_t(event_type),
|
out_handle, uint32_t(event_type), uint32_t(initial_state), lr);
|
||||||
uint32_t(initial_state), lr);
|
// Round-24: also log the underlying X_KEVENT guest VA for every
|
||||||
|
// NtCreateEvent so KeSetEvent probes can be cross-referenced by ptr.
|
||||||
|
XELOGKERNEL(
|
||||||
|
"AUDIT-HLC NtCreateEvent_inner handle={:08X} kevent_va={:08X} "
|
||||||
|
"lr={:08X}",
|
||||||
|
out_handle, ev->guest_object(), lr);
|
||||||
|
// When caller pinned a target handle, additionally emit the legacy
|
||||||
|
// _target line so older round-24 grep recipes still match.
|
||||||
|
if (cvars::audit_track_event_handle != 0 &&
|
||||||
|
out_handle == cvars::audit_track_event_handle) {
|
||||||
|
XELOGKERNEL(
|
||||||
|
"AUDIT-HLC NtCreateEvent_target handle={:08X} object_va={:08X} "
|
||||||
|
"lr={:08X}",
|
||||||
|
out_handle, ev->guest_object(), lr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -709,9 +724,24 @@ 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) {
|
if (cvars::audit_handle_lifecycle) {
|
||||||
uint32_t lr = static_cast<uint32_t>(cpu::ThreadState::Get()->context()->lr);
|
auto* ctx = cpu::ThreadState::Get()->context();
|
||||||
XELOGKERNEL("AUDIT-HLC NtSetEvent handle={:08X} lr={:08X}",
|
uint32_t lr = static_cast<uint32_t>(ctx->lr);
|
||||||
uint32_t(handle), lr);
|
// Round-24: walk PPC back-chain for caller's guest LR (same idiom as
|
||||||
|
// NtWaitForSingleObjectEx probe).
|
||||||
|
uint32_t guest_lr = 0;
|
||||||
|
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 NtSetEvent handle={:08X} lr={:08X} guest_lr={:08X}",
|
||||||
|
uint32_t(handle), lr, guest_lr);
|
||||||
}
|
}
|
||||||
return xeNtSetEvent(handle, previous_state_ptr);
|
return xeNtSetEvent(handle, previous_state_ptr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
#include "xenia/base/byte_stream.h"
|
#include "xenia/base/byte_stream.h"
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
|
#include "xenia/cpu/thread_state.h"
|
||||||
|
#include "xenia/kernel/kernel_flags.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
@@ -58,6 +60,13 @@ void XEvent::InitializeNative(void* native_ptr, X_DISPATCH_HEADER* header) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int32_t XEvent::Set(uint32_t priority_increment, bool wait) {
|
int32_t XEvent::Set(uint32_t priority_increment, bool wait) {
|
||||||
|
if (cvars::audit_handle_lifecycle) {
|
||||||
|
auto* ts = cpu::ThreadState::Get();
|
||||||
|
uint32_t lr = ts ? static_cast<uint32_t>(ts->context()->lr) : 0u;
|
||||||
|
XELOGKERNEL(
|
||||||
|
"AUDIT-HLC XEvent::Set handle={:08X} kevent_va={:08X} prio={} lr={:08X}",
|
||||||
|
handle(), guest_object(), priority_increment, lr);
|
||||||
|
}
|
||||||
set_priority_increment(priority_increment);
|
set_priority_increment(priority_increment);
|
||||||
event_->Set();
|
event_->Set();
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user