From 3907c46980c3f9deb54520c743cc2530b77637b9 Mon Sep 17 00:00:00 2001 From: TuxSH Date: Thu, 20 Jul 2017 00:56:08 +0200 Subject: [PATCH] Follow ARM's documentation on hw watchpoints to the letter --- k11_extension/include/globals.h | 1 + k11_extension/include/kernel.h | 26 +++-- k11_extension/include/svc/GetHandleInfo.h | 34 ++++++ k11_extension/source/globals.c | 1 + k11_extension/source/main.c | 1 + k11_extension/source/svc.c | 5 +- k11_extension/source/svc/GetHandleInfo.c | 62 ++++++++++ k11_extension/source/svc/GetProcessInfo.c | 10 +- k11_extension/source/svc/wrappers.s | 1 + sysmodules/rosalina/source/gdb/watchpoints.c | 113 ++++++++++++++++--- 10 files changed, 222 insertions(+), 32 deletions(-) create mode 100644 k11_extension/include/svc/GetHandleInfo.h create mode 100644 k11_extension/source/svc/GetHandleInfo.c diff --git a/k11_extension/include/globals.h b/k11_extension/include/globals.h index 063ab0c..36237bf 100644 --- a/k11_extension/include/globals.h +++ b/k11_extension/include/globals.h @@ -56,6 +56,7 @@ extern void (*KScheduler__AttemptSwitchingThreadContext)(KScheduler *this); extern Result (*ControlMemory)(u32 *addrOut, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader); extern void (*SleepThread)(s64 ns); extern Result (*CloseHandle)(Handle handle); +extern Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type); extern Result (*GetSystemInfo)(s64 *out, s32 type, s32 param); extern Result (*GetProcessInfo)(s64 *out, Handle processHandle, u32 type); extern Result (*GetThreadInfo)(s64 *out, Handle threadHandle, u32 type); diff --git a/k11_extension/include/kernel.h b/k11_extension/include/kernel.h index 807529f..5fba9bf 100644 --- a/k11_extension/include/kernel.h +++ b/k11_extension/include/kernel.h @@ -1137,21 +1137,29 @@ typedef struct FcramLayout extern bool isN3DS; extern void *officialSVCs[0x7E]; -#define KPROCESS_OFFSETOF(field) (isN3DS ? offsetof(KProcessN3DS, field) :\ -((kernelVersion >= SYSTEM_VERSION(2, 44, 6)) ? offsetof(KProcessO3DS8x, field) :\ -offsetof(KProcessO3DSPre8x, field))) +#define KPROCESSRELATED_OFFSETOFF(classname, field) (isN3DS ? offsetof(classname##N3DS, field) :\ +((kernelVersion >= SYSTEM_VERSION(2, 44, 6)) ? offsetof(classname##O3DS8x, field) :\ +offsetof(classname##O3DSPre8x, field))) -#define KPROCESS_GET_PTR(obj, field) (isN3DS ? &(obj)->N3DS.field :\ +#define KPROCESSRELATED_GET_PTR(obj, field) (isN3DS ? &(obj)->N3DS.field :\ ((kernelVersion >= SYSTEM_VERSION(2, 44, 6)) ? &(obj)->O3DS8x.field :\ -&(obj)->O3DSPre8x.field )) +&(obj)->O3DSPre8x.field)) -#define KPROCESS_GET_PTR_TYPE(type, obj, field) (isN3DS ? (type *)(&(obj)->N3DS.field) :\ +#define KPROCESSRELATED_GET_PTR_TYPE(type, obj, field) (isN3DS ? (type *)(&(obj)->N3DS.field) :\ ((kernelVersion >= SYSTEM_VERSION(2, 44, 6)) ? (type *)(&(obj)->O3DS8x.field) :\ -(type *)(&(obj)->O3DSPre8x.field) )) +(type *)(&(obj)->O3DSPre8x.field))) -#define KPROCESS_GET_RVALUE(obj, field) *(KPROCESS_GET_PTR(obj, field)) +#define KPROCESS_OFFSETOF(field) KPROCESSRELATED_OFFSETOFF(KProcess, field) +#define KPROCESS_GET_PTR(obj, field) KPROCESSRELATED_GET_PTR(obj, field) +#define KPROCESS_GET_PTR_TYPE(type, obj, field) KPROCESSRELATED_GET_PTR_TYPE(type, obj, field) +#define KPROCESS_GET_RVALUE(obj, field) *(KPROCESS_GET_PTR(obj, field)) +#define KPROCESS_GET_RVALUE_TYPE(type, obj, field) *(KPROCESS_GET_PTR_TYPE(type, obj, field)) -#define KPROCESS_GET_RVALUE_TYPE(type, obj, field) *(KPROCESS_GET_PTR(type, obj, field)) +#define KPROCESSHWINFO_OFFSETOF(field) KPROCESSRELATED_OFFSETOFF(KProcessHwInfo, field) +#define KPROCESSHWINFO_GET_PTR(obj, field) KPROCESSRELATED_GET_PTR(obj, field) +#define KPROCESSHWINFO_GET_PTR_TYPE(type, obj, field) KPROCESSRELATED_GET_PTR_TYPE(type, obj, field) +#define KPROCESSHWINFO_GET_RVALUE(obj, field) *(KPROCESSHWINFO_GET_PTR(obj, field)) +#define KPROCESSHWINFO_GET_RVALUE_TYPE(type, obj, field) *(KPROCESSHWINFO_GET_PTR_TYPE(type, obj, field)) static inline u32 idOfProcess(KProcess *process) { diff --git a/k11_extension/include/svc/GetHandleInfo.h b/k11_extension/include/svc/GetHandleInfo.h new file mode 100644 index 0000000..334cccd --- /dev/null +++ b/k11_extension/include/svc/GetHandleInfo.h @@ -0,0 +1,34 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016-2017 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 "utils.h" +#include "kernel.h" +#include "svc.h" + +Result GetHandleInfoHookWrapper(u32 dummy, Handle handle, u32 type); +Result GetHandleInfoHook(s64 *out, Handle handle, u32 type); diff --git a/k11_extension/source/globals.c b/k11_extension/source/globals.c index 03bf42d..4a02376 100644 --- a/k11_extension/source/globals.c +++ b/k11_extension/source/globals.c @@ -52,6 +52,7 @@ void (*KScheduler__AttemptSwitchingThreadContext)(KScheduler *this); Result (*ControlMemory)(u32 *addrOut, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader); void (*SleepThread)(s64 ns); Result (*CloseHandle)(Handle handle); +Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type); Result (*GetSystemInfo)(s64 *out, s32 type, s32 param); Result (*GetProcessInfo)(s64 *out, Handle processHandle, u32 type); Result (*GetThreadInfo)(s64 *out, Handle threadHandle, u32 type); diff --git a/k11_extension/source/main.c b/k11_extension/source/main.c index bb7262c..0611a7e 100644 --- a/k11_extension/source/main.c +++ b/k11_extension/source/main.c @@ -208,6 +208,7 @@ static void findUsefulSymbols(void) decodeARMBranch((u32 *)officialSVCs[0x01] + 5); SleepThread = (void (*)(s64))officialSVCs[0x0A]; CloseHandle = (Result (*)(Handle))officialSVCs[0x23]; + GetHandleInfo = (Result (*)(s64 *, Handle, u32))decodeARMBranch((u32 *)officialSVCs[0x29] + 3); GetSystemInfo = (Result (*)(s64 *, s32, s32))decodeARMBranch((u32 *)officialSVCs[0x2A] + 3); GetProcessInfo = (Result (*)(s64 *, Handle, u32))decodeARMBranch((u32 *)officialSVCs[0x2B] + 3); GetThreadInfo = (Result (*)(s64 *, Handle, u32))decodeARMBranch((u32 *)officialSVCs[0x2C] + 3); diff --git a/k11_extension/source/svc.c b/k11_extension/source/svc.c index 8f66e02..34bde61 100644 --- a/k11_extension/source/svc.c +++ b/k11_extension/source/svc.c @@ -28,9 +28,10 @@ #include "synchronization.h" #include "svc.h" #include "svc/ControlMemory.h" +#include "svc/GetHandleInfo.h" +#include "svc/GetSystemInfo.h" #include "svc/GetProcessInfo.h" #include "svc/GetThreadInfo.h" -#include "svc/GetSystemInfo.h" #include "svc/GetCFWInfo.h" #include "svc/ConnectToPort.h" #include "svc/SendSyncRequest.h" @@ -103,6 +104,8 @@ void *svcHook(u8 *pageEnd) doingVeryShittyPmResLimitWorkaround = true; } return officialSVCs[0x17]; + case 0x29: + return GetHandleInfoHookWrapper; case 0x2A: return GetSystemInfoHookWrapper; case 0x2B: diff --git a/k11_extension/source/svc/GetHandleInfo.c b/k11_extension/source/svc/GetHandleInfo.c new file mode 100644 index 0000000..389bd9d --- /dev/null +++ b/k11_extension/source/svc/GetHandleInfo.c @@ -0,0 +1,62 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016-2017 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 "svc/GetThreadInfo.h" +#include "memory.h" + +Result GetHandleInfoHook(s64 *out, Handle handle, u32 type) +{ + if(type == 0x10000) // KDebug and KProcess: get context ID + { + KProcessHwInfo *hwInfo; + KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess); + KAutoObject *obj; + if(handle == CUR_PROCESS_HANDLE) + { + obj = (KAutoObject *)(currentCoreContext->objectContext.currentProcess); + KAutoObject__AddReference(obj); + } + else + obj = KProcessHandleTable__ToKAutoObject(handleTable, handle); + + if(obj == NULL) + return 0xD8E007F7; + + if(strcmp(classNameOfAutoObject(obj), "KDebug") == 0) + hwInfo = hwInfoOfProcess(((KDebug *)obj)->owner); + else if(strcmp(classNameOfAutoObject(obj), "KProcess") == 0) + hwInfo = hwInfoOfProcess((KProcess *)obj); + else + hwInfo = NULL; + + *out = hwInfo != NULL ? KPROCESSHWINFO_GET_RVALUE(hwInfo, contextId) : -1; + + obj->vtable->DecrementReferenceCount(obj); + return 0; + } + else + return GetHandleInfo(out, handle, type); +} diff --git a/k11_extension/source/svc/GetProcessInfo.c b/k11_extension/source/svc/GetProcessInfo.c index ecb4835..b1e0dad 100644 --- a/k11_extension/source/svc/GetProcessInfo.c +++ b/k11_extension/source/svc/GetProcessInfo.c @@ -73,12 +73,12 @@ Result GetProcessInfoHook(s64 *out, Handle processHandle, u32 type) *out = (s64)(u64)(u32)codeSetOfProcess(process)->dataSection.section.loadAddress; break; case 0x10008: - *out = (isN3DS ? hwInfoOfProcess(process)->N3DS.translationTableBase : - (kernelVersion >= SYSTEM_VERSION(2, 44, 6) - ? hwInfoOfProcess(process)->O3DS8x.translationTableBase - : hwInfoOfProcess(process)->O3DSPre8x.translationTableBase) - ) & ~((1 << (14 - TTBCR)) - 1); + { + KProcessHwInfo *hwInfo = hwInfoOfProcess(process); + u32 ttb = KPROCESSHWINFO_GET_RVALUE(hwInfo, translationTableBase); + *out = ttb & ~((1 << (14 - TTBCR)) - 1); break; + } default: res = 0xD8E007ED; // invalid enum value break; diff --git a/k11_extension/source/svc/wrappers.s b/k11_extension/source/svc/wrappers.s index 50367a2..7482859 100644 --- a/k11_extension/source/svc/wrappers.s +++ b/k11_extension/source/svc/wrappers.s @@ -37,6 +37,7 @@ pop {r1, r2, r12, pc} .endm +GEN_GETINFO_WRAPPER Handle GEN_GETINFO_WRAPPER System GEN_GETINFO_WRAPPER Process GEN_GETINFO_WRAPPER Thread diff --git a/sysmodules/rosalina/source/gdb/watchpoints.c b/sysmodules/rosalina/source/gdb/watchpoints.c index d11dc7d..3b21a42 100644 --- a/sysmodules/rosalina/source/gdb/watchpoints.c +++ b/sysmodules/rosalina/source/gdb/watchpoints.c @@ -25,6 +25,8 @@ */ #include "gdb/watchpoints.h" +#include "csvc.h" + #define _REENT_ONLY #include @@ -53,6 +55,92 @@ typedef struct WatchpointManager static WatchpointManager manager; +static void K_EnableMonitorModeDebugging(void) +{ + __asm__ __volatile__("cpsid aif"); + + u32 DSCR; + __asm__ __volatile__("mrc p15, 0, %[val], c0, c1, 0" : [val] "=r" (DSCR)); + DSCR |= 0x8000; + __asm__ __volatile__("mcr p14, 0, %[val], c0, c1, 0" :: [val] "r" (DSCR)); +} + +static void K_DisableWatchpoint(u32 id) +{ + u32 control; + + __asm__ __volatile__("cpsid aif"); + + if(id == 0) + { + // WCR0 + __asm__ __volatile__("mrc p14, 0, %[val], c0, c0, 7" : [val] "=r" (control)); + control &= ~1; + __asm__ __volatile__("mcr p14, 0, %[val], c0, c0, 7" :: [val] "r" (control)); + + // BCR4 + __asm__ __volatile__("mrc p14, 0, %[val], c0, c4, 5" : [val] "=r" (control)); + control &= ~1; + __asm__ __volatile__("mcr p14, 0, %[val], c0, c4, 5" :: [val] "r" (control)); + } + else if(id == 1) + { + // WCR1 + __asm__ __volatile__("mrc p14, 0, %[val], c0, c1, 7" : [val] "=r" (control)); + control &= ~1; + __asm__ __volatile__("mcr p14, 0, %[val], c0, c1, 7" :: [val] "r" (control)); + + // BCR5 + __asm__ __volatile__("mrc p14, 0, %[val], c0, c5, 5" : [val] "=r" (control)); + control &= ~1; + __asm__ __volatile__("mcr p14, 0, %[val], c0, c5, 5" :: [val] "r" (control)); + } +} + +static void K_SetWatchpoint0WithContextId(u32 DVA, u32 WCR, u32 contextId) +{ + // http://infocenter.arm.com/help/topic/com.arm.doc.ddi0360f/CEGCFFDF.html + u32 BCR = + (1 << 21) | /* compare with context ID */ + (1 << 20) | /* linked (with a WRP in our case) */ + (0xf << 5) | /* byte address select, +0 to +3 as mandated when linking with a WRP */ + (3 << 1) | /* either privileged modes or user mode, as mandated when linking with a WRP */ + (1 << 0) ; /* enabled */ + + __asm__ __volatile__("cpsid aif"); + + K_DisableWatchpoint(0); + + __asm__ __volatile__("mcr p14, 0, %[val], c0, c0, 6" :: [val] "r" (DVA)); + __asm__ __volatile__("mcr p14, 0, %[val], c0, c4, 4" :: [val] "r" (contextId)); + __asm__ __volatile__("mcr p14, 0, %[val], c0, c0, 7" :: [val] "r" (WCR)); + __asm__ __volatile__("mcr p14, 0, %[val], c0, c4, 5" :: [val] "r" (BCR)); + + __asm__ __volatile__("mcr p15, 0, %[val], c7, c10, 5" :: [val] "r" (0) : "memory"); // DMB +} + +static void K_SetWatchpoint1WithContextId(u32 DVA, u32 WCR, u32 contextId) +{ + // http://infocenter.arm.com/help/topic/com.arm.doc.ddi0360f/CEGCFFDF.html + u32 BCR = + (1 << 21) | /* compare with context ID */ + (1 << 20) | /* linked (with a WRP in our case) */ + (0xf << 5) | /* byte address select, +0 to +3 as mandated when linking with a WRP */ + (3 << 1) | /* either privileged modes or user mode, as mandated when linking with a WRP */ + (1 << 0) ; /* enabled */ + + __asm__ __volatile__("cpsid aif"); + + K_DisableWatchpoint(1); + + __asm__ __volatile__("mcr p14, 0, %[val], c0, c1, 6" :: [val] "r" (DVA)); + __asm__ __volatile__("mcr p14, 0, %[val], c0, c5, 4" :: [val] "r" (contextId)); + __asm__ __volatile__("mcr p14, 0, %[val], c0, c1, 7" :: [val] "r" (WCR)); + __asm__ __volatile__("mcr p14, 0, %[val], c0, c5, 5" :: [val] "r" (BCR)); + + __asm__ __volatile__("mcr p15, 0, %[val], c7, c10, 5" :: [val] "r" (0) : "memory"); // DMB +} + void GDB_ResetWatchpoints(void) { static bool lockInitialized = false; @@ -63,11 +151,9 @@ void GDB_ResetWatchpoints(void) } RecursiveLock_Lock(&watchpointManagerLock); - svcSetHardwareBreakPoint(4, 0, 0); - svcSetHardwareBreakPoint(0x100, 0, 0); - - svcSetHardwareBreakPoint(5, 0, 0); - svcSetHardwareBreakPoint(0x101, 0, 0); + svcCustomBackdoor(K_EnableMonitorModeDebugging); + svcCustomBackdoor(K_DisableWatchpoint, 0); + svcCustomBackdoor(K_DisableWatchpoint, 1); memset(&manager, 0, sizeof(WatchpointManager)); @@ -93,26 +179,20 @@ int GDB_AddWatchpoint(GDBContext *ctx, u32 address, u32 size, WatchpointKind kin u32 id = manager.watchpoints[0].kind == WATCHPOINT_DISABLED ? 0 : 1; u32 selectMask = ((1 << size) - 1) << offset; - u32 BCR = (1 << 21) | /* compare with context ID */ - (1 << 20) | /* linked (with a WRP in our case) */ - (0xf << 5) | /* byte address select, +0 to +3 as mandated when linking with a WRP */ - (3 << 1) | /* either privileged modes or user mode, as mandated when linking with a WRP */ - (1 << 0) ; /* enabled */ - u32 WCR = (1 << 20) | /* linked */ ((4 + id) << 16) | /* ID of the linked BRP */ (selectMask << 5) | /* byte address select */ ((u32)kind << 3) | /* kind */ (2 << 1) | /* user mode only */ (1 << 0) ; /* enabled */ + + s64 out; - Result r = svcSetHardwareBreakPoint(0x100 | id, WCR, address & ~3); - - if(R_SUCCEEDED(r)) - r = svcSetHardwareBreakPoint(4 + id, BCR, (u32)ctx->debug); + Result r = svcGetHandleInfo(&out, ctx->debug, 0x10000); // context ID if(R_SUCCEEDED(r)) { + svcCustomBackdoor(id == 0 ? K_SetWatchpoint0WithContextId : K_SetWatchpoint1WithContextId, address, WCR, (u32)out); Watchpoint *watchpoint = &manager.watchpoints[id]; manager.total++; watchpoint->address = address; @@ -144,8 +224,7 @@ int GDB_RemoveWatchpoint(GDBContext *ctx, u32 address, WatchpointKind kind) } else { - svcSetHardwareBreakPoint(4 + id, 0, 0); - svcSetHardwareBreakPoint(0x100 | id, 0, 0); + svcCustomBackdoor(K_DisableWatchpoint, id); memset(&manager.watchpoints[id], 0, sizeof(Watchpoint)); manager.total--;