mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +00:00
kern: implement SvcDebugActiveProcess, svcGetDebugEvent, SvcWaitProcessWideKeyAtomic
This commit is contained in:
parent
1c5b58ce66
commit
36eb78a3ce
17 changed files with 728 additions and 24 deletions
|
@ -30,6 +30,11 @@ namespace ams::kern::arch::arm64 {
|
|||
class KDebug final : public KAutoObjectWithSlabHeapAndContainer<KDebug, KDebugBase> {
|
||||
MESOSPHERE_AUTOOBJECT_TRAITS(KDebug, KSynchronizationObject);
|
||||
public:
|
||||
explicit KDebug() { /* ... */ }
|
||||
virtual ~KDebug() { /* ... */ }
|
||||
|
||||
static void PostDestroy(uintptr_t arg) { /* ... */ }
|
||||
|
||||
/* TODO: This is a placeholder definition. */
|
||||
};
|
||||
|
||||
|
|
|
@ -283,6 +283,14 @@ namespace ams::kern {
|
|||
}
|
||||
}
|
||||
|
||||
constexpr bool IsPermittedDebug() const {
|
||||
return this->debug_capabilities.Get<DebugFlags::AllowDebug>();
|
||||
}
|
||||
|
||||
constexpr bool CanForceDebug() const {
|
||||
return this->debug_capabilities.Get<DebugFlags::ForceDebug>();
|
||||
}
|
||||
|
||||
/* TODO: Member functions. */
|
||||
};
|
||||
|
||||
|
|
|
@ -16,12 +16,42 @@
|
|||
#pragma once
|
||||
#include <mesosphere/kern_common.hpp>
|
||||
#include <mesosphere/kern_k_synchronization_object.hpp>
|
||||
#include <mesosphere/kern_k_process.hpp>
|
||||
#include <mesosphere/kern_k_event_info.hpp>
|
||||
#include <mesosphere/kern_k_light_lock.hpp>
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
class KDebugBase : public KSynchronizationObject {
|
||||
protected:
|
||||
using DebugEventList = util::IntrusiveListBaseTraits<KEventInfo>::ListType;
|
||||
private:
|
||||
DebugEventList event_info_list;
|
||||
u32 continue_flags;
|
||||
KProcess *process;
|
||||
KLightLock lock;
|
||||
KProcess::State old_process_state;
|
||||
public:
|
||||
explicit KDebugBase() { /* ... */ }
|
||||
virtual ~KDebugBase() { /* ... */ }
|
||||
public:
|
||||
void Initialize();
|
||||
Result Attach(KProcess *process);
|
||||
|
||||
Result GetDebugEventInfo(ams::svc::lp64::DebugEventInfo *out);
|
||||
Result GetDebugEventInfo(ams::svc::ilp32::DebugEventInfo *out);
|
||||
|
||||
/* TODO: This is a placeholder definition. */
|
||||
private:
|
||||
KScopedAutoObject<KProcess> GetProcess();
|
||||
|
||||
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 EnqueueDebugEventInfo(KEventInfo *info);
|
||||
public:
|
||||
virtual void OnFinalizeSynchronizationObject() override;
|
||||
virtual bool IsSignaled() const override;
|
||||
public:
|
||||
static KEventInfo *CreateDebugEvent(ams::svc::DebugEvent event, uintptr_t param0, uintptr_t param1, uintptr_t param2, uintptr_t param3, uintptr_t param4, u64 thread_id);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -21,7 +21,50 @@ namespace ams::kern {
|
|||
|
||||
class KEventInfo : public KSlabAllocated<KEventInfo>, public util::IntrusiveListBaseNode<KEventInfo> {
|
||||
public:
|
||||
/* TODO: This is a placeholder definition. */
|
||||
struct InfoCreateThread {
|
||||
u32 thread_id;
|
||||
uintptr_t tls_address;
|
||||
uintptr_t entrypoint;
|
||||
};
|
||||
|
||||
struct InfoExitProcess {
|
||||
ams::svc::ProcessExitReason reason;
|
||||
};
|
||||
|
||||
struct InfoExitThread {
|
||||
ams::svc::ThreadExitReason reason;
|
||||
};
|
||||
|
||||
struct InfoException {
|
||||
ams::svc::DebugException exception_type;
|
||||
s32 exception_data_count;
|
||||
uintptr_t exception_address;
|
||||
uintptr_t exception_data[4];
|
||||
};
|
||||
|
||||
struct InfoSystemCall {
|
||||
s64 tick;
|
||||
s32 id;
|
||||
};
|
||||
public:
|
||||
ams::svc::DebugEvent event;
|
||||
u32 thread_id;
|
||||
u32 flags;
|
||||
bool is_attached;
|
||||
bool continue_flag;
|
||||
bool ignore_continue;
|
||||
bool close_once;
|
||||
union {
|
||||
InfoCreateThread create_thread;
|
||||
InfoExitProcess exit_process;
|
||||
InfoExitThread exit_thread;
|
||||
InfoException exception;
|
||||
InfoSystemCall system_call;
|
||||
} info;
|
||||
KThread *debug_thread;
|
||||
public:
|
||||
explicit KEventInfo() : is_attached(), continue_flag(), ignore_continue() { /* ... */ }
|
||||
~KEventInfo() { /* ... */ }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -131,6 +131,8 @@ namespace ams::kern {
|
|||
|
||||
constexpr u64 GetProcessId() const { return this->process_id; }
|
||||
|
||||
constexpr State GetState() const { return this->state; }
|
||||
|
||||
constexpr u64 GetCoreMask() const { return this->capabilities.GetCoreMask(); }
|
||||
constexpr u64 GetPriorityMask() const { return this->capabilities.GetPriorityMask(); }
|
||||
|
||||
|
@ -139,6 +141,8 @@ namespace ams::kern {
|
|||
|
||||
constexpr bool CheckThreadPriority(s32 prio) const { return ((1ul << prio) & this->GetPriorityMask()) != 0; }
|
||||
|
||||
constexpr u32 GetCreateProcessFlags() const { return this->flags; }
|
||||
|
||||
constexpr bool Is64Bit() const { return this->flags & ams::svc::CreateProcessFlag_Is64Bit; }
|
||||
|
||||
constexpr KProcessAddress GetEntryPoint() const { return this->code_address; }
|
||||
|
@ -147,10 +151,32 @@ namespace ams::kern {
|
|||
return this->is_suspended;
|
||||
}
|
||||
|
||||
constexpr bool IsTerminated() const {
|
||||
return this->state == State_Terminated;
|
||||
}
|
||||
|
||||
constexpr bool IsAttachedToDebugger() const {
|
||||
return this->attached_object != nullptr;
|
||||
}
|
||||
|
||||
constexpr bool IsPermittedInterrupt(int32_t interrupt_id) const {
|
||||
return this->capabilities.IsPermittedInterrupt(interrupt_id);
|
||||
}
|
||||
|
||||
constexpr bool IsPermittedDebug() const {
|
||||
return this->capabilities.IsPermittedDebug();
|
||||
}
|
||||
|
||||
constexpr bool CanForceDebug() const {
|
||||
return this->capabilities.CanForceDebug();
|
||||
}
|
||||
|
||||
ThreadList &GetThreadList() { return this->thread_list; }
|
||||
const ThreadList &GetThreadList() const { return this->thread_list; }
|
||||
|
||||
KProcess::State SetDebugObject(void *debug_object);
|
||||
KEventInfo *GetJitDebugInfo();
|
||||
|
||||
bool EnterUserException();
|
||||
bool LeaveUserException();
|
||||
bool ReleaseUserException(KThread *thread);
|
||||
|
|
|
@ -311,10 +311,21 @@ namespace ams::kern {
|
|||
this->priority = priority;
|
||||
}
|
||||
|
||||
constexpr void ClearConditionVariableTree() {
|
||||
constexpr void SetConditionVariable(ConditionVariableThreadTree *tree, KProcessAddress address, uintptr_t cv_key, u32 value) {
|
||||
this->condvar_tree = tree;
|
||||
this->condvar_key = cv_key;
|
||||
this->address_key = address;
|
||||
this->address_key_value = value;
|
||||
}
|
||||
|
||||
constexpr void ClearConditionVariable() {
|
||||
this->condvar_tree = nullptr;
|
||||
}
|
||||
|
||||
constexpr bool IsWaitingForConditionVariable() const {
|
||||
return this->condvar_tree != nullptr;
|
||||
}
|
||||
|
||||
constexpr void SetupForAddressArbiterCompare(uintptr_t address, int priority) {
|
||||
this->condvar_key = address;
|
||||
this->priority = priority;
|
||||
|
@ -394,6 +405,8 @@ namespace ams::kern {
|
|||
constexpr KProcess *GetOwnerProcess() const { return this->parent; }
|
||||
constexpr bool IsUserThread() const { return this->parent != nullptr; }
|
||||
|
||||
constexpr uintptr_t GetEntrypoint() const { return this->entrypoint; }
|
||||
|
||||
constexpr KProcessAddress GetThreadLocalRegionAddress() const { return this->tls_address; }
|
||||
constexpr void *GetThreadLocalRegionHeapAddress() const { return this->tls_heap_address; }
|
||||
|
||||
|
@ -403,6 +416,9 @@ namespace ams::kern {
|
|||
constexpr u16 GetUserPreemptionState() const { return *GetPointer<u16>(this->tls_address + 0x100); }
|
||||
constexpr void SetKernelPreemptionState(u16 state) const { *GetPointer<u16>(this->tls_address + 0x100 + sizeof(u16)) = state; }
|
||||
|
||||
constexpr void SetDebugAttached() { this->debug_attached = true; }
|
||||
constexpr bool IsAttachedToDebugger() const { return this->debug_attached; }
|
||||
|
||||
void AddCpuTime(s64 amount) {
|
||||
this->cpu_time += amount;
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ namespace ams::kern::svc {
|
|||
/* 259 */ using ::ams::svc::ResultOutOfAddressSpace;
|
||||
/* 260 */ using ::ams::svc::ResultMessageTooLarge;
|
||||
|
||||
/* 517 */ using ::ams::svc::ResultInvalidProcessId;
|
||||
/* 520 */ using ::ams::svc::ResultProcessTerminated;
|
||||
|
||||
}
|
||||
|
|
|
@ -146,7 +146,7 @@ namespace ams::kern {
|
|||
}
|
||||
|
||||
it = this->tree.erase(it);
|
||||
target_thread->ClearConditionVariableTree();
|
||||
target_thread->ClearConditionVariable();
|
||||
++num_waiters;
|
||||
}
|
||||
}
|
||||
|
@ -168,7 +168,83 @@ namespace ams::kern {
|
|||
}
|
||||
|
||||
Result KConditionVariable::Wait(KProcessAddress addr, uintptr_t key, u32 value, s64 timeout) {
|
||||
MESOSPHERE_UNIMPLEMENTED();
|
||||
/* Prepare to wait. */
|
||||
KThread *cur_thread = GetCurrentThreadPointer();
|
||||
KHardwareTimer *timer;
|
||||
|
||||
{
|
||||
KScopedSchedulerLockAndSleep slp(std::addressof(timer), cur_thread, timeout);
|
||||
|
||||
/* Set the synced object. */
|
||||
cur_thread->SetSyncedObject(nullptr, ams::svc::ResultTimedOut());
|
||||
|
||||
/* Check that the thread isn't terminating. */
|
||||
if (cur_thread->IsTerminationRequested()) {
|
||||
slp.CancelSleep();
|
||||
return svc::ResultTerminationRequested();
|
||||
}
|
||||
|
||||
/* Update the value and process for the next owner. */
|
||||
{
|
||||
/* Remove waiter thread. */
|
||||
s32 num_waiters;
|
||||
KThread *next_owner_thread = cur_thread->RemoveWaiterByKey(std::addressof(num_waiters), GetInteger(addr));
|
||||
|
||||
/* Update for the next owner thread. */
|
||||
u32 next_value = 0;
|
||||
if (next_owner_thread != nullptr) {
|
||||
/* Get the next tag value. */
|
||||
next_value = next_owner_thread->GetAddressKeyValue();
|
||||
if (num_waiters > 1) {
|
||||
next_value |= ams::svc::HandleWaitMask;
|
||||
}
|
||||
|
||||
/* Wake up the next owner. */
|
||||
next_owner_thread->SetSyncedObject(nullptr, ResultSuccess());
|
||||
next_owner_thread->Wakeup();
|
||||
}
|
||||
|
||||
/* Write the value to userspace. */
|
||||
if (!WriteToUser(addr, std::addressof(next_value))) {
|
||||
slp.CancelSleep();
|
||||
return svc::ResultInvalidCurrentMemory();
|
||||
}
|
||||
}
|
||||
|
||||
/* Update condition variable tracking. */
|
||||
{
|
||||
cur_thread->SetConditionVariable(std::addressof(this->tree), addr, key, value);
|
||||
this->tree.insert(*cur_thread);
|
||||
}
|
||||
|
||||
/* If the timeout is non-zero, set the thread as waiting. */
|
||||
if (timeout != 0) {
|
||||
cur_thread->SetState(KThread::ThreadState_Waiting);
|
||||
}
|
||||
}
|
||||
|
||||
/* Cancel the timer wait. */
|
||||
if (timer != nullptr) {
|
||||
timer->CancelTask(cur_thread);
|
||||
}
|
||||
|
||||
/* Remove from the condition variable. */
|
||||
{
|
||||
KScopedSchedulerLock sl;
|
||||
|
||||
if (KThread *owner = cur_thread->GetLockOwner(); owner != nullptr) {
|
||||
owner->RemoveWaiter(cur_thread);
|
||||
}
|
||||
|
||||
if (cur_thread->IsWaitingForConditionVariable()) {
|
||||
this->tree.erase(this->tree.iterator_to(*cur_thread));
|
||||
cur_thread->ClearConditionVariable();
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the result. */
|
||||
KSynchronizationObject *dummy;
|
||||
return cur_thread->GetWaitResult(std::addressof(dummy));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
370
libraries/libmesosphere/source/kern_k_debug_base.cpp
Normal file
370
libraries/libmesosphere/source/kern_k_debug_base.cpp
Normal file
|
@ -0,0 +1,370 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
void KDebugBase::Initialize() {
|
||||
/* Clear the process and continue flags. */
|
||||
this->process = nullptr;
|
||||
this->continue_flags = 0;
|
||||
}
|
||||
|
||||
Result KDebugBase::Attach(KProcess *target) {
|
||||
/* Check that the process isn't null. */
|
||||
MESOSPHERE_ASSERT(target != nullptr);
|
||||
|
||||
/* Attach to the process. */
|
||||
{
|
||||
/* 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 the process isn't already being debugged. */
|
||||
R_UNLESS(!target->IsAttachedToDebugger(), svc::ResultBusy());
|
||||
|
||||
{
|
||||
/* Ensure the process is in a state that allows for debugging. */
|
||||
const KProcess::State state = target->GetState();
|
||||
switch (state) {
|
||||
case KProcess::State_Created:
|
||||
case KProcess::State_Running:
|
||||
case KProcess::State_Crashed:
|
||||
break;
|
||||
case KProcess::State_CreatedAttached:
|
||||
case KProcess::State_RunningAttached:
|
||||
case KProcess::State_DebugBreak:
|
||||
return svc::ResultBusy();
|
||||
case KProcess::State_Terminating:
|
||||
case KProcess::State_Terminated:
|
||||
return svc::ResultProcessTerminated();
|
||||
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
/* Set our process member, and open a reference to the target. */
|
||||
this->process = target;
|
||||
this->process->Open();
|
||||
|
||||
/* Set ourselves as the process's attached object. */
|
||||
this->old_process_state = this->process->SetDebugObject(this);
|
||||
|
||||
/* Send an event for our attaching to the process. */
|
||||
this->PushDebugEvent(ams::svc::DebugEvent_AttachProcess);
|
||||
|
||||
/* Send events for attaching to each thread in the process. */
|
||||
{
|
||||
auto end = this->process->GetThreadList().end();
|
||||
for (auto it = this->process->GetThreadList().begin(); it != end; ++it) {
|
||||
/* Request that we suspend the thread. */
|
||||
it->RequestSuspend(KThread::SuspendType_Debug);
|
||||
|
||||
/* If the thread is in a state for us to do so, generate the event. */
|
||||
if (const auto thread_state = it->GetState(); thread_state == KThread::ThreadState_Runnable || thread_state == KThread::ThreadState_Waiting) {
|
||||
/* Mark the thread as attached to. */
|
||||
it->SetDebugAttached();
|
||||
|
||||
/* Send the event. */
|
||||
this->PushDebugEvent(ams::svc::DebugEvent_AttachThread, it->GetId(), GetInteger(it->GetThreadLocalRegionAddress()), it->GetEntrypoint());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Send the process's jit debug info, if relevant. */
|
||||
if (KEventInfo *jit_info = this->process->GetJitDebugInfo(); jit_info != nullptr) {
|
||||
this->EnqueueDebugEventInfo(jit_info);
|
||||
}
|
||||
|
||||
/* Send an exception event to represent our attaching. */
|
||||
this->PushDebugEvent(ams::svc::DebugEvent_Exception, ams::svc::DebugException_DebuggerAttached);
|
||||
|
||||
/* Signal. */
|
||||
this->NotifyAvailable();
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
/* Allocate a new event. */
|
||||
KEventInfo *info = KEventInfo::Allocate();
|
||||
|
||||
/* Populate the event info. */
|
||||
if (info != nullptr) {
|
||||
/* Set common fields. */
|
||||
info->event = event;
|
||||
info->thread_id = 0;
|
||||
info->flags = 1; /* TODO: enum this in ams::svc */
|
||||
|
||||
/* Set event specific fields. */
|
||||
switch (event) {
|
||||
case ams::svc::DebugEvent_AttachProcess:
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugEvent_AttachThread:
|
||||
{
|
||||
/* Set the thread id. */
|
||||
info->thread_id = param0;
|
||||
|
||||
/* Set the thread creation info. */
|
||||
info->info.create_thread.thread_id = param0;
|
||||
info->info.create_thread.tls_address = param1;
|
||||
info->info.create_thread.entrypoint = param2;
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugEvent_ExitProcess:
|
||||
{
|
||||
/* Set the exit reason. */
|
||||
info->info.exit_process.reason = static_cast<ams::svc::ProcessExitReason>(param0);
|
||||
|
||||
/* Clear the thread id and flags. */
|
||||
info->thread_id = 0;
|
||||
info->flags = 0 /* TODO: enum this in ams::svc */;
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugEvent_ExitThread:
|
||||
{
|
||||
/* Set the thread id. */
|
||||
info->thread_id = param0;
|
||||
|
||||
/* Set the exit reason. */
|
||||
info->info.exit_thread.reason = static_cast<ams::svc::ThreadExitReason>(param1);
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugEvent_Exception:
|
||||
{
|
||||
/* Set the thread id. */
|
||||
info->thread_id = cur_thread_id;
|
||||
|
||||
/* Set the exception type, and clear the count. */
|
||||
info->info.exception.exception_type = static_cast<ams::svc::DebugException>(param0);
|
||||
info->info.exception.exception_data_count = 0;
|
||||
switch (static_cast<ams::svc::DebugException>(param0)) {
|
||||
case ams::svc::DebugException_UndefinedInstruction:
|
||||
case ams::svc::DebugException_BreakPoint:
|
||||
case ams::svc::DebugException_UndefinedSystemCall:
|
||||
{
|
||||
info->info.exception.exception_address = param1;
|
||||
|
||||
info->info.exception.exception_data_count = 1;
|
||||
info->info.exception.exception_data[0] = param2;
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugException_DebuggerAttached:
|
||||
{
|
||||
info->thread_id = 0;
|
||||
|
||||
info->info.exception.exception_address = 0;
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugException_UserBreak:
|
||||
{
|
||||
info->info.exception.exception_address = param1;
|
||||
|
||||
info->info.exception.exception_data_count = 3;
|
||||
info->info.exception.exception_data[0] = param2;
|
||||
info->info.exception.exception_data[1] = param3;
|
||||
info->info.exception.exception_data[2] = param4;
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugException_DebuggerBreak:
|
||||
{
|
||||
info->thread_id = 0;
|
||||
|
||||
info->info.exception.exception_address = 0;
|
||||
|
||||
info->info.exception.exception_data_count = 4;
|
||||
info->info.exception.exception_data[0] = param1;
|
||||
info->info.exception.exception_data[1] = param2;
|
||||
info->info.exception.exception_data[2] = param3;
|
||||
info->info.exception.exception_data[3] = param4;
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugException_MemorySystemError:
|
||||
{
|
||||
info->info.exception.exception_address = 0;
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugException_InstructionAbort:
|
||||
case ams::svc::DebugException_DataAbort:
|
||||
case ams::svc::DebugException_AlignmentFault:
|
||||
default:
|
||||
{
|
||||
info->info.exception.exception_address = param1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
void KDebugBase::PushDebugEvent(ams::svc::DebugEvent event, uintptr_t param0, uintptr_t param1, uintptr_t param2, uintptr_t param3, uintptr_t param4) {
|
||||
/* Create and enqueue and event. */
|
||||
if (KEventInfo *new_info = CreateDebugEvent(event, param0, param1, param2, param3, param4, GetCurrentThread().GetId()); new_info != nullptr) {
|
||||
this->EnqueueDebugEventInfo(new_info);
|
||||
}
|
||||
}
|
||||
|
||||
void KDebugBase::EnqueueDebugEventInfo(KEventInfo *info) {
|
||||
/* Lock the scheduler. */
|
||||
KScopedSchedulerLock sl;
|
||||
|
||||
/* Push the event to the back of the list. */
|
||||
this->event_info_list.push_back(*info);
|
||||
}
|
||||
|
||||
|
||||
KScopedAutoObject<KProcess> KDebugBase::GetProcess() {
|
||||
/* Lock ourselves. */
|
||||
KScopedLightLock lk(this->lock);
|
||||
|
||||
return this->process;
|
||||
}
|
||||
|
||||
Result KDebugBase::GetDebugEventInfo(ams::svc::lp64::DebugEventInfo *out) {
|
||||
/* Get the attached process. */
|
||||
KScopedAutoObject process = this->GetProcess();
|
||||
R_UNLESS(process.IsNotNull(), svc::ResultProcessTerminated());
|
||||
|
||||
/* Pop an event info from our queue. */
|
||||
KEventInfo *info = nullptr;
|
||||
{
|
||||
KScopedSchedulerLock sl;
|
||||
|
||||
/* Check that we have an event to dequeue. */
|
||||
R_UNLESS(!this->event_info_list.empty(), svc::ResultNoEvent());
|
||||
|
||||
/* Pop the event from the front of the queue. */
|
||||
info = std::addressof(this->event_info_list.front());
|
||||
this->event_info_list.pop_front();
|
||||
}
|
||||
MESOSPHERE_ASSERT(info != nullptr);
|
||||
|
||||
/* Free the event info once we're done with it. */
|
||||
ON_SCOPE_EXIT { KEventInfo::Free(info); };
|
||||
|
||||
/* Set common fields. */
|
||||
out->type = info->event;
|
||||
out->thread_id = info->thread_id;
|
||||
out->flags = info->flags;
|
||||
|
||||
/* Set event specific fields. */
|
||||
switch (info->event) {
|
||||
case ams::svc::DebugEvent_AttachProcess:
|
||||
{
|
||||
out->info.attach_process.program_id = process->GetProgramId();
|
||||
out->info.attach_process.process_id = process->GetId();
|
||||
out->info.attach_process.flags = process->GetCreateProcessFlags();
|
||||
out->info.attach_process.user_exception_context_address = GetInteger(process->GetProcessLocalRegionAddress());
|
||||
|
||||
std::memcpy(out->info.attach_process.name, process->GetName(), sizeof(out->info.attach_process.name));
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugEvent_AttachThread:
|
||||
{
|
||||
out->info.attach_thread.thread_id = info->info.create_thread.thread_id;
|
||||
out->info.attach_thread.tls_address = info->info.create_thread.tls_address;
|
||||
out->info.attach_thread.entrypoint = info->info.create_thread.entrypoint;
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugEvent_ExitProcess:
|
||||
{
|
||||
out->info.exit_process.reason = info->info.exit_process.reason;
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugEvent_ExitThread:
|
||||
{
|
||||
out->info.exit_thread.reason = info->info.exit_thread.reason;
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugEvent_Exception:
|
||||
{
|
||||
out->info.exception.type = info->info.exception.exception_type;
|
||||
out->info.exception.address = info->info.exception.exception_address;
|
||||
|
||||
switch (info->info.exception.exception_type) {
|
||||
case ams::svc::DebugException_UndefinedInstruction:
|
||||
{
|
||||
MESOSPHERE_ASSERT(info->info.exception.exception_data_count == 1);
|
||||
out->info.exception.specific.undefined_instruction.insn = info->info.exception.exception_data[0];
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugException_BreakPoint:
|
||||
{
|
||||
MESOSPHERE_ASSERT(info->info.exception.exception_data_count == 1);
|
||||
out->info.exception.specific.break_point.type = static_cast<ams::svc::BreakPointType>(info->info.exception.exception_data[0]);
|
||||
out->info.exception.specific.break_point.address = 0;
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugException_UserBreak:
|
||||
{
|
||||
MESOSPHERE_ASSERT(info->info.exception.exception_data_count == 3);
|
||||
out->info.exception.specific.user_break.break_reason = static_cast<ams::svc::BreakReason>(info->info.exception.exception_data[0]);
|
||||
out->info.exception.specific.user_break.address = info->info.exception.exception_data[1];
|
||||
out->info.exception.specific.user_break.size = info->info.exception.exception_data[2];
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugException_DebuggerBreak:
|
||||
{
|
||||
MESOSPHERE_ASSERT(info->info.exception.exception_data_count == 4);
|
||||
out->info.exception.specific.debugger_break.active_thread_ids[0] = info->info.exception.exception_data[0];
|
||||
out->info.exception.specific.debugger_break.active_thread_ids[1] = info->info.exception.exception_data[1];
|
||||
out->info.exception.specific.debugger_break.active_thread_ids[2] = info->info.exception.exception_data[2];
|
||||
out->info.exception.specific.debugger_break.active_thread_ids[3] = info->info.exception.exception_data[3];
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugException_UndefinedSystemCall:
|
||||
{
|
||||
MESOSPHERE_ASSERT(info->info.exception.exception_data_count == 1);
|
||||
out->info.exception.specific.undefined_system_call.id = info->info.exception.exception_data[0];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result KDebugBase::GetDebugEventInfo(ams::svc::ilp32::DebugEventInfo *out) {
|
||||
MESOSPHERE_UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void KDebugBase::OnFinalizeSynchronizationObject() {
|
||||
MESOSPHERE_UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
bool KDebugBase::IsSignaled() const {
|
||||
KScopedSchedulerLock sl;
|
||||
|
||||
return (!this->event_info_list.empty()) || this->process == nullptr || this->process->IsTerminated();
|
||||
}
|
||||
|
||||
}
|
|
@ -18,9 +18,9 @@
|
|||
namespace ams::kern {
|
||||
|
||||
void KInterruptTaskManager::TaskQueue::Enqueue(KInterruptTask *task) {
|
||||
MESOSPHERE_ASSERT(task->GetNextTask() == nullptr);
|
||||
MESOSPHERE_ASSERT(task != this->head);
|
||||
MESOSPHERE_ASSERT(task != this->tail);
|
||||
MESOSPHERE_AUDIT(task->GetNextTask() == nullptr);
|
||||
|
||||
/* Insert the task into the queue. */
|
||||
if (this->tail != nullptr) {
|
||||
|
@ -30,19 +30,31 @@ namespace ams::kern {
|
|||
}
|
||||
|
||||
this->tail = task;
|
||||
|
||||
/* Set the next task for auditing. */
|
||||
#if defined (MESOSPHERE_BUILD_FOR_AUDITING)
|
||||
task->SetNextTask(GetDummyInterruptTask());
|
||||
#endif
|
||||
}
|
||||
|
||||
void KInterruptTaskManager::TaskQueue::Dequeue() {
|
||||
MESOSPHERE_ASSERT(this->head != nullptr);
|
||||
MESOSPHERE_ASSERT(this->tail != nullptr);
|
||||
MESOSPHERE_AUDIT(this->tail->GetNextTask() == GetDummyInterruptTask());
|
||||
|
||||
/* Pop the task from the front of the queue. */
|
||||
KInterruptTask *old_head = this->head;
|
||||
|
||||
if (this->head == this->tail) {
|
||||
this->head = nullptr;
|
||||
this->tail = nullptr;
|
||||
} else {
|
||||
this->head = this->head->GetNextTask();
|
||||
}
|
||||
|
||||
#if defined (MESOSPHERE_BUILD_FOR_AUDITING)
|
||||
old_head->SetNextTask(nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
void KInterruptTaskManager::ThreadFunction(uintptr_t arg) {
|
||||
|
|
|
@ -451,6 +451,33 @@ namespace ams::kern {
|
|||
return ResultSuccess();
|
||||
}
|
||||
|
||||
KProcess::State KProcess::SetDebugObject(void *debug_object) {
|
||||
/* Cache our state to return it to the debug object. */
|
||||
const auto old_state = this->state;
|
||||
|
||||
/* Set the object. */
|
||||
this->attached_object = debug_object;
|
||||
|
||||
/* Update our state. */
|
||||
if (this->state != State_DebugBreak) {
|
||||
this->state = (this->state != State_Created) ? State_DebugBreak : State_CreatedAttached;
|
||||
|
||||
/* Signal. */
|
||||
this->is_signaled = true;
|
||||
this->NotifyAvailable();
|
||||
}
|
||||
|
||||
return old_state;
|
||||
}
|
||||
|
||||
KEventInfo *KProcess::GetJitDebugInfo() {
|
||||
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);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void KProcess::SetPreemptionState() {
|
||||
MESOSPHERE_UNIMPLEMENTED();
|
||||
}
|
||||
|
|
|
@ -21,7 +21,37 @@ namespace ams::kern::svc {
|
|||
|
||||
namespace {
|
||||
|
||||
constexpr bool IsKernelAddress(uintptr_t address) {
|
||||
return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd;
|
||||
}
|
||||
|
||||
Result WaitProcessWideKeyAtomic(uintptr_t address, uintptr_t cv_key, uint32_t tag, int64_t timeout_ns) {
|
||||
/* Validate input. */
|
||||
R_UNLESS(AMS_LIKELY(!IsKernelAddress(address)), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(util::IsAligned(address, sizeof(int32_t)), svc::ResultInvalidAddress());
|
||||
|
||||
/* Convert timeout from nanoseconds to ticks. */
|
||||
s64 timeout;
|
||||
if (timeout_ns > 0) {
|
||||
const ams::svc::Tick offset_tick(TimeSpan::FromNanoSeconds(timeout_ns));
|
||||
if (AMS_LIKELY(offset_tick > 0)) {
|
||||
timeout = KHardwareTimer::GetTick() + offset_tick + 2;
|
||||
if (AMS_UNLIKELY(timeout <= 0)) {
|
||||
timeout = std::numeric_limits<s64>::max();
|
||||
}
|
||||
} else {
|
||||
timeout = std::numeric_limits<s64>::max();
|
||||
}
|
||||
} else {
|
||||
timeout = timeout_ns;
|
||||
}
|
||||
|
||||
/* Wait on the condition variable. */
|
||||
return GetCurrentProcess().WaitConditionVariable(address, util::AlignDown(cv_key, sizeof(u32)), tag, timeout);
|
||||
}
|
||||
|
||||
void SignalProcessWideKey(uintptr_t cv_key, int32_t count) {
|
||||
/* Signal the condition variable. */
|
||||
return GetCurrentProcess().SignalConditionVariable(util::AlignDown(cv_key, sizeof(u32)), count);
|
||||
}
|
||||
|
||||
|
@ -30,7 +60,7 @@ namespace ams::kern::svc {
|
|||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result WaitProcessWideKeyAtomic64(ams::svc::Address address, ams::svc::Address cv_key, uint32_t tag, int64_t timeout_ns) {
|
||||
MESOSPHERE_PANIC("Stubbed SvcWaitProcessWideKeyAtomic64 was called.");
|
||||
return WaitProcessWideKeyAtomic(address, cv_key, tag, timeout_ns);
|
||||
}
|
||||
|
||||
void SignalProcessWideKey64(ams::svc::Address cv_key, int32_t count) {
|
||||
|
@ -40,7 +70,7 @@ namespace ams::kern::svc {
|
|||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result WaitProcessWideKeyAtomic64From32(ams::svc::Address address, ams::svc::Address cv_key, uint32_t tag, int64_t timeout_ns) {
|
||||
MESOSPHERE_PANIC("Stubbed SvcWaitProcessWideKeyAtomic64From32 was called.");
|
||||
return WaitProcessWideKeyAtomic(address, cv_key, tag, timeout_ns);
|
||||
}
|
||||
|
||||
void SignalProcessWideKey64From32(ams::svc::Address cv_key, int32_t count) {
|
||||
|
|
|
@ -21,14 +21,70 @@ namespace ams::kern::svc {
|
|||
|
||||
namespace {
|
||||
|
||||
Result DebugActiveProcess(ams::svc::Handle *out_handle, uint64_t process_id) {
|
||||
/* Get the process from its id. */
|
||||
KProcess *process = KProcess::GetProcessFromId(process_id);
|
||||
R_UNLESS(process != nullptr, svc::ResultInvalidProcessId());
|
||||
|
||||
/* Close the reference we opened to the process on scope exit. */
|
||||
ON_SCOPE_EXIT { process->Close(); };
|
||||
|
||||
/* Check that the debugging is allowed. */
|
||||
if (!process->IsPermittedDebug()) {
|
||||
R_UNLESS(GetCurrentProcess().CanForceDebug(), svc::ResultInvalidState());
|
||||
}
|
||||
|
||||
/* Disallow debugging one's own processs, to prevent softlocks. */
|
||||
R_UNLESS(process != GetCurrentProcessPointer(), svc::ResultInvalidState());
|
||||
|
||||
/* Get the current handle table. */
|
||||
auto &handle_table = GetCurrentProcess().GetHandleTable();
|
||||
|
||||
/* Create a new debug object. */
|
||||
KDebug *debug = KDebug::Create();
|
||||
R_UNLESS(debug != nullptr, svc::ResultOutOfResource());
|
||||
ON_SCOPE_EXIT { debug->Close(); };
|
||||
|
||||
/* Initialize the debug object. */
|
||||
debug->Initialize();
|
||||
|
||||
/* Register the debug object. */
|
||||
KDebug::Register(debug);
|
||||
|
||||
/* Try to attach to the target process. */
|
||||
R_TRY(debug->Attach(process));
|
||||
|
||||
/* Add the new debug object to the handle table. */
|
||||
R_TRY(handle_table.Add(out_handle, debug));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template<typename EventInfoType>
|
||||
Result GetDebugEvent(KUserPointer<EventInfoType *> out_info, ams::svc::Handle debug_handle) {
|
||||
/* Get the debug object. */
|
||||
KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject<KDebug>(debug_handle);
|
||||
R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Create and clear a new event info. */
|
||||
EventInfoType info;
|
||||
std::memset(std::addressof(info), 0, sizeof(info));
|
||||
|
||||
/* Get the next info from the debug object. */
|
||||
R_TRY(debug->GetDebugEventInfo(std::addressof(info)));
|
||||
|
||||
/* Copy the info out to the user. */
|
||||
R_TRY(out_info.CopyFrom(std::addressof(info)));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
|
||||
Result DebugActiveProcess64(ams::svc::Handle *out_handle, uint64_t process_id) {
|
||||
MESOSPHERE_PANIC("Stubbed SvcDebugActiveProcess64 was called.");
|
||||
return DebugActiveProcess(out_handle, process_id);
|
||||
}
|
||||
|
||||
Result BreakDebugProcess64(ams::svc::Handle debug_handle) {
|
||||
|
@ -40,7 +96,7 @@ namespace ams::kern::svc {
|
|||
}
|
||||
|
||||
Result GetDebugEvent64(KUserPointer<ams::svc::lp64::DebugEventInfo *> out_info, ams::svc::Handle debug_handle) {
|
||||
MESOSPHERE_PANIC("Stubbed SvcGetDebugEvent64 was called.");
|
||||
return GetDebugEvent(out_info, debug_handle);
|
||||
}
|
||||
|
||||
Result ContinueDebugEvent64(ams::svc::Handle debug_handle, uint32_t flags, KUserPointer<const uint64_t *> thread_ids, int32_t num_thread_ids) {
|
||||
|
@ -82,7 +138,7 @@ namespace ams::kern::svc {
|
|||
/* ============================= 64From32 ABI ============================= */
|
||||
|
||||
Result DebugActiveProcess64From32(ams::svc::Handle *out_handle, uint64_t process_id) {
|
||||
MESOSPHERE_PANIC("Stubbed SvcDebugActiveProcess64From32 was called.");
|
||||
return DebugActiveProcess(out_handle, process_id);
|
||||
}
|
||||
|
||||
Result BreakDebugProcess64From32(ams::svc::Handle debug_handle) {
|
||||
|
@ -94,7 +150,7 @@ namespace ams::kern::svc {
|
|||
}
|
||||
|
||||
Result GetDebugEvent64From32(KUserPointer<ams::svc::ilp32::DebugEventInfo *> out_info, ams::svc::Handle debug_handle) {
|
||||
MESOSPHERE_PANIC("Stubbed SvcGetDebugEvent64From32 was called.");
|
||||
return GetDebugEvent(out_info, debug_handle);
|
||||
}
|
||||
|
||||
Result ContinueDebugEvent64From32(ams::svc::Handle debug_handle, uint32_t flags, KUserPointer<const uint64_t *> thread_ids, int32_t num_thread_ids) {
|
||||
|
|
|
@ -35,17 +35,18 @@ namespace ams::kern::svc {
|
|||
R_UNLESS((das_address < das_address + das_size), svc::ResultInvalidMemoryRegion());
|
||||
|
||||
/* Create the device address space. */
|
||||
KScopedAutoObject das = KDeviceAddressSpace::Create();
|
||||
R_UNLESS(das.IsNotNull(), svc::ResultOutOfResource());
|
||||
KDeviceAddressSpace *das = KDeviceAddressSpace::Create();
|
||||
R_UNLESS(das != nullptr, svc::ResultOutOfResource());
|
||||
ON_SCOPE_EXIT { das->Close(); };
|
||||
|
||||
/* Initialize the device address space. */
|
||||
R_TRY(das->Initialize(das_address, das_size));
|
||||
|
||||
/* Register the device address space. */
|
||||
R_TRY(KDeviceAddressSpace::Register(das.GetPointerUnsafe()));
|
||||
R_TRY(KDeviceAddressSpace::Register(das));
|
||||
|
||||
/* Add to the handle table. */
|
||||
R_TRY(GetCurrentProcess().GetHandleTable().Add(out, das.GetPointerUnsafe()));
|
||||
R_TRY(GetCurrentProcess().GetHandleTable().Add(out, das));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
|
|
@ -43,17 +43,18 @@ namespace ams::kern::svc {
|
|||
auto &handle_table = process.GetHandleTable();
|
||||
|
||||
/* Create the interrupt event. */
|
||||
KScopedAutoObject event = KInterruptEvent::Create();
|
||||
R_UNLESS(event.IsNotNull(), svc::ResultOutOfResource());
|
||||
KInterruptEvent *event = KInterruptEvent::Create();
|
||||
R_UNLESS(event != nullptr, svc::ResultOutOfResource());
|
||||
ON_SCOPE_EXIT { event->Close(); };
|
||||
|
||||
/* Initialize the event. */
|
||||
R_TRY(event->Initialize(interrupt_id, type));
|
||||
|
||||
/* Register the event. */
|
||||
R_TRY(KInterruptEvent::Register(event.GetPointerUnsafe()));
|
||||
R_TRY(KInterruptEvent::Register(event));
|
||||
|
||||
/* Add the event to the handle table. */
|
||||
R_TRY(handle_table.Add(out, event.GetPointerUnsafe()));
|
||||
R_TRY(handle_table.Add(out, event));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
|
|
@ -44,13 +44,14 @@ namespace ams::kern::svc {
|
|||
R_UNLESS(thread_reservation.Succeeded(), svc::ResultLimitReached());
|
||||
|
||||
/* Create the thread. */
|
||||
KScopedAutoObject thread = KThread::Create();
|
||||
R_UNLESS(thread.IsNotNull(), svc::ResultOutOfResource());
|
||||
KThread *thread = KThread::Create();
|
||||
R_UNLESS(thread != nullptr, svc::ResultOutOfResource());
|
||||
ON_SCOPE_EXIT { thread->Close(); };
|
||||
|
||||
/* Initialize the thread. */
|
||||
{
|
||||
KScopedLightLock lk(process.GetStateLock());
|
||||
R_TRY(KThread::InitializeUserThread(thread.GetPointerUnsafe(), reinterpret_cast<KThreadFunction>(static_cast<uintptr_t>(f)), arg, stack_bottom, priority, core_id, std::addressof(process)));
|
||||
R_TRY(KThread::InitializeUserThread(thread, reinterpret_cast<KThreadFunction>(static_cast<uintptr_t>(f)), arg, stack_bottom, priority, core_id, std::addressof(process)));
|
||||
}
|
||||
|
||||
/* Commit the thread reservation. */
|
||||
|
@ -60,10 +61,10 @@ namespace ams::kern::svc {
|
|||
thread->GetContext().CloneFpuStatus();
|
||||
|
||||
/* Register the new thread. */
|
||||
R_TRY(KThread::Register(thread.GetPointerUnsafe()));
|
||||
R_TRY(KThread::Register(thread));
|
||||
|
||||
/* Add the thread to the handle table. */
|
||||
R_TRY(process.GetHandleTable().Add(out, thread.GetPointerUnsafe()));
|
||||
R_TRY(process.GetHandleTable().Add(out, thread));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
|
|
@ -70,6 +70,7 @@ namespace ams::svc {
|
|||
R_DEFINE_ERROR_RESULT(OutOfAddressSpace, 259);
|
||||
R_DEFINE_ERROR_RESULT(MessageTooLarge, 260);
|
||||
|
||||
R_DEFINE_ERROR_RESULT(InvalidProcessId, 517);
|
||||
R_DEFINE_ERROR_RESULT(ProcessTerminated, 520);
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue