mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
Each of the three spin-then-sleep semaphore loops watches a single atomic word, but ShortSpin() has no way to know that: it spends its share of SPIN_TIME_NS in batches of eight isb, and every one of those is a pipeline flush. arm64 can watch the word itself, so ShortSpinOn() does, and the loops that hold their whole predicate in one word take it. Hosts that cannot watch an address keep the old spin. Inspired by Whatcookie's work on arm64 for rpcs3.
229 lines
6.7 KiB
C++
229 lines
6.7 KiB
C++
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
|
|
// SPDX-License-Identifier: GPL-3.0+
|
|
|
|
#pragma once
|
|
|
|
#include "common/Pcsx2Defs.h"
|
|
|
|
#include <atomic>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
class Error;
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// PageProtectionMode
|
|
// --------------------------------------------------------------------------------------
|
|
class PageProtectionMode
|
|
{
|
|
protected:
|
|
bool m_read = false;
|
|
bool m_write = false;
|
|
bool m_exec = false;
|
|
|
|
public:
|
|
__fi constexpr PageProtectionMode() = default;
|
|
|
|
__fi constexpr PageProtectionMode& Read(bool allow = true)
|
|
{
|
|
m_read = allow;
|
|
return *this;
|
|
}
|
|
|
|
__fi constexpr PageProtectionMode& Write(bool allow = true)
|
|
{
|
|
m_write = allow;
|
|
return *this;
|
|
}
|
|
|
|
__fi constexpr PageProtectionMode& Execute(bool allow = true)
|
|
{
|
|
m_exec = allow;
|
|
return *this;
|
|
}
|
|
|
|
__fi constexpr PageProtectionMode& All(bool allow = true)
|
|
{
|
|
m_read = m_write = m_exec = allow;
|
|
return *this;
|
|
}
|
|
|
|
__fi constexpr bool CanRead() const { return m_read; }
|
|
__fi constexpr bool CanWrite() const { return m_write; }
|
|
__fi constexpr bool CanExecute() const { return m_exec && m_read; }
|
|
__fi constexpr bool IsNone() const { return !m_read && !m_write; }
|
|
};
|
|
|
|
static __fi PageProtectionMode PageAccess_None()
|
|
{
|
|
return PageProtectionMode();
|
|
}
|
|
|
|
static __fi PageProtectionMode PageAccess_ReadOnly()
|
|
{
|
|
return PageProtectionMode().Read();
|
|
}
|
|
|
|
static __fi PageProtectionMode PageAccess_WriteOnly()
|
|
{
|
|
return PageProtectionMode().Write();
|
|
}
|
|
|
|
static __fi PageProtectionMode PageAccess_ReadWrite()
|
|
{
|
|
return PageAccess_ReadOnly().Write();
|
|
}
|
|
|
|
static __fi PageProtectionMode PageAccess_ExecOnly()
|
|
{
|
|
return PageAccess_ReadOnly().Execute();
|
|
}
|
|
|
|
static __fi PageProtectionMode PageAccess_Any()
|
|
{
|
|
return PageProtectionMode().All();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// HostSys
|
|
// --------------------------------------------------------------------------------------
|
|
namespace HostSys
|
|
{
|
|
extern void MemProtect(void* baseaddr, size_t size, const PageProtectionMode& mode);
|
|
|
|
extern std::string GetFileMappingName(const char* prefix);
|
|
extern void* CreateSharedMemory(const char* name, size_t size);
|
|
extern void DestroySharedMemory(void* ptr);
|
|
|
|
/// JIT write protect for Apple Silicon. Needs to be called prior to writing to any RWX pages.
|
|
#if !defined(__APPLE__) || !defined(ARCH_ARM64)
|
|
// clang-format -off
|
|
[[maybe_unused]] __fi static void BeginCodeWrite() {}
|
|
[[maybe_unused]] __fi static void EndCodeWrite() {}
|
|
[[maybe_unused]] __fi static void BeginCodeWriteRange(void*, size_t) {}
|
|
[[maybe_unused]] __fi static void EndCodeWriteRange(void*, size_t) {}
|
|
// clang-format on
|
|
#else
|
|
void BeginCodeWrite();
|
|
void EndCodeWrite();
|
|
void BeginCodeWriteRange(void* address, size_t size);
|
|
void EndCodeWriteRange(void* address, size_t size);
|
|
#endif
|
|
|
|
/// Flushes the instruction cache on the host for the specified range.
|
|
/// Only needed on ARM64, X86 has coherent D/I cache.
|
|
#ifdef ARCH_X86
|
|
[[maybe_unused]] __fi static void FlushInstructionCache(void* address, u32 size) {}
|
|
#else
|
|
void FlushInstructionCache(void* address, u32 size);
|
|
#endif
|
|
|
|
/// Returns the size of pages for the current host.
|
|
size_t GetRuntimePageSize();
|
|
|
|
/// Returns the size of a cache line for the current host.
|
|
size_t GetRuntimeCacheLineSize();
|
|
} // namespace HostSys
|
|
|
|
namespace PageFaultHandler
|
|
{
|
|
enum class HandlerResult
|
|
{
|
|
ContinueExecution,
|
|
ExecuteNextHandler,
|
|
};
|
|
|
|
HandlerResult HandlePageFault(void* exception_pc, void* fault_address, bool is_write);
|
|
bool Install(Error* error = nullptr);
|
|
bool InstallSecondaryThread();
|
|
} // namespace PageFaultHandler
|
|
|
|
class SharedMemoryMappingArea
|
|
{
|
|
public:
|
|
// fixed_base_hint: when non-zero, the area is placed at that VA (256MB-stride
|
|
// fallback slots, then kernel placement) so the reserved region — and any JIT
|
|
// code mapped inside it — lands at the same address every run, which a caller
|
|
// can rely on for address-stable code caching. Zero = kernel-chosen placement
|
|
// (the default).
|
|
static std::unique_ptr<SharedMemoryMappingArea> Create(size_t size, bool jit = false, uptr fixed_base_hint = 0);
|
|
|
|
~SharedMemoryMappingArea();
|
|
|
|
__fi size_t GetSize() const { return m_size; }
|
|
__fi size_t GetNumPages() const { return m_num_pages; }
|
|
|
|
__fi u8* BasePointer() const { return m_base_ptr; }
|
|
__fi u8* OffsetPointer(size_t offset) const { return m_base_ptr + offset; }
|
|
__fi u8* PagePointer(size_t page) const { return m_base_ptr + __pagesize * page; }
|
|
|
|
u8* Map(void* file_handle, size_t file_offset, void* map_base, size_t map_size, const PageProtectionMode& mode);
|
|
bool Unmap(void* map_base, size_t map_size, bool is_file = true);
|
|
|
|
private:
|
|
SharedMemoryMappingArea(u8* base_ptr, size_t size, size_t num_pages);
|
|
|
|
u8* m_base_ptr;
|
|
size_t m_size;
|
|
size_t m_num_pages;
|
|
size_t m_num_mappings = 0;
|
|
|
|
#ifdef _WIN32
|
|
using PlaceholderMap = std::map<size_t, size_t>;
|
|
|
|
PlaceholderMap::iterator FindPlaceholder(size_t page);
|
|
|
|
PlaceholderMap m_placeholder_ranges;
|
|
#endif
|
|
};
|
|
|
|
extern u64 GetTickFrequency();
|
|
extern u64 GetCPUTicks();
|
|
extern u64 GetPhysicalMemory();
|
|
extern u64 GetAvailablePhysicalMemory();
|
|
/// Spin for a short period of time (call while spinning waiting for a lock)
|
|
/// Returns the approximate number of ns that passed
|
|
extern u32 ShortSpin();
|
|
/// ShortSpin() for a wait whose entire predicate is one atomic word that another
|
|
/// thread stores to. Where the host can watch an address, this parks the core
|
|
/// until that store lands; `expected` is the value already seen, and the wait
|
|
/// ends once `word` no longer holds it. Returns the approximate number of ns
|
|
/// that passed.
|
|
///
|
|
/// May return early for no reason: re-check the predicate in a loop, exactly
|
|
/// as around ShortSpin(). Splitting the wait across two locations parks on a
|
|
/// store to neither — `word` must carry it alone.
|
|
extern u32 ShortSpinOn(const std::atomic<s32>& word, s32 expected);
|
|
/// Number of ns to spin for before sleeping a thread
|
|
extern const u32 SPIN_TIME_NS;
|
|
/// Like C abort() but adds the given message to the crashlog
|
|
[[noreturn]] void AbortWithMessage(const char* msg);
|
|
|
|
extern std::string GetOSVersionString();
|
|
|
|
struct CPUInfo {
|
|
std::string name;
|
|
u32 num_big_cores;
|
|
u32 num_small_cores;
|
|
u32 num_threads;
|
|
u32 num_clusters;
|
|
};
|
|
|
|
const CPUInfo& GetCPUInfo();
|
|
|
|
namespace Common
|
|
{
|
|
/// Enables or disables the screen saver from starting.
|
|
bool InhibitScreensaver(bool inhibit);
|
|
|
|
/// Abstracts platform-specific code for asynchronously playing a sound.
|
|
/// On Windows, this will use PlaySound(). On Linux, it will shell out to aplay. On MacOS, it uses NSSound.
|
|
bool PlaySoundAsync(const char* path);
|
|
|
|
void SetMousePosition(int x, int y);
|
|
bool AttachMousePositionCb(std::function<void(int,int)> cb);
|
|
void DetachMousePositionCb();
|
|
} // namespace Common
|