kern: implement SvcDebugActiveProcess, svcGetDebugEvent, SvcWaitProcessWideKeyAtomic

This commit is contained in:
Michael Scire 2020-07-18 20:03:27 -07:00 committed by SciresM
parent 1c5b58ce66
commit 36eb78a3ce
17 changed files with 728 additions and 24 deletions

View file

@ -30,6 +30,11 @@ namespace ams::kern::arch::arm64 {
class KDebug final : public KAutoObjectWithSlabHeapAndContainer<KDebug, KDebugBase> { class KDebug final : public KAutoObjectWithSlabHeapAndContainer<KDebug, KDebugBase> {
MESOSPHERE_AUTOOBJECT_TRAITS(KDebug, KSynchronizationObject); MESOSPHERE_AUTOOBJECT_TRAITS(KDebug, KSynchronizationObject);
public: public:
explicit KDebug() { /* ... */ }
virtual ~KDebug() { /* ... */ }
static void PostDestroy(uintptr_t arg) { /* ... */ }
/* TODO: This is a placeholder definition. */ /* TODO: This is a placeholder definition. */
}; };

View file

@ -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. */ /* TODO: Member functions. */
}; };

View file

@ -16,12 +16,42 @@
#pragma once #pragma once
#include <mesosphere/kern_common.hpp> #include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_synchronization_object.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 { namespace ams::kern {
class KDebugBase : public KSynchronizationObject { 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: 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. */ /* 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);
}; };
} }

View file

@ -21,7 +21,50 @@ namespace ams::kern {
class KEventInfo : public KSlabAllocated<KEventInfo>, public util::IntrusiveListBaseNode<KEventInfo> { class KEventInfo : public KSlabAllocated<KEventInfo>, public util::IntrusiveListBaseNode<KEventInfo> {
public: 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() { /* ... */ }
}; };
} }

View file

@ -131,6 +131,8 @@ namespace ams::kern {
constexpr u64 GetProcessId() const { return this->process_id; } 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 GetCoreMask() const { return this->capabilities.GetCoreMask(); }
constexpr u64 GetPriorityMask() const { return this->capabilities.GetPriorityMask(); } 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 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 bool Is64Bit() const { return this->flags & ams::svc::CreateProcessFlag_Is64Bit; }
constexpr KProcessAddress GetEntryPoint() const { return this->code_address; } constexpr KProcessAddress GetEntryPoint() const { return this->code_address; }
@ -147,10 +151,32 @@ namespace ams::kern {
return this->is_suspended; 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 { constexpr bool IsPermittedInterrupt(int32_t interrupt_id) const {
return this->capabilities.IsPermittedInterrupt(interrupt_id); 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 EnterUserException();
bool LeaveUserException(); bool LeaveUserException();
bool ReleaseUserException(KThread *thread); bool ReleaseUserException(KThread *thread);

View file

@ -311,10 +311,21 @@ namespace ams::kern {
this->priority = priority; 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; this->condvar_tree = nullptr;
} }
constexpr bool IsWaitingForConditionVariable() const {
return this->condvar_tree != nullptr;
}
constexpr void SetupForAddressArbiterCompare(uintptr_t address, int priority) { constexpr void SetupForAddressArbiterCompare(uintptr_t address, int priority) {
this->condvar_key = address; this->condvar_key = address;
this->priority = priority; this->priority = priority;
@ -394,6 +405,8 @@ namespace ams::kern {
constexpr KProcess *GetOwnerProcess() const { return this->parent; } constexpr KProcess *GetOwnerProcess() const { return this->parent; }
constexpr bool IsUserThread() const { return this->parent != nullptr; } 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 KProcessAddress GetThreadLocalRegionAddress() const { return this->tls_address; }
constexpr void *GetThreadLocalRegionHeapAddress() const { return this->tls_heap_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 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 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) { void AddCpuTime(s64 amount) {
this->cpu_time += amount; this->cpu_time += amount;
} }

View file

@ -67,6 +67,7 @@ namespace ams::kern::svc {
/* 259 */ using ::ams::svc::ResultOutOfAddressSpace; /* 259 */ using ::ams::svc::ResultOutOfAddressSpace;
/* 260 */ using ::ams::svc::ResultMessageTooLarge; /* 260 */ using ::ams::svc::ResultMessageTooLarge;
/* 517 */ using ::ams::svc::ResultInvalidProcessId;
/* 520 */ using ::ams::svc::ResultProcessTerminated; /* 520 */ using ::ams::svc::ResultProcessTerminated;
} }

View file

@ -146,7 +146,7 @@ namespace ams::kern {
} }
it = this->tree.erase(it); it = this->tree.erase(it);
target_thread->ClearConditionVariableTree(); target_thread->ClearConditionVariable();
++num_waiters; ++num_waiters;
} }
} }
@ -168,7 +168,83 @@ namespace ams::kern {
} }
Result KConditionVariable::Wait(KProcessAddress addr, uintptr_t key, u32 value, s64 timeout) { 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));
} }
} }

