From 484641003cf727def0101f35c83d1ef135b93f54 Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Tue, 7 Mar 2023 19:18:06 -0500
Subject: [PATCH] kernel: clone fpu status on CreateThread

---
 src/core/hle/kernel/k_thread.cpp       | 24 +++++++++++++++++++++++-
 src/core/hle/kernel/k_thread.h         |  2 ++
 src/core/hle/kernel/svc/svc_thread.cpp |  3 +++
 3 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 8c403f5fdd..15ae652f9d 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -49,6 +49,7 @@ static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context,
     context.cpu_registers[0] = arg;
     context.cpu_registers[15] = entry_point;
     context.cpu_registers[13] = stack_top;
+    context.fpscr = 0;
 }
 
 static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context, VAddr stack_top,
@@ -58,8 +59,8 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context,
     context.cpu_registers[18] = Kernel::KSystemControl::GenerateRandomU64() | 1;
     context.pc = entry_point;
     context.sp = stack_top;
-    // TODO(merry): Perform a hardware test to determine the below value.
     context.fpcr = 0;
+    context.fpsr = 0;
 }
 } // namespace
 
@@ -815,6 +816,27 @@ void KThread::Continue() {
     KScheduler::OnThreadStateChanged(kernel, this, old_state);
 }
 
+void KThread::CloneFpuStatus() {
+    // We shouldn't reach here when starting kernel threads.
+    ASSERT(this->GetOwnerProcess() != nullptr);
+    ASSERT(this->GetOwnerProcess() == GetCurrentProcessPointer(kernel));
+
+    if (this->GetOwnerProcess()->Is64BitProcess()) {
+        // Clone FPSR and FPCR.
+        ThreadContext64 cur_ctx{};
+        kernel.System().CurrentArmInterface().SaveContext(cur_ctx);
+
+        this->GetContext64().fpcr = cur_ctx.fpcr;
+        this->GetContext64().fpsr = cur_ctx.fpsr;
+    } else {
+        // Clone FPSCR.
+        ThreadContext32 cur_ctx{};
+        kernel.System().CurrentArmInterface().SaveContext(cur_ctx);
+
+        this->GetContext32().fpscr = cur_ctx.fpscr;
+    }
+}
+
 Result KThread::SetActivity(Svc::ThreadActivity activity) {
     // Lock ourselves.
     KScopedLightLock lk(activity_pause_lock);
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index bd125f5f16..9423f08ca7 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -254,6 +254,8 @@ public:
         thread_context_32.tpidr = static_cast<u32>(value);
     }
 
+    void CloneFpuStatus();
+
     [[nodiscard]] ThreadContext32& GetContext32() {
         return thread_context_32;
     }
diff --git a/src/core/hle/kernel/svc/svc_thread.cpp b/src/core/hle/kernel/svc/svc_thread.cpp
index b398078418..9bc1ebe74d 100644
--- a/src/core/hle/kernel/svc/svc_thread.cpp
+++ b/src/core/hle/kernel/svc/svc_thread.cpp
@@ -82,6 +82,9 @@ Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point,
     // Commit the thread reservation.
     thread_reservation.Commit();
 
+    // Clone the current fpu status to the new thread.
+    thread->CloneFpuStatus();
+
     // Register the new thread.
     KThread::Register(kernel, thread);