diff --git a/thermosphere/src/gdb/hyp_gdb_context.hpp b/thermosphere/src/gdb/hvisor_gdb_context.hpp similarity index 92% rename from thermosphere/src/gdb/hyp_gdb_context.hpp rename to thermosphere/src/gdb/hvisor_gdb_context.hpp index fcc2194f9..d65b6dc63 100644 --- a/thermosphere/src/gdb/hyp_gdb_context.hpp +++ b/thermosphere/src/gdb/hvisor_gdb_context.hpp @@ -27,6 +27,8 @@ #include "../defines.hpp" #include "../transport_interface.h" +#include + #define _REENT_ONLY #include @@ -35,7 +37,7 @@ #define DECLARE_VERBOSE_HANDLER(name) DECLARE_HANDLER(Verbose##name) #define DECLARE_XFER_HANDLER(name) DECLARE_HANDLER(Xfer##name) -namespace ams::hyp::gdb { +namespace ams::hvisor::gdb { struct PackedGdbHioRequest { // TODO revamp @@ -79,8 +81,8 @@ namespace ams::hyp::gdb { u32 m_attachedCoreList = 0; - int m_selectedThreadId = 0; - int m_selectedThreadIdForContinuing = 0; + int m_selectedCoreId = 0; + int m_selectedCoreIdForContinuing = 0; u32 m_sentDebugEventCoreList = 0; u32 m_acknowledgedDebugEventCoreList = 0; @@ -97,8 +99,8 @@ namespace ams::hyp::gdb { size_t m_targetXmlLen = 0; - char *m_commandData = nullptr; - char *m_commandEnd = nullptr; + char m_commandLetter = '\0'; + std::string_view m_commandData{}; size_t m_lastSentPacketSize = 0ul; char *m_buffer = nullptr; char *m_workBuffer = nullptr; @@ -109,11 +111,12 @@ namespace ams::hyp::gdb { // Comms int ReceivePacket(); int DoSendPacket(); - int SendPacket(const char *packetData, size_t len); + int SendPacket(std::string_view packetData); int SendFormattedPacket(const char *packetDataFmt, ...); int SendHexPacket(const void *packetData, size_t len); - int SendNotificationPacket(const char *packetData, size_t len); - int SendStreamData(const char *streamData, size_t offset, size_t length, size_t totalSize, bool forceEmptyLast); + int SendNotificationPacket(std::string_view packetData); + int SendStreamData(std::string_view streamData, size_t offset, size_t length, bool forceEmptyLast); + int ReplyOk(); int ReplyEmpty(); int ReplyErrno(int no); diff --git a/thermosphere/src/gdb/hyp_gdb_defines_internal.hpp b/thermosphere/src/gdb/hvisor_gdb_defines_internal.hpp similarity index 80% rename from thermosphere/src/gdb/hyp_gdb_defines_internal.hpp rename to thermosphere/src/gdb/hvisor_gdb_defines_internal.hpp index 12595498e..9b48fb2b9 100644 --- a/thermosphere/src/gdb/hyp_gdb_defines_internal.hpp +++ b/thermosphere/src/gdb/hvisor_gdb_defines_internal.hpp @@ -24,7 +24,7 @@ #pragma once -#include "hyp_gdb_context.hpp" +#include "hvisor_gdb_context.hpp" // 512+24 is the ideal size as IDA will try to read exactly 0x100 bytes at a time. // IDA seems to want additional bytes as well. @@ -38,7 +38,7 @@ #define GDB_VERBOSE_HANDLER(name) GDB_HANDLER(Verbose##name) #define GDB_XFER_HANDLER(name) GDB_HANDLER(Xfer##name) -#define GDB_DEFINE_HANDLER(name) int Context::HANDLER(name)() -#define GDB_DEFINE_QUERY_HANDLER(name) DEFINE_HANDLER(Query##name) -#define GDB_DECLARE_VERBOSE_HANDLER(name) DEFINE_HANDLER(Verbose##name) -#define GDB_DECLARE_Xfer_HANDLER(name) DEFINE_HANDLER(Xfer##name) +#define GDB_DEFINE_HANDLER(name) int Context::GDB_HANDLER(name)() +#define GDB_DEFINE_QUERY_HANDLER(name) GDB_DEFINE_HANDLER(Query##name) +#define GDB_DECLARE_VERBOSE_HANDLER(name) GDB_DEFINE_HANDLER(Verbose##name) +#define GDB_DECLARE_XFER_HANDLER(name) GDB_DEFINE_HANDLER(Xfer##name) diff --git a/thermosphere/src/gdb/hyp_gdb_packet_data.cpp b/thermosphere/src/gdb/hvisor_gdb_packet_data.cpp similarity index 97% rename from thermosphere/src/gdb/hyp_gdb_packet_data.cpp rename to thermosphere/src/gdb/hvisor_gdb_packet_data.cpp index 555cd2fba..5432ce2f0 100644 --- a/thermosphere/src/gdb/hyp_gdb_packet_data.cpp +++ b/thermosphere/src/gdb/hvisor_gdb_packet_data.cpp @@ -16,10 +16,11 @@ #pragma once -#include "hyp_gdb_packet_data.hpp" +#include "hvisor_gdb_packet_data.hpp" #include -namespace ams::hyp::gdb { +namespace ams::hvisor::gdb { + u8 ComputeChecksum(std::string_view packetData) { return std::accumulate(packetData.cbegin(), packetData.cend(), u8{0u}); @@ -98,4 +99,5 @@ namespace ams::hyp::gdb { return dst8 - reinterpret_cast(dst); } + } diff --git a/thermosphere/src/gdb/hyp_gdb_packet_data.hpp b/thermosphere/src/gdb/hvisor_gdb_packet_data.hpp similarity index 61% rename from thermosphere/src/gdb/hyp_gdb_packet_data.hpp rename to thermosphere/src/gdb/hvisor_gdb_packet_data.hpp index e4eeb6ff6..cfee0afdd 100644 --- a/thermosphere/src/gdb/hyp_gdb_packet_data.hpp +++ b/thermosphere/src/gdb/hvisor_gdb_packet_data.hpp @@ -19,7 +19,7 @@ #include "../defines.hpp" #include -namespace ams::hyp::gdb { +namespace ams::hvisor::gdb { constexpr unsigned long DecodeHexDigit(char src) { @@ -36,7 +36,9 @@ namespace ams::hyp::gdb { { unsigned long res = 0; long mult = 1; - auto errval = std::tuple{false, 0ul, str.end()}; + auto errval = std::tuple{0ul, 0ul}; + + size_t total = 0; if ((base == 0 && !allowPrefix) || base > 16 || str.empty()) { return errval; @@ -48,12 +50,14 @@ namespace ams::hyp::gdb { return errval; } str.remove_prefix(1); + ++total; } else if (str[0] == '-') { if (!allowPrefix) { return errval; } str.remove_prefix(1); mult = -1; + ++total; } if (str.empty()) { @@ -68,11 +72,9 @@ namespace ams::hyp::gdb { } else { str.remove_prefix(2); base = 16; + total += 2; } - } else if (base == 0 || str[0] == '0') { - if (!allowPrefix) { - return errval; - } + } else if (base == 0 && str[0] == '0') { base = 8; } else if (base == 0) { base = 10; @@ -92,13 +94,61 @@ namespace ams::hyp::gdb { res *= base; res += v; + ++total; } - return std::tuple{true, res * mult, it}; + return std::tuple{total, res * mult}; } - std::string_view::iterator ParseIntegerList(unsigned long *dst, std::string_view str, size_t nb, char sep, char lastSep, u32 base, bool allowPrefix); - std::string_view::iterator ParseHexIntegerList(unsigned long *dst, std::string_view str, size_t nb, char lastSep); + template + constexpr auto ParseIntegerList(std::string_view str, u32 base, bool allowPrefix, char sep, char lastSep = '\0') + { + // First element is parsed size + std::array res{ 0 }; + + size_t total = 0; + for (size_t i = 0; i < N && !str.empty(); i++) { + auto [nread, val] = ParseInteger(str, base, allowPrefix); + + // Parse failure + if (nread == 0) { + return res; + } + + str.remove_prefix(nread); + + // Check separators + if (i != N - 1) { + if (str.empty() || str[0] != sep)) { + return res; + } + str.remove_prefix(1); + ++total; + } else if (i == N - 1) { + if (lastSep == '\0') && !str.empty()) { + return res; + } else if (lastSep != '\0') { + if (str.empty() || str[0] != lastSep)) { + return res; + } + str.remove_prefix(1); + ++total; + } + } + + total += nread; + res[1 + i] = val; + } + + res[0] = total; + return res; + } + + template + constexpr auto ParseHexIntegerList(std::string_view str, char lastSep = '\0') + { + return ParseIntegerList(str, 16, false, ',', lastSep); + } u8 ComputeChecksum(std::string_view packetData); size_t EncodeHex(char *dst, const void *src, size_t len); diff --git a/thermosphere/src/gdb/hvisor_gdb_thread.cpp b/thermosphere/src/gdb/hvisor_gdb_thread.cpp new file mode 100644 index 000000000..8c9780025 --- /dev/null +++ b/thermosphere/src/gdb/hvisor_gdb_thread.cpp @@ -0,0 +1,147 @@ +/* +* 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 "hvisor_gdb_defines_internal.hpp" +#include "hvisor_gdb_packet_data.hpp" + +#include "../core_ctx.h" + +namespace { +} + +namespace ams::hvisor::gdb { + + int ConvertTidToCoreId(unsigned long tid) + { + switch (tid) { + case ULONG_MAX: + return -1; + case 0: + return currentCoreCtx->coreId; + default: + return currentCoreCtx->coreId - 1; + } + } + + std::optional ParseConvertExactlyOneTid(std::string_view str) + { + if (str.size() == 2 && str[0] == '-' && str[1] == '1') { + return -1; + } else { + auto [n, tid] = ParseHexIntegerList<1>(str); + if (n != 0 && tid < MAX_CORE + 1) { + return ConvertTidToCoreId(tid); + } else { + return {}; + } + } + } + + // Hg, Hc + GDB_DEFINE_HANDLER(SetThreadId) + { + if (!m_commandData.starts_with('g') && !m_commandData.starts_with('c')) { + return ReplyErrno(EINVAL); + } + + char kind = m_commandData[0]; + m_commandData.remove_prefix(1); + + auto coreIdOpt = ParseConvertExactlyOneTid(m_commandData); + if (!coreIdOpt) { + return ReplyErrno(EILSEQ); + } + + int coreId = *coreIdOpt; + if (kind == 'g') { + if (coreId = -1) { + return ReplyErrno(EINVAL); + } + m_selectedCoreId = coreId; + MigrateRxIrq(m_selectedCoreId); + } else { + m_selectedCoreIdForContinuing = coreId; + } + + return ReplyOk(); + } + + GDB_DEFINE_HANDLER(IsThreadAlive) + { + int coreId = ParseConvertExactlyOneTid(m_commandData).value_or(-1); + if (coreId < 0) { + return ReplyErrno(EILSEQ); + } + + // Is the core off? + if (m_attachedCoreList & BIT(coreId)) { + return ReplyOk(); + } else { + return ReplyErrno(ESRCH); + } + } + + GDB_DEFINE_QUERY_HANDLER(CurrentThreadId) + { + return SendFormattedPacket("QC%x", 1 + currentCoreCtx->coreId); + } + + GDB_DEFINE_QUERY_HANDLER(fThreadInfo) + { + // We have made our GDB packet big enough to list all the thread ids (coreIds + 1 for each coreId) + char *buf = GetInPlaceOutputBuffer(); + size_t n = 0; + + for (int coreId: util::BitsOf{m_attachedCoreList}) { + n += sprintf(buf + n, "%lx,", 1u + coreId); + } + + // Remove trailing comma + --n; + + return SendStreamData(std::string_view{buf, n}, 0, n, true); + } + + GDB_DEFINE_QUERY_HANDLER(sThreadInfo) + { + // We have made our GDB packet big enough to list all the thread ids (coreIds + 1 for each coreId) in fThreadInfo + // Note: we assume GDB doesn't accept notifications during the sequence transfer... + return SendPacket("l"); + } + + GDB_DEFINE_QUERY_HANDLER(ThreadEvents) + { + if (m_commandData.size() != 1) { + return ReplyErrno(EILSEQ); + } + + switch (m_commandData[0]) { + case '0': + m_catchThreadEvents = false; + return ReplyOk(); + case '1': + m_catchThreadEvents = true; + return ReplyOk(); + default: + return ReplyErrno(EILSEQ); + } + } + + GDB_DEFINE_QUERY_HANDLER(ThreadExtraInfo) + { + int coreId = ParseConvertExactlyOneTid(m_commandData).value_or(-1); + if (coreId < 0) { + return ReplyErrno(EILSEQ); + } + + size_t n = sprintf(m_workBuffer, "TODO"); + + return SendHexPacket(m_workBuffer, n); + } +} diff --git a/thermosphere/src/gdb/hvisor_gdb_thread.hpp b/thermosphere/src/gdb/hvisor_gdb_thread.hpp new file mode 100644 index 000000000..9d886fd7e --- /dev/null +++ b/thermosphere/src/gdb/hvisor_gdb_thread.hpp @@ -0,0 +1,26 @@ +/* + * 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 . + */ + +#pragma once + +#include "hvisor_gdb_context.hpp" + +namespace ams::hvisor::gdb { + + int ConvertTidToCoreId(unsigned long tid); + std::optional ParseConvertExactlyOneTid(std::string_view str); + +} diff --git a/thermosphere/src/gdb/regs.h b/thermosphere/src/gdb/regs.h index 528c1bc9a..0f694e4f3 100644 --- a/thermosphere/src/gdb/regs.h +++ b/thermosphere/src/gdb/regs.h @@ -8,8 +8,3 @@ #pragma once #include "context.h" - -GDB_DECLARE_HANDLER(ReadRegisters); -GDB_DECLARE_HANDLER(WriteRegisters); -GDB_DECLARE_HANDLER(ReadRegister); -GDB_DECLARE_HANDLER(WriteRegister); diff --git a/thermosphere/src/gdb/thread.c b/thermosphere/src/gdb/thread.c deleted file mode 100644 index d7fa9a302..000000000 --- a/thermosphere/src/gdb/thread.c +++ /dev/null @@ -1,119 +0,0 @@ -/* -* 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 - -#include "thread.h" -#include "net.h" -#include "../core_ctx.h" - -GDB_DECLARE_HANDLER(SetThreadId) -{ - // Id = 0 means any thread - if (ctx->commandData[0] == 'g') { - if(strcmp(ctx->commandData + 1, "-1") == 0) { - return GDB_ReplyErrno(ctx, EINVAL); - } - - unsigned long id; - if (GDB_ParseHexIntegerList(&id, ctx->commandData + 1, 1, 0) == NULL) { - return GDB_ReplyErrno(ctx, EILSEQ); - } else if (id >= MAX_CORE + 1) { - return GDB_ReplyErrno(ctx, EINVAL); - } - - ctx->selectedThreadId = id == 0 ? (int)currentCoreCtx->coreId + 1 : (int)id; - GDB_MigrateRxIrq(ctx, (u32)(ctx->selectedThreadId - 1)); - return GDB_ReplyOk(ctx); - } else if (ctx->commandData[0] == 'c') { - if(strcmp(ctx->commandData + 1, "-1") == 0) { - ctx->selectedThreadIdForContinuing = -1; - } else { - unsigned long id; - if (GDB_ParseHexIntegerList(&id, ctx->commandData + 1, 1, 0) == NULL) { - return GDB_ReplyErrno(ctx, EILSEQ); - } else if (id >= MAX_CORE + 1) { - return GDB_ReplyErrno(ctx, EINVAL); - } - ctx->selectedThreadIdForContinuing = id == 0 ? (int)currentCoreCtx->coreId + 1 : (int)id; - } - - return GDB_ReplyOk(ctx); - } - else - return GDB_ReplyErrno(ctx, EPERM); -} - -GDB_DECLARE_HANDLER(IsThreadAlive) -{ - unsigned long threadId; - - if (GDB_ParseHexIntegerList(&threadId, ctx->commandData, 1, 0) == NULL || threadId < 1) { - return GDB_ReplyErrno(ctx, EILSEQ); - } - - u32 coreMask = ctx->attachedCoreList; - return (coreMask & BIT(threadId - 1)) != 0 ? GDB_ReplyOk(ctx) : GDB_ReplyErrno(ctx, ESRCH); -} - -GDB_DECLARE_QUERY_HANDLER(CurrentThreadId) -{ - return GDB_SendFormattedPacket(ctx, "QC%x", 1 + currentCoreCtx->coreId); -} - -GDB_DECLARE_QUERY_HANDLER(fThreadInfo) -{ - // We have made our GDB packet big enough to list all the thread ids (coreIds + 1 for each coreId) - char *buf = ctx->buffer + 1; - size_t n = 0; - - u32 coreMask = ctx->attachedCoreList; - - FOREACH_BIT (tmp, coreId, coreMask) { - n += sprintf(buf + n, "%lx,", 1 + coreId); - } - - // Remove trailing comma - buf[--n] = 0; - - return GDB_SendStreamData(ctx, buf, 0, n, n, true); -} - -GDB_DECLARE_QUERY_HANDLER(sThreadInfo) -{ - // We have made our GDB packet big enough to list all the thread ids (coreIds + 1 for each coreId) in fThreadInfo - // Note: we assume GDB doesn't accept notifications during the sequence transfer... - return GDB_SendPacket(ctx, "l", 1); -} - -GDB_DECLARE_QUERY_HANDLER(ThreadEvents) -{ - switch (ctx->commandData[0]) { - case '0': - ctx->catchThreadEvents = false; - return GDB_ReplyOk(ctx); - case '1': - ctx->catchThreadEvents = true; - return GDB_ReplyOk(ctx); - default: - return GDB_ReplyErrno(ctx, EILSEQ); - } -} - -GDB_DECLARE_QUERY_HANDLER(ThreadExtraInfo) -{ - unsigned long id; - int n; - - if(GDB_ParseHexIntegerList(&id, ctx->commandData, 1, 0) == NULL) - return GDB_ReplyErrno(ctx, EILSEQ); - - n = sprintf(ctx->workBuffer, "TODO"); - - return GDB_SendHexPacket(ctx, ctx->workBuffer, n); -} diff --git a/thermosphere/src/gdb/thread.h b/thermosphere/src/gdb/thread.h deleted file mode 100644 index 5f35064b2..000000000 --- a/thermosphere/src/gdb/thread.h +++ /dev/null @@ -1,20 +0,0 @@ -/* -* This file is part of Luma3DS. -* Copyright (C) 2016-2019 Aurora Wright, TuxSH -* -* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later) -*/ - -#pragma once - -#include "context.h" - -GDB_DECLARE_HANDLER(SetThreadId); -GDB_DECLARE_HANDLER(IsThreadAlive); - -GDB_DECLARE_QUERY_HANDLER(CurrentThreadId); -GDB_DECLARE_QUERY_HANDLER(fThreadInfo); -GDB_DECLARE_QUERY_HANDLER(sThreadInfo); -GDB_DECLARE_QUERY_HANDLER(ThreadEvents); -GDB_DECLARE_QUERY_HANDLER(ThreadExtraInfo); -GDB_DECLARE_QUERY_HANDLER(GetTLSAddr);