This commit is contained in:
Vladislav Mikhalin 2024-08-29 20:47:38 +03:00
parent 749fe92882
commit 522a74ee7e
8 changed files with 667 additions and 87 deletions

View File

@ -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

View File

@ -13,6 +13,9 @@
#include <mutex> // 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) {

View File

@ -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);

View File

@ -1,10 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <chrono>
#include <thread>
#include <boost/asio/io_context.hpp>
#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 <boost/asio/io_context.hpp>
#include <atomic>
#include <chrono>
#include <thread>
#ifdef _WIN64
#include <io.h>
#include <objbase.h>
@ -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<VAddr>(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<ExceptionHandler> 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);

View File

@ -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 <cstdint>
#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

View File

@ -1,10 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <mutex>
#include <semaphore>
#include <thread>
#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 <semaphore.h>
#include <mutex>
#include <thread>
#ifdef _WIN64
#include <windows.h>
#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;
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);
}
if (sem != nullptr) {
*sem = new PthreadSemInternal{
.semaphore = std::counting_semaphore<ORBIS_KERNEL_SEM_VALUE_MAX>{value},
.value = {static_cast<int>(value)},
};
}
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<s64>(abstime->tv_sec) - static_cast<s64>(curr_time.tv_sec)) * 1000000000L;
remaining_ns += static_cast<s64>(abstime->tv_nsec) - static_cast<s64>(curr_time.tv_nsec);
const system_clock::time_point time{
duration_cast<system_clock::duration>(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_timedwait(sem_t* sem, const timespec* t) {
int result = sem_timedwait(sem, t);
if (result == -1) {
SetPosixErrno(errno);
}
return result;
}
int PS4_SYSV_ABI posix_sem_post(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);
}
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_destroy(sem_t* sem) {
int result = sem_destroy(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_getvalue(sem_t* sem, int* sval) {
int result = sem_getvalue(sem, sval);
if (result == -1) {
SetPosixErrno(errno);
}
if (sval) {
*sval = (*sem)->value;
}
return 0;
return result;
}
int PS4_SYSV_ABI posix_pthread_attr_getstacksize(const pthread_attr_t* attr, size_t* size) {

View File

@ -112,11 +112,6 @@ struct PthreadRwInternal {
std::string name;
};
struct PthreadSemInternal {
std::counting_semaphore<ORBIS_KERNEL_SEM_VALUE_MAX> semaphore;
std::atomic<s32> value;
};
class PThreadPool {
public:
ScePthread Create();

View File

@ -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 <Windows.h>
#include <atomic>
#include <vector>
#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<s8> 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<PNT_QUEUE_APC_THREAD_EX>(
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<uintptr_t>(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<PXMM_SAVE_AREA32>(&ucontext.uc_mcontext.mc_fpstate[0]);
auto uc_xstate = reinterpret_cast<PXSTATE>(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<YMMCONTEXT*>(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<int>(reinterpret_cast<intptr_t>(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<PVOID>(static_cast<intptr_t>(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