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,103 @@
Phase C+9 — XamContentCreateEnumerator canary-parity X_ERROR_NO_SUCH_USER
=========================================================================
Scope: `crates/xenia-kernel/src/xam.rs`
LOC: ~85 body + ~125 test = ~210 net additive
Registration change — xam.rs:59:
--------------------------------
- state.register_export(Xam, 0x025C, "XamContentCreateEnumerator", stub_success);
+ state.register_export(Xam, 0x025C, "XamContentCreateEnumerator", xam_content_create_enumerator);
Body — new function after `xam_user_get_signin_state` (xam.rs:384):
-------------------------------------------------------------------
+ // ===== Content =====
+
+ /// `XamContentCreateEnumerator(user_index, device_id, content_type,
+ /// content_flags, items_per_enumerate, buffer_size_ptr, handle_out)`.
+ /// Mirrors xenia-canary `XamContentCreateEnumerator_entry`
+ /// (xam_content.cc:129-220). Reading-error #28 discipline applied: body
+ /// shape verified against canary source.
+ ///
+ /// Canary's normal-path success returns `X_ERROR_SUCCESS` (0) with a
+ /// fresh enumerator handle in `*handle_out`. The Phase A oracle at
+ /// `tid_event_idx=102197` shows canary returning `X_ERROR_NO_SUCH_USER`
+ /// (`0x525`, 1317) with empty side_effects — the call hit the
+ /// `if (!user) return X_ERROR_NO_SUCH_USER;` early-return at
+ /// xam_content.cc:153-155 because no profile is installed in canary's
+ /// default `--mute=true` config (no `--profile_slot_*` flags).
+ ///
+ /// Ours has no profile-manager state, so all `user_index != 0xFE`
+ /// queries miss. Mirror the early-return: write `*buffer_size_ptr` per
+ /// canary line 145-147 (which executes *before* the user check) and
+ /// return `X_ERROR_NO_SUCH_USER`. Implementing real content enumeration
+ /// is an XAM-content-subsystem session (escalation-tier scope), not
+ /// this fix.
+ ///
+ /// Side note on internal consistency: ours's `xam_user_get_signin_state`
+ /// returns 1 for `user_index == 0`, conflicting with the "no profile"
+ /// model used here. That divergence surfaces later in the Phase A trace
+ /// (idx 107996+) and is a separate fix — deferred per single-fix
+ /// discipline.
+ fn xam_content_create_enumerator(
+ ctx: &mut PpcContext,
+ mem: &GuestMemory,
+ _state: &mut KernelState,
+ ) {
+ const X_USER_INDEX_NONE: u32 = 0xFE;
+ const X_USER_INDEX_LATEST: u32 = 0xFD;
+ const X_USER_MAX_USER_COUNT: u32 = 4;
+ const X_E_INVALIDARG: u32 = 0x8007_0057;
+ const X_ERROR_NO_SUCH_USER: u32 = 0x0000_0525;
+ const X_ERROR_SUCCESS: u32 = 0;
+ const X_CONTENT_DATA_SIZE: u32 = 0x134;
+
+ let user_index = ctx.gpr[3] as u32;
+ let device_id = ctx.gpr[4] as u32;
+ let _content_type = ctx.gpr[5] as u32;
+ let _content_flags = ctx.gpr[6] as u32;
+ let items_per_enumerate = ctx.gpr[7] as u32;
+ let buffer_size_ptr = ctx.gpr[8] as u32;
+ let handle_out = ctx.gpr[9] as u32;
+
+ let device_unknown = device_id != 0 && device_id > 2;
+ if device_unknown || handle_out == 0 {
+ if buffer_size_ptr != 0 {
+ mem.write_u32(buffer_size_ptr, 0);
+ }
+ ctx.gpr[3] = X_E_INVALIDARG as u64;
+ return;
+ }
+
+ if buffer_size_ptr != 0 {
+ mem.write_u32(buffer_size_ptr, X_CONTENT_DATA_SIZE.wrapping_mul(items_per_enumerate));
+ }
+
+ if user_index != X_USER_INDEX_NONE {
+ let out_of_range = user_index >= X_USER_MAX_USER_COUNT
+ && user_index != X_USER_INDEX_LATEST;
+ let _ = out_of_range; // documentation only — both branches → no user
+ ctx.gpr[3] = X_ERROR_NO_SUCH_USER as u64;
+ return;
+ }
+
+ if handle_out != 0 {
+ mem.write_u32(handle_out, 0);
+ }
+ ctx.gpr[3] = X_ERROR_SUCCESS as u64;
+ }
Tests — 5 new in `mod tests`:
-----------------------------
+ xam_content_create_enumerator_returns_no_such_user_for_user0
+ xam_content_create_enumerator_invalid_handle_out_returns_invalidarg
+ xam_content_create_enumerator_unknown_device_returns_invalidarg
+ xam_content_create_enumerator_user_none_returns_success
+ xam_content_create_enumerator_out_of_range_user_returns_no_such_user
The first test is the THE Phase A oracle case: user_index=0,
device_id=1 (HDD), items_per_enumerate=4 → asserts ret=0x525 and
buffer_size_ptr=0x134*4 (canary writes size BEFORE the user check).