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:
MechaCat02
2026-06-05 07:19:08 +02:00
parent acd1656753
commit ef93a4fa14
620 changed files with 108303 additions and 1 deletions

View File

@@ -0,0 +1,20 @@
# Phase 3 — Additional fixes
**None.**
Phase 0 audit (`hallucination-audit.md`) covered all 147 xboxkrnl ords
ours registers. Findings:
* 2 hallucinations (ord 0x82, 0x98) → fixed in Phase 2.
* 11 class-E candidates → 1 already C+6, 9 fixed in Phase 1 plus 0x98
fixed in Phase 2.
* 0 ghost ords.
* 145 MATCH (name agrees with canary table).
XAM table audit deferred — Stage 1 inventory classified all XAM
registrations as MATCH at the time of authoring; no name-lookup
asymmetries surfaced during C+6 framing diagnosis on the XAM
sub-chains. If a future Phase C+N first divergence surfaces an XAM
call-name mismatch, run the same audit pattern on
`crates/xenia-kernel/src/xam.rs` against
`xenia-canary/src/xenia/kernel/xam/xam_table.inc`.

View File

@@ -0,0 +1,195 @@
# Phase A diff report
**This report is the output of Phase A's diff harness. Divergences
shown here are INPUT for Phase B (first-divergence localization),
not findings of Phase A.** Phase A's job is to make the harness
itself correct, not to analyze what it surfaces.
## Summary
| canary_tid | ours_tid | matched | canary_total | ours_total | first_divergence_at |
|---|---|---|---|---|---|
| 4 | 11 | 5 | 47573 | 9 | 5 |
| 6 | 1 | 102158 | 329948 | 108486 | 102158 |
| 7 | 2 | 26 | 29 | 30 | 26 |
| 12 | 7 | 2 | 6689 | 3 | 2 |
| 14 | 9 | 39 | 1371603 | 75 | 39 |
| 15 | 10 | 15 | 863209 | 15 | — |
## canary_tid=4 → ours_tid=11
First divergence at `tid_event_idx=5`: payload.return_value: canary=1 ours=0
**Pre-context (last 5 matching events):**
```
canary: [0] import.call RtlEnterCriticalSection
ours: [0] import.call RtlEnterCriticalSection
canary: [1] kernel.call RtlEnterCriticalSection
ours: [1] kernel.call RtlEnterCriticalSection
canary: [2] kernel.return RtlEnterCriticalSection
ours: [2] kernel.return RtlEnterCriticalSection
canary: [3] import.call KeSetEvent
ours: [3] import.call KeSetEvent
canary: [4] kernel.call KeSetEvent
ours: [4] kernel.call KeSetEvent
```
**Divergent event:**
```
canary: [5] kernel.return KeSetEvent
ours: [5] kernel.return KeSetEvent
```
**Next event after the divergence (if any):**
```
canary: [6] import.call KeWaitForMultipleObjects
ours: [6] import.call KeWaitForMultipleObjects
```
**Raw events (JSON):**
```json
{"deterministic": true, "engine": "canary", "guest_cycle": 0, "host_ns": 1080594600, "kind": "kernel.return", "payload": {"name": "KeSetEvent", "return_value": 1, "side_effects": [], "status": "0x00000001"}, "schema_version": 1, "tid": 4, "tid_event_idx": 5}
{"deterministic": true, "engine": "ours", "guest_cycle": 33, "host_ns": 1666588967, "kind": "kernel.return", "payload": {"name": "KeSetEvent", "return_value": 0, "side_effects": [], "status": "0x00000000"}, "schema_version": 1, "tid": 11, "tid_event_idx": 5}
```
## canary_tid=6 → ours_tid=1
First divergence at `tid_event_idx=102158`: payload.return_value: canary=1 ours=0
**Pre-context (last 5 matching events):**
```
canary: [102153] import.call XamTaskSchedule
ours: [102153] import.call XamTaskSchedule
canary: [102154] kernel.call XamTaskSchedule
ours: [102154] kernel.call XamTaskSchedule
canary: [102155] kernel.return XamTaskSchedule
ours: [102155] kernel.return XamTaskSchedule
canary: [102156] import.call XamTaskCloseHandle
ours: [102156] import.call XamTaskCloseHandle
canary: [102157] kernel.call XamTaskCloseHandle
ours: [102157] kernel.call XamTaskCloseHandle
```
**Divergent event:**
```
canary: [102158] kernel.return XamTaskCloseHandle
ours: [102158] kernel.return XamTaskCloseHandle
```
**Next event after the divergence (if any):**
```
canary: [102159] import.call KeWaitForSingleObject
ours: [102159] import.call KeWaitForSingleObject
```
**Raw events (JSON):**
```json
{"deterministic": true, "engine": "canary", "guest_cycle": 0, "host_ns": 727355400, "kind": "kernel.return", "payload": {"name": "XamTaskCloseHandle", "return_value": 1, "side_effects": [], "status": "0x00000001"}, "schema_version": 1, "tid": 6, "tid_event_idx": 102158}
{"deterministic": true, "engine": "ours", "guest_cycle": 5378571, "host_ns": 453853532, "kind": "kernel.return", "payload": {"name": "XamTaskCloseHandle", "return_value": 0, "side_effects": [], "status": "0x00000000"}, "schema_version": 1, "tid": 1, "tid_event_idx": 102158}
```
## canary_tid=7 → ours_tid=2
First divergence at `tid_event_idx=26`: payload.return_value: canary=1 ours=0
**Pre-context (last 5 matching events):**
```
canary: [21] import.call ExRegisterTitleTerminateNotification
ours: [21] import.call ExRegisterTitleTerminateNotification
canary: [22] kernel.call ExRegisterTitleTerminateNotification
ours: [22] kernel.call ExRegisterTitleTerminateNotification
canary: [23] kernel.return ExRegisterTitleTerminateNotification
ours: [23] kernel.return ExRegisterTitleTerminateNotification
canary: [24] import.call KeSetEvent
ours: [24] import.call KeSetEvent
canary: [25] kernel.call KeSetEvent
ours: [25] kernel.call KeSetEvent
```
**Divergent event:**
```
canary: [26] kernel.return KeSetEvent
ours: [26] kernel.return KeSetEvent
```
**Next event after the divergence (if any):**
```
canary: [27] import.call KeWaitForSingleObject
ours: [27] import.call KeWaitForSingleObject
```
**Raw events (JSON):**
```json
{"deterministic": true, "engine": "canary", "guest_cycle": 0, "host_ns": 729504400, "kind": "kernel.return", "payload": {"name": "KeSetEvent", "return_value": 1, "side_effects": [], "status": "0x00000001"}, "schema_version": 1, "tid": 7, "tid_event_idx": 26}
{"deterministic": true, "engine": "ours", "guest_cycle": 4262, "host_ns": 454097237, "kind": "kernel.return", "payload": {"name": "KeSetEvent", "return_value": 0, "side_effects": [], "status": "0x00000000"}, "schema_version": 1, "tid": 2, "tid_event_idx": 26}
```
## canary_tid=12 → ours_tid=7
First divergence at `tid_event_idx=2`: payload.return_value: canary=258 ours=0
**Pre-context (last 5 matching events):**
```
canary: [0] import.call KeWaitForSingleObject
ours: [0] import.call KeWaitForSingleObject
canary: [1] kernel.call KeWaitForSingleObject
ours: [1] kernel.call KeWaitForSingleObject
```
**Divergent event:**
```
canary: [2] kernel.return KeWaitForSingleObject
ours: [2] kernel.return KeWaitForSingleObject
```
**Next event after the divergence (if any):**
```
canary: [3] import.call RtlEnterCriticalSection
ours: <end of stream>
```
**Raw events (JSON):**
```json
{"deterministic": true, "engine": "canary", "guest_cycle": 0, "host_ns": 904485700, "kind": "kernel.return", "payload": {"name": "KeWaitForSingleObject", "return_value": 258, "side_effects": [], "status": "0x00000102"}, "schema_version": 1, "tid": 12, "tid_event_idx": 2}
{"deterministic": true, "engine": "ours", "guest_cycle": 30, "host_ns": 480331827, "kind": "kernel.return", "payload": {"name": "KeWaitForSingleObject", "return_value": 0, "side_effects": [], "status": "0x00000000"}, "schema_version": 1, "tid": 7, "tid_event_idx": 2}
```
## canary_tid=14 → ours_tid=9
First divergence at `tid_event_idx=39`: payload.ord: canary=503 ours=293
**Pre-context (last 5 matching events):**
```
canary: [34] kernel.call KeReleaseSpinLockFromRaisedIrql
ours: [34] kernel.call KeReleaseSpinLockFromRaisedIrql
canary: [35] kernel.return KeReleaseSpinLockFromRaisedIrql
ours: [35] kernel.return KeReleaseSpinLockFromRaisedIrql
canary: [36] import.call KfLowerIrql
ours: [36] import.call KfLowerIrql
canary: [37] kernel.call KfLowerIrql
ours: [37] kernel.call KfLowerIrql
canary: [38] kernel.return KfLowerIrql
ours: [38] kernel.return KfLowerIrql
```
**Divergent event:**
```
canary: [39] import.call XAudioGetVoiceCategoryVolumeChangeMask
ours: [39] import.call RtlEnterCriticalSection
```
**Next event after the divergence (if any):**
```
canary: [40] kernel.call XAudioGetVoiceCategoryVolumeChangeMask
ours: [40] kernel.call RtlEnterCriticalSection
```
**Raw events (JSON):**
```json
{"deterministic": true, "engine": "canary", "guest_cycle": 0, "host_ns": 1082563200, "kind": "import.call", "payload": {"module": "xboxkrnl.exe", "name": "XAudioGetVoiceCategoryVolumeChangeMask", "ord": 503}, "schema_version": 1, "tid": 14, "tid_event_idx": 39}
{"deterministic": true, "engine": "ours", "guest_cycle": 417, "host_ns": 1666772300, "kind": "import.call", "payload": {"module": "xboxkrnl.exe", "name": "RtlEnterCriticalSection", "ord": 293}, "schema_version": 1, "tid": 9, "tid_event_idx": 39}
```
## canary_tid=15 → ours_tid=10
No divergence within the 15 compared events (canary has 863209, ours has 15).

