diff --git a/sysmodules/rosalina/include/gdb.h b/sysmodules/rosalina/include/gdb.h
index d5c3ae9..286b395 100644
--- a/sysmodules/rosalina/include/gdb.h
+++ b/sysmodules/rosalina/include/gdb.h
@@ -58,6 +58,24 @@ typedef struct Breakpoint
bool persistent;
} Breakpoint;
+typedef struct PackedGdbHioRequest
+{
+ char magic[4]; // "GDB\x00"
+ u32 version;
+
+ // Request
+ char functionName[16+1];
+ char paramFormat[8+1];
+
+ u32 parameters[8];
+ u32 stringLengths[8];
+
+ // Return
+ int retval;
+ int gdbErrno;
+ bool ctrlC;
+} PackedGdbHioRequest;
+
enum
{
GDB_FLAG_SELECTED = 1,
@@ -128,6 +146,9 @@ typedef struct GDBContext
u32 nbWatchpoints;
u32 watchpoints[2];
+ u32 currentHioRequestTargetAddr;
+ PackedGdbHioRequest currentHioRequest;
+
bool enableExternalMemoryAccess;
char *commandData, *commandEnd;
int latestSentPacketSize;
diff --git a/sysmodules/rosalina/include/gdb/debug.h b/sysmodules/rosalina/include/gdb/debug.h
index a7156c3..2e41a73 100644
--- a/sysmodules/rosalina/include/gdb/debug.h
+++ b/sysmodules/rosalina/include/gdb/debug.h
@@ -38,6 +38,7 @@ GDB_DECLARE_HANDLER(Continue);
GDB_DECLARE_VERBOSE_HANDLER(Continue);
GDB_DECLARE_HANDLER(GetStopReason);
+void GDB_ContinueExecution(GDBContext *ctx);
void GDB_PreprocessDebugEvent(GDBContext *ctx, DebugEventInfo *info);
int GDB_SendStopReply(GDBContext *ctx, const DebugEventInfo *info);
int GDB_HandleDebugEvents(GDBContext *ctx);
diff --git a/sysmodules/rosalina/include/gdb/hio.h b/sysmodules/rosalina/include/gdb/hio.h
new file mode 100644
index 0000000..52f2270
--- /dev/null
+++ b/sysmodules/rosalina/include/gdb/hio.h
@@ -0,0 +1,35 @@
+/*
+* This file is part of Luma3DS
+* Copyright (C) 2016-2019 Aurora Wright, TuxSH
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that 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 .
+*
+* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
+* * Requiring preservation of specified reasonable legal notices or
+* author attributions in that material or in the Appropriate Legal
+* Notices displayed by works containing it.
+* * Prohibiting misrepresentation of the origin of that material,
+* or requiring that modified versions of such material be marked in
+* reasonable ways as different from the original version.
+*/
+
+#pragma once
+
+#include "gdb.h"
+
+bool GDB_FetchPackedHioRequest(GDBContext *ctx, u32 addr);
+bool GDB_IsHioInProgress(GDBContext *ctx);
+int GDB_SendCurrentHioRequest(GDBContext *ctx);
+
+GDB_DECLARE_HANDLER(HioReply);
\ No newline at end of file
diff --git a/sysmodules/rosalina/include/gdb/mem.h b/sysmodules/rosalina/include/gdb/mem.h
index d10f586..5c16255 100644
--- a/sysmodules/rosalina/include/gdb/mem.h
+++ b/sysmodules/rosalina/include/gdb/mem.h
@@ -28,8 +28,11 @@
#include "gdb.h"
-Result GDB_ReadMemoryInPage(void *out, GDBContext *ctx, u32 addr, u32 len);
-Result GDB_WriteMemoryInPage(GDBContext *ctx, const void *in, u32 addr, u32 len);
+Result GDB_ReadTargetMemoryInPage(void *out, GDBContext *ctx, u32 addr, u32 len);
+Result GDB_WriteTargetMemoryInPage(GDBContext *ctx, const void *in, u32 addr, u32 len);
+u32 GDB_ReadTargetMemory(void *out, GDBContext *ctx, u32 addr, u32 len);
+u32 GDB_WriteTargetMemory(GDBContext *ctx, const void *in, u32 addr, u32 len);
+
int GDB_SendMemory(GDBContext *ctx, const char *prefix, u32 prefixLen, u32 addr, u32 len);
int GDB_WriteMemory(GDBContext *ctx, const void *buf, u32 addr, u32 len);
u32 GDB_SearchMemory(bool *found, GDBContext *ctx, u32 addr, u32 len, const void *pattern, u32 patternLen);
diff --git a/sysmodules/rosalina/source/gdb.c b/sysmodules/rosalina/source/gdb.c
index 4885172..6873ab9 100644
--- a/sysmodules/rosalina/source/gdb.c
+++ b/sysmodules/rosalina/source/gdb.c
@@ -180,6 +180,9 @@ void GDB_DetachFromProcess(GDBContext *ctx)
ctx->totalNbCreatedThreads = 0;
memset(ctx->threadInfos, 0, sizeof(ctx->threadInfos));
+ ctx->currentHioRequestTargetAddr = 0;
+ memset(&ctx->currentHioRequest, 0, sizeof(PackedGdbHioRequest));
+
ctx->state = GDB_STATE_CONNECTED;
}
diff --git a/sysmodules/rosalina/source/gdb/debug.c b/sysmodules/rosalina/source/gdb/debug.c
index bd515af..daafecd 100644
--- a/sysmodules/rosalina/source/gdb/debug.c
+++ b/sysmodules/rosalina/source/gdb/debug.c
@@ -31,6 +31,7 @@
#include "gdb/net.h"
#include "gdb/thread.h"
#include "gdb/mem.h"
+#include "gdb/hio.h"
#include "gdb/watchpoints.h"
#include "fmt.h"
@@ -190,7 +191,7 @@ GDB_DECLARE_HANDLER(Break)
}
}
-static void GDB_ContinueExecution(GDBContext *ctx)
+void GDB_ContinueExecution(GDBContext *ctx)
{
ctx->selectedThreadId = ctx->selectedThreadIdForContinuing = 0;
svcContinueDebugEvent(ctx->debug, ctx->continueFlags);
@@ -376,6 +377,8 @@ void GDB_PreprocessDebugEvent(GDBContext *ctx, DebugEventInfo *info)
info->flags = 1;
info->syscall.syscall = sz;
}
+ else if (info->output_string.string_size == 0)
+ GDB_FetchPackedHioRequest(ctx, info->output_string.string_addr);
break;
}
@@ -572,25 +575,34 @@ int GDB_SendStopReply(GDBContext *ctx, const DebugEventInfo *info)
case DBGEVENT_OUTPUT_STRING:
{
- u32 addr = info->output_string.string_addr;
- u32 remaining = info->output_string.string_size;
- u32 sent = 0;
- int total = 0;
- while(remaining > 0)
+ // Regular "output string"
+ if (!GDB_IsHioInProgress(ctx))
{
- u32 pending = (GDB_BUF_LEN - 1) / 2;
- pending = pending < remaining ? pending : remaining;
+ u32 addr = info->output_string.string_addr;
+ u32 remaining = info->output_string.string_size;
+ u32 sent = 0;
+ int total = 0;
+ while(remaining > 0)
+ {
+ u32 pending = (GDB_BUF_LEN - 1) / 2;
+ pending = pending < remaining ? pending : remaining;
- int res = GDB_SendMemory(ctx, "O", 1, addr + sent, pending);
- if(res < 0 || (u32) res != 5 + 2 * pending)
- break;
+ int res = GDB_SendMemory(ctx, "O", 1, addr + sent, pending);
+ if(res < 0 || (u32) res != 5 + 2 * pending)
+ break;
- sent += pending;
- remaining -= pending;
- total += res;
+ sent += pending;
+ remaining -= pending;
+ total += res;
+ }
+
+ return total;
+ }
+ else // HIO
+ {
+ return GDB_SendCurrentHioRequest(ctx);
}
- return total;
}
default:
break;
@@ -617,7 +629,8 @@ int GDB_HandleDebugEvents(GDBContext *ctx)
GDB_PreprocessDebugEvent(ctx, &info);
int ret = 0;
- bool continueAutomatically = info.type == DBGEVENT_OUTPUT_STRING || info.type == DBGEVENT_ATTACH_PROCESS ||
+ bool continueAutomatically = (info.type == DBGEVENT_OUTPUT_STRING && !GDB_IsHioInProgress(ctx)) ||
+ info.type == DBGEVENT_ATTACH_PROCESS ||
(info.type == DBGEVENT_ATTACH_THREAD && (info.attach_thread.creator_thread_id == 0 || !ctx->catchThreadEvents)) ||
(info.type == DBGEVENT_EXIT_THREAD && (info.exit_thread.reason >= EXITTHREAD_EVENT_EXIT_PROCESS || !ctx->catchThreadEvents)) ||
info.type == DBGEVENT_EXIT_PROCESS || !(info.flags & 1);
diff --git a/sysmodules/rosalina/source/gdb/hio.c b/sysmodules/rosalina/source/gdb/hio.c
new file mode 100644
index 0000000..775fffe
--- /dev/null
+++ b/sysmodules/rosalina/source/gdb/hio.c
@@ -0,0 +1,148 @@
+/*
+* This file is part of Luma3DS
+* Copyright (C) 2016-2019 Aurora Wright, TuxSH
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that 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 .
+*
+* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
+* * Requiring preservation of specified reasonable legal notices or
+* author attributions in that material or in the Appropriate Legal
+* Notices displayed by works containing it.
+* * Prohibiting misrepresentation of the origin of that material,
+* or requiring that modified versions of such material be marked in
+* reasonable ways as different from the original version.
+*/
+
+#include
+
+#include "gdb.h"
+#include "gdb/hio.h"
+#include "gdb/net.h"
+#include "gdb/mem.h"
+#include "gdb/debug.h"
+#include "fmt.h"
+
+bool GDB_FetchPackedHioRequest(GDBContext *ctx, u32 addr)
+{
+ u32 total = GDB_ReadTargetMemory(&ctx->currentHioRequest, ctx, addr, sizeof(PackedGdbHioRequest));
+ if (total != sizeof(PackedGdbHioRequest) || memcmp(&ctx->currentHioRequest.magic, "GDB\x00", 4) != 0)
+ {
+ memset(&ctx->currentHioRequest, 0, sizeof(PackedGdbHioRequest));
+ ctx->currentHioRequestTargetAddr = 0;
+ return false;
+ }
+ else
+ {
+ ctx->currentHioRequestTargetAddr = addr;
+ return true;
+ }
+}
+
+bool GDB_IsHioInProgress(GDBContext *ctx)
+{
+ return ctx->currentHioRequestTargetAddr != 0;
+}
+
+int GDB_SendCurrentHioRequest(GDBContext *ctx)
+{
+ char buf[256+1];
+ char tmp[32+1];
+ u32 nStr = 0;
+
+ sprintf(buf, "F%s", ctx->currentHioRequest.functionName);
+
+ for (u32 i = 0; i < 8 && ctx->currentHioRequest.paramFormat[i] != 0; i++)
+ {
+ switch (ctx->currentHioRequest.paramFormat[i])
+ {
+ case 'i':
+ case 'I':
+ case 'p':
+ sprintf(tmp, ",%lx", ctx->currentHioRequest.parameters[i]);
+ break;
+ case 's':
+ sprintf(tmp, ",%lx/%lx", ctx->currentHioRequest.parameters[i], ctx->currentHioRequest.stringLengths[nStr++]);
+ break;
+ default:
+ tmp[0] = 0;
+ break;
+ }
+ strcat(buf, tmp);
+ }
+
+ return GDB_SendPacket(ctx, buf, strlen(buf));
+}
+
+GDB_DECLARE_HANDLER(HioReply)
+{
+ if (!GDB_IsHioInProgress(ctx))
+ return GDB_ReplyErrno(ctx, EPERM);
+
+ // Reply in the form of Fretcode,errno,Ctrl-C flag;call-specific attachment
+ // "Call specific attachement" is always empty, though.
+
+ const char *pos = ctx->commandData;
+ u32 retval;
+
+ if (*pos == 0 || *pos == ',')
+ return GDB_ReplyErrno(ctx, EILSEQ);
+ else if (*pos == '-')
+ {
+ pos++;
+ ctx->currentHioRequest.retval = -1;
+ }
+ else if (*pos == '+')
+ {
+ pos++;
+ ctx->currentHioRequest.retval = 1;
+ }
+ else
+ ctx->currentHioRequest.retval = 1;
+
+ pos = GDB_ParseHexIntegerList(&retval, pos, 1, ',');
+
+ if (pos == NULL)
+ return GDB_ReplyErrno(ctx, EILSEQ);
+
+ ctx->currentHioRequest.retval *= retval;
+ ctx->currentHioRequest.gdbErrno = 0;
+ ctx->currentHioRequest.ctrlC = false;
+
+ if (*pos != 0)
+ {
+ u32 errno_;
+ pos = GDB_ParseHexIntegerList(&errno_, pos, 1, ',');
+ ctx->currentHioRequest.gdbErrno = (int)errno_;
+ if (pos == NULL)
+ return GDB_ReplyErrno(ctx, EILSEQ);
+
+ if (*pos != 0)
+ {
+ if (*pos == 'C')
+ return GDB_ReplyErrno(ctx, EILSEQ);
+
+ ctx->currentHioRequest.ctrlC = true;
+ }
+ }
+
+ memset(ctx->currentHioRequest.paramFormat, 0, sizeof(ctx->currentHioRequest.paramFormat));
+
+ u32 total = GDB_WriteTargetMemory(ctx, &ctx->currentHioRequest, ctx->currentHioRequestTargetAddr, sizeof(PackedGdbHioRequest));
+
+ memset(&ctx->currentHioRequest, 0, sizeof(PackedGdbHioRequest));
+ ctx->currentHioRequestTargetAddr = 0;
+
+ GDB_ContinueExecution(ctx);
+ return total == sizeof(PackedGdbHioRequest) ? 0 : GDB_ReplyErrno(ctx, EFAULT);
+}
diff --git a/sysmodules/rosalina/source/gdb/mem.c b/sysmodules/rosalina/source/gdb/mem.c
index 3fdd812..1dedab0 100644
--- a/sysmodules/rosalina/source/gdb/mem.c
+++ b/sysmodules/rosalina/source/gdb/mem.c
@@ -34,7 +34,7 @@ static void *k_memcpy_no_interrupt(void *dst, const void *src, u32 len)
return memcpy(dst, src, len);
}
-Result GDB_ReadMemoryInPage(void *out, GDBContext *ctx, u32 addr, u32 len)
+Result GDB_ReadTargetMemoryInPage(void *out, GDBContext *ctx, u32 addr, u32 len)
{
s64 TTBCR;
svcGetSystemInfo(&TTBCR, 0x10002, 0);
@@ -83,7 +83,7 @@ Result GDB_ReadMemoryInPage(void *out, GDBContext *ctx, u32 addr, u32 len)
}
}
-Result GDB_WriteMemoryInPage(GDBContext *ctx, const void *in, u32 addr, u32 len)
+Result GDB_WriteTargetMemoryInPage(GDBContext *ctx, const void *in, u32 addr, u32 len)
{
s64 TTBCR;
svcGetSystemInfo(&TTBCR, 0x10002, 0);
@@ -133,7 +133,7 @@ Result GDB_WriteMemoryInPage(GDBContext *ctx, const void *in, u32 addr, u32 len)
svcFlushEntireDataCache();
svcInvalidateEntireInstructionCache();
- Result ret = GDB_WriteMemoryInPage(ctx, PA_FROM_VA_PTR(in), addr, len);
+ Result ret = GDB_WriteTargetMemoryInPage(ctx, PA_FROM_VA_PTR(in), addr, len);
svcFlushEntireDataCache();
svcInvalidateEntireInstructionCache();
return ret;
@@ -141,6 +141,47 @@ Result GDB_WriteMemoryInPage(GDBContext *ctx, const void *in, u32 addr, u32 len)
}
}
+u32 GDB_ReadTargetMemory(void *out, GDBContext *ctx, u32 addr, u32 len)
+{
+ Result r = 0;
+ u32 remaining = len, total = 0;
+ u8 *out8 = (u8 *)out;
+ do
+ {
+ u32 nb = (remaining > 0x1000 - (addr & 0xFFF)) ? 0x1000 - (addr & 0xFFF) : remaining;
+ r = GDB_ReadTargetMemoryInPage(out8 + total, ctx, addr, nb);
+ if(R_SUCCEEDED(r))
+ {
+ addr += nb;
+ total += nb;
+ remaining -= nb;
+ }
+ }
+ while(remaining > 0 && R_SUCCEEDED(r));
+
+ return total;
+}
+
+u32 GDB_WriteTargetMemory(GDBContext *ctx, const void *in, u32 addr, u32 len)
+{
+ Result r = 0;
+ u32 remaining = len, total = 0;
+ do
+ {
+ u32 nb = (remaining > 0x1000 - (addr & 0xFFF)) ? 0x1000 - (addr & 0xFFF) : remaining;
+ r = GDB_WriteTargetMemoryInPage(ctx, (u8 *)in + total, addr, nb);
+ if(R_SUCCEEDED(r))
+ {
+ addr += nb;
+ total += nb;
+ remaining -= nb;
+ }
+ }
+ while(remaining > 0 && R_SUCCEEDED(r));
+
+ return total;
+}
+
int GDB_SendMemory(GDBContext *ctx, const char *prefix, u32 prefixLen, u32 addr, u32 len)
{
char buf[GDB_BUF_LEN];
@@ -154,21 +195,7 @@ int GDB_SendMemory(GDBContext *ctx, const char *prefix, u32 prefixLen, u32 addr,
if(prefixLen + 2 * len > GDB_BUF_LEN) // gdb shouldn't send requests which responses don't fit in a packet
return prefix == NULL ? GDB_ReplyErrno(ctx, ENOMEM) : -1;
- Result r = 0;
- u32 remaining = len, total = 0;
- do
- {
- u32 nb = (remaining > 0x1000 - (addr & 0xFFF)) ? 0x1000 - (addr & 0xFFF) : remaining;
- r = GDB_ReadMemoryInPage(membuf + total, ctx, addr, nb);
- if(R_SUCCEEDED(r))
- {
- addr += nb;
- total += nb;
- remaining -= nb;
- }
- }
- while(remaining > 0 && R_SUCCEEDED(r));
-
+ u32 total = GDB_ReadTargetMemory(membuf, ctx, addr, len);
if(total == 0)
return prefix == NULL ? GDB_ReplyErrno(ctx, EFAULT) : -EFAULT;
else
@@ -180,22 +207,8 @@ int GDB_SendMemory(GDBContext *ctx, const char *prefix, u32 prefixLen, u32 addr,
int GDB_WriteMemory(GDBContext *ctx, const void *buf, u32 addr, u32 len)
{
- Result r = 0;
- u32 remaining = len, total = 0;
- do
- {
- u32 nb = (remaining > 0x1000 - (addr & 0xFFF)) ? 0x1000 - (addr & 0xFFF) : remaining;
- r = GDB_WriteMemoryInPage(ctx, (u8 *)buf + total, addr, nb);
- if(R_SUCCEEDED(r))
- {
- addr += nb;
- total += nb;
- remaining -= nb;
- }
- }
- while(remaining > 0 && R_SUCCEEDED(r));
-
- if(R_FAILED(r))
+ u32 total = GDB_WriteTargetMemory(ctx, buf, addr, len);
+ if(total != len)
return GDB_ReplyErrno(ctx, EFAULT);
else
return GDB_ReplyOk(ctx);
@@ -223,7 +236,7 @@ u32 GDB_SearchMemory(bool *found, GDBContext *ctx, u32 addr, u32 len, const void
break;
}
- if(R_FAILED(GDB_ReadMemoryInPage(buf + 0x1000 * nbPages, ctx, addrBase + nbPages * 0x1000, 0x1000)))
+ if(R_FAILED(GDB_ReadTargetMemoryInPage(buf + 0x1000 * nbPages, ctx, addrBase + nbPages * 0x1000, 0x1000)))
break;
}
diff --git a/sysmodules/rosalina/source/gdb/server.c b/sysmodules/rosalina/source/gdb/server.c
index 73e3efb..f1dc7bf 100644
--- a/sysmodules/rosalina/source/gdb/server.c
+++ b/sysmodules/rosalina/source/gdb/server.c
@@ -32,6 +32,7 @@
#include "gdb/debug.h"
#include "gdb/regs.h"
#include "gdb/mem.h"
+#include "gdb/hio.h"
#include "gdb/watchpoints.h"
#include "gdb/breakpoints.h"
#include "gdb/stop_point.h"
@@ -272,6 +273,7 @@ static const struct
{ 'c', GDB_HANDLER(Continue) },
{ 'C', GDB_HANDLER(Continue) },
{ 'D', GDB_HANDLER(Detach) },
+ { 'F', GDB_HANDLER(HioReply) },
{ 'g', GDB_HANDLER(ReadRegisters) },
{ 'G', GDB_HANDLER(WriteRegisters) },
{ 'H', GDB_HANDLER(SetThreadId) },