/* * Copyright (c) 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 . */ #pragma once #include #include #include #include #include namespace ams::kern { class KDebugBase : public KSynchronizationObject { protected: using DebugEventList = util::IntrusiveListBaseTraits::ListType; private: DebugEventList m_event_info_list; u32 m_continue_flags; KSharedAutoObject m_process_holder; KLightLock m_lock; KProcess::State m_old_process_state; bool m_is_attached; bool m_is_force_debug_prod; public: explicit KDebugBase() { /* ... */ } protected: bool Is64Bit() const; public: void Initialize(); void Finalize(); Result Attach(KProcess *process); Result BreakProcess(); Result TerminateProcess(); Result ContinueDebug(const u32 flags, const u64 *thread_ids, size_t num_thread_ids); Result QueryMemoryInfo(ams::svc::MemoryInfo *out_memory_info, ams::svc::PageInfo *out_page_info, KProcessAddress address); Result ReadMemory(KProcessAddress buffer, KProcessAddress address, size_t size); Result WriteMemory(KProcessAddress buffer, KProcessAddress address, size_t size); Result GetThreadContext(ams::svc::ThreadContext *out, u64 thread_id, u32 context_flags); Result SetThreadContext(const ams::svc::ThreadContext &ctx, u64 thread_id, u32 context_flags); Result GetRunningThreadInfo(ams::svc::LastThreadContext *out_context, u64 *out_thread_id); Result GetDebugEventInfo(ams::svc::lp64::DebugEventInfo *out); Result GetDebugEventInfo(ams::svc::ilp32::DebugEventInfo *out); ALWAYS_INLINE bool IsAttached() const { return m_is_attached; } ALWAYS_INLINE bool IsForceDebugProd() const { return m_is_force_debug_prod; } ALWAYS_INLINE bool OpenProcess() { return m_process_holder.Open(); } ALWAYS_INLINE void CloseProcess() { return m_process_holder.Close(); } ALWAYS_INLINE KProcess *GetProcessUnsafe() const { return m_process_holder.Get(); } private: void PushDebugEvent(ams::svc::DebugEvent event, const uintptr_t *params, size_t num_params); void EnqueueDebugEventInfo(KEventInfo *info); template requires (std::same_as || std::same_as) Result GetDebugEventInfoImpl(T *out); public: virtual bool IsSignaled() const override; private: /* NOTE: This is public/virtual override in Nintendo's kernel. */ void OnFinalizeSynchronizationObject(); private: static Result ProcessDebugEvent(ams::svc::DebugEvent event, const uintptr_t *params, size_t num_params); public: 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, u64 thread_id, const uintptr_t *params, size_t num_params); }; }