handoff: VSync/event-wedge fixes + iterate 2.A–2.BC research notes
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>
This commit is contained in:
120
audit-runs/phase-c8-keResetEvent/investigation.md
Normal file
120
audit-runs/phase-c8-keResetEvent/investigation.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# Phase C+8 — KeResetEvent investigation
|
||||
|
||||
## Framing (reading-error #28 verification — MANDATORY)
|
||||
|
||||
Per memory note `phase_c7_keSetEvent_2026_05_14`, reading-error class
|
||||
#28 is **"canary source supersedes NT-doc semantics"**: the predecessor
|
||||
`KeSetEvent` bug was that ours returned the NT-documented "prior signaled
|
||||
state", while canary unconditionally `return 1` from `XEvent::Set`.
|
||||
|
||||
Before writing any fix we **read canary's actual `XEvent::Reset` body**.
|
||||
|
||||
### Canary truth — `xenia-canary/src/xenia/kernel/xevent.cc` (lines 60-82)
|
||||
|
||||
```cpp
|
||||
int32_t XEvent::Set(uint32_t priority_increment, bool wait) {
|
||||
set_priority_increment(priority_increment);
|
||||
event_->Set();
|
||||
return 1;
|
||||
}
|
||||
|
||||
int32_t XEvent::Pulse(uint32_t priority_increment, bool wait) {
|
||||
set_priority_increment(priority_increment);
|
||||
event_->Pulse();
|
||||
return 1;
|
||||
}
|
||||
|
||||
int32_t XEvent::Reset() {
|
||||
event_->Reset();
|
||||
return 1; // <-- HARDCODED CONSTANT 1, exact sibling of Set()
|
||||
}
|
||||
|
||||
void XEvent::Query(uint32_t* out_type, uint32_t* out_state) { ... }
|
||||
void XEvent::Clear() { event_->Reset(); } // NB: used only internally
|
||||
```
|
||||
|
||||
### Canary truth — `xenia-canary/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc`
|
||||
|
||||
```cpp
|
||||
dword_result_t KeResetEvent_entry(pointer_t<X_KEVENT> event_ptr) {
|
||||
auto ev = XObject::GetNativeObject<XEvent>(kernel_state(), event_ptr);
|
||||
if (!ev) {
|
||||
assert_always();
|
||||
return 0;
|
||||
}
|
||||
return ev->Reset(); // <-- returns the 1 from XEvent::Reset
|
||||
}
|
||||
DECLARE_XBOXKRNL_EXPORT1(KeResetEvent, kThreading, kImplemented);
|
||||
```
|
||||
|
||||
```cpp
|
||||
dword_result_t NtClearEvent_entry(dword_t handle) {
|
||||
return xeNtClearEvent(handle);
|
||||
}
|
||||
DECLARE_XBOXKRNL_EXPORT2(NtClearEvent, kThreading, kImplemented,
|
||||
kHighFrequency);
|
||||
```
|
||||
|
||||
`xeNtClearEvent` (search the file) calls `XEvent::Clear()` which is the
|
||||
`void`-returning variant — it does not propagate `Reset()`'s return.
|
||||
`xeNtClearEvent` returns `X_STATUS_SUCCESS` on hit, `X_STATUS_INVALID_HANDLE`
|
||||
on miss. **No out-pointer**. So `NtClearEvent` is NOT in the same class as
|
||||
`NtSetEvent`: it has no PreviousState argument at all.
|
||||
|
||||
### Ours truth pre-fix — `crates/xenia-kernel/src/exports.rs:4160-4172`
|
||||
|
||||
```rust
|
||||
fn ke_reset_event(ctx: &mut PpcContext, mem: &GuestMemory, state: &mut KernelState) {
|
||||
let h = ctx.gpr[3] as u32;
|
||||
ensure_dispatcher_object(state, mem, h);
|
||||
let previous = match state.objects.get_mut(&h) {
|
||||
Some(KernelObject::Event { signaled, .. }) => {
|
||||
let prev = *signaled;
|
||||
*signaled = false;
|
||||
prev as u32
|
||||
}
|
||||
_ => 0,
|
||||
};
|
||||
ctx.gpr[3] = previous as u64; // <-- BUG: returns prior state
|
||||
}
|
||||
```
|
||||
|
||||
Mirror-image of the pre-C+7 `ke_set_event` bug. Ours returns the prior
|
||||
signaled state (`0` since the event had just been reset earlier in the
|
||||
boot); canary returns the constant `1`.
|
||||
|
||||
`nt_clear_event` (4197-4203) already returns `STATUS_SUCCESS` — no out
|
||||
pointer involved. Verified canary parity for that branch.
|
||||
|
||||
### Phase A event at idx 102164 (raw)
|
||||
|
||||
```json
|
||||
canary: {"kind":"kernel.return","payload":{"name":"KeResetEvent",
|
||||
"return_value":1,"status":"0x00000001"},"tid":6,
|
||||
"tid_event_idx":102164}
|
||||
ours: {"kind":"kernel.return","payload":{"name":"KeResetEvent",
|
||||
"return_value":0,"status":"0x00000000"},"tid":1,
|
||||
"tid_event_idx":102164}
|
||||
```
|
||||
|
||||
return_value mismatch exactly matches the canary-hardcoded-`1` premise.
|
||||
|
||||
## Fix shape
|
||||
|
||||
`ke_reset_event`: set return to constant `1` on shadow hit, `0` on miss
|
||||
(canary's `assert_always(); return 0` path). Retain the `*signaled = false`
|
||||
side effect — that is real semantic state used by waiter wake plumbing,
|
||||
and unchanged from pre-fix behavior. Add an `audit_signal`-style record
|
||||
mirroring the predecessor's bookkeeping. Mirror of C+7 `ke_set_event` fix.
|
||||
|
||||
`nt_clear_event`: already canary-parity. No change.
|
||||
|
||||
Body LOC: ~10. Tests: ~4 new (mirrors of C+7's `ke_set_event_*` tests).
|
||||
|
||||
## Cascade (predictions)
|
||||
|
||||
- A=verify canary's return: DONE, `1` confirmed.
|
||||
- B=land fix: ~95% (one-line return change + minor bookkeeping).
|
||||
- C=main chain advances past 102164: ~85% (the bug is the diff key).
|
||||
- D=clean re-validation: ~80%.
|
||||
- E=no escalation: ~95% (small).
|
||||
Reference in New Issue
Block a user