Compare commits

..

34 Commits

Author SHA1 Message Date
Beatrice
0bce9523a2 Update 'README.md' 2022-05-31 00:48:33 +02:00
Lorenzo Dellacà
884b4a4e4a removed exception dump parser, moved to new repo: https://github.com/LumaTeam/luma3ds_exception_dump_parser 2020-07-25 13:49:44 +02:00
Lorenzo Dellacà
08110cb125 added "-lm" flag 2020-07-22 22:15:42 +02:00
Lorenzo Dellacà
fd8dd82a8d fixed various build errors 2020-07-22 22:11:32 +02:00
Lorenzo Dellacà
47b5466a6e Merge branch 'master' into 3gx-master
* master:
  rosalina: work around gsp bug
  fix typo in screen filters menu
2020-07-22 19:27:49 +02:00
Lorenzo Dellacà
15342b5030 Merge branch 'master' into 3gx-master
* master:
  removed duplicate include
2020-07-22 19:24:15 +02:00
Lorenzo Dellacà
832c215a2d removed duplicate include 2020-07-22 19:21:59 +02:00
Lorenzo Dellacà
979c59d6f2 Merge commit 'bb07a7334f064c9512bd7e387dab1b9ef9e228cd' into 3gx-master
* commit 'bb07a7334f064c9512bd7e387dab1b9ef9e228cd': (23 commits)
  update gitignore and makefile
  rosalina: ndm + shutdown issue workaround
  rosalina: ndm doesn't exist on SAFE_FIRM
  Update bug-report.md, etc
  rosalina: forgot float suffix in luminance.c
  rosalina: display min/max luminance
  hbloader: allow homebrew to write to the shared config page
  rosalina: minor menu changes
  rosalina/sm: properly interact with ndm
  k11ext: refactor ndm:u workaround
  k11ext: fix oops
  rosalina: properly rewrite luminance-setting menu, etc.
  sysmodules: use libctru configmem defs
  Fix release building (#1454)
  sysmodules: introduce "luma shared config", rewrite ndmu workaround
  rosalina: autoclose menu on sleep mode/shell closed to prevent lockup
  rosalina: prevent disconnect when shell is closed
  rosalina: properly restore screen filters when lid is reopened
  rosalina: prevent sleep mode entry if debugger/input redir is enabled to prevent lockup
  Separate exception dump parser in another repo, add boot.3dsx to release command
  ...

# Conflicts:
#	.gitignore
#	exception_dump_parser/luma3ds_exception_dump_parser/__main__.py
#	sysmodules/rosalina/source/input_redirection.c
#	sysmodules/rosalina/source/menu.c
2020-07-21 11:27:18 +02:00
Lorenzo Dellacà
5670062a0f updated .gitignore 2020-07-08 00:44:26 +02:00
Lorenzo Dellacà
53ab41e6d0 fixed ARM "undefined instruction" kernel panic when closing games 2020-07-07 00:16:45 +02:00
Lorenzo Dellacà
1176fa8f28 Merge branch 'master' into 3gx-master
* master:
  Change screen filters presets (adding more) menu & print CCT. Fixes #1442.
  rosalina: implement 800px top-screen screenshot, etc. Fixes #1443

# Conflicts:
#	sysmodules/rosalina/source/draw.c
2020-07-06 20:20:27 +02:00
Lorenzo Dellacà
aa577d0e39 Update README.md 2020-07-04 14:43:01 +02:00
Lorenzo Dellacà
241dd35000 Update README.md 2020-07-04 14:33:01 +02:00
Lorenzo Dellacà
ca48641a7e fix make error 2020-07-04 11:51:15 +02:00
Lorenzo Dellacà
1fce207bcf trying to fix crashes (cheats, closing games) 2020-07-04 11:49:36 +02:00
Lorenzo Dellacà
c58cb2d916 credits update 2020-07-04 11:30:08 +02:00
Lorenzo Dellacà
d734e36a3a added debug info, udpated makefile to show beta version 2020-07-04 04:16:55 +02:00
Lorenzo Dellacà
28d84f30bb no message 2020-07-04 03:49:02 +02:00
Lorenzo Dellacà
0bb56031d7 no message 2020-07-04 03:38:28 +02:00
Lorenzo Dellacà
cc9977774e fix compile errors 2020-07-04 03:30:15 +02:00
Lorenzo Dellacà
a39053c3c3 Merge remote-tracking branch 'origin/master'
* origin/master: (98 commits)
  rosalina: fix for latest libctru changes
  pm: fix critical bugs where 1.0(?) titles not in the list have scheduling mode misconfigured
  loader: revert to use the NS patch due to a Nintendo bug: https://www.3dbrew.org/wiki/NCCH/Extended_Header#Flag1
  loader: replace NS N3DS CPU patch with exheader override, fix overriding exheader with homebrew
  rosalina: ntp: use PTMSYSM_SetRtcTime
  revert the memory map to the old one (mostly)
  fix module loading
  kext: fix outer memory cacheability on newer versions
  so bascially rosalina's image...
  rosalina: add hidden debug info menu
  rosalina: refactor menu handling
  rosalina: rephrase brightness warning
  rosalina: add brightness control menu
  rosalina/pm: remove fs patch, use pm instead
  rosalina: cleanup variable names
  rosalina: reorder menus
  Fix latest commit
  rosalina menu: add scrolling, cpad and inputredir support (note: no ZL/ZR due to technical reasons)
  stuff
  newlib...
  ...

# Conflicts:
#	k11_extension/source/main.c
#	k11_extension/source/svc/UnmapProcessMemoryEx.c
#	sysmodules/rosalina/Makefile
#	sysmodules/rosalina/include/menu.h
#	sysmodules/rosalina/include/utils.h
#	sysmodules/rosalina/source/errdisp.c
#	sysmodules/rosalina/source/main.c
#	sysmodules/rosalina/source/menu.c
#	sysmodules/rosalina/source/menus.c
2020-07-04 02:43:27 +02:00
PabloMK7
f1b787c7d9 Merge pull request #3 from AuroraWright/master
Merge 10.0.1
2019-07-06 09:37:41 +02:00
PabloMK7
6b13b5d06b Merge branch 'AuroraWright-master' 2019-06-29 23:35:07 +02:00
PabloMK7
9705083b65 Fix conflicting files 2019-06-29 23:34:45 +02:00
PabloMK7
2423d1802e Add plgldr string to config menu 2019-06-29 17:16:17 +02:00
PabloMK7
0b68baa0dd Merge branch 'Nanquitas-master' 2019-06-29 16:26:26 +02:00
PabloMK7
8168d2c2f9 Manually merge files 2019-06-29 16:26:03 +02:00
Nanquitas
4b341e039a Merge branch 'master' of https://github.com/AuroraWright/Luma3DS 2018-11-15 13:49:35 +01:00
Nanquitas
1ae01c2406 GDB: add 'catchsvc' command to catch svc with IDA
Usage:
   - 'catchsvc 0' : Don't catch svcs
   - 'catchsvc 1' : Catch all svcs
   - 'catchsvc 1;19;24;32;' : Only catch svc 0x19, svc 0x24 and svc 0x32
2018-11-15 13:44:45 +01:00
Nanquitas
2182742708 Implement plugin loader 2018-11-15 13:38:19 +01:00
Nanquitas
2520079536 Increase code dump on exception 2018-08-04 16:22:16 +02:00
Aurora Wright
d7095ce37d Fix patchKernel9Panic on 11.8 NATIVE_FIRM (pattern tested down to 3.0) 2018-08-04 16:11:14 +02:00
Nanquitas
0a87e41c66 socAccept: Fix an omitted comment, which masked a condition 2018-08-04 16:10:35 +02:00
Nanquitas
bec8daf028 Fix sleep issue (freeze) when InputRedirection is enabled 2018-06-23 23:06:18 +02:00
57 changed files with 2747 additions and 90 deletions

1
.gitignore vendored
View File

@@ -19,5 +19,6 @@ exceptions/arm11/build
.project
.cproject
.settings
.idea/
Luma3DS*.zip

View File

@@ -1,7 +1,46 @@
# Luma3DS
*Noob-proof (N)3DS "Custom Firmware"*
# Luma3DS-3GX Plugin Edition
*Noob-proof (N)3DS "Custom Firmware", with 3GX plugins support*
### What it is
### 3GX Plugin Edition
This edition of **Luma3DS** allows the loading of **.3GX plugins** in Luma3DS, which are otherwise officially unsupported.
**Warning!** This was a quick, unstable hotfix I published to get things to work while waiting for official updates. The official firmware has now been updated to include 3gx plugins too. Do NOT use this fork.
#
### How to install this Edition
1. download the latest `boot.firm` from [the releases page](https://github.com/mind-overflow/Luma3DS-3GX/releases/latest)
2. put the downloaded `boot.firm` file in the `root` directory of your SD card (`sd:/boot.firm`), overwriting the official Luma3DS `boot.firm`.
3. (re)boot your 3DS, and when prompted, enable:
- "Enable game patching"
- "Show NAND or user string in System Settings"
4. press `START` and let your 3DS boot.
You successfully installed the 3GX Plugin Loader! Now, proceed to the next step to learn how to install and enable 3GX plugins.
#
### How to install 3GX plugins
Plugins have to be installed in the `sd:/luma/plugins` folder.
Usually, you need to put your specific plugin in the `<TITLEID>` subdirectory, eg: `sd:/luma/plugins/<TITLEID>/<filename>.3gx`.
However, a `default.3gx` plugin can also be placed in the main `sd:/luma/plugins` directory: `sd:/luma/plugins/default.3gx`.
So:
``` yaml
sd:/luma/plugins/default.3gx # will be loaded for all games, low priority
sd:/luma/plugins/<TITLEID>/<filename>.3gx # will only be loaded for the specified title, high priority
```
Now you know how to install 3GX plugins! Proceed to the next step to learn how how to enable 3GX plugins.
#
### How to enable 3GX plugins
1. when booted, press `L + D-Pad Down + Select` to open the Rosalina menu.
2. Press `D-Pad Down` again until `Plugin Loader`, is selected, then press `A` and set it to `[Enabled]`.
Done! You learned to install the 3GX Plugin loader, install 3GX Plugins and enable them. Now, simply launch the game you want to play and press `SELECT` to open up the 3GX menu!
#
### Luma3DS introduction
**Luma3DS** is a program to patch the system software of (New) Nintendo (2)3DS handheld consoles "on the fly", adding features such as per-game language settings, debugging capabilities for developers, and removing restrictions enforced by Nintendo such as the region lock.
It also allows you to run unauthorized ("homebrew") content by removing signature checks.
@@ -16,7 +55,7 @@ Since v8.0, Luma3DS has its own in-game menu, triggerable by <kbd>L+Down+Select<
2. [makerom](https://github.com/jakcron/Project_CTR) in PATH
3. [firmtool](https://github.com/TuxSH/firmtool)
4. Up-to-date devkitARM+libctru
1. Clone the repository with `git clone https://github.com/LumaTeam/Luma3DS.git`
1. Clone the repository with `git clone https://github.com/mind-overflow/Luma3DS-3GX.git`
2. Run `make`.
The produced `boot.firm` is meant to be copied to the root of your SD card for usage with Boot9Strap.

View File

@@ -158,7 +158,7 @@ $(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
memory.o strings.o: CFLAGS += -O3
config.o: CFLAGS += -DCONFIG_TITLE="\"$(APP_TITLE) $(REVISION) configuration\""
config.o: CFLAGS += -DCONFIG_TITLE="\"$(APP_TITLE) $(REVISION)_3gx_beta configuration\""
patches.o: CFLAGS += -DVERSION_MAJOR="$(VERSION_MAJOR)" -DVERSION_MINOR="$(VERSION_MINOR)"\
-DVERSION_BUILD="$(VERSION_BUILD)" -DISRELEASE="$(IS_RELEASE)" -DCOMMIT_HASH="0x$(COMMIT)"
#---------------------------------------------------------------------------------

View File

@@ -131,6 +131,7 @@ u32 installK11Extension(u8 *pos, u32 size, bool needToInitSd, u32 baseK11VA, u32
u32 config, multiConfig, bootConfig;
u64 hbldr3dsxTitleId;
u32 rosalinaMenuCombo;
u32 rosalinaFlags;
} info;
};
@@ -203,6 +204,7 @@ u32 installK11Extension(u8 *pos, u32 size, bool needToInitSd, u32 baseK11VA, u32
info->bootConfig = configData.bootConfig;
info->hbldr3dsxTitleId = configData.hbldr3dsxTitleId;
info->rosalinaMenuCombo = configData.rosalinaMenuCombo;
info->rosalinaFlags = configData.rosalinaFlags;
info->versionMajor = VERSION_MAJOR;
info->versionMinor = VERSION_MINOR;
info->versionBuild = VERSION_BUILD;
@@ -229,6 +231,10 @@ u32 patchKernel11(u8 *pos, u32 size, u32 baseK11VA, u32 *arm11SvcTable, u32 *arm
u8 *ControlMemoryPos = instrPos + 8 + displ;
u32 *off;
// Patch ControlMemory bounds checks for mem mapping
for (off = (u32 *)ControlMemoryPos; *off != 0xE0E01BF5; ++off);
*off = 0;
/*
Here we replace currentProcess->processID == 1 by additionnalParameter == 1.
This patch should be generic enough to work even on firmware version 5.0.

View File

@@ -69,6 +69,7 @@ typedef struct __attribute__((packed, aligned(4)))
u32 config, multiConfig, bootConfig;
u64 hbldr3dsxTitleId;
u32 rosalinaMenuCombo;
u32 rosalinaFlags;
} CfgData;
typedef struct

View File

@@ -44,8 +44,12 @@ extern KAutoObject * (*KProcessHandleTable__ToKAutoObject)(KProcessHandleTable *
extern void (*KSynchronizationObject__Signal)(KSynchronizationObject *this, bool isPulse);
extern Result (*WaitSynchronization1)(void *this_unused, KThread *thread, KSynchronizationObject *syncObject, s64 timeout);
extern Result (*KProcessHandleTable__CreateHandle)(KProcessHandleTable *this, Handle *out, KAutoObject *obj, u8 token);
extern Result (*KProcessHwInfo__QueryMemory)(KProcessHwInfo *this, MemoryInfo *memoryInfo, PageInfo *pageInfo, void *address);
extern Result (*KProcessHwInfo__MapProcessMemory)(KProcessHwInfo *this, KProcessHwInfo *other, void *dst, void *src, u32 nbPages);
extern Result (*KProcessHwInfo__UnmapProcessMemory)(KProcessHwInfo *this, void *addr, u32 nbPages);
extern Result (*KProcessHwInfo__CheckVaState)(KProcessHwInfo *hwInfo, u32 va, u32 size, u32 state, u32 perm);
extern Result (*KProcessHwInfo__GetListOfKBlockInfoForVA)(KProcessHwInfo *hwInfo, KLinkedList *list, u32 va, u32 sizeInPage);
extern Result (*KProcessHwInfo__MapListOfKBlockInfo)(KProcessHwInfo *this, u32 va, KLinkedList *list, u32 state, u32 perm, u32 sbz);
extern Result (*KEvent__Clear)(KEvent *this);
extern void (*KObjectMutex__WaitAndAcquire)(KObjectMutex *this);
extern void (*KObjectMutex__ErrorOccured)(void);
@@ -53,8 +57,11 @@ extern void (*KObjectMutex__ErrorOccured)(void);
extern void (*KScheduler__AdjustThread)(KScheduler *this, KThread *thread, u32 oldSchedulingMask);
extern void (*KScheduler__AttemptSwitchingThreadContext)(KScheduler *this);
extern void (*KLinkedList_KBlockInfo__Clear)(KLinkedList *list);
extern Result (*ControlMemory)(u32 *addrOut, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader);
extern void (*SleepThread)(s64 ns);
extern Result (*CreateEvent)(Handle *out, ResetType resetType);
extern Result (*CloseHandle)(Handle handle);
extern Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type);
extern Result (*GetSystemInfo)(s64 *out, s32 type, s32 param);
@@ -65,6 +72,7 @@ extern Result (*SendSyncRequest)(Handle handle);
extern Result (*OpenProcess)(Handle *out, u32 processId);
extern Result (*GetProcessId)(u32 *out, Handle process);
extern Result (*DebugActiveProcess)(Handle *out, u32 processId);
extern Result (*SignalEvent)(Handle event);
extern Result (*UnmapProcessMemory)(Handle processHandle, void *dst, u32 size);
extern Result (*KernelSetState)(u32 type, u32 varg1, u32 varg2, u32 varg3);
@@ -129,6 +137,7 @@ typedef struct CfwInfo
u32 config, multiConfig, bootConfig;
u64 hbldr3dsxTitleId;
u32 rosalinaMenuCombo;
u32 rosalinaFlags;
} CfwInfo;
extern CfwInfo cfwInfo;
@@ -137,3 +146,5 @@ extern u32 stolenSystemMemRegionSize;
extern vu32 rosalinaState;
extern bool hasStartedRosalinaNetworkFuncsOnce;
KLinkedList* KLinkedList__Initialize(KLinkedList *list);

View File

@@ -105,6 +105,14 @@ typedef struct ALIGN(4) KMutex
union KProcess *owner;
} KMutex;
typedef struct KAddressArbiter
{
KAutoObject autoObject;
struct KThread *first;
struct KThread *last;
union KProcess *owner;
} KAddressArbiter;
/* 92 */
typedef struct KMutexLinkedList
{
@@ -112,6 +120,30 @@ typedef struct KMutexLinkedList
KMutex *last;
} KMutexLinkedList;
enum
{
TOKEN_KAUTOOBJECT = 0,
TOKEN_KSYNCHRONIZATIONOBJECT = 1,
TOKEN_KEVENT = 0x1F,
TOKEN_KSEMAPHORE = 0x2F,
TOKEN_KTIMER = 0x35,
TOKEN_KMUTEX = 0x39,
TOKEN_KDEBUG = 0x4D,
TOKEN_KSERVERPORT = 0x55,
TOKEN_KDMAOBJECT = 0x59,
TOKEN_KCLIENTPORT = 0x65,
TOKEN_KCODESET = 0x68,
TOKEN_KSESSION = 0x70,
TOKEN_KTHREAD = 0x8D,
TOKEN_KSERVERSESSION = 0x95,
TOKEN_KADDRESSARBITER = 0x98,
TOKEN_KCLIENTSESSION = 0xA5,
TOKEN_KPORT = 0xA8,
TOKEN_KSHAREDMEMORY = 0xB0,
TOKEN_KPROCESS = 0xC5,
TOKEN_KRESOURCELIMIT = 0xC8
};
/* 45 */
typedef struct KClassToken
{
@@ -540,6 +572,20 @@ typedef struct KBlockInfo
u32 pageCount;
} KBlockInfo;
typedef struct KSharedMemory
{
KAutoObject autoObject;
KLinkedList ownedKBlockInfo;
union KProcess *owner;
u32 ownerPermissions;
u32 otherPermissions;
u8 isBlockInfoGenerated;
s8 allBlockInfoGenerated;
u8 unknown_1;
u8 unknown_2;
u32 address;
} KSharedMemory;
/* 25 */
typedef struct KMemoryBlock
{
@@ -1037,10 +1083,26 @@ typedef struct KProcess##sys\
KThread *mainThread;\
u32 interruptEnabledFlags[4];\
KProcessHandleTable handleTable;\
u8 gap234[52];\
/* Custom fields for plugin system
{ */ \
u32 customFlags; /* see KProcess_CustomFlags enum below */ \
Handle onMemoryLayoutChangeEvent;\
Handle onProcessExitEvent;\
Handle resumeProcessExitEvent;\
/* } */ \
u8 gap234[36];\
u64 unused;\
} KProcess##sys;
enum KProcess_CustomFlags
{
ForceRWXPages = 1 << 0,
SignalOnMemLayoutChanges = 1 << 1,
SignalOnExit = 1 << 2,
MemLayoutChanged = 1 << 16
};
INSTANCIATE_KPROCESS(N3DS);
INSTANCIATE_KPROCESS(O3DS8x);
INSTANCIATE_KPROCESS(O3DSPre8x);

129
k11_extension/include/mmu.h Normal file
View File

@@ -0,0 +1,129 @@
#pragma once
#include "types.h"
#include "kernel.h"
typedef struct
{
u32 bits1_0 : 2; ///< 0b00
} Desc_TranslationFault;
typedef struct
{
u32 bits1_0 : 2; ///< 0b01
u32 sbz : 3;
u32 domain : 4;
u32 p : 1;
u32 addr : 21;
} Desc_CoarsePageTable;
typedef struct
{
u32 bits1_0 : 2; ///< 0b10
u32 b : 1;
u32 c : 1;
u32 xn : 1;
u32 domain : 4;
u32 p : 1;
u32 ap : 2;
u32 tex : 3;
u32 apx : 1;
u32 s : 1;
u32 ng : 1;
u32 bit18 : 1; ///< 0
u32 sbz : 1;
u32 addr : 12;
} Desc_Section;
typedef struct
{
u32 bits1_0 : 2; ///< 0b10
u32 b : 1;
u32 c : 1;
u32 xn : 1;
u32 domain : 4;
u32 p : 1;
u32 ap : 2;
u32 tex : 3;
u32 sbz : 3;
u32 bit18 : 1; ///< 1
u32 sbz2 : 5;
u32 addr : 8;
} Desc_Supersection;
typedef struct
{
u32 bits1_0 : 2; ///< 0b11
} Desc_Reserved;
typedef struct
{
u32 bits1_0 : 2; ///< 0b01
u32 b : 1;
u32 c : 1;
u32 ap : 2;
u32 sbz : 3;
u32 apx : 1;
u32 s : 1;
u32 ng : 1;
u32 tex : 3;
u32 xn : 1;
u32 addr : 16;
} Desc_LargePage;
typedef struct
{
u32 xn : 1;
u32 bit1 : 1; ///< 1
u32 b : 1;
u32 c : 1;
u32 ap : 2;
u32 tex : 3;
u32 apx : 1;
u32 s : 1;
u32 ng : 1;
u32 addr : 20;
} Desc_SmallPage;
typedef union
{
u32 raw;
Desc_TranslationFault translationFault;
Desc_CoarsePageTable coarsePageTable;
Desc_Section section;
Desc_Supersection supersection;
Desc_Reserved reserved;
} L1Descriptor;
typedef union
{
u32 raw;
Desc_TranslationFault translationFault;
Desc_LargePage largePage;
Desc_SmallPage smallPage;
} L2Descriptor;
typedef enum
{
Descriptor_TranslationFault,
Descriptor_CoarsePageTable,
Descriptor_Section,
Descriptor_Supersection,
Descriptor_Reserved,
Descriptor_LargePage,
Descriptor_SmallPage
} DescType;
void L1MMUTable__RWXForAll(u32 *table);
void L2MMUTable__RWXForAll(u32 *table);
u32 L1MMUTable__GetPAFromVA(u32 *table, u32 va);
u32 L2MMUTable__GetPAFromVA(u32 *table, u32 va);
u32 L1MMUTable__GetAddressUserPerm(u32 *table, u32 va);
u32 L2MMUTable__GetAddressUserPerm(u32 *table, u32 va);
void KProcessHwInfo__SetMMUTableToRWX(KProcessHwInfo *hwInfo);
u32 KProcessHwInfo__GetPAFromVA(KProcessHwInfo *hwInfo, u32 va);
u32 KProcessHwInfo__GetAddressUserPerm(KProcessHwInfo *hwInfo, u32 va);

View File

@@ -0,0 +1,21 @@
#pragma once
#include "utils.h"
#include "kernel.h"
#include "svc.h"
/// Operations for svcControlProcess
typedef enum ProcessOp
{
PROCESSOP_GET_ALL_HANDLES, ///< List all handles of the process, varg3 can be either 0 to fetch all handles, or token of the type to fetch
///< svcControlProcess(handle, PROCESSOP_GET_ALL_HANDLES, (u32)&outBuf, 0)
PROCESSOP_SET_MMU_TO_RWX, ///< Set the whole memory of the process with rwx access
///< svcControlProcess(handle, PROCESSOP_SET_MMU_TO_RWX, 0, 0)
PROCESSOP_GET_ON_MEMORY_CHANGE_EVENT,
PROCESSOP_GET_ON_EXIT_EVENT,
PROCESSOP_GET_PA_FROM_VA, ///< Get the physical address of the va within the process
///< svcControlProcess(handle, PROCESSOP_GET_PA_FROM_VA, (u32)&outPa, va)
PROCESSOP_SCHEDULE_THREADS,
} ProcessOp;
Result ControlProcess(Handle process, ProcessOp op, u32 varg2, u32 varg3);

View File

@@ -30,4 +30,5 @@
#include "kernel.h"
#include "svc.h"
Result MapProcessMemoryEx(Handle processHandle, void *dst, void *src, u32 size);
Result MapProcessMemoryEx(Handle dstProcessHandle, u32 vaDst, Handle srcProcessHandle, u32 vaSrc, u32 size);
Result MapProcessMemoryExWrapper(Handle dstProcessHandle, u32 vaDst, Handle srcProcessHandle, u32 vaSrc, u32 size);

View File

@@ -28,10 +28,43 @@
#include "fatalExceptionHandlers.h"
#include "utils.h"
#include "kernel.h"
#include "memory.h"
#include "mmu.h"
#include "globals.h"
#define REG_DUMP_SIZE 4 * 23
#define CODE_DUMP_SIZE 48
#define CODE_DUMP_SIZE 96
// Return true if parameters are invalid
static bool checkExceptionHandlerValidity(KProcess *process, vu32 *threadLocalStorage)
{
if (process == NULL)
return true;
u32 stackBottom = threadLocalStorage[0x11];
u32 exceptionBuf = threadLocalStorage[0x12];
MemoryInfo memInfo;
PageInfo pageInfo;
KProcessHwInfo *hwInfo = hwInfoOfProcess(process);
u32 perm = KProcessHwInfo__GetAddressUserPerm(hwInfo, threadLocalStorage[0x10]);
if (stackBottom != 1)
{
if (KProcessHwInfo__QueryMemory(hwInfo, &memInfo, &pageInfo, (void *)stackBottom)
|| (memInfo.permissions & MEMPERM_RW) != MEMPERM_RW)
return true;
}
if (exceptionBuf > 1)
{
if (KProcessHwInfo__QueryMemory(hwInfo, &memInfo, &pageInfo, (void *)exceptionBuf)
|| (memInfo.permissions & MEMPERM_RW) != MEMPERM_RW)
return true;
}
return (perm & MEMPERM_RX) != MEMPERM_RX;
}
bool isExceptionFatal(u32 spsr, u32 *regs, u32 index)
{
@@ -43,7 +76,7 @@ bool isExceptionFatal(u32 spsr, u32 *regs, u32 index)
KProcess *currentProcess = currentCoreContext->objectContext.currentProcess;
if(thread != NULL && thread->threadLocalStorage != NULL && *((vu32 *)thread->threadLocalStorage + 0x10) != 0)
return false;
return checkExceptionHandlerValidity(currentProcess, (vu32 *)thread->threadLocalStorage);
if(currentProcess != NULL)
{
@@ -52,7 +85,7 @@ bool isExceptionFatal(u32 spsr, u32 *regs, u32 index)
thread = KPROCESS_GET_RVALUE(currentProcess, mainThread);
if(thread != NULL && thread->threadLocalStorage != NULL && *((vu32 *)thread->threadLocalStorage + 0x10) != 0)
return false;
return checkExceptionHandlerValidity(currentProcess, thread->threadLocalStorage);
if(index == 3 && strcmp(codeSetOfProcess(currentProcess)->processName, "menu") == 0 && // workaround a Home Menu bug leading to a dabort
regs[0] == 0x3FFF && regs[2] == 0 && regs[5] == 2 && regs[7] == 1)
@@ -70,6 +103,7 @@ bool isDataAbortExceptionRangeControlled(u32 spsr, u32 addr)
((u32)safecpy <= addr && addr < (u32)safecpy + safecpy_sz)
);
}
void fatalExceptionHandlersMain(u32 *registerDump, u32 type, u32 cpuId)
{
ExceptionDumpHeader dumpHeader;
@@ -96,7 +130,7 @@ void fatalExceptionHandlersMain(u32 *registerDump, u32 type, u32 cpuId)
registerDump[15] = pc;
//Dump code
u8 *instr = (u8 *)pc + ((cpsr & 0x20) ? 2 : 4) - dumpHeader.codeDumpSize; //wouldn't work well on 32-bit Thumb instructions, but it isn't much of a problem
u8 *instr = (u8 *)pc + ((cpsr & 0x20) ? 2 : 4) - (dumpHeader.codeDumpSize >> 1) ; //wouldn't work well on 32-bit Thumb instructions, but it isn't much of a problem
dumpHeader.codeDumpSize = ((u32)instr & (((cpsr & 0x20) != 0) ? 1 : 3)) != 0 ? 0 : safecpy(codeDump, instr, dumpHeader.codeDumpSize);
//Copy register dump and code dump

View File

@@ -40,8 +40,12 @@ KAutoObject * (*KProcessHandleTable__ToKAutoObject)(KProcessHandleTable *this, H
void (*KSynchronizationObject__Signal)(KSynchronizationObject *this, bool isPulse);
Result (*WaitSynchronization1)(void *this_unused, KThread *thread, KSynchronizationObject *syncObject, s64 timeout);
Result (*KProcessHandleTable__CreateHandle)(KProcessHandleTable *this, Handle *out, KAutoObject *obj, u8 token);
Result (*KProcessHwInfo__QueryMemory)(KProcessHwInfo *this, MemoryInfo *memoryInfo, PageInfo *pageInfo, void *address);
Result (*KProcessHwInfo__MapProcessMemory)(KProcessHwInfo *this, KProcessHwInfo *other, void *dst, void *src, u32 nbPages);
Result (*KProcessHwInfo__UnmapProcessMemory)(KProcessHwInfo *this, void *addr, u32 nbPages);
Result (*KProcessHwInfo__CheckVaState)(KProcessHwInfo *hwInfo, u32 va, u32 size, u32 state, u32 perm);
Result (*KProcessHwInfo__GetListOfKBlockInfoForVA)(KProcessHwInfo *hwInfo, KLinkedList *list, u32 va, u32 sizeInPage);
Result (*KProcessHwInfo__MapListOfKBlockInfo)(KProcessHwInfo *this, u32 va, KLinkedList *list, u32 state, u32 perm, u32 sbz);
Result (*KEvent__Clear)(KEvent *this);
void (*KObjectMutex__WaitAndAcquire)(KObjectMutex *this);
void (*KObjectMutex__ErrorOccured)(void);
@@ -49,8 +53,11 @@ void (*KObjectMutex__ErrorOccured)(void);
void (*KScheduler__AdjustThread)(KScheduler *this, KThread *thread, u32 oldSchedulingMask);
void (*KScheduler__AttemptSwitchingThreadContext)(KScheduler *this);
void (*KLinkedList_KBlockInfo__Clear)(KLinkedList *list);
Result (*ControlMemory)(u32 *addrOut, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader);
void (*SleepThread)(s64 ns);
Result (*CreateEvent)(Handle *out, ResetType resetType);
Result (*CloseHandle)(Handle handle);
Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type);
Result (*GetSystemInfo)(s64 *out, s32 type, s32 param);
@@ -61,6 +68,7 @@ Result (*SendSyncRequest)(Handle handle);
Result (*OpenProcess)(Handle *out, u32 processId);
Result (*GetProcessId)(u32 *out, Handle process);
Result (*DebugActiveProcess)(Handle *out, u32 processId);
Result (*SignalEvent)(Handle event);
Result (*UnmapProcessMemory)(Handle processHandle, void *dst, u32 size);
Result (*KernelSetState)(u32 type, u32 varg1, u32 varg2, u32 varg3);
@@ -113,3 +121,10 @@ u32 stolenSystemMemRegionSize;
vu32 rosalinaState;
bool hasStartedRosalinaNetworkFuncsOnce;
KLinkedList* KLinkedList__Initialize(KLinkedList *list)
{
list->size = 0;
list->nodes.first = list->nodes.last = (KLinkedListNode *)&list->nodes;
return list;
}

View File

@@ -123,11 +123,61 @@ void configHook(vu8 *cfgPage)
*isDevUnit = true; // enable debug features
}
void KProcessHwInfo__MapL1Section_Hook(void);
void KProcessHwInfo__MapL2Section_Hook(void);
static void installMmuHooks(void)
{
u32 *mapL1Section = NULL;
u32 *mapL2Section = NULL;
u32 *off;
for(off = (u32 *)officialSVCs[0x1F]; *off != 0xE1CD60F0; ++off);
off = decodeArmBranch(off + 1);
for (; *off != 0xE58D5000; ++off);
off = decodeArmBranch(off + 1);
for (; *off != 0xE58DC000; ++off);
off = decodeArmBranch(off + 1);
for (; *off != 0xE1A0000B; ++off);
off = decodeArmBranch(off + 1);
for (; *off != 0xE59D2030; ++off);
off = decodeArmBranch(off + 1);
for (; *off != 0xE88D1100; ++off);
mapL2Section = (u32 *)PA_FROM_VA_PTR(decodeArmBranch(off + 1));
do
{
for (; *off != 0xE58D8000; ++off);
u32 *loc = (u32 *)PA_FROM_VA_PTR(decodeArmBranch(++off));
if (loc != mapL2Section)
mapL1Section = loc;
} while (mapL1Section == NULL);
mapL1Section[1] = 0xE28FE004; // add lr, pc, #4
mapL1Section[2] = 0xE51FF004; // ldr pc, [pc, #-4]
mapL1Section[3] = (u32)KProcessHwInfo__MapL1Section_Hook;
mapL2Section[1] = 0xE28FE004; // add lr, pc, #4
mapL2Section[2] = 0xE51FF004; // ldr pc, [pc, #-4]
mapL2Section[3] = (u32)KProcessHwInfo__MapL2Section_Hook;
}
static void findUsefulSymbols(void)
{
u32 *off;
for(off = (u32 *)0xFFFF0000; *off != 0xE1A0D002; off++);
// Patch ERRF__DumpException
for(off = (u32 *)0xFFFF0000; *off != 0xE1A04005; ++off);
++off;
*(u32 *)PA_FROM_VA_PTR(off) = makeArmBranch(off, off + 51, false);
for(; *off != 0xE2100102; ++off);
KProcessHwInfo__QueryMemory = (Result (*)(KProcessHwInfo *, MemoryInfo *, PageInfo *, void *))decodeArmBranch(off - 1);
for(; *off != 0xE1A0D002; off++);
off += 3;
initFPU = (void (*) (void))off;
@@ -190,6 +240,18 @@ static void findUsefulSymbols(void)
for(off = (u32 *)officialSVCs[0x72]; *off != 0xE2041102; off++);
KProcessHwInfo__UnmapProcessMemory = (Result (*)(KProcessHwInfo *, void *, u32))decodeArmBranch(off - 1);
for (off = (u32 *)officialSVCs[0x70]; *off != 0xE8881200 && *off != 0xE8891900; ++off);
for (off = (u32 *)decodeArmBranch(off + 1); *off != 0xE2101102; ++off);
KProcessHwInfo__CheckVaState = (Result (*)(KProcessHwInfo *, u32, u32, u32, u32))decodeArmBranch(off - 1);
for (; *off != 0xE28D1008; ++off);
KProcessHwInfo__GetListOfKBlockInfoForVA = (Result (*)(KProcessHwInfo*, KLinkedList*, u32, u32))decodeArmBranch(off + 1);
for (; *off != 0xE2000102; ++off);
KProcessHwInfo__MapListOfKBlockInfo = (Result (*)(KProcessHwInfo*, u32, KLinkedList*, u32, u32, u32))decodeArmBranch(off - 1);
for (; *off != 0xE8BD8FF0; ++off);
KLinkedList_KBlockInfo__Clear = (void (*)(KLinkedList *))decodeArmBranch(off - 6);
for(off = (u32 *)officialSVCs[0x7C]; *off != 0x03530000; off++);
KObjectMutex__WaitAndAcquire = (void (*)(KObjectMutex *))decodeArmBranch(++off);
for(; *off != 0xE320F000; off++);
@@ -235,6 +297,7 @@ static void findUsefulSymbols(void)
ControlMemory = (Result (*)(u32 *, u32, u32, u32, MemOp, MemPerm, bool))
decodeArmBranch((u32 *)officialSVCs[0x01] + 5);
SleepThread = (void (*)(s64))officialSVCs[0x0A];
CreateEvent = (Result (*)(Handle *, ResetType))decodeArmBranch((u32 *)officialSVCs[0x17] + 3);
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);
@@ -245,6 +308,7 @@ static void findUsefulSymbols(void)
OpenProcess = (Result (*)(Handle *, u32))decodeArmBranch((u32 *)officialSVCs[0x33] + 3);
GetProcessId = (Result (*)(u32 *, Handle))decodeArmBranch((u32 *)officialSVCs[0x35] + 3);
DebugActiveProcess = (Result (*)(Handle *, u32))decodeArmBranch((u32 *)officialSVCs[0x60] + 3);
SignalEvent = (Result (*)(Handle event))officialSVCs[0x18];
UnmapProcessMemory = (Result (*)(Handle, void *, u32))officialSVCs[0x72];
KernelSetState = (Result (*)(u32, u32, u32, u32))((u32 *)officialSVCs[0x7C] + 1);
@@ -276,6 +340,8 @@ static void findUsefulSymbols(void)
invalidateInstructionCacheRange = (void (*)(void *, u32))off2;
}
}
installMmuHooks();
}
void main(FcramLayout *layout, KCoreContext *ctxs)

315
k11_extension/source/mmu.c Normal file
View File

@@ -0,0 +1,315 @@
#include "mmu.h"
#include "globals.h"
#include "utils.h"
DescType L1Descriptor__GetType(u32 descriptor)
{
L1Descriptor pdesc = {descriptor};
if (pdesc.reserved.bits1_0 == 0b00)
return Descriptor_TranslationFault;
if (pdesc.reserved.bits1_0 == 0b01)
return Descriptor_CoarsePageTable;
if (pdesc.reserved.bits1_0 == 0b10)
return pdesc.section.bit18 == 0 ? Descriptor_Section : Descriptor_Supersection;
return Descriptor_Reserved;
}
DescType L2Descriptor__GetType(u32 descriptor)
{
L2Descriptor pdesc = {descriptor};
if (pdesc.translationFault.bits1_0 == 0b01)
return Descriptor_LargePage;
if (pdesc.smallPage.bit1 == 1)
return Descriptor_SmallPage;
return Descriptor_TranslationFault;
}
void L1MMUTable__RWXForAll(u32 *table)
{
u32 *tableEnd = table + 1024;
for (; table != tableEnd; ++table)
{
L1Descriptor descriptor = {*table};
switch (L1Descriptor__GetType(descriptor.raw))
{
case Descriptor_CoarsePageTable:
{
u32 *l2table = (u32 *)((descriptor.coarsePageTable.addr << 10) - 0x40000000);
L2MMUTable__RWXForAll(l2table);
break;
}
case Descriptor_Section:
{
descriptor.section.xn = 0;
descriptor.section.apx = 0;
descriptor.section.ap = 3;
*table = descriptor.raw;
break;
}
case Descriptor_Supersection:
{
descriptor.supersection.xn = 0;
descriptor.supersection.ap = 3;
*table = descriptor.raw;
break;
}
default:
break;
}
}
}
void L2MMUTable__RWXForAll(u32 *table)
{
u32 *tableEnd = table + 256;
for (; table != tableEnd; ++table)
{
L2Descriptor descriptor = {*table};
switch (L2Descriptor__GetType(descriptor.raw))
{
case Descriptor_LargePage:
{
descriptor.largePage.xn = 0;
descriptor.largePage.apx = 0;
descriptor.largePage.ap = 3;
*table = descriptor.raw;
break;
}
case Descriptor_SmallPage:
{
descriptor.smallPage.xn = 0;
descriptor.smallPage.apx = 0;
descriptor.smallPage.ap = 3;
*table = descriptor.raw;
break;
}
default:
break;
}
}
}
u32 L1MMUTable__GetPAFromVA(u32 *table, u32 va)
{
u32 pa = 0;
L1Descriptor descriptor = {table[va >> 20]};
switch (L1Descriptor__GetType(descriptor.raw))
{
case Descriptor_CoarsePageTable:
{
u32 *l2table = (u32 *)((descriptor.coarsePageTable.addr << 10) - 0x40000000);
pa = L2MMUTable__GetPAFromVA(l2table, va);
break;
}
case Descriptor_Section:
{
pa = descriptor.section.addr << 20;
pa |= (va << 12) >> 12;
break;
}
case Descriptor_Supersection:
{
pa = descriptor.supersection.addr << 24;
pa |= (va << 8) >> 8;
break;
}
default:
// VA not found
break;
}
return pa;
}
u32 L2MMUTable__GetPAFromVA(u32 *table, u32 va)
{
u32 pa = 0;
L2Descriptor descriptor = {table[(va << 12) >> 24]};
switch(L2Descriptor__GetType(descriptor.raw))
{
case Descriptor_LargePage:
{
pa = descriptor.largePage.addr << 16;
pa |= va & 0xFFFF;
break;
}
case Descriptor_SmallPage:
{
pa = descriptor.smallPage.addr << 12;
pa |= va & 0xFFF;
break;
}
default:
break;
}
return pa;
}
u32 L1MMUTable__GetAddressUserPerm(u32 *table, u32 va)
{
u32 perm = 0;
L1Descriptor descriptor = {table[va >> 20]};
switch (L1Descriptor__GetType(descriptor.raw))
{
case Descriptor_CoarsePageTable:
{
u32 *l2table = (u32 *)((descriptor.coarsePageTable.addr << 10) - 0x40000000);
perm = L2MMUTable__GetAddressUserPerm(l2table, va);
break;
}
case Descriptor_Section:
{
perm = descriptor.section.ap >> 1;
if (perm)
{
perm |= (!descriptor.section.apx && (descriptor.section.ap & 1)) << 1;
perm |= (!descriptor.section.xn) << 2;
}
break;
}
case Descriptor_Supersection:
{
perm = descriptor.supersection.ap >> 1;
if (perm)
{
perm |= (descriptor.supersection.ap & 1) << 1;
perm |= (!descriptor.supersection.xn) << 2;
}
break;
}
default:
// VA not found
break;
}
return perm;
}
u32 L2MMUTable__GetAddressUserPerm(u32 *table, u32 va)
{
u32 perm = 0;
L2Descriptor descriptor = {table[(va << 12) >> 24]};
switch(L2Descriptor__GetType(descriptor.raw))
{
case Descriptor_LargePage:
{
perm = descriptor.largePage.ap >> 1;
if (perm)
{
perm |= (!descriptor.largePage.apx && (descriptor.largePage.ap & 1)) << 1;
perm |= (!descriptor.largePage.xn) << 2;
}
break;
}
case Descriptor_SmallPage:
{
perm = descriptor.smallPage.ap >> 1;
if (perm)
{
perm |= (!descriptor.smallPage.apx && (descriptor.smallPage.ap & 1)) << 1;
perm |= (!descriptor.smallPage.xn) << 2;
}
break;
}
default:
break;
}
return perm;
}
void KProcessHwInfo__SetMMUTableToRWX(KProcessHwInfo *hwInfo)
{
KObjectMutex *mutex = KPROCESSHWINFO_GET_PTR(hwInfo, mutex);
u32 *table = KPROCESSHWINFO_GET_RVALUE(hwInfo, mmuTableVA);
KObjectMutex__Acquire(mutex);
L1MMUTable__RWXForAll(table);
KObjectMutex__Release(mutex);
}
u32 KProcessHwInfo__GetPAFromVA(KProcessHwInfo *hwInfo, u32 va)
{
KObjectMutex *mutex = KPROCESSHWINFO_GET_PTR(hwInfo, mutex);
u32 *table = KPROCESSHWINFO_GET_RVALUE(hwInfo, mmuTableVA);
KObjectMutex__Acquire(mutex);
u32 pa = L1MMUTable__GetPAFromVA(table, va);
KObjectMutex__Release(mutex);
return pa;
}
u32 KProcessHwInfo__GetAddressUserPerm(KProcessHwInfo *hwInfo, u32 va)
{
KObjectMutex *mutex = KPROCESSHWINFO_GET_PTR(hwInfo, mutex);
u32 *table = KPROCESSHWINFO_GET_RVALUE(hwInfo, mmuTableVA);
KObjectMutex__Acquire(mutex);
u32 perm = L1MMUTable__GetAddressUserPerm(table, va);
KObjectMutex__Release(mutex);
return perm;
}
static union
{
u32 raw;
struct
{
u32 xn : 1;
u32 unkn : 1;
u32 cb : 2;
u32 ap : 2;
u32 tex : 3;
u32 apx : 1;
u32 s : 1;
u32 ng : 1;
};
} g_rwxState;
// This function patch the permission when memory is mapped in the mmu table (rwx)
KProcessHwInfo *PatchDescriptorAccessControl(KProcessHwInfo *hwInfo, u32 **outState)
{
KProcess *process = (KProcess *)((u32)hwInfo - 0x1C);
u32 state = **outState;
u32 flags = KPROCESS_GET_RVALUE(process, customFlags);
if (flags & SignalOnMemLayoutChanges)
*KPROCESS_GET_PTR(process, customFlags) |= MemLayoutChanged;
if (!(flags & ForceRWXPages))
return hwInfo;
g_rwxState.raw = state;
g_rwxState.xn = 0;
g_rwxState.ap = 3;
g_rwxState.apx = 0;
*outState = &g_rwxState.raw;
return hwInfo;
}

View File

@@ -44,6 +44,7 @@
#include "svc/MapProcessMemoryEx.h"
#include "svc/UnmapProcessMemoryEx.h"
#include "svc/ControlService.h"
#include "svc/ControlProcess.h"
#include "svc/CopyHandle.h"
#include "svc/TranslateHandle.h"
@@ -59,13 +60,16 @@ void signalSvcEntry(u8 *pageEnd)
// Since DBGEVENT_SYSCALL_ENTRY is non blocking, we'll cheat using EXCEVENT_UNDEFINED_SYSCALL (debug->svcId is fortunately an u16!)
if(debugOfProcess(currentProcess) != NULL && shouldSignalSyscallDebugEvent(currentProcess, svcId))
{
SignalDebugEvent(DBGEVENT_OUTPUT_STRING, 0xFFFFFFFE, svcId);
}
}
void signalSvcReturn(u8 *pageEnd)
{
u32 svcId = (u32) *(u8 *)(pageEnd - 0xB5);
KProcess *currentProcess = currentCoreContext->objectContext.currentProcess;
u32 flags = KPROCESS_GET_RVALUE(currentProcess, customFlags);
if(svcId == 0xFE)
svcId = *(u32 *)(pageEnd - 0x110 + 8 * 4); // r12 ; note: max theortical SVC atm: 0x1FFFFFFF. We don't support catching svcIds >= 0x100 atm either
@@ -73,6 +77,13 @@ void signalSvcReturn(u8 *pageEnd)
// Since DBGEVENT_SYSCALL_RETURN is non blocking, we'll cheat using EXCEVENT_UNDEFINED_SYSCALL (debug->svcId is fortunately an u16!)
if(debugOfProcess(currentProcess) != NULL && shouldSignalSyscallDebugEvent(currentProcess, svcId))
SignalDebugEvent(DBGEVENT_OUTPUT_STRING, 0xFFFFFFFF, svcId);
// Signal if the memory layout of the process changed
if (flags & SignalOnMemLayoutChanges && flags & MemLayoutChanged)
{
*KPROCESS_GET_PTR(currentProcess, customFlags) = flags & ~MemLayoutChanged;
SignalEvent(KPROCESS_GET_RVALUE(currentProcess, onMemoryLayoutChangeEvent));
}
}
void postprocessSvc(void)
@@ -91,10 +102,26 @@ void *svcHook(u8 *pageEnd)
u32 svcId = *(u8 *)(pageEnd - 0xB5);
if(svcId == 0xFE)
svcId = *(u32 *)(pageEnd - 0x110 + 8 * 4); // r12 ; note: max theortical SVC atm: 0x3FFFFFFF. We don't support catching svcIds >= 0x100 atm either
switch(svcId)
{
case 0x01:
return ControlMemoryHookWrapper;
case 0x03: /* svcExitProcess */
{
// Signal that the process is about to be terminated
u32 flags = KPROCESS_GET_RVALUE(currentProcess, customFlags);
if (flags & SignalOnExit)
{
SignalEvent(KPROCESS_GET_RVALUE(currentProcess, onProcessExitEvent));
KEvent* event = (KEvent *)KProcessHandleTable__ToKAutoObject(handleTableOfProcess(currentProcess),
KPROCESS_GET_RVALUE(currentProcess, resumeProcessExitEvent));
((KAutoObject *)event)->vtable->DecrementReferenceCount((KAutoObject *)event);
}
return officialSVCs[0x3];
}
case 0x29:
return GetHandleInfoHookWrapper;
case 0x2A:
@@ -136,7 +163,7 @@ void *svcHook(u8 *pageEnd)
return invalidateEntireInstructionCache;
case 0xA0:
return MapProcessMemoryEx;
return MapProcessMemoryExWrapper;
case 0xA1:
return UnmapProcessMemoryEx;
case 0xA2:
@@ -148,6 +175,8 @@ void *svcHook(u8 *pageEnd)
return CopyHandleWrapper;
case 0xB2:
return TranslateHandleWrapper;
case 0xB3:
return ControlProcess;
default:
return (svcId <= 0x7D) ? officialSVCs[svcId] : NULL;

View File

@@ -0,0 +1,220 @@
#include "svc/ControlProcess.h"
#include "memory.h"
#include "mmu.h"
#include "synchronization.h"
typedef bool (*ThreadPredicate)(KThread *thread);
static void rosalinaLockThread(KThread *thread)
{
KThread *syncThread = synchronizationMutex->owner;
if(syncThread == NULL || syncThread != thread)
rosalinaRescheduleThread(thread, true);
}
Result ControlProcess(Handle processHandle, ProcessOp op, u32 varg2, u32 varg3)
{
Result res = 0;
KProcess *process;
KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess);
if(processHandle == CUR_PROCESS_HANDLE)
{
process = currentCoreContext->objectContext.currentProcess;
KAutoObject__AddReference((KAutoObject *)process);
}
else
process = KProcessHandleTable__ToKProcess(handleTable, processHandle);
if(process == NULL)
return 0xD8E007F7; // invalid handle
switch (op)
{
case PROCESSOP_GET_ALL_HANDLES:
{
KProcessHandleTable *table = handleTableOfProcess(process);
u32 *originalHandleList = (u32 *)varg2;
u32 count = 0;
u32 searchForToken = varg3;
HandleDescriptor *handleDesc = table->handleTable == NULL ? table->internalTable : table->handleTable;
for (u32 idx = 0; idx < (u32)table->maxHandleCount; ++idx, ++handleDesc)
{
if (handleDesc->pointer == NULL)
continue;
if (searchForToken)
{
KClassToken token;
handleDesc->pointer->vtable->GetClassToken(&token, handleDesc->pointer);
if (searchForToken != token.flags)
continue;
}
*originalHandleList++ = idx | ((handleDesc->info << 16) >> 1);
++count;
}
res = count;
break;
}
case PROCESSOP_SET_MMU_TO_RWX:
{
KProcessHwInfo *hwInfo = hwInfoOfProcess(process);
*KPROCESS_GET_PTR(process, customFlags) |= ForceRWXPages;
KProcessHwInfo__SetMMUTableToRWX(hwInfo);
break;
}
case PROCESSOP_GET_ON_MEMORY_CHANGE_EVENT:
{
// Only accept current process for this command
if (process != currentCoreContext->objectContext.currentProcess)
{
res = 0xD8E007F7; // invalid handle
break;
}
Handle *onMemoryLayoutChangeEvent = KPROCESS_GET_PTR(process, onMemoryLayoutChangeEvent);
if (*onMemoryLayoutChangeEvent == 0)
res = CreateEvent(onMemoryLayoutChangeEvent, RESET_ONESHOT);
if (res >= 0)
{
*KPROCESS_GET_PTR(process, customFlags) |= SignalOnMemLayoutChanges;
KAutoObject * event = KProcessHandleTable__ToKAutoObject(handleTable, *onMemoryLayoutChangeEvent);
createHandleForThisProcess((Handle *)varg2, event);
((KAutoObject *)event)->vtable->DecrementReferenceCount((KAutoObject *)event); ///< This avoid an extra operation on process exit
///< Closing the handle in the handle table will destroy the event
}
break;
}
case PROCESSOP_GET_ON_EXIT_EVENT:
{
// Only accept current process for this command
if (process != currentCoreContext->objectContext.currentProcess)
{
res = 0xD8E007F7; // invalid handle
break;
}
Handle *onProcessExitEvent = KPROCESS_GET_PTR(process, onProcessExitEvent);
Handle *resumeProcessExitEvent = KPROCESS_GET_PTR(process, resumeProcessExitEvent);
if (*onProcessExitEvent == 0)
res = CreateEvent(onProcessExitEvent, RESET_ONESHOT);
if (*resumeProcessExitEvent == 0)
res |= CreateEvent(resumeProcessExitEvent, RESET_ONESHOT);
if (res >= 0)
{
*KPROCESS_GET_PTR(process, customFlags) |= SignalOnExit;
KAutoObject * event = KProcessHandleTable__ToKAutoObject(handleTable, *onProcessExitEvent);
createHandleForThisProcess((Handle *)varg2, event);
((KAutoObject *)event)->vtable->DecrementReferenceCount((KAutoObject *)event); ///< See higher
event = KProcessHandleTable__ToKAutoObject(handleTable, *resumeProcessExitEvent);
createHandleForThisProcess((Handle *)varg3, event);
((KAutoObject *)event)->vtable->DecrementReferenceCount((KAutoObject *)event); ///< See higher
}
break;
}
case PROCESSOP_GET_PA_FROM_VA:
{
KProcessHwInfo *hwInfo = hwInfoOfProcess(process);
u32 pa = KProcessHwInfo__GetPAFromVA(hwInfo, varg3);
*(u32 *)varg2 = pa;
if (pa == 0)
res = 0xE0E01BF5; ///< Invalid address
break;
}
case PROCESSOP_SCHEDULE_THREADS:
{
ThreadPredicate threadPredicate = (ThreadPredicate)varg3;
KRecursiveLock__Lock(criticalSectionLock);
if (varg2 == 0) // Unlock
{
for(KLinkedListNode *node = threadList->list.nodes.first; node != (KLinkedListNode *)&threadList->list.nodes; node = node->next)
{
KThread *thread = (KThread *)node->key;
if((thread->schedulingMask & 0xF) == 2) // thread is terminating
continue;
if(thread->schedulingMask & 0x40)
rosalinaRescheduleThread(thread, false);
}
}
else // Lock
{
bool currentThreadsFound = false;
for(KLinkedListNode *node = threadList->list.nodes.first; node != (KLinkedListNode *)&threadList->list.nodes; node = node->next)
{
KThread *thread = (KThread *)node->key;
if(thread->ownerProcess != process
|| (threadPredicate != NULL && !threadPredicate(thread)))
continue;
if(thread == coreCtxs[thread->coreId].objectContext.currentThread)
currentThreadsFound = true;
else
rosalinaLockThread(thread);
}
if(currentThreadsFound)
{
for(KLinkedListNode *node = threadList->list.nodes.first; node != (KLinkedListNode *)&threadList->list.nodes; node = node->next)
{
KThread *thread = (KThread *)node->key;
if(thread->ownerProcess != process
|| (threadPredicate != NULL && !threadPredicate(thread)))
continue;
if(!(thread->schedulingMask & 0x40))
{
rosalinaLockThread(thread);
KRecursiveLock__Lock(criticalSectionLock);
if(thread->coreId != getCurrentCoreID())
{
u32 cpsr = __get_cpsr();
__disable_irq();
coreCtxs[thread->coreId].objectContext.currentScheduler->triggerCrossCoreInterrupt = true;
currentCoreContext->objectContext.currentScheduler->triggerCrossCoreInterrupt = true;
__set_cpsr_cx(cpsr);
}
KRecursiveLock__Unlock(criticalSectionLock);
}
}
KScheduler__TriggerCrossCoreInterrupt(currentCoreContext->objectContext.currentScheduler);
}
}
KRecursiveLock__Unlock(criticalSectionLock);
break;
}
default:
res = 0xF8C007F4;
}
((KAutoObject *)process)->vtable->DecrementReferenceCount((KAutoObject *)process);
return res;
}

View File

@@ -29,11 +29,14 @@
Result GetHandleInfoHook(s64 *out, Handle handle, u32 type)
{
if(type == 0x10000) // KDebug and KProcess: get context ID
Result res = 0;
if(type >= 0x10000)
{
KProcessHwInfo *hwInfo;
KProcessHwInfo *hwInfo;
KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess);
KAutoObject *obj;
KAutoObject *obj;
if(handle == CUR_PROCESS_HANDLE)
{
obj = (KAutoObject *)(currentCoreContext->objectContext.currentProcess);
@@ -45,18 +48,82 @@ Result GetHandleInfoHook(s64 *out, Handle handle, u32 type)
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;
switch (type)
{
case 0x10000: ///< Get ctx id (should probably move it to GetProcessInfo)
{
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;
*out = hwInfo != NULL ? KPROCESSHWINFO_GET_RVALUE(hwInfo, contextId) : -1;
break;
}
case 0x10001: ///< Get referenced object flags (token)
{
KClassToken token;
obj->vtable->GetClassToken(&token, obj);
*out = token.flags;
break;
}
case 0x10002: ///< Get object owner
{
Handle hOut;
KClassToken token;
KProcess * owner = NULL;
obj->vtable->GetClassToken(&token, obj);
switch(token.flags)
{
case TOKEN_KEVENT:
owner = ((KEvent *)obj)->owner;
break;
case TOKEN_KSEMAPHORE:
owner = ((KSemaphore *)obj)->owner;
break;
case TOKEN_KTIMER:
owner = ((KTimer *)obj)->owner;
break;
case TOKEN_KMUTEX:
owner = ((KMutex *)obj)->owner;
break;
case TOKEN_KDEBUG:
owner = ((KDebug *)obj)->owner;
break;
case TOKEN_KTHREAD:
owner = ((KThread *)obj)->ownerProcess;
break;
case TOKEN_KADDRESSARBITER:
owner = ((KAddressArbiter *)obj)->owner;
break;
case TOKEN_KSHAREDMEMORY:
owner = ((KSharedMemory *)obj)->owner;
break;
default:
break;
}
if (owner == NULL)
res = 0xD8E007F7;
res = createHandleForThisProcess(&hOut, (KAutoObject *)owner);
*out = hOut;
break;
}
default:
res = 0xF8C007F4;
break;
}
obj->vtable->DecrementReferenceCount(obj);
return 0;
return res;
}
else
return GetHandleInfo(out, handle, type);
return GetHandleInfo(out, handle, type);
}

View File

@@ -79,6 +79,14 @@ Result GetProcessInfoHook(s64 *out, Handle processHandle, u32 type)
*out = ttb & ~((1 << (14 - TTBCR)) - 1);
break;
}
case 0x10009:
{
KProcessHwInfo *hwInfo = hwInfoOfProcess(process);
u32 mmusize = KPROCESSHWINFO_GET_RVALUE(hwInfo, mmuTableSize);
u32 mmupa = (u32)PA_FROM_VA_PTR(KPROCESSHWINFO_GET_RVALUE(hwInfo, mmuTableVA));
*out = (s64)(mmusize | ((s64)mmupa << 32));
break;
}
default:
res = 0xD8E007ED; // invalid enum value
break;

View File

@@ -64,6 +64,9 @@ Result GetSystemInfoHook(s64 *out, s32 type, s32 param)
case 0x101:
*out = cfwInfo.rosalinaMenuCombo;
break;
case 0x102:
*out = cfwInfo.rosalinaFlags;
break;
case 0x200: // isRelease
*out = cfwInfo.flags & 1;

View File

@@ -35,6 +35,7 @@
static u32 nbEnabled = 0;
static u32 maskedPids[MAX_DEBUG];
static u32 masks[MAX_DEBUG][8] = {0};
static u32 *homeBtnPressed = NULL;
bool shouldSignalSyscallDebugEvent(KProcess *process, u8 svcId)
{
@@ -186,6 +187,15 @@ Result KernelSetStateHook(u32 type, u32 varg1, u32 varg2, u32 varg3)
KRecursiveLock__Unlock(&dbgParamsLock);
break;
}
case 0x10007:
{
// A bit crude but do the job for a simple notification + reboot, nothing sensitive here
if (varg1 > 255 && homeBtnPressed == NULL)
homeBtnPressed = PA_FROM_VA_PTR((u32 *)varg1);
else if (homeBtnPressed != NULL && *homeBtnPressed == 0)
*homeBtnPressed = varg1;
break;
}
default:
{
res = KernelSetState(type, varg1, varg2, varg3);

View File

@@ -26,19 +26,61 @@
#include "svc/MapProcessMemoryEx.h"
Result MapProcessMemoryEx(Handle processHandle, void *dst, void *src, u32 size)
Result MapProcessMemoryEx(Handle dstProcessHandle, u32 vaDst, Handle srcProcessHandle, u32 vaSrc, u32 size)
{
Result res = 0;
u32 sizeInPage = size >> 12;
KLinkedList list;
KProcess *srcProcess;
KProcess *dstProcess;
KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess);
KProcessHwInfo *currentHwInfo = hwInfoOfProcess(currentCoreContext->objectContext.currentProcess);
KProcess *process = KProcessHandleTable__ToKProcess(handleTable, processHandle);
if(process == NULL)
if (dstProcessHandle == CUR_PROCESS_HANDLE)
{
dstProcess = currentCoreContext->objectContext.currentProcess;
KAutoObject__AddReference((KAutoObject *)dstProcess);
}
else
dstProcess = KProcessHandleTable__ToKProcess(handleTable, dstProcessHandle);
if (dstProcess == NULL)
return 0xD8E007F7;
Result res = KProcessHwInfo__MapProcessMemory(currentHwInfo, hwInfoOfProcess(process), dst, src, size >> 12);
if (srcProcessHandle == CUR_PROCESS_HANDLE)
{
srcProcess = currentCoreContext->objectContext.currentProcess;
KAutoObject__AddReference((KAutoObject *)srcProcess);
}
else
srcProcess = KProcessHandleTable__ToKProcess(handleTable, srcProcessHandle);
KAutoObject *obj = (KAutoObject *)process;
obj->vtable->DecrementReferenceCount(obj);
if (srcProcess == NULL)
{
res = 0xD8E007F7;
goto exit1;
}
KLinkedList__Initialize(&list);
res = KProcessHwInfo__GetListOfKBlockInfoForVA(hwInfoOfProcess(srcProcess), &list, vaSrc, sizeInPage);
if (res >= 0)
{
// Check if the destination address is free and large enough
res = KProcessHwInfo__CheckVaState(hwInfoOfProcess(dstProcess), vaDst, size, 0, 0);
if (res == 0)
res = KProcessHwInfo__MapListOfKBlockInfo(hwInfoOfProcess(dstProcess), vaDst, &list, 0x5806, MEMPERM_RW | 0x18, 0);
}
KLinkedList_KBlockInfo__Clear(&list);
((KAutoObject *)srcProcess)->vtable->DecrementReferenceCount((KAutoObject *)srcProcess);
exit1:
((KAutoObject *)dstProcess)->vtable->DecrementReferenceCount((KAutoObject *)dstProcess);
invalidateEntireInstructionCache();
flushEntireDataCache();
return res;
}

View File

@@ -29,12 +29,45 @@
Result UnmapProcessMemoryEx(Handle processHandle, void *dst, u32 size)
{
if(GET_VERSION_MINOR(kernelVersion) < 37) // < 6.x
if (GET_VERSION_MINOR(kernelVersion) < 37) // < 6.x
return UnmapProcessMemory(processHandle, dst, size); // equivalent when size <= 64MB
KProcessHwInfo *currentHwInfo = hwInfoOfProcess(currentCoreContext->objectContext.currentProcess);
Result res = 0;
u32 sizeInPage = size >> 12;
KLinkedList list;
KProcess *process;
KProcessHwInfo *hwInfo;
KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess);
Result res = KProcessHwInfo__UnmapProcessMemory(currentHwInfo, dst, size >> 12);
if (processHandle == CUR_PROCESS_HANDLE)
{
process = currentCoreContext->objectContext.currentProcess;
KAutoObject__AddReference((KAutoObject *)process);
}
else
process = KProcessHandleTable__ToKProcess(handleTable, processHandle);
if (process == NULL)
return 0xD8E007F7;
hwInfo = hwInfoOfProcess(process);
KLinkedList__Initialize(&list);
res = KProcessHwInfo__GetListOfKBlockInfoForVA(hwInfo, &list, (u32)dst, sizeInPage);
if (res >= 0)
{
// Check for dst address to be in the right state (0x5806 as we set it with svcMapProcessMemoryEx)
res = KProcessHwInfo__CheckVaState(hwInfo, (u32)dst, size, 0x5806, 0);
if (res == 0)
res = KProcessHwInfo__MapListOfKBlockInfo(hwInfo, (u32)dst, &list, 0, 0, 0);
}
KLinkedList_KBlockInfo__Clear(&list);
((KAutoObject *)process)->vtable->DecrementReferenceCount((KAutoObject *)process);
invalidateEntireInstructionCache();
flushEntireDataCache();

View File

@@ -84,3 +84,12 @@ ControlMemoryEx:
ldr r1, [sp, #12]
add sp, #20
pop {pc}
.global MapProcessMemoryExWrapper
.type MapProcessMemoryExWrapper, %function
MapProcessMemoryExWrapper:
push {lr}
str r4, [sp, #-4]!
bl MapProcessMemoryEx
add sp, #4
pop {pc}

View File

@@ -96,6 +96,36 @@ KObjectMutex__Release:
blx r12
bx lr
.global KProcessHwInfo__MapL1Section_Hook
.type KProcessHwInfo__MapL1Section_Hook, %function
KProcessHwInfo__MapL1Section_Hook:
@r0 => hwInfo
@sp + 0x34 => our ptr to state
add r1, sp, #0x34
str lr, [sp, #-4]!
bl PatchDescriptorAccessControl
ldr lr, [sp], #4
ldmfd sp, {r0-r4}
sub sp, sp, #0x14
add r4, sp, #0x48
mov r11, #0
mov pc, lr
.global KProcessHwInfo__MapL2Section_Hook
.type KProcessHwInfo__MapL2Section_Hook, %function
KProcessHwInfo__MapL2Section_Hook:
@r0 => hwInfo
@sp + 0x34 => our ptr to state
add r1, sp, #0x34
str lr, [sp, #-4]!
bl PatchDescriptorAccessControl
ldr lr, [sp], #4
ldmfd sp, {r0-r4}
sub sp, sp, #0x4C
mov r4, r1
mov r6, r2
mov pc, lr
.global safecpy
.type safecpy, %function
safecpy:

View File

@@ -154,6 +154,21 @@ static Result loadCode(u64 titleId, prog_addrs_t *shared, u64 programHandle, int
return 0;
}
static Result PLGLDR_Init(Handle *session)
{
Result res;
while (1)
{
res = svcConnectToPort(session, "plg:ldr");
if (R_LEVEL(res) != RL_PERMANENT ||
R_SUMMARY(res) != RS_NOTFOUND ||
R_DESCRIPTION(res) != RD_NOT_FOUND
) break;
svcSleepThread(500000);
}
return res;
}
static Result GetProgramInfo(ExHeader_Info *exheaderInfo, u64 programHandle)
{
Result res = 0;
@@ -257,6 +272,9 @@ static Result LoadProcess(Handle *process, u64 programHandle)
// load code
if (R_SUCCEEDED(res = loadCode(titleId, &sharedAddr, programHandle, csi->flags.compress_exefs_code)))
{
u32 *code = (u32 *)sharedAddr.text_addr;
bool isHomebrew = code[0] == 0xEA000006 && code[8] == 0xE1A0400E;
memcpy(&codesetinfo.name, csi->name, 8);
codesetinfo.program_id = titleId;
codesetinfo.text_addr = vaddr.text_addr;
@@ -273,7 +291,37 @@ static Result LoadProcess(Handle *process, u64 programHandle)
{
res = svcCreateProcess(process, codeset, g_exheaderInfo.aci.kernel_caps.descriptors, count);
svcCloseHandle(codeset);
res = R_SUCCEEDED(res) ? 0 : res;
if (res >= 0)
{
// Try to load a plugin for the game
if (!isHomebrew && ((u32)((titleId >> 0x20) & 0xFFFFFFEDULL) == 0x00040000))
{
// Special case handling: games rebooting the 3DS on old models
if (!isN3DS && g_exheaderInfo.aci.local_caps.core_info.o3ds_system_mode > 0)
{
// Check if the plugin loader is enabled, otherwise skip the loading part
s64 out;
svcGetSystemInfo(&out, 0x10000, 0x102);
if ((out & 1) == 0)
return 0;
}
Handle plgldr = 0;
if (R_SUCCEEDED(PLGLDR_Init(&plgldr)))
{
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(1, 0, 2);
cmdbuf[1] = IPC_Desc_SharedHandles(1);
cmdbuf[2] = *process;
svcSendSyncRequest(plgldr);
svcCloseHandle(plgldr);
}
}
return 0;
}
}
}

View File

@@ -28,8 +28,9 @@ INCLUDES := include include/gdb include/menus include/redshift
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
DEFINES := -DARM11 -D_3DS
CFLAGS := -g -std=gnu11 -Wall -Wextra -Werror -Wno-unused-value -Os -mword-relocations \
-fomit-frame-pointer -ffunction-sections -fdata-sections -fno-math-errno \
CFLAGS := -g -std=gnu11 -Wall -Wextra -Wno-unused-value -O2 -mword-relocations \
-fomit-frame-pointer -ffunction-sections -fdata-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE)
@@ -39,7 +40,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map),-wrap,exit,--section-start,.text=0x14000000
LIBS := -lctru
LIBS := -lm -lctru
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing

View File

@@ -0,0 +1,37 @@
#pragma once
#include <3ds/types.h>
#include "ifile.h"
#define _3GX_MAGIC (0x3130303024584733) /* "3GX$0001" */
typedef struct PACKED
{
u32 authorLen;
const char * authorMsg;
u32 titleLen;
const char * titleMsg;
u32 summaryLen;
const char * summaryMsg;
u32 descriptionLen;
const char * descriptionMsg;
} _3gx_Infos;
typedef struct PACKED
{
u32 count;
u32 * titles;
} _3gx_Targets;
typedef struct PACKED
{
u64 magic;
u32 version;
u32 codeSize;
u32 * code;
_3gx_Infos infos;
_3gx_Targets targets;
} _3gx_Header;
Result Read_3gx_Header(IFile *file, _3gx_Header *header);
Result Read_3gx_Code(IFile *file, _3gx_Header *header, void *dst);

View File

@@ -71,18 +71,20 @@ void svcInvalidateEntireInstructionCache(void);
///@{
/**
* @brief Maps a block of process memory.
* @param process Handle of the process.
* @param dstProcessHandle Handle of the process to map the memory in (destination)
* @param destAddress Address of the mapped block in the current process.
* @param srcProcessHandle Handle of the process to map the memory from (source)
* @param srcAddress Address of the mapped block in the source process.
* @param size Size of the block of the memory to map (truncated to a multiple of 0x1000 bytes).
*/
Result svcMapProcessMemoryEx(Handle process, u32 destAddr, u32 srcAddr, u32 size);
Result svcMapProcessMemoryEx(Handle dstProcessHandle, u32 destAddress, Handle srcProcessHandle, u32 vaSrc, u32 size);
/**
* @brief Unmaps a block of process memory.
* @param process Handle of the process.
* @param destAddress Address of the block of memory to unmap, in the current (destination) process.
* @param process Handle of the process to unmap the memory from
* @param destAddress Address of the block of memory to unmap
* @param size Size of the block of memory to unmap (truncated to a multiple of 0x1000 bytes).
* This function should only be used to unmap memory mapped with svcMapProcessMemoryEx
*/
Result svcUnmapProcessMemoryEx(Handle process, u32 destAddress, u32 size);
@@ -134,4 +136,19 @@ Result svcCopyHandle(Handle *out, Handle outProcess, Handle in, Handle inProcess
* @param in The input handle.
*/
Result svcTranslateHandle(u32 *outKAddr, char *outClassName, Handle in);
/// Operations for svcControlProcess
typedef enum ProcessOp
{
PROCESSOP_GET_ALL_HANDLES, ///< List all handles of the process, varg3 can be either 0 to fetch all handles, or token of the type to fetch
///< svcControlProcess(handle, PROCESSOP_GET_ALL_HANDLES, (u32)&outBuf, 0)
PROCESSOP_SET_MMU_TO_RWX, ///< Set the whole memory of the process with rwx access
///< svcControlProcess(handle, PROCESSOP_SET_MMU_TO_RWX, 0, 0)
PROCESSOP_GET_ON_MEMORY_CHANGE_EVENT,
PROCESSOP_GET_ON_EXIT_EVENT,
PROCESSOP_GET_PA_FROM_VA, ///< Get the physical address of the va within the process
///< svcControlProcess(handle, PROCESSOP_GET_PA_FROM_VA, (u32)&outPa, va)
} ProcessOp;
Result svcControlProcess(Handle process, ProcessOp op, u32 varg2, u32 varg3);
///@}

View File

@@ -73,6 +73,7 @@
#define COLOR_WHITE RGB565(0x1F, 0x3F, 0x1F)
#define COLOR_RED RGB565(0x1F, 0x00, 0x00)
#define COLOR_GREEN RGB565(0x00, 0x1F, 0x00)
#define COLOR_LIME RGB565(0x00, 0xFF, 0x00)
#define COLOR_BLACK RGB565(0x00, 0x00, 0x00)
#define DRAW_MAX_FORMATTED_STRING_SIZE 512

View File

@@ -12,11 +12,14 @@
#define GDB_REMOTE_COMMAND_HANDLER(name) GDB_HANDLER(RemoteCommand##name)
#define GDB_DECLARE_REMOTE_COMMAND_HANDLER(name) GDB_DECLARE_HANDLER(RemoteCommand##name)
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ConvertVAToPA);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(SyncRequestInfo);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ListAllHandles);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMemRegions);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(FlushCaches);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(CatchSvc);
GDB_DECLARE_QUERY_HANDLER(Rcmd);

View File

@@ -18,8 +18,6 @@
#include <3ds/types.h>
#include <3ds/types.h>
/// Luma shared config type.
typedef struct LumaSharedConfig {
u64 hbldr_3dsx_tid; ///< Title ID to use for 3DSX loading.

View File

@@ -30,6 +30,8 @@
#include <string.h>
u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize);
void *memset32(void *dest, u32 value, u32 size);
void hexItoa(u64 number, char *out, u32 digits, bool uppercase);
unsigned long int xstrtoul(const char *nptr, char **endptr, int base, bool allowPrefix, bool *ok);
unsigned long long int xstrtoull(const char *nptr, char **endptr, int base, bool allowPrefix, bool *ok);

View File

@@ -81,7 +81,10 @@ bool menuCheckN3ds(void);
u32 menuCountItems(const Menu *menu);
MyThread *menuCreateThread(void);
void menuEnter(void);
void menuLeave(void);
void menuThreadMain(void);
void menuShow(Menu *root);
void menuEnter(void);
void menuLeave(void);
void menuThreadMain(void);
void menuShow(Menu *root);
void DispMessage(const char *title, const char *message);
u32 DispErrMessage(const char *title, const char *message, const Result error);
void DisplayPluginMenu(u32 *cmdbuf);

View File

@@ -0,0 +1,32 @@
#pragma once
#include <3ds/types.h>
#define MAX_BUFFER (50)
#define MAX_ITEMS_COUNT (64)
typedef struct
{
bool noFlash;
u32 lowTitleId;
char path[256];
u32 config[32];
} PluginLoadParameters;
typedef struct
{
u32 nbItems;
u8 states[MAX_ITEMS_COUNT];
char title[MAX_BUFFER];
char items[MAX_ITEMS_COUNT][MAX_BUFFER];
char hints[MAX_ITEMS_COUNT][MAX_BUFFER];
} PluginMenu;
Result plgLdrInit(void);
void plgLdrExit(void);
Result PLGLDR__IsPluginLoaderEnabled(bool *isEnabled);
Result PLGLDR__SetPluginLoaderState(bool enabled);
Result PLGLDR__SetPluginLoadParameters(PluginLoadParameters *parameters);
Result PLGLDR__DisplayMenu(PluginMenu *menu);
Result PLGLDR__DisplayMessage(const char *title, const char *body);
Result PLGLDR__DisplayErrMessage(const char *title, const char *body, u32 error);

View File

@@ -0,0 +1,9 @@
#pragma once
#include <3ds/types.h>
#include "MyThread.h"
MyThread * PluginLoader__CreateThread(void);
bool PluginLoader__IsEnabled(void);
void PluginLoader__MenuCallback(void);
void PluginLoader__UpdateMenu(void);

View File

@@ -0,0 +1,31 @@
/*
* This file is part of Luma3DS
* Copyright (C) 2016-2018 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 <http://www.gnu.org/licenses/>.
*
* 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
void Sleep__Init(void);
void Sleep__HandleNotification(u32 notifId);
bool Sleep__Status(void);

View File

@@ -77,3 +77,4 @@ void server_run(struct sock_server *serv);
void server_kill_connections(struct sock_server *serv);
void server_set_should_close_all(struct sock_server *serv);
void server_finalize(struct sock_server *serv);
bool Wifi__IsConnected(void);

View File

@@ -29,6 +29,7 @@
#include <3ds/svc.h>
#include <3ds/srv.h>
#include <3ds/result.h>
#include <3ds/ipc.h>
#include "csvc.h"
#include "luma_shared_config.h"
@@ -58,6 +59,22 @@ static inline void *decodeArmBranch(const void *src)
return (void *)((const u8 *)src + 8 + off);
}
static inline void assertSuccess(Result res)
{
if(R_FAILED(res))
svcBreak(USERBREAK_PANIC);
}
static inline void error(u32* cmdbuf, Result rc)
{
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);
cmdbuf[1] = rc;
}
extern bool isN3DS;
Result OpenProcessByName(const char *name, Handle *h);
Result SaveSettings(void);
static inline bool isServiceUsable(const char *name)
{
bool r;

View File

@@ -0,0 +1,62 @@
#include <3ds.h>
#include "3gx.h"
Result Read_3gx_Header(IFile *file, _3gx_Header *header)
{
u64 total;
char * dst;
Result res = 0;
res = IFile_Read(file, &total, header, sizeof(_3gx_Header));
if (R_FAILED(res))
return res;
// Read author
file->pos = (u32)header->infos.authorMsg;
dst = (char *)header + sizeof(_3gx_Header);
res = IFile_Read(file, &total, dst, header->infos.authorLen);
if (R_FAILED(res))
return res;
// Relocate ptr
header->infos.authorMsg = dst;
// Read title
file->pos = (u32)header->infos.titleMsg;
dst += header->infos.authorLen;
res = IFile_Read(file, &total, dst, header->infos.titleLen);
if (R_FAILED(res))
return res;
// Relocate ptr
header->infos.titleMsg = dst;
// Declare other members as null (unused in our case)
header->infos.summaryLen = 0;
header->infos.summaryMsg = NULL;
header->infos.descriptionLen = 0;
header->infos.descriptionMsg = NULL;
// Read targets compatibility
file->pos = (u32)header->targets.titles;
dst += header->infos.titleLen;
dst += 4 - ((u32)dst & 3); // 4 bytes aligned
res = IFile_Read(file, &total, dst, header->targets.count * sizeof(u32));
if (R_FAILED(res))
return res;
// Relocate ptr
header->targets.titles = (u32 *)dst;
return res;
}
Result Read_3gx_Code(IFile *file, _3gx_Header *header, void *dst)
{
u64 total;
Result res = 0;
file->pos = (u32)header->code;
res = IFile_Read(file, &total, dst, header->codeSize);
return res;
}

View File

@@ -59,7 +59,10 @@ SVC_BEGIN svcInvalidateEntireInstructionCache
SVC_END
SVC_BEGIN svcMapProcessMemoryEx
str r4, [sp, #-4]!
ldr r4, [sp, #4]
svc 0xA0
ldr r4, [sp], #4
bx lr
SVC_END
@@ -99,3 +102,8 @@ SVC_BEGIN svcTranslateHandle
str r1, [r2]
bx lr
SVC_END
SVC_BEGIN svcControlProcess
svc 0xB3
bx lr
SVC_END

View File

@@ -33,13 +33,6 @@
#include "ifile.h"
extern Handle preTerminationEvent;
static inline void assertSuccess(Result res)
{
if(R_FAILED(res))
svcBreak(USERBREAK_PANIC);
}
static MyThread errDispThread;
static u8 ALIGN(8) errDispThreadStack[0xD00];

View File

@@ -10,6 +10,7 @@
#include "csvc.h"
#include "fmt.h"
#include "gdb/breakpoints.h"
#include "utils.h"
#include "../utils.h"
@@ -19,12 +20,15 @@ struct
GDBCommandHandler handler;
} remoteCommandHandlers[] =
{
{ "convertvatopa" , GDB_REMOTE_COMMAND_HANDLER(ConvertVAToPA) },
{ "syncrequestinfo" , GDB_REMOTE_COMMAND_HANDLER(SyncRequestInfo) },
{ "translatehandle" , GDB_REMOTE_COMMAND_HANDLER(TranslateHandle) },
{ "listallhandles" , GDB_REMOTE_COMMAND_HANDLER(ListAllHandles) },
{ "getmmuconfig" , GDB_REMOTE_COMMAND_HANDLER(GetMmuConfig) },
{ "getmemregions" , GDB_REMOTE_COMMAND_HANDLER(GetMemRegions) },
{ "flushcaches" , GDB_REMOTE_COMMAND_HANDLER(FlushCaches) },
{ "toggleextmemaccess", GDB_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess) },
{ "catchsvc" , GDB_REMOTE_COMMAND_HANDLER(CatchSvc) },
};
static const char *GDB_SkipSpaces(const char *pos)
@@ -34,6 +38,50 @@ static const char *GDB_SkipSpaces(const char *pos)
return nextpos;
}
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ConvertVAToPA)
{
bool ok;
int n;
u32 val;
u32 pa;
char * end;
char outbuf[GDB_BUF_LEN / 2 + 1];
if(ctx->commandData[0] == 0)
return GDB_ReplyErrno(ctx, EILSEQ);
val = xstrtoul(ctx->commandData, &end, 0, true, &ok);
if(!ok)
return GDB_ReplyErrno(ctx, EILSEQ);
if (val >= 0x40000000)
pa = svcConvertVAToPA((const void *)val, false);
else
{
Handle process;
Result r = svcOpenProcess(&process, ctx->pid);
if(R_FAILED(r))
{
n = sprintf(outbuf, "Invalid process (wtf?)\n");
goto end;
}
r = svcControlProcess(process, PROCESSOP_GET_PA_FROM_VA, (u32)&pa, val);
svcCloseHandle(process);
if (R_FAILED(r))
{
n = sprintf(outbuf, "An error occured: %08X\n", r);
goto end;
}
}
n = sprintf(outbuf, "va: 0x%08X, pa: 0x%08X, b31: 0x%08X\n", val, pa, pa | (1 << 31));
end:
return GDB_SendHexPacket(ctx, outbuf, n);
}
GDB_DECLARE_REMOTE_COMMAND_HANDLER(SyncRequestInfo)
{
char outbuf[GDB_BUF_LEN / 2 + 1];
@@ -118,6 +166,29 @@ end:
return GDB_SendHexPacket(ctx, outbuf, n);
}
enum
{
TOKEN_KAUTOOBJECT = 0,
TOKEN_KSYNCHRONIZATIONOBJECT = 1,
TOKEN_KEVENT = 0x1F,
TOKEN_KSEMAPHORE = 0x2F,
TOKEN_KTIMER = 0x35,
TOKEN_KMUTEX = 0x39,
TOKEN_KDEBUG = 0x4D,
TOKEN_KSERVERPORT = 0x55,
TOKEN_KDMAOBJECT = 0x59,
TOKEN_KCLIENTPORT = 0x65,
TOKEN_KCODESET = 0x68,
TOKEN_KSESSION = 0x70,
TOKEN_KTHREAD = 0x8D,
TOKEN_KSERVERSESSION = 0x95,
TOKEN_KCLIENTSESSION = 0xA5,
TOKEN_KPORT = 0xA8,
TOKEN_KSHAREDMEMORY = 0xB0,
TOKEN_KPROCESS = 0xC5,
TOKEN_KRESOURCELIMIT = 0xC8
};
GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle)
{
bool ok;
@@ -126,10 +197,11 @@ GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle)
int n;
Result r;
u32 kernelAddr;
s64 token;
Handle handle, process;
s64 refcountRaw;
u32 refcount;
char classBuf[32], serviceBuf[12] = { 0 };
char classBuf[32], serviceBuf[12] = { 0 }, ownerBuf[50] = { 0 };
char outbuf[GDB_BUF_LEN / 2 + 1];
if(ctx->commandData[0] == 0)
@@ -161,19 +233,98 @@ GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle)
svcTranslateHandle(&kernelAddr, classBuf, handle);
svcGetHandleInfo(&refcountRaw, handle, 1);
svcGetHandleInfo(&token, handle, 0x10001);
svcControlService(SERVICEOP_GET_NAME, serviceBuf, handle);
refcount = (u32)(refcountRaw - 1);
if(serviceBuf[0] != 0)
n = sprintf(outbuf, "(%s *)0x%08lx /* %s handle, %lu %s */\n", classBuf, kernelAddr, serviceBuf, refcount, refcount == 1 ? "reference" : "references");
else if (token == TOKEN_KPROCESS)
{
svcGetProcessInfo((s64 *)serviceBuf, handle, 0x10000);
n = sprintf(outbuf, "(%s *)0x%08x /* process: %s, %u %s */\n", classBuf, kernelAddr, serviceBuf, refcount, refcount == 1 ? "reference" : "references");
}
else
n = sprintf(outbuf, "(%s *)0x%08lx /* %lu %s */\n", classBuf, kernelAddr, refcount, refcount == 1 ? "reference" : "references");
{
s64 owner;
if (R_SUCCEEDED(svcGetHandleInfo(&owner, handle, 0x10002)))
{
svcGetProcessInfo((s64 *)serviceBuf, (u32)owner, 0x10000);
svcCloseHandle((u32)owner);
sprintf(ownerBuf, " owner: %s", serviceBuf);
}
n = sprintf(outbuf, "(%s *)0x%08x /* %u %s%s */\n", classBuf, kernelAddr, refcount, refcount == 1 ? "reference" : "references", ownerBuf);
}
end:
svcCloseHandle(handle);
svcCloseHandle(process);
return GDB_SendHexPacket(ctx, outbuf, n);
}
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ListAllHandles)
{
bool ok;
u32 val;
char *end;
int n = 0;
Result r;
s32 count = 0;
Handle process, procHandles[0x100];
char outbuf[GDB_BUF_LEN / 2 + 1];
if(ctx->commandData[0] == 0)
val = 0; ///< All handles
else
{ // Get handles of specified type
val = xstrtoul(ctx->commandData, &end, 0, true, &ok);
if(!ok)
return GDB_ReplyErrno(ctx, EILSEQ);
end = (char *)GDB_SkipSpaces(end);
if(*end != 0)
return GDB_ReplyErrno(ctx, EILSEQ);
}
r = svcOpenProcess(&process, ctx->pid);
if(R_FAILED(r))
{
n = sprintf(outbuf, "Invalid process (wtf?)\n");
goto end;
}
if (R_FAILED(count = svcControlProcess(process, PROCESSOP_GET_ALL_HANDLES, (u32)procHandles, val)))
n = sprintf(outbuf, "An error occured: %08X\n", count);
else if (count == 0)
n = sprintf(outbuf, "Process has no handles ?\n");
else
{
n = sprintf(outbuf, "Found %d handles.\n", count);
const char *comma = "";
for (s32 i = 0; i < count && n < (GDB_BUF_LEN >> 1) - 20; ++i)
{
Handle handle = procHandles[i];
n += sprintf(outbuf + n, "%s0x%08X", comma, handle);
if (((i + 1) % 8) == 0)
{
outbuf[n++] = '\n';
comma = "";
}
else
comma = ", ";
}
}
end:
svcCloseHandle(process);
return GDB_SendHexPacket(ctx, outbuf, n);
}
extern bool isN3DS;
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig)
{
@@ -249,6 +400,41 @@ GDB_DECLARE_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess)
return GDB_SendHexPacket(ctx, outbuf, n);
}
GDB_DECLARE_REMOTE_COMMAND_HANDLER(CatchSvc)
{
if(ctx->commandData[0] == '0')
{
memset(ctx->svcMask, 0, 32);
return R_SUCCEEDED(svcKernelSetState(0x10002, ctx->pid, false)) ? GDB_ReplyOk(ctx) : GDB_ReplyErrno(ctx, EPERM);
}
else if(ctx->commandData[0] == '1')
{
if(ctx->commandData[1] == ';')
{
u32 id;
const char *pos = ctx->commandData + 1;
memset(ctx->svcMask, 0, 32);
do
{
pos = GDB_ParseHexIntegerList(&id, pos + 1, 1, ';');
if(pos == NULL)
return GDB_ReplyErrno(ctx, EILSEQ);
if(id < 0xFE)
ctx->svcMask[id / 32] |= 1 << (31 - (id % 32));
}
while(*pos != 0);
}
else
memset(ctx->svcMask, 0xFF, 32);
return R_SUCCEEDED(svcKernelSetState(0x10002, ctx->pid, true, ctx->svcMask)) ? GDB_ReplyOk(ctx) : GDB_ReplyErrno(ctx, EPERM);
}
else
return GDB_ReplyErrno(ctx, EILSEQ);
}
GDB_DECLARE_QUERY_HANDLER(Rcmd)
{
char commandData[GDB_BUF_LEN / 2 + 1];

View File

@@ -149,20 +149,8 @@ static const u32 kernelCaps[] =
0xFE000200, // Handle table size: 0x200
};
static inline void assertSuccess(Result res)
{
if(R_FAILED(res))
svcBreak(USERBREAK_PANIC);
}
static u16 hbldrTarget[PATH_MAX+1];
static inline void error(u32* cmdbuf, Result rc)
{
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);
cmdbuf[1] = rc;
}
static u16 *u16_strncpy(u16 *dest, const u16 *src, u32 size)
{
u32 i;

View File

@@ -32,6 +32,8 @@
#include "process_patches.h"
#include "menus.h"
#include "memory.h"
#include "sleep.h"
#include "sock_util.h"
bool inputRedirectionEnabled = false;
Handle inputRedirectionThreadStartedEvent;
@@ -121,6 +123,13 @@ void inputRedirectionThreadMain(void)
pfd.events = POLLIN;
pfd.revents = 0;
if (Sleep__Status())
{
while (!Wifi__IsConnected()
&& inputRedirectionEnabled && !preTerminationRequested)
svcSleepThread(1000000000ULL);
}
int pollres = socPoll(&pfd, 1, 10);
if(pollres > 0 && (pfd.revents & POLLIN))
{
@@ -214,7 +223,7 @@ static Result InputRedirection_DoUndoIrPatches(Handle processHandle, bool doPatc
totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize);
svcGetProcessInfo(&startAddress, processHandle, 0x10005);
res = svcMapProcessMemoryEx(processHandle, 0x00100000, (u32) startAddress, totalSize);
res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, processHandle, (u32) startAddress, totalSize);
if(R_SUCCEEDED(res) && !patchPrepared)
{
@@ -232,6 +241,8 @@ static Result InputRedirection_DoUndoIrPatches(Handle processHandle, bool doPatc
0xE3A0B080, // mov r11, #0x80
};
svcGetProcessInfo(&startAddress, processHandle, 0x10005);
res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, processHandle, (u32) startAddress, totalSize);
u32 irDataPhys = (u32)PA_FROM_VA_PTR(irData);
u32 irCodePhys = (u32)PA_FROM_VA_PTR(&irCodePatchFunc);
@@ -347,7 +358,7 @@ static Result InputRedirection_DoUndoHidPatches(Handle processHandle, bool doPat
totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize);
svcGetProcessInfo(&startAddress, processHandle, 0x10005);
res = svcMapProcessMemoryEx(processHandle, 0x00100000, (u32) startAddress, totalSize);
res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, processHandle, (u32) startAddress, totalSize);
if (R_SUCCEEDED(res) && !patchPrepared)
{

View File

@@ -34,6 +34,7 @@
#include "utils.h"
#include "MyThread.h"
#include "menus/miscellaneous.h"
#include "plgloader.h"
#include "menus/debugger.h"
#include "menus/screen_filters.h"
#include "menus/cheats.h"
@@ -262,17 +263,14 @@ int main(void)
MyThread *menuThread = menuCreateThread();
MyThread *taskRunnerThread = taskRunnerCreateThread();
MyThread *errDispThread = errDispCreateThread();
MyThread *plgloaderThread = PluginLoader__CreateThread();
if (R_FAILED(ServiceManager_Run(services, notifications, NULL)))
svcBreak(USERBREAK_PANIC);
TaskRunner_Terminate();
MyThread_Join(menuThread, -1LL);
MyThread_Join(taskRunnerThread, -1LL);
MyThread_Join(errDispThread, -1LL);
MyThread_Join(plgloaderThread, -1LL);
return 0;
}

