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) },