--- a/xenia-rs/tools/diff-events/diff_events.py +++ b/xenia-rs/tools/diff-events/diff_events.py @@ -287,6 +287,25 @@ ALLOCATOR_RETURN_FNS = frozenset( # creation call. "XamNotifyCreateListener", + # Phase C+25: `MmGetPhysicalAddress` is a VA→PA translator whose + # return depends on which heap region the input VA lives in. This + # is the downstream consequence of C+2's deferred Path β (canary + # has three physical heaps at vA0/vC0/vE0 routed by page size, + # ours has a single unified heap_cursor starting at 0x40000000). + # Concretely: at C+25 idx 105,112 canary returned 0x150B0000 + # (input 0xF50AF000 in `vE0000000` heap: addr - 0xE0000000 + 0x1000 + # per `PhysicalHeap::GetPhysicalAddress`, see `memory.cc:2317`), + # while ours returned 0x0ADCF000 (input ~0x4ADCF000 in unified heap, + # masked via `& 0x1FFF_FFFF` per `exports.rs:985`). Both engines' + # translations are SELF-CONSISTENT — game code passes the PA + # opaquely to GPU (`VdInitializeRingBuffer` is the very next call) + # and the GPU translates it back to a host pointer using the same + # engine's heap map. Per-(tid,name) ordinal sentinel preserves the + # opaque-pass-through semantics while exposing actual divergences + # (e.g. game-side arithmetic on the PA, or a translation-count + # mismatch). Lifting the engine-side three-physical-heaps memory + # model is the C+2 Path β deferral, out of scope for C+25 (see + # `project_phase_c2_MmAllocatePhysicalMemoryEx_2026_05_13.md`). + "MmGetPhysicalAddress", ] ) --- a/xenia-rs/tools/diff-events/test_diff_events.py +++ b/xenia-rs/tools/diff-events/test_diff_events.py @@ -686,6 +686,150 @@ def test_collect_shared_global_sids_single_tid_excluded() -> None: +# === Phase C+25 — MmGetPhysicalAddress canonicalization === +# (4 new tests; see investigation.md for details) +def test_mm_get_physical_address_in_allocator_set() -> None: ... +def test_mm_get_physical_address_canonicalization() -> None: ... +def test_mm_get_physical_address_cross_engine_alignment() -> None: ... +def test_mm_get_physical_address_count_mismatch_still_diverges() -> None: ... + def main() -> int: ... # Phase C+25 test_mm_get_physical_address_in_allocator_set() test_mm_get_physical_address_canonicalization() test_mm_get_physical_address_cross_engine_alignment() test_mm_get_physical_address_count_mismatch_still_diverges() Engine: UNTOUCHED. Python-only fix. Phase B image_canonical_sha256 ea8d160e… UNCHANGED by definition (no Rust source modified). Build clean. Kernel tests 217 pass unchanged.