View File

@@ -51,6 +51,15 @@ u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize)
return NULL;
}
void *memset32(void *dest, u32 value, u32 size)
{
u32 *dest32 = (u32 *)dest;
for(u32 i = 0; i < size/4; i++) dest32[i] = value;
return dest;
}
void hexItoa(u64 number, char *out, u32 digits, bool uppercase)
{
const char hexDigits[] = "0123456789ABCDEF";

View File

@@ -32,6 +32,7 @@
#include "ifile.h"
#include "menus.h"
#include "utils.h"
#include "plgloader.h"
#include "menus/n3ds.h"
#include "menus/cheats.h"
#include "minisoc.h"
@@ -143,6 +144,7 @@ u32 waitCombo(void)
static MyThread menuThread;
static u8 ALIGN(8) menuThreadStack[0x1000];
static u8 batteryLevel = 255;
static u32 homeBtnPressed = 0;
static inline u32 menuAdvanceCursor(u32 pos, u32 numItems, s32 displ)
{
@@ -168,14 +170,17 @@ u32 menuCountItems(const Menu *menu)
MyThread *menuCreateThread(void)
{
if(R_FAILED(MyThread_Create(&menuThread, menuThreadMain, menuThreadStack, 0x1000, 52, CORE_SYSTEM)))
svcKernelSetState(0x10007, &homeBtnPressed);
if(R_FAILED(MyThread_Create(&menuThread, menuThreadMain, menuThreadStack, 0x3000, 52, CORE_SYSTEM)))
svcBreak(USERBREAK_PANIC);
return &menuThread;
}
u32 menuCombo;
void menuThreadMain(void)
u32 DispWarningOnHome(void);
void menuThreadMain(void)
{
if(isN3DS)
N3DSMenu_UpdateStatus();
@@ -192,15 +197,26 @@ void menuThreadMain(void)
if (menuShouldExit)
continue;
Cheat_ApplyCheats();
if((scanHeldKeys() & menuCombo) == menuCombo)
if ((scanHeldKeys() & menuCombo) == menuCombo)
{
menuEnter();
if(isN3DS) N3DSMenu_UpdateStatus();
PluginLoader__UpdateMenu();
menuShow(&rosalinaMenu);
menuLeave();
}
// Check for home button on O3DS Mode3 with plugin loaded
if (homeBtnPressed != 0)
{
if (DispWarningOnHome())
svcKernelSetState(7); ///< reboot is fine since exiting a mode3 game reboot anyway
homeBtnPressed = 0;
}
svcSleepThread(50 * 1000 * 1000LL);
}
}
@@ -406,3 +422,158 @@ void menuShow(Menu *root)
}
while(!menuShouldExit);
}
static const char *__press_b_to_close = "Press [B] to close";
void DispMessage(const char *title, const char *message)
{
menuEnter();
Draw_Lock();
Draw_ClearFramebuffer();
Draw_FlushFramebuffer();
Draw_DrawString(10, 10, COLOR_TITLE, title);
Draw_DrawString(30, 30, COLOR_WHITE, message);
Draw_DrawString(200, 220, COLOR_TITLE, __press_b_to_close);
u32 keys = 0;
do
{
keys = waitComboWithTimeout(1000);
}while (!preTerminationRequested && !(keys & KEY_B));
Draw_Unlock(); ///< Keep it locked until we exit the message
menuLeave();
}
u32 DispErrMessage(const char *title, const char *message, const Result error)
{
char buf[100];
sprintf(buf, "Error code: 0x%08X", (unsigned int)error);
menuEnter();
Draw_Lock();
Draw_ClearFramebuffer();
Draw_FlushFramebuffer();
Draw_DrawString(10, 10, COLOR_TITLE, title);
u32 posY = Draw_DrawString(30, 30, COLOR_WHITE, message);
Draw_DrawString(30, posY + 20, COLOR_RED, buf);
Draw_DrawString(200, 220, COLOR_TITLE, __press_b_to_close);
u32 keys = 0;
do
{
keys = waitComboWithTimeout(1000);
}while (!preTerminationRequested && !(keys & KEY_B));
Draw_Unlock(); ///< Keep it locked until we exit the message
menuLeave();
return error;
}
u32 DispWarningOnHome(void)
{
menuEnter();
Draw_Lock();
Draw_ClearFramebuffer();
Draw_FlushFramebuffer();
Draw_DrawString(10, 10, COLOR_TITLE, "Warning");
u32 posY = Draw_DrawString(30, 40, COLOR_WHITE, "Due to memory shortage the home button\nis disabled.");
Draw_DrawString(30, posY + 20, COLOR_WHITE, "Press [DPAD UP + B] to exit the application.");
Draw_DrawString(200, 220, COLOR_TITLE, __press_b_to_close);
u32 keys = 0;
do
{
keys = waitComboWithTimeout(1000);
}while (!preTerminationRequested && !(keys & KEY_B));
Draw_Unlock(); ///< Keep it locked until we exit the message
menuLeave();
return (keys & KEY_UP) > 0;
}
typedef char string[50];
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
void DisplayPluginMenu(u32 *cmdbuf)
{
u32 cursor = 0;
u32 nbItems = cmdbuf[1];
u8 *states = (u8 *)cmdbuf[3];
char buffer[60];
const char *title = (const char *)cmdbuf[5];
const string *items = (const string *)cmdbuf[7];
const string *hints = (const string *)cmdbuf[9];
menuEnter();
Draw_Lock();
do
{
// Draw the menu
{
// Clear screen
Draw_ClearFramebuffer();
Draw_FlushFramebuffer();
// Draw title
Draw_DrawString(10, 10, COLOR_TITLE, title);
// Draw items
u32 i = MAX(0, (int)cursor - 7);
u32 end = MIN(nbItems, i + 16);
u32 posY = 30;
for (; i < end; ++i, posY += 10)
{
sprintf(buffer, "[ ] %s", items[i]);
Draw_DrawString(30, posY, COLOR_WHITE, buffer);
if (i == cursor) Draw_DrawCharacter(10, posY, COLOR_TITLE, '>');
if (states[i]) Draw_DrawCharacter(36, posY, COLOR_LIME, 'x');
}
// Draw hint
if (hints[cursor])
Draw_DrawString(10, 200, COLOR_TITLE, hints[cursor]);
}
// Wait for input
u32 pressed = waitInput();
if (pressed & KEY_A)
states[cursor] = !states[cursor];
if (pressed & KEY_B)
break;
if (pressed & KEY_DOWN)
if (++cursor >= nbItems)
cursor = 0;
if (pressed & KEY_UP)
if (--cursor >= nbItems)
cursor = nbItems - 1;
} while (true);
Draw_Unlock();
menuLeave();
}

