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 /**