mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 10:56:58 +02:00
Linux: port the emulator to a working state on Linux (#117)
Linux: port the emulator to a working state
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <random>
|
||||
#include <system_error>
|
||||
#include <vector>
|
||||
|
||||
namespace Libs::LibKernel::FileSystem {
|
||||
@@ -153,6 +154,11 @@ void FileDescriptors::DeleteDescriptor(int d) {
|
||||
EXIT_IF(m_files[index] == nullptr);
|
||||
EXIT_IF(m_files[index]->opened);
|
||||
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
// Close host files opened before a failed descriptor setup.
|
||||
m_files[index]->f.Close();
|
||||
#endif
|
||||
|
||||
delete m_files[index];
|
||||
m_files[index] = nullptr;
|
||||
}
|
||||
@@ -223,6 +229,52 @@ void MountPoints::Umount(const std::string& folder_or_point) {
|
||||
}
|
||||
}
|
||||
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
// Resolve guest paths case-insensitively on case-sensitive hosts.
|
||||
static std::filesystem::path ResolvePathIgnoringCase(const std::filesystem::path& path) {
|
||||
std::error_code ec;
|
||||
if (std::filesystem::exists(path, ec)) {
|
||||
return path;
|
||||
}
|
||||
|
||||
// Preserve unmatched components for the caller's ENOENT path.
|
||||
std::filesystem::path resolved = path.has_root_path() ? path.root_path() : std::filesystem::path(".");
|
||||
bool matched = true;
|
||||
|
||||
for (const auto& component: path.relative_path()) {
|
||||
if (component.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto candidate = resolved / component;
|
||||
if (!matched || std::filesystem::exists(candidate, ec)) {
|
||||
resolved = std::move(candidate);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
for (std::filesystem::directory_iterator entry(resolved, ec), end; entry != end;
|
||||
entry.increment(ec)) {
|
||||
if (ec) {
|
||||
break;
|
||||
}
|
||||
if (Common::EqualNoCase(entry->path().filename().string(), component.string())) {
|
||||
resolved = entry->path();
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
resolved = std::move(candidate);
|
||||
matched = false;
|
||||
}
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::filesystem::path MountPoints::GetRealFilename(const std::string& mounted_file_name) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
@@ -239,7 +291,11 @@ std::filesystem::path MountPoints::GetRealFilename(const std::string& mounted_fi
|
||||
while (Common::StartsWith(rel_path, '/')) {
|
||||
rel_path = Common::RemoveFirst(rel_path, 1);
|
||||
}
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
return p.dir / rel_path;
|
||||
#else
|
||||
return ResolvePathIgnoringCase(p.dir / rel_path);
|
||||
#endif
|
||||
}
|
||||
|
||||
return mounted_file_name;
|
||||
@@ -260,7 +316,11 @@ std::filesystem::path MountPoints::GetRealDirectory(const std::string& mounted_d
|
||||
while (Common::StartsWith(rel_path, '/')) {
|
||||
rel_path = Common::RemoveFirst(rel_path, 1);
|
||||
}
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
return p.dir / rel_path;
|
||||
#else
|
||||
return ResolvePathIgnoringCase(p.dir / rel_path);
|
||||
#endif
|
||||
}
|
||||
|
||||
return mounted_directory;
|
||||
|
||||
@@ -242,6 +242,13 @@ static bool VirtualRangesOverlap(uint64_t left_start, uint64_t left_size, uint64
|
||||
static bool CommitFixedHostRange(uint64_t start, uint64_t size, VirtualMemory::Mode mode) {
|
||||
constexpr uint64_t PAGE_SIZE = 0x4000;
|
||||
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
// Prefer one syscall, then fall back to the per-page path.
|
||||
if (size > PAGE_SIZE && VirtualMemory::AllocFixed(start, size, mode)) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (uint64_t addr = start; addr < start + size; addr += PAGE_SIZE) {
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
MEMORY_BASIC_INFORMATION info {};
|
||||
@@ -2992,6 +2999,13 @@ static bool ReserveFixedHostRange(uint64_t start, uint64_t size) {
|
||||
g_test_host_reservation_pages_before_failure = UINT32_MAX;
|
||||
#endif
|
||||
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
// Prefer one syscall, then fall back to the per-page path.
|
||||
if (size > PAGE_SIZE && VirtualMemory::ReserveFixed(start, size)) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool host_mutated = false;
|
||||
for (uint64_t addr = start; addr < start + size; addr += PAGE_SIZE) {
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
|
||||
@@ -385,9 +385,12 @@ public:
|
||||
m_maps.emplace(old.vaddr, old);
|
||||
return false;
|
||||
}
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
if (preserve_placeholder && placeholder_preserved != nullptr) {
|
||||
*placeholder_preserved = true;
|
||||
}
|
||||
#endif
|
||||
// Only Windows preserves placeholders.
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1079,7 +1082,10 @@ private:
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool UnmapViewPreservePlaceholder(void* /*vaddr*/, uint64_t /*size*/) { return false; }
|
||||
// POSIX has no placeholder equivalent.
|
||||
static bool UnmapViewPreservePlaceholder(void* vaddr, uint64_t size) {
|
||||
return munmap(vaddr, size) == 0;
|
||||
}
|
||||
|
||||
int m_fd = -1;
|
||||
#endif
|
||||
|
||||
+109
-35
@@ -46,6 +46,17 @@
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
#include <fmt/format.h>
|
||||
#include <pthread_time.h>
|
||||
#elif !defined(__APPLE__)
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
#include <csignal> // pthread_kill is declared here, not in <pthread.h>
|
||||
#endif
|
||||
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS && (defined(_M_X64) || defined(__x86_64__))
|
||||
#include <x86intrin.h>
|
||||
#endif
|
||||
|
||||
#ifdef pthread_attr_getguardsize
|
||||
@@ -99,8 +110,21 @@ static constexpr int KERNEL_PTHREAD_MUTEX_RECURSIVE = 2;
|
||||
static constexpr int KERNEL_PTHREAD_MUTEX_NORMAL = 3;
|
||||
static constexpr int KERNEL_PTHREAD_MUTEX_ADAPTIVE = 4;
|
||||
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
// OS-level thread id reported to the guest.
|
||||
static uint64_t GetHostThreadId() {
|
||||
#if defined(__APPLE__)
|
||||
uint64_t tid = 0;
|
||||
pthread_threadid_np(nullptr, &tid);
|
||||
return tid;
|
||||
#else
|
||||
return static_cast<uint64_t>(::syscall(SYS_gettid));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static uint64_t KernelReadTscNative() {
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS && (defined(_M_X64) || defined(__x86_64__))
|
||||
#if defined(_M_X64) || defined(__x86_64__)
|
||||
return __rdtsc();
|
||||
#else
|
||||
return Common::Timer::QueryPerformanceCounter();
|
||||
@@ -109,7 +133,7 @@ static uint64_t KernelReadTscNative() {
|
||||
|
||||
static uint64_t KernelGetTscFrequencyNative() {
|
||||
static const uint64_t frequency = [] {
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS && (defined(_M_X64) || defined(__x86_64__))
|
||||
#if defined(_M_X64) || defined(__x86_64__)
|
||||
const auto host_frequency = Common::Timer::QueryPerformanceFrequency();
|
||||
if (host_frequency == 0) {
|
||||
return uint64_t {1000000000};
|
||||
@@ -784,7 +808,11 @@ static KYTY_SYSV_ABI void* RunOnGuestStack(void* arg, pthread_entry_func_t func,
|
||||
|
||||
if (g_pthread_self != nullptr) {
|
||||
g_pthread_self->guest_host_rbx = host_rbx;
|
||||
#if defined(__APPLE__)
|
||||
g_pthread_self->guest_host_rsp = host_rsp - (2u * sizeof(uint64_t));
|
||||
#else
|
||||
g_pthread_self->guest_host_rsp = host_rsp - (4u * sizeof(uint64_t));
|
||||
#endif
|
||||
g_pthread_self->guest_host_rbp = host_rbp;
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
uintptr_t host_gs8 = 0;
|
||||
@@ -794,45 +822,14 @@ static KYTY_SYSV_ABI void* RunOnGuestStack(void* arg, pthread_entry_func_t func,
|
||||
: "=r"(host_gs8), "=r"(host_gs10)
|
||||
:
|
||||
: "memory");
|
||||
g_pthread_self->guest_host_rsp -= 2u * sizeof(uint64_t);
|
||||
g_pthread_self->guest_host_gs8 = host_gs8;
|
||||
g_pthread_self->guest_host_gs10 = host_gs10;
|
||||
#endif
|
||||
}
|
||||
|
||||
// The guest ABI expects the entry argument in rdi and a 16-byte aligned stack before call.
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
asm volatile("pushq %%r12\n\t"
|
||||
"pushq %%r13\n\t"
|
||||
"pushq %%r14\n\t"
|
||||
"pushq %%r15\n\t"
|
||||
"movq %%gs:0x08, %%r14\n\t"
|
||||
"movq %%gs:0x10, %%r15\n\t"
|
||||
"xorq %%rcx, %%rcx\n\t"
|
||||
"movq %%rcx, %%gs:0x08\n\t"
|
||||
"movq %%rcx, %%gs:0x10\n\t"
|
||||
"movq %%rsp, %%r12\n\t"
|
||||
"movq %%rbp, %%r13\n\t"
|
||||
"movq %[guest_rsp], %%rsp\n\t"
|
||||
"movq %[guest_rbp], %%rbp\n\t"
|
||||
"callq *%%rsi\n\t"
|
||||
"movq %%r13, %%rbp\n\t"
|
||||
"movq %%r12, %%rsp\n\t"
|
||||
"movq %%r14, %%gs:0x08\n\t"
|
||||
"movq %%r15, %%gs:0x10\n\t"
|
||||
"popq %%r15\n\t"
|
||||
"popq %%r14\n\t"
|
||||
"popq %%r13\n\t"
|
||||
"popq %%r12\n\t"
|
||||
: "=a"(ret), "+D"(arg), "+S"(func)
|
||||
: [guest_rsp] "r"(guest_rsp), [guest_rbp] "r"(guest_rbp)
|
||||
: "cc", "memory", "rcx", "rdx", "r8", "r9", "r10", "r11", "xmm0", "xmm1", "xmm2",
|
||||
"xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "xmm8", "xmm9", "xmm10", "xmm11",
|
||||
"xmm12", "xmm13", "xmm14", "xmm15");
|
||||
#else
|
||||
// Pin the stack operands to registers the template never touches (r14/r15 are
|
||||
// SysV callee-saved, so guest code preserves them): plain "r" constraints may
|
||||
// allocate them into r12/r13, which the template overwrites before use.
|
||||
#if defined(__APPLE__)
|
||||
// Keep inputs out of r12/r13.
|
||||
register uintptr_t guest_rsp_reg asm("r14") = guest_rsp;
|
||||
register uintptr_t guest_rbp_reg asm("r15") = guest_rbp;
|
||||
asm volatile("pushq %%r12\n\t"
|
||||
@@ -851,6 +848,45 @@ static KYTY_SYSV_ABI void* RunOnGuestStack(void* arg, pthread_entry_func_t func,
|
||||
: "cc", "memory", "rcx", "rdx", "r8", "r9", "r10", "r11", "xmm0", "xmm1", "xmm2",
|
||||
"xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "xmm8", "xmm9", "xmm10", "xmm11",
|
||||
"xmm12", "xmm13", "xmm14", "xmm15");
|
||||
#else
|
||||
// PthreadExit resumes at this frame, so all four saved registers stay on the host stack.
|
||||
asm volatile("pushq %%r12\n\t"
|
||||
"pushq %%r13\n\t"
|
||||
"pushq %%r14\n\t"
|
||||
"pushq %%r15\n\t"
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
"movq %%gs:0x08, %%r14\n\t"
|
||||
"movq %%gs:0x10, %%r15\n\t"
|
||||
"xorq %%rcx, %%rcx\n\t"
|
||||
"movq %%rcx, %%gs:0x08\n\t"
|
||||
"movq %%rcx, %%gs:0x10\n\t"
|
||||
#endif
|
||||
"movq %%rsp, %%r12\n\t"
|
||||
"movq %%rbp, %%r13\n\t"
|
||||
"movq %[guest_rsp], %%rsp\n\t"
|
||||
"movq %[guest_rbp], %%rbp\n\t"
|
||||
"callq *%%rsi\n\t"
|
||||
"movq %%r13, %%rbp\n\t"
|
||||
"movq %%r12, %%rsp\n\t"
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
"movq %%r14, %%gs:0x08\n\t"
|
||||
"movq %%r15, %%gs:0x10\n\t"
|
||||
#endif
|
||||
"popq %%r15\n\t"
|
||||
"popq %%r14\n\t"
|
||||
"popq %%r13\n\t"
|
||||
"popq %%r12\n\t"
|
||||
: "=a"(ret), "+D"(arg), "+S"(func)
|
||||
: [guest_rsp] "r"(guest_rsp), [guest_rbp] "r"(guest_rbp)
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
: "cc", "memory", "rcx", "rdx", "r8", "r9", "r10", "r11", "xmm0", "xmm1", "xmm2",
|
||||
"xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "xmm8", "xmm9", "xmm10", "xmm11",
|
||||
"xmm12", "xmm13", "xmm14", "xmm15");
|
||||
#else
|
||||
: "cc", "memory", "rcx", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "xmm0",
|
||||
"xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "xmm8", "xmm9", "xmm10",
|
||||
"xmm11", "xmm12", "xmm13", "xmm14", "xmm15");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
g_guest_entry_return_rsp = 0;
|
||||
@@ -886,6 +922,29 @@ static void UpdateCurrentThreadStackAttr(PthreadAttr* attr) {
|
||||
(*attr)->stack_size = high - low;
|
||||
(*attr)->stack_user = true;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
// macOS reports a stack top and size.
|
||||
void* top = pthread_get_stackaddr_np(pthread_self());
|
||||
const size_t size = pthread_get_stacksize_np(pthread_self());
|
||||
|
||||
if (top != nullptr && size != 0) {
|
||||
(*attr)->stack_addr = static_cast<void*>(static_cast<uint8_t*>(top) - size);
|
||||
(*attr)->stack_size = size;
|
||||
(*attr)->stack_user = true;
|
||||
}
|
||||
#else
|
||||
// Record the main thread's stack bounds.
|
||||
pthread_attr_t self_attr {};
|
||||
if (pthread_getattr_np(pthread_self(), &self_attr) == 0) {
|
||||
void* base = nullptr;
|
||||
size_t size = 0;
|
||||
if (pthread_attr_getstack(&self_attr, &base, &size) == 0 && base != nullptr && size != 0) {
|
||||
(*attr)->stack_addr = base;
|
||||
(*attr)->stack_size = size;
|
||||
(*attr)->stack_user = true;
|
||||
}
|
||||
pthread_attr_destroy(&self_attr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -926,6 +985,8 @@ void PthreadInitSelfForMainThread() {
|
||||
uint64_t os_thread_id = 0;
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
os_thread_id = static_cast<uint64_t>(GetCurrentThreadId());
|
||||
#else
|
||||
os_thread_id = GetHostThreadId();
|
||||
#endif
|
||||
g_pthread_self->host_thread_id = os_thread_id;
|
||||
g_pthread_main = g_pthread_self;
|
||||
@@ -3118,6 +3179,17 @@ uint64_t PthreadGetHostThreadId(Pthread thread) {
|
||||
return thread != nullptr ? thread->host_thread_id : 0;
|
||||
}
|
||||
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
// Raise a host signal on another guest thread.
|
||||
bool PthreadKillHost(Pthread thread, int host_signal) {
|
||||
if (thread == nullptr || thread->free) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ::pthread_kill(thread->p, host_signal) == 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void PthreadQueuePendingSignal(Pthread thread, int signum) {
|
||||
if (thread == nullptr || signum < 0 || signum >= 64) {
|
||||
return;
|
||||
@@ -3215,6 +3287,8 @@ static void* RunThread(void* arg) {
|
||||
uint64_t os_thread_id = 0;
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
os_thread_id = static_cast<uint64_t>(GetCurrentThreadId());
|
||||
#else
|
||||
os_thread_id = GetHostThreadId();
|
||||
#endif
|
||||
thread->host_thread_id = os_thread_id;
|
||||
|
||||
|
||||
@@ -112,6 +112,9 @@ void PthreadQueuePendingSignal(Pthread thread, int signum);
|
||||
bool PthreadHasPendingSignal(Pthread thread, int signum);
|
||||
bool PthreadTakePendingSignal(Pthread thread, int signum);
|
||||
bool PthreadGetGuestStack(Pthread thread, uint64_t* stack_addr, uint64_t* stack_size);
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
bool PthreadKillHost(Pthread thread, int host_signal);
|
||||
#endif
|
||||
int PthreadGetPriorityForKernel(Pthread thread);
|
||||
int PthreadGetCurrentPriorityForKernel();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user