View File

@@ -35,6 +35,7 @@
#include "menus/miscellaneous.h"
#include "menus/sysconfig.h"
#include "menus/screen_filters.h"
#include "plgloader.h"
#include "ifile.h"
#include "memory.h"
#include "fmt.h"
@@ -46,6 +47,7 @@ Menu rosalinaMenu = {
{
{ "Take screenshot", METHOD, .method = &RosalinaMenu_TakeScreenshot },
{ "Change screen brightness", METHOD, .method = &RosalinaMenu_ChangeScreenBrightness },
{ "", METHOD, .method = PluginLoader__MenuCallback},
{ "Cheats...", METHOD, .method = &RosalinaMenu_Cheats },
{ "Process list", METHOD, .method = &RosalinaMenu_ProcessList },
{ "Debugger options...", MENU, .menu = &debuggerMenu },
@@ -112,6 +114,7 @@ void RosalinaMenu_ShowCredits(void)
u32 posY = Draw_DrawString(10, 30, COLOR_WHITE, "Luma3DS (c) 2016-2020 AuroraWright, TuxSH") + SPACING_Y;
posY = Draw_DrawString(10, posY + SPACING_Y, COLOR_WHITE, "3gx implementation update by mind_overflow");
posY = Draw_DrawString(10, posY + SPACING_Y, COLOR_WHITE, "3DSX loading code by fincs");
posY = Draw_DrawString(10, posY + SPACING_Y, COLOR_WHITE, "Networking code & basic GDB functionality by Stary");
posY = Draw_DrawString(10, posY + SPACING_Y, COLOR_WHITE, "InputRedirection by Stary (PoC by ShinyQuagsire)");

View File

@@ -31,6 +31,7 @@
#include "memory.h"
#include "draw.h"
#include "hbloader.h"
#include "plgloader.h"
#include "fmt.h"
#include "utils.h" // for makeArmBranch
#include "minisoc.h"
@@ -164,7 +165,7 @@ void MiscellaneousMenu_ChangeMenuCombo(void)
while(!(waitInput() & KEY_B) && !menuShouldExit);
}
void MiscellaneousMenu_SaveSettings(void)
Result SaveSettings(void)
{
Result res;
@@ -179,12 +180,14 @@ void MiscellaneousMenu_SaveSettings(void)
u32 config, multiConfig, bootConfig;
u64 hbldr3dsxTitleId;
u32 rosalinaMenuCombo;
u32 rosalinaFlags;
} configData;
u32 formatVersion;
u32 config, multiConfig, bootConfig;
s64 out;
bool isSdMode;
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 2))) svcBreak(USERBREAK_ASSERT);
formatVersion = (u32)out;
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 3))) svcBreak(USERBREAK_ASSERT);
@@ -204,6 +207,7 @@ void MiscellaneousMenu_SaveSettings(void)
configData.bootConfig = bootConfig;
configData.hbldr3dsxTitleId = Luma_SharedConfig->hbldr_3dsx_tid;
configData.rosalinaMenuCombo = menuCombo;
configData.rosalinaFlags = PluginLoader__IsEnabled();
FS_ArchiveID archiveId = isSdMode ? ARCHIVE_SDMC : ARCHIVE_NAND_RW;
res = IFile_Open(&file, archiveId, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, "/luma/config.bin"), FS_OPEN_CREATE | FS_OPEN_WRITE);
@@ -211,6 +215,14 @@ void MiscellaneousMenu_SaveSettings(void)
if(R_SUCCEEDED(res))
res = IFile_Write(&file, &total, &configData, sizeof(configData), 0);
IFile_Close(&file);
return res;
}
void MiscellaneousMenu_SaveSettings(void)
{
Result res = SaveSettings();
Draw_Lock();
Draw_ClearFramebuffer();
Draw_FlushFramebuffer();

View File

@@ -233,8 +233,8 @@ static void ProcessListMenu_MemoryViewer(const ProcessInfo *info)
svcQueryProcessMemory(&mem, &out, processHandle, heapStartAddress);
heapTotalSize = mem.size;
Result codeRes = svcMapProcessMemoryEx(processHandle, codeDestAddress, codeStartAddress, codeTotalSize);
Result heapRes = svcMapProcessMemoryEx(processHandle, heapDestAddress, heapStartAddress, heapTotalSize);
Result codeRes = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, codeDestAddress, processHandle, codeStartAddress, codeTotalSize);
Result heapRes = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, heapDestAddress, processHandle, heapStartAddress, heapTotalSize);
bool codeAvailable = R_SUCCEEDED(codeRes);
bool heapAvailable = R_SUCCEEDED(heapRes);
@@ -575,9 +575,9 @@ static void ProcessListMenu_MemoryViewer(const ProcessInfo *info)
}
if(codeAvailable)
svcUnmapProcessMemoryEx(processHandle, codeDestAddress, codeTotalSize);
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, codeDestAddress, codeTotalSize);
if(heapAvailable)
svcUnmapProcessMemoryEx(processHandle, heapDestAddress, heapTotalSize);
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, heapDestAddress, heapTotalSize);
svcCloseHandle(processHandle);
}

