Fix the guest page size at compile time for the Steam Frame build

Every translated guest load and store tests RequiresCheckedAccess(). On
x86-64 and Windows that is a compile-time false, but on AArch64 it read
a global flag, which under PIE is a GOT load plus a byte load and a
branch on every access, re-read after every guest store. The flag is
only true on kernels with pages larger than 4 KiB.

The Steam Frame's SteamOS kernel uses 4 KiB pages (getconf PAGESIZE on
the headset reports 4096), so the native --headset steam_frame build now
folds the test away like x86-64 does. GuestFlat::Initialize checks the
page size at startup in that build and refuses to start with a clear
message if it is ever anything else. Android and other AArch64 builds
still probe at runtime.

Checked with clang --target=aarch64-linux-gnu -O2 -fPIE: a FlatRead32
loop loses both flag loads and the checked Memory::Read32 fallback.

Follows the finding from nx-mod/wiicompiled-nx 18ef16f6, which made the
flag hidden instead; a compile-time constant removes it entirely.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018NKSMYZU43wUfYtm1sxGsC
This commit is contained in:
Claude committed 2026-10-05 15:22:17 +00:00
1 parent 8f725001de
commit 93df677470
2 files changed
+18 -4

No files matched your search

+7 -2
View File
@@ -71,10 +71,15 @@ bool IsActive();
// accesses must use the checked Memory::* path.
// Windows user mode and x86-64 always use a 4 KiB base page, so those builds
// fold this to a compile-time false: it appears in every flat access and must
// not become a hot-path load. Only AArch64, where the page size is a kernel
// configuration (4/16/64 KiB), has to probe it at runtime.
// not become a hot-path load. On AArch64 the page size is a kernel
// configuration (4/16/64 KiB), so it is probed at runtime, except in the native
// Steam Frame build: that build only runs on the headset, whose SteamOS kernel
// uses 4 KiB pages, and Initialize() refuses to start on any other page size.
#if defined(_WIN32) || defined(__x86_64__)
#define MKW_GUEST_FLAT_FIXED_PAGE_SIZE 1
#elif defined(MKW_HEADSET_STEAM_FRAME) && !defined(__ANDROID__)
#define MKW_GUEST_FLAT_FIXED_PAGE_SIZE 1
#define MKW_GUEST_FLAT_VERIFY_PAGE_SIZE 1
#endif
#if defined(MKW_GUEST_FLAT_FIXED_PAGE_SIZE)
+11 -2
View File
@@ -11,6 +11,7 @@
#include <mutex>
#include <sstream>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include "memory.h"
@@ -61,8 +62,9 @@ constexpr size_t kAllocationGranularity = 0x10000; // 64 KiB
constexpr size_t kHostPageSize = 0x1000;
// Only hosts that can expose a page larger than 4 KiB need to discover their
// size at runtime; see RequiresCheckedAccess() in guest_flat_memory.h.
#if !defined(MKW_GUEST_FLAT_FIXED_PAGE_SIZE)
// size at runtime, or check it when the build assumes 4 KiB; see
// RequiresCheckedAccess() in guest_flat_memory.h.
#if !defined(MKW_GUEST_FLAT_FIXED_PAGE_SIZE) || defined(MKW_GUEST_FLAT_VERIFY_PAGE_SIZE)
size_t HostPageSize()
{
const long size = sysconf(_SC_PAGESIZE);
@@ -521,6 +523,13 @@ void Initialize(const std::vector<RegionRequest>& regions) {
#if !defined(MKW_GUEST_FLAT_FIXED_PAGE_SIZE)
g_requiresCheckedAccess = HostPageSize() > kGuestPageSize;
#elif defined(MKW_GUEST_FLAT_VERIFY_PAGE_SIZE)
if (HostPageSize() != kGuestPageSize) {
throw std::runtime_error(
"This is a Steam Frame build, which assumes the headset's 4 KiB memory pages, but this "
"kernel uses " + std::to_string(HostPageSize()) + "-byte pages. Build without "
"--headset steam_frame for this device.");
}
#endif
if (g_initialized) {