From de81d28b1d9db4f21e6c33b12718b847bf1eee4f Mon Sep 17 00:00:00 2001 From: PixelyIon Date: Sat, 9 Apr 2022 17:02:40 +0530 Subject: [PATCH] Implement SVC `GetThreadContext3` A partial implementation of the `GetThreadContext3` SVC, we cannot return the whole thread context as the kernel only stores the registers we need according to the ARMv8 ABI convention and so far usages of this SVC do not require the unavailable registers but all future usage must be monitored and potentially require extending the amount of saved registers. --- app/src/main/cpp/skyline/kernel/svc.cpp | 57 +++++++++++++++++++++++++ app/src/main/cpp/skyline/kernel/svc.h | 8 +++- app/src/main/cpp/skyline/nce/guest.h | 4 +- 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/app/src/main/cpp/skyline/kernel/svc.cpp b/app/src/main/cpp/skyline/kernel/svc.cpp index 5f04761c..21ab07f7 100644 --- a/app/src/main/cpp/skyline/kernel/svc.cpp +++ b/app/src/main/cpp/skyline/kernel/svc.cpp @@ -1140,6 +1140,63 @@ namespace skyline::kernel::svc { } } + void GetThreadContext3(const DeviceState &state) { + KHandle threadHandle{state.ctx->gpr.w1}; + try { + auto thread{state.process->GetHandle(threadHandle)}; + if (thread == state.thread) { + Logger::Warn("Thread attempting to retrieve own context: 0x{:X}", threadHandle); + state.ctx->gpr.w0 = result::Busy; + return; + } + + std::scoped_lock guard{thread->coreMigrationMutex}; + if (!thread->isPaused) { + Logger::Warn("Attemping to get context of running thread: 0x{:X}", threadHandle); + state.ctx->gpr.w0 = result::InvalidState; + return; + } + + struct ThreadContext { + std::array gpr; + u64 fp; + u64 lr; + u64 sp; + u64 pc; + u32 pstate; + u32 _pad_; + std::array vreg; + u32 fpcr; + u32 fpsr; + u64 tpidr; + }; + static_assert(sizeof(ThreadContext) == 0x320); + + auto &context{*reinterpret_cast(state.ctx->gpr.x0)}; + context = {}; // Zero-initialize the contents of the context as not all fields are set + + auto& targetContext{thread->ctx}; + for (size_t i{}; i < targetContext.gpr.regs.size(); i++) + context.gpr[i] = targetContext.gpr.regs[i]; + + for (size_t i{}; i < targetContext.fpr.regs.size(); i++) + context.vreg[i] = targetContext.fpr.regs[i]; + + context.fpcr = targetContext.fpr.fpcr; + context.fpsr = targetContext.fpr.fpsr; + + context.tpidr = reinterpret_cast(targetContext.tpidrEl0); + + // Note: We don't write the whole context as we only store the parts required according to the ARMv8 ABI for syscall handling + Logger::Debug("Written partial context for thread 0x{:X}", threadHandle); + + state.ctx->gpr.w0 = Result{}; + } catch (const std::out_of_range &) { + Logger::Warn("'handle' invalid: 0x{:X}", threadHandle); + state.ctx->gpr.w0 = result::InvalidHandle; + } + } + void WaitForAddress(const DeviceState &state) { auto address{reinterpret_cast(state.ctx->gpr.x0)}; if (!util::IsWordAligned(address)) [[unlikely]] { diff --git a/app/src/main/cpp/skyline/kernel/svc.h b/app/src/main/cpp/skyline/kernel/svc.h index 7ed6af68..e8acfc21 100644 --- a/app/src/main/cpp/skyline/kernel/svc.h +++ b/app/src/main/cpp/skyline/kernel/svc.h @@ -228,6 +228,12 @@ namespace skyline::kernel::svc { */ void SetThreadActivity(const DeviceState &state); + /** + * @brief Gets the context structure of a paused thread + * @url https://switchbrew.org/wiki/SVC#GetThreadContext3 + */ + void GetThreadContext3(const DeviceState &state); + /** * @brief Waits on an address based on the value of the address * @url https://switchbrew.org/wiki/SVC#WaitForAddress @@ -312,7 +318,7 @@ namespace skyline::kernel::svc { SVC_NONE, // 0x30 SVC_NONE, // 0x31 SVC_ENTRY(SetThreadActivity), // 0x32 - SVC_NONE, // 0x33 + SVC_ENTRY(GetThreadContext3), // 0x33 SVC_ENTRY(WaitForAddress), // 0x34 SVC_ENTRY(SignalToAddress), // 0x35 SVC_NONE, // 0x36 diff --git a/app/src/main/cpp/skyline/nce/guest.h b/app/src/main/cpp/skyline/nce/guest.h index 7b379eab..b8283183 100644 --- a/app/src/main/cpp/skyline/nce/guest.h +++ b/app/src/main/cpp/skyline/nce/guest.h @@ -103,8 +103,8 @@ namespace skyline { }; namespace guest { - constexpr size_t SaveCtxSize{39}; //!< The size of the SaveCtx function in 32-bit ARMv8 instructions - constexpr size_t LoadCtxSize{39}; //!< The size of the LoadCtx function in 32-bit ARMv8 instructions + constexpr size_t SaveCtxSize{34}; //!< The size of the SaveCtx function in 32-bit ARMv8 instructions + constexpr size_t LoadCtxSize{34}; //!< The size of the LoadCtx function in 32-bit ARMv8 instructions constexpr size_t RescaleClockSize{16}; //!< The size of the RescaleClock function in 32-bit ARMv8 instructions /**