View File

@@ -0,0 +1,10 @@
{
"instructions": 50000000,
"imports": 40470,
"unimpl": 0,
"draws": 0,
"swaps": 1,
"unique_render_targets": 0,
"shader_blobs_live": 0,
"texture_cache_entries": 0
}

View File

@@ -0,0 +1,10 @@
{
"instructions": 50000000,
"imports": 40470,
"unimpl": 0,
"draws": 0,
"swaps": 1,
"unique_render_targets": 0,
"shader_blobs_live": 0,
"texture_cache_entries": 0
}

View File

@@ -0,0 +1,10 @@
{
"instructions": 50000000,
"imports": 40470,
"unimpl": 0,
"draws": 0,
"swaps": 1,
"unique_render_targets": 0,
"shader_blobs_live": 0,
"texture_cache_entries": 0
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,101 @@
# Phase 0 — Hallucination audit (Phase C+6½)
## Method
For every xboxkrnl.exe ord ours registers in `register_exports()`,
cross-reference against canary's `xboxkrnl_table.inc` (authoritative
Xbox 360 ord→name mapping). Three classes of mismatch are possible:
1. **MATCH** — ours's name == canary's name. Most exports.
2. **HALLUCINATION** — ours's name ≠ canary's name at the same ord.
The hallucinated name may be a real NT function that exists at a
*different* ord on Xbox 360, making it look plausible.
3. **GHOST ORD** — ours registers an ord canary's table doesn't have.
For each HALLUCINATION, additional severity classification:
* **CRITICAL** — ours's stub body performs different semantics than
canary's named function. Game gets wrong data on every call.
* **HIGH** — ours's stub body is harmless (e.g. `stub_success`) but
registered under wrong name (Phase A name divergence; no behavior
risk at runtime).
* **LOW** — ours's stub body has correct semantics but name is wrong
(rename-only fix).
## Headline
| count | category |
|------:|---|
| 147 | total xboxkrnl ords ours registers |
| 145 | MATCH (name agrees with canary table) |
| **2** | **HALLUCINATION** |
| 0 | GHOST ORD (no ord registered in ours that canary's table lacks) |
## Hallucinations found
### ord 0x82 — `KeQueryInterruptTime`
| | |
|---|---|
| canary table | `xboxkrnl_table.inc:130``KeQueryInterruptTime` |
| canary shim | `xboxkrnl_misc.cc:119-127` — DECLARED, both engines emit Phase A events |
| ours-pre-fix | `KeQueryIdealProcessor` body — returns `thread.ideal_processor` u8 via gpr[3] |
| canary semantics | Returns 64-bit `bundle->interrupt_time` (kernel timer ISR's monotonic counter) via gpr[3] |
| severity | **CRITICAL** — wildly different semantics. Game code reading the system interrupt-time counter for timing/scheduling decisions received a 1-byte processor index. Counter-value would clamp to 0..7 (or 0xFF for unset), producing nonsensical "timestamps" smaller than 1µs. |
| live in current run | NO (0 hits in 50M-instr Phase A log; latent) |
| fix | New body `ke_query_interrupt_time` returns a synthetic monotonic u64 (`0x0000_0001_0000_0000`), matching the `KeQuerySystemTime` static-fake pattern. Renamed in registration. |
### ord 0x98 — `KeSetBackgroundProcessors`
| | |
|---|---|
| canary table | `xboxkrnl_table.inc:166``KeSetBackgroundProcessors` |
| canary shim | **NOT declared** (table-entry-only — class E, syscall-thunk path emits NO Phase A events) |
| ours-pre-fix | `KeSetIdealProcessor` body — sets `thread.ideal_processor = ctx.gpr[4] as u8`, returns prior value |
| canary semantics | Configures background-processor affinity mask; canary stub is no-op (no DECLARE shim). |
| severity | **CRITICAL** — ours actively performed wrong state mutation under the wrong semantic name. Game calling `KeSetBackgroundProcessors` to declare its CPUs-for-background-work set was instead pinning the calling thread's ideal-processor hint, which affects spawn-sibling placement decisions later. |
| live in current run | NO (0 hits in 50M-instr Phase A log; latent) |
| fix | Body replaced with `stub_success` no-op (matching canary's effective behavior since canary has no shim). Underlying `Scheduler::set_ideal_ref`/`ideal_ref` retained (used by `NtSetInformationThread` info-class `ThreadIdealProcessor`). Registered via `register_unimplemented_export` so Phase A emitter stays silent (matching canary's syscall-thunk). |
## No other hallucinations
Cross-reference covered all 147 ours-registered xboxkrnl ords. Both
known hallucinations were the ones C+6 had already flagged out-of-scope
for that session. **No new hallucinations surfaced.**
XAM table not audited in this pass (ours's registrations are in
`crates/xenia-kernel/src/xam.rs` and Stage 1 already classified them
MATCH at the same level of trust as xboxkrnl pre-C+6; no name-lookup
asymmetries surfaced during C+6 framing diagnosis). Recommended as
follow-up if a future Phase C+N first divergence is an XAM call-name
mismatch.
## Class E sister sweep candidates (Phase 1)
In addition to the 2 hallucinations, audited which ours-registered ords
have NO `DECLARE_XBOXKRNL_EXPORT` shim in canary — these are class-E
candidates for `register_unimplemented_export`:
| ord | canary name | ours name | already class-E? |
|---|---|---|---|
| 0x003 | DbgPrint | DbgPrint | NO — needs fix |
| 0x03C | IoDismountVolumeByFileHandle | IoDismountVolumeByFileHandle | YES (C+6) |
| 0x098 | KeSetBackgroundProcessors | (hallucinated KeSetIdealProcessor) | NO — Phase 2 |
| 0x119 | RtlCaptureContext | RtlCaptureContext | NO — needs fix |
| 0x13B | sprintf | sprintf | NO — needs fix |
| 0x147 | RtlUnwind | RtlUnwind | NO — needs fix |
| 0x14D | _vsnprintf | _vsnprintf | NO — needs fix |
| 0x1A5 | __C_specific_handler | __C_specific_handler | NO — needs fix |
| 0x257 | XeKeysConsoleSignatureVerification | XeKeysConsoleSignatureVerification | NO — needs fix |
| 0x259 | StfsCreateDevice | StfsCreateDevice | NO — needs fix |
| 0x25A | StfsControlDevice | StfsControlDevice | NO — needs fix |
11 ords total need the class-E rewire — 1 already fixed (0x03C in C+6),
9 + the 0x98 hallucination fixed here.
The C+6 sister-bugs note projected "12+" — the actual count is 11 (10
new + 1 already done). The two "unnamed" in the original list resolve
to **DbgPrint** (ord 0x03, always present in C+6's analysis but not
named explicitly in the sister-bugs list) and **KeSetBackgroundProcessors**
(ord 0x98, originally listed under "different class" hallucinations but
ALSO a class-E candidate since canary has no shim).

View File

@@ -0,0 +1,142 @@
# Phase C+6½ — re-validation (HARD GATES)
## Gate 1 — Determinism (cvar-OFF, ours)
3 fresh runs of `check -n 50000000 --stable-digest` post-fix:
| run | digest md5 |
|-----|------------|
| 1 | c6d895829b4611964978990ae1cb8a6a |
| 2 | c6d895829b4611964978990ae1cb8a6a |
| 3 | c6d895829b4611964978990ae1cb8a6a |
| C+6 baseline (cvar-OFF) | `c6d895829b4611964978990ae1cb8a6a` |
**Result**: ✅ byte-identical across 3 runs AND **UNCHANGED from C+6**.
Why digest unchanged despite Phase 2 behavior fixes:
* Phase 1 sister sweep is purely emitter-suppression (cvar-OFF inert).
* Phase 2 fixes ord 0x82 (`ke_query_interrupt_time`) and 0x98
(`stub_success`) — but neither ord is called in the 50M-instr window
(verified: 0 hits in event log for both names, before AND after).
The new bodies are LATENT for now.
Cvar-OFF baseline preserved. No regression to determinism.
## Gate 2 — Phase B `image_canonical_sha256`
Phase B snapshot captured to `snap/ours/`:
```
$ grep image_loaded_sha256 snap/ours/config.json
"image_loaded_sha256": "ea8d160e9369328a5b922258a92113efb8d7ce3e1a5c12cc521e375985c91c18",
```
Matches the Phase-A/B verify baseline ✅. The fix touches only
kernel-export shim/emitter — no PE image bytes modified.
## Gate 3 — Phase A matched-prefix extension (KEY METRIC)
Diffed `/tmp/c6half_phasea.jsonl` (50M-instr Phase A capture post-fix)
against `audit-runs/phase-c-first-divergence/phase-a/canary.jsonl`.
| chain | C+6 | C+6½ | Δ |
|---|---|---|---|
| canary tid=6 → ours tid=1 (main) | 102158 | **102158** | **0** (preserved) |
| canary tid=4 → ours tid=11 | 5 | 5 | 0 |
| **canary tid=7 → ours tid=2** | **15** | **26** | **+11** ✅ |
| canary tid=12 → ours tid=7 | 2 | 2 | 0 |
| canary tid=14 → ours tid=9 | 39 | 39 | 0 |
| canary tid=15 → ours tid=10 | (no div) | (no div) | 0 |
**Gate 3 ✅**:
* tid=7→tid=2 chain advanced from 15 to 26 (+11) — direct payoff of
the `StfsCreateDevice` class-E fix (C+6 predicted this).
* Main chain unchanged at 102158 (no regression).
* All other chains stable.
### New tid=7→tid=2 first divergence
idx=26: `kernel.return KeSetEvent` `return_value=1 (canary) vs 0 (ours)`.
Same divergence class as tid=4→tid=11 (also at idx=5 on KeSetEvent).
`KeSetEvent` semantically returns "the previous signaled state of the
event" — canary returns 1 (event was already signaled), ours returns
0 (correct STATUS_SUCCESS but wrong semantic). This is the `KeSetEvent`
return-value bug class, deferred — would need investigation of ours's
`nt_set_event`/`ke_set_event` body return semantics (likely a sister
to the `KeSetEvent_entry` body returning `prev_signaled` instead of
`STATUS_SUCCESS`).
## Gate 4 — Build
```
$ cargo build --release
Compiling xenia-kernel v0.1.0
Compiling xenia-app v0.1.0
Finished `release` profile [optimized] target(s) in 6.73s
```
One pre-existing dead-code warning (`walk_committed_regions`); not
introduced by this fix. Canary untouched.
## Gate 5 — Phase A determinism (emitter)
Two cvar-ON captures of the same engine binary, md5-summing only
deterministic fields (excluding `host_ns` and `guest_cycle`):
```
run1 (det-fields-only md5): 11a07772f600abab9dcfe4af2300b554
run2 (det-fields-only md5): 11a07772f600abab9dcfe4af2300b554
```
Byte-identical ✅.
Note: digest changes from C+6's `7312446e…` because the suppression
flips events on/off in the deterministic stream for 9 more ords AND
the renamed ord 0x82/0x98 produce different name strings. Expected.
## Gate 6 — Kernel unit tests
```
$ cargo test --release -p xenia-kernel --lib
test result: ok. 146 passed; 0 failed; 0 ignored; 0 measured;
0 filtered out
```
Test count: 145 (C+6) → 146 (C+6½) = +1 net.
* Renamed: `ke_set_ideal_processor_round_trips`
`scheduler_ideal_processor_round_trips` (still exercises round-trip,
but via scheduler API directly since the hallucinated kernel-export
funcs were deleted).
* Added: `ke_query_interrupt_time_returns_synthetic_u64` — asserts
the new ord 0x82 body returns a non-zero u64 (> u32::MAX) in gpr[3],
guarding against regression to byte-sized return.
No regressions in any other test.
## Summary
All 6 gates pass.
* **Phase 1**: 9 class-E sister bugs fixed (DbgPrint, RtlCaptureContext,
sprintf, RtlUnwind, _vsnprintf, __C_specific_handler,
XeKeysConsoleSignatureVerification, StfsCreateDevice, StfsControlDevice).
All registered via `register_unimplemented_export` to mirror canary's
syscall-thunk silence. Bodies retained (harmless side effects).
* **Phase 2**: 2 hallucinated imports fixed (ord 0x82
`KeQueryInterruptTime`, ord 0x98 `KeSetBackgroundProcessors`).
Both rename + body fix; old hallucinated bodies deleted.
* **Phase 3**: no additional findings beyond the explicit list.
**Headline metric**: tid=7→tid=2 chain matched-prefix grew +11 (15→26),
exactly as the C+6 sister-bugs note predicted for `StfsCreateDevice`.
Main chain preserved at 102158. No regressions.
Diff-tool unchanged. Canary unchanged. Heap memory model (C+2) and
clock (Stage 2) deferred items untouched.
Next divergence (per metric):
* Main chain still blocked at 102158 (`XamTaskCloseHandle return=1
vs 0`) — Phase C+7 target as noted in C+6 memory.
* tid=7→tid=2 now blocked at 26 (`KeSetEvent return=1 vs 0`) — same
bug class as tid=4→tid=11 at idx=5 (KeSetEvent prev-signaled
return-value semantics).

View File

@@ -0,0 +1,90 @@
# Phase 1+2+3 — sister-bug + hallucination fixes (Phase C+6½)
All fixes land in `crates/xenia-kernel/src/exports.rs`. No
`state.rs` changes (re-uses the `register_unimplemented_export` API
introduced in C+6).
## Phase 1 — Class-E sister sweep (8 ords)
For each, behavior verification per CLI directive:
| ord | name | reg-kind change | body verified | severity | notes |
|---|---|---|---|---|---|
| 0x003 | DbgPrint | `register_export``register_unimplemented_export` | reads cstring at gpr[3], logs, sets gpr[3]=0 — harmless side effect | LOW (rename only, body kept) | Body retains `tracing::info!` for diagnostics; only Phase A emitter goes silent. |
| 0x119 | RtlCaptureContext | `register_export``register_unimplemented_export` | writes ctx.gpr[0..32] to guest mem at *gpr[3] — guest-visible side effect retained | LOW | Body kept; Phase A emitter silent. |
| 0x13B | sprintf | `register_export``register_unimplemented_export` | naïve fmt→dest copy (no varargs interpretation); harmless | LOW | Body kept; Phase A emitter silent. |
| 0x147 | RtlUnwind | `register_export``register_unimplemented_export` | `tracing::warn!` only — no-op | LOW | Body kept; Phase A emitter silent. |
| 0x14D | _vsnprintf | `register_export``register_unimplemented_export` | naïve fmt→dest copy; harmless | LOW | Body kept; Phase A emitter silent. |
| 0x1A5 | __C_specific_handler | `register_export``register_unimplemented_export` | logs + returns ExceptionContinueSearch (1) | LOW | Body kept; Phase A emitter silent. |
| 0x257 | XeKeysConsoleSignatureVerification | `register_export``register_unimplemented_export` | `stub_success` (gpr[3]=0) — no-op | LOW | Body kept; Phase A emitter silent. |
| 0x259 | StfsCreateDevice | `register_export``register_unimplemented_export` | `stub_success` | LOW — **drives tid=7→tid=2 advance** | C+6 noted this specifically. Verified +11 advance in Phase 4 diff. |
| 0x25A | StfsControlDevice | `register_export``register_unimplemented_export` | `stub_success` | LOW | Body kept; Phase A emitter silent. |
**Total Phase 1: 9 LOW-severity rename-only fixes** (all bodies kept
intact; only emitter coverage suppressed to match canary's
syscall-thunk silence).
## Phase 2 — Hallucinated import behavior fixes (2 ords)
### ord 0x82 — `KeQueryInterruptTime` (CRITICAL)
| step | action |
|---|---|
| 1. Verified ours's pre-fix body | `ke_query_ideal_processor`: returned `thread.ideal_processor` u8 via `gpr[3]`. |
| 2. Verified canary's body | `KeQueryInterruptTime_entry` (xboxkrnl_misc.cc:119-127): returns `bundle->interrupt_time` u64 via gpr[3]. |
| 3. Verified canary's shim status | DECLARED (`xboxkrnl_misc.cc:127`) — both engines emit Phase A events. NOT class E. |
| 4. Body fix | New `fn ke_query_interrupt_time` returns synthetic `0x0000_0001_0000_0000` (monotonic u64, matches `ke_query_system_time` static-fake pattern). |
| 5. Registration fix | `register_export(0x82, "KeQueryInterruptTime", ke_query_interrupt_time)`. |
| 6. Old body | `fn ke_query_ideal_processor` DELETED. Scheduler's `ideal_ref` method retained (used by `NtSetInformationThread::ThreadIdealProcessor`). |
| 7. Test | New unit test `ke_query_interrupt_time_returns_synthetic_u64` asserts non-zero u64 (> u32::MAX), guarding against regression to a byte-sized return. |
### ord 0x98 — `KeSetBackgroundProcessors` (CRITICAL)
| step | action |
|---|---|
| 1. Verified ours's pre-fix body | `ke_set_ideal_processor`: set `thread.ideal_processor = ctx.gpr[4] as u8`, returned prev. **Active wrong state mutation.** |
| 2. Verified canary's body | NOT DECLARED. Canary routes through syscall thunk → no state mutation. Effective semantics: no-op. |
| 3. Verified canary's shim status | Class E (table-entry-only). Phase A emits NOTHING for this ord. |
| 4. Body fix | Body replaced with `stub_success` (no state mutation, matches canary's no-op semantics). |
| 5. Registration fix | `register_unimplemented_export(0x98, "KeSetBackgroundProcessors", stub_success)`. Suppresses Phase A emitter (matches canary). |
| 6. Old body | `fn ke_set_ideal_processor` DELETED. Scheduler's `set_ideal_ref` method retained (used by `NtSetInformationThread::ThreadIdealProcessor`). |
| 7. Test | Renamed `ke_set_ideal_processor_round_trips``scheduler_ideal_processor_round_trips`, exercises the scheduler methods directly (still validating the round-trip relied on by `nt_set_information_thread`). |
## Phase 3 — Additional findings
**None.** Phase 0's full ord scan surfaced exactly 2 hallucinations
(both already flagged by C+6) and 11 class-E candidates (10 new + 1
C+6 already fixed). No ghost ords. No other name mismatches.
## LOC footprint
| section | LOC delta |
|---|---|
| Registration calls (10 lines changed + 30 lines of comment context) | +35 net |
| `fn ke_query_interrupt_time` (12 lines including doc) | +12 |
| Removed `fn ke_set_ideal_processor` body | -10 |
| Removed `fn ke_query_ideal_processor` body | -12 |
| Doc-block replacing both above | +35 |
| Unit test rename + new `ke_query_interrupt_time` test | +20 |
| **Total** | **~80 net additive** |
All changes in `crates/xenia-kernel/src/exports.rs`. State.rs untouched
(reused C+6's `register_unimplemented_export` API). Diff tool untouched.
Canary untouched.
## Behavior verification summary
Per the CLI directive "DO NOT JUST RENAME": every fix in this session
had a body-level verification step. Specifically:
* **Phase 1 (9 ords)**: bodies retained as-is. Verified each body is
harmless side-effect-only OR `stub_success`. No behavior change;
only emitter coverage adjusted.
* **Phase 2 (2 ords)**: bodies replaced. ord 0x82's new body returns
semantically-correct u64 (vs. wrong u8 ideal-processor). ord 0x98's
body removed (no state mutation, matches canary's no-op syscall
thunk).
Both Phase 2 hallucinated ords are LATENT in the current 50M run (0
hits in event log) — fix prevents future divergence when boot
progresses past current matched-prefix horizon.

View File

@@ -0,0 +1,25 @@
{
"build_id": "ours-phaseB",
"cvars": {
"phase_b_dump_section_content": false,
"phase_b_snapshot_and_exit": true,
"phase_b_snapshot_dir": "/tmp/c6half_snap"
},
"deterministic_skip": [
"host_ns_at_snapshot",
"wall_clock_iso8601",
"build_id",
"iso_path",
"cvars.phase_b_snapshot_dir"
],
"engine": "ours",
"host_ns_at_snapshot": 0,
"image_loaded_sha256": "ea8d160e9369328a5b922258a92113efb8d7ce3e1a5c12cc521e375985c91c18",
"iso_path": "",
"schema_version": 1,
"wall_clock_iso8601": "epoch:0",
"xex_entry_point": "0x824ab748",
"xex_header_sha256": "0000000000000000000000000000000000000000000000000000000000000000",
"xex_image_base": "0x82000000",
"xex_image_size": 9568256
}

View File

@@ -0,0 +1,234 @@
{
"cr": [
"0x0",
"0x0",
"0x0",
"0x0",
"0x0",
"0x0",
"0x0",
"0x0"
],
"ctr": "0x0000000000000000",
"deterministic_skip": [
"hw_id"
],
"engine": "ours",
"fpr": [
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000"
],
"fpscr": "0x00000000",
"gpr": [
"0x0000000000000000",
"0x00000000700fff00",
"0x0000000020000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x000000007fff0000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000",
"0x0000000000000000"
],
"hw_id": 0,
"lr": "0x00000000bcbcbcbc",
"msr": "0x0000000000009030",
"pc": "0x824ab748",
"pcr_base": "0x7fff0000",
"schema_version": 1,
"stack_base": "0x00000000",
"stack_limit": "0x00000000",
"thread_id": 1,
"tls_base": "0x00000000",
"vr": [
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000",
"00000000000000000000000000000000"
],
"vrsave": "0xffffffff",
"vscr": "00000000000000000000000000010000",
"xer": {
"ca": 0,
"ov": 0,
"so": 0,
"tbc": 0
}
}

View File

@@ -0,0 +1,62 @@
{
"deterministic_skip": [
"raw_handle_id",
"exports_registered_count"
],
"engine": "ours",
"exports_registered_count": 199,
"exports_registered_sample": [
"xam.xex!NetDll_WSACleanup",
"xam.xex!NetDll_WSAStartup",
"xam.xex!XGetAVPack",
"xam.xex!XGetGameRegion",
"xam.xex!XGetLanguage",
"xam.xex!XGetVideoMode",
"xam.xex!XMsgInProcessCall",
"xam.xex!XMsgStartIORequest",
"xam.xex!XMsgStartIORequestEx",
"xam.xex!XNotifyGetNext",
"xam.xex!XNotifyPositionUI",
"xam.xex!XamAlloc",
"xam.xex!XamContentClose",
"xam.xex!XamContentCreate",
"xam.xex!XamContentCreateEnumerator",
"xam.xex!XamContentDelete",
"xam.xex!XamContentGetCreator",
"xam.xex!XamContentGetDeviceData",
"xam.xex!XamContentGetDeviceName",
"xam.xex!XamContentGetDeviceState",
"xam.xex!XamContentSetThumbnail",
"xam.xex!XamEnableInactivityProcessing",
"xam.xex!XamEnumerate",
"xam.xex!XamFree",
"xam.xex!XamGetExecutionId",
"xam.xex!XamGetSystemVersion",
"xam.xex!XamInputGetCapabilities",
"xam.xex!XamInputGetKeystrokeEx",
"xam.xex!XamInputGetState",
"xam.xex!XamInputSetState",
"xam.xex!XamLoaderLaunchTitle",
"xam.xex!XamLoaderTerminateTitle"
],
"exports_registered_sha256": "bca7668a2a76ce1d1cc4dba8a862a2f16ec6ee3b2aab8a71d8d8bc0ccc89a097",
"handle_name_table": [],
"notification_listeners": [],
"objects": [
{
"details": {
"entry_pc": "0x824ab748",
"exit_code": null,
"hw_id": 0,
"is_entry_thread": true,
"thread_id": 1
},
"handle_semantic_id": "9879c5053fedb1d0",
"name": null,
"raw_handle_id": "0x00001000",
"type": "Thread",
"type_code": 5
}
],
"schema_version": 1
}

View File

@@ -0,0 +1,11 @@
{
"engine": "ours",
"files": {
"config.json": "11f2d8e5d95d066b932de6d80748c1d312ed4ff47498d2b8e07146e41a0e9a1a",
"cpu_state.json": "4e6df54ca1939d08854f3a52b49ed2c5ee0823d63cdecad8a7395203dac5443a",
"kernel.json": "2db219d4ca8b0313e53be379b8fcf90ab13b99116e6fac5601f6bdefd1aa6900",
"memory.json": "b96ae4daebfbdd314e574492c1e162f532fa4f89ff5c0d7c6c29743797089cf1",
"vfs.json": "97bb2bda57266d8e0dd1da13309eab5ece43130ef378a0b682917d299e9dc4e1"
},
"schema_version": 1
}

View File

@@ -0,0 +1,84 @@
{
"committed_pages_total": 2594,
"deterministic_skip": [
"host_base_pointer"
],
"engine": "ours",
"guest_address_space_bytes": 4294967296,
"heaps": [
{
"base": "0x00000000",
"name": "v00000000",
"page_size": 4096,
"page_state_histogram": {
"committed": 0
},
"size": "0x40000000"
},
{
"base": "0x40000000",
"name": "v40000000",
"page_size": 4096,
"page_state_histogram": {
"committed": 266
},
"size": "0x40000000"
},
{
"base": "0x80000000",
"name": "v80000000",
"page_size": 4096,
"page_state_histogram": {
"committed": 2336
},
"size": "0x40000000"
},
{
"base": "0x90000000",
"name": "v90000000",
"page_size": 4096,
"page_state_histogram": {
"committed": 0
},
"size": "0x40000000"
}
],
"page_size": 4096,
"regions": [
{
"byte_count": 1048576,
"end": "0x70100000",
"protect": 0,
"section_kind": null,
"sha256": "30e14955ebf1352266dc2ff8067e68104607e750abb9d3b36582b8af909fcb58",
"start": "0x70000000"
},
{
"byte_count": 4096,
"end": "0x7ffe1000",
"protect": 0,
"section_kind": null,
"sha256": "ad7facb2586fc6e966c004d7d1d16b024f5805ff7cb47c7a85dabd8b48892ca7",
"start": "0x7ffe0000"
},
{
"byte_count": 4096,
"end": "0x7fff1000",
"protect": 0,
"section_kind": null,
"sha256": "e35cddaf9c210aed7505ec4cf1c599f58ac2b7ec25b0885db1ee49aba2db519a",
"start": "0x7fff0000"
},
{
"byte_count": 9568256,
"end": "0x82920000",
"protect": 0,
"section_kind": null,
"sha256": "ea8d160e9369328a5b922258a92113efb8d7ce3e1a5c12cc521e375985c91c18",
"start": "0x82000000"
}
],
"regions_walked": [],
"schema_version": 1,
"section_contents": null
}

View File

@@ -0,0 +1,71 @@
{
"cache_root_listing": [],
"deterministic_skip": [
"host_path_realpath"
],
"engine": "ours",
"mounted_devices_observed_count": 1,
"resolve_path_probes": [
{
"is_directory": true,
"path": "\\Device\\Cdrom0",
"resolved": true,
"size": null
},
{
"is_directory": true,
"path": "\\Device\\Cdrom0\\dat",
"resolved": true,
"size": 4096
},
{
"is_directory": null,
"path": "\\Device\\Cdrom0\\dat\\movie",
"resolved": false,
"size": null
},
{
"is_directory": null,
"path": "\\Device\\Cdrom0\\dat\\movie\\opening.bik",
"resolved": false,
"size": null
},
{
"is_directory": false,
"path": "\\Device\\Cdrom0\\default.xex",
"resolved": true,
"size": 3497984
},
{
"is_directory": null,
"path": "\\Device\\HardDisk0\\Partition1",
"resolved": false,
"size": null
},
{
"is_directory": true,
"path": "cache:\\",
"resolved": true,
"size": null
},
{
"is_directory": null,
"path": "cache:\\nonexistent_probe",
"resolved": false,
"size": null
},
{
"is_directory": true,
"path": "game:\\dat",
"resolved": true,
"size": 4096
},
{
"is_directory": false,
"path": "game:\\default.xex",
"resolved": true,
"size": 3497984
}
],
"schema_version": 1
}