View File

@@ -0,0 +1,143 @@
#include <3ds.h>
#include "plgldr.h"
#include <string.h>
static Handle plgLdrHandle;
static int plgLdrRefCount;
Result plgLdrInit(void)
{
Result res = 0;
if (AtomicPostIncrement(&plgLdrRefCount) == 0)
res = svcConnectToPort(&plgLdrHandle, "plg:ldr");
if (R_FAILED(res))
AtomicDecrement(&plgLdrRefCount);
return res;
}
void plgLdrExit(void)
{
if (AtomicDecrement(&plgLdrRefCount))
return;
svcCloseHandle(plgLdrHandle);
}
Result PLGLDR__IsPluginLoaderEnabled(bool *isEnabled)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(2, 0, 0);
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
*isEnabled = cmdbuf[2];
}
return res;
}
Result PLGLDR__SetPluginLoaderState(bool enabled)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(3, 1, 0);
cmdbuf[1] = (u32)enabled;
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}
Result PLGLDR__SetPluginLoadParameters(PluginLoadParameters *parameters)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(4, 2, 4);
cmdbuf[1] = (u32)parameters->noFlash;
cmdbuf[2] = parameters->lowTitleId;
cmdbuf[3] = IPC_Desc_Buffer(256, IPC_BUFFER_R);
cmdbuf[4] = (u32)parameters->path;
cmdbuf[5] = IPC_Desc_Buffer(32 * sizeof(u32), IPC_BUFFER_R);
cmdbuf[6] = (u32)parameters->config;
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}
Result PLGLDR__DisplayMenu(PluginMenu *menu)
{
Result res = 0;
u32 nbItems = menu->nbItems;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(5, 1, 8);
cmdbuf[1] = nbItems;
cmdbuf[2] = IPC_Desc_Buffer(nbItems, IPC_BUFFER_RW);
cmdbuf[3] = (u32)menu->states;
cmdbuf[4] = IPC_Desc_Buffer(MAX_BUFFER, IPC_BUFFER_R);
cmdbuf[5] = (u32)menu->title;
cmdbuf[6] = IPC_Desc_Buffer(MAX_BUFFER * nbItems, IPC_BUFFER_R);
cmdbuf[7] = (u32)menu->items;
cmdbuf[8] = IPC_Desc_Buffer(MAX_BUFFER * nbItems, IPC_BUFFER_R);
cmdbuf[9] = (u32)menu->hints;
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}
Result PLGLDR__DisplayMessage(const char *title, const char *body)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(6, 0, 4);
cmdbuf[1] = IPC_Desc_Buffer(strlen(title), IPC_BUFFER_R);
cmdbuf[2] = (u32)title;
cmdbuf[3] = IPC_Desc_Buffer(strlen(body), IPC_BUFFER_R);
cmdbuf[4] = (u32)body;
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}
Result PLGLDR__DisplayErrMessage(const char *title, const char *body, u32 error)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(7, 1, 4);
cmdbuf[1] = error;
cmdbuf[2] = IPC_Desc_Buffer(strlen(title), IPC_BUFFER_R);
cmdbuf[3] = (u32)title;
cmdbuf[4] = IPC_Desc_Buffer(strlen(body), IPC_BUFFER_R);
cmdbuf[5] = (u32)body;
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}

