/*
* Copyright (c) 2018-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 .
*/
#include
#include "dmnt2_debug_log.hpp"
#include "dmnt2_gdb_packet_parser.hpp"
namespace ams::dmnt {
namespace {
constexpr const char TargetXmlAarch64[] =
"l"
""
""
"aarch64"
"GNU/Linux"
""
""
"";
constexpr const char Aarch64CoreXml[] =
"l\n"
"\n"
"\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\t\n"
"\t\t\n"
"\t\t\n"
"\n"
"\t\t\n"
"\t\t\n"
"\t\t\n"
"\t\t\n"
"\n"
"\t\t\n"
"\n"
"\t\t\n"
"\t\t\n"
"\t\t\n"
"\t\t\n"
"\t\t\n"
"\t\t\n"
"\n"
"\t\t\n"
"\t\t\n"
"\t\t\n"
"\t\t\n"
"\t\n"
"\t\n"
"";
constexpr const char Aarch64FpuXml[] =
"l\n"
"\n"
"\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\t\n"
"\t\t\n"
"\t\t\n"
"\t\n"
"\t\n"
"\t\t\n"
"\t\t\n"
"\t\t\n"
"\t\n"
"\t\n"
"\t\t\n"
"\t\t\n"
"\t\t\n"
"\t\n"
"\t\n"
"\t\t\n"
"\t\t\n"
"\t\n"
"\t\n"
"\t\t\n"
"\t\t\n"
"\t\n"
"\t\n"
"\t\t\n"
"\t\t\n"
"\t\t\n"
"\t\t\n"
"\t\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"";
constexpr const char TargetXmlAarch32[] =
"l"
""
""
""
""
"";
constexpr const char ArmCoreXml[] =
"l\n"
"\n"
"\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\n";
constexpr const char ArmVfpXml[] =
"l\n"
"\n"
"\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\t\n"
"\n";
bool ParsePrefix(char *&packet, const char *prefix) {
const auto len = std::strlen(prefix);
if (std::strncmp(packet, prefix, len) == 0) {
packet += len;
return true;
} else {
return false;
}
}
void SetReplyOk(char *reply) {
std::strcpy(reply, "OK");
}
void SetReplyError(char *reply, const char *err) {
AMS_DMNT2_GDB_LOG_ERROR("Reply Error: %s\n", err);
std::strcpy(reply, err);
}
void SetReply(char *reply, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
void SetReply(char *reply, const char *fmt, ...) {
std::va_list vl;
va_start(vl, fmt);
util::VSNPrintf(reply, GdbPacketBufferSize, fmt, vl);
va_end(vl);
}
void AppendReply(char *reply, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
void AppendReply(char *reply, const char *fmt, ...) {
const auto len = std::strlen(reply);
std::va_list vl;
va_start(vl, fmt);
util::VSNPrintf(reply + len, GdbPacketBufferSize - len, fmt, vl);
va_end(vl);
}
constexpr int DecodeHex(char c) {
if ('a' <= c && c <= 'f') {
return 10 + (c - 'a');
} else if ('A' <= c && c <= 'F') {
return 10 + (c - 'A');
} else if ('0' <= c && c <= '9') {
return 0 + (c - '0');
} else {
return -1;
}
}
void ParseOffsetLength(const char *packet, u32 &offset, u32 &length) {
/* Default to zero. */
offset = 0;
length = 0;
bool parsed_offset = false;
while (*packet) {
const char c = *(packet++);
if (c == ',') {
parsed_offset = true;
} else if (auto hex = DecodeHex(c); hex >= 0) {
if (parsed_offset) {
length <<= 4;
length |= hex;
} else {
offset <<= 4;
offset |= hex;
}
}
}
AMS_DMNT2_GDB_LOG_DEBUG("Offset/Length %x/%x\n", offset, length);
}
}
void GdbPacketParser::Process() {
/* Log the packet we're processing. */
AMS_DMNT2_GDB_LOG_DEBUG("Receive: %s\n", m_receive_packet);
/* Clear our reply packet. */
m_reply_packet[0] = 0;
/* Handle the received packet. */
switch (m_receive_packet[0]) {
case 'q':
this->q();
break;
case '!':
SetReplyOk(m_reply_packet);
break;
default:
AMS_DMNT2_GDB_LOG_DEBUG("Not Implemented: %s\n", m_receive_packet);
break;
}
}
void GdbPacketParser::q() {
if (ParsePrefix(m_receive_packet, "qSupported:")) {
this->qSupported();
} else if (ParsePrefix(m_receive_packet, "qXfer:features:read:")) {
this->qXferFeaturesRead();
} else {
AMS_DMNT2_GDB_LOG_DEBUG("Not Implemented q: %s\n", m_receive_packet);
}
}
void GdbPacketParser::qSupported() {
/* Current string from devkita64-none-elf-gdb: */
/* qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+ */
SetReply(m_reply_packet, "PacketSize=%lx", GdbPacketBufferSize - 1);
AppendReply(m_reply_packet, ";multiprocess+");
AppendReply(m_reply_packet, ";qXfer:osdata:read+");
AppendReply(m_reply_packet, ";qXfer:features:read+");
AppendReply(m_reply_packet, ";qXfer:libraries:read+");
AppendReply(m_reply_packet, ";qXfer:threads:read+");
AppendReply(m_reply_packet, ";swbreak+");
AppendReply(m_reply_packet, ";hwbreak+");
}
void GdbPacketParser::qXferFeaturesRead() {
u32 offset, length;
if (ParsePrefix(m_receive_packet, "target.xml:")) {
/* Parse offset/length. */
ParseOffsetLength(m_receive_packet, offset, length);
/* Send the desired xml. */
std::strncpy(m_reply_packet, (this->Is64Bit() ? TargetXmlAarch64 : TargetXmlAarch32) + offset, length);
m_reply_packet[length] = 0;
m_reply_packet += std::strlen(m_reply_packet);
} else if (ParsePrefix(m_receive_packet, "aarch64-core.xml:")) {
/* Parse offset/length. */
ParseOffsetLength(m_receive_packet, offset, length);
/* Send the desired xml. */
std::strncpy(m_reply_packet, Aarch64CoreXml + offset, length);
m_reply_packet[length] = 0;
m_reply_packet += std::strlen(m_reply_packet);
} else if (ParsePrefix(m_receive_packet, "aarch64-fpu.xml:")) {
/* Parse offset/length. */
ParseOffsetLength(m_receive_packet, offset, length);
/* Send the desired xml. */
std::strncpy(m_reply_packet, Aarch64FpuXml + offset, length);
m_reply_packet[length] = 0;
m_reply_packet += std::strlen(m_reply_packet);
} else if (ParsePrefix(m_receive_packet, "arm-core.xml:")) {
/* Parse offset/length. */
ParseOffsetLength(m_receive_packet, offset, length);
/* Send the desired xml. */
std::strncpy(m_reply_packet, ArmCoreXml + offset, length);
m_reply_packet[length] = 0;
m_reply_packet += std::strlen(m_reply_packet);
} else if (ParsePrefix(m_receive_packet, "arm-vfp.xml:")) {
/* Parse offset/length. */
ParseOffsetLength(m_receive_packet, offset, length);
/* Send the desired xml. */
std::strncpy(m_reply_packet, ArmVfpXml + offset, length);
m_reply_packet[length] = 0;
m_reply_packet += std::strlen(m_reply_packet);
} else {
AMS_DMNT2_GDB_LOG_DEBUG("Not Implemented qxfer:features:read: %s\n", m_receive_packet);
}
}
}