From 522a74ee7e86d407ef548b4f431ebd0d6611f5ad Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Thu, 29 Aug 2024 20:47:38 +0300 Subject: [PATCH] WIP --- CMakeLists.txt | 6 + src/audio_core/sdl_audio.cpp | 3 + src/audio_core/sdl_audio.h | 2 +- src/core/libraries/kernel/libkernel.cpp | 85 ++++- src/core/libraries/kernel/orbis_signals.h | 185 ++++++++++ .../libraries/kernel/thread_management.cpp | 141 ++++---- src/core/libraries/kernel/thread_management.h | 5 - src/core/libraries/kernel/win32_signals.cpp | 325 ++++++++++++++++++ 8 files changed, 666 insertions(+), 86 deletions(-) create mode 100644 src/core/libraries/kernel/orbis_signals.h create mode 100644 src/core/libraries/kernel/win32_signals.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2da9465cc9..e5e7842d98 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -107,6 +107,10 @@ check_symbol_exists(pthread_mutex_timedlock "pthread.h" HAVE_PTHREAD_MUTEX_TIMED if(HAVE_PTHREAD_MUTEX_TIMEDLOCK OR WIN32) add_compile_options(-DHAVE_PTHREAD_MUTEX_TIMEDLOCK) endif() +check_symbol_exists(sem_timedwait "semaphore.h" HAVE_SEM_TIMEDWAIT) +if(HAVE_SEM_TIMEDWAIT OR WIN32) + add_compile_options(-DHAVE_SEM_TIMEDWAIT) +endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") # libc++ requires -fexperimental-library to enable std::jthread and std::stop_token support. @@ -184,10 +188,12 @@ set(KERNEL_LIB src/core/libraries/kernel/libkernel.h src/core/libraries/kernel/memory_management.cpp src/core/libraries/kernel/memory_management.h + src/core/libraries/kernel/orbis_signals.h src/core/libraries/kernel/thread_management.cpp src/core/libraries/kernel/thread_management.h src/core/libraries/kernel/time_management.cpp src/core/libraries/kernel/time_management.h + src/core/libraries/kernel/win32_signals.cpp ) set(NETWORK_LIBS src/core/libraries/network/http.cpp diff --git a/src/audio_core/sdl_audio.cpp b/src/audio_core/sdl_audio.cpp index f544c52f9f..b50725856f 100644 --- a/src/audio_core/sdl_audio.cpp +++ b/src/audio_core/sdl_audio.cpp @@ -13,6 +13,9 @@ #include // std::unique_lock namespace Audio { +SDLAudio::~SDLAudio() { + std::unique_lock lock{m_mutex}; +} int SDLAudio::AudioOutOpen(int type, u32 samples_num, u32 freq, Libraries::AudioOut::OrbisAudioOutParamFormat format) { diff --git a/src/audio_core/sdl_audio.h b/src/audio_core/sdl_audio.h index 07e82145e9..80aa568051 100644 --- a/src/audio_core/sdl_audio.h +++ b/src/audio_core/sdl_audio.h @@ -12,7 +12,7 @@ namespace Audio { class SDLAudio { public: SDLAudio() = default; - virtual ~SDLAudio() = default; + ~SDLAudio(); int AudioOutOpen(int type, u32 samples_num, u32 freq, Libraries::AudioOut::OrbisAudioOutParamFormat format); diff --git a/src/core/libraries/kernel/libkernel.cpp b/src/core/libraries/kernel/libkernel.cpp index d56f4dc416..94d0076eec 100644 --- a/src/core/libraries/kernel/libkernel.cpp +++ b/src/core/libraries/kernel/libkernel.cpp @@ -1,10 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include -#include - -#include +#include "core/libraries/kernel/libkernel.h" #include "common/assert.h" #include "common/debug.h" @@ -19,14 +16,20 @@ #include "core/libraries/kernel/event_flag/event_flag.h" #include "core/libraries/kernel/event_queues.h" #include "core/libraries/kernel/file_system.h" -#include "core/libraries/kernel/libkernel.h" #include "core/libraries/kernel/memory_management.h" +#include "core/libraries/kernel/orbis_signals.h" #include "core/libraries/kernel/thread_management.h" #include "core/libraries/kernel/time_management.h" #include "core/libraries/libs.h" #include "core/linker.h" #include "core/memory.h" +#include + +#include +#include +#include + #ifdef _WIN64 #include #include @@ -86,11 +89,11 @@ static PS4_SYSV_ABI void stack_chk_fail() { int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len) { LOG_INFO(Kernel_Vmm, "addr = {}, len = {:#x}", fmt::ptr(addr), len); if (len == 0) { - return ORBIS_OK; + return ORBIS_KERNEL_ERROR_EINVAL; } auto* memory = Core::Memory::Instance(); memory->UnmapMemory(std::bit_cast(addr), len); - return SCE_OK; + return ORBIS_OK; } struct iovec { @@ -398,6 +401,70 @@ int PS4_SYSV_ABI posix_getpagesize() { return 4096; } +int sceKernelError(int err) { + if (err == 0) { + return 0; + } + return err + -SCE_KERNEL_ERROR_UNKNOWN; +} + +int PS4_SYSV_ABI sceKernelRaiseException(ScePthread thread, s32 sig) { + LOG_INFO(Lib_Kernel, "called. thread = {} sig = {}", uintptr_t(thread), sig); + if (sig != ORBIS_SIGUSR1) { + return sceKernelError(POSIX_EINVAL); + } + Orbis::pthread_kill(thread->pth, sig); + return ORBIS_OK; +} + +using ExceptionHandler = void (*PS4_SYSV_ABI)(s32, void*); + +static std::atomic g_exception_handlers[32]; + +void ExceptionSignalHandler(s32 sig, Orbis::siginfo_t*, void* context) { + LOG_INFO(Lib_Kernel, "called. sig = {}", sig); + if (sig > ORBIS_SIG_MAXSIG32) { + return; + } + const auto handler = g_exception_handlers[ORBIS_SIG_IDX(sig)].load(); + if (intptr_t(handler) > 0) { + handler(sig, context); + } +} + +int PS4_SYSV_ABI sceKernelInstallExceptionHandler(s32 sig, ExceptionHandler handler) { + LOG_INFO(Lib_Kernel, "called. sig = {}", sig); + if (sig > ORBIS_SIG_MAXSIG32) { + return sceKernelError(POSIX_EINVAL); + } + + auto expected = ExceptionHandler(nullptr); + if (!g_exception_handlers[ORBIS_SIG_IDX(sig)].compare_exchange_strong(expected, handler)) { + return sceKernelError(POSIX_EAGAIN); + } + + struct Orbis::sigaction sact = { + .sa_sigaction = &ExceptionSignalHandler, + .sa_flags = ORBIS_SA_RESTART, + }; + return sceKernelError(Orbis::sigaction(sig, &sact, nullptr)); +} + +int PS4_SYSV_ABI sceKernelRemoveExceptionHandler(s32 sig) { + LOG_INFO(Lib_Kernel, "called. sig = {}", sig); + if (sig > ORBIS_SIG_MAXSIG32) { + return sceKernelError(POSIX_EINVAL); + } + auto expected = g_exception_handlers[sig - 1].load(); + if (!g_exception_handlers[ORBIS_SIG_IDX(sig)].compare_exchange_strong(expected, nullptr)) { + return sceKernelError(POSIX_EAGAIN); + } + struct Orbis::sigaction sact = { + .sa_flags = ORBIS_SA_RESETHAND, + }; + return sceKernelError(Orbis::sigaction(sig, &sact, nullptr)); +} + void LibKernel_Register(Core::Loader::SymbolsResolver* sym) { service_thread = std::jthread{KernelServiceThread}; @@ -471,6 +538,10 @@ void LibKernel_Register(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("DRuBt2pvICk", "libkernel", 1, "libkernel", 1, 1, ps4__read); LIB_FUNCTION("k+AXqu2-eBc", "libScePosix", 1, "libkernel", 1, 1, posix_getpagesize); + LIB_FUNCTION("WkwEd3N7w0Y", "libkernel_unity", 1, "libkernel", 1, 1, + sceKernelInstallExceptionHandler); + LIB_FUNCTION("il03nluKfMk", "libkernel_unity", 1, "libkernel", 1, 1, sceKernelRaiseException); + Libraries::Kernel::fileSystemSymbolsRegister(sym); Libraries::Kernel::timeSymbolsRegister(sym); Libraries::Kernel::pthreadSymbolsRegister(sym); diff --git a/src/core/libraries/kernel/orbis_signals.h b/src/core/libraries/kernel/orbis_signals.h new file mode 100644 index 0000000000..85927956e9 --- /dev/null +++ b/src/core/libraries/kernel/orbis_signals.h @@ -0,0 +1,185 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +#include + +#define ORBIS_SA_RESTART 0x0002 +#define ORBIS_SA_RESETHAND 0x0004 +#define ORBIS_SA_NODEFER 0x0010 +#define ORBIS_SA_SIGINFO 0x0040 + +#define ORBIS_SIGKILL 9 +#define ORBIS_SIGURG 16 +#define ORBIS_SIGSTOP 17 +#define ORBIS_SIGCHLD 20 +#define ORBIS_SIGWINCH 28 +#define ORBIS_SIGINFO 29 +#define ORBIS_SIGUSR1 30 + +#define ORBIS_SI_USER 0x10001 + +#define ORBIS_SIG_WORDS 4 +#define ORBIS_SIG_MAXSIG 128 +#define ORBIS_SIG_MAXSIG32 32 +#define ORBIS_SIG_IDX(sig) ((sig)-1) +#define ORBIS_SIG_WORD(sig) (_SIG_IDX(sig) >> 5) +#define ORBIS_SIG_BIT(sig) (1 << (_SIG_IDX(sig) & 31)) +#define ORBIS_SIG_VALID(sig) ((sig) <= _SIG_MAXSIG && (sig) > 0) + +#define ORBIS_SIG_ERR (uintptr_t(-1)) +#define ORBIS_SIG_DFL (uintptr_t(0)) +#define ORBIS_SIG_IGN (uintptr_t(1)) +#define ORBIS_SIG_CATCH (uintptr_t(2)) +#define ORBIS_SIG_HOLD (uintptr_t(3)) + +#define ORBIS_SS_ONSTACK 0x0001; // take signal on alternate stack +#define ORBIS_SS_DISABLE 0x0004; // disable taking signals on alternate stack +#define ORBIS_MINSIGSTKSZ (512 * 4); // minimum stack size +#define ORBIS_SIGSTKSZ (MINSIGSTKSZ + 0x8000); // recommended stack size + +#define ORBIS_UC_SIGMASK 0x01; // valid uc_sigmask +#define ORBIS_UC_STACK 0x02; // valid uc_stack +#define ORBIS_UC_CPU 0x04; // valid GPR context in uc_mcontext +#define ORBIS_UC_FPU 0x08; // valid FPU context in uc_mcontext + +#define ORBIS_MC_HASSEGS 0x01 +#define ORBIS_MC_FPFMT_XMM 0x10002 +#define ORBIS_MC_FPOWNED_FPU 0x20001 // FP state came from FPU + +namespace Orbis { +using pid_t = u32; +using uid_t = u32; + +union sigval { + /* Members as suggested by Annex C of POSIX 1003.1b. */ + s32 sival_int; + void* sival_ptr; + /* 6.0 compatibility */ + s32 sigval_int; + void* sigval_ptr; +}; + +struct siginfo_t { + s32 si_signo; /* signal number */ + s32 si_errno; /* errno association */ + /* + * Cause of signal, one of the SI_ macros or signal-specific + * values, i.e. one of the FPE_... values for SIGFPE. This + * value is equivalent to the second argument to an old-style + * FreeBSD signal handler. + */ + s32 si_code; /* signal code */ + pid_t si_pid; /* sending process */ + uid_t si_uid; /* sender's ruid */ + s32 si_status; /* exit value */ + void* si_addr; /* faulting instruction */ + sigval si_value; /* signal value */ + union { + struct { + s32 _trapno; /* machine specific trap code */ + } _fault; + struct { + s32 _timerid; + s32 _overrun; + } _timer; + struct { + s32 _mqd; + } _mesgq; + struct { + s64 _band; /* band event for SIGPOLL */ + } _poll; /* was this ever used ? */ + struct { + s64 __spare1__; + s32 __spare2__[7]; + } __spare__; + } _reason; +}; + +#pragma pack(push, 4) +struct sigset_t { + u32 __bits[ORBIS_SIG_WORDS]; +}; + +struct mcontext_t { + s64 mc_onstack; /* sigstack state to restore */ + s64 mc_rdi; /* machine state (struct trapframe) */ + s64 mc_rsi; + s64 mc_rdx; + s64 mc_rcx; + s64 mc_r8; + s64 mc_r9; + s64 mc_rax; + s64 mc_rbx; + s64 mc_rbp; + s64 mc_r10; + s64 mc_r11; + s64 mc_r12; + s64 mc_r13; + s64 mc_r14; + s64 mc_r15; + s32 mc_trapno; + s16 mc_fs; + s16 mc_gs; + s64 mc_addr; + s32 mc_flags; + s16 mc_es; + s16 mc_ds; + s64 mc_err; + s64 mc_rip; + s64 mc_cs; + s64 mc_rflags; + s64 mc_rsp; + s64 mc_ss; + s64 mc_len; /* sizeof(mcontext_t) */ + s64 mc_fpformat; + s64 mc_ownedfp; + s64 mc_lbrfrom; + s64 mc_lbrto; + s64 mc_aux1; + s64 mc_aux2; + s64 mc_fpstate[104]; + s64 mc_fsbase; + s64 mc_gsbase; + s64 mc_spare[6]; +}; + +struct stack_t { + void* ss_sp; // signal stack base + size_t ss_size; // signal stack length SIGSTKSZ + s32 ss_flags; // SS_DISABLE and/or SS_ONSTACK + s32 _align; +}; + +struct ucontext_t { + sigset_t sc_mask; /* signal mask to restore */ + s32 field1_0x10[12]; + struct mcontext_t uc_mcontext; + struct ucontext_t* uc_link; + struct stack_t uc_stack; + s32 uc_flags; + s32 __spare[4]; + s32 field7_0x4f4[3]; +}; + +struct sigaction { + union { + void (*sa_handler)(s32, s32, void*); + void (*sa_sigaction)(s32, siginfo_t*, void*); + }; + s32 sa_flags; + sigset_t sa_mask; +}; +#pragma pack(pop) + +static_assert(sizeof(ucontext_t) == 0x500); + +using pthread_t = uintptr_t; + +s32 sigaction(s32 sig, const struct sigaction* act, struct sigaction* oldact); +s32 pthread_kill(pthread_t thread, s32 sig); + +} // namespace Orbis diff --git a/src/core/libraries/kernel/thread_management.cpp b/src/core/libraries/kernel/thread_management.cpp index 8f97ed8795..4654374f1a 100644 --- a/src/core/libraries/kernel/thread_management.cpp +++ b/src/core/libraries/kernel/thread_management.cpp @@ -1,10 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include -#include -#include - #include "common/alignment.h" #include "common/assert.h" #include "common/error.h" @@ -19,6 +15,12 @@ #include "core/libraries/libs.h" #include "core/linker.h" #include "core/tls.h" + +#include + +#include +#include + #ifdef _WIN64 #include #else @@ -1373,97 +1375,90 @@ int PS4_SYSV_ABI posix_pthread_detach(ScePthread thread) { return pthread_detach(thread->pth); } -int PS4_SYSV_ABI posix_sem_init(PthreadSemInternal** sem, int pshared, unsigned int value) { - if (value > ORBIS_KERNEL_SEM_VALUE_MAX) { - SetPosixErrno(EINVAL); - return -1; - } - if (sem != nullptr) { - *sem = new PthreadSemInternal{ - .semaphore = std::counting_semaphore{value}, - .value = {static_cast(value)}, - }; +int PS4_SYSV_ABI posix_sem_init(sem_t* sem, int pshared, unsigned int value) { + int result = sem_init(sem, pshared, value); + if (result == -1) { + SetPosixErrno(errno); } - return 0; + return result; } -int PS4_SYSV_ABI posix_sem_wait(PthreadSemInternal** sem) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; +int PS4_SYSV_ABI posix_sem_wait(sem_t* sem) { + int result = sem_wait(sem); + if (result == -1) { + SetPosixErrno(errno); } - (*sem)->semaphore.acquire(); - --(*sem)->value; - return 0; + return result; } -int PS4_SYSV_ABI posix_sem_trywait(PthreadSemInternal** sem) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; +int PS4_SYSV_ABI posix_sem_trywait(sem_t* sem) { + int result = sem_trywait(sem); + if (result == -1) { + SetPosixErrno(errno); } - if (!(*sem)->semaphore.try_acquire()) { - SetPosixErrno(EAGAIN); - return -1; - } - --(*sem)->value; - return 0; + return result; } -int PS4_SYSV_ABI posix_sem_timedwait(PthreadSemInternal** sem, const timespec* t) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; - } +#ifndef HAVE_SEM_TIMEDWAIT +int sem_timedwait(sem_t* sem, const struct timespec* abstime) { + int rc; + while ((rc = sem_trywait(sem)) == EAGAIN) { + struct timespec curr_time; + clock_gettime(CLOCK_REALTIME, &curr_time); - using std::chrono::duration_cast; - using std::chrono::nanoseconds; - using std::chrono::seconds; - using std::chrono::system_clock; + s64 remaining_ns = 0; + remaining_ns += + (static_cast(abstime->tv_sec) - static_cast(curr_time.tv_sec)) * 1000000000L; + remaining_ns += static_cast(abstime->tv_nsec) - static_cast(curr_time.tv_nsec); - const system_clock::time_point time{ - duration_cast(seconds{t->tv_sec} + nanoseconds{t->tv_nsec})}; - if (!(*sem)->semaphore.try_acquire_until(time)) { - SetPosixErrno(ETIMEDOUT); - return -1; + if (remaining_ns <= 0) { + return ETIMEDOUT; + } + + struct timespec sleep_time; + sleep_time.tv_sec = 0; + if (remaining_ns < 5000000L) { + sleep_time.tv_nsec = remaining_ns; + } else { + sleep_time.tv_nsec = 5000000; + } + + nanosleep(&sleep_time, nullptr); } - --(*sem)->value; - return 0; + return rc; } +#endif -int PS4_SYSV_ABI posix_sem_post(PthreadSemInternal** sem) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; +int PS4_SYSV_ABI posix_sem_timedwait(sem_t* sem, const timespec* t) { + int result = sem_timedwait(sem, t); + if (result == -1) { + SetPosixErrno(errno); } - if ((*sem)->value == ORBIS_KERNEL_SEM_VALUE_MAX) { - SetPosixErrno(EOVERFLOW); - return -1; - } - ++(*sem)->value; - (*sem)->semaphore.release(); - return 0; + return result; } -int PS4_SYSV_ABI posix_sem_destroy(PthreadSemInternal** sem) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; +int PS4_SYSV_ABI posix_sem_post(sem_t* sem) { + int result = sem_post(sem); + if (result == -1) { + SetPosixErrno(errno); } - delete *sem; - *sem = nullptr; - return 0; + return result; } -int PS4_SYSV_ABI posix_sem_getvalue(PthreadSemInternal** sem, int* sval) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; +int PS4_SYSV_ABI posix_sem_destroy(sem_t* sem) { + int result = sem_destroy(sem); + if (result == -1) { + SetPosixErrno(errno); } - if (sval) { - *sval = (*sem)->value; + return result; +} + +int PS4_SYSV_ABI posix_sem_getvalue(sem_t* sem, int* sval) { + int result = sem_getvalue(sem, sval); + if (result == -1) { + SetPosixErrno(errno); } - return 0; + return result; } int PS4_SYSV_ABI posix_pthread_attr_getstacksize(const pthread_attr_t* attr, size_t* size) { diff --git a/src/core/libraries/kernel/thread_management.h b/src/core/libraries/kernel/thread_management.h index a2b2f6feaf..b90c840938 100644 --- a/src/core/libraries/kernel/thread_management.h +++ b/src/core/libraries/kernel/thread_management.h @@ -112,11 +112,6 @@ struct PthreadRwInternal { std::string name; }; -struct PthreadSemInternal { - std::counting_semaphore semaphore; - std::atomic value; -}; - class PThreadPool { public: ScePthread Create(); diff --git a/src/core/libraries/kernel/win32_signals.cpp b/src/core/libraries/kernel/win32_signals.cpp new file mode 100644 index 0000000000..a8890586c3 --- /dev/null +++ b/src/core/libraries/kernel/win32_signals.cpp @@ -0,0 +1,325 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/libraries/kernel/orbis_signals.h" + +#ifdef _WIN64 +#include "common/assert.h" +#include "common/enum.h" +#include "common/error.h" +#include "common/types.h" +#include "core/libraries/error_codes.h" + +#include "pthread.h" +extern "C" { +// Hack to get winpthreads internal state to get detached thread handle +#include "externals/winpthreads/src/thread.h" +} + +#include + +#include +#include + +#define NTDLL_VERSION_WIN10_RS5_1809 17763 +#define DR7_LAST_BRANCH 0x100 + +enum QUEUE_USER_APC_FLAGS { + QUEUE_USER_APC_FLAGS_NONE = 0x00000000, + QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC = 0x00000001, + QUEUE_USER_APC_CALLBACK_DATA_CONTEXT = 0x00010000, +}; + +using NTSTATUS = LONG; +using PPS_APC_ROUTINE = VOID(NTAPI*)(PVOID, PVOID, PVOID, PCONTEXT); +union USER_APC_OPTION { + ULONG_PTR UserApcFlags; + HANDLE MemoryReserveHandle; +}; + +using PNT_QUEUE_APC_THREAD_EX = NTSTATUS(NTAPI*)(HANDLE, USER_APC_OPTION, PPS_APC_ROUTINE, PVOID, + PVOID, PVOID); + +static PNT_QUEUE_APC_THREAD_EX pfnNtQueueApcThreadEx; + +struct Orbis::sigaction g_sigacts[ORBIS_SIG_MAXSIG32]; + +static FARPROC GetModuleProcAddr(const wchar_t* dll, u16 min_version, const char* proc_name) { + // DWORD ver_handle = 0; + // DWORD ver_size = GetFileVersionInfoSizeW(dll, &ver_handle); + // if (ver_size == 0) { + // UNREACHABLE_MSG("Could not get {} version", dll); + // } + // std::vector ver_buf(ver_size, 0); + // if (GetFileVersionInfoW(dll, ver_handle, ver_size, ver_buf.data()) == FALSE) { + // UNREACHABLE_MSG("Could not get {} version", dll); + // } + + // UINT size = 0; + // LPBYTE buffer = NULL; + // if (VerQueryValueW(ver_buf.data(), L"\\", (VOID FAR * FAR*)&buffer, &size) == FALSE || + // size == 0) { + // UNREACHABLE_MSG("Could not get {} version", dll); + // } + + // const VS_FIXEDFILEINFO* p_ver = (VS_FIXEDFILEINFO*)buffer; + // const u16 version = (p_ver->dwProductVersionLS >> 16 & 0xFFFF); + // ASSERT_MSG(version >= min_version, "Your Windows version is too old, please update."); + + return GetProcAddress(GetModuleHandleW(dll), proc_name); +} + +NTSTATUS NtQueueApcThreadEx(HANDLE ThreadHandle, USER_APC_OPTION UserApcOption, + PPS_APC_ROUTINE ApcRoutine, PVOID arg1, PVOID arg2, PVOID arg3) { + LOG_INFO(Lib_Kernel, "called"); + static const auto pfn = reinterpret_cast( + GetModuleProcAddr(L"ntdll.dll", NTDLL_VERSION_WIN10_RS5_1809, "NtQueueApcThreadEx")); + return pfn(ThreadHandle, UserApcOption, ApcRoutine, arg1, arg2, arg3); +} + +namespace Orbis { + +static uintptr_t SigDfl(int sig) { + switch (sig) { + case ORBIS_SIGURG: + case ORBIS_SIGCHLD: + case ORBIS_SIGWINCH: + case ORBIS_SIGINFO: + return ORBIS_SIG_IGN; + default: + return ORBIS_SIG_ERR; + } +} + +static uintptr_t SigActType(s32 sig, const struct sigaction& act) { + switch (reinterpret_cast(act.sa_sigaction)) { + case ORBIS_SIG_DFL: + return SigDfl(sig); + case ORBIS_SIG_IGN: + return ORBIS_SIG_IGN; + case ORBIS_SIG_ERR: + return ORBIS_SIG_ERR; + case ORBIS_SIG_CATCH: + return ORBIS_SIG_ERR; + case ORBIS_SIG_HOLD: + return ORBIS_SIG_ERR; + default: + return ORBIS_SIG_DFL; + } +} + +s32 sigaction(s32 sig, const struct sigaction* act, struct sigaction* oldact) { + LOG_INFO(Lib_Kernel, "called. sig = {}", sig); + if (sig > ORBIS_SIG_MAXSIG32) { + return POSIX_EINVAL; + } + + struct sigaction tmp = {}; + if (act != nullptr) { + tmp = *act; + } + + switch (sig) { + case ORBIS_SIGKILL: + case ORBIS_SIGSTOP: { + if (tmp.sa_sigaction != ORBIS_SIG_DFL) { + return POSIX_EINVAL; + } + } + default: + break; + } + + auto& info = g_sigacts[ORBIS_SIG_IDX(sig)]; + if (oldact != nullptr) { + *oldact = info; + } + info = tmp; + + return 0; +} + +#pragma pack(push, 1) +struct YMMCONTEXT { + M128A Ymm0; + M128A Ymm1; + M128A Ymm2; + M128A Ymm3; + M128A Ymm4; + M128A Ymm5; + M128A Ymm6; + M128A Ymm7; + M128A Ymm8; + M128A Ymm9; + M128A Ymm10; + M128A Ymm11; + M128A Ymm12; + M128A Ymm13; + M128A Ymm14; + M128A Ymm15; +}; + +struct XSTATE { + DWORD64 Mask; + DWORD64 CompactionMask; + DWORD64 Reserved[6]; + YMMCONTEXT YmmContext; +}; +using PXSTATE = XSTATE*; +#pragma pack(pop) + +// Taken from fpPS4 +ucontext_t UContextFromWin64(CONTEXT& context) { + ucontext_t ucontext{}; + + const u32 flags = context.ContextFlags & (~CONTEXT_AMD64); + if ((flags & CONTEXT_INTEGER) != 0) { + ucontext.uc_flags |= ORBIS_UC_CPU; + ucontext.uc_mcontext.mc_rax = context.Rax; + ucontext.uc_mcontext.mc_rbx = context.Rbx; + ucontext.uc_mcontext.mc_rcx = context.Rcx; + ucontext.uc_mcontext.mc_rdx = context.Rdx; + ucontext.uc_mcontext.mc_rsi = context.Rsi; + ucontext.uc_mcontext.mc_rdi = context.Rdi; + ucontext.uc_mcontext.mc_r8 = context.R8; + ucontext.uc_mcontext.mc_r9 = context.R9; + ucontext.uc_mcontext.mc_r10 = context.R10; + ucontext.uc_mcontext.mc_r11 = context.R11; + ucontext.uc_mcontext.mc_r12 = context.R12; + ucontext.uc_mcontext.mc_r13 = context.R13; + ucontext.uc_mcontext.mc_r14 = context.R14; + ucontext.uc_mcontext.mc_r15 = context.R15; + } + if ((flags & CONTEXT_CONTROL) != 0) { + ucontext.uc_flags |= ORBIS_UC_CPU; + ucontext.uc_mcontext.mc_rsp = context.Rsp; + ucontext.uc_mcontext.mc_rbp = context.Rbp; + ucontext.uc_mcontext.mc_rip = context.Rip; + ucontext.uc_mcontext.mc_rflags = context.EFlags; + ucontext.uc_mcontext.mc_cs = context.SegCs; + ucontext.uc_mcontext.mc_ss = context.SegSs; + } + if ((flags & CONTEXT_SEGMENTS) != 0) { + ucontext.uc_flags |= ORBIS_UC_CPU; + ucontext.uc_mcontext.mc_ds = context.SegDs; + ucontext.uc_mcontext.mc_es = context.SegEs; + ucontext.uc_mcontext.mc_fs = context.SegFs; + ucontext.uc_mcontext.mc_gs = context.SegGs; + } + + auto uc_xsave = reinterpret_cast(&ucontext.uc_mcontext.mc_fpstate[0]); + auto uc_xstate = reinterpret_cast(uc_xsave + 1); + if ((flags & CONTEXT_FLOATING_POINT) != 0) { + ucontext.uc_flags |= ORBIS_UC_FPU; + *uc_xsave = context.FltSave; + uc_xstate->Mask = uc_xstate->Mask | XSTATE_MASK_LEGACY; + } + if ((flags & CONTEXT_XSTATE) != 0) { + DWORD64 xs_mask{}; + ASSERT(GetXStateFeaturesMask(&context, &xs_mask) == TRUE); + if ((xs_mask & XSTATE_MASK_AVX) != 0) { + DWORD length; + auto xs_ymm = + reinterpret_cast(LocateXStateFeature(&context, XSTATE_AVX, &length)); + ASSERT(length >= sizeof(YMMCONTEXT)); + uc_xstate->YmmContext = *xs_ymm; + } + } + if ((flags & CONTEXT_DEBUG_REGISTERS) != 0) { + ucontext.uc_mcontext.mc_spare[0] = context.Dr0; + ucontext.uc_mcontext.mc_spare[1] = context.Dr1; + ucontext.uc_mcontext.mc_spare[2] = context.Dr2; + ucontext.uc_mcontext.mc_spare[3] = context.Dr3; + ucontext.uc_mcontext.mc_spare[4] = context.Dr6; + ucontext.uc_mcontext.mc_spare[5] = context.Dr7; + } + + // fix me + if (context.Dr7 & DR7_LAST_BRANCH) { + ucontext.uc_mcontext.mc_lbrfrom = context.LastBranchFromRip; + ucontext.uc_mcontext.mc_lbrto = context.LastBranchToRip; + } else { + ucontext.uc_mcontext.mc_lbrfrom = 0; // context.Rsp; + ucontext.uc_mcontext.mc_lbrto = 0; // context.Rbp; + } + + ucontext.uc_flags |= (flags << 8); // set as extended + + ucontext.uc_mcontext.mc_addr = ucontext.uc_mcontext.mc_rip; + ucontext.uc_mcontext.mc_len = sizeof(mcontext_t); + ucontext.uc_mcontext.mc_flags = ORBIS_MC_HASSEGS; + ucontext.uc_mcontext.mc_fpformat = ORBIS_MC_FPFMT_XMM; + ucontext.uc_mcontext.mc_ownedfp = ORBIS_MC_FPOWNED_FPU; + + return ucontext; +} + +static thread_local sigset_t g_sc_mask; + +VOID NTAPI ThreadApcProc(PVOID arg1, PVOID arg2, PVOID arg3, PCONTEXT context) { + int sig = static_cast(reinterpret_cast(arg1)); + LOG_INFO(Lib_Kernel, "called. sig = {}", sig); + const auto sact = g_sigacts[ORBIS_SIG_IDX(sig)]; + if (sact.sa_flags & ORBIS_SA_RESETHAND) { + g_sigacts[ORBIS_SIG_IDX(sig)] = {}; + } + switch (SigActType(sig, sact)) { + case ORBIS_SIG_IGN: + case ORBIS_SIG_ERR: + return; + } + ucontext_t ucontext = UContextFromWin64(*context); + ucontext.uc_mcontext.mc_err = errno; + + sigset_t save = g_sc_mask; + if (sact.sa_flags & ORBIS_SA_NODEFER) { + ucontext.sc_mask = sact.sa_mask; + } else { + g_sc_mask.__bits[0] |= sact.sa_mask.__bits[0]; + g_sc_mask.__bits[1] |= sact.sa_mask.__bits[1]; + g_sc_mask.__bits[2] |= sact.sa_mask.__bits[2]; + g_sc_mask.__bits[3] |= sact.sa_mask.__bits[3]; + ucontext.sc_mask = g_sc_mask; + } + + if (sact.sa_flags & ORBIS_SA_SIGINFO) { + siginfo_t info = { + .si_signo = sig, + .si_code = ORBIS_SI_USER, + .si_pid = GetCurrentProcessId(), + }; + sact.sa_sigaction(sig, &info, &ucontext); + } else { + sact.sa_handler(sig, ORBIS_SI_USER, &ucontext); + } + g_sc_mask = save; +} + +s32 pthread_kill(pthread_t thread, s32 sig) { + int result = 0; + LOG_INFO(Lib_Kernel, "called. sig = {}", sig); + if (sig == 0) { + return result; + } + const auto tv = __pth_gpointer_locked(thread); + const auto handle = OpenThread(THREAD_SET_CONTEXT, FALSE, tv->tid); + USER_APC_OPTION option; + option.UserApcFlags = QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC; + + if (NtQueueApcThreadEx(handle, option, &ThreadApcProc, + reinterpret_cast(static_cast(sig)), nullptr, + nullptr) != 0) { + const auto error = GetLastError(); + LOG_ERROR(Lib_Kernel, "NtQueueApcThreadEx failed. error = {}: {}", error, + Common::NativeErrorToString(error)); + // TODO: error conversion + // result = ??? + } else if (tv->evStart != nullptr) { + SetEvent(tv->evStart); + } + CloseHandle(handle); + return result; +} + +} // namespace Orbis +#endif