View File

@@ -0,0 +1,599 @@
#include <3ds.h>
#include "3gx.h"
#include "ifile.h"
#include "utils.h" // for makeARMBranch
#include "plgloader.h"
#include "fmt.h"
#include "menu.h"
#include "menus.h"
#include "memory.h"
#include "sleep.h"
#define MEMPERM_RW (MEMPERM_READ | MEMPERM_WRITE)
#define MemBlockSize (5*1024*1024) /* 5 MiB */
typedef struct
{
Result code;
const char * message;
} Error;
typedef struct
{
bool isEnabled;
bool noFlash;
u32 titleid;
char path[256];
u32 config[32];
} PluginLoadParameters;
#define HeaderMagic (0x24584733) /* "3GX$" */
typedef struct
{
u32 magic;
u32 version;
u32 heapVA;
u32 heapSize;
u32 pluginSize;
const char* pluginPathPA;
u32 isDefaultPlugin;
u32 reserved[25];
u32 config[32];
} PluginHeader;
static bool g_isEnabled;
static u8 * g_memBlock;
static char g_path[256];
static Handle g_process = 0;
static u32 g_codeSize;
static u32 g_heapSize;
static Error g_error;
static PluginLoadParameters g_userDefinedLoadParameters;
static MyThread g_pluginLoaderThread;
static u8 ALIGN(8) g_pluginLoaderThreadStack[0x4000];
// pluginLoader.s
void gamePatchFunc(void);
void PluginLoader__ThreadMain(void);
MyThread * PluginLoader__CreateThread(void)
{
s64 out;
svcGetSystemInfo(&out, 0x10000, 0x102);
g_isEnabled = out & 1;
g_userDefinedLoadParameters.isEnabled = false;
if(R_FAILED(MyThread_Create(&g_pluginLoaderThread, PluginLoader__ThreadMain, g_pluginLoaderThreadStack, 0x4000, 20, CORE_SYSTEM)))
svcBreak(USERBREAK_PANIC);
return &g_pluginLoaderThread;
}
bool PluginLoader__IsEnabled(void)
{
return g_isEnabled;
}
void PluginLoader__MenuCallback(void)
{
g_isEnabled = !g_isEnabled;
SaveSettings();
PluginLoader__UpdateMenu();
}
void PluginLoader__UpdateMenu(void)
{
static const char *status[2] =
{
"Plugin Loader: [Disabled]",
"Plugin Loader: [Enabled]"
};
rosalinaMenu.items[isN3DS + 1].title = status[g_isEnabled];
}
static Result MapPluginInProcess(Handle proc, u32 size)
{
u32 heapsize = MemBlockSize - size;
Result res;
PluginHeader *header = (PluginHeader *)g_memBlock;
header->heapVA = 0x06000000;
g_heapSize = header->heapSize = heapsize;
g_codeSize = size;
// From now on, all memory page mapped to the process should be rwx
svcControlProcess(proc, PROCESSOP_SET_MMU_TO_RWX, 0, 0);
// Plugin
if (R_FAILED((res = svcMapProcessMemoryEx(proc, 0x07000000, CUR_PROCESS_HANDLE, (u32)g_memBlock, size))))
{
g_error.message = "Couldn't map plugin memory block";
g_error.code = res;
return res;
}
// Heap (to be used by the plugin)
if (R_FAILED((res = svcMapProcessMemoryEx(proc, 0x06000000, CUR_PROCESS_HANDLE, (u32)g_memBlock + size, heapsize))))
{
g_error.message = "Couldn't map heap memory block";
g_error.code = res;
goto exit;
}
// Clear heap section
memset32(g_memBlock + size, 0, heapsize);
exit:
return res;
}
static u32 strlen16(const u16 *str)
{
u32 size = 0;
while (str && *str++) ++size;
return size;
}
static Result FindPluginFile(u64 tid)
{
char filename[256];
u32 entriesNb = 0;
bool found = false;
Handle dir = 0;
Result res;
FS_Archive sdmcArchive = 0;
FS_DirectoryEntry entries[10];
sprintf(g_path, "/luma/plugins/%016llX", tid);
if (R_FAILED((res =FSUSER_OpenArchive(&sdmcArchive, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")))))
goto exit;
if (R_FAILED((res = FSUSER_OpenDirectory(&dir, sdmcArchive, fsMakePath(PATH_ASCII, g_path)))))
goto exit;
strcat(g_path, "/");
while (!found && R_SUCCEEDED(FSDIR_Read(dir, &entriesNb, 10, entries)))
{
if (entriesNb == 0)
break;
static const u16 * validExtension = u"3gx";
for (u32 i = 0; i < entriesNb; ++i)
{
FS_DirectoryEntry *entry = &entries[i];
// If entry is a folder, skip it
if (entry->attributes & 1)
continue;
// Check extension
u32 size = strlen16(entry->name);
if (size <= 5)
continue;
u16 *fileExt = entry->name + size - 3;
if (memcmp(fileExt, validExtension, 3 * sizeof(u16)))
continue;
// Convert name from utf16 to utf8
int units = utf16_to_utf8((u8 *)filename, entry->name, 100);
if (units == -1)
continue;
filename[units] = 0;
found = true;
break;
}
}
if (!found)
res = MAKERESULT(28, 4, 0, 1018);
else
{
u32 len = strlen(g_path);
filename[256 - len] = 0;
strcat(g_path, filename);
}
((PluginHeader *)g_memBlock)->pluginPathPA = PA_FROM_VA_PTR(g_path);
exit:
FSDIR_Close(dir);
FSUSER_CloseArchive(sdmcArchive);
return res;
}
static Result OpenFile(IFile *file, const char *path)
{
return IFile_Open(file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, path), FS_OPEN_READ);
}
static Result CheckPluginCompatibility(_3gx_Header *header, u32 processTitle)
{
static char errorBuf[0x100];
if (header->targets.count == 0)
return 0;
for (u32 i = 0; i < header->targets.count; ++i)
{
if (header->targets.titles[i] == processTitle)
return 0;
}
sprintf(errorBuf, "The plugin - %s -\nis not compatible with this game.\n" \
"Contact \"%s\" for more infos.", header->infos.titleMsg, header->infos.authorMsg);
g_error.message = errorBuf;
return -1;
}
static bool TryToLoadPlugin(Handle process)
{
u64 fileSize;
u64 tid;
u32 procStart = 0x00100000;
IFile plugin;
PluginHeader *hdr = (PluginHeader *)g_memBlock;
_3gx_Header *header;
Result res;
// Clear the memblock
memset32(g_memBlock, 0, MemBlockSize);
hdr->magic = HeaderMagic;
// Get title id
svcGetProcessInfo((s64 *)&tid, process, 0x10001);
if (R_FAILED((res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, procStart, process, procStart, 0x1000))))
{
g_error.message = "Couldn't map process";
g_error.code = res;
return false;
}
// Try to open plugin file
if (g_userDefinedLoadParameters.isEnabled && (u32)tid == g_userDefinedLoadParameters.titleid)
{
g_userDefinedLoadParameters.isEnabled = false;
if (OpenFile(&plugin, g_userDefinedLoadParameters.path))
goto exitFail;
hdr->pluginPathPA = PA_FROM_VA_PTR(g_userDefinedLoadParameters.path);
memcpy(hdr->config, g_userDefinedLoadParameters.config, 32 * sizeof(u32));
}
else
{
if (R_FAILED(FindPluginFile(tid)) || OpenFile(&plugin, g_path))
{
// Try to open default plugin
const char *defaultPath = "/luma/plugins/default.3gx";
if (OpenFile(&plugin, defaultPath))
goto exitFail;
hdr->isDefaultPlugin = 1;
hdr->pluginPathPA = PA_FROM_VA_PTR(defaultPath);
}
}
if (R_FAILED((res = IFile_GetSize(&plugin, &fileSize))))
g_error.message = "Couldn't get file size";
// Plugins will rarely exceed 1MB so this is fine
header = (_3gx_Header *)(g_memBlock + MemBlockSize - (u32)fileSize);
// Read header
if (!res && R_FAILED((res = Read_3gx_Header(&plugin, header))))
g_error.message = "Couldn't read file";
// Check titles compatibility
if (!res) res = CheckPluginCompatibility(header, (u32)tid);
// Read code
if (!res && R_FAILED(res = Read_3gx_Code(&plugin, header, g_memBlock + sizeof(PluginHeader))))
g_error.message = "Couldn't read plugin's code";
// Close file
IFile_Close(&plugin);
if (R_FAILED(res))
{
g_error.code = res;
goto exitFail;
}
hdr->version = header->version;
// Code size must be page aligned
fileSize = (header->codeSize + 0x1100) & ~0xFFF;
if (MapPluginInProcess(process, fileSize) == 0)
// Install hook
{
extern u32 g_savedGameInstr[2];
u32 *game = (u32 *)procStart;
g_savedGameInstr[0] = game[0];
g_savedGameInstr[1] = game[1];
game[0] = 0xE51FF004; // ldr pc, [pc, #-4]
game[1] = (u32)PA_FROM_VA_PTR(gamePatchFunc);
}
else
goto exitFail;
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, procStart, 0x1000);
return true;
exitFail:
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, procStart, 0x1000);
return false;
}
static void SetKernelConfigurationMemoryFlag(bool loaded)
{
u32 *flag = (u32 *)PA_FROM_VA_PTR(0x1FF800F0);
*flag = loaded;
}
static void PluginLoader_HandleCommands(void)
{
u32 *cmdbuf = getThreadCommandBuffer();
switch (cmdbuf[0] >> 16)
{
case 1: // Load plugin
{
if (cmdbuf[0] != IPC_MakeHeader(1, 0, 2))
{
error(cmdbuf, 0xD9001830);
break;
}
Handle process = cmdbuf[2];
if (g_isEnabled && TryToLoadPlugin(process))
{
if (!g_userDefinedLoadParameters.isEnabled && g_userDefinedLoadParameters.noFlash)
g_userDefinedLoadParameters.noFlash = false;
else
{
for (u32 i = 0; i < 64; i++)
{
REG32(0x10202204) = 0x01FF9933;
svcSleepThread(5000000);
}
REG32(0x10202204) = 0;
}
g_process = process;
}
else
svcCloseHandle(process);
cmdbuf[0] = IPC_MakeHeader(1, 1, 0);
cmdbuf[1] = 0;
break;
}
case 2: // Check if plugin loader is enabled
{
if (cmdbuf[0] != IPC_MakeHeader(2, 0, 0))
{
error(cmdbuf, 0xD9001830);
break;
}
cmdbuf[0] = IPC_MakeHeader(2, 2, 0);
cmdbuf[1] = 0;
cmdbuf[2] = (u32)g_isEnabled;
break;
}
case 3: // Enable / Disable plugin loader
{
if (cmdbuf[0] != IPC_MakeHeader(3, 1, 0))
{
error(cmdbuf, 0xD9001830);
break;
}
if (cmdbuf[1] != g_isEnabled)
{
g_isEnabled = cmdbuf[1];
SaveSettings();
}
cmdbuf[0] = IPC_MakeHeader(3, 1, 0);
cmdbuf[1] = 0;
break;
}
case 4: // Define next plugin load settings
{
if (cmdbuf[0] != IPC_MakeHeader(4, 2, 4))
{
error(cmdbuf, 0xD9001830);
break;
}
g_userDefinedLoadParameters.isEnabled = true;
g_userDefinedLoadParameters.noFlash = cmdbuf[1];
g_userDefinedLoadParameters.titleid = cmdbuf[2];
strncpy(g_userDefinedLoadParameters.path, (const char *)cmdbuf[4], 255);
memcpy(g_userDefinedLoadParameters.config, (void *)cmdbuf[6], 32 * sizeof(u32));
cmdbuf[0] = IPC_MakeHeader(4, 1, 0);
cmdbuf[1] = 0;
break;
}
case 5: // Display menu
{
if (cmdbuf[0] != IPC_MakeHeader(5, 1, 8))
{
error(cmdbuf, 0xD9001830);
break;
}
u32 nbItems = cmdbuf[1];
u32 states = cmdbuf[3];
DisplayPluginMenu(cmdbuf);
cmdbuf[0] = IPC_MakeHeader(5, 1, 2);
cmdbuf[1] = 0;
cmdbuf[2] = IPC_Desc_Buffer(nbItems, IPC_BUFFER_RW);
cmdbuf[3] = states;
break;
}
case 6: // Display message
{
if (cmdbuf[0] != IPC_MakeHeader(6, 0, 4))
{
error(cmdbuf, 0xD9001830);
break;
}
const char *title = (const char *)cmdbuf[2];
const char *body = (const char *)cmdbuf[4];
DispMessage(title, body);
cmdbuf[0] = IPC_MakeHeader(6, 1, 0);
cmdbuf[1] = 0;
break;
}
case 7: // Display error message
{
if (cmdbuf[0] != IPC_MakeHeader(7, 1, 4))
{
error(cmdbuf, 0xD9001830);
break;
}
const char *title = (const char *)cmdbuf[3];
const char *body = (const char *)cmdbuf[5];
DispErrMessage(title, body, cmdbuf[1]);
cmdbuf[0] = IPC_MakeHeader(7, 1, 0);
cmdbuf[1] = 0;
break;
}
}
}
void PluginLoader__ThreadMain(void)
{
const char *title = "Plugin loader";
Result res = 0;
MemOp memRegion = isN3DS ? MEMOP_REGION_BASE : MEMOP_REGION_SYSTEM;
Handle handles[3];
Handle serverHandle, clientHandle, sessionHandle = 0;
u32 *cmdbuf = getThreadCommandBuffer();
u32 replyTarget = 0;
u32 nbHandle;
s32 index;
// Wait for the system to be completely started
{
bool isAcuRegistered = false;
while (true)
{
if (R_SUCCEEDED(srvIsServiceRegistered(&isAcuRegistered, "ac:u"))
&& isAcuRegistered)
break;
svcSleepThread(100000);
}
}
// Init memory block to hold the plugin
{
u32 free = (u32)osGetMemRegionFree(memRegion);
u32 temp = 0;
svcControlMemoryEx(&temp, 0x00100000, 0, free - MemBlockSize, memRegion | MEMOP_ALLOC, MEMPERM_RW, true);
if (R_FAILED((res = svcControlMemoryEx((u32 *)&g_memBlock, 0x07000000, 0, MemBlockSize, memRegion | MEMOP_ALLOC, MEMPERM_RW, true))))
{
svcSleepThread(5000000000ULL); ///< Wait until the system started to display the error
DispErrMessage(title, "Couldn't allocate memblock", res);
return;
}
svcControlMemoryEx(&temp, (u32)temp, 0, MemBlockSize, memRegion | MEMOP_FREE, 0, true);
}
assertSuccess(svcCreatePort(&serverHandle, &clientHandle, "plg:ldr", 1));
do
{
g_error.message = NULL;
g_error.code = 0;
handles[0] = serverHandle;
handles[1] = sessionHandle == 0 ? g_process : sessionHandle;
handles[2] = g_process;
if(replyTarget == 0) // k11
cmdbuf[0] = 0xFFFF0000;
nbHandle = 1 + (sessionHandle != 0) + (g_process != 0);
res = svcReplyAndReceive(&index, handles, nbHandle, replyTarget);
if(R_FAILED(res))
{
if((u32)res == 0xC920181A) // session closed by remote
{
svcCloseHandle(sessionHandle);
sessionHandle = 0;
replyTarget = 0;
}
else
svcBreak(USERBREAK_PANIC);
}
else
{
if(index == 0)
{
Handle session;
assertSuccess(svcAcceptSession(&session, serverHandle));
if(sessionHandle == 0)
sessionHandle = session;
else
svcCloseHandle(session);
}
else if (index == 1 && handles[1] == sessionHandle)
{
PluginLoader_HandleCommands();
replyTarget = sessionHandle;
}
else ///< The process in which we injected the plugin is terminating
{
// Unmap plugin's memory before closing the process
svcUnmapProcessMemoryEx(g_process, 0x07000000, g_codeSize);
svcUnmapProcessMemoryEx(g_process, 0x06000000, g_heapSize);
svcCloseHandle(g_process);
g_process = 0;
}
}
if (g_error.message != NULL)
DispErrMessage(title, g_error.message, g_error.code);
SetKernelConfigurationMemoryFlag(g_process != 0);
} while(!preTerminationRequested);
svcCloseHandle(sessionHandle);
svcCloseHandle(clientHandle);
svcCloseHandle(serverHandle);
svcControlMemoryEx((u32 *)&g_memBlock, (u32)g_memBlock, 0, MemBlockSize, memRegion | MEMOP_FREE, 0, true);
}

