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>
104 lines
4.3 KiB
Diff
104 lines
4.3 KiB
Diff
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).
|