Source changes (dormant parity infra, retained from iterate 2.AI/2.AO): - xenia-kernel/exports.rs: nt_create_event manual_reset polarity + related event wiring - xenia-gpu/mmio_region.rs: D1MODE_VBLANK_VLINE_STATUS hardcode parity Also lands the audit-runs/ analysis notes (.md/.txt/.json digests) for the iterate 2.x VSync/0x10e8/0x1004 wedge investigation. Raw trace dumps (.jsonl/.gz/.csv/.stdout) and agent worktrees (.claude/) are gitignored as regenerable local artifacts — see memory + HANDOFF for the running findings. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
7.2 KiB
Phase 2.0 — Suspect re-verification (Stage 2, 2026-05-14)
Stage 1 flagged 7 imports as "MATCH-but-suspect": classified MATCH at inventory time but with body shorter than canary, suggesting possible DIVERGENT-shallow status. This document records the verdict for each after byte-level scrutiny of both engines' source.
Methodology: read canary impl + ours impl side-by-side, ask "for the
observable inputs (Phase A kernel.return.payload.* fields, guest-memory
writes flowing to subsequent kernel.call.payload.* fields) is the
output bit-equivalent?". Cite the exact axis when divergent.
Verdict summary
| # | Import | Verdict | In-scope this stage? | Notes |
|---|---|---|---|---|
| A | RtlFillMemoryUlong |
MATCH (false alarm) | n/a | See §A |
| B | XMACreateContext |
DIVERGENT (signature) | NO (T3 audio) | See §B |
| C | XamUserGetSigninState |
DIVERGENT (state coupling) | NO (T3 XAM) | See §C |
| D | XamUserGetXUID |
DIVERGENT (signature + state) | NO (T3 XAM) | See §D |
| E | MmGetPhysicalAddress |
MATCH-by-equivalence (already reclassified in Batch 0) | n/a | See §E |
| F | KeQuerySystemTime |
DIVERGENT (fake constant vs live clock) | DEFER (needs deterministic guest-tick clock; not LOW) | See §F |
| G | KeSetAffinityThread |
DIVERGENT (return mechanism) | YES (Batch 3) | See §G |
Net: 3 false alarms (A, E, by analysis; plus implicit re-confirmation of others as DIVERGENT). 4 actual DIVERGENT cases (B/C/D Tier-3 defer; F defer pending clock infra; G in-scope).
§A RtlFillMemoryUlong — MATCH (false alarm)
Canary (xboxkrnl_rtl.cc:72-83):
uint32_t swapped_pattern = xe::byte_swap(pattern.value());
for (uint32_t n = 0; n < count; n++, p++) {
*p = swapped_pattern; // direct write to uint32_t* on x86 host → LE
}
Ours (exports.rs:2380-2389):
let pattern = ctx.gpr[5] as u32;
for i in 0..count {
mem.write_u32(dest + i * 4, pattern); // GuestMemory.write_u32 uses val.to_be_bytes()
}
Byte-level equivalence: canary writes byte_swap(P) as LE on x86,
producing bytes [P_byte0, P_byte1, P_byte2, P_byte3] (where byte_n
is the n-th byte of the original P in BE convention). Ours's
write_u32 calls val.to_be_bytes() and copies, producing the same
bytes [P_byte0, ...]. For pattern=0x11223344: both produce
[0x11, 0x22, 0x33, 0x44] in memory.
Verdict: MATCH. The Stage 1 endianness suspicion was an artifact
of comparing C++ source semantics (xe::byte_swap is loud) to Rust
source semantics (to_be_bytes is buried in write_u32). The actual
output bytes are bit-identical. No engine fix.
§B XMACreateContext — DIVERGENT (deferred Tier-3)
Canary (xboxkrnl_audio_xma.cc:57): writes allocated context VA
to *context_out_ptr, returns X_STATUS_NO_MEMORY or _SUCCESS in r3
depending on alloc result.
Ours (exports.rs near 3338): allocates a handle, stores in
ctx.gpr[3] as a u32 return. No OUT-ptr write.
Verdict: DIVERGENT (signature mismatch). Tier-3 audio subsystem. Defer to a future XMA/audio session.
§C XamUserGetSigninState — DIVERGENT (deferred Tier-3)
Canary (xam_user.cc:89-99): reads XamState, looks up
user_profile[user_index]->signin_state(). Returns 0 for invalid
index or unsigned user.
Ours (xam.rs near 332): returns 1 for user_index==0, else 0.
Hardcoded.
Verdict: DIVERGENT (state coupling). Tier-3 XAM. Defer.
§D XamUserGetXUID — DIVERGENT (deferred Tier-3)
Canary (xam_user.cc:29-66): 3-param signature
(user_index, type_mask, xuid_ptr); validates each input; reads
profile XUID; writes OUT-ptr.
Ours (xam.rs near 309): 2-param (user_index, xuid_ptr); ignores
type_mask; writes 0; always returns SUCCESS.
Verdict: DIVERGENT (signature + state). Tier-3 XAM. Defer.
§E MmGetPhysicalAddress — MATCH-by-equivalence
Canary (xboxkrnl_memory.cc:642-654): heap-lookup; returns mapped
physical address or 0 on lookup failure.
Ours (exports.rs:705-708): masks input with 0x1FFF_FFFF. For any
address allocated through ours's heap (in the 0x4xxxxxxx range), the
result is identity (i.e. va & 0x1FFF_FFFF is the physical address).
The lookup-failure return path is unreachable for game-allocated VAs.
Verdict: MATCH-by-equivalence. Stage 1 classified STUB; Batch 0 re-classified MATCH (this document confirms). No engine fix in Stage 2. A future Stage may surface a divergence on the lookup-failure branch (e.g. game arithmetic on an unallocated VA), at which point the C+2 deferred 3-physical-heap memory model would need attention.
§F KeQuerySystemTime — DIVERGENT (deferred — clock infra)
Canary (xboxkrnl_threading.cc:458-473): reads
Clock::QueryGuestSystemTime() (a deterministic tick-based guest
clock), writes the u64 FILETIME via OUT-ptr, also updates a
KeTimestampBundle kernel-state struct. Void return.
Ours (exports.rs:495-502): writes a hardcoded fake constant
132_500_000_000_000_000 (≈ year 2021 FILETIME). No clock read, no
timestamp bundle. Void return framing was fixed in Phase C+1.
Verdict: DIVERGENT in semantics. The fake constant has produced Phase A matched-prefix 102,032 because the game has not yet read this value into a control-flow decision. Replacing with a deterministic guest-tick clock requires new infrastructure:
- A
KernelState::guest_filetime()method that derives a u64 FILETIME fromscheduler.current_tick()or similar. - A separate
KeTimestampBundleguest-memory struct allocated at startup and updated on each call (mirrors canary's behavior).
Estimated 60-100 LOC additive plus reading-error #23 risk (the live
clock value flows to the game's branch decisions). Out of scope for
Stage 2 LOW sweep. Recorded in deferred.md.
§G KeSetAffinityThread — DIVERGENT (in-scope Batch 3)
Canary (xboxkrnl_threading.cc:323-346):
dword_result_t KeSetAffinityThread_entry(lpvoid_t thread_ptr,
dword_t affinity,
lpdword_t previous_affinity_ptr) {
if (!affinity) return X_STATUS_INVALID_PARAMETER;
auto thread = XObject::GetNativeObject<XThread>(kernel_state(), thread_ptr);
if (!thread) return X_STATUS_INVALID_HANDLE;
if (previous_affinity_ptr) {
*previous_affinity_ptr = uint32_t(1) << thread->active_cpu();
}
thread->SetAffinity(affinity);
return X_STATUS_SUCCESS;
}
Ours (exports.rs:465-474):
let handle = resolve_pseudo_handle(state, ctx.gpr[3] as u32);
let new_mask = (ctx.gpr[4] as u32) as u8;
let old = state.set_affinity(handle, new_mask, mem);
ctx.gpr[3] = old as u64;
Verdict: DIVERGENT (return mechanism). Canary writes prev to OUT-ptr
(r5 = previous_affinity_ptr) and returns STATUS_SUCCESS (0) in r3.
Ours stores prev in r3 and ignores r5. Game callers expecting status
in r3 will treat ours's prev-affinity as a non-zero NTSTATUS error.
Stage 2 fix (Batch 3): write prev_affinity to *r5, return 0 in
r3, preserve canary's invalid-parameter / invalid-handle early returns.
#23 risk: MED. Game's CRT may have been treating ours's r3 (prev affinity = small bitmask, 1..63 in practice) as a "success" code via 0-vs-nonzero check — i.e. always failing. Fixing to return SUCCESS=0 flips that branch direction. If matched-prefix regresses, revert.