View File

@@ -0,0 +1,33 @@
.section .data
.balign 4
.arm
.global gamePatchFunc
.type gamePatchFunc, %function
gamePatchFunc:
stmfd sp!, {r0-r12, lr}
mrs r0, cpsr
stmfd sp!, {r0}
adr r0, g_savedGameInstr
ldr r1, =0x00100000
ldr r2, [r0]
str r2, [r1]
ldr r2, [r0, #4]
str r2, [r1, #4]
svc 0x92
svc 0x94
startplugin:
ldr r5, =0x07000100
blx r5
exit:
ldmfd sp!, {r0}
msr cpsr, r0
ldmfd sp!, {r0-r12, lr}
ldr lr, =0x00100000
mov pc, lr
.global g_savedGameInstr
g_savedGameInstr:
.word 0, 0

View File

@@ -68,11 +68,11 @@ Result PatchProcessByName(const char *name, Result (*func)(u32 size))
s64 textTotalRoundedSize = 0, startAddress = 0;
svcGetProcessInfo(&textTotalRoundedSize, processHandle, 0x10002); // only patch .text
svcGetProcessInfo(&startAddress, processHandle, 0x10005);
if(R_FAILED(res = svcMapProcessMemoryEx(processHandle, 0x00100000, (u32) startAddress, textTotalRoundedSize)))
if(R_FAILED(res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, processHandle, (u32) startAddress, textTotalRoundedSize)))
return res;
res = func(textTotalRoundedSize);
svcUnmapProcessMemoryEx(processHandle, 0x00100000, textTotalRoundedSize);
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, textTotalRoundedSize);
return res;
}

