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:
110
audit-runs/phase-c13-game-dat-files-tbl/investigation.md
Normal file
110
audit-runs/phase-c13-game-dat-files-tbl/investigation.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# 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.jsonl`
|
||||
events 103860-103862: import.call → kernel.call → kernel.return = 0.
|
||||
- `xenia-rs/audit-runs/phase-c12-NtQueryFullAttributesFile/canary-cold-tid6-250k.jsonl`
|
||||
events 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:
|
||||
|
||||
1. Translate the `OBJECT_ATTRIBUTES.name` ANSI_STRING to a path.
|
||||
2. Path validation: `IsValidPath` → `STATUS_OBJECT_NAME_INVALID` on
|
||||
reject.
|
||||
3. Resolve the `root_directory` handle if non-zero.
|
||||
4. Call `kernel_state()->file_system()->OpenFile(...)`.
|
||||
5. **Return the `OpenFile` status verbatim.** If the file isn't
|
||||
present in any mounted device, that's `STATUS_OBJECT_NAME_NOT_FOUND`
|
||||
(`VirtualFileSystem::ResolvePath` returns nullptr for unresolved
|
||||
paths, which `OpenFile` translates to NOT_FOUND).
|
||||
6. 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:
|
||||
|
||||
1. **Empty path** (root-of-device) → synth-empty handle (special-cased
|
||||
for `NtCreateFile("game:\")` disc-validation probe).
|
||||
2. **`cache:` prefix** → host-FS-backed via `open_cache_file` (AUDIT-038).
|
||||
3. **Otherwise**: call `vfs.read_file(&path)`. On `Ok(bytes)` return
|
||||
handle. **On `Err(e)`: synth a zero-byte file and return SUCCESS.**
|
||||
|
||||
The (3) `Err` branch was the synth-empty fallback. Its docstring
|
||||
acknowledged two rationales:
|
||||
|
||||
> 1. Writable system partitions (`cache:`, `partition0:`, `partition1:`)
|
||||
> aren't backed by the disc — we synth so opens succeed.
|
||||
> 2. 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:
|
||||
|
||||
1. New helper `is_disc_prefix(raw_path: &str) -> bool` that
|
||||
recognises `game:\`, `d:\`/`D:\`, and `\Device\Cdrom0\` (the
|
||||
subset of `path::DEVICE_PREFIXES` that resolves to the read-only
|
||||
disc).
|
||||
|
||||
2. `open_vfs_file` captures the raw path via
|
||||
`crate::path::object_attributes_raw_name` (alongside the existing
|
||||
normalised `path`). In the `Err(e)` arm, if `is_disc_prefix(&raw_path)`,
|
||||
return `STATUS_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 `RtlNtStatusToDosError` and
|
||||
continues; +712 events of further progress observed in cold-vs-cold
|
||||
(no fault, no `XamShowDirtyDiscErrorUI`).
|
||||
- **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 `imports` 40447 → 40390 (-57). Phase B
|
||||
`image_loaded_sha256` is unchanged.
|
||||
Reference in New Issue
Block a user