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>
4.8 KiB
Phase C+13 investigation — NtCreateFile game:\dat\files.tbl
Predecessor state (from Phase C+12)
Cold-vs-cold first divergence at tid_event_idx=103862 on the
canary tid=6 → ours tid=1 main chain:
canary: NtCreateFile(path="game:\\dat\\files.tbl") -> 0xc0000034 (STATUS_OBJECT_NAME_NOT_FOUND)
ours: NtCreateFile(path="game:\\dat\\files.tbl") -> 0x00000000 (STATUS_SUCCESS, synth-empty stub)
Verified from the C+12 archived JSONLs:
xenia-rs/audit-runs/phase-c12-NtQueryFullAttributesFile/ours-cold.jsonlevents 103860-103862: import.call → kernel.call → kernel.return = 0.xenia-rs/audit-runs/phase-c12-NtQueryFullAttributesFile/canary-cold-tid6-250k.jsonlevents 103860-103862: same import.call → kernel.call → kernel.return =0xc0000034.
Canary's next event (idx 103863): RtlNtStatusToDosError(0xc0000034) -> 2
(ERROR_FILE_NOT_FOUND). Sylpheed has proper NOT_FOUND handling and
continues; the synth-empty was masking the correct branch.
Canary semantics (xboxkrnl_io.cc:39-111)
NtCreateFile_entry is a thin wrapper:
- Translate the
OBJECT_ATTRIBUTES.nameANSI_STRING to a path. - Path validation:
IsValidPath→STATUS_OBJECT_NAME_INVALIDon reject. - Resolve the
root_directoryhandle if non-zero. - Call
kernel_state()->file_system()->OpenFile(...). - Return the
OpenFilestatus verbatim. If the file isn't present in any mounted device, that'sSTATUS_OBJECT_NAME_NOT_FOUND(VirtualFileSystem::ResolvePathreturns nullptr for unresolved paths, whichOpenFiletranslates to NOT_FOUND). - On success: alloc handle and write to
handle_out.
For game:\dat\files.tbl specifically: the game: symlink points
to the disc device. The disc image ISO doesn't contain dat/files.tbl
(disc dump omission — likely a region-specific or build-time-only
file). OpenFile returns NOT_FOUND, the function returns NOT_FOUND.
Ours pre-fix (exports.rs open_vfs_file)
open_vfs_file had three paths:
- Empty path (root-of-device) → synth-empty handle (special-cased
for
NtCreateFile("game:\")disc-validation probe). cache:prefix → host-FS-backed viaopen_cache_file(AUDIT-038).- Otherwise: call
vfs.read_file(&path). OnOk(bytes)return handle. OnErr(e): synth a zero-byte file and return SUCCESS.
The (3) Err branch was the synth-empty fallback. Its docstring
acknowledged two rationales:
- Writable system partitions (
cache:,partition0:,partition1:) aren't backed by the disc — we synth so opens succeed.- Disc files that didn't make it into the ISO rip — returning NOT_FOUND would make Sylpheed's boot validator call
XamShowDirtyDiscErrorUI→ dashboard exit.
But rationale (1) was already covered by the AUDIT-038 cache:
short-circuit, and rationale (2) is empirically WRONG: canary returns
NOT_FOUND for game:\dat\files.tbl and Sylpheed continues — there
is no XamShowDirtyDiscErrorUI event in canary's trace anywhere
near idx 103862. The synth-empty was misleading the boot trajectory
in a non-canary direction.
Fix shape
Two surgical changes:
-
New helper
is_disc_prefix(raw_path: &str) -> boolthat recognisesgame:\,d:\/D:\, and\Device\Cdrom0\(the subset ofpath::DEVICE_PREFIXESthat resolves to the read-only disc). -
open_vfs_filecaptures the raw path viacrate::path::object_attributes_raw_name(alongside the existing normalisedpath). In theErr(e)arm, ifis_disc_prefix(&raw_path), returnSTATUS_OBJECT_NAME_NOT_FOUND(mirroring canary). Otherwise keep the legacy synth-empty.
The cache: short-circuit is upstream of this branch and unchanged;
its existing NOT_FOUND-on-FILE_OPEN-miss behaviour is preserved.
Tests
is_disc_prefix_recognises_disc_aliases(12 assertions, mix of disc and non-disc prefixes including case variants).nt_create_file_game_prefix_missing_returns_not_found— primary.nt_create_file_cdrom_prefix_missing_returns_not_found—\Device\Cdrom0\alias.nt_create_file_non_disc_prefix_missing_still_synthesizes— regression guard for\Device\Harddisk0\Partition1\sys.bin.
Risk analysis (pre-fix)
- Sylpheed might crash on NOT_FOUND: REFUTED. Canary returns
NOT_FOUND, then Sylpheed calls
RtlNtStatusToDosErrorand continues; +712 events of further progress observed in cold-vs-cold (no fault, noXamShowDirtyDiscErrorUI). - Reading-error #23 regression: matched-prefix MUST grow, not shrink. Cold-vs-cold confirms 103862 → 104574 (+712); no regression.
- Sister-chain regression: confirmed unchanged for all 5 sister
chains (see
cold-vs-cold-result.md). - Stable digest shift: expected; the synth-empty was emitting
one extra kernel.call/return pair (synth-success path); removing
it for disc paths drops
imports40447 → 40390 (-57). Phase Bimage_loaded_sha256is unchanged.