From dd7f0b805b1903ea6e88312822aef397fbb1cb06 Mon Sep 17 00:00:00 2001 From: TuxSH <1922548+TuxSH@users.noreply.github.com> Date: Fri, 7 Feb 2020 02:08:03 +0000 Subject: [PATCH] thermosphere: partially rewrite gdb context top-level --- thermosphere/src/debug_manager.h | 4 +- thermosphere/src/gdb/context.cpp | 244 ------------------- thermosphere/src/gdb/hvisor_gdb_context.cpp | 255 ++++++++++++++++++++ thermosphere/src/gdb/hvisor_gdb_context.hpp | 29 ++- 4 files changed, 279 insertions(+), 253 deletions(-) delete mode 100644 thermosphere/src/gdb/context.cpp create mode 100644 thermosphere/src/gdb/hvisor_gdb_context.cpp diff --git a/thermosphere/src/debug_manager.h b/thermosphere/src/debug_manager.h index d41287f64..62b9bca17 100644 --- a/thermosphere/src/debug_manager.h +++ b/thermosphere/src/debug_manager.h @@ -17,10 +17,10 @@ #pragma once #include "exceptions.h" -#include "gdb/context.h" +//#include "gdb/hvisor_context.h" #include "transport_interface.h" -extern GDBContext g_gdbContext; +//extern GDBContext g_gdbContext; typedef enum DebugEventType { DBGEVENT_NONE = 0, diff --git a/thermosphere/src/gdb/context.cpp b/thermosphere/src/gdb/context.cpp deleted file mode 100644 index 6a55b37b6..000000000 --- a/thermosphere/src/gdb/context.cpp +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright (c) 2019 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 . - */ - -// Lots of code from: -/* -* This file is part of Luma3DS. -* Copyright (C) 2016-2019 Aurora Wright, TuxSH -* -* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later) -*/ - -#include - -//#include "context.h" - -#include "net.h" -#include "debug.h" - -#include "../breakpoints.h" -#include "../software_breakpoints.h" -#include "../watchpoints.h" -#include "../fpu.h" - -static TEMPORARY char g_gdbWorkBuffer[GDB_WORK_BUF_LEN]; -static TEMPORARY char g_gdbBuffer[GDB_BUF_LEN + 4 + 1]; - -static const struct{ - char command; - GDBCommandHandler handler; -} gdbCommandHandlers[] = { - { '?', GDB_HANDLER(GetStopReason) }, - //{ '!', GDB_HANDLER(EnableExtendedMode) }, // note: stubbed - //{ 'c', GDB_HANDLER(ContinueOrStepDeprecated) }, - //{ 'C', GDB_HANDLER(ContinueOrStepDeprecated) }, - { 'D', GDB_HANDLER(Detach) }, - { 'F', GDB_HANDLER(HioReply) }, - { 'g', GDB_HANDLER(ReadRegisters) }, - { 'G', GDB_HANDLER(WriteRegisters) }, - { 'H', GDB_HANDLER(SetThreadId) }, - { 'k', GDB_HANDLER(Kill) }, - { 'm', GDB_HANDLER(ReadMemory) }, - { 'M', GDB_HANDLER(WriteMemory) }, - { 'p', GDB_HANDLER(ReadRegister) }, - { 'P', GDB_HANDLER(WriteRegister) }, - { 'q', GDB_HANDLER(ReadQuery) }, - { 'Q', GDB_HANDLER(WriteQuery) }, - //{ 's', GDB_HANDLER(ContinueOrStepDeprecated) }, - //{ 'S', GDB_HANDLER(ContinueOrStepDeprecated) }, - { 'T', GDB_HANDLER(IsThreadAlive) }, - { 'v', GDB_HANDLER(VerboseCommand) }, - { 'X', GDB_HANDLER(WriteMemoryRaw) }, - { 'z', GDB_HANDLER(ToggleStopPoint) }, - { 'Z', GDB_HANDLER(ToggleStopPoint) }, -}; - -static inline GDBCommandHandler GDB_GetCommandHandler(char command) -{ - static const u32 nbHandlers = sizeof(gdbCommandHandlers) / sizeof(gdbCommandHandlers[0]); - - size_t i; - for (i = 0; i < nbHandlers && gdbCommandHandlers[i].command != command; i++); - - return i < nbHandlers ? gdbCommandHandlers[i].handler : GDB_HANDLER(Unsupported); -} - -static int GDB_ProcessPacket(GDBContext *ctx, size_t len) -{ - int ret; - - ENSURE(ctx->state != GDB_STATE_DISCONNECTED); - - // Handle the packet... - if (ctx->buffer[0] == '\x03') { - GDB_BreakAllCores(ctx); - ret = 0; - } else { - GDBCommandHandler handler = GDB_GetCommandHandler(ctx->buffer[1]); - ctx->commandData = ctx->buffer + 2; - ret = handler(ctx); - } - - - // State changes... - if (ctx->state == GDB_STATE_DETACHING) { - return -1; - } - - return ret; -} - -static size_t GDB_ReceiveDataCallback(TransportInterface *iface, void *ctxVoid) -{ - return (size_t)GDB_ReceivePacket((GDBContext *)ctxVoid); -} - -static void GDB_Disconnect(GDBContext *ctx) -{ - GDB_DetachFromContext(ctx); - - ctx->flags = 0; - ctx->state = GDB_STATE_DISCONNECTED; - - ctx->selectedThreadId = 0; - ctx->selectedThreadIdForContinuing = 0; - ctx->sentDebugEventCoreList = 0; - ctx->acknowledgedDebugEventCoreList = 0; - - ctx->attachedCoreList = 0; - ctx->sendOwnDebugEventDisallowed = false; - ctx->catchThreadEvents = false; - ctx->lastSentPacketSize = 0; - ctx->lastDebugEvent = NULL; - - ctx->processEnded = false; - ctx->processExited = false; - - ctx->noAckSent = false; - - ctx->currentHioRequestTargetAddr = 0; - memset(&ctx->currentHioRequest, 0, sizeof(PackedGdbHioRequest)); - - ctx->targetXmlLen = 0; -} - -static void GDB_ProcessDataCallback(TransportInterface *iface, void *ctxVoid, size_t sz) -{ - int r = (int)sz; - GDBContext *ctx = (GDBContext *)ctxVoid; - - if (r == -1) { - // Not sure if GDB has something to forcefully close connections over UART... - char c = '\x04'; // ctrl-D - transportInterfaceWriteData(iface, &c, 1); - GDB_Disconnect(ctx); - } - - r = GDB_ProcessPacket(ctx, sz); - if (r == -1) { - GDB_Disconnect(ctx); - } -} - -void GDB_InitializeContext(GDBContext *ctx, TransportInterfaceType ifaceType, u32 ifaceId, u32 ifaceFlags) -{ - memset(ctx, 0, sizeof(GDBContext)); - ctx->workBuffer = g_gdbWorkBuffer; - ctx->buffer = g_gdbBuffer; - ctx->transportInterface = transportInterfaceCreate( - ifaceType, - ifaceId, - ifaceFlags, - GDB_ReceiveDataCallback, - GDB_ProcessDataCallback, - ctx - ); -} - -void GDB_AttachToContext(GDBContext *ctx) -{ - // TODO: move the debug traps enable here? - - ctx->attachedCoreList = getActiveCoreMask(); - - // We're in full-stop mode at this point - // Break cores, but don't send the debug event (it will be fetched with '?') - // Initialize lastDebugEvent - - debugManagerSetReportingEnabled(true); - ctx->sendOwnDebugEventDisallowed = true; - - GDB_BreakAllCores(ctx); - - DebugEventInfo *info = debugManagerGetDebugEvent(currentCoreCtx->coreId); - info->preprocessed = true; - info->handled = true; - ctx->lastDebugEvent = info; - - ctx->state = GDB_STATE_ATTACHED; - - ctx->sendOwnDebugEventDisallowed = false; -} - -void GDB_DetachFromContext(GDBContext *ctx) -{ - removeAllWatchpoints(); - removeAllBreakpoints(); - removeAllSoftwareBreakpoints(true); - - // Reports to gdb are prevented because of "detaching" state? - - // TODO: disable debug traps - - if(ctx->flags & GDB_FLAG_TERMINATE) { - // TODO: redefine what it means for thermosphère, if anything. - ctx->processEnded = true; - ctx->processExited = false; - } - - ctx->currentHioRequestTargetAddr = 0; - memset(&ctx->currentHioRequest, 0, sizeof(PackedGdbHioRequest)); - - debugManagerSetReportingEnabled(false); - debugManagerContinueCores(getActiveCoreMask()); -} - -void GDB_AcquireContext(GDBContext *ctx) -{ - transportInterfaceAcquire(ctx->transportInterface); -} - -void GDB_ReleaseContext(GDBContext *ctx) -{ - transportInterfaceRelease(ctx->transportInterface); -} - -void GDB_MigrateRxIrq(GDBContext *ctx, u32 coreId) -{ - fpuCleanInvalidateRegisterCache(); - transportInterfaceSetInterruptAffinity(ctx->transportInterface, BIT(coreId)); -} - -GDB_DECLARE_HANDLER(Unsupported) -{ - return GDB_ReplyEmpty(ctx); -} - -GDB_DECLARE_HANDLER(EnableExtendedMode) -{ - // We don't support it for now... - return GDB_HandleUnsupported(ctx); -} diff --git a/thermosphere/src/gdb/hvisor_gdb_context.cpp b/thermosphere/src/gdb/hvisor_gdb_context.cpp new file mode 100644 index 000000000..83ff86f5e --- /dev/null +++ b/thermosphere/src/gdb/hvisor_gdb_context.cpp @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2019-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 . + */ + +// Lots of code from: +/* +* This file is part of Luma3DS. +* Copyright (C) 2016-2019 Aurora Wright, TuxSH +* +* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later) +*/ + +#include "hvisor_gdb_defines_internal.hpp" +#include "hvisor_gdb_packet_data.hpp" + +#include "../breakpoints.h" +#include "../software_breakpoints.h" +#include "../watchpoints.h" +#include "../fpu.h" +#include "../debug_manager.h" + +namespace { + + TEMPORARY char g_gdbWorkBuffer[GDB_WORK_BUF_LEN]; + TEMPORARY char g_gdbBuffer[GDB_BUF_LEN + 4 + 1]; + +} + +namespace ams::hvisor::gdb { + + void Context::Disconnect() + { + Detach(); + auto *iface = m_transportInterface; + + *this = {}; + m_transportInterface = iface; + } + + void Context::Initialize(TransportInterfaceType ifaceType, u32 ifaceId, u32 ifaceFlags) + { + m_workBuffer = g_gdbWorkBuffer; + m_buffer = g_gdbBuffer; + /*m_transportInterface = transportInterfaceCreate( + ifaceType, + ifaceId, + ifaceFlags, + GDB_ReceiveDataCallback, + GDB_ProcessDataCallback, + ctx + );*/ + } + + void Context::Attach() + { + // TODO: move the debug traps enable here? + + m_attachedCoreList = getActiveCoreMask(); + + // We're in full-stop mode at this point + // Break cores, but don't send the debug event (it will be fetched with '?') + // Initialize lastDebugEvent + + debugManagerSetReportingEnabled(true); + m_sendOwnDebugEventDisallowed = true; + + BreakAllCores(); + + DebugEventInfo *info = debugManagerGetDebugEvent(currentCoreCtx->coreId); + info->preprocessed = true; + info->handled = true; + m_lastDebugEvent = info; + + m_state = State::Attached; + + m_sendOwnDebugEventDisallowed = false; + } + + void Context::Detach() + { + removeAllWatchpoints(); + removeAllBreakpoints(); + removeAllSoftwareBreakpoints(true); + + // Reports to gdb are prevented because of "detaching" state? + + // TODO: disable debug traps + + m_currentHioRequestTargetAddr = 0; + memset(&m_currentHioRequest, 0, sizeof(PackedGdbHioRequest)); + + debugManagerSetReportingEnabled(false); + debugManagerContinueCores(getActiveCoreMask()); + } + + void Context::MigrateRxIrq(u32 coreId) const + { + fpuCleanInvalidateRegisterCache(); + //transportInterfaceSetInterruptAffinity(ctx->transportInterface, BIT(coreId)); + } + + GDB_DEFINE_HANDLER(Unsupported) + { + return ReplyEmpty(); + } + +#define COMMAND_CASE(letter, method) case letter: return GDB_HANDLER(method)(); + + int Context::ProcessPacket() + { + m_commandLetter = m_commandData[0]; + m_commandData.remove_prefix(1); + + switch (m_commandLetter) { + COMMAND_CASE('?', GetStopReason) + //COMMAND_CASE('c', ContinueOrStepDeprecated) + //COMMAND_CASE('C', ContinueOrStepDeprecated) + COMMAND_CASE('D', Detach) + COMMAND_CASE('F', HioReply) + COMMAND_CASE('g', ReadRegisters) + COMMAND_CASE('G', WriteRegisters) + COMMAND_CASE('H', SetThreadId) + COMMAND_CASE('k', Kill) + COMMAND_CASE('m', ReadMemory) + COMMAND_CASE('M', WriteMemory) + COMMAND_CASE('p', ReadRegister) + COMMAND_CASE('P', WriteRegister) + COMMAND_CASE('q', ReadQuery) + COMMAND_CASE('Q', WriteQuery) + //COMMAND_CASE('s', ContinueOrStepDeprecated) + //COMMAND_CASE('S', ContinueOrStepDeprecated) + COMMAND_CASE('T', IsThreadAlive) + COMMAND_CASE('v', VerboseCommand) + COMMAND_CASE('X', WriteMemoryRaw) + COMMAND_CASE('z', ToggleStopPoint) + COMMAND_CASE('Z', ToggleStopPoint) + + default: + return HandleUnsupported(); + } + } + +#undef COMMAND_CASE + + /* + static const struct{ + char command; + GDBCommandHandler handler; + } gdbCommandHandlers[] = { + { '?', GDB_HANDLER(GetStopReason) }, + //{ '!', GDB_HANDLER(EnableExtendedMode) }, // note: stubbed + //{ 'c', GDB_HANDLER(ContinueOrStepDeprecated) }, + //{ 'C', GDB_HANDLER(ContinueOrStepDeprecated) }, + { 'D', GDB_HANDLER(Detach) }, + { 'F', GDB_HANDLER(HioReply) }, + { 'g', GDB_HANDLER(ReadRegisters) }, + { 'G', GDB_HANDLER(WriteRegisters) }, + { 'H', GDB_HANDLER(SetThreadId) }, + { 'k', GDB_HANDLER(Kill) }, + { 'm', GDB_HANDLER(ReadMemory) }, + { 'M', GDB_HANDLER(WriteMemory) }, + { 'p', GDB_HANDLER(ReadRegister) }, + { 'P', GDB_HANDLER(WriteRegister) }, + { 'q', GDB_HANDLER(ReadQuery) }, + { 'Q', GDB_HANDLER(WriteQuery) }, + //{ 's', GDB_HANDLER(ContinueOrStepDeprecated) }, + //{ 'S', GDB_HANDLER(ContinueOrStepDeprecated) }, + { 'T', GDB_HANDLER(IsThreadAlive) }, + { 'v', GDB_HANDLER(VerboseCommand) }, + { 'X', GDB_HANDLER(WriteMemoryRaw) }, + { 'z', GDB_HANDLER(ToggleStopPoint) }, + { 'Z', GDB_HANDLER(ToggleStopPoint) }, + }; + + static inline GDBCommandHandler GDB_GetCommandHandler(char command) + { + static const u32 nbHandlers = sizeof(gdbCommandHandlers) / sizeof(gdbCommandHandlers[0]); + + size_t i; + for (i = 0; i < nbHandlers && gdbCommandHandlers[i].command != command; i++); + + return i < nbHandlers ? gdbCommandHandlers[i].handler : GDB_HANDLER(Unsupported); + } + + static int GDB_ProcessPacket(GDBContext *ctx, size_t len) + { + int ret; + + ENSURE(ctx->state != GDB_STATE_DISCONNECTED); + + // Handle the packet... + if (ctx->buffer[0] == '\x03') { + GDB_BreakAllCores(ctx); + ret = 0; + } else { + GDBCommandHandler handler = GDB_GetCommandHandler(ctx->buffer[1]); + ctx->commandData = ctx->buffer + 2; + ret = handler(ctx); + } + + + // State changes... + if (ctx->state == GDB_STATE_DETACHING) { + return -1; + } + + return ret; + } + + static size_t GDB_ReceiveDataCallback(TransportInterface *iface, void *ctxVoid) + { + return (size_t)GDB_ReceivePacket((GDBContext *)ctxVoid); + } + + static void GDB_ProcessDataCallback(TransportInterface *iface, void *ctxVoid, size_t sz) + { + int r = (int)sz; + GDBContext *ctx = (GDBContext *)ctxVoid; + + if (r == -1) { + // Not sure if GDB has something to forcefully close connections over UART... + char c = '\x04'; // ctrl-D + transportInterfaceWriteData(iface, &c, 1); + GDB_Disconnect(ctx); + } + + r = GDB_ProcessPacket(ctx, sz); + if (r == -1) { + GDB_Disconnect(ctx); + } + } + + void GDB_AcquireContext(GDBContext *ctx) + { + transportInterfaceAcquire(ctx->transportInterface); + } + + void GDB_ReleaseContext(GDBContext *ctx) + { + transportInterfaceRelease(ctx->transportInterface); + } +*/ +} diff --git a/thermosphere/src/gdb/hvisor_gdb_context.hpp b/thermosphere/src/gdb/hvisor_gdb_context.hpp index 4245cc30f..1daf53945 100644 --- a/thermosphere/src/gdb/hvisor_gdb_context.hpp +++ b/thermosphere/src/gdb/hvisor_gdb_context.hpp @@ -38,6 +38,8 @@ #define DECLARE_REMOTE_HANDLER(name) DECLARE_HANDLER(Remote##name) #define DECLARE_XFER_HANDLER(name) DECLARE_HANDLER(Xfer##name) +struct DebugEventInfo; + namespace ams::hvisor::gdb { struct PackedGdbHioRequest { @@ -58,12 +60,7 @@ namespace ams::hvisor::gdb { bool ctrlC; }; - struct DebugEventInfo; - class Context final { - NON_COPYABLE(Context); - NON_MOVEABLE(Context); - private: enum class State { Disconnected = 0, @@ -106,8 +103,20 @@ namespace ams::hvisor::gdb { char *m_buffer = nullptr; char *m_workBuffer = nullptr; + private: + Context(const Context &) = default; + Context &operator=(const Context &) = default; + + Context(Context &&) = default; + Context &operator=(Context &&) = default; + private: void MigrateRxIrq(u32 coreId) const; + int ProcessPacket(); + void Disconnect(); + + // Debug + void BreakAllCores(); // Comms int ReceivePacket(); @@ -125,12 +134,12 @@ namespace ams::hvisor::gdb { int WriteMemoryImpl(size_t (*decoder)(void *, const void *, size_t)); // Helpers - char *GetInPlaceOutputBuffer() const + constexpr char *GetInPlaceOutputBuffer() const { return m_buffer + 1; } - char *GetWorkBuffer() const + constexpr char *GetWorkBuffer() const { return m_workBuffer; } @@ -195,12 +204,18 @@ namespace ams::hvisor::gdb { DECLARE_QUERY_HANDLER(Rcmd); public: + Context() = default; + void Initialize(TransportInterfaceType ifaceType, u32 ifaceId, u32 ifaceFlags); void Attach(); void Detach(); + void lock(); + void unlock(); + /* TODO: parent void Acquire(); void Release(); + */ constexpr bool IsAttached() const {