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.
This commit is contained in:
Brian Degenhardt
2026-08-10 15:58:14 -07:00
parent 85a88d75da
commit bb9df22fc9
@@ -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";
const u32* host = nullptr;
if (p)
{
ASSERT_EQ(page, kTargetPage) << "the control page moved; re-point kTargetPage";
std::memset(p, 0xEE, 0x1000);
const u32* host = reinterpret_cast<const u32*>(
host = reinterpret_cast<const u32*>(
static_cast<uptr>(page) + kSetIndex * 64);
}
{
EeRecTestHarness h;
@@ -463,6 +475,7 @@ 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);
if (host)
EXPECT_EQ(host[0], 0xEEEEEEEEu) << "the write-back still reaches a host address";
}
@@ -471,6 +484,7 @@ TEST(EeCache2Console, DxstgWriteBackTargetsTheTaggedGuestPage)
for (const bool invalidate_first : {false, true})
{
SCOPED_TRACE(invalidate_first ? "filled then invalidated" : "never filled");
if (p)
std::memset(p, 0xEE, 0x1000);
EeRecTestHarness h;
resetCache();
@@ -489,9 +503,11 @@ TEST(EeCache2Console, DxstgWriteBackTargetsTheTaggedGuestPage)
RunCacheOp(0x12, kProbeLine);
RunCacheOp(0x14, kProbeLine);
EXPECT_EQ(memRead32(kTarget), 0u) << "the cleared line did not write back";
if (host)
EXPECT_EQ(host[0], 0xEEEEEEEEu) << "the write-back still reaches a host address";
}
if (p)
munmap(p, 0x1000);
}