diff --git a/thermosphere/src/gdb/hvisor_gdb_context.cpp b/thermosphere/src/gdb/hvisor_gdb_context.cpp index 83ff86f5e..0a9eabd64 100644 --- a/thermosphere/src/gdb/hvisor_gdb_context.cpp +++ b/thermosphere/src/gdb/hvisor_gdb_context.cpp @@ -137,8 +137,8 @@ namespace ams::hvisor::gdb { COMMAND_CASE('M', WriteMemory) COMMAND_CASE('p', ReadRegister) COMMAND_CASE('P', WriteRegister) - COMMAND_CASE('q', ReadQuery) - COMMAND_CASE('Q', WriteQuery) + COMMAND_CASE('q', Query) + COMMAND_CASE('Q', Query) //COMMAND_CASE('s', ContinueOrStepDeprecated) //COMMAND_CASE('S', ContinueOrStepDeprecated) COMMAND_CASE('T', IsThreadAlive) diff --git a/thermosphere/src/gdb/hvisor_gdb_context.hpp b/thermosphere/src/gdb/hvisor_gdb_context.hpp index 1daf53945..d098c5ad6 100644 --- a/thermosphere/src/gdb/hvisor_gdb_context.hpp +++ b/thermosphere/src/gdb/hvisor_gdb_context.hpp @@ -36,7 +36,7 @@ #define DECLARE_QUERY_HANDLER(name) DECLARE_HANDLER(Query##name) #define DECLARE_VERBOSE_HANDLER(name) DECLARE_HANDLER(Verbose##name) #define DECLARE_REMOTE_HANDLER(name) DECLARE_HANDLER(Remote##name) -#define DECLARE_XFER_HANDLER(name) DECLARE_HANDLER(Xfer##name) +#define DECLARE_XFER_HANDLER(name) int HandleXfer##name(bool write, std::string_view annex, size_t offset, size_t length) struct DebugEventInfo; @@ -95,7 +95,7 @@ namespace ams::hvisor::gdb { uintptr_t m_currentHioRequestTargetAddr = 0ul; PackedGdbHioRequest m_currentHioRequest{}; - size_t m_targetXmlLen = 0; + std::string_view m_targetXml{}; char m_commandLetter = '\0'; std::string_view m_commandData{}; @@ -146,8 +146,7 @@ namespace ams::hvisor::gdb { private: // Meta DECLARE_HANDLER(Unsupported); - DECLARE_HANDLER(ReadQuery); - DECLARE_HANDLER(WriteQuery); + DECLARE_HANDLER(Query); DECLARE_QUERY_HANDLER(Xfer); DECLARE_HANDLER(VerboseCommand); diff --git a/thermosphere/src/gdb/hvisor_gdb_defines_internal.hpp b/thermosphere/src/gdb/hvisor_gdb_defines_internal.hpp index 453079a80..f7183e32e 100644 --- a/thermosphere/src/gdb/hvisor_gdb_defines_internal.hpp +++ b/thermosphere/src/gdb/hvisor_gdb_defines_internal.hpp @@ -43,6 +43,7 @@ #define GDB_DEFINE_QUERY_HANDLER(name) GDB_DEFINE_HANDLER(Query##name) #define GDB_DEFINE_VERBOSE_HANDLER(name) GDB_DEFINE_HANDLER(Verbose##name) #define GDB_DEFINE_REMOTE_COMMAND_HANDLER(name) GDB_DEFINE_HANDLER(RemoteCommand##name) -#define GDB_DECLARE_XFER_HANDLER(name) GDB_DEFINE_HANDLER(Xfer##name) +#define GDB_DEFINE_XFER_HANDLER(name)\ + int Context::GDB_XFER_HANDLER(name)(bool write, std::string_view annex, size_t offset, size_t length) -#define GDB_TEST_NO_CMD_DATA() do { if (!m_commandData.empty()) return ReplyErrno(EILSEQ); } while (false) +#define GDB_CHECK_NO_CMD_DATA() do { if (!m_commandData.empty()) return ReplyErrno(EILSEQ); } while (false) diff --git a/thermosphere/src/gdb/hvisor_gdb_packet_data.hpp b/thermosphere/src/gdb/hvisor_gdb_packet_data.hpp index 838b37bbf..002b31812 100644 --- a/thermosphere/src/gdb/hvisor_gdb_packet_data.hpp +++ b/thermosphere/src/gdb/hvisor_gdb_packet_data.hpp @@ -119,16 +119,16 @@ namespace ams::hvisor::gdb { // Check separators if (i != N - 1) { - if (str.empty() || str[0] != sep)) { + if (str.empty() || str[0] != sep) { return res; } str.remove_prefix(1); ++total; } else if (i == N - 1) { - if (lastSep == '\0') && !str.empty()) { + if ((lastSep == '\0') && !str.empty()) { return res; } else if (lastSep != '\0') { - if (str.empty() || str[0] != lastSep)) { + if (str.empty() || str[0] != lastSep) { return res; } str.remove_prefix(1); @@ -150,6 +150,28 @@ namespace ams::hvisor::gdb { return ParseIntegerList(str, 16, false, ',', lastSep); } + template + constexpr auto SplitString(std::string_view data, char delim) + { + static_assert(N != 0); + + std::array res = {}; + size_t delimPos = 0; + + for (size_t i = 0; i < N - 1; i++) { + delimPos = data.find(delim); + if (delimPos == std::string_view::npos) { + return res; + } + + res[i] = std::string_view{data.data(), delimPos}; + data.remove_prefix(delimPos + 1); + } + res[N - 1] = data; + + return res; + } + constexpr std::optional DecodeHexByte(std::string_view data) { if (data.size() < 2) { diff --git a/thermosphere/src/gdb/hvisor_gdb_query.cpp b/thermosphere/src/gdb/hvisor_gdb_query.cpp new file mode 100644 index 000000000..d0302f6a4 --- /dev/null +++ b/thermosphere/src/gdb/hvisor_gdb_query.cpp @@ -0,0 +1,107 @@ +/* + * 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 . + */ + +/* +* 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" + +namespace ams::hvisor::gdb { + + GDB_DEFINE_QUERY_HANDLER(Supported) + { + // Ignore what gdb sent... + return SendFormattedPacket( + "PacketSize=%x;" + "qXfer:features:read+;" + "QStartNoAckMode+;QThreadEvents+" + "vContSupported+;swbreak+;hwbreak+", + + GDB_BUF_LEN + ); + } + + GDB_DEFINE_QUERY_HANDLER(StartNoAckMode) + { + GDB_CHECK_NO_CMD_DATA(); + + m_noAckSent = true; + return ReplyOk(); + } + + GDB_DEFINE_QUERY_HANDLER(Attached) + { + GDB_CHECK_NO_CMD_DATA(); + + return SendPacket("1"); + } + +#define QUERY_CMD_CASE2(name, fun) if (cmdName==name) { return GDB_QUERY_HANDLER(fun)(); } else +#define QUERY_CMD_CASE(fun) QUERY_CMD_CASE2(STRINGIZE(fun), fun) + + GDB_DEFINE_HANDLER(Query) + { + // Extract name + char delim = ':'; + + size_t delimPos = m_commandData.find_first_of(":,"); + std::string_view cmdName = m_commandData; + if (delimPos != std::string_view::npos) { + delim = m_commandData[delimPos]; + cmdName.remove_suffix(cmdName.size() - delimPos); + m_commandData.remove_prefix(delimPos + 1); + } + + // Only 2 commands are delimited by a comma, all with lowercase 'q' prefix + // We don't handle qP nor qL + if (delim != ':') { + if (m_commandLetter != 'q') { + return ReplyErrno(EILSEQ); + } else if (cmdName != "Rcmd" && cmdName != "ThreadExtraInfo") { + return ReplyErrno(EILSEQ); + } + } + + if (m_commandLetter == 'q') { + QUERY_CMD_CASE(Supported) + QUERY_CMD_CASE(Xfer) + QUERY_CMD_CASE(Attached) + QUERY_CMD_CASE(fThreadInfo) + QUERY_CMD_CASE(sThreadInfo) + QUERY_CMD_CASE(ThreadExtraInfo) + QUERY_CMD_CASE2("C", CurrentThreadId) + QUERY_CMD_CASE(Rcmd) + /*default :*/{ + return HandleUnsupported(); + } + } else { + QUERY_CMD_CASE(StartNoAckMode) + QUERY_CMD_CASE(ThreadEvents) + /*default :*/{ + return HandleUnsupported(); + } + } + } + + #undef QUERY_CMD_CASE + #undef QUERY_CMD_CASE2 + +} \ No newline at end of file diff --git a/thermosphere/src/gdb/hvisor_gdb_regs.cpp b/thermosphere/src/gdb/hvisor_gdb_regs.cpp index 5efc61165..e0e4e6a02 100644 --- a/thermosphere/src/gdb/hvisor_gdb_regs.cpp +++ b/thermosphere/src/gdb/hvisor_gdb_regs.cpp @@ -75,7 +75,7 @@ namespace ams::hvisor::gdb { GDB_DEFINE_HANDLER(ReadRegisters) { ENSURE(m_selectedCoreId == currentCoreCtx->coreId); - GDB_TEST_NO_CMD_DATA(); + GDB_CHECK_NO_CMD_DATA(); ExceptionStackFrame *frame = currentCoreCtx->guestFrame; FpuRegisterCache *fpuRegCache = fpuReadRegisters(); diff --git a/thermosphere/src/gdb/hvisor_gdb_thread.cpp b/thermosphere/src/gdb/hvisor_gdb_thread.cpp index 6873d2d47..6bc3eb9ed 100644 --- a/thermosphere/src/gdb/hvisor_gdb_thread.cpp +++ b/thermosphere/src/gdb/hvisor_gdb_thread.cpp @@ -96,13 +96,13 @@ namespace ams::hvisor::gdb { GDB_DEFINE_QUERY_HANDLER(CurrentThreadId) { - GDB_TEST_NO_CMD_DATA(); + GDB_CHECK_NO_CMD_DATA(); return SendFormattedPacket("QC%x", 1 + currentCoreCtx->coreId); } GDB_DEFINE_QUERY_HANDLER(fThreadInfo) { - GDB_TEST_NO_CMD_DATA(); + GDB_CHECK_NO_CMD_DATA(); // We have made our GDB packet big enough to list all the thread ids (coreIds + 1 for each coreId) char *buf = GetInPlaceOutputBuffer(); @@ -120,7 +120,7 @@ namespace ams::hvisor::gdb { GDB_DEFINE_QUERY_HANDLER(sThreadInfo) { - GDB_TEST_NO_CMD_DATA(); + GDB_CHECK_NO_CMD_DATA(); // 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... diff --git a/thermosphere/src/gdb/hvisor_gdb_xfer.cpp b/thermosphere/src/gdb/hvisor_gdb_xfer.cpp new file mode 100644 index 000000000..9e83bdb31 --- /dev/null +++ b/thermosphere/src/gdb/hvisor_gdb_xfer.cpp @@ -0,0 +1,143 @@ +/* + * 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 . + */ + +/* +* 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" + +namespace { + + std::string_view GenerateTargetXml(char *buf) + { + int pos; + const char *hdr = ""; + const char *cpuDescBegin = ""; + const char *cpuDescEnd = + "" + "" + ""; + + const char *fpuDescBegin = + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + ""; + + const char *fpuDescEnd = "\r\n\r\n"; + const char *footer = ""; + + std::strcpy(buf, hdr); + + // CPU registers + std::strcat(buf, cpuDescBegin); + pos = static_cast(std::strlen(buf)); + for (u32 i = 0; i < 31; i++) { + pos += std::sprintf(buf + pos, "", i); + } + std::strcat(buf, cpuDescEnd); + + std::strcat(buf, fpuDescBegin); + pos = static_cast(std::strlen(buf)); + for (u32 i = 0; i < 32; i++) { + pos += std::sprintf(buf + pos, "", i); + } + std::strcat(buf, fpuDescEnd); + + std::strcat(buf, footer); + return std::string_view{buf}; + } + +} + +namespace ams::hvisor::gdb { + + GDB_DEFINE_XFER_HANDLER(Features) + { + if (write || annex != "target.xml") { + return ReplyEmpty(); + } + + // Generate the target xml on-demand + // This is a bit whack, we rightfully assume that GDB won't sent any other command during the stream transfer + if (m_targetXml.empty()) { + m_targetXml = GenerateTargetXml(m_workBuffer); + } + + int n = SendStreamData(m_targetXml, offset, length, false); + + // Transfer ended + if(offset + length >= m_targetXml.size()) { + m_targetXml = {}; + } + + return n; + } + + GDB_DEFINE_QUERY_HANDLER(Xfer) + { + // e.g. qXfer:features:read:annex:offset,length + + // Split + auto [cmd, directionStr, annex, offsetlen] = SplitString<4>(m_commandData, ':'); + if (offsetlen.empty()) { + return ReplyErrno(EILSEQ); + } + + // Check direction + bool isWrite; + if (directionStr == "read") { + isWrite = false; + } else if (directionStr == "write") { + isWrite = true; + } else { + return ReplyErrno(EILSEQ); + } + + // Get offset and length + auto [nread, off, len] = ParseHexIntegerList<2>(offsetlen, isWrite ? ':' : '\0'); + if (nread == 0) { + return ReplyErrno(EILSEQ); + } + + // Get data/nothing + m_commandData = offsetlen; + m_commandData.remove_prefix(nread); + + // Run command + if (cmd == "features") { + return HandleXferFeatures(isWrite, annex, off, len); + } else { + return HandleUnsupported(); + } + } + +} \ No newline at end of file diff --git a/thermosphere/src/gdb/query.c b/thermosphere/src/gdb/query.c deleted file mode 100644 index 9a5e29af2..000000000 --- a/thermosphere/src/gdb/query.c +++ /dev/null @@ -1,108 +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 "../utils.h" - -#include "query.h" -#include "xfer.h" -#include "thread.h" -#include "mem.h" -#include "net.h" -#include "remote_command.h" - -typedef enum GDBQueryDirection { - GDB_QUERY_DIRECTION_READ, - GDB_QUERY_DIRECTION_WRITE -} GDBQueryDirection; - -#define GDB_QUERY_HANDLER_LIST_ITEM_3(name, name2, direction) { name, GDB_QUERY_HANDLER(name2), GDB_QUERY_DIRECTION_##direction } -#define GDB_QUERY_HANDLER_LIST_ITEM(name, direction) GDB_QUERY_HANDLER_LIST_ITEM_3(STRINGIZE(name), name, direction) - -static const struct { - const char *name; - GDBCommandHandler handler; - GDBQueryDirection direction; -} gdbQueryHandlers[] = -{ - GDB_QUERY_HANDLER_LIST_ITEM(Supported, READ), - GDB_QUERY_HANDLER_LIST_ITEM(Xfer, READ), - GDB_QUERY_HANDLER_LIST_ITEM(StartNoAckMode, WRITE), - GDB_QUERY_HANDLER_LIST_ITEM(Attached, READ), - GDB_QUERY_HANDLER_LIST_ITEM(fThreadInfo, READ), - GDB_QUERY_HANDLER_LIST_ITEM(sThreadInfo, READ), - GDB_QUERY_HANDLER_LIST_ITEM(ThreadEvents, WRITE), - GDB_QUERY_HANDLER_LIST_ITEM(ThreadExtraInfo, READ), - GDB_QUERY_HANDLER_LIST_ITEM_3("C", CurrentThreadId, READ), - GDB_QUERY_HANDLER_LIST_ITEM_3("Search", SearchMemory, READ), - GDB_QUERY_HANDLER_LIST_ITEM(Rcmd, READ), -}; - -static int GDB_HandleQuery(GDBContext *ctx, GDBQueryDirection direction) -{ - char *nameBegin = ctx->commandData; // w/o leading 'q'/'Q' - if(*nameBegin == 0) - return GDB_ReplyErrno(ctx, EILSEQ); - - char *nameEnd; - char *queryData = NULL; - - for(nameEnd = nameBegin; *nameEnd != 0 && *nameEnd != ':' && *nameEnd != ','; nameEnd++); - if(*nameEnd != 0) - { - *nameEnd = 0; - queryData = nameEnd + 1; - } - else - queryData = nameEnd; - - for(u32 i = 0; i < sizeof(gdbQueryHandlers) / sizeof(gdbQueryHandlers[0]); i++) - { - if(strcmp(gdbQueryHandlers[i].name, nameBegin) == 0 && gdbQueryHandlers[i].direction == direction) - { - ctx->commandData = queryData; - return gdbQueryHandlers[i].handler(ctx); - } - } - - return GDB_HandleUnsupported(ctx); // No handler found! -} - -int GDB_HandleReadQuery(GDBContext *ctx) -{ - return GDB_HandleQuery(ctx, GDB_QUERY_DIRECTION_READ); -} - -int GDB_HandleWriteQuery(GDBContext *ctx) -{ - return GDB_HandleQuery(ctx, GDB_QUERY_DIRECTION_WRITE); -} - -GDB_DECLARE_QUERY_HANDLER(Supported) -{ - // TODO! - return GDB_SendFormattedPacket(ctx, - "PacketSize=%x;" - "qXfer:features:read+;" - "QStartNoAckMode+;QThreadEvents+" - "vContSupported+;swbreak+;hwbreak+", - - GDB_BUF_LEN // should have been sizeof(ctx->buffer) but GDB memory functions are bugged - ); -} - -GDB_DECLARE_QUERY_HANDLER(StartNoAckMode) -{ - ctx->noAckSent = true; - return GDB_ReplyOk(ctx); -} - -GDB_DECLARE_QUERY_HANDLER(Attached) -{ - return GDB_SendPacket(ctx, "1", 1); -} diff --git a/thermosphere/src/gdb/query.h b/thermosphere/src/gdb/query.h deleted file mode 100644 index 21649a2e3..000000000 --- a/thermosphere/src/gdb/query.h +++ /dev/null @@ -1,12 +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" - - diff --git a/thermosphere/src/gdb/xfer.c b/thermosphere/src/gdb/xfer.c deleted file mode 100644 index d7df8f857..000000000 --- a/thermosphere/src/gdb/xfer.c +++ /dev/null @@ -1,162 +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 "../utils.h" - -#include "xfer.h" -#include "net.h" - - -struct { - const char *name; - int (*handler)(GDBContext *ctx, bool write, const char *annex, size_t offset, size_t length); -} xferCommandHandlers[] = { - { "features", GDB_XFER_HANDLER(Features) }, -}; - -static void GDB_GenerateTargetXml(char *buf) -{ - int pos; - const char *hdr = ""; - const char *cpuDescBegin = ""; - const char *cpuDescEnd = - "" - "" - ""; - - const char *fpuDescBegin = - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - ""; - - const char *fpuDescEnd = "\r\n\r\n"; - const char *footer = ""; - - strcpy(buf, hdr); - - // CPU registers - strcat(buf, cpuDescBegin); - pos = (int)strlen(buf); - for (u32 i = 0; i < 31; i++) { - pos += sprintf(buf + pos, "", i); - } - strcat(buf, cpuDescEnd); - - strcat(buf, fpuDescBegin); - pos = (int)strlen(buf); - for (u32 i = 0; i < 32; i++) { - pos += sprintf(buf + pos, "", i); - } - strcat(buf, fpuDescEnd); - - strcat(buf, footer); - - DEBUG("target.xml length is 0x%x\n", strlen(buf)); -} - -GDB_DECLARE_XFER_HANDLER(Features) -{ - if(strcmp(annex, "target.xml") != 0 || write) { - return GDB_ReplyEmpty(ctx); - } - - // Generate the target xml on-demand - // This is a bit whack, we rightfully assume that GDB won't sent any other command during the stream transfer - if (ctx->targetXmlLen == 0) { - GDB_GenerateTargetXml(ctx->workBuffer); - ctx->targetXmlLen = strlen(ctx->workBuffer); - } - - int n = GDB_SendStreamData(ctx, ctx->workBuffer, offset, length, ctx->targetXmlLen, false); - - // Transfer ended - if(offset + length >= ctx->targetXmlLen) { - ctx->targetXmlLen = 0; - } - - return n; -} - -GDB_DECLARE_QUERY_HANDLER(Xfer) -{ - const char *objectStart = ctx->commandData; - char *objectEnd = (char*)strchr(objectStart, ':'); - if (objectEnd == NULL) { - return -1; - } - *objectEnd = 0; - - char *opStart = objectEnd + 1; - char *opEnd = (char*)strchr(opStart, ':'); - if(opEnd == NULL) { - return -1; - } - *opEnd = 0; - - char *annexStart = opEnd + 1; - char *annexEnd = (char*)strchr(annexStart, ':'); - if(annexEnd == NULL) { - return -1; - } - *annexEnd = 0; - - const char *offStart = annexEnd + 1; - size_t offset, length; - - bool write; - const char *pos; - if (strcmp(opStart, "read") == 0) { - unsigned long lst[2]; - if(GDB_ParseHexIntegerList(lst, offStart, 2, 0) == NULL) { - return GDB_ReplyErrno(ctx, EILSEQ); - } - - offset = lst[0]; - length = lst[1]; - write = false; - } else if (strcmp(opStart, "write") == 0) { - pos = GDB_ParseHexIntegerList(&offset, offStart, 1, ':'); - if (pos == NULL || *pos++ != ':') { - return GDB_ReplyErrno(ctx, EILSEQ); - } - - size_t len = strlen(pos); - if (len == 0 || (len % 2) != 0) { - return GDB_ReplyErrno(ctx, EILSEQ); - } - length = len / 2; - write = true; - } else { - return GDB_ReplyErrno(ctx, EILSEQ); - } - - for (size_t i = 0; i < sizeof(xferCommandHandlers) / sizeof(xferCommandHandlers[0]); i++) { - if (strcmp(objectStart, xferCommandHandlers[i].name) == 0) { - if(write) { - ctx->commandData = (char *)pos; - } - - return xferCommandHandlers[i].handler(ctx, write, annexStart, offset, length); - } - } - - return GDB_HandleUnsupported(ctx); -}