mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-12-18 08:22:04 +00:00
kern: Svc(Legacy)ContinueDebugEvent
This commit is contained in:
parent
3289b45408
commit
b143f1e05f
9 changed files with 200 additions and 6 deletions
|
@ -36,9 +36,10 @@ namespace ams::kern {
|
||||||
virtual ~KDebugBase() { /* ... */ }
|
virtual ~KDebugBase() { /* ... */ }
|
||||||
public:
|
public:
|
||||||
void Initialize();
|
void Initialize();
|
||||||
|
|
||||||
Result Attach(KProcess *process);
|
Result Attach(KProcess *process);
|
||||||
|
|
||||||
KScopedAutoObject<KProcess> GetProcess();
|
Result ContinueDebug(const u32 flags, const u64 *thread_ids, size_t num_thread_ids);
|
||||||
|
|
||||||
Result QueryMemoryInfo(ams::svc::MemoryInfo *out_memory_info, ams::svc::PageInfo *out_page_info, KProcessAddress address);
|
Result QueryMemoryInfo(ams::svc::MemoryInfo *out_memory_info, ams::svc::PageInfo *out_page_info, KProcessAddress address);
|
||||||
Result ReadMemory(KProcessAddress buffer, KProcessAddress address, size_t size);
|
Result ReadMemory(KProcessAddress buffer, KProcessAddress address, size_t size);
|
||||||
|
@ -49,6 +50,8 @@ namespace ams::kern {
|
||||||
Result GetDebugEventInfo(ams::svc::lp64::DebugEventInfo *out);
|
Result GetDebugEventInfo(ams::svc::lp64::DebugEventInfo *out);
|
||||||
Result GetDebugEventInfo(ams::svc::ilp32::DebugEventInfo *out);
|
Result GetDebugEventInfo(ams::svc::ilp32::DebugEventInfo *out);
|
||||||
|
|
||||||
|
KScopedAutoObject<KProcess> GetProcess();
|
||||||
|
|
||||||
/* TODO: This is a placeholder definition. */
|
/* TODO: This is a placeholder definition. */
|
||||||
private:
|
private:
|
||||||
void PushDebugEvent(ams::svc::DebugEvent event, uintptr_t param0 = 0, uintptr_t param1 = 0, uintptr_t param2 = 0, uintptr_t param3 = 0, uintptr_t param4 = 0);
|
void PushDebugEvent(ams::svc::DebugEvent event, uintptr_t param0 = 0, uintptr_t param1 = 0, uintptr_t param2 = 0, uintptr_t param3 = 0, uintptr_t param4 = 0);
|
||||||
|
|
|
@ -194,6 +194,7 @@ namespace ams::kern {
|
||||||
void ClearDebugObject(KProcess::State state);
|
void ClearDebugObject(KProcess::State state);
|
||||||
|
|
||||||
KEventInfo *GetJitDebugInfo();
|
KEventInfo *GetJitDebugInfo();
|
||||||
|
void ClearJitDebugInfo();
|
||||||
|
|
||||||
bool EnterUserException();
|
bool EnterUserException();
|
||||||
bool LeaveUserException();
|
bool LeaveUserException();
|
||||||
|
@ -296,6 +297,18 @@ namespace ams::kern {
|
||||||
|
|
||||||
Result Reset();
|
Result Reset();
|
||||||
|
|
||||||
|
void SetDebugBreak() {
|
||||||
|
if (this->state == State_RunningAttached) {
|
||||||
|
this->ChangeState(State_DebugBreak);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetAttached() {
|
||||||
|
if (this->state == State_DebugBreak) {
|
||||||
|
this->ChangeState(State_RunningAttached);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Result SetActivity(ams::svc::ProcessActivity activity);
|
Result SetActivity(ams::svc::ProcessActivity activity);
|
||||||
|
|
||||||
void PinCurrentThread();
|
void PinCurrentThread();
|
||||||
|
|
|
@ -402,6 +402,16 @@ namespace ams::kern {
|
||||||
return this->wait_result;
|
return this->wait_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr void SetDebugExceptionResult(Result result) {
|
||||||
|
MESOSPHERE_ASSERT_THIS();
|
||||||
|
this->debug_exception_result = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Result GetDebugExceptionResult() const {
|
||||||
|
MESOSPHERE_ASSERT_THIS();
|
||||||
|
return this->debug_exception_result;
|
||||||
|
}
|
||||||
|
|
||||||
void WaitCancel();
|
void WaitCancel();
|
||||||
|
|
||||||
bool IsWaitCancelled() const { return this->wait_cancelled; }
|
bool IsWaitCancelled() const { return this->wait_cancelled; }
|
||||||
|
|
|
@ -24,6 +24,8 @@ namespace ams::kern::svc {
|
||||||
|
|
||||||
/* 33 */ using ::ams::svc::ResultNotImplemented;
|
/* 33 */ using ::ams::svc::ResultNotImplemented;
|
||||||
|
|
||||||
|
/* 54 */ using ::ams::svc::ResultStopProcessingException;
|
||||||
|
|
||||||
/* 57 */ using ::ams::svc::ResultNoSynchronizationObject;
|
/* 57 */ using ::ams::svc::ResultNoSynchronizationObject;
|
||||||
|
|
||||||
/* 59 */ using ::ams::svc::ResultTerminationRequested;
|
/* 59 */ using ::ams::svc::ResultTerminationRequested;
|
||||||
|
|
|
@ -341,6 +341,95 @@ namespace ams::kern {
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result KDebugBase::ContinueDebug(const u32 flags, const u64 *thread_ids, size_t num_thread_ids) {
|
||||||
|
/* Get the attached process. */
|
||||||
|
KScopedAutoObject target = this->GetProcess();
|
||||||
|
R_UNLESS(target.IsNotNull(), svc::ResultProcessTerminated());
|
||||||
|
|
||||||
|
/* Lock both ourselves, the target process, and the scheduler. */
|
||||||
|
KScopedLightLock state_lk(target->GetStateLock());
|
||||||
|
KScopedLightLock list_lk(target->GetListLock());
|
||||||
|
KScopedLightLock this_lk(this->lock);
|
||||||
|
KScopedSchedulerLock sl;
|
||||||
|
|
||||||
|
/* Check that we're still attached to the process, and that it's not terminated. */
|
||||||
|
R_UNLESS(this->process == target.GetPointerUnsafe(), svc::ResultProcessTerminated());
|
||||||
|
R_UNLESS(!target->IsTerminated(), svc::ResultProcessTerminated());
|
||||||
|
|
||||||
|
/* Check that we have no pending events. */
|
||||||
|
R_UNLESS(this->event_info_list.empty(), svc::ResultBusy());
|
||||||
|
|
||||||
|
/* Clear the target's JIT debug info. */
|
||||||
|
target->ClearJitDebugInfo();
|
||||||
|
|
||||||
|
/* Set our continue flags. */
|
||||||
|
this->continue_flags = flags;
|
||||||
|
|
||||||
|
/* Iterate over threads, continuing them as we should. */
|
||||||
|
bool has_debug_break_thread = false;
|
||||||
|
{
|
||||||
|
/* Parse our flags. */
|
||||||
|
const bool exception_handled = (this->continue_flags & ams::svc::ContinueFlag_ExceptionHandled) != 0;
|
||||||
|
const bool continue_all = (this->continue_flags & ams::svc::ContinueFlag_ContinueAll) != 0;
|
||||||
|
const bool continue_others = (this->continue_flags & ams::svc::ContinueFlag_ContinueOthers) != 0;
|
||||||
|
|
||||||
|
/* Update each thread. */
|
||||||
|
auto end = target->GetThreadList().end();
|
||||||
|
for (auto it = target->GetThreadList().begin(); it != end; ++it) {
|
||||||
|
/* Determine if we should continue the thread. */
|
||||||
|
bool should_continue;
|
||||||
|
{
|
||||||
|
if (continue_all) {
|
||||||
|
/* Continue all threads. */
|
||||||
|
should_continue = true;
|
||||||
|
} else if (continue_others) {
|
||||||
|
/* Continue the thread if it doesn't match one of our target ids. */
|
||||||
|
const u64 thread_id = it->GetId();
|
||||||
|
should_continue = true;
|
||||||
|
for (size_t i = 0; i < num_thread_ids; ++i) {
|
||||||
|
if (thread_ids[i] == thread_id) {
|
||||||
|
should_continue = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Continue the thread if it matches one of our target ids. */
|
||||||
|
const u64 thread_id = it->GetId();
|
||||||
|
should_continue = false;
|
||||||
|
for (size_t i = 0; i < num_thread_ids; ++i) {
|
||||||
|
if (thread_ids[i] == thread_id) {
|
||||||
|
should_continue = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Continue the thread if we should. */
|
||||||
|
if (should_continue) {
|
||||||
|
if (exception_handled) {
|
||||||
|
it->SetDebugExceptionResult(svc::ResultStopProcessingException());
|
||||||
|
}
|
||||||
|
it->Resume(KThread::SuspendType_Debug);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the thread has debug suspend requested, note so. */
|
||||||
|
if (it->IsSuspendRequested(KThread::SuspendType_Debug)) {
|
||||||
|
has_debug_break_thread = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the process's state. */
|
||||||
|
if (has_debug_break_thread) {
|
||||||
|
target->SetDebugBreak();
|
||||||
|
} else {
|
||||||
|
target->SetAttached();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
KEventInfo *KDebugBase::CreateDebugEvent(ams::svc::DebugEvent event, uintptr_t param0, uintptr_t param1, uintptr_t param2, uintptr_t param3, uintptr_t param4, u64 cur_thread_id) {
|
KEventInfo *KDebugBase::CreateDebugEvent(ams::svc::DebugEvent event, uintptr_t param0, uintptr_t param1, uintptr_t param2, uintptr_t param3, uintptr_t param4, u64 cur_thread_id) {
|
||||||
/* Allocate a new event. */
|
/* Allocate a new event. */
|
||||||
KEventInfo *info = KEventInfo::Allocate();
|
KEventInfo *info = KEventInfo::Allocate();
|
||||||
|
@ -350,7 +439,7 @@ namespace ams::kern {
|
||||||
/* Set common fields. */
|
/* Set common fields. */
|
||||||
info->event = event;
|
info->event = event;
|
||||||
info->thread_id = 0;
|
info->thread_id = 0;
|
||||||
info->flags = 1; /* TODO: enum this in ams::svc */
|
info->flags = ams::svc::DebugEventFlag_Stopped;
|
||||||
|
|
||||||
/* Set event specific fields. */
|
/* Set event specific fields. */
|
||||||
switch (event) {
|
switch (event) {
|
||||||
|
|
|
@ -1000,6 +1000,9 @@ namespace ams::kern {
|
||||||
}
|
}
|
||||||
|
|
||||||
KEventInfo *KProcess::GetJitDebugInfo() {
|
KEventInfo *KProcess::GetJitDebugInfo() {
|
||||||
|
MESOSPHERE_ASSERT_THIS();
|
||||||
|
MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread());
|
||||||
|
|
||||||
if (this->is_jit_debug) {
|
if (this->is_jit_debug) {
|
||||||
return KDebugBase::CreateDebugEvent(this->jit_debug_event_type, this->jit_debug_exception_type, this->jit_debug_params[0], this->jit_debug_params[1], this->jit_debug_params[2], this->jit_debug_params[3], this->jit_debug_thread_id);
|
return KDebugBase::CreateDebugEvent(this->jit_debug_event_type, this->jit_debug_exception_type, this->jit_debug_params[0], this->jit_debug_params[1], this->jit_debug_params[2], this->jit_debug_params[3], this->jit_debug_thread_id);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1007,6 +1010,13 @@ namespace ams::kern {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void KProcess::ClearJitDebugInfo() {
|
||||||
|
MESOSPHERE_ASSERT_THIS();
|
||||||
|
MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread());
|
||||||
|
|
||||||
|
this->is_jit_debug = false;
|
||||||
|
}
|
||||||
|
|
||||||
KProcess *KProcess::GetProcessFromId(u64 process_id) {
|
KProcess *KProcess::GetProcessFromId(u64 process_id) {
|
||||||
/* Lock the list. */
|
/* Lock the list. */
|
||||||
KProcess::ListAccessor accessor;
|
KProcess::ListAccessor accessor;
|
||||||
|
|
|
@ -21,6 +21,8 @@ namespace ams::kern::svc {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
constexpr inline int32_t MaximumDebuggableThreadCount = 0x60;
|
||||||
|
|
||||||
Result DebugActiveProcess(ams::svc::Handle *out_handle, uint64_t process_id) {
|
Result DebugActiveProcess(ams::svc::Handle *out_handle, uint64_t process_id) {
|
||||||
/* Get the process from its id. */
|
/* Get the process from its id. */
|
||||||
KProcess *process = KProcess::GetProcessFromId(process_id);
|
KProcess *process = KProcess::GetProcessFromId(process_id);
|
||||||
|
@ -79,6 +81,60 @@ namespace ams::kern::svc {
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result ContinueDebugEventImpl(ams::svc::Handle debug_handle, uint32_t flags, const uint64_t *thread_ids, int32_t num_thread_ids) {
|
||||||
|
/* Get the debug object. */
|
||||||
|
KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject<KDebug>(debug_handle);
|
||||||
|
R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle());
|
||||||
|
|
||||||
|
/* Continue the event. */
|
||||||
|
R_TRY(debug->ContinueDebug(flags, thread_ids, num_thread_ids));
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ContinueDebugEvent(ams::svc::Handle debug_handle, uint32_t flags, KUserPointer<const uint64_t *> user_thread_ids, int32_t num_thread_ids) {
|
||||||
|
/* Only allow invoking the svc on development hardware. */
|
||||||
|
R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultNotImplemented());
|
||||||
|
|
||||||
|
/* Verify that the flags are valid. */
|
||||||
|
R_UNLESS((flags | ams::svc::ContinueFlag_AllMask) == ams::svc::ContinueFlag_AllMask, svc::ResultInvalidEnumValue());
|
||||||
|
|
||||||
|
/* Verify that continue all and continue others flags are exclusive. */
|
||||||
|
constexpr u32 AllAndOthersMask = ams::svc::ContinueFlag_ContinueAll | ams::svc::ContinueFlag_ContinueOthers;
|
||||||
|
R_UNLESS((flags & AllAndOthersMask) != AllAndOthersMask, svc::ResultInvalidEnumValue());
|
||||||
|
|
||||||
|
/* Verify that the number of thread ids is valid. */
|
||||||
|
R_UNLESS((0 <= num_thread_ids && num_thread_ids <= MaximumDebuggableThreadCount), svc::ResultOutOfRange());
|
||||||
|
|
||||||
|
/* Copy the threads from userspace. */
|
||||||
|
uint64_t thread_ids[MaximumDebuggableThreadCount];
|
||||||
|
if (num_thread_ids > 0) {
|
||||||
|
R_TRY(user_thread_ids.CopyArrayTo(thread_ids, num_thread_ids));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Continue the event. */
|
||||||
|
R_TRY(ContinueDebugEventImpl(debug_handle, flags, thread_ids, num_thread_ids));
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result LegacyContinueDebugEvent(ams::svc::Handle debug_handle, uint32_t flags, uint64_t thread_id) {
|
||||||
|
/* Only allow invoking the svc on development hardware. */
|
||||||
|
R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultNotImplemented());
|
||||||
|
|
||||||
|
/* Verify that the flags are valid. */
|
||||||
|
R_UNLESS((flags | ams::svc::ContinueFlag_AllMask) == ams::svc::ContinueFlag_AllMask, svc::ResultInvalidEnumValue());
|
||||||
|
|
||||||
|
/* Verify that continue all and continue others flags are exclusive. */
|
||||||
|
constexpr u32 AllAndOthersMask = ams::svc::ContinueFlag_ContinueAll | ams::svc::ContinueFlag_ContinueOthers;
|
||||||
|
R_UNLESS((flags & AllAndOthersMask) != AllAndOthersMask, svc::ResultInvalidEnumValue());
|
||||||
|
|
||||||
|
/* Continue the event. */
|
||||||
|
R_TRY(ContinueDebugEventImpl(debug_handle, flags, std::addressof(thread_id), 1));
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
Result QueryDebugProcessMemory(ams::svc::MemoryInfo *out_memory_info, ams::svc::PageInfo *out_page_info, ams::svc::Handle debug_handle, uintptr_t address) {
|
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. */
|
/* Get the debug object. */
|
||||||
KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject<KDebug>(debug_handle);
|
KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject<KDebug>(debug_handle);
|
||||||
|
@ -173,11 +229,11 @@ namespace ams::kern::svc {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ContinueDebugEvent64(ams::svc::Handle debug_handle, uint32_t flags, KUserPointer<const uint64_t *> thread_ids, int32_t num_thread_ids) {
|
Result ContinueDebugEvent64(ams::svc::Handle debug_handle, uint32_t flags, KUserPointer<const uint64_t *> thread_ids, int32_t num_thread_ids) {
|
||||||
MESOSPHERE_PANIC("Stubbed SvcContinueDebugEvent64 was called.");
|
return ContinueDebugEvent(debug_handle, flags, thread_ids, num_thread_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result LegacyContinueDebugEvent64(ams::svc::Handle debug_handle, uint32_t flags, uint64_t thread_id) {
|
Result LegacyContinueDebugEvent64(ams::svc::Handle debug_handle, uint32_t flags, uint64_t thread_id) {
|
||||||
MESOSPHERE_PANIC("Stubbed SvcLegacyContinueDebugEvent64 was called.");
|
return LegacyContinueDebugEvent(debug_handle, flags, thread_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetDebugThreadContext64(KUserPointer<ams::svc::ThreadContext *> out_context, ams::svc::Handle debug_handle, uint64_t thread_id, uint32_t context_flags) {
|
Result GetDebugThreadContext64(KUserPointer<ams::svc::ThreadContext *> out_context, ams::svc::Handle debug_handle, uint64_t thread_id, uint32_t context_flags) {
|
||||||
|
@ -227,11 +283,11 @@ namespace ams::kern::svc {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ContinueDebugEvent64From32(ams::svc::Handle debug_handle, uint32_t flags, KUserPointer<const uint64_t *> thread_ids, int32_t num_thread_ids) {
|
Result ContinueDebugEvent64From32(ams::svc::Handle debug_handle, uint32_t flags, KUserPointer<const uint64_t *> thread_ids, int32_t num_thread_ids) {
|
||||||
MESOSPHERE_PANIC("Stubbed SvcContinueDebugEvent64From32 was called.");
|
return ContinueDebugEvent(debug_handle, flags, thread_ids, num_thread_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result LegacyContinueDebugEvent64From32(ams::svc::Handle debug_handle, uint32_t flags, uint64_t thread_id) {
|
Result LegacyContinueDebugEvent64From32(ams::svc::Handle debug_handle, uint32_t flags, uint64_t thread_id) {
|
||||||
MESOSPHERE_PANIC("Stubbed SvcLegacyContinueDebugEvent64From32 was called.");
|
return LegacyContinueDebugEvent(debug_handle, flags, thread_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetDebugThreadContext64From32(KUserPointer<ams::svc::ThreadContext *> out_context, ams::svc::Handle debug_handle, uint64_t thread_id, uint32_t context_flags) {
|
Result GetDebugThreadContext64From32(KUserPointer<ams::svc::ThreadContext *> out_context, ams::svc::Handle debug_handle, uint64_t thread_id, uint32_t context_flags) {
|
||||||
|
|
|
@ -27,6 +27,8 @@ namespace ams::svc {
|
||||||
|
|
||||||
R_DEFINE_ERROR_RESULT(NotImplemented, 33);
|
R_DEFINE_ERROR_RESULT(NotImplemented, 33);
|
||||||
|
|
||||||
|
R_DEFINE_ERROR_RESULT(StopProcessingException, 54);
|
||||||
|
|
||||||
R_DEFINE_ERROR_RESULT(NoSynchronizationObject, 57);
|
R_DEFINE_ERROR_RESULT(NoSynchronizationObject, 57);
|
||||||
|
|
||||||
R_DEFINE_ERROR_RESULT(TerminationRequested, 59);
|
R_DEFINE_ERROR_RESULT(TerminationRequested, 59);
|
||||||
|
|
|
@ -292,6 +292,15 @@ namespace ams::svc {
|
||||||
ThreadContextFlag_All = (ThreadContextFlag_General | ThreadContextFlag_Control | ThreadContextFlag_Fpu | ThreadContextFlag_FpuControl),
|
ThreadContextFlag_All = (ThreadContextFlag_General | ThreadContextFlag_Control | ThreadContextFlag_Fpu | ThreadContextFlag_FpuControl),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum ContinueFlag : u32 {
|
||||||
|
ContinueFlag_ExceptionHandled = (1u << 0),
|
||||||
|
ContinueFlag_EnableExceptionEvent = (1u << 1),
|
||||||
|
ContinueFlag_ContinueAll = (1u << 2),
|
||||||
|
ContinueFlag_ContinueOthers = (1u << 3),
|
||||||
|
|
||||||
|
ContinueFlag_AllMask = (1u << 4) - 1,
|
||||||
|
};
|
||||||
|
|
||||||
enum ThreadExitReason : u32 {
|
enum ThreadExitReason : u32 {
|
||||||
ThreadExitReason_ExitThread = 0,
|
ThreadExitReason_ExitThread = 0,
|
||||||
ThreadExitReason_TerminateThread = 1,
|
ThreadExitReason_TerminateThread = 1,
|
||||||
|
|
Loading…
Reference in a new issue