From bb9df22fc9681674676ccfc1f890205c7cc696e5 Mon Sep 17 00:00:00 2001 From: Brian Degenhardt Date: Sun, 9 Aug 2026 21:38:12 -0700 Subject: [PATCH] Tests: stop the DXSTG write-back check skipping on 16K-page hosts MapAt's candidate addresses are 4K-aligned and none is 16K-aligned, so on a 16K-page kernel -- Asahi, Apple Silicon, some Android, and one of our own CI jobs -- the kernel rejects every one of them and the mapping fails. The write-back check treated that as a precondition and skipped outright, which took its guest-side assertions with it: the ones that actually pin where a DXSTG-steered eviction lands, none of which need anything from the host. The mapping is only the negative control, there to show the write-back did not ALSO reach the host page carrying the same number. Make it optional. The guest-side half now runs everywhere and only the control drops out. DxstgDirtyStaysInsideGuestMemory still skips, and should: it is entirely about the host page. That leaves one skip here on a 16K-page host instead of two, and none at all on a 4K one. --- .../ee_cache2_console_conformance_tests.cpp | 44 +++++++++++++------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/tests/ctest/core/recompilers/ee_cache2_console_conformance_tests.cpp b/tests/ctest/core/recompilers/ee_cache2_console_conformance_tests.cpp index 1006fa5f60..46ea4f5048 100644 --- a/tests/ctest/core/recompilers/ee_cache2_console_conformance_tests.cpp +++ b/tests/ctest/core/recompilers/ee_cache2_console_conformance_tests.cpp @@ -145,10 +145,14 @@ u32 Obs(int id, const char* name) // process. Elsewhere the two callers skip. // // The candidates are 4K-aligned but none is 16K-aligned, so on a 16K-page -// kernel — Asahi, Apple Silicon, some Android — every one is rejected outright -// and the two callers always skip. Re-picking them 16K-aligned would need the -// tag/index constraints re-derived against the console capture, so it is left -// to whoever holds that data. +// kernel — Asahi, Apple Silicon, some Android — every one is rejected outright. +// Re-picking them 16K-aligned would need the tag/index constraints re-derived +// against the console capture, so it is left to whoever holds that data. +// +// A null return is therefore routine, not exceptional, and callers should treat +// the mapping as an optional negative control rather than a precondition. Only +// DxstgDirtyStaysInsideGuestMemory is wholly about the host page and has to +// skip; the write-back check keeps its guest-side half running everywhere. void* MapAt(u32* chosen) { #if defined(MAP_FIXED_NOREPLACE) @@ -439,14 +443,22 @@ TEST(EeCache2Console, DxstgWriteBackTargetsTheTaggedGuestPage) constexpr u32 kTargetPage = 0x00129000; constexpr u32 kTarget = kTargetPage + kSetIndex * 64; + // The host mapping is only the negative control: proof that the write-back + // did not ALSO reach the host page that happens to carry the same number. + // Everything else here is guest-side and needs nothing from the host, so it + // runs unconditionally. On a 16K-page kernel -- Asahi, Apple Silicon, some + // Android -- MapAt cannot honour a 4K-aligned request and returns null; + // only the control is skipped then, not the whole test. u32 page = 0; void* p = MapAt(&page); - if (!p) - GTEST_SKIP() << "could not map a page at any candidate host address"; - ASSERT_EQ(page, kTargetPage) << "the control page moved; re-point kTargetPage"; - std::memset(p, 0xEE, 0x1000); - const u32* host = reinterpret_cast( - static_cast(page) + kSetIndex * 64); + const u32* host = nullptr; + if (p) + { + ASSERT_EQ(page, kTargetPage) << "the control page moved; re-point kTargetPage"; + std::memset(p, 0xEE, 0x1000); + host = reinterpret_cast( + static_cast(page) + kSetIndex * 64); + } { EeRecTestHarness h; @@ -463,7 +475,8 @@ TEST(EeCache2Console, DxstgWriteBackTargetsTheTaggedGuestPage) // 64 bytes of guest cache line, at the guest physical page the tag names. EXPECT_EQ(memRead32(kTarget), 0x5A5A0009u); EXPECT_EQ(memRead32(kTarget + 4), 0xDEADBEEFu); - EXPECT_EQ(host[0], 0xEEEEEEEEu) << "the write-back still reaches a host address"; + if (host) + EXPECT_EQ(host[0], 0xEEEEEEEEu) << "the write-back still reaches a host address"; } // Never filled, and filled-then-invalidated. Both used to be declined @@ -471,7 +484,8 @@ TEST(EeCache2Console, DxstgWriteBackTargetsTheTaggedGuestPage) for (const bool invalidate_first : {false, true}) { SCOPED_TRACE(invalidate_first ? "filled then invalidated" : "never filled"); - std::memset(p, 0xEE, 0x1000); + if (p) + std::memset(p, 0xEE, 0x1000); EeRecTestHarness h; resetCache(); memWrite32(kTarget, 0xA5A5A5A5u); @@ -489,10 +503,12 @@ TEST(EeCache2Console, DxstgWriteBackTargetsTheTaggedGuestPage) RunCacheOp(0x12, kProbeLine); RunCacheOp(0x14, kProbeLine); EXPECT_EQ(memRead32(kTarget), 0u) << "the cleared line did not write back"; - EXPECT_EQ(host[0], 0xEEEEEEEEu) << "the write-back still reaches a host address"; + if (host) + EXPECT_EQ(host[0], 0xEEEEEEEEu) << "the write-back still reaches a host address"; } - munmap(p, 0x1000); + if (p) + munmap(p, 0x1000); } // A DXSTG naming a page that does not resolve to plain guest memory leaves the