From 8b9c1d07bea2d13b69f6995a620b9ceda34c43f0 Mon Sep 17 00:00:00 2001 From: Exverge Date: Wed, 8 Jul 2026 22:04:59 -0400 Subject: [PATCH] [nce] no more asm --- src/common/assert.h | 33 +++- src/core/CMakeLists.txt | 2 - src/core/arm/nce/arm_nce.cpp | 140 +++++++++++---- src/core/arm/nce/arm_nce.h | 5 +- src/core/arm/nce/arm_nce.s | 189 --------------------- src/core/arm/nce/arm_nce_asm_definitions.h | 40 ----- src/core/arm/nce/guest_context.h | 1 - 7 files changed, 140 insertions(+), 270 deletions(-) delete mode 100644 src/core/arm/nce/arm_nce.s delete mode 100644 src/core/arm/nce/arm_nce_asm_definitions.h diff --git a/src/common/assert.h b/src/common/assert.h index 7da6641c55..da7f090d6b 100644 --- a/src/common/assert.h +++ b/src/common/assert.h @@ -16,21 +16,42 @@ void AssertFailSoftImpl(); [[noreturn]] void AssertFatalImpl(); -// Prevents errors on old GCC... smh... -#if defined(_MSC_VER) || defined(__clang__) +#if defined(__GNUC__) || defined(__clang__) +#define YUZU_NO_INLINE __attribute__((noinline)) +#elif defined(_MSC_VER) +#define YUZU_NO_INLINE __declspec(noinline) +#else #define YUZU_NO_INLINE +#endif + +#if !defined(__clang__) && !defined(__GNUC__) +#define YUZU_ALWAYS_INLINE __attribute__((always_inline)) +#elif defined(_MSC_VER) +#define YUZU_ALWAYS_INLINE [[msvc::forceinline]] #else -#define YUZU_NO_INLINE __attribute__((noinline)) +#define YUZU_ALWAYS_INLINE #endif +// Prevents errors on old GCC... smh... +#if defined(__GNUC__) && !defined(__clang__) #define ASSERT_MSG(_a_, ...) \ - ([&]() YUZU_NO_INLINE { \ + ([&]() YUZU_NO_INLINE { \ auto&& assert_condition = (_a_); \ - if (!(assert_condition)) [[unlikely]] { \ - LOG_CRITICAL(Debug, __FILE__ ": assert " __VA_ARGS__); \ + if (!(assert_condition)) [[unlikely]] { \ + LOG_CRITICAL(Debug, __FILE__ ": assert " __VA_ARGS__); \ AssertFailSoftImpl(); \ } \ }()) +#else +#define ASSERT_MSG(_a_, ...) \ + ([&]() YUZU_ALWAYS_INLINE { \ + auto&& assert_condition = (_a_); \ + if (!(assert_condition)) [[unlikely]] { \ + LOG_CRITICAL(Debug, __FILE__ ": assert " __VA_ARGS__); \ + AssertFailSoftImpl(); \ + } \ + }()) +#endif #define ASSERT(_a_) ASSERT_MSG(_a_, "{}", #_a_) #define UNREACHABLE_MSG(...) \ diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 16806ff0c0..f43b1c376b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1237,10 +1237,8 @@ if (HAS_NCE) set(CMAKE_ASM_FLAGS "${CFLAGS} -x assembler-with-cpp") target_sources(core PRIVATE - arm/nce/arm_nce_asm_definitions.h arm/nce/arm_nce.cpp arm/nce/arm_nce.h - arm/nce/arm_nce.s arm/nce/guest_context.h arm/nce/instructions.h arm/nce/interpreter_visitor.cpp diff --git a/src/core/arm/nce/arm_nce.cpp b/src/core/arm/nce/arm_nce.cpp index 19d28e3aa5..cc0cc3f587 100644 --- a/src/core/arm/nce/arm_nce.cpp +++ b/src/core/arm/nce/arm_nce.cpp @@ -3,19 +3,8 @@ // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#ifdef __aarch64__ -// short asm ops should always be inlined -#if !defined(__clang__) && !defined(__GNUC__) -#define ALWAYS_INLINE __attribute__((always_inline)) -#elif defined(_MSC_VER) -// todo: windows support?? it supports native context switching and signal handling -// https://learn.microsoft.com/en-us/windows/win32/debug/using-a-vectored-exception-handler -// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadcontext -#define ALWAYS_INLINE __forceinline -#else -#define ALWAYS_INLINE -#endif +#ifdef __aarch64__ #include #include @@ -48,14 +37,14 @@ constexpr u32 StackSize = 128_KiB; } // namespace -ALWAYS_INLINE +YUZU_ALWAYS_INLINE void* ArmNce::GetGuestParameters() { void* nep; /* NativeExecutionParameters* */ #ifdef __APPLE__ // https://github.com/apple-oss-distributions/xnu/blob/f6217f891ac0bb64f3d375211650a4c1ff8ca1ea/libsyscall/os/tsd.h#L156-L189 asm volatile( - "mrs %[out], TPIDRRO_EL0\n" - "ldr %[out], [ %[out], #%[off] ]\n" + "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"(CONTEXT_KEY * 8) : "memory"); @@ -67,12 +56,23 @@ void* ArmNce::GetGuestParameters() { return nep; } +YUZU_ALWAYS_INLINE void ArmNce::LockThreadParameters(void* tpidr) { - + auto* nep = static_cast(tpidr); + + u32 value; + do { + do { + value = nep->lock.load(std::memory_order_acquire); + } while (value == SpinLockLocked); + } while (!nep->lock.compare_exchange_weak(value, SpinLockLocked, + std::memory_order_relaxed, + std::memory_order_relaxed)); } +YUZU_ALWAYS_INLINE void ArmNce::UnlockThreadParameters(void* tpidr) { - + static_cast(tpidr)->lock.store(SpinLockUnlocked, std::memory_order_release); } void ArmNce::GuestMemoryFaultSignalHandler(int sig, void* raw_info, void* raw_context) { @@ -149,22 +149,102 @@ void ArmNce::GuestMemoryFaultSignalHandler(int sig, void* raw_info, void* raw_co } } +// This function has to be marked naked so that the compiler doesn't touch the stack +// or implement a return (we "artificially" return later by setting PC to the LR value) +YUZU_NO_INLINE +__attribute__((naked)) +HaltReason ArmNce::ReturnToRunCodeByExceptionLevelChange(int tid, void *tpidr) { + // x0 - tid + // x1 - tpidr + // x19 - callee-saved register and our tpidr storage + // + // uses tkill on linux and pthread_kill on macOS, both have the same signature: + // syscall (u32 tid, u64 signal) + asm volatile( + "str x19, [SP, #-0x10]!\n" + "mov x19, x1\n" // move tpidr to x19 so it doesn't get clobbered + + "mov x1, #%[sig]\n" // set x1 to SIGUSR2 +#ifdef __linux__ + "mov x8, %[syscall]\n" + "svc #0\n" + "brk 0x0\n" + :: [syscall] "i"(__NR_tkill), +#elif defined(__APPLE__) + "mov x8, #328\n" + "svc #0\n" + "brk 0x0\n" + :: +#endif + [sig] "i"(SIGUSR2) + : "memory" + ); +} + +void ArmNce::ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void *info, void *raw_context) { + auto tpidr = static_cast(RestoreGuestContext(raw_context)); + + RestoreGuestContext(raw_context); +#ifndef __APPLE__ + // Save old value of TPIDR_EL0, load guest one + u64 tpidr_el0; + asm volatile("mrs %0, TPIDR_EL0" + "msr TPIDR_EL0, %1" + : "=r"(tpidr_el0) + : "r"(tpidr)); + tpidr->tpidr_el0 = tpidr_el0; +#else + tpidr->is_actually_running = true; +#endif + + // todo: is Lock called? + UnlockThreadParameters(tpidr); + // sigaction restores context and returns to guest +} + +void ArmNce::BreakFromRunCodeSignalHandler(int sig, void *info, void *raw_context) { + NativeExecutionParameters* tpidr = reinterpret_cast(GetGuestParameters()); +#ifdef __APPLE__ + if (tpidr->is_actually_running) { +#else + if (tpidr->magic == Common::MakeMagic('Y', 'U', 'Z', 'U')) { + // Load the host's TPIDR_EL0 value + u64 scratch; + asm volatile( + "ldr %[scratch], [ %[tpidr], #[off] ]\n" + "msr TPIDR_EL0, %[scratch]\n" + : [scratch] "=&r"(scratch), + : [tpidr] "r"(nep), + : "memory" + ); +#endif + SaveGuestContext(static_cast(tpidr->native_context), raw_context); + // Returning from here will enter host code. + } +} + +HaltReason ArmNce::ReturnToRunCodeByTrampoline(void *tpidr, GuestContext *ctx, u64 trampoline_addr) { + // todo + return HaltReason::DataAbort; +} + void* ArmNce::RestoreGuestContext(void* raw_context) { // Retrieve the host context. auto host_ctx = KernelContext(&static_cast(raw_context)->uc_mcontext); - // Thread-local parameters will be located in x9. - auto* tpidr = reinterpret_cast(host_ctx.regs()[9]); + // Thread-local parameters will be located in x19. + auto* tpidr = reinterpret_cast(host_ctx.regs()[19]); auto* guest_ctx = static_cast(tpidr->native_context); // Save host callee-saved registers. + host_ctx.regs()[19] = *reinterpret_cast(*host_ctx.sp()); // load x19 original value from the stack std::memcpy(guest_ctx->host_ctx.host_saved_vregs.data(), &host_ctx.vregs()[8], sizeof(guest_ctx->host_ctx.host_saved_vregs)); std::memcpy(guest_ctx->host_ctx.host_saved_regs.data(), &host_ctx.regs()[19], sizeof(guest_ctx->host_ctx.host_saved_regs)); // Save stack pointer. - guest_ctx->host_ctx.host_sp = *host_ctx.sp(); + guest_ctx->host_ctx.host_sp = *host_ctx.sp() + 16; // +16 for the space we reserve for x19 // Restore all guest state except tpidr_el0. *host_ctx.sp() = guest_ctx->sp; @@ -370,17 +450,17 @@ void ArmNce::Initialize() { sigset_t signal_mask; sigemptyset(&signal_mask); - sigaddset(&signal_mask, ReturnToRunCodeByExceptionLevelChangeSignal); - sigaddset(&signal_mask, BreakFromRunCodeSignal); - sigaddset(&signal_mask, GuestAlignmentFaultSignal); - sigaddset(&signal_mask, GuestAccessFaultSignal); + sigaddset(&signal_mask, SIGUSR2); // ReturnToCodeByExceptionLevel + sigaddset(&signal_mask, SIGURG); // BreakFromRunCode + sigaddset(&signal_mask, SIGBUS); + sigaddset(&signal_mask, SIGSEGV); struct sigaction return_to_run_code_action {}; return_to_run_code_action.sa_flags = SA_SIGINFO | SA_ONSTACK; return_to_run_code_action.sa_sigaction = reinterpret_cast( &ArmNce::ReturnToRunCodeByExceptionLevelChangeSignalHandler); return_to_run_code_action.sa_mask = signal_mask; - Common::SigAction(ReturnToRunCodeByExceptionLevelChangeSignal, &return_to_run_code_action, + Common::SigAction(SIGUSR2, &return_to_run_code_action, nullptr); struct sigaction break_from_run_code_action {}; @@ -388,21 +468,21 @@ void ArmNce::Initialize() { break_from_run_code_action.sa_sigaction = reinterpret_cast(&ArmNce::BreakFromRunCodeSignalHandler); break_from_run_code_action.sa_mask = signal_mask; - Common::SigAction(BreakFromRunCodeSignal, &break_from_run_code_action, nullptr); + Common::SigAction(SIGURG, &break_from_run_code_action, nullptr); struct sigaction alignment_fault_action {}; alignment_fault_action.sa_flags = SA_SIGINFO | SA_ONSTACK; alignment_fault_action.sa_sigaction = - reinterpret_cast(&ArmNce::GuestAlignmentFaultSignalHandler); + reinterpret_cast(&ArmNce::GuestMemoryFaultSignalHandler); alignment_fault_action.sa_mask = signal_mask; - Common::SigAction(GuestAlignmentFaultSignal, &alignment_fault_action, nullptr); + Common::SigAction(SIGBUS, &alignment_fault_action, nullptr); struct sigaction access_fault_action {}; access_fault_action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART; access_fault_action.sa_sigaction = reinterpret_cast(&ArmNce::GuestMemoryFaultSignalHandler); access_fault_action.sa_mask = signal_mask; - Common::SigAction(GuestAccessFaultSignal, &access_fault_action, &g_orig_segv_action); + Common::SigAction(SIGSEGV, &access_fault_action, &g_orig_segv_action); }); } @@ -461,7 +541,7 @@ void ArmNce::SignalInterrupt(Kernel::KThread* thread) { "mov x1, %1\n" // BreakFromRunCodeSignal "mov x16, #328\n" // syscall code for __pthread_kill "svc #0x80\n" - :: "r"(static_cast(m_thread_id)), "r"(static_cast(BreakFromRunCodeSignal)) + :: "r"(static_cast(m_thread_id)), "r"(static_cast(SIGURG)) : "x0", "x1", "x16", "memory", "cc"); #endif } else { diff --git a/src/core/arm/nce/arm_nce.h b/src/core/arm/nce/arm_nce.h index 052005b595..1f5d1d4a9c 100644 --- a/src/core/arm/nce/arm_nce.h +++ b/src/core/arm/nce/arm_nce.h @@ -11,6 +11,9 @@ #include "core/arm/arm_interface.h" #include "core/arm/nce/guest_context.h" +#define SpinLockLocked 0 +#define SpinLockUnlocked 1 + namespace Core::Memory { class Memory; } @@ -74,7 +77,6 @@ private: static void ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void* info, void* raw_context); static void BreakFromRunCodeSignalHandler(int sig, void* info, void* raw_context); - static void GuestAlignmentFaultSignalHandler(int sig, void* info, void* raw_context); static void GuestMemoryFaultSignalHandler(int sig, void* info, void* raw_context); static void LockThreadParameters(void* tpidr); @@ -84,7 +86,6 @@ private: static void* RestoreGuestContext(void* raw_context); static void SaveGuestContext(GuestContext* ctx, void* raw_context); static bool HandleFailedGuestFault(GuestContext* ctx, void* info, void* raw_context); - static bool HandleGuestAlignmentFault(GuestContext* ctx, void* info, void* raw_context); public: Core::System& m_system; diff --git a/src/core/arm/nce/arm_nce.s b/src/core/arm/nce/arm_nce.s deleted file mode 100644 index b14438a50c..0000000000 --- a/src/core/arm/nce/arm_nce.s +++ /dev/null @@ -1,189 +0,0 @@ -/* SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project */ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#define __ASSEMBLY__ -#include "core/arm/nce/arm_nce_asm_definitions.h" - -#define LOAD_IMMEDIATE_32(reg, val) \ - mov reg, #(((val) >> 0x00) & 0xFFFF); \ - movk reg, #(((val) >> 0x10) & 0xFFFF), lsl #16 - -#ifdef __APPLE__ -#define SYM(name) _##name - -.macro ASM_FUNCTION_START name - -.text -.align 2 -.global _\name -_\name: - -.endm -#else -#define SYM(name) name -.macro ASM_FUNCTION_START name - -.section .text.\name, "ax", %progbits -.global \name -.type \name, %function -\name: - -.endm -#endif - -/* static HaltReason Core::ArmNce::ReturnToRunCodeByTrampoline(void* tpidr, Core::GuestContext* ctx, u64 trampoline_addr) */ -#ifndef __APPLE__ -ASM_FUNCTION_START _ZN4Core6ArmNce27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm -#else -ASM_FUNCTION_START _ZN4Core6ArmNce27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEy -#endif - /* Back up host sp to x3. */ - /* Back up host tpidr_el0 to x4. */ - mov x3, sp - mrs x4, tpidr_el0 - - /* Load guest sp. x5 is used as a scratch register. */ - ldr x5, [x1, #(GuestContextSp)] - mov sp, x5 - - /* Offset GuestContext pointer to the host member. */ - add x5, x1, #(GuestContextHostContext) - - /* Save original host sp and tpidr_el0 (x3, x4) to host context. */ - stp x3, x4, [x5, #(HostContextSpTpidrEl0)] - - /* Save all callee-saved host GPRs. */ - stp x19, x20, [x5, #(HostContextRegs+0x0)] - stp x21, x22, [x5, #(HostContextRegs+0x10)] - stp x23, x24, [x5, #(HostContextRegs+0x20)] - stp x25, x26, [x5, #(HostContextRegs+0x30)] - stp x27, x28, [x5, #(HostContextRegs+0x40)] - stp x29, x30, [x5, #(HostContextRegs+0x50)] - - /* Save all callee-saved host FPRs. */ - stp q8, q9, [x5, #(HostContextVregs+0x0)] - stp q10, q11, [x5, #(HostContextVregs+0x20)] - stp q12, q13, [x5, #(HostContextVregs+0x40)] - stp q14, q15, [x5, #(HostContextVregs+0x60)] - - /* Load guest tpidr_el0 from argument. */ - msr tpidr_el0, x0 - - /* Tail call the trampoline to restore guest state. */ - br x2 - - -/* static HaltReason Core::ArmNce::ReturnToRunCodeByExceptionLevelChange(int tid, void* tpidr) */ -ASM_FUNCTION_START _ZN4Core6ArmNce37ReturnToRunCodeByExceptionLevelChangeEiPv - /* This jumps to the signal handler, which will restore the entire context. */ - /* On entry, x0 = thread id, which is already in the right place. */ - - /* Move tpidr to x9 so it is not trampled. */ - mov x9, x1 - - /* Set up arguments. */ - /* On entry, x0 = thread id, which is already in the right place. */ - mov x1, #(ReturnToRunCodeByExceptionLevelChangeSignal) - - /* Tail call the signal handler. */ -#ifndef __APPLE__ - mov x8, #(__NR_tkill) - svc #0 -#else - mov x16, #(__pthread_pkill) - svc #0x80 -#endif - - /* Block execution from flowing here. */ - brk #1000 - - -/* static void Core::ArmNce::ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void* info, void* raw_context) */ -ASM_FUNCTION_START _ZN4Core6ArmNce50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_ - stp x29, x30, [sp, #-0x10]! - mov x29, sp - - /* Call the context restorer with the raw context. */ - mov x0, x2 - bl SYM(_ZN4Core6ArmNce19RestoreGuestContextEPv) - - /* Save the old value of tpidr_el0. */ - mrs x8, tpidr_el0 - ldr x9, [x0, #(TpidrEl0NativeContext)] - str x8, [x9, #(GuestContextHostContext + HostContextTpidrEl0)] - - /* Set our new tpidr_el0. */ - msr tpidr_el0, x0 - - /* Unlock the context. */ - bl SYM(_ZN4Core6ArmNce22UnlockThreadParametersEPv) - - /* Returning from here will enter the guest. */ - ldp x29, x30, [sp], #0x10 - ret - - -/* static void Core::ArmNce::BreakFromRunCodeSignalHandler(int sig, void* info, void* raw_context) */ -ASM_FUNCTION_START _ZN4Core6ArmNce29BreakFromRunCodeSignalHandlerEiPvS1_ - /* Check to see if we have the correct TLS magic. */ - mrs x8, tpidr_el0 - ldr w9, [x8, #(TpidrEl0TlsMagic)] - - LOAD_IMMEDIATE_32(w10, TlsMagic) - - cmp w9, w10 - b.ne 1f - - /* Correct TLS magic, so this is a guest interrupt. */ - /* Restore host tpidr_el0. */ - ldr x0, [x8, #(TpidrEl0NativeContext)] - ldr x3, [x0, #(GuestContextHostContext + HostContextTpidrEl0)] - msr tpidr_el0, x3 - - /* Tail call the restorer. */ - mov x1, x2 - b SYM(_ZN4Core6ArmNce16SaveGuestContextEPNS_12GuestContextEPv) - - /* Returning from here will enter host code. */ - -1: - /* Incorrect TLS magic, so this is a spurious signal. */ - ret - -/* static void Core::ArmNce::LockThreadParameters(void* tpidr) */ -ASM_FUNCTION_START _ZN4Core6ArmNce20LockThreadParametersEPv - /* Offset to lock member. */ - add x0, x0, #(TpidrEl0Lock) - -1: - /* Clear the monitor. */ - clrex - -2: - /* Load-linked with acquire ordering. */ - ldaxr w1, [x0] - - /* If the value was SpinLockLocked, clear monitor and retry. */ - cbz w1, 1b - - /* Store-conditional SpinLockLocked with relaxed ordering. */ - stxr w1, wzr, [x0] - - /* If we failed to store, retry. */ - cbnz w1, 2b - - ret - - -/* static void Core::ArmNce::UnlockThreadParameters(void* tpidr) */ -ASM_FUNCTION_START _ZN4Core6ArmNce22UnlockThreadParametersEPv - /* Offset to lock member. */ - add x0, x0, #(TpidrEl0Lock) - - /* Load SpinLockUnlocked. */ - mov w1, #(SpinLockUnlocked) - - /* Store value with release ordering. */ - stlr w1, [x0] - - ret diff --git a/src/core/arm/nce/arm_nce_asm_definitions.h b/src/core/arm/nce/arm_nce_asm_definitions.h deleted file mode 100644 index 6c63670b59..0000000000 --- a/src/core/arm/nce/arm_nce_asm_definitions.h +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#if defined(__ASSEMBLY__) && defined(__linux__) -#include -#include -#else -#define SIGUSR2 31 -#define SIGURG 16 -#define SIGSEGV 11 -#define SIGBUS 10 - -#define __pthread_pkill 328 -#endif - -#define ReturnToRunCodeByExceptionLevelChangeSignal SIGUSR2 -#define BreakFromRunCodeSignal SIGURG -#define GuestAccessFaultSignal SIGSEGV -#define GuestAlignmentFaultSignal SIGBUS - -#define GuestContextSp 0xF8 -#define GuestContextHostContext 0x320 - -#define HostContextSpTpidrEl0 0xE0 -#define HostContextTpidrEl0 0xE8 -#define HostContextRegs 0x0 -#define HostContextVregs 0x60 - -#define TpidrEl0NativeContext 0x10 -#define TpidrEl0Lock 0x18 -#define TpidrEl0TlsMagic 0x20 -#define TlsMagic 0x555a5559 - -#define SpinLockLocked 0 -#define SpinLockUnlocked 1 diff --git a/src/core/arm/nce/guest_context.h b/src/core/arm/nce/guest_context.h index fe085665ac..25cb7da9fd 100644 --- a/src/core/arm/nce/guest_context.h +++ b/src/core/arm/nce/guest_context.h @@ -11,7 +11,6 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "core/arm/arm_interface.h" -#include "core/arm/nce/arm_nce_asm_definitions.h" #ifdef __linux__ #include