2026-01-12 05:18:12 -05:00
|
|
|
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
|
2024-07-30 13:42:36 +02:00
|
|
|
// SPDX-License-Identifier: GPL-3.0+
|
2009-07-03 00:49:40 +00:00
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
2023-06-15 01:36:54 +10:00
|
|
|
#include "common/Pcsx2Defs.h"
|
|
|
|
|
|
2021-09-01 16:31:46 -04:00
|
|
|
#include <atomic>
|
2025-02-17 10:41:31 -05:00
|
|
|
#include <functional>
|
2022-10-12 23:57:53 +10:00
|
|
|
#include <map>
|
|
|
|
|
#include <memory>
|
2022-05-18 23:27:23 +10:00
|
|
|
#include <string>
|
2023-10-10 11:28:28 +01:00
|
|
|
|
2024-05-07 01:37:56 +10:00
|
|
|
class Error;
|
|
|
|
|
|
2010-01-22 15:22:01 +00:00
|
|
|
// --------------------------------------------------------------------------------------
|
2010-10-22 16:23:52 +00:00
|
|
|
// PageProtectionMode
|
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
|
class PageProtectionMode
|
2009-07-03 00:49:40 +00:00
|
|
|
{
|
2010-10-22 16:23:52 +00:00
|
|
|
protected:
|
2023-12-26 21:25:10 +10:00
|
|
|
bool m_read = false;
|
|
|
|
|
bool m_write = false;
|
|
|
|
|
bool m_exec = false;
|
2010-10-22 16:23:52 +00:00
|
|
|
|
|
|
|
|
public:
|
2023-12-26 21:25:10 +10:00
|
|
|
__fi constexpr PageProtectionMode() = default;
|
2010-10-22 16:23:52 +00:00
|
|
|
|
2023-12-26 21:25:10 +10:00
|
|
|
__fi constexpr PageProtectionMode& Read(bool allow = true)
|
2021-09-06 14:28:26 -04:00
|
|
|
{
|
|
|
|
|
m_read = allow;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
2010-10-22 16:23:52 +00:00
|
|
|
|
2023-12-26 21:25:10 +10:00
|
|
|
__fi constexpr PageProtectionMode& Write(bool allow = true)
|
2021-09-06 14:28:26 -04:00
|
|
|
{
|
|
|
|
|
m_write = allow;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
2010-10-22 16:23:52 +00:00
|
|
|
|
2023-12-26 21:25:10 +10:00
|
|
|
__fi constexpr PageProtectionMode& Execute(bool allow = true)
|
2021-09-06 14:28:26 -04:00
|
|
|
{
|
|
|
|
|
m_exec = allow;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
2010-11-03 14:18:30 +00:00
|
|
|
|
2023-12-26 21:25:10 +10:00
|
|
|
__fi constexpr PageProtectionMode& All(bool allow = true)
|
2021-09-06 14:28:26 -04:00
|
|
|
{
|
|
|
|
|
m_read = m_write = m_exec = allow;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
2016-11-12 16:28:37 +01:00
|
|
|
|
2023-12-26 21:25:10 +10:00
|
|
|
__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; }
|
2009-07-03 00:49:40 +00:00
|
|
|
};
|
|
|
|
|
|
2010-10-22 16:23:52 +00:00
|
|
|
static __fi PageProtectionMode PageAccess_None()
|
|
|
|
|
{
|
2021-09-06 14:28:26 -04:00
|
|
|
return PageProtectionMode();
|
2010-10-22 16:23:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static __fi PageProtectionMode PageAccess_ReadOnly()
|
|
|
|
|
{
|
2021-09-06 14:28:26 -04:00
|
|
|
return PageProtectionMode().Read();
|
2010-10-22 16:23:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static __fi PageProtectionMode PageAccess_WriteOnly()
|
|
|
|
|
{
|
2021-09-06 14:28:26 -04:00
|
|
|
return PageProtectionMode().Write();
|
2010-10-22 16:23:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static __fi PageProtectionMode PageAccess_ReadWrite()
|
|
|
|
|
{
|
2021-09-06 14:28:26 -04:00
|
|
|
return PageAccess_ReadOnly().Write();
|
2010-10-22 16:23:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static __fi PageProtectionMode PageAccess_ExecOnly()
|
|
|
|
|
{
|
2021-09-06 14:28:26 -04:00
|
|
|
return PageAccess_ReadOnly().Execute();
|
2010-10-22 16:23:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static __fi PageProtectionMode PageAccess_Any()
|
|
|
|
|
{
|
2021-09-06 14:28:26 -04:00
|
|
|
return PageProtectionMode().All();
|
2010-10-22 16:23:52 +00:00
|
|
|
}
|
|
|
|
|
|
2010-01-22 15:22:01 +00:00
|
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
|
// HostSys
|
|
|
|
|
// --------------------------------------------------------------------------------------
|
2009-07-03 00:49:40 +00:00
|
|
|
namespace HostSys
|
|
|
|
|
{
|
2021-09-06 14:28:26 -04:00
|
|
|
extern void MemProtect(void* baseaddr, size_t size, const PageProtectionMode& mode);
|
2009-07-03 00:49:40 +00:00
|
|
|
|
2022-10-12 23:57:53 +10:00
|
|
|
extern std::string GetFileMappingName(const char* prefix);
|
|
|
|
|
extern void* CreateSharedMemory(const char* name, size_t size);
|
|
|
|
|
extern void DestroySharedMemory(void* ptr);
|
2022-12-28 16:53:37 +10:00
|
|
|
|
2023-12-26 20:25:26 +10:00
|
|
|
/// JIT write protect for Apple Silicon. Needs to be called prior to writing to any RWX pages.
|
2026-03-10 23:40:32 -05:00
|
|
|
#if !defined(__APPLE__) || !defined(ARCH_ARM64)
|
2023-12-26 20:25:26 +10:00
|
|
|
// clang-format -off
|
|
|
|
|
[[maybe_unused]] __fi static void BeginCodeWrite() {}
|
|
|
|
|
[[maybe_unused]] __fi static void EndCodeWrite() {}
|
2026-07-12 16:39:22 +02:00
|
|
|
[[maybe_unused]] __fi static void BeginCodeWriteRange(void*, size_t) {}
|
|
|
|
|
[[maybe_unused]] __fi static void EndCodeWriteRange(void*, size_t) {}
|
2023-12-26 20:25:26 +10:00
|
|
|
// clang-format on
|
|
|
|
|
#else
|
|
|
|
|
void BeginCodeWrite();
|
|
|
|
|
void EndCodeWrite();
|
2026-07-12 16:39:22 +02:00
|
|
|
void BeginCodeWriteRange(void* address, size_t size);
|
|
|
|
|
void EndCodeWriteRange(void* address, size_t size);
|
2023-12-26 20:25:26 +10:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/// Flushes the instruction cache on the host for the specified range.
|
|
|
|
|
/// Only needed on ARM64, X86 has coherent D/I cache.
|
2026-03-10 23:40:32 -05:00
|
|
|
#ifdef ARCH_X86
|
2023-12-26 20:25:26 +10:00
|
|
|
[[maybe_unused]] __fi static void FlushInstructionCache(void* address, u32 size) {}
|
|
|
|
|
#else
|
|
|
|
|
void FlushInstructionCache(void* address, u32 size);
|
|
|
|
|
#endif
|
2024-03-21 16:43:50 +10:00
|
|
|
|
|
|
|
|
/// 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();
|
2024-05-29 20:30:53 +10:00
|
|
|
} // namespace HostSys
|
2009-07-03 00:49:40 +00:00
|
|
|
|
2024-05-07 01:37:56 +10:00
|
|
|
namespace PageFaultHandler
|
|
|
|
|
{
|
2024-05-29 20:30:53 +10:00
|
|
|
enum class HandlerResult
|
|
|
|
|
{
|
|
|
|
|
ContinueExecution,
|
|
|
|
|
ExecuteNextHandler,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
HandlerResult HandlePageFault(void* exception_pc, void* fault_address, bool is_write);
|
|
|
|
|
bool Install(Error* error = nullptr);
|
2026-03-23 23:01:37 -05:00
|
|
|
bool InstallSecondaryThread();
|
2024-05-07 01:37:56 +10:00
|
|
|
} // namespace PageFaultHandler
|
|
|
|
|
|
2022-10-29 13:39:19 +10:00
|
|
|
class SharedMemoryMappingArea
|
|
|
|
|
{
|
|
|
|
|
public:
|
2026-06-20 20:27:55 -07:00
|
|
|
// 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);
|
2022-10-29 13:39:19 +10:00
|
|
|
|
|
|
|
|
~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);
|
2024-08-21 03:02:09 -05:00
|
|
|
bool Unmap(void* map_base, size_t map_size, bool is_file = true);
|
2022-10-29 13:39:19 +10:00
|
|
|
|
|
|
|
|
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
|
|
|
|
|
};
|
|
|
|
|
|
2016-11-12 16:28:37 +01:00
|
|
|
extern u64 GetTickFrequency();
|
|
|
|
|
extern u64 GetCPUTicks();
|
2010-11-23 21:32:52 +00:00
|
|
|
extern u64 GetPhysicalMemory();
|
2025-04-15 09:57:15 +02:00
|
|
|
extern u64 GetAvailablePhysicalMemory();
|
2021-03-28 02:50:18 -05:00
|
|
|
/// 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();
|
2026-08-06 22:21:26 +02:00
|
|
|
/// 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);
|
2021-03-28 02:50:18 -05:00
|
|
|
/// Number of ns to spin for before sleeping a thread
|
|
|
|
|
extern const u32 SPIN_TIME_NS;
|
2022-05-30 00:49:07 -05:00
|
|
|
/// Like C abort() but adds the given message to the crashlog
|
|
|
|
|
[[noreturn]] void AbortWithMessage(const char* msg);
|
2009-11-26 03:37:10 +00:00
|
|
|
|
2022-05-18 23:27:23 +10:00
|
|
|
extern std::string GetOSVersionString();
|
2015-02-26 04:08:58 +02:00
|
|
|
|
2024-09-13 03:48:07 -05:00
|
|
|
struct CPUInfo {
|
|
|
|
|
std::string name;
|
|
|
|
|
u32 num_big_cores;
|
|
|
|
|
u32 num_small_cores;
|
|
|
|
|
u32 num_threads;
|
|
|
|
|
u32 num_clusters;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const CPUInfo& GetCPUInfo();
|
|
|
|
|
|
2022-04-18 23:35:14 +10:00
|
|
|
namespace Common
|
|
|
|
|
{
|
2024-05-23 23:35:34 +10:00
|
|
|
/// Enables or disables the screen saver from starting.
|
|
|
|
|
bool InhibitScreensaver(bool inhibit);
|
|
|
|
|
|
2022-04-18 23:35:14 +10:00
|
|
|
/// 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);
|
2025-02-17 10:41:31 -05:00
|
|
|
|
|
|
|
|
void SetMousePosition(int x, int y);
|
|
|
|
|
bool AttachMousePositionCb(std::function<void(int,int)> cb);
|
|
|
|
|
void DetachMousePositionCb();
|
2022-04-18 23:35:14 +10:00
|
|
|
} // namespace Common
|