View File

@@ -0,0 +1,61 @@
/*
* This file is part of Luma3DS
* Copyright (C) 2016-2018 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 <http://www.gnu.org/licenses/>.
*
* 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 <3ds.h>
static bool g_isSleeping = false;
static LightEvent g_onWakeUpEvent;
void Sleep__Init(void)
{
srvSubscribe(0x214); ///< Sleep entry
srvSubscribe(0x213); ///< Sleep exit
LightEvent_Init(&g_onWakeUpEvent, RESET_STICKY);
}
void Sleep__HandleNotification(u32 notifId)
{
if (notifId == 0x214) ///< Sleep entry
{
LightEvent_Clear(&g_onWakeUpEvent);
g_isSleeping = true;
}
else if (notifId == 0x213) ///< Sleep exit
{
g_isSleeping = false;
LightEvent_Signal(&g_onWakeUpEvent);
}
}
bool Sleep__Status(void)
{
if (g_isSleeping)
{
LightEvent_Wait(&g_onWakeUpEvent);
return true;
}
return false;
}

View File

@@ -9,10 +9,12 @@
#include <3ds/result.h>
#include <3ds/svc.h>
#include <3ds/synchronization.h>
#include <3ds/services/ac.h>
#include <arpa/inet.h>
#include "memory.h"
#include "minisoc.h"
#include "sock_util.h"
#include "sleep.h"
extern Handle preTerminationEvent;
extern bool preTerminationRequested;
@@ -185,6 +187,14 @@ void server_run(struct sock_server *serv)
for(nfds_t i = 0; i < serv->nfds; i++)
fds[i].revents = 0;
if (Sleep__Status())
{
while (!Wifi__IsConnected()
&& serv->running && !preTerminationRequested)
svcSleepThread(1000000000ULL);
}
int pollres = socPoll(fds, serv->nfds, 50);
if(server_should_exit(serv) || pollres < -10000)
@@ -321,3 +331,13 @@ void server_finalize(struct sock_server *serv)
svcClearEvent(serv->started_event);
svcCloseHandle(serv->started_event);
}
bool Wifi__IsConnected(void)
{
u32 status = 0;
u32 wifistatus = 0;
acInit();
return R_SUCCEEDED(ACU_GetWifiStatus(&wifistatus)) && wifistatus > 0
&& R_SUCCEEDED(ACU_GetStatus(&status)) && status != 1;
}

View File

@@ -31,6 +31,14 @@ static bool doPublishNotification(ProcessData *processData, u32 notificationId,
}
}
// Handle special case for home button notifications on Mode3 O3DS with plugin loaded
if ((notificationId == 0x204 || notificationId == 0x205)
&& *(u32 *)0x1FF80030 == 3 && *(u32 *)0x1FF800F0)
{
svcKernelSetState(0x10007, 1);
return true;
}
if(processData->nbPendingNotifications < 0x10)
{
s32 count;