Browse Source

[nce] set stack region in NT_TIB

remotes/1785372757367212240/tmp_refs/heads/variable-page-size
Exverge 4 weeks ago
parent
commit
b03ea9bd6e
No known key found for this signature in database GPG Key ID: DAD399BCC5FB77E4
  1. 99
      src/core/arm/nce/arm_nce.cpp
  2. 34
      src/core/arm/nce/arm_nce.h
  3. 2
      src/core/arm/nce/patcher.cpp
  4. 28
      src/core/hle/kernel/k_thread.h

99
src/core/arm/nce/arm_nce.cpp

@ -25,7 +25,6 @@
#define YUZU_NAKED_END #define YUZU_NAKED_END
#endif #endif
#include <cinttypes>
#include <memory> #include <memory>
#include "common/signal_chain.h" #include "common/signal_chain.h"
@ -52,26 +51,18 @@ struct sigaction g_orig_bus_action;
struct sigaction g_orig_segv_action; struct sigaction g_orig_segv_action;
#endif #endif
using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters;
using namespace Common::Literals; using namespace Common::Literals;
constexpr u32 StackSize = 128_KiB; constexpr u32 StackSize = 128_KiB;
} // namespace } // namespace
YUZU_ALWAYS_INLINE YUZU_ALWAYS_INLINE
void* ArmNce::GetGuestParameters() {
void* nep; /* NativeExecutionParameters* */
NativeExecutionParameters* ArmNce::GetGuestParameters() {
NativeExecutionParameters* nep = nullptr;
#if defined(__APPLE__) #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<NativeExecutionParameters*>(pthread_getspecific(ContextKey));
#elif defined(_WIN32) #elif defined(_WIN32)
nep = TlsGetValue(ContextKey);
nep = static_cast<NativeExecutionParameters*>(TlsGetValue(ContextKey));
#elif defined(__linux__) #elif defined(__linux__)
asm volatile( asm volatile(
"mrs %0, TPIDR_EL0\n" "mrs %0, TPIDR_EL0\n"
@ -81,9 +72,7 @@ void* ArmNce::GetGuestParameters() {
} }
YUZU_ALWAYS_INLINE YUZU_ALWAYS_INLINE
void ArmNce::LockThreadParameters(void* tpidr) {
auto* nep = static_cast<NativeExecutionParameters*>(tpidr);
void ArmNce::LockThreadParameters(NativeExecutionParameters* nep) {
u32 value; u32 value;
do { do {
do { do {
@ -95,28 +84,27 @@ void ArmNce::LockThreadParameters(void* tpidr) {
} }
YUZU_ALWAYS_INLINE YUZU_ALWAYS_INLINE
void ArmNce::UnlockThreadParameters(void* tpidr) {
static_cast<NativeExecutionParameters*>(tpidr)->lock.store(SpinLockUnlocked, std::memory_order_release);
void ArmNce::UnlockThreadParameters(NativeExecutionParameters* nep) {
nep->lock.store(SpinLockUnlocked, std::memory_order_release);
} }
#ifndef _WIN32 #ifndef _WIN32
YUZU_NAKED YUZU_NAKED
YUZU_NO_INLINE YUZU_NO_INLINE
HaltReason ArmNce::ReturnToRunCodeByExceptionLevelChange(int tid, void *tpidr) {
HaltReason ArmNce::ReturnToRunCodeByExceptionLevelChange(int tid, NativeExecutionParameters* tpidr) {
// x0 - tid // x0 - tid
// x1 - tpidr // x1 - tpidr
// x9 - NativeExecutionParameters*
// x9 - NativeExecutionParameters* (on Linux)
// //
// uses tkill on linux and pthread_kill on macOS, both have the same signature: // uses tkill on linux and pthread_kill on macOS, both have the same signature:
/* syscall (u32 tid, u64 signal) */ /* syscall (u32 tid, u64 signal) */
// tid is already in x0 so we don't have to explicitly pass it // tid is already in x0 so we don't have to explicitly pass it
asm volatile( asm volatile(
"mov x9, x1\n" // move tpidr to x9 so it doesn't get clobbered
"mov x1, #%[sig]\n" // set x1 to SIGUSR2 "mov x1, #%[sig]\n" // set x1 to SIGUSR2
#if defined(__linux__) #if defined(__linux__)
"mov x9, x1\n" // move tpidr to x9 so it doesn't get clobbered
"mov x8, %[syscall]\n" "mov x8, %[syscall]\n"
"svc #0\n" "svc #0\n"
"brk 0x0\n" "brk 0x0\n"
@ -136,36 +124,36 @@ HaltReason ArmNce::ReturnToRunCodeByExceptionLevelChange(int tid, void *tpidr) {
} }
YUZU_NAKED_END YUZU_NAKED_END
#else #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? RaiseException(ExceptionLevelChangeSignal, 0, 0, nullptr); // TODO: pass tpidr through arguments?
__builtin_unreachable(); __builtin_unreachable();
} }
#endif #endif
void ArmNce::ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void *info, void *raw_context) { void ArmNce::ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void *info, void *raw_context) {
auto tpidr = static_cast<NativeExecutionParameters*>(RestoreGuestContext(raw_context));
NativeExecutionParameters* nep = RestoreGuestContext(raw_context);
#if !defined(__APPLE__) && !defined(_WIN32) #if !defined(__APPLE__) && !defined(_WIN32)
// Save old value of TPIDR_EL0, load guest one // Save old value of TPIDR_EL0, load guest one
u64 tpidr_el0;
u64 tpidr;
asm volatile("mrs %0, TPIDR_EL0\n" asm volatile("mrs %0, TPIDR_EL0\n"
"msr TPIDR_EL0, %1\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 #else
tpidr->is_actually_running = true;
nep->is_actually_running = true;
#endif #endif
UnlockThreadParameters(tpidr);
UnlockThreadParameters(nep);
// sigaction restores context and returns to guest // sigaction restores context and returns to guest
} }
YUZU_NAKED YUZU_NAKED
YUZU_NO_INLINE YUZU_NO_INLINE
HaltReason ArmNce::ReturnToRunCodeByTrampoline(void *tpidr, u64 trampoline_addr) {
HaltReason ArmNce::ReturnToRunCodeByTrampoline(NativeExecutionParameters* nep, u64 trampoline_addr) {
// x0 - NativeExecutionParameters* // x0 - NativeExecutionParameters*
// x1 - addr
// x1 - trampoline_addr
// x2 - GuestContext* // x2 - GuestContext*
// x3 - Host SP // x3 - Host SP
@ -226,19 +214,19 @@ static_assert(offsetof(HostContext, host_sp) == 0xE0); // TODO: don't use magic
#ifndef _WIN32 #ifndef _WIN32
void ArmNce::BreakFromRunCodeSignalHandler(int sig, void *info, void *raw_context) { void ArmNce::BreakFromRunCodeSignalHandler(int sig, void *info, void *raw_context) {
NativeExecutionParameters* tpidr = static_cast<NativeExecutionParameters *>(GetGuestParameters());
NativeExecutionParameters* tpidr = GetGuestParameters();
#if defined(__APPLE__) || defined(_WIN32) #if defined(__APPLE__) || defined(_WIN32)
if (tpidr->is_actually_running) { if (tpidr->is_actually_running) {
tpidr->is_actually_running = false; tpidr->is_actually_running = false;
#else #else
if (tpidr->magic == Common::MakeMagic('Y', 'U', 'Z', 'U')) { if (tpidr->magic == Common::MakeMagic('Y', 'U', 'Z', 'U')) {
// Load the host's TPIDR_EL0 value // Load the host's TPIDR_EL0 value
void* host_tpidr = reinterpret_cast<GuestContext*>(&tpidr->native_context)->host_ctx.host_tpidr_el0;
void* host_tpidr = tpidr->native_context->host_ctx.host_tpidr_el0;
asm volatile( asm volatile(
"msr TPIDR_EL0, %[host_tpidr]\n" "msr TPIDR_EL0, %[host_tpidr]\n"
:: [host_tpidr] "r"(host_tpidr)); :: [host_tpidr] "r"(host_tpidr));
#endif #endif
SaveGuestContext(static_cast<GuestContext *>(tpidr->native_context), raw_context);
SaveGuestContext(tpidr->native_context, raw_context);
// SaveGuestContext loads host context, returning from here will enter host code. // 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 #endif
void ArmNce::GuestMemoryFaultSignalHandler(int sig, void* raw_info, void* raw_context) { void ArmNce::GuestMemoryFaultSignalHandler(int sig, void* raw_info, void* raw_context) {
NativeExecutionParameters* nep = static_cast<NativeExecutionParameters*>(GetGuestParameters());
NativeExecutionParameters* nep = GetGuestParameters();
#if defined(__APPLE__) || defined(_WIN32) #if defined(__APPLE__) || defined(_WIN32)
if (nep->is_actually_running) { if (nep->is_actually_running) {
@ -254,13 +242,13 @@ void ArmNce::GuestMemoryFaultSignalHandler(int sig, void* raw_info, void* raw_co
#else #else
if (nep->magic == Common::MakeMagic('Y', 'U', 'Z', 'U')) { if (nep->magic == Common::MakeMagic('Y', 'U', 'Z', 'U')) {
// Load the host's TPIDR_EL0 value // Load the host's TPIDR_EL0 value
void* host_tpidr = reinterpret_cast<GuestContext*>(&nep->native_context)->host_ctx.host_tpidr_el0;
void* host_tpidr = nep->native_context->host_ctx.host_tpidr_el0;
asm volatile( asm volatile(
"msr TPIDR_EL0, %[host_tpidr]\n" "msr TPIDR_EL0, %[host_tpidr]\n"
:: [host_tpidr] "r"(host_tpidr)); :: [host_tpidr] "r"(host_tpidr));
#endif #endif
auto* guest_ctx = static_cast<GuestContext*>(nep->native_context);
auto* guest_ctx = nep->native_context;
auto& memory = guest_ctx->parent->m_running_thread->GetOwnerProcess()->GetMemory(); auto& memory = guest_ctx->parent->m_running_thread->GetOwnerProcess()->GetMemory();
#ifndef _WIN32 #ifndef _WIN32
@ -331,17 +319,18 @@ void ArmNce::GuestMemoryFaultSignalHandler(int sig, void* raw_info, void* raw_co
#endif #endif
} }
void* ArmNce::RestoreGuestContext(void* raw_context) {
NativeExecutionParameters* ArmNce::RestoreGuestContext(void* raw_context) {
// Retrieve the host context. // Retrieve the host context.
auto host_ctx = KernelContext(raw_context); auto host_ctx = KernelContext(raw_context);
#ifdef __linux__ #ifdef __linux__
// Thread-local parameters will be located in x9. // Thread-local parameters will be located in x9.
auto* tpidr = reinterpret_cast<NativeExecutionParameters*>(host_ctx.regs()[9]);
auto* nep = reinterpret_cast<NativeExecutionParameters*>(host_ctx.regs()[9]);
#else #else
auto* tpidr = static_cast<NativeExecutionParameters*>(GetGuestParameters());
auto* nep = GetGuestParameters();
#endif #endif
auto* guest_ctx = static_cast<GuestContext*>(tpidr->native_context);
auto* guest_ctx = nep->native_context;
// Save host callee-saved registers. // Save host callee-saved registers.
std::memcpy(guest_ctx->host_ctx.host_saved_vregs.data(), &host_ctx.vregs()[8], 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.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)); 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 the new thread-local storage pointer.
return tpidr;
return nep;
} }
void ArmNce::SaveGuestContext(GuestContext* guest_ctx, void* raw_context) { 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. // Restore stack pointer.
*host_ctx.sp() = guest_ctx->host_ctx.host_sp; *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. // Restore host callee-saved registers.
std::memcpy(&host_ctx.regs()[19], guest_ctx->host_ctx.host_saved_regs.data(), std::memcpy(&host_ctx.regs()[19], guest_ctx->host_ctx.host_saved_regs.data(),
sizeof(guest_ctx->host_ctx.host_saved_regs)); 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); ASSERT(pthread_setspecific(ContextKey, thread_params) == 0);
#elif defined(_WIN32) #elif defined(_WIN32)
ASSERT_MSG(TlsSetValue(ContextKey, thread_params), "Failed to set TLS value: id {}, error {}", ContextKey, GetLastError()); 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 #endif
// Move non-critical operations outside the locked section // Move non-critical operations outside the locked section
@ -529,6 +540,8 @@ ArmNce::~ArmNce() = default;
#ifdef _WIN32 #ifdef _WIN32
LONG WINAPI ArmNce::VectoredExceptionHandler(PEXCEPTION_POINTERS info) { 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; DWORD code = info->ExceptionRecord->ExceptionCode;
if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_DATATYPE_MISALIGNMENT) { if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_DATATYPE_MISALIGNMENT) {

34
src/core/arm/nce/arm_nce.h

@ -43,6 +43,28 @@ constexpr u64 TlsSlots = offsetof(TEB, TlsSlots);
#endif #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<u32> 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 { class ArmNce final : public ArmInterface {
public: public:
ArmNce(System& system, bool uses_wall_clock, std::size_t core_index); ArmNce(System& system, bool uses_wall_clock, std::size_t core_index);
@ -81,11 +103,11 @@ protected:
private: private:
// Only confirmed to be valid on Apple systems. // 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 #ifndef _WIN32
static HaltReason ReturnToRunCodeByExceptionLevelChange(int tid, void* tpidr);
static HaltReason ReturnToRunCodeByExceptionLevelChange(int tid, NativeExecutionParameters* tpidr);
#else #else
static HaltReason ReturnToRunCodeByExceptionLevelChange(void* tid, void* tpidr); static HaltReason ReturnToRunCodeByExceptionLevelChange(void* tid, void* tpidr);
static LONG VectoredExceptionHandler(PEXCEPTION_POINTERS info); static LONG VectoredExceptionHandler(PEXCEPTION_POINTERS info);
@ -97,10 +119,10 @@ private:
static void GuestMemoryFaultSignalHandler(int sig, void* info, void* raw_context); static void GuestMemoryFaultSignalHandler(int sig, void* info, void* raw_context);
static bool HandleFailedGuestFault(GuestContext* ctx, 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); static void SaveGuestContext(GuestContext* ctx, void* raw_context);
public: public:

2
src/core/arm/nce/patcher.cpp

@ -27,8 +27,6 @@ namespace Core::NCE {
using namespace Common::Literals; using namespace Common::Literals;
using namespace oaknut::util; using namespace oaknut::util;
using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters;
constexpr size_t MaxRelativeBranch = 128_MiB; constexpr size_t MaxRelativeBranch = 128_MiB;
constexpr u32 ModuleCodeIndex = 0x24 / sizeof(u32); constexpr u32 ModuleCodeIndex = 0x24 / sizeof(u32);

28
src/core/hle/kernel/k_thread.h

@ -33,6 +33,10 @@
#include "core/hle/kernel/svc_types.h" #include "core/hle/kernel/svc_types.h"
#include "core/hle/result.h" #include "core/hle/result.h"
#ifdef HAS_NCE
#include "core/arm/nce/arm_nce.h"
#endif
namespace Common { namespace Common {
class Fiber; class Fiber;
} }
@ -665,25 +669,11 @@ public:
return m_stack_top; 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<u32> lock{1};
u64 tpidr_el0{};
u64 tpidrro_el0{};
void* native_context{};
};
NativeExecutionParameters& GetNativeExecutionParameters() {
#ifdef HAS_NCE
Core::NativeExecutionParameters& GetNativeExecutionParameters() {
return m_native_execution_parameters; return m_native_execution_parameters;
} }
#endif
private: private:
KThread* RemoveWaiterByKey(KernelCore& kernel, bool* out_has_waiters, KProcessAddress key, bool is_kernel_address_key); 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{}; ThreadWaitReasonForDebugging m_wait_reason_for_debugging{};
uintptr_t m_argument{}; uintptr_t m_argument{};
KProcessAddress m_stack_top{}; KProcessAddress m_stack_top{};
NativeExecutionParameters m_native_execution_parameters{};
#ifdef HAS_NCE
Core::NativeExecutionParameters m_native_execution_parameters{};
#endif
public: public:
using ConditionVariableThreadTreeType = ConditionVariableThreadTree; using ConditionVariableThreadTreeType = ConditionVariableThreadTree;

Loading…
Cancel
Save