From 5c4fbf5c6754764aaf491cb285c4ac8bbe480a63 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 31 Jul 2020 02:49:43 -0700 Subject: [PATCH] kern SvcGetDebugThreadContext, SvcSetDebugThreadContext --- .../mesosphere/arch/arm64/kern_k_debug.hpp | 6 + .../arch/arm64/kern_k_thread_context.hpp | 2 + .../include/mesosphere/kern_k_debug_base.hpp | 8 + .../include/mesosphere/kern_k_thread.hpp | 5 + .../mesosphere/svc/kern_svc_results.hpp | 1 + .../source/arch/arm64/kern_k_debug.cpp | 176 ++++++++++++++++++ .../arch/arm64/kern_k_thread_context.cpp | 15 ++ .../source/kern_k_debug_base.cpp | 106 +++++++++++ .../source/svc/kern_svc_debug.cpp | 47 ++++- .../include/vapours/results/svc_results.hpp | 1 + 10 files changed, 363 insertions(+), 4 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_debug.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_debug.hpp index 9151e9fd4..6424d7be6 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_debug.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_debug.hpp @@ -34,6 +34,12 @@ namespace ams::kern::arch::arm64 { virtual ~KDebug() { /* ... */ } static void PostDestroy(uintptr_t arg) { /* ... */ } + public: + virtual Result GetThreadContextImpl(ams::svc::ThreadContext *out, KThread *thread, u32 context_flags) override; + virtual Result SetThreadContextImpl(const ams::svc::ThreadContext &ctx, KThread *thread, u32 context_flags) override; + private: + Result GetFpuContext(ams::svc::ThreadContext *out, KThread *thread, u32 context_flags); + Result SetFpuContext(const ams::svc::ThreadContext &ctx, KThread *thread, u32 context_flags); public: static uintptr_t GetProgramCounter(const KThread &thread); static void SetPreviousProgramCounter(); diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_thread_context.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_thread_context.hpp index 47ef49aa9..6a123eee5 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_thread_context.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_thread_context.hpp @@ -74,6 +74,8 @@ namespace ams::kern::arch::arm64 { void CloneFpuStatus(); + void SetFpuRegisters(const u128 *v, bool is_64_bit); + const u128 *GetFpuRegisters() const { return this->fpu_registers; } public: static void OnThreadTerminating(const KThread *thread); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_debug_base.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_debug_base.hpp index c4483b00d..dc3951236 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_debug_base.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_debug_base.hpp @@ -34,6 +34,8 @@ namespace ams::kern { public: explicit KDebugBase() { /* ... */ } virtual ~KDebugBase() { /* ... */ } + protected: + bool Is64Bit() const; public: void Initialize(); @@ -47,6 +49,12 @@ namespace ams::kern { Result ReadMemory(KProcessAddress buffer, KProcessAddress address, size_t size); Result WriteMemory(KProcessAddress buffer, KProcessAddress address, size_t size); + Result GetThreadContext(ams::svc::ThreadContext *out, u64 thread_id, u32 context_flags); + Result SetThreadContext(const ams::svc::ThreadContext &ctx, u64 thread_id, u32 context_flags); + + virtual Result GetThreadContextImpl(ams::svc::ThreadContext *out, KThread *thread, u32 context_flags) = 0; + virtual Result SetThreadContextImpl(const ams::svc::ThreadContext &ctx, KThread *thread, u32 context_flags) = 0; + Result GetRunningThreadInfo(ams::svc::LastThreadContext *out_context, u64 *out_thread_id); Result GetDebugEventInfo(ams::svc::lp64::DebugEventInfo *out); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp index 6a13f6a50..8f6e515a0 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp @@ -274,6 +274,11 @@ namespace ams::kern { return this->GetStackParameters().is_calling_svc; } + ALWAYS_INLINE u8 GetSvcId() const { + MESOSPHERE_ASSERT_THIS(); + return this->GetStackParameters().current_svc_id; + } + ALWAYS_INLINE void RegisterDpc(DpcFlag flag) { this->GetStackParameters().dpc_flags |= flag; } diff --git a/libraries/libmesosphere/include/mesosphere/svc/kern_svc_results.hpp b/libraries/libmesosphere/include/mesosphere/svc/kern_svc_results.hpp index a1aa17d2c..3a03d53a4 100644 --- a/libraries/libmesosphere/include/mesosphere/svc/kern_svc_results.hpp +++ b/libraries/libmesosphere/include/mesosphere/svc/kern_svc_results.hpp @@ -72,6 +72,7 @@ namespace ams::kern::svc { /* 517 */ using ::ams::svc::ResultInvalidProcessId; /* 518 */ using ::ams::svc::ResultInvalidThreadId; + /* 519 */ using ::ams::svc::ResultInvalidId; /* 520 */ using ::ams::svc::ResultProcessTerminated; } diff --git a/libraries/libmesosphere/source/arch/arm64/kern_k_debug.cpp b/libraries/libmesosphere/source/arch/arm64/kern_k_debug.cpp index 72af04700..b6286a02a 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_k_debug.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_k_debug.cpp @@ -36,6 +36,9 @@ namespace ams::kern::arch::arm64 { (((1ul << 2) - 1) << 1); /* Privileged Access Control. */ static_assert(ForbiddenWatchPointFlagsMask == 0xFFFFFFFF00F0E006ul); + + constexpr inline u32 El0PsrMask = 0xFF0FFE20; + } uintptr_t KDebug::GetProgramCounter(const KThread &thread) { @@ -64,6 +67,179 @@ namespace ams::kern::arch::arm64 { } } + Result KDebug::GetThreadContextImpl(ams::svc::ThreadContext *out, KThread *thread, u32 context_flags) { + MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread()); + MESOSPHERE_ASSERT(thread != GetCurrentThreadPointer()); + + /* Get the exception context. */ + const KExceptionContext *e_ctx = GetExceptionContext(thread); + + /* If general registers are requested, get them. */ + if ((context_flags & ams::svc::ThreadContextFlag_General) != 0) { + if (!thread->IsCallingSvc() || thread->GetSvcId() == svc::SvcId_ReturnFromException) { + if (this->Is64Bit()) { + /* Get X0-X28. */ + for (auto i = 0; i <= 28; ++i) { + out->r[i] = e_ctx->x[i]; + } + } else { + /* Get R0-R12. */ + for (auto i = 0; i <= 12; ++i) { + out->r[i] = static_cast(e_ctx->x[i]); + } + } + } + } + + /* If control flags are requested, get them. */ + if ((context_flags & ams::svc::ThreadContextFlag_Control) != 0) { + if (this->Is64Bit()) { + out->fp = e_ctx->x[29]; + out->lr = e_ctx->x[30]; + out->sp = e_ctx->sp; + out->pc = e_ctx->pc; + out->pstate = (e_ctx->psr & El0PsrMask); + + /* Adjust PC if we should. */ + if (e_ctx->write == 0 && thread->IsCallingSvc()) { + out->pc -= sizeof(u32); + } + + out->tpidr = e_ctx->tpidr; + } else { + out->r[11] = static_cast(e_ctx->x[11]); + out->r[13] = static_cast(e_ctx->x[13]); + out->r[14] = static_cast(e_ctx->x[14]); + out->lr = 0; + out->sp = 0; + out->pc = e_ctx->pc; + out->pstate = (e_ctx->psr & El0PsrMask); + + /* Adjust PC if we should. */ + if (e_ctx->write == 0 && thread->IsCallingSvc()) { + out->pc -= (e_ctx->psr & 0x20) ? sizeof(u16) : sizeof(u32); + } + + out->tpidr = static_cast(e_ctx->tpidr); + } + } + + /* Get the FPU context. */ + return this->GetFpuContext(out, thread, context_flags); + } + + Result KDebug::SetThreadContextImpl(const ams::svc::ThreadContext &ctx, KThread *thread, u32 context_flags) { + MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread()); + MESOSPHERE_ASSERT(thread != GetCurrentThreadPointer()); + + /* Get the exception context. */ + KExceptionContext *e_ctx = GetExceptionContext(thread); + + /* If general registers are requested, set them. */ + if ((context_flags & ams::svc::ThreadContextFlag_General) != 0) { + if (this->Is64Bit()) { + /* Set X0-X28. */ + for (auto i = 0; i <= 28; ++i) { + e_ctx->x[i] = ctx.r[i]; + } + } else { + /* Set R0-R12. */ + for (auto i = 0; i <= 12; ++i) { + e_ctx->x[i] = static_cast(ctx.r[i]); + } + } + } + + /* If control flags are requested, set them. */ + if ((context_flags & ams::svc::ThreadContextFlag_Control) != 0) { + /* Mark ourselve as having adjusted pc. */ + e_ctx->write = 1; + + if (this->Is64Bit()) { + e_ctx->x[29] = ctx.fp; + e_ctx->x[30] = ctx.lr; + e_ctx->sp = ctx.sp; + e_ctx->pc = ctx.pc; + e_ctx->psr = ((ctx.pstate & El0PsrMask) | (e_ctx->psr & ~El0PsrMask)); + e_ctx->tpidr = ctx.tpidr; + } else { + e_ctx->x[13] = static_cast(ctx.r[13]); + e_ctx->x[14] = static_cast(ctx.r[14]); + e_ctx->x[30] = 0; + e_ctx->sp = 0; + e_ctx->pc = static_cast(ctx.pc); + e_ctx->psr = ((ctx.pstate & El0PsrMask) | (e_ctx->psr & ~El0PsrMask)); + e_ctx->tpidr = ctx.tpidr; + } + } + + /* Set the FPU context. */ + return this->SetFpuContext(ctx, thread, context_flags); + } + + Result KDebug::GetFpuContext(ams::svc::ThreadContext *out, KThread *thread, u32 context_flags) { + MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread()); + MESOSPHERE_ASSERT(thread != GetCurrentThreadPointer()); + + /* Succeed if there's nothing to do. */ + R_SUCCEED_IF((context_flags & (ams::svc::ThreadContextFlag_Fpu | ams::svc::ThreadContextFlag_FpuControl)) == 0); + + /* Get the thread context. */ + KThreadContext *t_ctx = std::addressof(thread->GetContext()); + + /* Get the FPU control registers, if required. */ + if ((context_flags & ams::svc::ThreadContextFlag_FpuControl) != 0) { + out->fpsr = t_ctx->GetFpsr(); + out->fpcr = t_ctx->GetFpcr(); + } + + /* Get the FPU registers, if required. */ + if ((context_flags & ams::svc::ThreadContextFlag_Fpu) != 0) { + static_assert(util::size(ams::svc::ThreadContext{}.v) == KThreadContext::NumFpuRegisters); + const u128 *f = t_ctx->GetFpuRegisters(); + + if (this->Is64Bit()) { + for (size_t i = 0; i < KThreadContext::NumFpuRegisters; ++i) { + out->v[i] = f[i]; + } + } else { + for (size_t i = 0; i < KThreadContext::NumFpuRegisters / 2; ++i) { + out->v[i] = f[i]; + } + for (size_t i = KThreadContext::NumFpuRegisters / 2; i < KThreadContext::NumFpuRegisters; ++i) { + out->v[i] = 0; + } + } + } + + return ResultSuccess(); + } + + Result KDebug::SetFpuContext(const ams::svc::ThreadContext &ctx, KThread *thread, u32 context_flags) { + MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread()); + MESOSPHERE_ASSERT(thread != GetCurrentThreadPointer()); + + /* Succeed if there's nothing to do. */ + R_SUCCEED_IF((context_flags & (ams::svc::ThreadContextFlag_Fpu | ams::svc::ThreadContextFlag_FpuControl)) == 0); + + /* Get the thread context. */ + KThreadContext *t_ctx = std::addressof(thread->GetContext()); + + /* Set the FPU control registers, if required. */ + if ((context_flags & ams::svc::ThreadContextFlag_FpuControl) != 0) { + t_ctx->SetFpsr(ctx.fpsr); + t_ctx->SetFpcr(ctx.fpcr); + } + + /* Set the FPU registers, if required. */ + if ((context_flags & ams::svc::ThreadContextFlag_Fpu) != 0) { + static_assert(util::size(ams::svc::ThreadContext{}.v) == KThreadContext::NumFpuRegisters); + t_ctx->SetFpuRegisters(ctx.v, this->Is64Bit()); + } + + return ResultSuccess(); + } + Result KDebug::BreakIfAttached(ams::svc::BreakReason break_reason, uintptr_t address, size_t size) { return KDebugBase::OnDebugEvent(ams::svc::DebugEvent_Exception, ams::svc::DebugException_UserBreak, GetProgramCounter(GetCurrentThread()), break_reason, address, size); } diff --git a/libraries/libmesosphere/source/arch/arm64/kern_k_thread_context.cpp b/libraries/libmesosphere/source/arch/arm64/kern_k_thread_context.cpp index 3164fba9d..83b4707b4 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_k_thread_context.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_k_thread_context.cpp @@ -189,6 +189,18 @@ namespace ams::kern::arch::arm64 { this->SetFpsr(psr); } + void KThreadContext::SetFpuRegisters(const u128 *v, bool is_64_bit) { + if (is_64_bit) { + for (size_t i = 0; i < KThreadContext::NumFpuRegisters; ++i) { + this->fpu_registers[i] = v[i]; + } + } else { + for (size_t i = 0; i < KThreadContext::NumFpuRegisters / 2; ++i) { + this->fpu_registers[i] = v[i]; + } + } + } + void GetUserContext(ams::svc::ThreadContext *out, const KThread *thread) { MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread()); MESOSPHERE_ASSERT(thread->IsSuspended()); @@ -256,6 +268,9 @@ namespace ams::kern::arch::arm64 { for (size_t i = 0; i < KThreadContext::NumFpuRegisters / 2; ++i) { out->v[i] = f[i]; } + for (size_t i = KThreadContext::NumFpuRegisters / 2; i < KThreadContext::NumFpuRegisters; ++i) { + out->v[i] = 0; + } } /* Copy fpcr/fpsr. */ diff --git a/libraries/libmesosphere/source/kern_k_debug_base.cpp b/libraries/libmesosphere/source/kern_k_debug_base.cpp index 720ec4f61..93c3d044c 100644 --- a/libraries/libmesosphere/source/kern_k_debug_base.cpp +++ b/libraries/libmesosphere/source/kern_k_debug_base.cpp @@ -31,6 +31,13 @@ namespace ams::kern { this->continue_flags = 0; } + bool KDebugBase::Is64Bit() const { + MESOSPHERE_ASSERT(this->lock.IsLockedByCurrentThread()); + MESOSPHERE_ASSERT(this->process != nullptr); + return this->process->Is64Bit(); + } + + Result KDebugBase::QueryMemoryInfo(ams::svc::MemoryInfo *out_memory_info, ams::svc::PageInfo *out_page_info, KProcessAddress address) { /* Lock ourselves. */ KScopedLightLock lk(this->lock); @@ -459,6 +466,105 @@ namespace ams::kern { return ResultSuccess(); } + Result KDebugBase::GetThreadContext(ams::svc::ThreadContext *out, u64 thread_id, u32 context_flags) { + /* Lock ourselves. */ + KScopedLightLock lk(this->lock); + + /* Get the thread from its id. */ + KScopedAutoObject thread = KThread::GetThreadFromId(thread_id); + R_UNLESS(thread.IsNotNull(), svc::ResultInvalidId()); + + /* Verify that the thread is owned by our process. */ + R_UNLESS(this->process == thread->GetOwnerProcess(), svc::ResultInvalidId()); + + /* Verify that the thread isn't terminated. */ + R_UNLESS(thread->GetState() != KThread::ThreadState_Terminated, svc::ResultTerminationRequested()); + + /* Check that the thread is not the current one. */ + /* NOTE: Nintendo does not check this, and thus the following loop will deadlock. */ + R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(), svc::ResultInvalidId()); + + /* Try to get the thread context until the thread isn't current on any core. */ + while (true) { + KScopedSchedulerLock sl; + + /* The thread needs to be requested for debug suspension. */ + R_UNLESS(thread->IsSuspendRequested(KThread::SuspendType_Debug), svc::ResultInvalidState()); + + /* If the thread's raw state isn't runnable, check if it's current on some core. */ + if (thread->GetRawState() != KThread::ThreadState_Runnable) { + bool current = false; + for (auto i = 0; i < static_cast(cpu::NumCores); ++i) { + if (thread.GetPointerUnsafe() == Kernel::GetCurrentContext(i).current_thread) { + current = true; + } + break; + } + + /* If the thread is current, retry until it isn't. */ + if (current) { + continue; + } + } + + /* Get the thread context. */ + return this->GetThreadContextImpl(out, thread.GetPointerUnsafe(), context_flags); + } + } + + Result KDebugBase::SetThreadContext(const ams::svc::ThreadContext &ctx, u64 thread_id, u32 context_flags) { + /* Lock ourselves. */ + KScopedLightLock lk(this->lock); + + /* Get the thread from its id. */ + KScopedAutoObject thread = KThread::GetThreadFromId(thread_id); + R_UNLESS(thread.IsNotNull(), svc::ResultInvalidId()); + + /* Verify that the thread is owned by our process. */ + R_UNLESS(this->process == thread->GetOwnerProcess(), svc::ResultInvalidId()); + + /* Verify that the thread isn't terminated. */ + R_UNLESS(thread->GetState() != KThread::ThreadState_Terminated, svc::ResultTerminationRequested()); + + /* Check that the thread is not the current one. */ + /* NOTE: Nintendo does not check this, and thus the following loop will deadlock. */ + R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(), svc::ResultInvalidId()); + + /* Try to get the thread context until the thread isn't current on any core. */ + while (true) { + KScopedSchedulerLock sl; + + /* The thread needs to be requested for debug suspension. */ + R_UNLESS(thread->IsSuspendRequested(KThread::SuspendType_Debug), svc::ResultInvalidState()); + + /* If the thread's raw state isn't runnable, check if it's current on some core. */ + if (thread->GetRawState() != KThread::ThreadState_Runnable) { + bool current = false; + for (auto i = 0; i < static_cast(cpu::NumCores); ++i) { + if (thread.GetPointerUnsafe() == Kernel::GetCurrentContext(i).current_thread) { + current = true; + } + break; + } + + /* If the thread is current, retry until it isn't. */ + if (current) { + continue; + } + } + + /* Verify that the thread's svc state is valid. */ + if (thread->IsCallingSvc()) { + R_UNLESS(thread->GetSvcId() != svc::SvcId_Break, svc::ResultInvalidState()); + R_UNLESS(thread->GetSvcId() != svc::SvcId_ReturnFromException, svc::ResultInvalidState()); + } + + /* Set the thread context. */ + return this->SetThreadContextImpl(ctx, thread.GetPointerUnsafe(), context_flags); + } + } + + Result KDebugBase::ContinueDebug(const u32 flags, const u64 *thread_ids, size_t num_thread_ids) { /* Get the attached process. */ KScopedAutoObject target = this->GetProcess(); diff --git a/libraries/libmesosphere/source/svc/kern_svc_debug.cpp b/libraries/libmesosphere/source/svc/kern_svc_debug.cpp index 6df5aa244..846b749f6 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_debug.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_debug.cpp @@ -163,6 +163,45 @@ namespace ams::kern::svc { return ResultSuccess(); } + Result GetDebugThreadContext(KUserPointer out_context, ams::svc::Handle debug_handle, uint64_t thread_id, uint32_t context_flags) { + /* Validate the context flags. */ + R_UNLESS((context_flags | ams::svc::ThreadContextFlag_All) == ams::svc::ThreadContextFlag_All, svc::ResultInvalidEnumValue()); + + /* Get the debug object. */ + KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject(debug_handle); + R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle()); + + /* Get the thread context. */ + ams::svc::ThreadContext context = {}; + R_TRY(debug->GetThreadContext(std::addressof(context), thread_id, context_flags)); + + /* Copy the context to userspace. */ + R_TRY(out_context.CopyFrom(std::addressof(context))); + + return ResultSuccess(); + } + + Result SetDebugThreadContext(ams::svc::Handle debug_handle, uint64_t thread_id, KUserPointer user_context, uint32_t context_flags) { + /* Only allow invoking the svc on development hardware. */ + R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultNotImplemented()); + + /* Validate the context flags. */ + R_UNLESS((context_flags | ams::svc::ThreadContextFlag_All) == ams::svc::ThreadContextFlag_All, svc::ResultInvalidEnumValue()); + + /* Copy the thread context from userspace. */ + ams::svc::ThreadContext context; + R_TRY(user_context.CopyTo(std::addressof(context))); + + /* Get the debug object. */ + KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject(debug_handle); + R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle()); + + /* Set the thread context. */ + R_TRY(debug->SetThreadContext(context, thread_id, context_flags)); + + return ResultSuccess(); + } + Result QueryDebugProcessMemory(ams::svc::MemoryInfo *out_memory_info, ams::svc::PageInfo *out_page_info, ams::svc::Handle debug_handle, uintptr_t address) { /* Get the debug object. */ KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject(debug_handle); @@ -374,11 +413,11 @@ namespace ams::kern::svc { } Result GetDebugThreadContext64(KUserPointer out_context, ams::svc::Handle debug_handle, uint64_t thread_id, uint32_t context_flags) { - MESOSPHERE_PANIC("Stubbed SvcGetDebugThreadContext64 was called."); + return GetDebugThreadContext(out_context, debug_handle, thread_id, context_flags); } Result SetDebugThreadContext64(ams::svc::Handle debug_handle, uint64_t thread_id, KUserPointer context, uint32_t context_flags) { - MESOSPHERE_PANIC("Stubbed SvcSetDebugThreadContext64 was called."); + return SetDebugThreadContext(debug_handle, thread_id, context, context_flags); } Result QueryDebugProcessMemory64(KUserPointer out_memory_info, ams::svc::PageInfo *out_page_info, ams::svc::Handle debug_handle, ams::svc::Address address) { @@ -428,11 +467,11 @@ namespace ams::kern::svc { } Result GetDebugThreadContext64From32(KUserPointer out_context, ams::svc::Handle debug_handle, uint64_t thread_id, uint32_t context_flags) { - MESOSPHERE_PANIC("Stubbed SvcGetDebugThreadContext64From32 was called."); + return GetDebugThreadContext(out_context, debug_handle, thread_id, context_flags); } Result SetDebugThreadContext64From32(ams::svc::Handle debug_handle, uint64_t thread_id, KUserPointer context, uint32_t context_flags) { - MESOSPHERE_PANIC("Stubbed SvcSetDebugThreadContext64From32 was called."); + return SetDebugThreadContext(debug_handle, thread_id, context, context_flags); } Result QueryDebugProcessMemory64From32(KUserPointer out_memory_info, ams::svc::PageInfo *out_page_info, ams::svc::Handle debug_handle, ams::svc::Address address) { diff --git a/libraries/libvapours/include/vapours/results/svc_results.hpp b/libraries/libvapours/include/vapours/results/svc_results.hpp index d07d50e5e..e73beb865 100644 --- a/libraries/libvapours/include/vapours/results/svc_results.hpp +++ b/libraries/libvapours/include/vapours/results/svc_results.hpp @@ -75,6 +75,7 @@ namespace ams::svc { R_DEFINE_ERROR_RESULT(InvalidProcessId, 517); R_DEFINE_ERROR_RESULT(InvalidThreadId, 518); + R_DEFINE_ERROR_RESULT(InvalidId, 519); R_DEFINE_ERROR_RESULT(ProcessTerminated, 520); }