Browse Source

[nce] no more asm

remotes/1785372757367212240/tmp_refs/heads/variable-page-size
Exverge 1 month ago
parent
commit
8b9c1d07be
No known key found for this signature in database GPG Key ID: DAD399BCC5FB77E4
  1. 33
      src/common/assert.h
  2. 2
      src/core/CMakeLists.txt
  3. 140
      src/core/arm/nce/arm_nce.cpp
  4. 5
      src/core/arm/nce/arm_nce.h
  5. 189
      src/core/arm/nce/arm_nce.s
  6. 40
      src/core/arm/nce/arm_nce_asm_definitions.h
  7. 1
      src/core/arm/nce/guest_context.h

33
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(...) \

2
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

140
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 <cinttypes>
#include <memory>
@ -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<NativeExecutionParameters*>(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<NativeExecutionParameters*>(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<NativeExecutionParameters*>(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<NativeExecutionParameters *>(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<GuestContext *>(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<ucontext_t*>(raw_context)->uc_mcontext);
// Thread-local parameters will be located in x9.
auto* tpidr = reinterpret_cast<NativeExecutionParameters*>(host_ctx.regs()[9]);
// Thread-local parameters will be located in x19.
auto* tpidr = reinterpret_cast<NativeExecutionParameters*>(host_ctx.regs()[19]);
auto* guest_ctx = static_cast<GuestContext*>(tpidr->native_context);
// Save host callee-saved registers.
host_ctx.regs()[19] = *reinterpret_cast<u64*>(*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<HandlerType>(
&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<HandlerType>(&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<HandlerType>(&ArmNce::GuestAlignmentFaultSignalHandler);
reinterpret_cast<HandlerType>(&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<HandlerType>(&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<u64>(m_thread_id)), "r"(static_cast<u64>(BreakFromRunCodeSignal))
:: "r"(static_cast<u64>(m_thread_id)), "r"(static_cast<u64>(SIGURG))
: "x0", "x1", "x16", "memory", "cc");
#endif
} else {

5
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;

189
src/core/arm/nce/arm_nce.s

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

40
src/core/arm/nce/arm_nce_asm_definitions.h

@ -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 <asm-generic/signal.h>
#include <asm-generic/unistd.h>
#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

1
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 <signal.h>

Loading…
Cancel
Save