diff --git a/src/core/arm/nce/arm_nce.cpp b/src/core/arm/nce/arm_nce.cpp index 66213a7d16..1bff53a909 100644 --- a/src/core/arm/nce/arm_nce.cpp +++ b/src/core/arm/nce/arm_nce.cpp @@ -25,7 +25,6 @@ #define YUZU_NAKED_END #endif -#include #include #include "common/signal_chain.h" @@ -52,26 +51,18 @@ struct sigaction g_orig_bus_action; struct sigaction g_orig_segv_action; #endif -using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters; - using namespace Common::Literals; constexpr u32 StackSize = 128_KiB; } // namespace YUZU_ALWAYS_INLINE -void* ArmNce::GetGuestParameters() { - void* nep; /* NativeExecutionParameters* */ +NativeExecutionParameters* ArmNce::GetGuestParameters() { + NativeExecutionParameters* nep = nullptr; #if defined(__APPLE__) - // https://github.com/apple-oss-distributions/xnu/blob/f6217f891ac0bb64f3d375211650a4c1ff8ca1ea/libsyscall/os/tsd.h#L156-L189 - asm volatile( - "mrs %[out], TPIDRRO_EL0\n" // load pthreads TLS storage - "ldr %[out], [ %[out], #%[off] ]\n" // accessed like an array, so i * sizeof(u64) - : [out] "=&r"(nep) - : [off] "i"((ContextKey - 1) * 8) - : "memory"); + nep = static_cast(pthread_getspecific(ContextKey)); #elif defined(_WIN32) - nep = TlsGetValue(ContextKey); + nep = static_cast(TlsGetValue(ContextKey)); #elif defined(__linux__) asm volatile( "mrs %0, TPIDR_EL0\n" @@ -81,9 +72,7 @@ void* ArmNce::GetGuestParameters() { } YUZU_ALWAYS_INLINE -void ArmNce::LockThreadParameters(void* tpidr) { - auto* nep = static_cast(tpidr); - +void ArmNce::LockThreadParameters(NativeExecutionParameters* nep) { u32 value; do { do { @@ -95,28 +84,27 @@ void ArmNce::LockThreadParameters(void* tpidr) { } YUZU_ALWAYS_INLINE -void ArmNce::UnlockThreadParameters(void* tpidr) { - static_cast(tpidr)->lock.store(SpinLockUnlocked, std::memory_order_release); +void ArmNce::UnlockThreadParameters(NativeExecutionParameters* nep) { + nep->lock.store(SpinLockUnlocked, std::memory_order_release); } #ifndef _WIN32 YUZU_NAKED YUZU_NO_INLINE -HaltReason ArmNce::ReturnToRunCodeByExceptionLevelChange(int tid, void *tpidr) { +HaltReason ArmNce::ReturnToRunCodeByExceptionLevelChange(int tid, NativeExecutionParameters* tpidr) { // x0 - tid // x1 - tpidr - // x9 - NativeExecutionParameters* + // x9 - NativeExecutionParameters* (on Linux) // // uses tkill on linux and pthread_kill on macOS, both have the same signature: /* syscall (u32 tid, u64 signal) */ // tid is already in x0 so we don't have to explicitly pass it asm volatile( - "mov x9, x1\n" // move tpidr to x9 so it doesn't get clobbered - "mov x1, #%[sig]\n" // set x1 to SIGUSR2 #if defined(__linux__) + "mov x9, x1\n" // move tpidr to x9 so it doesn't get clobbered "mov x8, %[syscall]\n" "svc #0\n" "brk 0x0\n" @@ -136,36 +124,36 @@ HaltReason ArmNce::ReturnToRunCodeByExceptionLevelChange(int tid, void *tpidr) { } YUZU_NAKED_END #else -HaltReason ArmNce::ReturnToRunCodeByExceptionLevelChange(void* tid, void *tpidr) { +HaltReason ArmNce::ReturnToRunCodeByExceptionLevelChange(void* tid, NativeExecutionParameters *tpidr) { RaiseException(ExceptionLevelChangeSignal, 0, 0, nullptr); // TODO: pass tpidr through arguments? __builtin_unreachable(); } #endif void ArmNce::ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void *info, void *raw_context) { - auto tpidr = static_cast(RestoreGuestContext(raw_context)); + NativeExecutionParameters* nep = RestoreGuestContext(raw_context); #if !defined(__APPLE__) && !defined(_WIN32) // Save old value of TPIDR_EL0, load guest one - u64 tpidr_el0; + u64 tpidr; asm volatile("mrs %0, TPIDR_EL0\n" "msr TPIDR_EL0, %1\n" - : "=r"(tpidr_el0) - : "r"(tpidr)); - tpidr->tpidr_el0 = tpidr_el0; + : "=r"(tpidr) + : "r"(nep)); + nep->tpidr_el0 = tpidr; #else - tpidr->is_actually_running = true; + nep->is_actually_running = true; #endif - UnlockThreadParameters(tpidr); + UnlockThreadParameters(nep); // sigaction restores context and returns to guest } YUZU_NAKED YUZU_NO_INLINE -HaltReason ArmNce::ReturnToRunCodeByTrampoline(void *tpidr, u64 trampoline_addr) { +HaltReason ArmNce::ReturnToRunCodeByTrampoline(NativeExecutionParameters* nep, u64 trampoline_addr) { // x0 - NativeExecutionParameters* - // x1 - addr + // x1 - trampoline_addr // x2 - GuestContext* // x3 - Host SP @@ -226,19 +214,19 @@ static_assert(offsetof(HostContext, host_sp) == 0xE0); // TODO: don't use magic #ifndef _WIN32 void ArmNce::BreakFromRunCodeSignalHandler(int sig, void *info, void *raw_context) { - NativeExecutionParameters* tpidr = static_cast(GetGuestParameters()); + NativeExecutionParameters* tpidr = GetGuestParameters(); #if defined(__APPLE__) || defined(_WIN32) if (tpidr->is_actually_running) { tpidr->is_actually_running = false; #else if (tpidr->magic == Common::MakeMagic('Y', 'U', 'Z', 'U')) { // Load the host's TPIDR_EL0 value - void* host_tpidr = reinterpret_cast(&tpidr->native_context)->host_ctx.host_tpidr_el0; + void* host_tpidr = tpidr->native_context->host_ctx.host_tpidr_el0; asm volatile( "msr TPIDR_EL0, %[host_tpidr]\n" :: [host_tpidr] "r"(host_tpidr)); #endif - SaveGuestContext(static_cast(tpidr->native_context), raw_context); + SaveGuestContext(tpidr->native_context, raw_context); // SaveGuestContext loads host context, returning from here will enter host code. } } @@ -246,7 +234,7 @@ void ArmNce::BreakFromRunCodeSignalHandler(int sig, void *info, void *raw_contex #endif void ArmNce::GuestMemoryFaultSignalHandler(int sig, void* raw_info, void* raw_context) { - NativeExecutionParameters* nep = static_cast(GetGuestParameters()); + NativeExecutionParameters* nep = GetGuestParameters(); #if defined(__APPLE__) || defined(_WIN32) if (nep->is_actually_running) { @@ -254,13 +242,13 @@ void ArmNce::GuestMemoryFaultSignalHandler(int sig, void* raw_info, void* raw_co #else if (nep->magic == Common::MakeMagic('Y', 'U', 'Z', 'U')) { // Load the host's TPIDR_EL0 value - void* host_tpidr = reinterpret_cast(&nep->native_context)->host_ctx.host_tpidr_el0; + void* host_tpidr = nep->native_context->host_ctx.host_tpidr_el0; asm volatile( "msr TPIDR_EL0, %[host_tpidr]\n" :: [host_tpidr] "r"(host_tpidr)); #endif - auto* guest_ctx = static_cast(nep->native_context); + auto* guest_ctx = nep->native_context; auto& memory = guest_ctx->parent->m_running_thread->GetOwnerProcess()->GetMemory(); #ifndef _WIN32 @@ -331,17 +319,18 @@ void ArmNce::GuestMemoryFaultSignalHandler(int sig, void* raw_info, void* raw_co #endif } -void* ArmNce::RestoreGuestContext(void* raw_context) { +NativeExecutionParameters* ArmNce::RestoreGuestContext(void* raw_context) { // Retrieve the host context. auto host_ctx = KernelContext(raw_context); #ifdef __linux__ // Thread-local parameters will be located in x9. - auto* tpidr = reinterpret_cast(host_ctx.regs()[9]); + auto* nep = reinterpret_cast(host_ctx.regs()[9]); #else - auto* tpidr = static_cast(GetGuestParameters()); + auto* nep = GetGuestParameters(); #endif - auto* guest_ctx = static_cast(tpidr->native_context); + + auto* guest_ctx = nep->native_context; // Save host callee-saved registers. std::memcpy(guest_ctx->host_ctx.host_saved_vregs.data(), &host_ctx.vregs()[8], @@ -361,8 +350,18 @@ void* ArmNce::RestoreGuestContext(void* raw_context) { std::memcpy(host_ctx.regs(), guest_ctx->cpu_registers.data(), sizeof(guest_ctx->cpu_registers)); std::memcpy(host_ctx.vregs(), guest_ctx->vector_registers.data(), sizeof(guest_ctx->vector_registers)); +#ifdef _WIN32 + auto tib = (PNT_TIB)NtCurrentTeb(); + // Save host stack base/limit + nep->host_stack_base = tib->StackBase; + nep->host_stack_limit = tib->StackLimit; + // Set guest stack base/limit for CET + tib->StackBase = nep->guest_stack_base; + tib->StartLimit = nep->guest_stack_limit; +#endif + // Return the new thread-local storage pointer. - return tpidr; + return nep; } void ArmNce::SaveGuestContext(GuestContext* guest_ctx, void* raw_context) { @@ -381,6 +380,13 @@ void ArmNce::SaveGuestContext(GuestContext* guest_ctx, void* raw_context) { // Restore stack pointer. *host_ctx.sp() = guest_ctx->host_ctx.host_sp; +#ifdef _WIN32 + // Restore stack base/limit for CET + auto tib = (PNT_TIB)NtCurrentTeb(); + tib->StackBase = nep->host_stack_base; + tib->StartLimit = nep->host_stack_limit; +#endif + // Restore host callee-saved registers. std::memcpy(&host_ctx.regs()[19], guest_ctx->host_ctx.host_saved_regs.data(), sizeof(guest_ctx->host_ctx.host_saved_regs)); @@ -456,6 +462,11 @@ HaltReason ArmNce::RunThread(Kernel::KThread* thread) { ASSERT(pthread_setspecific(ContextKey, thread_params) == 0); #elif defined(_WIN32) ASSERT_MSG(TlsSetValue(ContextKey, thread_params), "Failed to set TLS value: id {}, error {}", ContextKey, GetLastError()); + ASSERT_MSG(TlsSetValue(NCEStorage, thread_params->native_context->cpu_registers[18]), + "Failed to set TLS value: id {}, error {}", ContextKey, GetLastError()); + + thread_params->guest_stack_base = thread->GetOwnerProcess()->GetPageTable().GetStackRegionStart() + thread->GetOwnerProcess()->GetPageTable().GetStackRegionSize(); + thread_params->guest_stack_limit = thread->GetOwnerProcess()->GetPageTable().GetStackRegionStart(); #endif // Move non-critical operations outside the locked section @@ -529,6 +540,8 @@ ArmNce::~ArmNce() = default; #ifdef _WIN32 LONG WINAPI ArmNce::VectoredExceptionHandler(PEXCEPTION_POINTERS info) { + // TODO: Windows doesn't allocate a separate stack so either on the guest + // or current host stack, is that okay? DWORD code = info->ExceptionRecord->ExceptionCode; if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_DATATYPE_MISALIGNMENT) { diff --git a/src/core/arm/nce/arm_nce.h b/src/core/arm/nce/arm_nce.h index f7d546a59f..97137d375a 100644 --- a/src/core/arm/nce/arm_nce.h +++ b/src/core/arm/nce/arm_nce.h @@ -43,6 +43,28 @@ constexpr u64 TlsSlots = offsetof(TEB, TlsSlots); #endif +struct NativeExecutionParameters { + +#if (defined(__APPLE__) || defined(_WIN32)) && HAS_NCE + // Are we in actual guest code? + bool is_actually_running{}; +#endif + // Are we in any stage of performing guest operations? + bool is_running{}; + u32 magic{Common::MakeMagic('Y', 'U', 'Z', 'U')}; + std::atomic lock{1}; + u64 tpidr_el0{}; + u64 tpidrro_el0{}; + GuestContext* native_context{}; + +#ifdef _WIN32 + u64 guest_stack_base; + u64 guest_stack_limit; + u64 host_stack_base; + u64 host_stack_limit; +#endif +}; + class ArmNce final : public ArmInterface { public: ArmNce(System& system, bool uses_wall_clock, std::size_t core_index); @@ -81,11 +103,11 @@ protected: private: // Only confirmed to be valid on Apple systems. - static void* GetGuestParameters(); + static NativeExecutionParameters* GetGuestParameters(); - static HaltReason ReturnToRunCodeByTrampoline(void* tpidr, u64 trampoline_addr); + static HaltReason ReturnToRunCodeByTrampoline(NativeExecutionParameters* tpidr, u64 trampoline_addr); #ifndef _WIN32 - static HaltReason ReturnToRunCodeByExceptionLevelChange(int tid, void* tpidr); + static HaltReason ReturnToRunCodeByExceptionLevelChange(int tid, NativeExecutionParameters* tpidr); #else static HaltReason ReturnToRunCodeByExceptionLevelChange(void* tid, void* tpidr); static LONG VectoredExceptionHandler(PEXCEPTION_POINTERS info); @@ -97,10 +119,10 @@ private: static void GuestMemoryFaultSignalHandler(int sig, void* info, void* raw_context); static bool HandleFailedGuestFault(GuestContext* ctx, void* info, void* raw_context); - static void LockThreadParameters(void* tpidr); - static void UnlockThreadParameters(void* tpidr); + static void LockThreadParameters(NativeExecutionParameters* tpidr); + static void UnlockThreadParameters(NativeExecutionParameters* tpidr); - static void* RestoreGuestContext(void* raw_context); + static NativeExecutionParameters* RestoreGuestContext(void* raw_context); static void SaveGuestContext(GuestContext* ctx, void* raw_context); public: diff --git a/src/core/arm/nce/patcher.cpp b/src/core/arm/nce/patcher.cpp index f4362fae44..ddccd2f95c 100644 --- a/src/core/arm/nce/patcher.cpp +++ b/src/core/arm/nce/patcher.cpp @@ -27,8 +27,6 @@ namespace Core::NCE { using namespace Common::Literals; using namespace oaknut::util; -using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters; - constexpr size_t MaxRelativeBranch = 128_MiB; constexpr u32 ModuleCodeIndex = 0x24 / sizeof(u32); diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index 148fb14e81..66469813be 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -33,6 +33,10 @@ #include "core/hle/kernel/svc_types.h" #include "core/hle/result.h" +#ifdef HAS_NCE +#include "core/arm/nce/arm_nce.h" +#endif + namespace Common { class Fiber; } @@ -665,25 +669,11 @@ public: return m_stack_top; } -public: - // TODO: This shouldn't be defined in kernel namespace - struct NativeExecutionParameters { -#if (defined(__APPLE__) || defined(_WIN32)) && HAS_NCE - // Are we in actual guest code? - bool is_actually_running{}; -#endif - // Are we in any stage of performing guest operations? - bool is_running{}; - u32 magic{Common::MakeMagic('Y', 'U', 'Z', 'U')}; - std::atomic lock{1}; - u64 tpidr_el0{}; - u64 tpidrro_el0{}; - void* native_context{}; - }; - - NativeExecutionParameters& GetNativeExecutionParameters() { +#ifdef HAS_NCE + Core::NativeExecutionParameters& GetNativeExecutionParameters() { return m_native_execution_parameters; } +#endif private: KThread* RemoveWaiterByKey(KernelCore& kernel, bool* out_has_waiters, KProcessAddress key, bool is_kernel_address_key); @@ -941,7 +931,9 @@ private: ThreadWaitReasonForDebugging m_wait_reason_for_debugging{}; uintptr_t m_argument{}; KProcessAddress m_stack_top{}; - NativeExecutionParameters m_native_execution_parameters{}; +#ifdef HAS_NCE + Core::NativeExecutionParameters m_native_execution_parameters{}; +#endif public: using ConditionVariableThreadTreeType = ConditionVariableThreadTree;