mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-15 09:36:35 +00:00
thermosphere: rewrite packet comms
This commit is contained in:
parent
2077062b79
commit
697e61850f
8 changed files with 261 additions and 418 deletions
|
@ -24,4 +24,4 @@
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
using std::size_t;
|
#include "debug_log.h"
|
||||||
|
|
222
thermosphere/src/gdb/hvisor_gdb_comms.cpp
Normal file
222
thermosphere/src/gdb/hvisor_gdb_comms.cpp
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#include "hvisor_gdb_defines_internal.hpp"
|
||||||
|
#include "hvisor_gdb_packet_data.hpp"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void WriteAck(TransportInterface *iface)
|
||||||
|
{
|
||||||
|
char c = '+';
|
||||||
|
transportInterfaceWriteData(iface, &c, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int WriteNack(TransportInterface *iface)
|
||||||
|
{
|
||||||
|
char c = '-';
|
||||||
|
transportInterfaceWriteData(iface, &c, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ams::hvisor::gdb {
|
||||||
|
|
||||||
|
int Context::ReceivePacket()
|
||||||
|
{
|
||||||
|
char hdr;
|
||||||
|
bool ctrlC = false;
|
||||||
|
TransportInterface *iface = m_transportInterface;
|
||||||
|
|
||||||
|
// Read the first character...
|
||||||
|
transportInterfaceReadData(iface, &hdr, 1);
|
||||||
|
|
||||||
|
switch (hdr) {
|
||||||
|
case '+': {
|
||||||
|
// Ack, don't do anything else except maybe NoAckMode state transition
|
||||||
|
if (m_noAckSent) {
|
||||||
|
m_noAck = true;
|
||||||
|
m_noAckSent = false;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case '-':
|
||||||
|
// Nack, return the previous packet
|
||||||
|
transportInterfaceWriteData(iface, m_buffer, m_lastSentPacketSize);
|
||||||
|
return m_lastSentPacketSize;
|
||||||
|
case '$':
|
||||||
|
// Normal packet, handled below
|
||||||
|
break;
|
||||||
|
case '\x03':
|
||||||
|
// Normal packet (Control-C), handled below
|
||||||
|
ctrlC = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Oops, send a nack
|
||||||
|
DEBUG("Received a packed with an invalid header from GDB, hdr=%c\n", hdr);
|
||||||
|
return WriteNack(iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We didn't get a nack past this point, read the remaining data if any
|
||||||
|
|
||||||
|
m_buffer[0] = hdr;
|
||||||
|
if (ctrlC) {
|
||||||
|
// Will never normally happen, but ok
|
||||||
|
if (m_state < State::Attached) {
|
||||||
|
DEBUG("Received connection from GDB, now attaching...\n");
|
||||||
|
Attach();
|
||||||
|
m_state = State::Attached;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t delimPos = transportInterfaceReadDataUntil(iface, m_buffer + 1, 4 + GDB_BUF_LEN - 1, '#');
|
||||||
|
if (m_buffer[delimPos] != '#') {
|
||||||
|
// The packet is malformed, send a nack
|
||||||
|
return WriteNack(iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_commandData = std::string_view{m_buffer + 1, delimPos};
|
||||||
|
|
||||||
|
// Read the checksum
|
||||||
|
size_t checksumPos = delimPos + 1;
|
||||||
|
transportInterfaceReadData(iface, m_buffer + checksumPos, 2);
|
||||||
|
|
||||||
|
auto checksumOpt = DecodeHexByte(std::string_view{m_buffer + checksumPos, 2});
|
||||||
|
|
||||||
|
if (!checksumOpt || *checksumOpt != ComputeChecksum(m_commandData)) {
|
||||||
|
// Malformed or invalid checksum
|
||||||
|
return WriteNack(iface);
|
||||||
|
} else if (!m_noAck) {
|
||||||
|
WriteAck(iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
// State transitions...
|
||||||
|
if (m_state < State::Attached) {
|
||||||
|
DEBUG("Received connection from GDB, now attaching...\n");
|
||||||
|
Attach();
|
||||||
|
m_state = State::Attached;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug
|
||||||
|
/*m_buffer[checksumPos + 2] = '\0';
|
||||||
|
DEBUGRAW("->");
|
||||||
|
DEBUGRAW(m_buffer);
|
||||||
|
DEBUGRAW("\n");*/
|
||||||
|
|
||||||
|
return static_cast<int>(delimPos + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Context::DoSendPacket(size_t len)
|
||||||
|
{
|
||||||
|
transportInterfaceWriteData(m_transportInterface, m_buffer, len);
|
||||||
|
m_lastSentPacketSize = len;
|
||||||
|
|
||||||
|
// Debugging:
|
||||||
|
/*m_buffer[len] = 0;
|
||||||
|
DEBUGRAW("<-");
|
||||||
|
DEBUGRAW(ctx->buffer);
|
||||||
|
DEBUGRAW("\n");*/
|
||||||
|
|
||||||
|
return static_cast<int>(len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Context::SendPacket(std::string_view packetData, char hdr)
|
||||||
|
{
|
||||||
|
u8 checksum = ComputeChecksum(packetData);
|
||||||
|
if (packetData.data() != m_buffer + 1) {
|
||||||
|
std::memmove(m_buffer + 1, packetData.data(), packetData.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t checksumPos = 1 + packetData.size() + 1;
|
||||||
|
m_buffer[0] = '$';
|
||||||
|
m_buffer[checksumPos - 1] = '#';
|
||||||
|
EncodeHex(m_buffer + checksumPos, &checksum, 1);
|
||||||
|
|
||||||
|
return DoSendPacket(4 + packetData.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
int Context::SendFormattedPacket(const char *packetDataFmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, packetDataFmt);
|
||||||
|
int n = vsprintf(m_buffer + 1, packetDataFmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
if (n < 0) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return SendPacket(std::string_view{m_buffer + 1, n});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Context::SendHexPacket(const void *packetData, size_t len)
|
||||||
|
{
|
||||||
|
if (4 + 2 * len < GDB_BUF_LEN) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
EncodeHex(m_buffer + 1, packetData, len);
|
||||||
|
return SendPacket(std::string_view{m_buffer + 1, 2 * len});
|
||||||
|
}
|
||||||
|
|
||||||
|
int Context::SendStreamData(std::string_view streamData, size_t offset, size_t length, bool forceEmptyLast)
|
||||||
|
{
|
||||||
|
size_t totalSize = streamData.size();
|
||||||
|
|
||||||
|
// GDB_BUF_LEN does not include the usual $#<1-byte checksum>
|
||||||
|
length = std::min(length, GDB_BUF_LEN - 1ul);
|
||||||
|
|
||||||
|
char letter;
|
||||||
|
|
||||||
|
if ((forceEmptyLast && offset >= totalSize) || (!forceEmptyLast && offset + length >= totalSize)) {
|
||||||
|
length = offset >= totalSize ? 0 : totalSize - offset;
|
||||||
|
letter = 'l';
|
||||||
|
} else {
|
||||||
|
letter = 'm';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: ctx->buffer[0] = '$'
|
||||||
|
if (streamData.data() + offset != m_buffer + 2) {
|
||||||
|
memmove(m_buffer + 2, streamData.data() + offset, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_buffer[1] = letter;
|
||||||
|
return SendPacket(std::string_view{m_buffer + 1, 1 + length});
|
||||||
|
}
|
||||||
|
|
||||||
|
int Context::ReplyOk()
|
||||||
|
{
|
||||||
|
return SendPacket("OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
int Context::ReplyEmpty()
|
||||||
|
{
|
||||||
|
return SendPacket("");
|
||||||
|
}
|
||||||
|
|
||||||
|
int Context::ReplyErrno(int no)
|
||||||
|
{
|
||||||
|
u8 no8 = static_cast<u8>(no);
|
||||||
|
char resp[] = "E00";
|
||||||
|
EncodeHex(resp + 1, &no8, 1);
|
||||||
|
return SendPacket(resp);
|
||||||
|
}
|
||||||
|
}
|
|
@ -110,11 +110,10 @@ namespace ams::hvisor::gdb {
|
||||||
|
|
||||||
// Comms
|
// Comms
|
||||||
int ReceivePacket();
|
int ReceivePacket();
|
||||||
int DoSendPacket();
|
int DoSendPacket(size_t len);
|
||||||
int SendPacket(std::string_view packetData);
|
int SendPacket(std::string_view packetData, char hdr = '$');
|
||||||
int SendFormattedPacket(const char *packetDataFmt, ...);
|
int SendFormattedPacket(const char *packetDataFmt, ...);
|
||||||
int SendHexPacket(const void *packetData, size_t len);
|
int SendHexPacket(const void *packetData, size_t len);
|
||||||
int SendNotificationPacket(std::string_view packetData);
|
|
||||||
int SendStreamData(std::string_view streamData, size_t offset, size_t length, bool forceEmptyLast);
|
int SendStreamData(std::string_view streamData, size_t offset, size_t length, bool forceEmptyLast);
|
||||||
int ReplyOk();
|
int ReplyOk();
|
||||||
int ReplyEmpty();
|
int ReplyEmpty();
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "hvisor_gdb_packet_data.hpp"
|
#include "hvisor_gdb_packet_data.hpp"
|
||||||
#include <cctype>
|
|
||||||
|
|
||||||
namespace ams::hvisor::gdb {
|
namespace ams::hvisor::gdb {
|
||||||
|
|
||||||
|
@ -44,14 +43,13 @@ namespace ams::hvisor::gdb {
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
u8 *dst8 = reinterpret_cast<u8 *>(dst);
|
u8 *dst8 = reinterpret_cast<u8 *>(dst);
|
||||||
for (i = 0; i < data.size() / 2; i++) {
|
for (i = 0; i < data.size() / 2; i++) {
|
||||||
auto v1 = DecodeHexDigit(data[2 * i]);
|
auto bOpt = DecodeHexByte(data);
|
||||||
auto v2 = DecodeHexDigit(data[2 * i + 1]);
|
if (!bOpt) {
|
||||||
|
|
||||||
if (v1 >= 16 || v2 >= 16) {
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
dst8[i] = (v1 << 4) | v2;
|
dst8[i] = *bOpt;
|
||||||
|
data.remove_prefix(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
|
|
|
@ -150,9 +150,26 @@ namespace ams::hvisor::gdb {
|
||||||
return ParseIntegerList<N>(str, 16, false, ',', lastSep);
|
return ParseIntegerList<N>(str, 16, false, ',', lastSep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr std::optional<u8> DecodeHexByte(std::string_view data)
|
||||||
|
{
|
||||||
|
if (data.size() < 2) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto v1 = DecodeHexDigit(data[0]);
|
||||||
|
auto v2 = DecodeHexDigit(data[1]);
|
||||||
|
|
||||||
|
if (v1 >= 16 || v2 >= 16) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return (v1 << 4) | v2;
|
||||||
|
}
|
||||||
|
|
||||||
u8 ComputeChecksum(std::string_view packetData);
|
u8 ComputeChecksum(std::string_view packetData);
|
||||||
size_t EncodeHex(char *dst, const void *src, size_t len);
|
size_t EncodeHex(char *dst, const void *src, size_t len);
|
||||||
size_t DecodeHex(void *dst, std::string_view data);
|
size_t DecodeHex(void *dst, std::string_view data);
|
||||||
|
|
||||||
size_t EscapeBinaryData(size_t *encodedCount, void *dst, const void *src, size_t len, size_t maxLen);
|
size_t EscapeBinaryData(size_t *encodedCount, void *dst, const void *src, size_t len, size_t maxLen);
|
||||||
size_t UnescapeBinaryData(void *dst, const void *src, size_t len);
|
size_t UnescapeBinaryData(void *dst, const void *src, size_t len);
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,27 @@
|
||||||
/*
|
/*
|
||||||
* This file is part of Luma3DS.
|
* Copyright (c) 2019-2020 Atmosphère-NX
|
||||||
* Copyright (C) 2016-2019 Aurora Wright, TuxSH
|
*
|
||||||
*
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
|
#include "hvisor_gdb_thread.hpp"
|
||||||
#include "hvisor_gdb_defines_internal.hpp"
|
#include "hvisor_gdb_defines_internal.hpp"
|
||||||
#include "hvisor_gdb_packet_data.hpp"
|
#include "hvisor_gdb_packet_data.hpp"
|
||||||
|
|
||||||
#include "../core_ctx.h"
|
#include "../core_ctx.h"
|
||||||
|
|
||||||
namespace {
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace ams::hvisor::gdb {
|
namespace ams::hvisor::gdb {
|
||||||
|
|
||||||
int ConvertTidToCoreId(unsigned long tid)
|
int ConvertTidToCoreId(unsigned long tid)
|
||||||
|
|
|
@ -1,371 +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 "net.h"
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "../pattern_utils.h"
|
|
||||||
|
|
||||||
u8 GDB_ComputeChecksum(const char *packetData, size_t len)
|
|
||||||
{
|
|
||||||
unsigned long cksum = 0;
|
|
||||||
for(size_t i = 0; i < len; i++) {
|
|
||||||
cksum += packetData[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return (u8)cksum;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GDB_EncodeHex(char *dst, const void *src, size_t len)
|
|
||||||
{
|
|
||||||
static const char *alphabet = "0123456789abcdef";
|
|
||||||
const u8 *src8 = (const u8 *)src;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < len; i++) {
|
|
||||||
dst[2 * i] = alphabet[(src8[i] & 0xf0) >> 4];
|
|
||||||
dst[2 * i + 1] = alphabet[src8[i] & 0x0f];
|
|
||||||
}
|
|
||||||
|
|
||||||
return 2 * len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u32 GDB_DecodeHexDigit(char src, bool *ok)
|
|
||||||
{
|
|
||||||
*ok = true;
|
|
||||||
switch (src) {
|
|
||||||
case '0' ... '9': return 0 + (src - '0');
|
|
||||||
case 'a' ... 'f': return 10 + (src - 'a');
|
|
||||||
case 'A' ... 'F': return 10 + (src - 'A');
|
|
||||||
default:
|
|
||||||
*ok = false;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GDB_DecodeHex(void *dst, const char *src, size_t len) {
|
|
||||||
size_t i = 0;
|
|
||||||
u8 *dst8 = (u8 *)dst;
|
|
||||||
for (i = 0; i < len && src[2 * i] != 0 && src[2 * i + 1] != 0; i++) {
|
|
||||||
bool ok1, ok2;
|
|
||||||
dst8[i] = GDB_DecodeHexDigit(src[2 * i], &ok1) << 4;
|
|
||||||
dst8[i] |= GDB_DecodeHexDigit(src[2 * i + 1], &ok2);
|
|
||||||
if (!ok1 || !ok2) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GDB_EscapeBinaryData(size_t *encodedCount, void *dst, const void *src, size_t len, size_t maxLen)
|
|
||||||
{
|
|
||||||
u8 *dst8 = (u8 *)dst;
|
|
||||||
const u8 *src8 = (const u8 *)src;
|
|
||||||
|
|
||||||
maxLen = maxLen >= len ? len : maxLen;
|
|
||||||
|
|
||||||
while ((uintptr_t)dst8 < (uintptr_t)dst + maxLen) {
|
|
||||||
if (*src8 == '$' || *src8 == '#' || *src8 == '}' || *src8 == '*') {
|
|
||||||
if ((uintptr_t)dst8 + 1 >= (uintptr_t)dst + maxLen) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
*dst8++ = '}';
|
|
||||||
*dst8++ = *src8++ ^ 0x20;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
*dst8++ = *src8++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*encodedCount = dst8 - (u8 *)dst;
|
|
||||||
return src8 - (u8 *)src;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GDB_UnescapeBinaryData(void *dst, const void *src, size_t len)
|
|
||||||
{
|
|
||||||
u8 *dst8 = (u8 *)dst;
|
|
||||||
const u8 *src8 = (const u8 *)src;
|
|
||||||
|
|
||||||
while ((uintptr_t)src8 < (uintptr_t)src + len) {
|
|
||||||
if (*src8 == '}') {
|
|
||||||
src8++;
|
|
||||||
*dst8++ = *src8++ ^ 0x20;
|
|
||||||
} else {
|
|
||||||
*dst8++ = *src8++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dst8 - (u8 *)dst;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *GDB_ParseIntegerList(unsigned long *dst, const char *src, size_t nb, char sep, char lastSep, u32 base, bool allowPrefix)
|
|
||||||
{
|
|
||||||
const char *pos = src;
|
|
||||||
const char *endpos;
|
|
||||||
bool ok;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < nb; i++) {
|
|
||||||
unsigned long n = xstrtoul(pos, (char **)&endpos, (int) base, allowPrefix, &ok);
|
|
||||||
if(!ok || endpos == pos) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i != nb - 1) {
|
|
||||||
if (*endpos != sep) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
pos = endpos + 1;
|
|
||||||
} else {
|
|
||||||
if (*endpos != lastSep && *endpos != 0) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
pos = endpos;
|
|
||||||
}
|
|
||||||
|
|
||||||
dst[i] = n;
|
|
||||||
}
|
|
||||||
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *GDB_ParseHexIntegerList(unsigned long *dst, const char *src, size_t nb, char lastSep)
|
|
||||||
{
|
|
||||||
return GDB_ParseIntegerList(dst, src, nb, ',', lastSep, 16, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int GDB_SendNackIfPossible(GDBContext *ctx) {
|
|
||||||
if (ctx->flags & GDB_FLAG_NOACK) {
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
char hdr = '-';
|
|
||||||
transportInterfaceWriteData(ctx->transportInterface, &hdr, 1);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int GDB_ReceivePacket(GDBContext *ctx)
|
|
||||||
{
|
|
||||||
char hdr;
|
|
||||||
bool ctrlC = false;
|
|
||||||
TransportInterface *iface = ctx->transportInterface;
|
|
||||||
|
|
||||||
// Read the first character...
|
|
||||||
transportInterfaceReadData(iface, &hdr, 1);
|
|
||||||
|
|
||||||
// Check if the ack/nack packets are not allowed
|
|
||||||
if ((hdr == '+' || hdr == '-') && (ctx->flags & GDB_FLAG_NOACK) != 0) {
|
|
||||||
DEBUG("Received a packed with an invalid header from GDB, hdr=%c\n", hdr);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (hdr) {
|
|
||||||
case '+': {
|
|
||||||
// Ack, don't do anything else except maybe NoAckMode state transition
|
|
||||||
if (ctx->noAckSent) {
|
|
||||||
ctx->flags |= GDB_FLAG_NOACK;
|
|
||||||
ctx->noAckSent = false;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
case '-':
|
|
||||||
// Nack, return the previous packet
|
|
||||||
transportInterfaceWriteData(iface, ctx->buffer, ctx->lastSentPacketSize);
|
|
||||||
return ctx->lastSentPacketSize;
|
|
||||||
case '$':
|
|
||||||
// Normal packet, handled below
|
|
||||||
break;
|
|
||||||
case '\x03':
|
|
||||||
// Normal packet (Control-C), handled below
|
|
||||||
ctrlC = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// Oops, send a nack
|
|
||||||
DEBUG("Received a packed with an invalid header from GDB, hdr=%c\n", hdr);
|
|
||||||
return GDB_SendNackIfPossible(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We didn't get a nack past this point, read the remaining data if any
|
|
||||||
|
|
||||||
ctx->buffer[0] = hdr;
|
|
||||||
if (ctrlC) {
|
|
||||||
// Will never normally happen, but ok
|
|
||||||
if (ctx->state < GDB_STATE_ATTACHED) {
|
|
||||||
DEBUG("Received connection from GDB, now attaching...\n");
|
|
||||||
GDB_AttachToContext(ctx);
|
|
||||||
ctx->state = GDB_STATE_ATTACHED;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t delimPos = transportInterfaceReadDataUntil(iface, ctx->buffer + 1, 4 + GDB_BUF_LEN - 1, '#');
|
|
||||||
if (ctx->buffer[delimPos] != '#') {
|
|
||||||
// The packet is malformed, send a nack
|
|
||||||
return GDB_SendNackIfPossible(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the checksum
|
|
||||||
size_t checksumPos = delimPos + 1;
|
|
||||||
u8 checksum;
|
|
||||||
transportInterfaceReadData(iface, ctx->buffer + checksumPos, 2);
|
|
||||||
|
|
||||||
if (GDB_DecodeHex(&checksum, ctx->buffer + checksumPos, 1) != 1) {
|
|
||||||
// Malformed checksum
|
|
||||||
return GDB_SendNackIfPossible(ctx);
|
|
||||||
} else if (GDB_ComputeChecksum(ctx->buffer + 1, delimPos - 1) != checksum) {
|
|
||||||
// Invalid checksum
|
|
||||||
return GDB_SendNackIfPossible(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ok, send ack (if possible)
|
|
||||||
if (!(ctx->flags & GDB_FLAG_NOACK)) {
|
|
||||||
hdr = '+';
|
|
||||||
transportInterfaceWriteData(iface, &hdr, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// State transitions...
|
|
||||||
if (ctx->state < GDB_STATE_ATTACHED) {
|
|
||||||
DEBUG("Received connection from GDB, now attaching...\n");
|
|
||||||
GDB_AttachToContext(ctx);
|
|
||||||
ctx->state = GDB_STATE_ATTACHED;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug
|
|
||||||
ctx->buffer[checksumPos + 2] = '\0';
|
|
||||||
DEBUGRAW("->");
|
|
||||||
DEBUGRAW(ctx->buffer);
|
|
||||||
DEBUGRAW("\n");
|
|
||||||
|
|
||||||
// Set helper attributes, change '#' to NUL
|
|
||||||
ctx->commandData = ctx->buffer + 2;
|
|
||||||
ctx->commandEnd = ctx->buffer + delimPos;
|
|
||||||
ctx->buffer[delimPos] = '\0';
|
|
||||||
|
|
||||||
return (int)(delimPos + 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int GDB_DoSendPacket(GDBContext *ctx, size_t len)
|
|
||||||
{
|
|
||||||
transportInterfaceWriteData(ctx->transportInterface, ctx->buffer, len);
|
|
||||||
ctx->lastSentPacketSize = len;
|
|
||||||
|
|
||||||
// Debugging:
|
|
||||||
ctx->buffer[len] = 0;
|
|
||||||
DEBUGRAW("<-");
|
|
||||||
DEBUGRAW(ctx->buffer);
|
|
||||||
DEBUGRAW("\n");
|
|
||||||
|
|
||||||
return (int)len;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GDB_SendPacket(GDBContext *ctx, const char *packetData, size_t len)
|
|
||||||
{
|
|
||||||
if (packetData != ctx->buffer + 1) {
|
|
||||||
memmove(ctx->buffer + 1, packetData, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->buffer[0] = '$';
|
|
||||||
|
|
||||||
char *checksumLoc = ctx->buffer + len + 1;
|
|
||||||
*checksumLoc++ = '#';
|
|
||||||
|
|
||||||
hexItoa(GDB_ComputeChecksum(ctx->buffer + 1, len), checksumLoc, 2, false);
|
|
||||||
return GDB_DoSendPacket(ctx, 4 + len);
|
|
||||||
}
|
|
||||||
|
|
||||||
int GDB_SendFormattedPacket(GDBContext *ctx, const char *packetDataFmt, ...)
|
|
||||||
{
|
|
||||||
va_list args;
|
|
||||||
|
|
||||||
va_start(args, packetDataFmt);
|
|
||||||
int n = vsprintf(ctx->buffer + 1, packetDataFmt, args);
|
|
||||||
va_end(args);
|
|
||||||
|
|
||||||
if (n < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->buffer[0] = '$';
|
|
||||||
char *checksumLoc = ctx->buffer + n + 1;
|
|
||||||
*checksumLoc++ = '#';
|
|
||||||
|
|
||||||
hexItoa(GDB_ComputeChecksum(ctx->buffer + 1, n), checksumLoc, 2, false);
|
|
||||||
return GDB_DoSendPacket(ctx, 4 + n);
|
|
||||||
}
|
|
||||||
|
|
||||||
int GDB_SendHexPacket(GDBContext *ctx, const void *packetData, size_t len)
|
|
||||||
{
|
|
||||||
if(4 + 2 * len > GDB_BUF_LEN)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
ctx->buffer[0] = '$';
|
|
||||||
GDB_EncodeHex(ctx->buffer + 1, packetData, len);
|
|
||||||
|
|
||||||
char *checksumLoc = ctx->buffer + 2 * len + 1;
|
|
||||||
*checksumLoc++ = '#';
|
|
||||||
|
|
||||||
hexItoa(GDB_ComputeChecksum(ctx->buffer + 1, 2 * len), checksumLoc, 2, false);
|
|
||||||
return GDB_DoSendPacket(ctx, 4 + 2 * len);
|
|
||||||
}
|
|
||||||
|
|
||||||
int GDB_SendNotificationPacket(GDBContext *ctx, const char *packetData, size_t len)
|
|
||||||
{
|
|
||||||
if (packetData != ctx->buffer + 1) {
|
|
||||||
memmove(ctx->buffer + 1, packetData, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->buffer[0] = '%';
|
|
||||||
|
|
||||||
char *checksumLoc = ctx->buffer + len + 1;
|
|
||||||
*checksumLoc++ = '#';
|
|
||||||
|
|
||||||
hexItoa(GDB_ComputeChecksum(ctx->buffer + 1, len), checksumLoc, 2, false);
|
|
||||||
return GDB_DoSendPacket(ctx, 4 + len);
|
|
||||||
}
|
|
||||||
|
|
||||||
int GDB_SendStreamData(GDBContext *ctx, const char *streamData, size_t offset, size_t length, size_t totalSize, bool forceEmptyLast)
|
|
||||||
{
|
|
||||||
// GDB_BUF_LEN does not include the usual %#<1-byte checksum>
|
|
||||||
if(length > GDB_BUF_LEN - 1) {
|
|
||||||
length = GDB_BUF_LEN - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
char letter;
|
|
||||||
|
|
||||||
if ((forceEmptyLast && offset >= totalSize) || (!forceEmptyLast && offset + length >= totalSize)) {
|
|
||||||
length = offset >= totalSize ? 0 : totalSize - offset;
|
|
||||||
letter = 'l';
|
|
||||||
} else {
|
|
||||||
letter = 'm';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: ctx->buffer[0] = '$'
|
|
||||||
if (streamData + offset != ctx->buffer + 2) {
|
|
||||||
memmove(ctx->buffer + 2, streamData + offset, length);
|
|
||||||
}
|
|
||||||
ctx->buffer[1] = letter;
|
|
||||||
return GDB_SendPacket(ctx, ctx->buffer + 1, 1 + length);
|
|
||||||
}
|
|
||||||
|
|
||||||
int GDB_ReplyEmpty(GDBContext *ctx)
|
|
||||||
{
|
|
||||||
return GDB_SendPacket(ctx, "", 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int GDB_ReplyOk(GDBContext *ctx)
|
|
||||||
{
|
|
||||||
return GDB_SendPacket(ctx, "OK", 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
int GDB_ReplyErrno(GDBContext *ctx, int no)
|
|
||||||
{
|
|
||||||
char buf[] = "E01";
|
|
||||||
hexItoa(no & 0xFF, buf + 1, 2, false);
|
|
||||||
return GDB_SendPacket(ctx, buf, 3);
|
|
||||||
}
|
|
|
@ -1,29 +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"
|
|
||||||
|
|
||||||
u8 GDB_ComputeChecksum(const char *packetData, size_t len);
|
|
||||||
size_t GDB_EncodeHex(char *dst, const void *src, size_t len);
|
|
||||||
size_t GDB_DecodeHex(void *dst, const char *src, size_t len);
|
|
||||||
size_t GDB_EscapeBinaryData(size_t *encodedCount, void *dst, const void *src, size_t len, size_t maxLen);
|
|
||||||
size_t GDB_UnescapeBinaryData(void *dst, const void *src, size_t len);
|
|
||||||
|
|
||||||
const char *GDB_ParseIntegerList(unsigned long *dst, const char *src, size_t nb, char sep, char lastSep, u32 base, bool allowPrefix);
|
|
||||||
const char *GDB_ParseHexIntegerList(unsigned long *dst, const char *src, size_t nb, char lastSep);
|
|
||||||
|
|
||||||
int GDB_ReceivePacket(GDBContext *ctx);
|
|
||||||
int GDB_SendPacket(GDBContext *ctx, const char *packetData, size_t len);
|
|
||||||
int GDB_SendFormattedPacket(GDBContext *ctx, const char *packetDataFmt, ...);
|
|
||||||
int GDB_SendHexPacket(GDBContext *ctx, const void *packetData, size_t len);
|
|
||||||
int GDB_SendNotificationPacket(GDBContext *ctx, const char *packetData, size_t len);
|
|
||||||
int GDB_SendStreamData(GDBContext *ctx, const char *streamData, size_t offset, size_t length, size_t totalSize, bool forceEmptyLast);
|
|
||||||
int GDB_ReplyEmpty(GDBContext *ctx);
|
|
||||||
int GDB_ReplyOk(GDBContext *ctx);
|
|
||||||
int GDB_ReplyErrno(GDBContext *ctx, int no);
|
|
Loading…
Reference in a new issue