View 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();
}
}

View file

@ -18,9 +18,9 @@
namespace ams::kern { namespace ams::kern {
void KInterruptTaskManager::TaskQueue::Enqueue(KInterruptTask *task) { void KInterruptTaskManager::TaskQueue::Enqueue(KInterruptTask *task) {
MESOSPHERE_ASSERT(task->GetNextTask() == nullptr);
MESOSPHERE_ASSERT(task != this->head); MESOSPHERE_ASSERT(task != this->head);
MESOSPHERE_ASSERT(task != this->tail); MESOSPHERE_ASSERT(task != this->tail);
MESOSPHERE_AUDIT(task->GetNextTask() == nullptr);
/* Insert the task into the queue. */ /* Insert the task into the queue. */
if (this->tail != nullptr) { if (this->tail != nullptr) {
@ -30,19 +30,31 @@ namespace ams::kern {
} }
this->tail = task; this->tail = task;
/* Set the next task for auditing. */
#if defined (MESOSPHERE_BUILD_FOR_AUDITING)
task->SetNextTask(GetDummyInterruptTask());
#endif
} }
void KInterruptTaskManager::TaskQueue::Dequeue() { void KInterruptTaskManager::TaskQueue::Dequeue() {
MESOSPHERE_ASSERT(this->head != nullptr); MESOSPHERE_ASSERT(this->head != nullptr);
MESOSPHERE_ASSERT(this->tail != nullptr); MESOSPHERE_ASSERT(this->tail != nullptr);
MESOSPHERE_AUDIT(this->tail->GetNextTask() == GetDummyInterruptTask());
/* Pop the task from the front of the queue. */ /* Pop the task from the front of the queue. */
KInterruptTask *old_head = this->head;
if (this->head == this->tail) { if (this->head == this->tail) {
this->head = nullptr; this->head = nullptr;
this->tail = nullptr; this->tail = nullptr;
} else { } else {
this->head = this->head->GetNextTask(); this->head = this->head->GetNextTask();
} }
#if defined (MESOSPHERE_BUILD_FOR_AUDITING)
old_head->SetNextTask(nullptr);
#endif
} }
void KInterruptTaskManager::ThreadFunction(uintptr_t arg) { void KInterruptTaskManager::ThreadFunction(uintptr_t arg) {

View file

@ -451,6 +451,33 @@ namespace ams::kern {
return ResultSuccess(); 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() { void KProcess::SetPreemptionState() {
MESOSPHERE_UNIMPLEMENTED(); MESOSPHERE_UNIMPLEMENTED();
} }

View file

@ -21,7 +21,37 @@ namespace ams::kern::svc {
namespace { 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) { void SignalProcessWideKey(uintptr_t cv_key, int32_t count) {
/* Signal the condition variable. */
return GetCurrentProcess().SignalConditionVariable(util::AlignDown(cv_key, sizeof(u32)), count); return GetCurrentProcess().SignalConditionVariable(util::AlignDown(cv_key, sizeof(u32)), count);
} }
@ -30,7 +60,7 @@ namespace ams::kern::svc {
/* ============================= 64 ABI ============================= */ /* ============================= 64 ABI ============================= */
Result WaitProcessWideKeyAtomic64(ams::svc::Address address, ams::svc::Address cv_key, uint32_t tag, int64_t timeout_ns) { 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) { void SignalProcessWideKey64(ams::svc::Address cv_key, int32_t count) {
@ -40,7 +70,7 @@ namespace ams::kern::svc {
/* ============================= 64From32 ABI ============================= */ /* ============================= 64From32 ABI ============================= */
Result WaitProcessWideKeyAtomic64From32(ams::svc::Address address, ams::svc::Address cv_key, uint32_t tag, int64_t timeout_ns) { 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) { void SignalProcessWideKey64From32(ams::svc::Address cv_key, int32_t count) {

View file

@ -21,14 +21,70 @@ namespace ams::kern::svc {
namespace { 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 ============================= */ /* ============================= 64 ABI ============================= */
Result DebugActiveProcess64(ams::svc::Handle *out_handle, uint64_t process_id) { 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) { 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) { 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) { 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 ============================= */ /* ============================= 64From32 ABI ============================= */
Result DebugActiveProcess64From32(ams::svc::Handle *out_handle, uint64_t process_id) { 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) { 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) { 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) { Result ContinueDebugEvent64From32(ams::svc::Handle debug_handle, uint32_t flags, KUserPointer<const uint64_t *> thread_ids, int32_t num_thread_ids) {

View file

@ -35,17 +35,18 @@ namespace ams::kern::svc {
R_UNLESS((das_address < das_address + das_size), svc::ResultInvalidMemoryRegion()); R_UNLESS((das_address < das_address + das_size), svc::ResultInvalidMemoryRegion());
/* Create the device address space. */ /* Create the device address space. */
KScopedAutoObject das = KDeviceAddressSpace::Create(); KDeviceAddressSpace *das = KDeviceAddressSpace::Create();
R_UNLESS(das.IsNotNull(), svc::ResultOutOfResource()); R_UNLESS(das != nullptr, svc::ResultOutOfResource());
ON_SCOPE_EXIT { das->Close(); };
/* Initialize the device address space. */ /* Initialize the device address space. */
R_TRY(das->Initialize(das_address, das_size)); R_TRY(das->Initialize(das_address, das_size));
/* Register the device address space. */ /* Register the device address space. */
R_TRY(KDeviceAddressSpace::Register(das.GetPointerUnsafe())); R_TRY(KDeviceAddressSpace::Register(das));
/* Add to the handle table. */ /* Add to the handle table. */
R_TRY(GetCurrentProcess().GetHandleTable().Add(out, das.GetPointerUnsafe())); R_TRY(GetCurrentProcess().GetHandleTable().Add(out, das));
return ResultSuccess(); return ResultSuccess();
} }

View file

@ -43,17 +43,18 @@ namespace ams::kern::svc {
auto &handle_table = process.GetHandleTable(); auto &handle_table = process.GetHandleTable();
/* Create the interrupt event. */ /* Create the interrupt event. */
KScopedAutoObject event = KInterruptEvent::Create(); KInterruptEvent *event = KInterruptEvent::Create();
R_UNLESS(event.IsNotNull(), svc::ResultOutOfResource()); R_UNLESS(event != nullptr, svc::ResultOutOfResource());
ON_SCOPE_EXIT { event->Close(); };
/* Initialize the event. */ /* Initialize the event. */
R_TRY(event->Initialize(interrupt_id, type)); R_TRY(event->Initialize(interrupt_id, type));
/* Register the event. */ /* Register the event. */
R_TRY(KInterruptEvent::Register(event.GetPointerUnsafe())); R_TRY(KInterruptEvent::Register(event));
/* Add the event to the handle table. */ /* Add the event to the handle table. */
R_TRY(handle_table.Add(out, event.GetPointerUnsafe())); R_TRY(handle_table.Add(out, event));
return ResultSuccess(); return ResultSuccess();
} }

View file

@ -44,13 +44,14 @@ namespace ams::kern::svc {
R_UNLESS(thread_reservation.Succeeded(), svc::ResultLimitReached()); R_UNLESS(thread_reservation.Succeeded(), svc::ResultLimitReached());
/* Create the thread. */ /* Create the thread. */
KScopedAutoObject thread = KThread::Create(); KThread *thread = KThread::Create();
R_UNLESS(thread.IsNotNull(), svc::ResultOutOfResource()); R_UNLESS(thread != nullptr, svc::ResultOutOfResource());
ON_SCOPE_EXIT { thread->Close(); };
/* Initialize the thread. */ /* Initialize the thread. */
{ {
KScopedLightLock lk(process.GetStateLock()); 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. */ /* Commit the thread reservation. */
@ -60,10 +61,10 @@ namespace ams::kern::svc {
thread->GetContext().CloneFpuStatus(); thread->GetContext().CloneFpuStatus();
/* Register the new thread. */ /* Register the new thread. */
R_TRY(KThread::Register(thread.GetPointerUnsafe())); R_TRY(KThread::Register(thread));
/* Add the thread to the handle table. */ /* Add the thread to the handle table. */
R_TRY(process.GetHandleTable().Add(out, thread.GetPointerUnsafe())); R_TRY(process.GetHandleTable().Add(out, thread));
return ResultSuccess(); return ResultSuccess();
} }

View file

@ -70,6 +70,7 @@ namespace ams::svc {
R_DEFINE_ERROR_RESULT(OutOfAddressSpace, 259); R_DEFINE_ERROR_RESULT(OutOfAddressSpace, 259);
R_DEFINE_ERROR_RESULT(MessageTooLarge, 260); R_DEFINE_ERROR_RESULT(MessageTooLarge, 260);
R_DEFINE_ERROR_RESULT(InvalidProcessId, 517);
R_DEFINE_ERROR_RESULT(ProcessTerminated, 520); R_DEFINE_ERROR_RESULT(ProcessTerminated, 520);
} }