mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-12-24 03:06:17 +00:00
kern SvcGetDebugThreadContext, SvcSetDebugThreadContext
This commit is contained in:
parent
3afd723b92
commit
5c4fbf5c67
10 changed files with 363 additions and 4 deletions
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
@ -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<u32>(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<u32>(e_ctx->x[11]);
|
||||
out->r[13] = static_cast<u32>(e_ctx->x[13]);
|
||||
out->r[14] = static_cast<u32>(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<u32>(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<u32>(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<u32>(ctx.r[13]);
|
||||
e_ctx->x[14] = static_cast<u32>(ctx.r[14]);
|
||||
e_ctx->x[30] = 0;
|
||||
e_ctx->sp = 0;
|
||||
e_ctx->pc = static_cast<u32>(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);
|
||||
}
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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<s32>(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<s32>(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();
|
||||
|
|
|
@ -163,6 +163,45 @@ namespace ams::kern::svc {
|
|||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GetDebugThreadContext(KUserPointer<ams::svc::ThreadContext *> 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<KDebug>(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<const ams::svc::ThreadContext *> 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<KDebug>(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<KDebug>(debug_handle);
|
||||
|
@ -374,11 +413,11 @@ namespace ams::kern::svc {
|
|||
}
|
||||
|
||||
Result GetDebugThreadContext64(KUserPointer<ams::svc::ThreadContext *> 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<const ams::svc::ThreadContext *> context, uint32_t context_flags) {
|
||||
MESOSPHERE_PANIC("Stubbed SvcSetDebugThreadContext64 was called.");
|
||||
return SetDebugThreadContext(debug_handle, thread_id, context, context_flags);
|
||||
}
|
||||
|
||||
Result QueryDebugProcessMemory64(KUserPointer<ams::svc::lp64::MemoryInfo *> 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<ams::svc::ThreadContext *> 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<const ams::svc::ThreadContext *> context, uint32_t context_flags) {
|
||||
MESOSPHERE_PANIC("Stubbed SvcSetDebugThreadContext64From32 was called.");
|
||||
return SetDebugThreadContext(debug_handle, thread_id, context, context_flags);
|
||||
}
|
||||
|
||||
Result QueryDebugProcessMemory64From32(KUserPointer<ams::svc::ilp32::MemoryInfo *> out_memory_info, ams::svc::PageInfo *out_page_info, ams::svc::Handle debug_handle, ams::svc::Address address) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue