kern: use variable-count parameter arrays for DebugEvents

This commit is contained in:
Michael Scire 2023-02-21 09:16:15 -07:00 committed by SciresM
parent db510f96c3
commit 8db22967bf
7 changed files with 125 additions and 98 deletions

View file

@ -74,7 +74,7 @@ namespace ams::kern {
return m_process_holder.Get();
}
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, const uintptr_t *params, size_t num_params);
void EnqueueDebugEventInfo(KEventInfo *info);
template<typename T> requires (std::same_as<T, ams::svc::lp64::DebugEventInfo> || std::same_as<T, ams::svc::ilp32::DebugEventInfo>)
@ -85,13 +85,13 @@ namespace ams::kern {
/* NOTE: This is public/virtual override in Nintendo's kernel. */
void OnFinalizeSynchronizationObject();
private:
static Result ProcessDebugEvent(ams::svc::DebugEvent event, uintptr_t param0, uintptr_t param1, uintptr_t param2, uintptr_t param3, uintptr_t param4);
static Result ProcessDebugEvent(ams::svc::DebugEvent event, const uintptr_t *params, size_t num_params);
public:
static Result OnDebugEvent(ams::svc::DebugEvent event, uintptr_t param0 = 0, uintptr_t param1 = 0, uintptr_t param2 = 0, uintptr_t param3 = 0, uintptr_t param4 = 0);
static Result OnDebugEvent(ams::svc::DebugEvent event, const uintptr_t *params, size_t num_params);
static Result OnExitProcess(KProcess *process);
static Result OnTerminateProcess(KProcess *process);
static Result OnExitThread(KThread *thread);
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);
static KEventInfo *CreateDebugEvent(ams::svc::DebugEvent event, u64 thread_id, const uintptr_t *params, size_t num_params);
};
}

View file

@ -38,7 +38,7 @@ namespace ams::kern {
ams::svc::DebugException exception_type;
s32 exception_data_count;
uintptr_t exception_address;
uintptr_t exception_data[4];
uintptr_t exception_data[std::max<size_t>(4, cpu::NumCores)];
};
struct InfoSystemCall {

View file

@ -229,73 +229,71 @@ namespace ams::kern::arch::arm64 {
{
/* Collect additional information based on the ec. */
ams::svc::DebugException exception;
uintptr_t param2 = 0;
uintptr_t param3 = 0;
uintptr_t params[3] = {};
switch (ec) {
case EsrEc_Unknown:
case EsrEc_IllegalExecution:
case EsrEc_BkptInstruction:
case EsrEc_BrkInstruction:
{
exception = ams::svc::DebugException_UndefinedInstruction;
param2 = far;
param3 = data;
params[0] = ams::svc::DebugException_UndefinedInstruction;
params[1] = far;
params[2] = data;
}
break;
case EsrEc_PcAlignmentFault:
case EsrEc_SpAlignmentFault:
{
exception = ams::svc::DebugException_AlignmentFault;
param2 = far;
params[0] = ams::svc::DebugException_AlignmentFault;
params[1] = far;
}
break;
case EsrEc_Svc32:
case EsrEc_Svc64:
{
exception = ams::svc::DebugException_UndefinedSystemCall;
param2 = far;
param3 = (esr & 0xFF);
params[0] = ams::svc::DebugException_UndefinedSystemCall;
params[1] = far;
params[2] = (esr & 0xFF);
}
break;
case EsrEc_BreakPointEl0:
case EsrEc_SoftwareStepEl0:
{
exception = ams::svc::DebugException_BreakPoint;
param2 = far;
param3 = ams::svc::BreakPointType_HardwareInstruction;
params[0] = ams::svc::DebugException_BreakPoint;
params[1] = far;
params[2] = ams::svc::BreakPointType_HardwareInstruction;
}
break;
case EsrEc_WatchPointEl0:
{
exception = ams::svc::DebugException_BreakPoint;
param2 = far;
param3 = ams::svc::BreakPointType_HardwareData;
params[0] = ams::svc::DebugException_BreakPoint;
params[1] = far;
params[2] = ams::svc::BreakPointType_HardwareData;
}
break;
case EsrEc_SErrorInterrupt:
{
exception = ams::svc::DebugException_MemorySystemError;
param2 = far;
params[0] = ams::svc::DebugException_MemorySystemError;
params[1] = far;
}
break;
case EsrEc_InstructionAbortEl0:
{
exception = ams::svc::DebugException_InstructionAbort;
param2 = far;
params[0] = ams::svc::DebugException_InstructionAbort;
params[1] = far;
}
break;
case EsrEc_DataAbortEl0:
default:
{
exception = ams::svc::DebugException_DataAbort;
param2 = far;
params[0] = ams::svc::DebugException_DataAbort;
params[1] = far;
}
break;
}
/* Process the debug event. */
Result result = KDebug::OnDebugEvent(ams::svc::DebugEvent_Exception, exception, param2, param3);
Result result = KDebug::OnDebugEvent(ams::svc::DebugEvent_Exception, params, util::size(params));
/* If we should stop processing the exception, do so. */
if (svc::ResultStopProcessingException::Includes(result)) {
@ -340,7 +338,7 @@ namespace ams::kern::arch::arm64 {
/* If the SVC is handled, handle it. */
if (!svc::ResultNotHandled::Includes(result)) {
/* If we successfully enter jit debug, stop processing the exception. */
if (cur_process.EnterJitDebug(ams::svc::DebugEvent_Exception, exception, param2, param3)) {
if (cur_process.EnterJitDebug(ams::svc::DebugEvent_Exception, static_cast<ams::svc::DebugException>(params[0]), params[1], params[2])) {
return;
}
}
@ -420,58 +418,56 @@ namespace ams::kern::arch::arm64 {
GetCurrentThread().RestoreDebugParams(std::addressof(far), std::addressof(esr), std::addressof(data));
/* Collect additional information based on the ec. */
ams::svc::DebugException exception;
uintptr_t param2 = 0;
uintptr_t param3 = 0;
uintptr_t params[3] = {};
switch ((esr >> 26) & 0x3F) {
case EsrEc_Unknown:
case EsrEc_IllegalExecution:
case EsrEc_BkptInstruction:
case EsrEc_BrkInstruction:
{
exception = ams::svc::DebugException_UndefinedInstruction;
param2 = far;
param3 = data;
params[0] = ams::svc::DebugException_UndefinedInstruction;
params[1] = far;
params[2] = data;
}
break;
case EsrEc_PcAlignmentFault:
case EsrEc_SpAlignmentFault:
{
exception = ams::svc::DebugException_AlignmentFault;
param2 = far;
params[0] = ams::svc::DebugException_AlignmentFault;
params[1] = far;
}
break;
case EsrEc_Svc32:
case EsrEc_Svc64:
{
exception = ams::svc::DebugException_UndefinedSystemCall;
param2 = far;
param3 = (esr & 0xFF);
params[0] = ams::svc::DebugException_UndefinedSystemCall;
params[1] = far;
params[2] = (esr & 0xFF);
}
break;
case EsrEc_SErrorInterrupt:
{
exception = ams::svc::DebugException_MemorySystemError;
param2 = far;
params[0] = ams::svc::DebugException_MemorySystemError;
params[1] = far;
}
break;
case EsrEc_InstructionAbortEl0:
{
exception = ams::svc::DebugException_InstructionAbort;
param2 = far;
params[0] = ams::svc::DebugException_InstructionAbort;
params[1] = far;
}
break;
case EsrEc_DataAbortEl0:
default:
{
exception = ams::svc::DebugException_DataAbort;
param2 = far;
params[0] = ams::svc::DebugException_DataAbort;
params[1] = far;
}
break;
}
/* Process the debug event. */
Result result = KDebug::OnDebugEvent(ams::svc::DebugEvent_Exception, exception, param2, param3);
Result result = KDebug::OnDebugEvent(ams::svc::DebugEvent_Exception, params, util::size(params));
/* If the SVC is handled, handle it. */
if (!svc::ResultNotHandled::Includes(result)) {
@ -481,7 +477,7 @@ namespace ams::kern::arch::arm64 {
}
/* If we successfully enter jit debug, restore. */
if (cur_process.EnterJitDebug(ams::svc::DebugEvent_Exception, exception, param2, param3)) {
if (cur_process.EnterJitDebug(ams::svc::DebugEvent_Exception, static_cast<ams::svc::DebugException>(params[0]), params[1], params[2])) {
svc::RestoreContext(reinterpret_cast<uintptr_t>(e_ctx));
}
}

View file

@ -249,7 +249,8 @@ namespace ams::kern::arch::arm64 {
}
Result KDebug::BreakIfAttached(ams::svc::BreakReason break_reason, uintptr_t address, size_t size) {
R_RETURN(KDebugBase::OnDebugEvent(ams::svc::DebugEvent_Exception, ams::svc::DebugException_UserBreak, GetProgramCounter(GetCurrentThread()), break_reason, address, size));
const uintptr_t params[5] = { ams::svc::DebugException_UserBreak, GetProgramCounter(GetCurrentThread()), break_reason, address, size };
R_RETURN(KDebugBase::OnDebugEvent(ams::svc::DebugEvent_Exception, params, util::size(params)));
}
#define MESOSPHERE_SET_HW_BREAK_POINT(ID, FLAGS, VALUE) \

View file

@ -26,7 +26,9 @@ namespace ams::kern::arch::arm64 {
/* Send KDebug event for this thread's creation. */
{
KScopedInterruptEnable ei;
KDebug::OnDebugEvent(ams::svc::DebugEvent_CreateThread, GetCurrentThread().GetId(), GetInteger(GetCurrentThread().GetThreadLocalRegionAddress()));
const uintptr_t params[2] = { GetCurrentThread().GetId(), GetInteger(GetCurrentThread().GetThreadLocalRegionAddress()) };
KDebug::OnDebugEvent(ams::svc::DebugEvent_CreateThread, params, util::size(params));
}
/* Handle any pending dpc. */

View file

@ -289,7 +289,7 @@ namespace ams::kern {
m_old_process_state = target->SetDebugObject(this);
/* Send an event for our attaching to the process. */
this->PushDebugEvent(ams::svc::DebugEvent_CreateProcess);
this->PushDebugEvent(ams::svc::DebugEvent_CreateProcess, nullptr, 0);
/* Send events for attaching to each thread in the process. */
{
@ -304,7 +304,8 @@ namespace ams::kern {
it->SetDebugAttached();
/* Send the event. */
this->PushDebugEvent(ams::svc::DebugEvent_CreateThread, it->GetId(), GetInteger(it->GetThreadLocalRegionAddress()));
const uintptr_t params[2] = { it->GetId(), GetInteger(it->GetThreadLocalRegionAddress()) };
this->PushDebugEvent(ams::svc::DebugEvent_CreateThread, params, util::size(params));
}
}
}
@ -315,7 +316,8 @@ namespace ams::kern {
}
/* Send an exception event to represent our attaching. */
this->PushDebugEvent(ams::svc::DebugEvent_Exception, ams::svc::DebugException_DebuggerAttached);
const uintptr_t params[1] = { static_cast<uintptr_t>(ams::svc::DebugException_DebuggerAttached) };
this->PushDebugEvent(ams::svc::DebugEvent_Exception, params, util::size(params));
/* Signal. */
this->NotifyAvailable();
@ -353,22 +355,22 @@ namespace ams::kern {
/* Get the currently active threads. */
constexpr u64 ThreadIdNoThread = -1ll;
constexpr u64 ThreadIdUnknownThread = -2ll;
u64 thread_ids[cpu::NumCores];
for (size_t i = 0; i < util::size(thread_ids); ++i) {
uintptr_t debug_info_params[1 + cpu::NumCores] = { static_cast<uintptr_t>(ams::svc::DebugException_DebuggerBreak), };
for (size_t i = 0; i < cpu::NumCores; ++i) {
/* Get the currently running thread. */
KThread *thread = target->GetRunningThread(i);
/* Check that the thread's idle count is correct. */
if (target->GetRunningThreadIdleCount(i) == Kernel::GetScheduler(i).GetIdleCount()) {
if (thread != nullptr && static_cast<size_t>(thread->GetActiveCore()) == i) {
thread_ids[i] = thread->GetId();
debug_info_params[1 + i] = thread->GetId();
} else {
/* We found an unknown thread. */
thread_ids[i] = ThreadIdUnknownThread;
debug_info_params[1 + i] = ThreadIdUnknownThread;
}
} else {
/* We didn't find a thread. */
thread_ids[i] = ThreadIdNoThread;
debug_info_params[1 + i] = ThreadIdNoThread;
}
}
@ -382,11 +384,7 @@ namespace ams::kern {
}
/* Send an exception event to represent our breaking the process. */
/* TODO: How should this be handled in the case of more than 4 physical cores? */
static_assert(util::size(thread_ids) <= 4);
[&]<size_t... Ix>(std::index_sequence<Ix...>) ALWAYS_INLINE_LAMBDA {
this->PushDebugEvent(ams::svc::DebugEvent_Exception, ams::svc::DebugException_DebuggerBreak, thread_ids[Ix]...);
}(std::make_index_sequence<util::size(thread_ids)>());
this->PushDebugEvent(ams::svc::DebugEvent_Exception, debug_info_params, util::size(debug_info_params));
/* Signal. */
this->NotifyAvailable();
@ -734,7 +732,7 @@ namespace ams::kern {
R_SUCCEED();
}
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, u64 cur_thread_id, const uintptr_t *params, size_t num_params) {
/* Allocate a new event. */
KEventInfo *info = KEventInfo::Allocate();
@ -749,23 +747,33 @@ namespace ams::kern {
switch (event) {
case ams::svc::DebugEvent_CreateProcess:
{
/* ... */
/* Check parameters. */
MESOSPHERE_ASSERT(params == nullptr);
MESOSPHERE_ASSERT(num_params == 0);
}
break;
case ams::svc::DebugEvent_CreateThread:
{
/* Check parameters. */
MESOSPHERE_ASSERT(params != nullptr);
MESOSPHERE_ASSERT(num_params == 2);
/* Set the thread id. */
info->thread_id = param0;
info->thread_id = params[0];
/* Set the thread creation info. */
info->info.create_thread.thread_id = param0;
info->info.create_thread.tls_address = param1;
info->info.create_thread.thread_id = params[0];
info->info.create_thread.tls_address = params[1];
}
break;
case ams::svc::DebugEvent_ExitProcess:
{
/* Check parameters. */
MESOSPHERE_ASSERT(params != nullptr);
MESOSPHERE_ASSERT(num_params == 1);
/* Set the exit reason. */
info->info.exit_process.reason = static_cast<ams::svc::ProcessExitReason>(param0);
info->info.exit_process.reason = static_cast<ams::svc::ProcessExitReason>(params[0]);
/* Clear the thread id and flags. */
info->thread_id = 0;
@ -774,30 +782,40 @@ namespace ams::kern {
break;
case ams::svc::DebugEvent_ExitThread:
{
/* Check parameters. */
MESOSPHERE_ASSERT(params != nullptr);
MESOSPHERE_ASSERT(num_params == 2);
/* Set the thread id. */
info->thread_id = param0;
info->thread_id = params[0];
/* Set the exit reason. */
info->info.exit_thread.reason = static_cast<ams::svc::ThreadExitReason>(param1);
info->info.exit_thread.reason = static_cast<ams::svc::ThreadExitReason>(params[1]);
}
break;
case ams::svc::DebugEvent_Exception:
{
/* Check parameters. */
MESOSPHERE_ASSERT(params != nullptr);
MESOSPHERE_ASSERT(num_params >= 1);
/* 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_type = static_cast<ams::svc::DebugException>(params[0]);
info->info.exception.exception_data_count = 0;
switch (static_cast<ams::svc::DebugException>(param0)) {
switch (static_cast<ams::svc::DebugException>(params[0])) {
case ams::svc::DebugException_UndefinedInstruction:
case ams::svc::DebugException_BreakPoint:
case ams::svc::DebugException_UndefinedSystemCall:
{
info->info.exception.exception_address = param1;
MESOSPHERE_ASSERT(num_params >= 3);
info->info.exception.exception_address = params[1];
info->info.exception.exception_data_count = 1;
info->info.exception.exception_data[0] = param2;
info->info.exception.exception_data[0] = params[2];
}
break;
case ams::svc::DebugException_DebuggerAttached:
@ -809,12 +827,14 @@ namespace ams::kern {
break;
case ams::svc::DebugException_UserBreak:
{
info->info.exception.exception_address = param1;
MESOSPHERE_ASSERT(num_params >= 2);
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;
info->info.exception.exception_address = params[1];
info->info.exception.exception_data_count = 0;
for (size_t i = 2; i < num_params; ++i) {
info->info.exception.exception_data[info->info.exception.exception_data_count++] = params[i];
}
}
break;
case ams::svc::DebugException_DebuggerBreak:
@ -823,11 +843,10 @@ namespace ams::kern {
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;
info->info.exception.exception_data_count = 0;
for (size_t i = 1; i < num_params; ++i) {
info->info.exception.exception_data[info->info.exception.exception_data_count++] = params[i];
}
}
break;
case ams::svc::DebugException_MemorySystemError:
@ -840,7 +859,9 @@ namespace ams::kern {
case ams::svc::DebugException_AlignmentFault:
default:
{
info->info.exception.exception_address = param1;
MESOSPHERE_ASSERT(num_params >= 2);
info->info.exception.exception_address = params[1];
}
break;
}
@ -852,9 +873,9 @@ namespace ams::kern {
return info;
}
void KDebugBase::PushDebugEvent(ams::svc::DebugEvent event, uintptr_t param0, uintptr_t param1, uintptr_t param2, uintptr_t param3, uintptr_t param4) {
void KDebugBase::PushDebugEvent(ams::svc::DebugEvent event, const uintptr_t *params, size_t num_params) {
/* Create and enqueue and event. */
if (KEventInfo *new_info = CreateDebugEvent(event, param0, param1, param2, param3, param4, GetCurrentThread().GetId()); new_info != nullptr) {
if (KEventInfo *new_info = CreateDebugEvent(event, GetCurrentThread().GetId(), params, num_params); new_info != nullptr) {
this->EnqueueDebugEventInfo(new_info);
}
}
@ -961,7 +982,10 @@ namespace ams::kern {
break;
case ams::svc::DebugException_DebuggerBreak:
{
MESOSPHERE_ASSERT(info->info.exception.exception_data_count == 4);
/* TODO: How does this work with non-4 cpu count? */
static_assert(cpu::NumCores <= 4);
MESOSPHERE_ASSERT(info->info.exception.exception_data_count == cpu::NumCores);
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];
@ -1075,7 +1099,7 @@ namespace ams::kern {
return !empty || !m_is_attached || this->GetProcessUnsafe()->IsTerminated();
}
Result KDebugBase::ProcessDebugEvent(ams::svc::DebugEvent event, uintptr_t param0, uintptr_t param1, uintptr_t param2, uintptr_t param3, uintptr_t param4) {
Result KDebugBase::ProcessDebugEvent(ams::svc::DebugEvent event, const uintptr_t *params, size_t num_params) {
/* Get the current process. */
KProcess *process = GetCurrentProcessPointer();
@ -1117,7 +1141,7 @@ namespace ams::kern {
}
/* Push the event. */
debug->PushDebugEvent(event, param0, param1, param2, param3, param4);
debug->PushDebugEvent(event, params, num_params);
debug->NotifyAvailable();
/* Set the process as breaked. */
@ -1153,9 +1177,9 @@ namespace ams::kern {
R_SUCCEED();
}
Result KDebugBase::OnDebugEvent(ams::svc::DebugEvent event, uintptr_t param0, uintptr_t param1, uintptr_t param2, uintptr_t param3, uintptr_t param4) {
Result KDebugBase::OnDebugEvent(ams::svc::DebugEvent event, const uintptr_t *params, size_t num_params) {
if (KProcess *process = GetCurrentProcessPointer(); process != nullptr && process->IsAttachedToDebugger()) {
R_RETURN(ProcessDebugEvent(event, param0, param1, param2, param3, param4));
R_RETURN(ProcessDebugEvent(event, params, num_params));
}
R_SUCCEED();
}
@ -1170,7 +1194,8 @@ namespace ams::kern {
/* Push the event. */
if (KDebugBase *debug = GetDebugObject(process); debug != nullptr) {
debug->PushDebugEvent(ams::svc::DebugEvent_ExitProcess, ams::svc::ProcessExitReason_ExitProcess);
const uintptr_t params[1] = { static_cast<uintptr_t>(ams::svc::ProcessExitReason_ExitProcess) };
debug->PushDebugEvent(ams::svc::DebugEvent_ExitProcess, params, util::size(params));
debug->NotifyAvailable();
}
}
@ -1188,7 +1213,8 @@ namespace ams::kern {
/* Push the event. */
if (KDebugBase *debug = GetDebugObject(process); debug != nullptr) {
debug->PushDebugEvent(ams::svc::DebugEvent_ExitProcess, ams::svc::ProcessExitReason_TerminateProcess);
const uintptr_t params[1] = { static_cast<uintptr_t>(ams::svc::ProcessExitReason_TerminateProcess) };
debug->PushDebugEvent(ams::svc::DebugEvent_ExitProcess, params, util::size(params));
debug->NotifyAvailable();
}
}
@ -1202,7 +1228,8 @@ namespace ams::kern {
/* Check if we're attached to a debugger. */
if (KProcess *process = thread->GetOwnerProcess(); process != nullptr && process->IsAttachedToDebugger()) {
/* If we are, submit the event. */
R_TRY(OnDebugEvent(ams::svc::DebugEvent_ExitThread, thread->GetId(), thread->IsTerminationRequested() ? ams::svc::ThreadExitReason_TerminateThread : ams::svc::ThreadExitReason_ExitThread));
const uintptr_t params[2] = { thread->GetId(), static_cast<uintptr_t>(thread->IsTerminationRequested() ? ams::svc::ThreadExitReason_TerminateThread : ams::svc::ThreadExitReason_ExitThread) };
R_TRY(OnDebugEvent(ams::svc::DebugEvent_ExitThread, params, util::size(params)));
}
R_SUCCEED();

View file

@ -1242,7 +1242,8 @@ namespace ams::kern {
MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread());
if (m_is_jit_debug) {
return KDebugBase::CreateDebugEvent(m_jit_debug_event_type, m_jit_debug_exception_type, m_jit_debug_params[0], m_jit_debug_params[1], m_jit_debug_params[2], m_jit_debug_params[3], m_jit_debug_thread_id);
const uintptr_t params[5] = { m_jit_debug_exception_type, m_jit_debug_params[0], m_jit_debug_params[1], m_jit_debug_params[2], m_jit_debug_params[3] };
return KDebugBase::CreateDebugEvent(m_jit_debug_event_type, m_jit_debug_thread_id, params, util::size(params));
} else {
return nullptr;
}