Merge 3GX plugin loader fork (#1916)

This commit adds all the changes made to the 3GX plugin loader fork of Luma3DS. The most important features are:

- Add 3GX plugin loader support. New service added to rosalina: plg:ldr
- Add svcControlProcess, svcControlMemoryUnsafe and improve svcMapProcessMemoryEx (breaking change)
- Allow applications to override certain configurations depending on their needs:
    - Disable core2 thread redirection
    - Disable game patching for the next app
    - Force New 3DS speedup
    - Force next application in a specific memory mode
    - Block the opening of the Rosalina menu
- Add GDB commands to list all process handles and catch all SVC (latter is for IDA Pro as gdb client supports it)
- Other changes necessary for plugins to work properly. Please check changed files in this PR for more details. 

---------

Co-authored-by: PabloMK7 <hackyglitch@gmail.com>
Co-authored-by: Nanquitas <nath.doidi@gmail.com>
Co-authored-by: TuxSH <1922548+TuxSH@users.noreply.github.com>
This commit is contained in:
PabloMK7 2023-07-14 20:08:07 +02:00 committed by GitHub
parent b20c79eda4
commit c055fb6f5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
73 changed files with 4340 additions and 95 deletions

Binary file not shown.

View File

@ -460,6 +460,11 @@ static int configIniHandler(void* user, const char* section, const char* name, c
CHECK_PARSE_OPTION(parseKeyComboOption(&opt, value)); CHECK_PARSE_OPTION(parseKeyComboOption(&opt, value));
cfg->rosalinaMenuCombo = opt; cfg->rosalinaMenuCombo = opt;
return 1; return 1;
} else if (strcmp(name, "plugin_loader_enabled") == 0) {
bool opt;
CHECK_PARSE_OPTION(parseBoolOption(&opt, value));
cfg->pluginLoaderFlags = opt ? cfg->pluginLoaderFlags | 1 : cfg->pluginLoaderFlags & ~1;
return 1;
} else if (strcmp(name, "ntp_tz_offset_min") == 0) { } else if (strcmp(name, "ntp_tz_offset_min") == 0) {
s64 opt; s64 opt;
CHECK_PARSE_OPTION(parseDecIntOption(&opt, value, -779, 899)); CHECK_PARSE_OPTION(parseDecIntOption(&opt, value, -779, 899));
@ -650,7 +655,7 @@ static size_t saveLumaIniConfigToStr(char *out)
pinNumDigits, n3dsCpuStr, pinNumDigits, n3dsCpuStr,
autobootModeStr, autobootModeStr,
cfg->hbldr3dsxTitleId, rosalinaMenuComboStr, cfg->hbldr3dsxTitleId, rosalinaMenuComboStr, (int)(cfg->pluginLoaderFlags & 1),
(int)cfg->ntpTzOffetMinutes, (int)cfg->ntpTzOffetMinutes,
(int)cfg->topScreenFilter.cct, (int)cfg->bottomScreenFilter.cct, (int)cfg->topScreenFilter.cct, (int)cfg->bottomScreenFilter.cct,

View File

@ -36,7 +36,7 @@
#define CONFIG_FILE "config.ini" #define CONFIG_FILE "config.ini"
#define CONFIG_VERSIONMAJOR 3 #define CONFIG_VERSIONMAJOR 3
#define CONFIG_VERSIONMINOR 8 #define CONFIG_VERSIONMINOR 9
#define BOOTCFG_NAND BOOTCONFIG(0, 1) #define BOOTCFG_NAND BOOTCONFIG(0, 1)
#define BOOTCFG_EMUINDEX BOOTCONFIG(1, 3) #define BOOTCFG_EMUINDEX BOOTCONFIG(1, 3)

View File

@ -134,6 +134,7 @@ u32 installK11Extension(u8 *pos, u32 size, bool needToInitSd, u32 baseK11VA, u32
u32 splashDurationMsec; u32 splashDurationMsec;
u64 hbldr3dsxTitleId; u64 hbldr3dsxTitleId;
u32 rosalinaMenuCombo; u32 rosalinaMenuCombo;
u32 pluginLoaderFlags;
s16 ntpTzOffetMinutes; s16 ntpTzOffetMinutes;
ScreenFiltersCfgData topScreenFilter; ScreenFiltersCfgData topScreenFilter;
@ -216,6 +217,7 @@ u32 installK11Extension(u8 *pos, u32 size, bool needToInitSd, u32 baseK11VA, u32
info->splashDurationMsec = configData.splashDurationMsec; info->splashDurationMsec = configData.splashDurationMsec;
info->hbldr3dsxTitleId = configData.hbldr3dsxTitleId; info->hbldr3dsxTitleId = configData.hbldr3dsxTitleId;
info->rosalinaMenuCombo = configData.rosalinaMenuCombo; info->rosalinaMenuCombo = configData.rosalinaMenuCombo;
info->pluginLoaderFlags = configData.pluginLoaderFlags;
info->ntpTzOffetMinutes = configData.ntpTzOffetMinutes; info->ntpTzOffetMinutes = configData.ntpTzOffetMinutes;
info->topScreenFilter = configData.topScreenFilter; info->topScreenFilter = configData.topScreenFilter;
info->bottomScreenFilter = configData.bottomScreenFilter; info->bottomScreenFilter = configData.bottomScreenFilter;

View File

@ -77,6 +77,7 @@ typedef struct CfgData {
u64 hbldr3dsxTitleId; u64 hbldr3dsxTitleId;
u32 rosalinaMenuCombo; u32 rosalinaMenuCombo;
u32 pluginLoaderFlags;
s16 ntpTzOffetMinutes; s16 ntpTzOffetMinutes;
ScreenFiltersCfgData topScreenFilter; ScreenFiltersCfgData topScreenFilter;

View File

@ -31,6 +31,7 @@
extern KRecursiveLock *criticalSectionLock; extern KRecursiveLock *criticalSectionLock;
extern KObjectList *threadList; extern KObjectList *threadList;
extern KObjectList *resourceLimitList;
extern KObjectMutex *synchronizationMutex; extern KObjectMutex *synchronizationMutex;
extern void (*KRecursiveLock__Lock)(KRecursiveLock *this); extern void (*KRecursiveLock__Lock)(KRecursiveLock *this);
@ -44,18 +45,27 @@ extern KAutoObject * (*KProcessHandleTable__ToKAutoObject)(KProcessHandleTable *
extern void (*KSynchronizationObject__Signal)(KSynchronizationObject *this, bool isPulse); extern void (*KSynchronizationObject__Signal)(KSynchronizationObject *this, bool isPulse);
extern Result (*WaitSynchronization1)(void *this_unused, KThread *thread, KSynchronizationObject *syncObject, s64 timeout); 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 (*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__MapProcessMemory)(KProcessHwInfo *this, KProcessHwInfo *other, void *dst, void *src, u32 nbPages);
extern Result (*KProcessHwInfo__UnmapProcessMemory)(KProcessHwInfo *this, void *addr, 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 Result (*KEvent__Clear)(KEvent *this);
extern Result (*KEvent__Signal)(KEvent *this);
extern void (*KObjectMutex__WaitAndAcquire)(KObjectMutex *this); extern void (*KObjectMutex__WaitAndAcquire)(KObjectMutex *this);
extern void (*KObjectMutex__ErrorOccured)(void); extern void (*KObjectMutex__ErrorOccured)(void);
extern void (*KScheduler__AdjustThread)(KScheduler *this, KThread *thread, u32 oldSchedulingMask); extern void (*KScheduler__AdjustThread)(KScheduler *this, KThread *thread, u32 oldSchedulingMask);
extern void (*KScheduler__AttemptSwitchingThreadContext)(KScheduler *this); 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 Result (*ControlMemory)(u32 *addrOut, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader);
extern Result (*doControlMemory)(KProcessHwInfo *this, u32 addr, u32 requestedNbPages, u32 pa, u32 state, u32 perm, u32 a7, u32 region);
extern Result (*CreateThread)(Handle *outThreadHandle, u32 ep, u32 arg, u32 stackTop, s32 priority, s32 processorId); extern Result (*CreateThread)(Handle *outThreadHandle, u32 ep, u32 arg, u32 stackTop, s32 priority, s32 processorId);
extern void (*SleepThread)(s64 ns); extern void (*SleepThread)(s64 ns);
extern Result (*CreateEvent)(Handle *out, ResetType resetType);
extern Result (*CloseHandle)(Handle handle); extern Result (*CloseHandle)(Handle handle);
extern Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type); extern Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type);
extern Result (*GetSystemInfo)(s64 *out, s32 type, s32 param); extern Result (*GetSystemInfo)(s64 *out, s32 type, s32 param);
@ -66,6 +76,7 @@ extern Result (*SendSyncRequest)(Handle handle);
extern Result (*OpenProcess)(Handle *out, u32 processId); extern Result (*OpenProcess)(Handle *out, u32 processId);
extern Result (*GetProcessId)(u32 *out, Handle process); extern Result (*GetProcessId)(u32 *out, Handle process);
extern Result (*DebugActiveProcess)(Handle *out, u32 processId); extern Result (*DebugActiveProcess)(Handle *out, u32 processId);
extern Result (*SignalEvent)(Handle event);
extern Result (*UnmapProcessMemory)(Handle processHandle, void *dst, u32 size); extern Result (*UnmapProcessMemory)(Handle processHandle, void *dst, u32 size);
extern Result (*KernelSetState)(u32 type, u32 varg1, u32 varg2, u32 varg3); extern Result (*KernelSetState)(u32 type, u32 varg1, u32 varg2, u32 varg3);
@ -98,6 +109,7 @@ extern bool *isDevUnit;
extern vu8 *configPage; extern vu8 *configPage;
extern u32 kernelVersion; extern u32 kernelVersion;
extern FcramLayout fcramLayout; extern FcramLayout fcramLayout;
extern FcramDescriptor *fcramDescriptor;
extern KCoreContext *coreCtxs; extern KCoreContext *coreCtxs;
@ -111,9 +123,10 @@ extern Result (*InterruptManager__MapInterrupt)(InterruptManager *manager, KBase
extern InterruptManager *interruptManager; extern InterruptManager *interruptManager;
extern KBaseInterruptEvent *customInterruptEvent; extern KBaseInterruptEvent *customInterruptEvent;
extern void (*initFPU)(void); extern void (*initFPU)(void);
extern void (*mcuReboot)(void); extern void (*mcuReboot)(void);
extern void (*coreBarrier)(void); extern void (*coreBarrier)(void);
extern void* (*kAlloc)(FcramDescriptor *fcramDesc, u32 nbPages, u32 alignment, u32 region);
typedef struct ScreenFiltersCfgData { typedef struct ScreenFiltersCfgData {
u16 cct; u16 cct;
@ -139,6 +152,7 @@ typedef struct CfwInfo
u32 splashDurationMsec; u32 splashDurationMsec;
u64 hbldr3dsxTitleId; u64 hbldr3dsxTitleId;
u32 rosalinaMenuCombo; u32 rosalinaMenuCombo;
u32 pluginLoaderFlags;
s16 ntpTzOffetMinutes; s16 ntpTzOffetMinutes;
ScreenFiltersCfgData topScreenFilter; ScreenFiltersCfgData topScreenFilter;
@ -153,6 +167,24 @@ typedef struct CfwInfo
extern CfwInfo cfwInfo; extern CfwInfo cfwInfo;
extern u32 kextBasePa; extern u32 kextBasePa;
extern u32 stolenSystemMemRegionSize; extern u32 stolenSystemMemRegionSize;
extern bool disableThreadRedirection;
extern vu32 rosalinaState; extern vu32 rosalinaState;
extern bool hasStartedRosalinaNetworkFuncsOnce; extern bool hasStartedRosalinaNetworkFuncsOnce;
extern KEvent* signalPluginEvent;
typedef enum
{
PLG_CFG_NONE = 0,
PLG_CFG_RUNNING = 1,
PLG_CFG_INHOME = 2,
PLG_CFG_EXITING = 3,
PLG_CFG_HOME_EVENT = 1 << 16,
PLG_CFG_EXIT_EVENT = 2 << 16
} PLG_CFG_STATUS;
void PLG_SignalEvent(u32 event);
void PLG__WakeAppThread(void);
u32 PLG_GetStatus(void);
KLinkedList* KLinkedList__Initialize(KLinkedList *list);

View File

@ -106,6 +106,14 @@ typedef struct ALIGN(4) KMutex
union KProcess *owner; union KProcess *owner;
} KMutex; } KMutex;
typedef struct KAddressArbiter
{
KAutoObject autoObject;
struct KThread *first;
struct KThread *last;
union KProcess *owner;
} KAddressArbiter;
/* 92 */ /* 92 */
typedef struct KMutexLinkedList typedef struct KMutexLinkedList
{ {
@ -113,6 +121,30 @@ typedef struct KMutexLinkedList
KMutex *last; KMutex *last;
} KMutexLinkedList; } 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 */ /* 45 */
typedef struct KClassToken typedef struct KClassToken
{ {
@ -489,6 +521,9 @@ typedef enum MemOp
MEMOP_REGION_SYSTEM = 0x200, MEMOP_REGION_SYSTEM = 0x200,
MEMOP_REGION_BASE = 0x300, MEMOP_REGION_BASE = 0x300,
MEMOP_LINEAR = 0x10000, MEMOP_LINEAR = 0x10000,
MEMOP_OP_MASK = 0xFF,
MEMOP_REGION_MASK = 0xF00,
} MemOp; } MemOp;
/* 17 */ /* 17 */
@ -541,6 +576,20 @@ typedef struct KBlockInfo
u32 pageCount; u32 pageCount;
} KBlockInfo; } 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 */ /* 25 */
typedef struct KMemoryBlock typedef struct KMemoryBlock
{ {
@ -1038,10 +1087,24 @@ typedef struct KProcess##sys\
KThread *mainThread;\ KThread *mainThread;\
u32 interruptEnabledFlags[4];\ u32 interruptEnabledFlags[4];\
KProcessHandleTable handleTable;\ KProcessHandleTable handleTable;\
u8 gap234[52];\ /* Custom fields for plugin system */ \
/* { */ \
u32 customFlags; /* see KProcess_CustomFlags enum below */ \
Handle onMemoryLayoutChangeEvent;\
/* } */ \
u8 gap234[44];\
u64 unused;\ u64 unused;\
} KProcess##sys; } KProcess##sys;
enum KProcess_CustomFlags
{
ForceRWXPages = 1 << 0,
SignalOnMemLayoutChanges = 1 << 1,
SignalOnExit = 1 << 2,
MemLayoutChanged = 1 << 16
};
INSTANCIATE_KPROCESS(N3DS); INSTANCIATE_KPROCESS(N3DS);
INSTANCIATE_KPROCESS(O3DS8x); INSTANCIATE_KPROCESS(O3DS8x);
INSTANCIATE_KPROCESS(O3DSPre8x); INSTANCIATE_KPROCESS(O3DSPre8x);
@ -1135,6 +1198,28 @@ typedef struct FcramLayout
u32 baseSize; u32 baseSize;
} FcramLayout; } FcramLayout;
typedef struct RegionDescriptor
{
void *firstMemoryBlock;
void *lastMemoryBlock;
void *regionStart;
u32 regionSizeInBytes;
} RegionDescriptor;
typedef struct FcramDescriptor
{
RegionDescriptor appRegion;
RegionDescriptor sysRegion;
RegionDescriptor baseRegion;
RegionDescriptor * regionDescsPtr;
u32 fcramStart;
u32 fcramSizeInPages;
u32 baseMemoryStart;
u32 kernelUsageInBytes;
u32 unknown;
KObjectMutex mutex;
} FcramDescriptor;
extern bool isN3DS; extern bool isN3DS;
extern void *officialSVCs[0x7E]; extern void *officialSVCs[0x7E];

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,34 @@
/*
* 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
#include "utils.h"
#include "kernel.h"
#include "svc.h"
Result ControlMemoryUnsafe(u32 *out, u32 addr0, u32 size, MemOp op, MemPerm perm);
Result ControlMemoryUnsafeWrapper(u32 *out, u32 addr0, u32 size, MemOp op, MemPerm perm);

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_SIGNAL_ON_EXIT,
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

@ -0,0 +1,6 @@
#include "utils.h"
#include "kernel.h"
#include "svc.h"
void ExitProcessHook(void);
void ExitProcessHookWrapper(void);

View File

@ -30,7 +30,7 @@
#include "kernel.h" #include "kernel.h"
#include "svc.h" #include "svc.h"
extern bool svcSignalingEnabled; extern u8 svcSignalingEnabled;
bool shouldSignalSyscallDebugEvent(KProcess *process, u8 svcId); bool shouldSignalSyscallDebugEvent(KProcess *process, u8 svcId);
Result KernelSetStateHook(u32 type, u32 varg1, u32 varg2, u32 varg3); Result KernelSetStateHook(u32 type, u32 varg1, u32 varg2, u32 varg3);

View File

@ -30,4 +30,5 @@
#include "kernel.h" #include "kernel.h"
#include "svc.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 "fatalExceptionHandlers.h"
#include "utils.h" #include "utils.h"
#include "kernel.h" #include "kernel.h"
#include "memory.h"
#include "mmu.h"
#include "globals.h" #include "globals.h"
#define REG_DUMP_SIZE 4 * 23 #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) 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; KProcess *currentProcess = currentCoreContext->objectContext.currentProcess;
if(thread != NULL && thread->threadLocalStorage != NULL && *((vu32 *)thread->threadLocalStorage + 0x10) != 0) if(thread != NULL && thread->threadLocalStorage != NULL && *((vu32 *)thread->threadLocalStorage + 0x10) != 0)
return false; return checkExceptionHandlerValidity(currentProcess, (vu32 *)thread->threadLocalStorage);
if(currentProcess != NULL) if(currentProcess != NULL)
{ {
@ -52,7 +85,7 @@ bool isExceptionFatal(u32 spsr, u32 *regs, u32 index)
thread = KPROCESS_GET_RVALUE(currentProcess, mainThread); thread = KPROCESS_GET_RVALUE(currentProcess, mainThread);
if(thread != NULL && thread->threadLocalStorage != NULL && *((vu32 *)thread->threadLocalStorage + 0x10) != 0) 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 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) 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) ((u32)safecpy <= addr && addr < (u32)safecpy + safecpy_sz)
); );
} }
void fatalExceptionHandlersMain(u32 *registerDump, u32 type, u32 cpuId) void fatalExceptionHandlersMain(u32 *registerDump, u32 type, u32 cpuId)
{ {
ExceptionDumpHeader dumpHeader; ExceptionDumpHeader dumpHeader;
@ -96,7 +130,7 @@ void fatalExceptionHandlersMain(u32 *registerDump, u32 type, u32 cpuId)
registerDump[15] = pc; registerDump[15] = pc;
//Dump code //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); dumpHeader.codeDumpSize = ((u32)instr & (((cpsr & 0x20) != 0) ? 1 : 3)) != 0 ? 0 : safecpy(codeDump, instr, dumpHeader.codeDumpSize);
//Copy register dump and code dump //Copy register dump and code dump

View File

@ -25,6 +25,8 @@
*/ */
#include "globals.h" #include "globals.h"
#include "utils.h"
#include "ipc.h"
KRecursiveLock *criticalSectionLock; KRecursiveLock *criticalSectionLock;
KObjectList *threadList; KObjectList *threadList;
@ -40,18 +42,29 @@ KAutoObject * (*KProcessHandleTable__ToKAutoObject)(KProcessHandleTable *this, H
void (*KSynchronizationObject__Signal)(KSynchronizationObject *this, bool isPulse); void (*KSynchronizationObject__Signal)(KSynchronizationObject *this, bool isPulse);
Result (*WaitSynchronization1)(void *this_unused, KThread *thread, KSynchronizationObject *syncObject, s64 timeout); Result (*WaitSynchronization1)(void *this_unused, KThread *thread, KSynchronizationObject *syncObject, s64 timeout);
Result (*KProcessHandleTable__CreateHandle)(KProcessHandleTable *this, Handle *out, KAutoObject *obj, u8 token); 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__MapProcessMemory)(KProcessHwInfo *this, KProcessHwInfo *other, void *dst, void *src, u32 nbPages);
Result (*KProcessHwInfo__UnmapProcessMemory)(KProcessHwInfo *this, void *addr, 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); Result (*KEvent__Clear)(KEvent *this);
Result (*KEvent__Signal)(KEvent *this);
void (*KObjectMutex__WaitAndAcquire)(KObjectMutex *this); void (*KObjectMutex__WaitAndAcquire)(KObjectMutex *this);
void (*KObjectMutex__ErrorOccured)(void); void (*KObjectMutex__ErrorOccured)(void);
void (*KScheduler__AdjustThread)(KScheduler *this, KThread *thread, u32 oldSchedulingMask); void (*KScheduler__AdjustThread)(KScheduler *this, KThread *thread, u32 oldSchedulingMask);
void (*KScheduler__AttemptSwitchingThreadContext)(KScheduler *this); 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); Result (*ControlMemory)(u32 *addrOut, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader);
Result (*doControlMemory)(KProcessHwInfo *this, u32 addr, u32 requestedNbPages, u32 pa, u32 state, u32 perm, u32 a7, u32 region);
Result (*CreateThread)(Handle *outThreadHandle, u32 ep, u32 arg, u32 stackTop, s32 priority, s32 processorId); Result (*CreateThread)(Handle *outThreadHandle, u32 ep, u32 arg, u32 stackTop, s32 priority, s32 processorId);
void (*SleepThread)(s64 ns); void (*SleepThread)(s64 ns);
Result (*CreateEvent)(Handle *out, ResetType resetType);
Result (*CloseHandle)(Handle handle); Result (*CloseHandle)(Handle handle);
Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type); Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type);
Result (*GetSystemInfo)(s64 *out, s32 type, s32 param); Result (*GetSystemInfo)(s64 *out, s32 type, s32 param);
@ -62,6 +75,7 @@ Result (*SendSyncRequest)(Handle handle);
Result (*OpenProcess)(Handle *out, u32 processId); Result (*OpenProcess)(Handle *out, u32 processId);
Result (*GetProcessId)(u32 *out, Handle process); Result (*GetProcessId)(u32 *out, Handle process);
Result (*DebugActiveProcess)(Handle *out, u32 processId); Result (*DebugActiveProcess)(Handle *out, u32 processId);
Result (*SignalEvent)(Handle event);
Result (*UnmapProcessMemory)(Handle processHandle, void *dst, u32 size); Result (*UnmapProcessMemory)(Handle processHandle, void *dst, u32 size);
Result (*KernelSetState)(u32 type, u32 varg1, u32 varg2, u32 varg3); Result (*KernelSetState)(u32 type, u32 varg1, u32 varg2, u32 varg3);
@ -94,6 +108,7 @@ bool *isDevUnit;
vu8 *configPage; vu8 *configPage;
u32 kernelVersion; u32 kernelVersion;
FcramLayout fcramLayout; FcramLayout fcramLayout;
FcramDescriptor *fcramDescriptor;
KCoreContext *coreCtxs; KCoreContext *coreCtxs;
void *originalHandlers[8] = {NULL}; void *originalHandlers[8] = {NULL};
@ -104,15 +119,67 @@ Result (*InterruptManager__MapInterrupt)(InterruptManager *manager, KBaseInterru
u32 coreID, u32 priority, bool disableUponReceipt, bool levelHighActive); u32 coreID, u32 priority, bool disableUponReceipt, bool levelHighActive);
InterruptManager *interruptManager; InterruptManager *interruptManager;
void (*initFPU)(void); void (*initFPU)(void);
void (*mcuReboot)(void); void (*mcuReboot)(void);
void (*coreBarrier)(void); void (*coreBarrier)(void);
void* (*kAlloc)(FcramDescriptor *fcramDesc, u32 nbPages, u32 alignment, u32 region);
CfwInfo cfwInfo; CfwInfo cfwInfo;
u32 kextBasePa; u32 kextBasePa;
u32 stolenSystemMemRegionSize; u32 stolenSystemMemRegionSize;
bool disableThreadRedirection = false;
vu32 rosalinaState; vu32 rosalinaState;
bool hasStartedRosalinaNetworkFuncsOnce; bool hasStartedRosalinaNetworkFuncsOnce;
KEvent* signalPluginEvent = NULL;
u32 pidOffsetKProcess, hwInfoOffsetKProcess, codeSetOffsetKProcess, handleTableOffsetKProcess, debugOffsetKProcess, flagsKProcess; u32 pidOffsetKProcess, hwInfoOffsetKProcess, codeSetOffsetKProcess, handleTableOffsetKProcess, debugOffsetKProcess, flagsKProcess;
KLinkedList* KLinkedList__Initialize(KLinkedList *list)
{
list->size = 0;
list->nodes.first = list->nodes.last = (KLinkedListNode *)&list->nodes;
return list;
}
void PLG_SignalEvent(u32 event)
{
KThread *currentThread = currentCoreContext->objectContext.currentThread;
// Set configuration memory field with event
*(vu32 *)PA_FROM_VA_PTR((u32 *)0x1FF800F0) |= event;
// Send notification 0x1001
{
u32 *cmdbuf = (u32 *)((u8 *)currentCoreContext->objectContext.currentThread->threadLocalStorage + 0x80);
u32 backup[3] = { cmdbuf[0], cmdbuf[1], cmdbuf[2] };
Handle srvHandle;
SessionInfo *info = SessionInfo_FindFirst("srv:");
Result res = createHandleForThisProcess(&srvHandle, &info->session->clientSession.syncObject.autoObject);
if (res >= 0)
{
cmdbuf[0] = 0x000C0080;
cmdbuf[1] = 0x1001;
cmdbuf[2] = 0;
SendSyncRequest(srvHandle);
CloseHandle(srvHandle);
}
cmdbuf[0] = backup[0]; cmdbuf[1] = backup[1]; cmdbuf[2] = backup[2];
}
// Wait for notification 0x1002
WaitSynchronization1(NULL, currentThread, (KSynchronizationObject *)signalPluginEvent, U64_MAX);
}
void PLG__WakeAppThread(void)
{
KEvent__Signal(signalPluginEvent);
}
u32 PLG_GetStatus(void)
{
return (*(vu32 *)PA_FROM_VA_PTR((u32 *)0x1FF800F0)) & 0xFFFF;
}

View File

@ -130,11 +130,81 @@ void configHook(vu8 *cfgPage)
flagsKProcess = KPROCESS_OFFSETOF(kernelFlags); flagsKProcess = KPROCESS_OFFSETOF(kernelFlags);
} }
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) static void findUsefulSymbols(void)
{ {
u32 *off; u32 *off;
for(off = (u32 *)0xFFFF0000; *off != 0xE1A0D002; off++); // Get fcramDescriptor
for (off = (u32 *)0xFFF00000; ; ++off)
{
if ( (off[0] >> 16) == 0xE59F
&& (off[1] >> 16) == 0xE3A0
&& (off[2] >> 16) == 0xE3A0
&& (off[3] >> 16) == 0xE1A0
&& (off[4] >> 16) == 0xEB00)
{
fcramDescriptor = (FcramDescriptor *)off[2 + (off[0] & 0xFFFF) / 4];
break;
}
}
// Get kAlloc
for (; *off != 0xE1A00005 || *(off + 1) != 0xE320F000; ++off);
off = decodeArmBranch(off + 2);
for (; (*off >> 16) != 0xEB00; ++off);
kAlloc = (void* (*)(FcramDescriptor *, u32, u32, u32))decodeArmBranch(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; off += 3;
initFPU = (void (*) (void))off; initFPU = (void (*) (void))off;
@ -171,6 +241,8 @@ static void findUsefulSymbols(void)
KEvent__Clear = (Result (*)(KEvent *))decodeArmBranch(off + 1); KEvent__Clear = (Result (*)(KEvent *))decodeArmBranch(off + 1);
for(off = (u32 *)KEvent__Clear; *off != 0xE8BD8070; off++); for(off = (u32 *)KEvent__Clear; *off != 0xE8BD8070; off++);
synchronizationMutex = *(KObjectMutex **)(off + 1); synchronizationMutex = *(KObjectMutex **)(off + 1);
for(off = (u32 *)officialSVCs[0x18]; *off != 0xE1A04005; ++off);
KEvent__Signal = (Result (*)(KEvent *))decodeArmBranch(off + 1);
for(off = (u32 *)officialSVCs[0x24]; *off != 0xE59F004C; off++); for(off = (u32 *)officialSVCs[0x24]; *off != 0xE59F004C; off++);
WaitSynchronization1 = (Result (*)(void *, KThread *, KSynchronizationObject *, s64))decodeArmBranch(off + 6); WaitSynchronization1 = (Result (*)(void *, KThread *, KSynchronizationObject *, s64))decodeArmBranch(off + 6);
@ -197,6 +269,21 @@ static void findUsefulSymbols(void)
for(off = (u32 *)officialSVCs[0x72]; *off != 0xE2041102; off++); for(off = (u32 *)officialSVCs[0x72]; *off != 0xE2041102; off++);
KProcessHwInfo__UnmapProcessMemory = (Result (*)(KProcessHwInfo *, void *, u32))decodeArmBranch(off - 1); 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 *)KProcessHwInfo__MapListOfKBlockInfo; *off != 0xE1A0000B; ++off);
doControlMemory = (Result (*)(KProcessHwInfo*, u32, u32, u32, u32, u32, u32, u32))decodeArmBranch(off + 1);
for(off = (u32 *)officialSVCs[0x7C]; *off != 0x03530000; off++); for(off = (u32 *)officialSVCs[0x7C]; *off != 0x03530000; off++);
KObjectMutex__WaitAndAcquire = (void (*)(KObjectMutex *))decodeArmBranch(++off); KObjectMutex__WaitAndAcquire = (void (*)(KObjectMutex *))decodeArmBranch(++off);
for(; *off != 0xE320F000; off++); for(; *off != 0xE320F000; off++);
@ -243,6 +330,7 @@ static void findUsefulSymbols(void)
decodeArmBranch((u32 *)officialSVCs[0x01] + 5); decodeArmBranch((u32 *)officialSVCs[0x01] + 5);
CreateThread = (Result (*)(Handle *, u32, u32, u32, s32, s32))decodeArmBranch((u32 *)officialSVCs[0x08] + 5); CreateThread = (Result (*)(Handle *, u32, u32, u32, s32, s32))decodeArmBranch((u32 *)officialSVCs[0x08] + 5);
SleepThread = (void (*)(s64))officialSVCs[0x0A]; SleepThread = (void (*)(s64))officialSVCs[0x0A];
CreateEvent = (Result (*)(Handle *, ResetType))decodeArmBranch((u32 *)officialSVCs[0x17] + 3);
CloseHandle = (Result (*)(Handle))officialSVCs[0x23]; CloseHandle = (Result (*)(Handle))officialSVCs[0x23];
GetHandleInfo = (Result (*)(s64 *, Handle, u32))decodeArmBranch((u32 *)officialSVCs[0x29] + 3); GetHandleInfo = (Result (*)(s64 *, Handle, u32))decodeArmBranch((u32 *)officialSVCs[0x29] + 3);
GetSystemInfo = (Result (*)(s64 *, s32, s32))decodeArmBranch((u32 *)officialSVCs[0x2A] + 3); GetSystemInfo = (Result (*)(s64 *, s32, s32))decodeArmBranch((u32 *)officialSVCs[0x2A] + 3);
@ -253,6 +341,8 @@ static void findUsefulSymbols(void)
OpenProcess = (Result (*)(Handle *, u32))decodeArmBranch((u32 *)officialSVCs[0x33] + 3); OpenProcess = (Result (*)(Handle *, u32))decodeArmBranch((u32 *)officialSVCs[0x33] + 3);
GetProcessId = (Result (*)(u32 *, Handle))decodeArmBranch((u32 *)officialSVCs[0x35] + 3); GetProcessId = (Result (*)(u32 *, Handle))decodeArmBranch((u32 *)officialSVCs[0x35] + 3);
DebugActiveProcess = (Result (*)(Handle *, u32))decodeArmBranch((u32 *)officialSVCs[0x60] + 3); DebugActiveProcess = (Result (*)(Handle *, u32))decodeArmBranch((u32 *)officialSVCs[0x60] + 3);
SignalEvent = (Result (*)(Handle event))officialSVCs[0x18];
UnmapProcessMemory = (Result (*)(Handle, void *, u32))officialSVCs[0x72]; UnmapProcessMemory = (Result (*)(Handle, void *, u32))officialSVCs[0x72];
KernelSetState = (Result (*)(u32, u32, u32, u32))((u32 *)officialSVCs[0x7C] + 1); KernelSetState = (Result (*)(u32, u32, u32, u32))((u32 *)officialSVCs[0x7C] + 1);
@ -284,6 +374,8 @@ static void findUsefulSymbols(void)
invalidateInstructionCacheRange = (void (*)(void *, u32))off2; invalidateInstructionCacheRange = (void (*)(void *, u32))off2;
} }
} }
installMmuHooks();
} }
void main(FcramLayout *layout, KCoreContext *ctxs) void main(FcramLayout *layout, KCoreContext *ctxs)

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

@ -0,0 +1,319 @@
#include "mmu.h"
#include "globals.h"
#include "utils.h"
extern u8 svcSignalingEnabled;
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 permissions 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) {
svcSignalingEnabled |= 2;
*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,8 +44,11 @@
#include "svc/MapProcessMemoryEx.h" #include "svc/MapProcessMemoryEx.h"
#include "svc/UnmapProcessMemoryEx.h" #include "svc/UnmapProcessMemoryEx.h"
#include "svc/ControlService.h" #include "svc/ControlService.h"
#include "svc/ControlProcess.h"
#include "svc/ExitProcess.h"
#include "svc/CopyHandle.h" #include "svc/CopyHandle.h"
#include "svc/TranslateHandle.h" #include "svc/TranslateHandle.h"
#include "svc/ControlMemoryUnsafe.h"
void *officialSVCs[0x7E] = {NULL}; void *officialSVCs[0x7E] = {NULL};
void *alteredSvcTable[0x100] = {NULL}; void *alteredSvcTable[0x100] = {NULL};
@ -63,6 +66,7 @@ void buildAlteredSvcTable(void)
memcpy(alteredSvcTable, officialSVCs, 4 * 0x7E); memcpy(alteredSvcTable, officialSVCs, 4 * 0x7E);
alteredSvcTable[0x01] = ControlMemoryHookWrapper; alteredSvcTable[0x01] = ControlMemoryHookWrapper;
alteredSvcTable[0x03] = ExitProcessHookWrapper;
if (isN3DS) if (isN3DS)
alteredSvcTable[0x08] = CreateThreadHookWrapper; alteredSvcTable[0x08] = CreateThreadHookWrapper;
@ -90,13 +94,15 @@ void buildAlteredSvcTable(void)
alteredSvcTable[0x93] = invalidateInstructionCacheRange; alteredSvcTable[0x93] = invalidateInstructionCacheRange;
alteredSvcTable[0x94] = invalidateEntireInstructionCache; alteredSvcTable[0x94] = invalidateEntireInstructionCache;
alteredSvcTable[0xA0] = MapProcessMemoryEx; alteredSvcTable[0xA0] = MapProcessMemoryExWrapper;
alteredSvcTable[0xA1] = UnmapProcessMemoryEx; alteredSvcTable[0xA1] = UnmapProcessMemoryEx;
alteredSvcTable[0xA2] = ControlMemoryEx; alteredSvcTable[0xA2] = ControlMemoryEx;
alteredSvcTable[0xA3] = ControlMemoryUnsafeWrapper;
alteredSvcTable[0xB0] = ControlService; alteredSvcTable[0xB0] = ControlService;
alteredSvcTable[0xB1] = CopyHandleWrapper; alteredSvcTable[0xB1] = CopyHandleWrapper;
alteredSvcTable[0xB2] = TranslateHandleWrapper; alteredSvcTable[0xB2] = TranslateHandleWrapper;
alteredSvcTable[0xB3] = ControlProcess;
} }
void signalSvcEntry(u32 svcId) void signalSvcEntry(u32 svcId)
@ -111,10 +117,18 @@ void signalSvcEntry(u32 svcId)
void signalSvcReturn(u32 svcId) void signalSvcReturn(u32 svcId)
{ {
KProcess *currentProcess = currentCoreContext->objectContext.currentProcess; KProcess *currentProcess = currentCoreContext->objectContext.currentProcess;
u32 flags = KPROCESS_GET_RVALUE(currentProcess, customFlags);
// Since DBGEVENT_SYSCALL_RETURN is non blocking, we'll cheat using EXCEVENT_UNDEFINED_SYSCALL (debug->svcId is fortunately an u16!) // Since DBGEVENT_SYSCALL_RETURN is non blocking, we'll cheat using EXCEVENT_UNDEFINED_SYSCALL (debug->svcId is fortunately an u16!)
if(debugOfProcess(currentProcess) != NULL && svcId != 0xFF && shouldSignalSyscallDebugEvent(currentProcess, svcId)) if((svcSignalingEnabled & 1) != 0 && (currentProcess) != NULL && svcId != 0xFF && shouldSignalSyscallDebugEvent(currentProcess, svcId))
SignalDebugEvent(DBGEVENT_OUTPUT_STRING, 0xFFFFFFFF, svcId); SignalDebugEvent(DBGEVENT_OUTPUT_STRING, 0xFFFFFFFF, svcId);
if (flags & SignalOnMemLayoutChanges && flags & MemLayoutChanged)
{
*KPROCESS_GET_PTR(currentProcess, customFlags) = flags & ~MemLayoutChanged;
SignalEvent(KPROCESS_GET_RVALUE(currentProcess, onMemoryLayoutChangeEvent));
svcSignalingEnabled &= ~2;
}
} }
void postprocessSvc(void) void postprocessSvc(void)

View File

@ -0,0 +1,84 @@
/*
* 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 "globals.h"
#include "memory.h"
#include "svc/ControlMemoryUnsafe.h"
Result ControlMemoryUnsafe(u32 *out, u32 addr0, u32 size, MemOp op, MemPerm perm)
{
Result res = 0;
KProcess *currentProcess = currentCoreContext->objectContext.currentProcess;
KProcessHwInfo *hwInfo = hwInfoOfProcess(currentProcess);
KAutoObject__AddReference((KAutoObject *)currentProcess);
size = size >> 12 << 12;
switch (op & MEMOP_OP_MASK)
{
case MEMOP_FREE:
{
res = doControlMemory(hwInfo, addr0, size >> 12, 0, 0, 0, 0, 0);
break;
}
case MEMOP_COMMIT:
{
u32 pAddr = 0;
u32 state = 0xBB05;
u32 region = op & MEMOP_REGION_MASK;
perm = (perm & 7) | 0x18;
if (op & MEMOP_LINEAR)
{
void *kvAddr = kAlloc(fcramDescriptor, size >> 12, 0, region);
if (!kvAddr)
{
res = 0xD86007F3;
break;
}
memset(kvAddr, 0, size >> 2);
flushDataCacheRange(kvAddr, size);
pAddr = (u32)kvAddr + 0x40000000;
state = 0x3907;
}
res = doControlMemory(hwInfo, addr0, size >> 12, pAddr, state, perm, 0, region);
if (res >= 0 && out)
*out = addr0;
break;
}
default:
res = 0xE0E01BEE;
break;
}
((KAutoObject *)currentProcess)->vtable->DecrementReferenceCount((KAutoObject *)currentProcess);
return res;
}

View File

@ -0,0 +1,208 @@
#include "svc/ControlProcess.h"
#include "memory.h"
#include "mmu.h"
#include "synchronization.h"
typedef bool (*ThreadPredicate)(KThread *thread);
// Lock bit has to be different from Rosalina to avoid unintended unlock when using Rosalina menu
static void rescheduleThread(KThread *thread, bool lock)
{
KRecursiveLock__Lock(criticalSectionLock);
u32 oldSchedulingMask = thread->schedulingMask;
if(lock)
thread->schedulingMask |= 0x20;
else
thread->schedulingMask &= ~0x20;
KScheduler__AdjustThread(currentCoreContext->objectContext.currentScheduler, thread, oldSchedulingMask);
KRecursiveLock__Unlock(criticalSectionLock);
}
static void lockThread(KThread *thread)
{
KThread *syncThread = synchronizationMutex->owner;
if(syncThread == NULL || syncThread != thread)
rescheduleThread(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);
}
break;
}
case PROCESSOP_SIGNAL_ON_EXIT:
{
*KPROCESS_GET_PTR(process, customFlags) |= SignalOnExit;
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->ownerProcess == process && (thread->schedulingMask & 0x20)
&& (threadPredicate == NULL || threadPredicate(thread)))
rescheduleThread(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
lockThread(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 & 0x20))
{
lockThread(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,7 +29,7 @@
Result CreateThreadHook(Handle *outThreadHandle, u32 ep, u32 arg, u32 stackTop, s32 priority, s32 processorId) Result CreateThreadHook(Handle *outThreadHandle, u32 ep, u32 arg, u32 stackTop, s32 priority, s32 processorId)
{ {
u32 flags = flagsOfProcess(currentCoreContext->objectContext.currentProcess); u32 flags = flagsOfProcess(currentCoreContext->objectContext.currentProcess);
if (isN3DS && CONFIG(REDIRECTAPPTHREADS) && processorId == 1 && (flags & 0xF00) == 0x100) if (isN3DS && CONFIG(REDIRECTAPPTHREADS) && !disableThreadRedirection && processorId == 1 && (flags & 0xF00) == 0x100)
processorId = 2; processorId = 2;
return CreateThread(outThreadHandle, ep, arg, stackTop, priority, processorId); return CreateThread(outThreadHandle, ep, arg, stackTop, priority, processorId);

View File

@ -0,0 +1,32 @@
#include "svc/ExitProcess.h"
void ExitProcessHook(void) {
KProcess *currentProcess = currentCoreContext->objectContext.currentProcess;
u32 flags = KPROCESS_GET_RVALUE(currentProcess, customFlags);
if (flags & SignalOnExit)
{
// Signal that the process is about to be terminated
if (PLG_GetStatus() == PLG_CFG_RUNNING)
PLG_SignalEvent(PLG_CFG_EXIT_EVENT);
// Unlock all threads that might be locked
{
KRecursiveLock__Lock(criticalSectionLock);
for (KLinkedListNode *node = threadList->list.nodes.first;
node != (KLinkedListNode *)&threadList->list.nodes;
node = node->next)
{
KThread *thread = (KThread *)node->key;
if (thread->ownerProcess == currentProcess && thread->schedulingMask & 0x20)
thread->schedulingMask &= ~0x20;
}
KRecursiveLock__Unlock(criticalSectionLock);
}
}
return ((void(*)())officialSVCs[0x3])();
}

View File

@ -29,11 +29,14 @@
Result GetHandleInfoHook(s64 *out, Handle handle, u32 type) 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); KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess);
KAutoObject *obj; KAutoObject *obj;
if(handle == CUR_PROCESS_HANDLE) if(handle == CUR_PROCESS_HANDLE)
{ {
obj = (KAutoObject *)(currentCoreContext->objectContext.currentProcess); obj = (KAutoObject *)(currentCoreContext->objectContext.currentProcess);
@ -45,18 +48,82 @@ Result GetHandleInfoHook(s64 *out, Handle handle, u32 type)
if(obj == NULL) if(obj == NULL)
return 0xD8E007F7; return 0xD8E007F7;
if(strcmp(classNameOfAutoObject(obj), "KDebug") == 0) switch (type)
hwInfo = hwInfoOfProcess(((KDebug *)obj)->owner); {
else if(strcmp(classNameOfAutoObject(obj), "KProcess") == 0) case 0x10000: ///< Get ctx id (should probably move it to GetProcessInfo)
hwInfo = hwInfoOfProcess((KProcess *)obj); {
else if(strcmp(classNameOfAutoObject(obj), "KDebug") == 0)
hwInfo = NULL; 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); 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); *out = ttb & ~((1 << (14 - TTBCR)) - 1);
break; 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: default:
res = 0xD8E007ED; // invalid enum value res = 0xD8E007ED; // invalid enum value
break; break;

View File

@ -69,14 +69,15 @@ Result GetSystemInfoHook(s64 *out, s32 type, s32 param)
case 6: case 6:
*out = cfwInfo.splashDurationMsec; *out = cfwInfo.splashDurationMsec;
break; break;
case 0x10: case 0x10:
*out = (s64)cfwInfo.autobootTwlTitleId; *out = (s64)cfwInfo.autobootTwlTitleId;
break; break;
case 0x11: case 0x11:
*out = cfwInfo.autobootCtrAppmemtype; *out = cfwInfo.autobootCtrAppmemtype;
break; break;
case 0x80:
*out = fcramDescriptor->appRegion.regionSizeInBytes;
break;
case 0x100: case 0x100:
*out = (s64)cfwInfo.hbldr3dsxTitleId; *out = (s64)cfwInfo.hbldr3dsxTitleId;
break; break;
@ -116,7 +117,12 @@ Result GetSystemInfoHook(s64 *out, s32 type, s32 param)
case 0x10C: case 0x10C:
*out = (s64)cfwInfo.bottomScreenFilter.invert; *out = (s64)cfwInfo.bottomScreenFilter.invert;
break; break;
case 0x180:
*out = cfwInfo.pluginLoaderFlags;
break;
case 0x181:
*out = disableThreadRedirection;
break;
case 0x200: // isRelease case 0x200: // isRelease
*out = cfwInfo.flags & 1; *out = cfwInfo.flags & 1;
break; break;

View File

@ -35,8 +35,9 @@
static u32 nbEnabled = 0; static u32 nbEnabled = 0;
static u32 maskedPids[MAX_DEBUG]; static u32 maskedPids[MAX_DEBUG];
static u32 masks[MAX_DEBUG][8] = {0}; static u32 masks[MAX_DEBUG][8] = {0};
static bool forceBetterSoc = false;
bool svcSignalingEnabled = false; u8 svcSignalingEnabled = 0;
bool shouldSignalSyscallDebugEvent(KProcess *process, u8 svcId) bool shouldSignalSyscallDebugEvent(KProcess *process, u8 svcId)
{ {
@ -67,7 +68,7 @@ Result SetSyscallDebugEventMask(u32 pid, bool enable, const u32 *mask)
{ {
maskedPids[nbEnabled] = pid; maskedPids[nbEnabled] = pid;
memcpy(&masks[nbEnabled++], tmpMask, 32); memcpy(&masks[nbEnabled++], tmpMask, 32);
svcSignalingEnabled = true; svcSignalingEnabled |= 1;
} }
else else
{ {
@ -87,7 +88,7 @@ Result SetSyscallDebugEventMask(u32 pid, bool enable, const u32 *mask)
} }
maskedPids[--nbEnabled] = 0; maskedPids[--nbEnabled] = 0;
memset(&masks[nbEnabled], 0, 32); memset(&masks[nbEnabled], 0, 32);
svcSignalingEnabled = false; svcSignalingEnabled &= ~1;
} }
KRecursiveLock__Unlock(&syscallDebugEventMaskLock); KRecursiveLock__Unlock(&syscallDebugEventMaskLock);
@ -101,6 +102,16 @@ Result KernelSetStateHook(u32 type, u32 varg1, u32 varg2, u32 varg3)
switch(type) switch(type)
{ {
case 0xA: // Type 10 (ConfigureNew3DSCPU)
{
if (varg1 & (1 << 2)) // Lock faster speed
forceBetterSoc = true;
else if (varg1 & (1 << 3)) // Unlock faster speed
forceBetterSoc = false;
else
res = KernelSetState(type, forceBetterSoc ? 3 : varg1, varg2, varg3);
break;
}
case 0x10000: case 0x10000:
{ {
do do
@ -190,6 +201,20 @@ Result KernelSetStateHook(u32 type, u32 varg1, u32 varg2, u32 varg3)
KRecursiveLock__Unlock(&dbgParamsLock); KRecursiveLock__Unlock(&dbgParamsLock);
break; break;
} }
case 0x10007:
{
if (signalPluginEvent == NULL && varg1)
{
KProcessHandleTable *table = handleTableOfProcess(currentCoreContext->objectContext.currentProcess);
signalPluginEvent = (KEvent *)KProcessHandleTable__ToKAutoObject(table, varg1);
}
break;
}
case 0x10080:
{
disableThreadRedirection = varg1 != 0;
break;
}
default: default:
{ {
res = KernelSetState(type, varg1, varg2, varg3); res = KernelSetState(type, varg1, varg2, varg3);

View File

@ -26,19 +26,61 @@
#include "svc/MapProcessMemoryEx.h" #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); 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; 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; if (srcProcess == NULL)
obj->vtable->DecrementReferenceCount(obj); {
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; return res;
} }

View File

@ -162,6 +162,43 @@ Result SendSyncRequestHook(Handle handle)
break; break;
} }
case 0x00C0080: // srv: publishToSubscriber
{
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
if (info != NULL && strcmp(info->name, "srv:") == 0 && cmdbuf[1] == 0x1002)
{
// Wake up application thread
PLG__WakeAppThread();
cmdbuf[0] = 0xC0040;
cmdbuf[1] = 0;
skip = true;
}
break;
}
case 0x00D0080: // APT:ReceiveParameter
{
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
if (info != NULL && strncmp(info->name, "APT:", 4) == 0 && cmdbuf[1] == 0x300)
{
res = SendSyncRequest(handle);
skip = true;
if (res >= 0)
{
u32 plgStatus = PLG_GetStatus();
u32 command = cmdbuf[3];
if ((plgStatus == PLG_CFG_RUNNING && command == 3) // COMMAND_RESPONSE
|| (plgStatus == PLG_CFG_INHOME && (command >= 10 || command <= 12))) // COMMAND_WAKEUP_BY_EXIT || COMMAND_WAKEUP_BY_PAUSE
PLG_SignalEvent(PLG_CFG_HOME_EVENT);
}
}
break;
}
case 0x4010082: case 0x4010082:
{ {
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession); SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);

View File

@ -29,12 +29,30 @@
Result UnmapProcessMemoryEx(Handle processHandle, void *dst, u32 size) Result UnmapProcessMemoryEx(Handle processHandle, void *dst, u32 size)
{ {
Result res = 0;
KProcess *process;
KProcessHwInfo *hwInfo;
KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess);
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 return UnmapProcessMemory(processHandle, dst, size); // equivalent when size <= 64MB
KProcessHwInfo *currentHwInfo = hwInfoOfProcess(currentCoreContext->objectContext.currentProcess); if (processHandle == CUR_PROCESS_HANDLE)
{
process = currentCoreContext->objectContext.currentProcess;
KAutoObject__AddReference((KAutoObject *)process);
}
else
process = KProcessHandleTable__ToKProcess(handleTable, processHandle);
Result res = KProcessHwInfo__UnmapProcessMemory(currentHwInfo, dst, size >> 12); if (process == NULL)
return 0xD8E007F7;
hwInfo = hwInfoOfProcess(process);
res = KProcessHwInfo__UnmapProcessMemory(hwInfo, dst, size >> 12);
((KAutoObject *)process)->vtable->DecrementReferenceCount((KAutoObject *)process);
invalidateEntireInstructionCache(); invalidateEntireInstructionCache();
flushEntireDataCache(); flushEntireDataCache();

View File

@ -69,6 +69,14 @@ ControlMemoryHookWrapper:
add sp, #12 add sp, #12
pop {pc} pop {pc}
.global ExitProcessHookWrapper
.type ExitProcessHookWrapper, %function
ExitProcessHookWrapper:
push {lr}
bl ExitProcessHook
pop {pc}
.global ControlMemoryEx .global ControlMemoryEx
.type ControlMemoryEx, %function .type ControlMemoryEx, %function
ControlMemoryEx: ControlMemoryEx:
@ -97,3 +105,21 @@ CreateThreadHookWrapper:
ldr r1, [sp, #8] ldr r1, [sp, #8]
add sp, sp, #12 add sp, sp, #12
pop {pc} pop {pc}
.global ControlMemoryUnsafeWrapper
.type ControlMemoryUnsafeWrapper, %function
ControlMemoryUnsafeWrapper:
push {lr}
str r4, [sp, #-4]!
bl ControlMemoryUnsafe
add sp, #4
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 blx r12
bx lr 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 .global safecpy
.type safecpy, %function .type safecpy, %function
safecpy: safecpy:

View File

@ -8,8 +8,19 @@
#define SYSMODULE_CXI_COOKIE_MASK 0xEEEE000000000000ull #define SYSMODULE_CXI_COOKIE_MASK 0xEEEE000000000000ull
// Used by the custom loader command 0x101 (ControlApplicationMemoryModeOverride)
typedef struct ControlApplicationMemoryModeOverrideConfig {
u32 query : 1; //< Only query the current configuration, do not update it.
u32 enable_o3ds : 1; //< Enable o3ds memory mode override
u32 enable_n3ds : 1; //< Enable n3ds memory mode override
u32 o3ds_mode : 3; //< O3ds memory mode
u32 n3ds_mode : 3; //< N3ds memory mode
} ControlApplicationMemoryModeOverrideConfig;
static ControlApplicationMemoryModeOverrideConfig g_memoryOverrideConfig = { 0 };
extern u32 config, multiConfig, bootConfig; extern u32 config, multiConfig, bootConfig;
extern bool isN3DS, isSdMode; extern bool isN3DS, isSdMode, nextGamePatchDisabled;
static u64 g_cached_programHandle; // for exheader info only static u64 g_cached_programHandle; // for exheader info only
static ExHeader_Info g_exheaderInfo; static ExHeader_Info g_exheaderInfo;
@ -110,6 +121,11 @@ static inline bool IsSysmoduleId(u64 tid)
return (tid >> 32) == 0x00040130; return (tid >> 32) == 0x00040130;
} }
static inline bool IsApplicationId(u64 tid)
{
return (tid >> 32) == 0x00040000;
}
static inline bool IsSysmoduleCxiCookie(u64 programHandle) static inline bool IsSysmoduleCxiCookie(u64 programHandle)
{ {
return (programHandle >> 32) == (SYSMODULE_CXI_COOKIE_MASK >> 32); return (programHandle >> 32) == (SYSMODULE_CXI_COOKIE_MASK >> 32);
@ -211,6 +227,69 @@ static Result loadCode(const ExHeader_Info *exhi, u64 programHandle, const prog_
return 0; return 0;
} }
static u32 plgldrRefcount = 0;
static Handle plgldrHandle = 0;
Result plgldrInit(void)
{
Result res;
if (AtomicPostIncrement(&plgldrRefcount)) return 0;
for(res = 0xD88007FA; res == (Result)0xD88007FA; svcSleepThread(500 * 1000LL)) {
res = svcConnectToPort(&plgldrHandle, "plg:ldr");
if(R_FAILED(res) && res != (Result)0xD88007FA) {
AtomicDecrement(&plgldrRefcount);
return res;
}
}
return 0;
}
void plgldrExit(void)
{
if (AtomicDecrement(&plgldrRefcount)) return;
svcCloseHandle(plgldrHandle);
}
// Get plugin loader state
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;
}
// Try to load a plugin for the game
static Result PLGLDR_LoadPlugin(u32 processID)
{
// 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
bool enabled = false;
PLGLDR__IsPluginLoaderEnabled(&enabled);
if (!enabled) {
return 0;
}
}
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(1, 1, 0);
cmdbuf[1] = processID;
return svcSendSyncRequest(plgldrHandle);
}
static inline bool IsHioId(u64 id) static inline bool IsHioId(u64 id)
{ {
// FS loads HIO titles at boot when it can. For HIO titles, title/programId and "program handle" // FS loads HIO titles at boot when it can. For HIO titles, title/programId and "program handle"
@ -256,6 +335,8 @@ static Result GetProgramInfoImpl(ExHeader_Info *exheaderInfo, u64 programHandle)
if (R_FAILED(res)) if (R_FAILED(res))
return res; return res;
u64 originalTitleId = exheaderInfo->aci.local_caps.title_id;
// Tweak 3dsx placeholder title exheaderInfo // Tweak 3dsx placeholder title exheaderInfo
if (hbldrIs3dsxTitle(exheaderInfo->aci.local_caps.title_id)) if (hbldrIs3dsxTitle(exheaderInfo->aci.local_caps.title_id))
{ {
@ -263,7 +344,6 @@ static Result GetProgramInfoImpl(ExHeader_Info *exheaderInfo, u64 programHandle)
} }
else else
{ {
u64 originalTitleId = exheaderInfo->aci.local_caps.title_id;
bool exhLoadedExternally = false; bool exhLoadedExternally = false;
if (CONFIG(PATCHGAMES)) if (CONFIG(PATCHGAMES))
{ {
@ -280,6 +360,13 @@ static Result GetProgramInfoImpl(ExHeader_Info *exheaderInfo, u64 programHandle)
if(exhLoadedExternally) if(exhLoadedExternally)
exheaderInfo->aci.local_caps.title_id = originalTitleId; exheaderInfo->aci.local_caps.title_id = originalTitleId;
} }
if (IsApplicationId(originalTitleId)) {
if (g_memoryOverrideConfig.enable_o3ds)
exheaderInfo->aci.local_caps.core_info.o3ds_system_mode = g_memoryOverrideConfig.o3ds_mode;
if (g_memoryOverrideConfig.enable_n3ds)
exheaderInfo->aci.local_caps.core_info.n3ds_system_mode = g_memoryOverrideConfig.n3ds_mode;
}
return res; return res;
} }
@ -336,6 +423,9 @@ static Result LoadProcessImpl(Handle *outProcessHandle, const ExHeader_Info *exh
u64 titleId = exhi->aci.local_caps.title_id; u64 titleId = exhi->aci.local_caps.title_id;
if (R_SUCCEEDED(res = loadCode(exhi, programHandle, &mapped))) if (R_SUCCEEDED(res = loadCode(exhi, programHandle, &mapped)))
{ {
u32 *code = (u32 *)mapped.text_addr;
bool isHomebrew = code[0] == 0xEA000006 && code[8] == 0xE1A0400E;
memcpy(&csh.name, csi->name, 8); memcpy(&csh.name, csi->name, 8);
csh.program_id = titleId; csh.program_id = titleId;
csh.text_addr = vaddr.text_addr; csh.text_addr = vaddr.text_addr;
@ -354,6 +444,16 @@ static Result LoadProcessImpl(Handle *outProcessHandle, const ExHeader_Info *exh
res = svcCreateProcess(outProcessHandle, codeset, exhi->aci.kernel_caps.descriptors, count); res = svcCreateProcess(outProcessHandle, codeset, exhi->aci.kernel_caps.descriptors, count);
svcCloseHandle(codeset); svcCloseHandle(codeset);
res = R_SUCCEEDED(res) ? 0 : res; res = R_SUCCEEDED(res) ? 0 : res;
// check for plugin
if (!res && !isHomebrew && ((u32)((titleId >> 0x20) & 0xFFFFFFEDULL) == 0x00040000))
{
u32 processID;
assertSuccess(svcGetProcessId(&processID, *outProcessHandle));
assertSuccess(plgldrInit());
assertSuccess(PLGLDR_LoadPlugin(processID));
plgldrExit();
}
} }
} }
@ -441,6 +541,7 @@ void loaderHandleCommands(void *ctx)
(void)ctx; (void)ctx;
FS_ProgramInfo title; FS_ProgramInfo title;
FS_ProgramInfo update; FS_ProgramInfo update;
ControlApplicationMemoryModeOverrideConfig memModeOverride;
u32* cmdbuf; u32* cmdbuf;
u16 cmdid; u16 cmdid;
int res; int res;
@ -480,6 +581,20 @@ void loaderHandleCommands(void *ctx)
cmdbuf[2] = IPC_Desc_StaticBuffer(sizeof(ExHeader_Info), 0); //0x1000002; cmdbuf[2] = IPC_Desc_StaticBuffer(sizeof(ExHeader_Info), 0); //0x1000002;
cmdbuf[3] = (u32)&g_exheaderInfo; // official Loader makes a copy here, but this is isn't necessary cmdbuf[3] = (u32)&g_exheaderInfo; // official Loader makes a copy here, but this is isn't necessary
break; break;
// Custom
case 0x100: // DisableNextGamePatch
nextGamePatchDisabled = true;
cmdbuf[0] = IPC_MakeHeader(0x100, 1, 0);
cmdbuf[1] = (Result)0;
break;
case 0x101: // ControlApplicationMemoryModeOverride
memcpy(&memModeOverride, &cmdbuf[1], sizeof(ControlApplicationMemoryModeOverrideConfig));
if (!memModeOverride.query)
g_memoryOverrideConfig = memModeOverride;
cmdbuf[0] = IPC_MakeHeader(0x101, 2, 0);
cmdbuf[1] = (Result)0;
memcpy(&cmdbuf[2], &g_memoryOverrideConfig, sizeof(ControlApplicationMemoryModeOverrideConfig));
break;
default: // error default: // error
cmdbuf[0] = IPC_MakeHeader(0, 1, 0); cmdbuf[0] = IPC_MakeHeader(0, 1, 0);
cmdbuf[1] = 0xD900182F; cmdbuf[1] = 0xD900182F;

View File

@ -10,7 +10,7 @@
#include "hbldr.h" #include "hbldr.h"
u32 config, multiConfig, bootConfig; u32 config, multiConfig, bootConfig;
bool isN3DS, isSdMode; bool isN3DS, isSdMode, nextGamePatchDisabled;
// MAKE SURE fsreg has been init before calling this // MAKE SURE fsreg has been init before calling this
static Result fsldrPatchPermissions(void) static Result fsldrPatchPermissions(void)
@ -115,7 +115,7 @@ void initSystem(void)
} }
static const ServiceManagerServiceEntry services[] = { static const ServiceManagerServiceEntry services[] = {
{ "Loader", 1, loaderHandleCommands, false }, { "Loader", 2, loaderHandleCommands, false },
{ "hb:ldr", 2, hbldrHandleCommands, true }, { "hb:ldr", 2, hbldrHandleCommands, true },
{ NULL }, { NULL },
}; };
@ -129,6 +129,8 @@ static_assert(ARGVBUF_SIZE > 2 * PATH_MAX, "Wrong 3DSX argv buffer size");
int main(void) int main(void)
{ {
nextGamePatchDisabled = false;
// Loader doesn't use any input static buffer, so we should be fine // Loader doesn't use any input static buffer, so we should be fine
u32 *sbuf = getThreadStaticBuffers(); u32 *sbuf = getThreadStaticBuffers();
sbuf[0] = IPC_Desc_StaticBuffer(sizeof(staticBufferForHbldr), 0); sbuf[0] = IPC_Desc_StaticBuffer(sizeof(staticBufferForHbldr), 0);

View File

@ -930,7 +930,7 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 ro
if(!applyCodeIpsPatch(progId, code, size)) goto error; if(!applyCodeIpsPatch(progId, code, size)) goto error;
} }
if(CONFIG(PATCHGAMES)) if(CONFIG(PATCHGAMES) && !(isApp && nextGamePatchDisabled))
{ {
if (!isSysmodule) if (!isSysmodule)
{ {
@ -952,6 +952,7 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 ro
} }
} }
nextGamePatchDisabled = false;
return; return;
error: error:

View File

@ -42,7 +42,7 @@ enum singleOptions
}; };
extern u32 config, multiConfig, bootConfig; extern u32 config, multiConfig, bootConfig;
extern bool isN3DS, isSdMode; extern bool isN3DS, isSdMode, nextGamePatchDisabled;
void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 roSize, u32 dataSize, u32 roAddress, u32 dataAddress); void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 roSize, u32 dataSize, u32 roAddress, u32 dataAddress);
bool loadTitleCodeSection(u64 progId, u8 *code, u32 size); bool loadTitleCodeSection(u64 progId, u8 *code, u32 size);

View File

@ -311,7 +311,11 @@ Result setAppCpuTimeLimit(s64 limit)
// Prevent apps from enabling preemption on core1 (and kernel will // Prevent apps from enabling preemption on core1 (and kernel will
// redirect apps threads from core 1 to 2). // redirect apps threads from core 1 to 2).
if (IS_N3DS && CONFIG(REDIRECTAPPTHREADS)) if (IS_N3DS && CONFIG(REDIRECTAPPTHREADS))
return 0; {
s64 disableThreadRedir = 0;
if (R_SUCCEEDED(svcGetSystemInfo(&disableThreadRedir, 0x10000, 0x181)) && !disableThreadRedir)
return 0;
}
ResourceLimitType category = RESLIMIT_CPUTIME; ResourceLimitType category = RESLIMIT_CPUTIME;
return svcSetResourceLimitValues(g_manager.reslimits[0], &category, &limit, 1); return svcSetResourceLimitValues(g_manager.reslimits[0], &category, &limit, 1);

View File

@ -24,7 +24,7 @@ export HBLDR_DEFAULT_3DSX_TITLE_NAME ?= "hblauncher_loader"
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR)) TARGET := $(notdir $(CURDIR))
BUILD := build BUILD := build
SOURCES := source source/gdb source/menus source/redshift SOURCES := source source/gdb source/menus source/plugin source/redshift
DATA := source/gdb/xml data DATA := source/gdb/xml data
INCLUDES := include include/gdb include/menus include/redshift INCLUDES := include include/gdb include/menus include/redshift

View File

@ -18,6 +18,10 @@
#include <3ds/types.h> #include <3ds/types.h>
/// Allows or disables thread redirection patches for the new thread if specified in the affinity field in svcCreateThread
#define AFFINITY_DISABLE_THREAD_REDIRECTION(x) ((x >= 0) ? (x | 0x40) : x)
#define AFFINITY_ALLOW_THREAD_REDIRECTION(x) ((x >= 0) ? (x & ~0x40) : x)
/// Operations for svcControlService /// Operations for svcControlService
typedef enum ServiceOp typedef enum ServiceOp
{ {
@ -71,18 +75,20 @@ void svcInvalidateEntireInstructionCache(void);
///@{ ///@{
/** /**
* @brief Maps a block of process memory. * @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 destAddress Start address of the memory block in the destination process
* @param srcAddress Address of the mapped block in the source process. * @param srcProcessHandle Handle of the process to map the memory from (source)
* @param size Size of the block of the memory to map (truncated to a multiple of 0x1000 bytes). * @param srcAddress Start address of the memory 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 srcAddress, u32 size);
/** /**
* @brief Unmaps a block of process memory. * @brief Unmaps a block of process memory.
* @param process Handle of the process. * @param process Handle of the process to unmap the memory from
* @param destAddress Address of the block of memory to unmap, in the current (destination) process. * @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). * @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); Result svcUnmapProcessMemoryEx(Handle process, u32 destAddress, u32 size);
@ -104,6 +110,20 @@ Result svcUnmapProcessMemoryEx(Handle process, u32 destAddress, u32 size);
* @sa svcControlMemory * @sa svcControlMemory
*/ */
Result svcControlMemoryEx(u32* addr_out, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader); Result svcControlMemoryEx(u32* addr_out, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader);
/**
* @brief Controls memory mapping, this version removes all checks which were being done
* The only operations supported are MEMOP_FREE, MEMOP_ALLOC and MEMOP_ALLOC_LINEAR
* All memory allocated with this svc, must be freed with this svc as well
* @param[out] addr_out The virtual address resulting from the operation. Usually the same as addr0.
* @param addr0 The virtual address to be used for the operation.
* @param size The requested size for @ref MEMOP_ALLOC and @ref MEMOP_ALLOC_LINEAR.
* @param op Operation flags. See @ref MemOp.
* @param perm A combination of @ref MEMPERM_READ and @ref MEMPERM_WRITE
* Value 0 is used when unmapping memory.
* @sa svcControlMemory
*/
Result svcControlMemoryUnsafe(u32 *out, u32 addr0, u32 size, MemOp op, MemPerm perm);
///@} ///@}
///@name System ///@name System
@ -134,4 +154,31 @@ Result svcCopyHandle(Handle *out, Handle outProcess, Handle in, Handle inProcess
* @param in The input handle. * @param in The input handle.
*/ */
Result svcTranslateHandle(u32 *outKAddr, char *outClassName, Handle in); 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
///< s32 count = svcControlProcess(handle, PROCESSOP_GET_ALL_HANDLES, (u32)&outBuf, 0)
///< Returns how many handles were found
PROCESSOP_SET_MMU_TO_RWX, ///< Set the whole memory of the process with rwx access (in the mmu table only)
///< svcControlProcess(handle, PROCESSOP_SET_MMU_TO_RWX, 0, 0)
PROCESSOP_GET_ON_MEMORY_CHANGE_EVENT, ///< Get the handle of an event which will be signaled each time the memory layout of this process changes
///< svcControlProcess(handle, PROCESSOP_GET_ON_MEMORY_CHANGE_EVENT, &eventHandleOut, 0)
PROCESSOP_SIGNAL_ON_EXIT, ///< Set a flag to be signaled when the process will be exited
///< svcControlProcess(handle, PROCESSOP_SIGNAL_ON_EXIT, 0, 0)
PROCESSOP_GET_PA_FROM_VA, ///< Get the physical address of the VAddr within the process
///< svcControlProcess(handle, PROCESSOP_GET_PA_FROM_VA, (u32)&PAOut, VAddr)
PROCESSOP_SCHEDULE_THREADS, ///< Lock / Unlock the process's threads
///< svcControlProcess(handle, PROCESSOP_SCHEDULE_THREADS, lock, threadPredicate)
///< lock: 0 to unlock threads, any other value to lock threads
///< threadPredicate: can be NULL or a funcptr to a predicate (typedef bool (*ThreadPredicate)(KThread *thread);)
///< The predicate must return true to operate on the thread
} ProcessOp;
Result svcControlProcess(Handle process, ProcessOp op, u32 varg2, u32 varg3);
///@} ///@}

View File

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

View File

@ -12,11 +12,15 @@
#define GDB_REMOTE_COMMAND_HANDLER(name) GDB_HANDLER(RemoteCommand##name) #define GDB_REMOTE_COMMAND_HANDLER(name) GDB_HANDLER(RemoteCommand##name)
#define GDB_DECLARE_REMOTE_COMMAND_HANDLER(name) GDB_DECLARE_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(SyncRequestInfo);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle); GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ListAllHandles);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig); GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMemRegions); GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMemRegions);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(FlushCaches); GDB_DECLARE_REMOTE_COMMAND_HANDLER(FlushCaches);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess); GDB_DECLARE_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(CatchSvc);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetThreadPriority);
GDB_DECLARE_QUERY_HANDLER(Rcmd); GDB_DECLARE_QUERY_HANDLER(Rcmd);

View File

@ -38,3 +38,4 @@ MyThread *inputRedirectionCreateThread(void);
void inputRedirectionThreadMain(void); void inputRedirectionThreadMain(void);
Result InputRedirection_Disable(s64 timeout); Result InputRedirection_Disable(s64 timeout);
Result InputRedirection_DoOrUndoPatches(void); Result InputRedirection_DoOrUndoPatches(void);

View File

@ -56,3 +56,4 @@ enum multiOptions
void LumaConfig_ConvertComboToString(char *out, u32 combo); void LumaConfig_ConvertComboToString(char *out, u32 combo);
Result LumaConfig_SaveSettings(void); Result LumaConfig_SaveSettings(void);
void LumaConfig_RequestSaveSettings(void);

View File

@ -86,7 +86,10 @@ bool menuCheckN3ds(void);
u32 menuCountItems(const Menu *menu); u32 menuCountItems(const Menu *menu);
MyThread *menuCreateThread(void); MyThread *menuCreateThread(void);
void menuEnter(void); void menuEnter(void);
void menuLeave(void); void menuLeave(void);
void menuThreadMain(void); void menuThreadMain(void);
void menuShow(Menu *root); 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,5 @@
#pragma once
#include "plugin/plgloader.h"
#include "plugin/plgldr.h"
#include "plugin/3gx.h"

View File

@ -0,0 +1,76 @@
#pragma once
#include <3ds/types.h>
#include "ifile.h"
#define _3GX_MAGIC (0x3230303024584733) /* "3GX$0002" */
typedef struct PACKED
{
u32 authorLen;
const char* authorMsg;
u32 titleLen;
const char* titleMsg;
u32 summaryLen;
const char* summaryMsg;
u32 descriptionLen;
const char* descriptionMsg;
union {
u32 flags;
struct {
u32 embeddedExeLoadFunc : 1;
u32 embeddedSwapSaveLoadFunc : 1;
u32 memoryRegionSize : 2;
u32 unused : 28;
};
};
u32 exeLoadChecksum;
u32 builtInLoadExeArgs[4];
u32 builtInSwapSaveLoadArgs[4];
} _3gx_Infos;
typedef struct PACKED
{
u32 count;
u32 * titles;
} _3gx_Targets;
typedef struct PACKED
{
u32 nbSymbols;
u32 symbolsOffset;
u32 nameTableOffset;
} _3gx_Symtable;
typedef struct PACKED
{
u32 codeOffset;
u32 rodataOffset;
u32 dataOffset;
u32 codeSize;
u32 rodataSize;
u32 dataSize;
u32 bssSize;
u32 exeLoadFuncOffset; // NOP terminated
u32 swapSaveFuncOffset; // NOP terminated
u32 swapLoadFuncOffset; // NOP terminated
} _3gx_Executable;
typedef struct PACKED
{
u64 magic;
u32 version;
u32 reserved;
_3gx_Infos infos;
_3gx_Executable executable;
_3gx_Targets targets;
_3gx_Symtable symtable;
} _3gx_Header;
Result Check_3gx_Magic(IFile *file);
Result Read_3gx_Header(IFile *file, _3gx_Header *header);
Result Read_3gx_ParseHeader(IFile *file, _3gx_Header *header);
Result Read_3gx_LoadSegments(IFile *file, _3gx_Header *header, void *dst);
Result Read_3gx_EmbeddedPayloads(IFile *file, _3gx_Header *header);
Result Set_3gx_LoadParams(u32* loadFunc, u32* params);
void Reset_3gx_LoadParams(void);

View File

@ -0,0 +1,78 @@
#pragma once
#include <3ds/types.h>
#define MAX_BUFFER (50)
#define MAX_ITEMS_COUNT (64)
#define HeaderMagic (0x24584733) /* "3GX$" */
typedef struct
{
bool noFlash;
u8 pluginMemoryStrategy;
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;
typedef enum
{
PLG_WAIT = -1,
PLG_OK = 0,
PLG_SLEEP_ENTRY = 1,
PLG_SLEEP_EXIT = 2,
PLG_ABOUT_TO_SWAP = 3,
PLG_ABOUT_TO_EXIT = 4,
PLG_HOME_ENTER = 5,
PLG_HOME_EXIT = 6,
} PLG_Event;
typedef enum
{
PLG_STRATEGY_NONE = 2,
PLG_STRATEGY_SWAP = 0,
PLG_STRATEGY_MODE3 = 1
} PluginMemoryStrategy;
typedef struct
{
u32 magic;
u32 version;
u32 heapVA;
u32 heapSize;
u32 exeSize; // Include sizeof(PluginHeader) + .text + .rodata + .data + .bss (0x1000 aligned too)
u32 isDefaultPlugin;
s32* plgldrEvent; ///< Used for synchronization
s32* plgldrReply; ///< Used for synchronization
u8 notifyHomeEvent;
u8 padding[3];
u32 reserved[23];
u32 config[32];
} PluginHeader;
typedef void (*OnPlgLdrEventCb_t)(s32 eventType);
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);
Result PLGLDR__SetRosalinaMenuBlock(bool shouldBlock);
Result PLGLDR__SetSwapSettings(char* swapPath, void* saveFunc, void* loadFunc, void* args);
Result PLGLDR__SetExeLoadSettings(void* loadFunc, void* args);
Result PLGLDR__GetVersion(u32 *version);
void PLGLDR__SetEventCallback(OnPlgLdrEventCb_t cb);
void PLGLDR__Status(void);

View File

@ -0,0 +1,105 @@
#pragma once
#include <3ds/types.h>
#include "MyThread.h"
#include "plgldr.h"
#include "utils.h"
void PluginLoader__Init(void);
bool PluginLoader__IsEnabled(void);
void PluginLoader__MenuCallback(void);
void PluginLoader__UpdateMenu(void);
void PluginLoader__HandleKernelEvent(u32 notifId);
void PluginLoader__HandleCommands(void *ctx);
void PluginLoader__Error(const char *message, Result res);
Result MemoryBlock__SetSize(u32 size);
Result MemoryBlock__IsReady(void);
Result MemoryBlock__Free(void);
Result MemoryBlock__ToSwapFile(void);
Result MemoryBlock__FromSwapFile(void);
Result MemoryBlock__MountInProcess(void);
Result MemoryBlock__UnmountFromProcess(void);
Result MemoryBlock__SetSwapSettings(u32* func, bool isDec, u32* params);
void MemoryBlock__ResetSwapSettings(void);
PluginHeader* MemoryBlock__GetMappedPluginHeader();
extern u32 g_loadSaveSwapArgs[0x4];
extern u32 g_loadExeArgs[0x4];
extern char g_swapFileName[256];
extern u32 g_memBlockSize;
u32 saveSwapFunc(void* startAddr, void* endAddr, void* args);
u32 loadSwapFunc(void* startAddr, void* endAddr, void* args);
u32 loadExeFunc(void* startAddr, void* endAddr, void* args);
bool TryToLoadPlugin(Handle process);
void PLG__NotifyEvent(PLG_Event event, bool signal);
void PLG__SetConfigMemoryStatus(u32 status);
u32 PLG__GetConfigMemoryStatus(void);
u32 PLG__GetConfigMemoryEvent(void);
typedef enum
{
PLG_CFG_NONE = 0,
PLG_CFG_RUNNING = 1,
PLG_CFG_INHOME = 2,
PLG_CFG_EXITING = 3,
PLG_CFG_HOME_EVENT = 1 << 16,
PLG_CFG_EXIT_EVENT = 2 << 16
} PLG_CFG_STATUS;
typedef struct
{
bool isReady;
bool isAppRegion;
u8 * memblock;
} MemoryBlock;
typedef struct
{
Result code;
const char * message;
} Error;
typedef struct
{
bool isEnabled;
bool pluginIsSwapped;
bool pluginIsHome;
const char * pluginPath;
MemoryBlock memblock;
Error error;
PluginHeader header;
Handle target;
Handle arbiter;
Handle kernelEvent;
bool useUserLoadParameters;
PluginLoadParameters userLoadParameters;
s32 plgEvent;
s32 plgReply;
s32 * plgEventPA;
s32 * plgReplyPA;
bool isExeLoadFunctionset;
bool isSwapFunctionset;
u8 pluginMemoryStrategy;
u32 exeLoadChecksum;
u32 swapLoadChecksum;
} PluginLoaderContext;
extern PluginLoaderContext PluginLoaderCtx;
// Used by the custom loader command 0x101 (ControlApplicationMemoryModeOverride)
typedef struct ControlApplicationMemoryModeOverrideConfig {
u32 query : 1; //< Only query the current configuration, do not update it.
u32 enable_o3ds : 1; //< Enable o3ds memory mode override
u32 enable_n3ds : 1; //< Enable n3ds memory mode override
u32 o3ds_mode : 3; //< O3ds memory mode
u32 n3ds_mode : 3; //< N3ds memory mode
} ControlApplicationMemoryModeOverrideConfig;

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_kill_connections(struct sock_server *serv);
void server_set_should_close_all(struct sock_server *serv); void server_set_should_close_all(struct sock_server *serv);
void server_finalize(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/svc.h>
#include <3ds/srv.h> #include <3ds/srv.h>
#include <3ds/result.h> #include <3ds/result.h>
#include <3ds/ipc.h>
#include "csvc.h" #include "csvc.h"
#include "luma_shared_config.h" #include "luma_shared_config.h"
@ -60,6 +61,24 @@ static inline void *decodeArmBranch(const void *src)
return (void *)((const u8 *)src + 8 + off); 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);
extern bool saveSettingsRequest;
void RequestSaveSettings(void);
static inline bool isServiceUsable(const char *name) static inline bool isServiceUsable(const char *name)
{ {
bool r; bool r;

View File

@ -59,7 +59,10 @@ SVC_BEGIN svcInvalidateEntireInstructionCache
SVC_END SVC_END
SVC_BEGIN svcMapProcessMemoryEx SVC_BEGIN svcMapProcessMemoryEx
str r4, [sp, #-4]!
ldr r4, [sp, #4]
svc 0xA0 svc 0xA0
ldr r4, [sp], #4
bx lr bx lr
SVC_END SVC_END
@ -79,6 +82,19 @@ SVC_BEGIN svcControlMemoryEx
bx lr bx lr
SVC_END SVC_END
SVC_BEGIN svcControlMemoryUnsafe
str r4, [sp, #-4]!
ldr r4, [sp, #4]
svc 0xA3
ldr r4, [sp], #4
bx lr
SVC_END
SVC_BEGIN svcFreeMemory
svc 0xA3
bx lr
SVC_END
SVC_BEGIN svcControlService SVC_BEGIN svcControlService
svc 0xB0 svc 0xB0
bx lr bx lr
@ -99,3 +115,8 @@ SVC_BEGIN svcTranslateHandle
str r1, [r2] str r1, [r2]
bx lr bx lr
SVC_END SVC_END
SVC_BEGIN svcControlProcess
svc 0xB3
bx lr
SVC_END

View File

@ -34,12 +34,6 @@
extern Handle preTerminationEvent; extern Handle preTerminationEvent;
static inline void assertSuccess(Result res)
{
if(R_FAILED(res))
svcBreak(USERBREAK_PANIC);
}
static MyThread errDispThread; static MyThread errDispThread;
static u8 ALIGN(8) errDispThreadStack[0xD00]; static u8 ALIGN(8) errDispThreadStack[0xD00];
@ -440,4 +434,4 @@ void errDispThreadMain(void)
svcCloseHandle(sessionHandle); svcCloseHandle(sessionHandle);
svcCloseHandle(clientHandle); svcCloseHandle(clientHandle);
svcCloseHandle(serverHandle); svcCloseHandle(serverHandle);
} }

View File

@ -10,6 +10,7 @@
#include "csvc.h" #include "csvc.h"
#include "fmt.h" #include "fmt.h"
#include "gdb/breakpoints.h" #include "gdb/breakpoints.h"
#include "utils.h"
#include "../utils.h" #include "../utils.h"
@ -19,12 +20,16 @@ struct
GDBCommandHandler handler; GDBCommandHandler handler;
} remoteCommandHandlers[] = } remoteCommandHandlers[] =
{ {
{ "convertvatopa" , GDB_REMOTE_COMMAND_HANDLER(ConvertVAToPA) },
{ "syncrequestinfo" , GDB_REMOTE_COMMAND_HANDLER(SyncRequestInfo) }, { "syncrequestinfo" , GDB_REMOTE_COMMAND_HANDLER(SyncRequestInfo) },
{ "translatehandle" , GDB_REMOTE_COMMAND_HANDLER(TranslateHandle) }, { "translatehandle" , GDB_REMOTE_COMMAND_HANDLER(TranslateHandle) },
{ "listallhandles" , GDB_REMOTE_COMMAND_HANDLER(ListAllHandles) },
{ "getmmuconfig" , GDB_REMOTE_COMMAND_HANDLER(GetMmuConfig) }, { "getmmuconfig" , GDB_REMOTE_COMMAND_HANDLER(GetMmuConfig) },
{ "getmemregions" , GDB_REMOTE_COMMAND_HANDLER(GetMemRegions) }, { "getmemregions" , GDB_REMOTE_COMMAND_HANDLER(GetMemRegions) },
{ "flushcaches" , GDB_REMOTE_COMMAND_HANDLER(FlushCaches) }, { "flushcaches" , GDB_REMOTE_COMMAND_HANDLER(FlushCaches) },
{ "toggleextmemaccess", GDB_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess) }, { "toggleextmemaccess", GDB_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess) },
{ "catchsvc" , GDB_REMOTE_COMMAND_HANDLER(CatchSvc) },
{ "getthreadpriority" , GDB_REMOTE_COMMAND_HANDLER(GetThreadPriority)}
}; };
static const char *GDB_SkipSpaces(const char *pos) static const char *GDB_SkipSpaces(const char *pos)
@ -34,6 +39,50 @@ static const char *GDB_SkipSpaces(const char *pos)
return nextpos; 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: %08lX\n", r);
goto end;
}
}
n = sprintf(outbuf, "va: 0x%08lX, pa: 0x%08lX, b31: 0x%08lX\n", val, pa, pa | (1 << 31));
end:
return GDB_SendHexPacket(ctx, outbuf, n);
}
GDB_DECLARE_REMOTE_COMMAND_HANDLER(SyncRequestInfo) GDB_DECLARE_REMOTE_COMMAND_HANDLER(SyncRequestInfo)
{ {
char outbuf[GDB_BUF_LEN / 2 + 1]; char outbuf[GDB_BUF_LEN / 2 + 1];
@ -118,6 +167,29 @@ end:
return GDB_SendHexPacket(ctx, outbuf, n); 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) GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle)
{ {
bool ok; bool ok;
@ -126,10 +198,11 @@ GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle)
int n; int n;
Result r; Result r;
u32 kernelAddr; u32 kernelAddr;
s64 token;
Handle handle, process; Handle handle, process;
s64 refcountRaw; s64 refcountRaw;
u32 refcount; u32 refcount;
char classBuf[32], serviceBuf[12] = { 0 }; char classBuf[32], serviceBuf[12] = {0}, ownerBuf[50] = { 0 };
char outbuf[GDB_BUF_LEN / 2 + 1]; char outbuf[GDB_BUF_LEN / 2 + 1];
if(ctx->commandData[0] == 0) if(ctx->commandData[0] == 0)
@ -161,12 +234,29 @@ GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle)
svcTranslateHandle(&kernelAddr, classBuf, handle); svcTranslateHandle(&kernelAddr, classBuf, handle);
svcGetHandleInfo(&refcountRaw, handle, 1); svcGetHandleInfo(&refcountRaw, handle, 1);
svcGetHandleInfo(&token, handle, 0x10001);
svcControlService(SERVICEOP_GET_NAME, serviceBuf, handle); svcControlService(SERVICEOP_GET_NAME, serviceBuf, handle);
refcount = (u32)(refcountRaw - 1); refcount = (u32)(refcountRaw - 1);
if(serviceBuf[0] != 0) if(serviceBuf[0] != 0)
n = sprintf(outbuf, "(%s *)0x%08lx /* %s handle, %lu %s */\n", classBuf, kernelAddr, serviceBuf, refcount, refcount == 1 ? "reference" : "references"); 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%08lx /* process: %s, %lu %s */\n", classBuf, kernelAddr, serviceBuf, refcount, refcount == 1 ? "reference" : "references");
}
else 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%08lx /* %lu %s%s */\n", classBuf, kernelAddr, refcount, refcount == 1 ? "reference" : "references", ownerBuf);
}
end: end:
svcCloseHandle(handle); svcCloseHandle(handle);
@ -174,6 +264,68 @@ end:
return GDB_SendHexPacket(ctx, outbuf, n); 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: %08lX\n", count);
else if (count == 0)
n = sprintf(outbuf, "Process has no handles ?\n");
else
{
n = sprintf(outbuf, "Found %ld 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%08lX", 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; extern bool isN3DS;
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig) GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig)
{ {
@ -249,6 +401,53 @@ GDB_DECLARE_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess)
return GDB_SendHexPacket(ctx, outbuf, n); 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);
}
s32 GDB_GetDynamicThreadPriority(GDBContext *ctx, u32 threadId);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetThreadPriority)
{
int n;
char outbuf[GDB_BUF_LEN / 2 + 1];
n = sprintf(outbuf, "Thread (%ld) priority: 0x%02lX\n", ctx->selectedThreadId,
GDB_GetDynamicThreadPriority(ctx, ctx->selectedThreadId));
return GDB_SendHexPacket(ctx, outbuf, n);
}
GDB_DECLARE_QUERY_HANDLER(Rcmd) GDB_DECLARE_QUERY_HANDLER(Rcmd)
{ {
char commandData[GDB_BUF_LEN / 2 + 1]; char commandData[GDB_BUF_LEN / 2 + 1];

View File

@ -107,7 +107,7 @@ int GDB_EncodeThreadId(GDBContext *ctx, char *outbuf, u32 tid)
return sprintf(outbuf, "%lx", tid); return sprintf(outbuf, "%lx", tid);
} }
static s32 GDB_GetDynamicThreadPriority(GDBContext *ctx, u32 threadId) s32 GDB_GetDynamicThreadPriority(GDBContext *ctx, u32 threadId)
{ {
Handle process, thread; Handle process, thread;
Result r; Result r;

View File

@ -32,6 +32,8 @@
#include "process_patches.h" #include "process_patches.h"
#include "menus.h" #include "menus.h"
#include "memory.h" #include "memory.h"
#include "sleep.h"
#include "sock_util.h"
bool inputRedirectionEnabled = false; bool inputRedirectionEnabled = false;
Handle inputRedirectionThreadStartedEvent; Handle inputRedirectionThreadStartedEvent;
@ -121,6 +123,13 @@ void inputRedirectionThreadMain(void)
pfd.events = POLLIN; pfd.events = POLLIN;
pfd.revents = 0; pfd.revents = 0;
if (Sleep__Status())
{
while (!Wifi__IsConnected()
&& inputRedirectionEnabled && !preTerminationRequested)
svcSleepThread(1000000000ULL);
}
int pollres = socPoll(&pfd, 1, 10); int pollres = socPoll(&pfd, 1, 10);
if(pollres > 0 && (pfd.revents & POLLIN)) if(pollres > 0 && (pfd.revents & POLLIN))
{ {
@ -214,7 +223,7 @@ static Result InputRedirection_DoUndoIrPatches(Handle processHandle, bool doPatc
totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize); totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize);
svcGetProcessInfo(&startAddress, processHandle, 0x10005); 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) if(R_SUCCEEDED(res) && !patchPrepared)
{ {
@ -238,7 +247,7 @@ static Result InputRedirection_DoUndoIrPatches(Handle processHandle, bool doPatc
u32 *off = (u32 *)memsearch((u8 *)0x00100000, &irOrigReadingCode, totalSize, sizeof(irOrigReadingCode) - 4); u32 *off = (u32 *)memsearch((u8 *)0x00100000, &irOrigReadingCode, totalSize, sizeof(irOrigReadingCode) - 4);
if(off == NULL) if(off == NULL)
{ {
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize); svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
return -1; return -1;
} }
@ -248,7 +257,7 @@ static Result InputRedirection_DoUndoIrPatches(Handle processHandle, bool doPatc
off2 = (u32 *)memsearch((u8 *)0x00100000, &irOrigWaitSyncCodeOld, totalSize, sizeof(irOrigWaitSyncCodeOld)); off2 = (u32 *)memsearch((u8 *)0x00100000, &irOrigWaitSyncCodeOld, totalSize, sizeof(irOrigWaitSyncCodeOld));
if(off2 == NULL) if(off2 == NULL)
{ {
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize); svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
return -2; return -2;
} }
} }
@ -256,7 +265,7 @@ static Result InputRedirection_DoUndoIrPatches(Handle processHandle, bool doPatc
u32 *off3 = (u32 *)memsearch((u8 *)0x00100000, &irOrigCppFlagCode, totalSize, sizeof(irOrigCppFlagCode)); u32 *off3 = (u32 *)memsearch((u8 *)0x00100000, &irOrigCppFlagCode, totalSize, sizeof(irOrigCppFlagCode));
if(off3 == NULL) if(off3 == NULL)
{ {
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize); svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
return -3; return -3;
} }
@ -315,7 +324,7 @@ static Result InputRedirection_DoUndoIrPatches(Handle processHandle, bool doPatc
} }
svcInvalidateEntireInstructionCache(); svcInvalidateEntireInstructionCache();
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize); svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
return res; return res;
} }
@ -347,28 +356,28 @@ static Result InputRedirection_DoUndoHidPatches(Handle processHandle, bool doPat
totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize); totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize);
svcGetProcessInfo(&startAddress, processHandle, 0x10005); 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) if (R_SUCCEEDED(res) && !patchPrepared)
{ {
u32 *off = (u32 *)memsearch((u8 *)0x00100000, &hidOrigRegisterAndValue, totalSize, sizeof(hidOrigRegisterAndValue)); u32 *off = (u32 *)memsearch((u8 *)0x00100000, &hidOrigRegisterAndValue, totalSize, sizeof(hidOrigRegisterAndValue));
if(off == NULL) if(off == NULL)
{ {
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize); svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
return -1; return -1;
} }
u32 *off2 = (u32 *)memsearch((u8 *)off + sizeof(hidOrigRegisterAndValue), &hidOrigRegisterAndValue, totalSize - ((u32)off - 0x00100000), sizeof(hidOrigRegisterAndValue)); u32 *off2 = (u32 *)memsearch((u8 *)off + sizeof(hidOrigRegisterAndValue), &hidOrigRegisterAndValue, totalSize - ((u32)off - 0x00100000), sizeof(hidOrigRegisterAndValue));
if(off2 == NULL) if(off2 == NULL)
{ {
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize); svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
return -2; return -2;
} }
u32 *off3 = (u32 *)memsearch((u8 *)0x00100000, &hidOrigCode, totalSize, sizeof(hidOrigCode)); u32 *off3 = (u32 *)memsearch((u8 *)0x00100000, &hidOrigCode, totalSize, sizeof(hidOrigCode));
if(off3 == NULL) if(off3 == NULL)
{ {
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize); svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
return -3; return -3;
} }
@ -404,7 +413,7 @@ static Result InputRedirection_DoUndoHidPatches(Handle processHandle, bool doPat
} }
} }
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize); svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
return res; return res;
} }

View File

@ -32,6 +32,7 @@
#include "config_template_ini.h" #include "config_template_ini.h"
#include "ifile.h" #include "ifile.h"
#include "menus/miscellaneous.h" #include "menus/miscellaneous.h"
#include "plugin/plgloader.h"
typedef struct CfgData { typedef struct CfgData {
u16 formatVersionMajor, formatVersionMinor; u16 formatVersionMajor, formatVersionMinor;
@ -41,6 +42,7 @@ typedef struct CfgData {
u64 hbldr3dsxTitleId; u64 hbldr3dsxTitleId;
u32 rosalinaMenuCombo; u32 rosalinaMenuCombo;
u32 pluginLoaderFlags;
s16 ntpTzOffetMinutes; s16 ntpTzOffetMinutes;
ScreenFilter topScreenFilter; ScreenFilter topScreenFilter;
@ -50,6 +52,8 @@ typedef struct CfgData {
u8 autobootCtrAppmemtype; u8 autobootCtrAppmemtype;
} CfgData; } CfgData;
bool saveSettingsRequest = false;
void LumaConfig_ConvertComboToString(char *out, u32 combo) void LumaConfig_ConvertComboToString(char *out, u32 combo)
{ {
static const char *keys[] = { static const char *keys[] = {
@ -170,7 +174,7 @@ static size_t LumaConfig_SaveLumaIniConfigToStr(char *out, const CfgData *cfg)
pinNumDigits, n3dsCpuStr, pinNumDigits, n3dsCpuStr,
autobootModeStr, autobootModeStr,
cfg->hbldr3dsxTitleId, rosalinaMenuComboStr, cfg->hbldr3dsxTitleId, rosalinaMenuComboStr, (int)(cfg->pluginLoaderFlags & 1),
(int)cfg->ntpTzOffetMinutes, (int)cfg->ntpTzOffetMinutes,
(int)cfg->topScreenFilter.cct, (int)cfg->bottomScreenFilter.cct, (int)cfg->topScreenFilter.cct, (int)cfg->bottomScreenFilter.cct,
@ -190,6 +194,10 @@ static size_t LumaConfig_SaveLumaIniConfigToStr(char *out, const CfgData *cfg)
return n < 0 ? 0 : (size_t)n; return n < 0 ? 0 : (size_t)n;
} }
void LumaConfig_RequestSaveSettings(void) {
saveSettingsRequest = true;
}
Result LumaConfig_SaveSettings(void) Result LumaConfig_SaveSettings(void)
{ {
char inibuf[0x2000]; char inibuf[0x2000];
@ -238,6 +246,7 @@ Result LumaConfig_SaveSettings(void)
configData.splashDurationMsec = splashDurationMsec; configData.splashDurationMsec = splashDurationMsec;
configData.hbldr3dsxTitleId = Luma_SharedConfig->selected_hbldr_3dsx_tid; configData.hbldr3dsxTitleId = Luma_SharedConfig->selected_hbldr_3dsx_tid;
configData.rosalinaMenuCombo = menuCombo; configData.rosalinaMenuCombo = menuCombo;
configData.pluginLoaderFlags = PluginLoader__IsEnabled();
configData.ntpTzOffetMinutes = (s16)lastNtpTzOffset; configData.ntpTzOffetMinutes = (s16)lastNtpTzOffset;
configData.topScreenFilter = topScreenFilter; configData.topScreenFilter = topScreenFilter;
configData.bottomScreenFilter = bottomScreenFilter; configData.bottomScreenFilter = bottomScreenFilter;

View File

@ -30,6 +30,7 @@
#include "service_manager.h" #include "service_manager.h"
#include "errdisp.h" #include "errdisp.h"
#include "utils.h" #include "utils.h"
#include "sleep.h"
#include "MyThread.h" #include "MyThread.h"
#include "menus/miscellaneous.h" #include "menus/miscellaneous.h"
#include "menus/debugger.h" #include "menus/debugger.h"
@ -43,6 +44,7 @@
#include "shell.h" #include "shell.h"
#include "task_runner.h" #include "task_runner.h"
#include "plugin.h"
bool isN3DS; bool isN3DS;
@ -63,6 +65,7 @@ void __wrap_exit(int rc)
// Kernel will take care of it all // Kernel will take care of it all
/* /*
pmDbgExit(); pmDbgExit();
acExit();
fsExit(); fsExit();
svcCloseHandle(*fsRegGetSessionHandle()); svcCloseHandle(*fsRegGetSessionHandle());
srvExit(); srvExit();
@ -161,6 +164,9 @@ static void handleSleepNotification(u32 notificationId)
static void handleShellNotification(u32 notificationId) static void handleShellNotification(u32 notificationId)
{ {
// Quick dirty fix
Sleep__HandleNotification(notificationId);
if (notificationId == 0x213) { if (notificationId == 0x213) {
// Shell opened // Shell opened
// Note that this notification is also fired on system init. // Note that this notification is also fired on system init.
@ -223,6 +229,7 @@ static void handleRestartHbAppNotification(u32 notificationId)
#endif #endif
static const ServiceManagerServiceEntry services[] = { static const ServiceManagerServiceEntry services[] = {
{ "plg:ldr", 1, PluginLoader__HandleCommands, true },
{ NULL }, { NULL },
}; };
@ -239,11 +246,16 @@ static const ServiceManagerNotificationEntry notifications[] = {
{ 0x214, handleShellNotification }, { 0x214, handleShellNotification },
{ 0x1000, handleNextApplicationDebuggedByForce }, { 0x1000, handleNextApplicationDebuggedByForce },
{ 0x2000, handlePreTermNotification }, { 0x2000, handlePreTermNotification },
{ 0x1001, PluginLoader__HandleKernelEvent },
{ 0x000, NULL }, { 0x000, NULL },
}; };
// Some changes to commit
int main(void) int main(void)
{ {
Sleep__Init();
PluginLoader__Init();
if(R_FAILED(svcCreateEvent(&preTerminationEvent, RESET_STICKY))) if(R_FAILED(svcCreateEvent(&preTerminationEvent, RESET_STICKY)))
svcBreak(USERBREAK_ASSERT); svcBreak(USERBREAK_ASSERT);

View File

@ -32,9 +32,11 @@
#include "ifile.h" #include "ifile.h"
#include "menus.h" #include "menus.h"
#include "utils.h" #include "utils.h"
#include "luma_config.h"
#include "menus/n3ds.h" #include "menus/n3ds.h"
#include "menus/cheats.h" #include "menus/cheats.h"
#include "minisoc.h" #include "minisoc.h"
#include "plugin.h"
#include "menus/screen_filters.h" #include "menus/screen_filters.h"
#include "shell.h" #include "shell.h"
@ -257,6 +259,9 @@ MyThread *menuCreateThread(void)
return &menuThread; return &menuThread;
} }
u32 menuCombo;
u32 g_blockMenuOpen = 0;
void menuThreadMain(void) void menuThreadMain(void)
{ {
if(isN3DS) if(isN3DS)
@ -279,13 +284,19 @@ void menuThreadMain(void)
Cheat_ApplyCheats(); Cheat_ApplyCheats();
if((scanHeldKeys() & menuCombo) == menuCombo) if(((scanHeldKeys() & menuCombo) == menuCombo) && !g_blockMenuOpen)
{ {
menuEnter(); menuEnter();
if(isN3DS) N3DSMenu_UpdateStatus(); if(isN3DS) N3DSMenu_UpdateStatus();
PluginLoader__UpdateMenu();
menuShow(&rosalinaMenu); menuShow(&rosalinaMenu);
menuLeave(); menuLeave();
} }
if (saveSettingsRequest) {
LumaConfig_SaveSettings();
saveSettingsRequest = false;
}
} }
} }

View File

@ -35,6 +35,7 @@
#include "menus/miscellaneous.h" #include "menus/miscellaneous.h"
#include "menus/sysconfig.h" #include "menus/sysconfig.h"
#include "menus/screen_filters.h" #include "menus/screen_filters.h"
#include "plugin.h"
#include "ifile.h" #include "ifile.h"
#include "memory.h" #include "memory.h"
#include "fmt.h" #include "fmt.h"
@ -48,6 +49,7 @@ Menu rosalinaMenu = {
{ "Take screenshot", METHOD, .method = &RosalinaMenu_TakeScreenshot }, { "Take screenshot", METHOD, .method = &RosalinaMenu_TakeScreenshot },
{ "Change screen brightness", METHOD, .method = &RosalinaMenu_ChangeScreenBrightness }, { "Change screen brightness", METHOD, .method = &RosalinaMenu_ChangeScreenBrightness },
{ "Cheats...", METHOD, .method = &RosalinaMenu_Cheats }, { "Cheats...", METHOD, .method = &RosalinaMenu_Cheats },
{ "", METHOD, .method = PluginLoader__MenuCallback},
{ "Process list", METHOD, .method = &RosalinaMenu_ProcessList }, { "Process list", METHOD, .method = &RosalinaMenu_ProcessList },
{ "Debugger options...", MENU, .menu = &debuggerMenu }, { "Debugger options...", MENU, .menu = &debuggerMenu },
{ "System configuration...", MENU, .menu = &sysconfigMenu }, { "System configuration...", MENU, .menu = &sysconfigMenu },

View File

@ -36,6 +36,7 @@
#include "minisoc.h" #include "minisoc.h"
#include "ifile.h" #include "ifile.h"
#include "pmdbgext.h" #include "pmdbgext.h"
#include "plugin.h"
#include "process_patches.h" #include "process_patches.h"
typedef struct DspFirmSegmentHeader { typedef struct DspFirmSegmentHeader {

View File

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

View File

@ -0,0 +1,173 @@
#include <3ds.h>
#include <string.h>
#include <stdio.h>
#include "plugin.h"
#include "ifile.h"
#include "utils.h"
u32 g_loadExeArgs[0x4];
static inline u32 invertEndianness(u32 val)
{
return ((val & 0xFF) << 24) | ((val & 0xFF00) << 8) | ((val & 0xFF0000) >> 8) | ((val & 0xFF000000) >> 24);
}
Result Check_3gx_Magic(IFile *file)
{
u64 magic;
u64 total;
Result res;
int verDif;
file->pos = 0;
if (R_FAILED((res = IFile_Read(file, &total, &magic, sizeof(u64)))))
return res;
if ((u32)magic != (u32)_3GX_MAGIC) //Invalid file type
return MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_LDR, 1);
else if ((verDif = invertEndianness((u32)(magic >> 32)) - invertEndianness((u32)(_3GX_MAGIC >> 32))) != 0) //Invalid plugin version (2 -> outdated plugin; 3 -> outdated loader)
return MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_LDR, (verDif < 0) ? 2 : 3);
else return 0;
}
Result Read_3gx_Header(IFile *file, _3gx_Header *header)
{
u64 total;
Result res = 0;
file->pos = 0;
res = IFile_Read(file, &total, header, sizeof(_3gx_Header));
return res;
}
Result Read_3gx_ParseHeader(IFile *file, _3gx_Header *header)
{
u64 total;
char * dst;
Result res = 0;
// 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_LoadSegments(IFile *file, _3gx_Header *header, void *dst)
{
u32 size;
u64 total;
Result res = 0;
_3gx_Executable *exeHdr = &header->executable;
PluginLoaderContext *ctx = &PluginLoaderCtx;
file->pos = exeHdr->codeOffset;
size = exeHdr->codeSize + exeHdr->rodataSize + exeHdr->dataSize;
res = IFile_Read(file, &total, dst, size);
if (!res && !ctx->isExeLoadFunctionset) return MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_LDR, RD_NO_DATA);
u32 checksum = 0;
if (!res) checksum = loadExeFunc(dst, dst + size, g_loadExeArgs);
if (!res && checksum != ctx->exeLoadChecksum) res = MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_LDR, RD_INVALID_ADDRESS);
Reset_3gx_LoadParams();
return res;
}
Result Read_3gx_EmbeddedPayloads(IFile *file, _3gx_Header *header)
{
u32 tempBuff[32];
u32 tempBuff2[4];
u64 total;
Result res = 0;
PluginLoaderContext *ctx = &PluginLoaderCtx;
if (header->infos.embeddedExeLoadFunc) {
file->pos = header->executable.exeLoadFuncOffset;
res = IFile_Read(file, &total, tempBuff, sizeof(tempBuff));
memcpy(tempBuff2, header->infos.builtInLoadExeArgs, sizeof(tempBuff2));
if (!res) res = Set_3gx_LoadParams(tempBuff, tempBuff2);
if (!res) ctx->isExeLoadFunctionset = true;
}
if (!res && header->infos.embeddedSwapSaveLoadFunc) {
file->pos = header->executable.swapSaveFuncOffset;
res = IFile_Read(file, &total, tempBuff, sizeof(tempBuff));
memcpy(tempBuff2, header->infos.builtInSwapSaveLoadArgs, sizeof(tempBuff2));
if (!res) res = MemoryBlock__SetSwapSettings(tempBuff, false, tempBuff2);
file->pos = header->executable.swapLoadFuncOffset;
if (!res) res = IFile_Read(file, &total, tempBuff, sizeof(tempBuff));
if (!res) res = MemoryBlock__SetSwapSettings(tempBuff, true, tempBuff2);
if (!res) ctx->isSwapFunctionset = true;
}
return res;
}
Result Set_3gx_LoadParams(u32* loadFunc, u32* params)
{
u32* loadExeFuncAddr = PA_FROM_VA_PTR((u32)loadExeFunc); //Bypass mem permissions
memcpy(g_loadExeArgs, params, sizeof(g_loadExeArgs));
int i = 0;
for (; i < 32 && loadFunc[i] != 0xE320F000; i++)
loadExeFuncAddr[i] = loadFunc[i];
if (i >= 32) {
return -1;
}
return 0;
}
void Reset_3gx_LoadParams(void)
{
PluginLoaderContext *ctx = &PluginLoaderCtx;
u32* loadExeFuncAddr = PA_FROM_VA_PTR((u32)loadExeFunc); //Bypass mem permissions
memset(g_loadExeArgs, 0, sizeof(g_loadExeArgs));
loadExeFuncAddr[0] = 0xE12FFF1E; // BX LR
for (int i = 1; i < 32; i++) {
loadExeFuncAddr[i] = 0xE320F000; // NOP
}
ctx->isExeLoadFunctionset = false;
svcInvalidateEntireInstructionCache();
}

View File

@ -0,0 +1,130 @@
#include <3ds.h>
#include "menu.h"
#include "draw.h"
#include <stdio.h>
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%08lX", 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;
}
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][0])
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

@ -0,0 +1,309 @@
#include <3ds.h>
#include <string.h>
#include <stdio.h>
#include "csvc.h"
#include "plugin.h"
#include "ifile.h"
#include "ifile.h"
#include "utils.h"
// Use a global to avoid stack overflow, those structs are quite heavy
static FS_DirectoryEntry g_entries[10];
static char g_path[256];
static const char *g_dirPath = "/luma/plugins/%016llX";
static const char *g_defaultPath = "/luma/plugins/default.3gx";
// pluginLoader.s
void gamePatchFunc(void);
static u32 strlen16(const u16 *str)
{
if (!str) return 0;
const u16 *strEnd = str;
while (*strEnd) ++strEnd;
return strEnd - str;
}
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 = g_entries;
memset(entries, 0, sizeof(g_entries));
sprintf(g_path, g_dirPath, 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);
PluginLoaderCtx.pluginPath = 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 OpenPluginFile(u64 tid, IFile *plugin)
{
if (R_FAILED(FindPluginFile(tid)) || OpenFile(plugin, g_path))
{
// Try to open default plugin
if (OpenFile(plugin, g_defaultPath))
return -1;
PluginLoaderCtx.pluginPath = g_defaultPath;
PluginLoaderCtx.header.isDefaultPlugin = 1;
return 0;
}
return 0;
}
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);
PluginLoaderCtx.error.message = errorBuf;
return -1;
}
bool TryToLoadPlugin(Handle process)
{
u64 tid;
u64 fileSize;
IFile plugin;
Result res;
_3gx_Header fileHeader;
_3gx_Header *header = NULL;
_3gx_Executable *exeHdr = NULL;
PluginLoaderContext *ctx = &PluginLoaderCtx;
PluginHeader *pluginHeader = &ctx->header;
const u32 memRegionSizes[] =
{
5 * 1024 * 1024, // 5 MiB
2 * 1024 * 1024, // 2 MiB
10 * 1024 * 1024, // 10 MiB
5 * 1024 * 1024, // 5 MiB (Reserved)
};
// Get title id
svcGetProcessInfo((s64 *)&tid, process, 0x10001);
memset(pluginHeader, 0, sizeof(PluginHeader));
pluginHeader->magic = HeaderMagic;
// Try to open plugin file
if (ctx->useUserLoadParameters && (u32)tid == ctx->userLoadParameters.lowTitleId)
{
ctx->useUserLoadParameters = false;
ctx->pluginMemoryStrategy = ctx->userLoadParameters.pluginMemoryStrategy;
if (OpenFile(&plugin, ctx->userLoadParameters.path))
return false;
ctx->pluginPath = ctx->userLoadParameters.path;
memcpy(pluginHeader->config, ctx->userLoadParameters.config, 32 * sizeof(u32));
}
else
{
if (R_FAILED(OpenPluginFile(tid, &plugin)))
return false;
}
if (R_FAILED((res = IFile_GetSize(&plugin, &fileSize))))
ctx->error.message = "Couldn't get file size";
if (!res && R_FAILED(res = Check_3gx_Magic(&plugin)))
{
const char * errors[] =
{
"Couldn't read file.",
"Invalid plugin file\nNot a valid 3GX plugin format!",
"Outdated plugin file\nCheck for an updated plugin.",
"Outdated plugin loader\nCheck for Luma3DS updates."
};
ctx->error.message = errors[R_MODULE(res) == RM_LDR ? R_DESCRIPTION(res) : 0];
}
// Read header
if (!res && R_FAILED((res = Read_3gx_Header(&plugin, &fileHeader))))
ctx->error.message = "Couldn't read file";
// Set memory region size according to header
if (!res && R_FAILED((res = MemoryBlock__SetSize(memRegionSizes[fileHeader.infos.memoryRegionSize])))) {
ctx->error.message = "Couldn't set memblock size.";
}
// Ensure memory block is mounted
if (!res && R_FAILED((res = MemoryBlock__IsReady())))
ctx->error.message = "Failed to allocate memory.";
// Plugins will not exceed 5MB so this is fine
if (!res) {
header = (_3gx_Header *)(ctx->memblock.memblock + g_memBlockSize - (u32)fileSize);
memcpy(header, &fileHeader, sizeof(_3gx_Header));
}
// Parse rest of header
if (!res && R_FAILED((res = Read_3gx_ParseHeader(&plugin, header))))
ctx->error.message = "Couldn't read file";
// Read embedded save/load functions
if (!res && R_FAILED((res = Read_3gx_EmbeddedPayloads(&plugin, header))))
ctx->error.message = "Invalid save/load payloads.";
// Save exe checksum
if (!res)
ctx->exeLoadChecksum = header->infos.exeLoadChecksum;
// Check titles compatibility
if (!res) res = CheckPluginCompatibility(header, (u32)tid);
// Read code
if (!res && R_FAILED(res = Read_3gx_LoadSegments(&plugin, header, ctx->memblock.memblock + sizeof(PluginHeader)))) {
if (res == MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_LDR, RD_NO_DATA)) ctx->error.message = "This plugin requires a loading function.";
else if (res == MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_LDR, RD_INVALID_ADDRESS)) ctx->error.message = "This plugin file is corrupted.";
else ctx->error.message = "Couldn't read plugin's code";
}
if (R_FAILED(res))
{
ctx->error.code = res;
goto exitFail;
}
pluginHeader->version = header->version;
// Code size must be page aligned
exeHdr = &header->executable;
pluginHeader->exeSize = (sizeof(PluginHeader) + exeHdr->codeSize + exeHdr->rodataSize + exeHdr->dataSize + exeHdr->bssSize + 0x1000) & ~0xFFF;
pluginHeader->heapVA = 0x06000000;
pluginHeader->heapSize = g_memBlockSize - pluginHeader->exeSize;
pluginHeader->plgldrEvent = ctx->plgEventPA;
pluginHeader->plgldrReply = ctx->plgReplyPA;
// Clear old event data
PLG__NotifyEvent(PLG_OK, false);
// Copy header to memblock
memcpy(ctx->memblock.memblock, pluginHeader, sizeof(PluginHeader));
// Clear heap
memset(ctx->memblock.memblock + pluginHeader->exeSize, 0, pluginHeader->heapSize);
// Enforce RWX mmu mapping
svcControlProcess(process, PROCESSOP_SET_MMU_TO_RWX, 0, 0);
// Ask the kernel to signal when the process is about to be terminated
svcControlProcess(process, PROCESSOP_SIGNAL_ON_EXIT, 0, 0);
// Mount the plugin memory in the process
if (R_SUCCEEDED(MemoryBlock__MountInProcess()))
// Install hook
{
u32 procStart = 0x00100000;
u32 *game = (u32 *)procStart;
extern u32 g_savedGameInstr[2];
if (R_FAILED((res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, procStart, process, procStart, 0x1000))))
{
ctx->error.message = "Couldn't map process";
ctx->error.code = res;
goto exitFail;
}
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);
svcFlushEntireDataCache();
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, procStart, 0x1000);
}
else
goto exitFail;
IFile_Close(&plugin);
return true;
exitFail:
IFile_Close(&plugin);
MemoryBlock__Free();
return false;
}

View File

@ -0,0 +1,281 @@
#include <3ds.h>
#include <string.h>
#include <stdio.h>
#include "plugin.h"
#include "ifile.h"
#include "utils.h"
#define MEMPERM_RW (MEMPERM_READ | MEMPERM_WRITE)
u32 g_loadSaveSwapArgs[4];
char g_swapFileName[256];
u32 g_memBlockSize = 5 * 1024 * 1024;
Result MemoryBlock__SetSize(u32 size) {
PluginLoaderContext *ctx = &PluginLoaderCtx;
MemoryBlock *memblock = &ctx->memblock;
if (memblock->isReady)
return MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, RM_LDR, RD_ALREADY_INITIALIZED);
g_memBlockSize = size;
return 0;
}
Result MemoryBlock__IsReady(void)
{
PluginLoaderContext *ctx = &PluginLoaderCtx;
MemoryBlock *memblock = &ctx->memblock;
if (memblock->isReady)
return 0;
Result res;
if (isN3DS || (ctx->pluginMemoryStrategy == PLG_STRATEGY_MODE3))
{
s64 appRegionSize = 0;
s64 appRegionUsed = 0;
u32 appRegionFree;
u32 gameReserveSize;
vu32* appMemAllocPtr = (vu32 *)PA_FROM_VA_PTR(0x1FF80040);
u32 appMemAlloc = *appMemAllocPtr;
u32 temp;
memblock->isAppRegion = true;
svcGetSystemInfo(&appRegionSize, 0x10000, 0x80);
svcGetSystemInfo(&appRegionUsed, 0, 1);
appRegionFree = appRegionSize - appRegionUsed;
// Check if appmemalloc reports the entire available memory
if ((u32)appRegionSize == appMemAlloc)
*appMemAllocPtr -= g_memBlockSize; ///< Remove plugin share from available memory
gameReserveSize = appRegionFree - g_memBlockSize;
// First reserve the game memory size (to avoid heap relocation)
res = svcControlMemoryUnsafe((u32 *)&temp, 0x30000000,
gameReserveSize, MEMOP_REGION_APP | MEMOP_ALLOC | MEMOP_LINEAR_FLAG, MEMPERM_RW);
// Then allocate our plugin memory block
if (R_SUCCEEDED(res))
res = svcControlMemoryUnsafe((u32 *)&memblock->memblock, 0x07000000,
g_memBlockSize, MEMOP_REGION_APP | MEMOP_ALLOC | MEMOP_LINEAR_FLAG, MEMPERM_RW);
// Finally release game reserve block
if (R_SUCCEEDED(res))
res = svcControlMemoryUnsafe((u32 *)&temp, temp, gameReserveSize, MEMOP_FREE, 0);
}
else
{
memblock->isAppRegion = false;
res = svcControlMemoryUnsafe((u32 *)&memblock->memblock, 0x07000000,
g_memBlockSize, MEMOP_REGION_SYSTEM | MEMOP_ALLOC | MEMOP_LINEAR_FLAG, MEMPERM_RW);
}
if (R_FAILED(res)) {
if (isN3DS || (ctx->pluginMemoryStrategy == PLG_STRATEGY_MODE3))
PluginLoader__Error("Cannot map plugin memory.", res);
else
PluginLoader__Error("A console reboot is needed to\nclose extended memory games.\n\nPress [B] to reboot.", res);
svcKernelSetState(7);
}
else
{
// Clear the memblock
memset(memblock->memblock, 0, g_memBlockSize);
memblock->isReady = true;
/*if (isN3DS)
{
// Check if appmemalloc reports the entire available memory
s64 appRegionSize = 0;
vu32* appMemAlloc = (vu32 *)PA_FROM_VA_PTR(0x1FF80040);
svcGetSystemInfo(&appRegionSize, 0x10000, 0x80);
if ((u32)appRegionSize == *appMemAlloc)
*appMemAlloc -= g_memBlockSize; ///< Remove plugin share from available memory
} */
}
return res;
}
Result MemoryBlock__Free(void)
{
MemoryBlock *memblock = &PluginLoaderCtx.memblock;
if (!memblock->isReady)
return 0;
MemOp memRegion = memblock->isAppRegion ? MEMOP_REGION_APP : MEMOP_REGION_SYSTEM;
Result res = svcControlMemoryUnsafe((u32 *)&memblock->memblock, (u32)memblock->memblock,
g_memBlockSize, memRegion | MEMOP_FREE, 0);
memblock->isReady = false;
memblock->memblock = NULL;
if (R_FAILED(res))
PluginLoader__Error("Couldn't free memblock", res);
return res;
}
#define FS_OPEN_RWC (FS_OPEN_READ | FS_OPEN_WRITE | FS_OPEN_CREATE)
Result MemoryBlock__ToSwapFile(void)
{
MemoryBlock *memblock = &PluginLoaderCtx.memblock;
PluginLoaderContext *ctx = &PluginLoaderCtx;
u64 written = 0;
u64 toWrite = g_memBlockSize;
IFile file;
Result res = 0;
svcFlushDataCacheRange(memblock->memblock, g_memBlockSize);
res = IFile_Open(&file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""),
fsMakePath(PATH_ASCII, g_swapFileName), FS_OPEN_RWC);
if (R_FAILED(res)) {
PluginLoader__Error("CRITICAL: Failed to open swap file.\n\nConsole will now reboot.", res);
svcKernelSetState(7);
}
if (!ctx->isSwapFunctionset) {
PluginLoader__Error("CRITICAL: Swap save function\nis not set.\n\nConsole will now reboot.", res);
svcKernelSetState(7);
}
ctx->swapLoadChecksum = saveSwapFunc(memblock->memblock, memblock->memblock + g_memBlockSize, g_loadSaveSwapArgs);
res = IFile_Write(&file, &written, memblock->memblock, toWrite, FS_WRITE_FLUSH);
if (R_FAILED(res) || written != toWrite) {
PluginLoader__Error("CRITICAL: Couldn't write swap to SD.\n\nConsole will now reboot.", res);
svcKernelSetState(7);
}
IFile_Close(&file);
return res;
}
Result MemoryBlock__FromSwapFile(void)
{
MemoryBlock *memblock = &PluginLoaderCtx.memblock;
u64 read = 0;
u64 toRead = g_memBlockSize;
IFile file;
Result res = 0;
res = IFile_Open(&file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""),
fsMakePath(PATH_ASCII, g_swapFileName), FS_OPEN_READ);
if (R_FAILED(res)) {
PluginLoader__Error("CRITICAL: Failed to open swap file.\n\nConsole will now reboot.", res);
svcKernelSetState(7);
}
res = IFile_Read(&file, &read, memblock->memblock, toRead);
if (R_FAILED(res) || read != toRead) {
PluginLoader__Error("CRITICAL: Couldn't read swap from SD.\n\nConsole will now reboot.", res);
svcKernelSetState(7);
}
u32 checksum = loadSwapFunc(memblock->memblock, memblock->memblock + g_memBlockSize, g_loadSaveSwapArgs);
PluginLoaderContext *ctx = &PluginLoaderCtx;
if (checksum != ctx->swapLoadChecksum) {
res = -1;
PluginLoader__Error("CRITICAL: Swap file is corrupted.\n\nConsole will now reboot.", res);
svcKernelSetState(7);
}
svcFlushDataCacheRange(memblock->memblock, g_memBlockSize);
IFile_Close(&file);
return res;
}
Result MemoryBlock__MountInProcess(void)
{
Handle target = PluginLoaderCtx.target;
Error *error = &PluginLoaderCtx.error;
PluginHeader *header = &PluginLoaderCtx.header;
MemoryBlock *memblock = &PluginLoaderCtx.memblock;
Result res = 0;
// Executable
if (R_FAILED((res = svcMapProcessMemoryEx(target, 0x07000000, CUR_PROCESS_HANDLE, (u32)memblock->memblock, header->exeSize))))
{
error->message = "Couldn't map exe memory block";
error->code = res;
return res;
}
// Heap (to be used by the plugin)
if (R_FAILED((res = svcMapProcessMemoryEx(target, header->heapVA, CUR_PROCESS_HANDLE, (u32)memblock->memblock + header->exeSize, header->heapSize))))
{
error->message = "Couldn't map heap memory block";
error->code = res;
}
return res;
}
Result MemoryBlock__UnmountFromProcess(void)
{
Handle target = PluginLoaderCtx.target;
PluginHeader *header = &PluginLoaderCtx.header;
Result res = 0;
res = svcUnmapProcessMemoryEx(target, 0x07000000, header->exeSize);
res |= svcUnmapProcessMemoryEx(target, header->heapVA, header->heapSize);
return res;
}
Result MemoryBlock__SetSwapSettings(u32* func, bool isLoad, u32* params)
{
u32* physAddr = PA_FROM_VA_PTR(isLoad ? (u32)loadSwapFunc : (u32)saveSwapFunc); //Bypass mem permissions
memcpy(g_loadSaveSwapArgs, params, sizeof(g_loadSaveSwapArgs));
int i = 0;
for (; i < 32 && func[i] != 0xE320F000; i++)
physAddr[i] = func[i];
if (i >= 32) {
return -1;
}
return 0;
}
void MemoryBlock__ResetSwapSettings(void)
{
u32* savePhysAddr = PA_FROM_VA_PTR((u32)saveSwapFunc); //Bypass mem permissions
u32* loadPhysAddr = PA_FROM_VA_PTR((u32)loadSwapFunc);
PluginLoaderContext *ctx = &PluginLoaderCtx;
memset(g_loadSaveSwapArgs, 0, sizeof(g_loadSaveSwapArgs));
savePhysAddr[0] = loadPhysAddr[0] = 0xE12FFF1E; // BX LR
for (int i = 1; i < 32; i++) {
savePhysAddr[i] = loadPhysAddr[i] = 0xE320F000; // NOP
}
strcpy(g_swapFileName, "/luma/plugins/.swap");
ctx->isSwapFunctionset = false;
svcInvalidateEntireInstructionCache();
}
PluginHeader* MemoryBlock__GetMappedPluginHeader() {
MemoryBlock *memblock = &PluginLoaderCtx.memblock;
return memblock->isReady ? NULL : (PluginHeader*)memblock->memblock;
}

View File

@ -0,0 +1,339 @@
#include <3ds.h>
#include "plugin.h"
#include <string.h>
#include "csvc.h"
static Handle plgLdrHandle;
static Handle plgLdrArbiter;
static s32* plgEvent;
static s32* plgReply;
static int plgLdrRefCount;
static bool plgIsN3DS;
static OnPlgLdrEventCb_t onPlgLdrEventCb = NULL;
static Result PLGLDR__GetArbiter(void)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(9, 0, 0);
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
plgLdrArbiter = cmdbuf[3];
}
return res;
}
Result plgLdrInit(void)
{
s64 out = 0;
Result res = 0;
svcGetSystemInfo(&out, 0x10000, 0x201);
plgIsN3DS = out != 0;
if (AtomicPostIncrement(&plgLdrRefCount) == 0)
res = svcConnectToPort(&plgLdrHandle, "plg:ldr");
if (R_SUCCEEDED(res)
&& R_SUCCEEDED((res = PLGLDR__GetArbiter())))
{
PluginHeader *header = (PluginHeader *)0x07000000;
plgEvent = header->plgldrEvent;
plgReply = header->plgldrReply;
}
else
plgLdrExit();
return res;
}
void plgLdrExit(void)
{
if (AtomicDecrement(&plgLdrRefCount))
return;
if (plgLdrHandle)
svcCloseHandle(plgLdrHandle);
if (plgLdrArbiter)
svcCloseHandle(plgLdrArbiter);
plgLdrHandle = plgLdrArbiter = 0;
}
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 | (((u32)parameters->pluginMemoryStrategy) << 8);
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;
}
void Flash(u32 color);
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];
}
else Flash(0xFF0000);
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;
}
Result PLGLDR__GetVersion(u32 *version)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(8, 0, 0);
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
if (cmdbuf[0] != IPC_MakeHeader(8, 2, 0))
return 0xD900182F;
res = cmdbuf[1];
if (version)
*version = cmdbuf[2];
}
return res;
}
Result PLGLDR__GetPluginPath(char *path)
{
if (path == NULL)
return MAKERESULT(28, 7, 254, 1014); ///< Usage, App, Invalid argument
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(10, 0, 2);
cmdbuf[1] = IPC_Desc_Buffer(255, IPC_BUFFER_RW);
cmdbuf[2] = (u32)path;
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}
Result PLGLDR__SetRosalinaMenuBlock(bool shouldBlock)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(11, 1, 0);
cmdbuf[1] = (u32)shouldBlock;
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}
Result PLGLDR__SetSwapSettings(char* swapPath, void* saveFunc, void* loadFunc, void* args)
{
Result res = 0;
const char* trueSwapPath;
if (swapPath) trueSwapPath = swapPath;
else trueSwapPath = "\0";
u32 buf[4] = { 0 };
u32* trueArgs;
if (args) trueArgs = args;
else trueArgs = buf;
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(12, 2, 4);
cmdbuf[1] = (saveFunc) ? svcConvertVAToPA(saveFunc, false) | (1 << 31) : 0;
cmdbuf[2] = (loadFunc) ? svcConvertVAToPA(loadFunc, false) | (1 << 31) : 0;
cmdbuf[3] = IPC_Desc_Buffer(4 * sizeof(u32), IPC_BUFFER_R);
cmdbuf[4] = (u32)trueArgs;
cmdbuf[5] = IPC_Desc_Buffer(strlen(trueSwapPath) + 1, IPC_BUFFER_R);
cmdbuf[6] = (u32)trueSwapPath;
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}
Result PLGLDR__SetExeLoadSettings(void* loadFunc, void* args)
{
Result res = 0;
u32 buf[4] = { 0 };
u32* trueArgs;
if (args) trueArgs = args;
else trueArgs = buf;
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(13, 1, 2);
cmdbuf[1] = (loadFunc) ? svcConvertVAToPA(loadFunc, false) | (1 << 31) : 0;
cmdbuf[2] = IPC_Desc_Buffer(4 * sizeof(u32), IPC_BUFFER_R);
cmdbuf[3] = (u32)trueArgs;
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}
void PLGLDR__SetEventCallback(OnPlgLdrEventCb_t cb)
{
onPlgLdrEventCb = cb;
}
static s32 __ldrex__(s32 *addr)
{
s32 val;
do
val = __ldrex(addr);
while (__strex(addr, val));
return val;
}
static void __strex__(s32 *addr, s32 val)
{
do
__ldrex(addr);
while (__strex(addr, val));
}
void PLGLDR__Status(void)
{
s32 event = __ldrex__(plgEvent); // exclusive read necessary?
if (event <= 0)
return;
if (onPlgLdrEventCb)
onPlgLdrEventCb(event);
__strex__(plgReply, PLG_OK);
__strex__(plgEvent, PLG_WAIT);
if (event < PLG_ABOUT_TO_SWAP)
return;
svcArbitrateAddress(plgLdrArbiter, (u32)plgReply, ARBITRATION_SIGNAL, 1, 0);
if (event == PLG_ABOUT_TO_SWAP)
svcArbitrateAddress(plgLdrArbiter, (u32)plgEvent, ARBITRATION_WAIT_IF_LESS_THAN, PLG_OK, 0);
else if (event == PLG_ABOUT_TO_EXIT)
{
plgLdrExit();
svcExitThread();
}
}

View File

@ -0,0 +1,593 @@
#include <3ds.h>
#include "ifile.h"
#include "utils.h" // for makeARMBranch
#include "luma_config.h"
#include "plugin.h"
#include "fmt.h"
#include "menu.h"
#include "menus.h"
#include "memory.h"
#include "sleep.h"
#include "task_runner.h"
#define PLGLDR_VERSION (SYSTEM_VERSION(1, 0, 1))
#define THREADVARS_MAGIC 0x21545624 // !TV$
static const char *g_title = "Plugin loader";
PluginLoaderContext PluginLoaderCtx;
extern u32 g_blockMenuOpen;
void IR__Patch(void);
void IR__Unpatch(void);
void PluginLoader__Init(void)
{
PluginLoaderContext *ctx = &PluginLoaderCtx;
memset(ctx, 0, sizeof(PluginLoaderContext));
s64 pluginLoaderFlags = 0;
svcGetSystemInfo(&pluginLoaderFlags, 0x10000, 0x180);
ctx->isEnabled = pluginLoaderFlags & 1;
ctx->plgEventPA = (s32 *)PA_FROM_VA_PTR(&ctx->plgEvent);
ctx->plgReplyPA = (s32 *)PA_FROM_VA_PTR(&ctx->plgReply);
ctx->pluginMemoryStrategy = PLG_STRATEGY_SWAP;
MemoryBlock__ResetSwapSettings();
assertSuccess(svcCreateAddressArbiter(&ctx->arbiter));
assertSuccess(svcCreateEvent(&ctx->kernelEvent, RESET_ONESHOT));
svcKernelSetState(0x10007, ctx->kernelEvent, 0, 0);
}
void PluginLoader__Error(const char *message, Result res)
{
DispErrMessage(g_title, message, res);
}
bool PluginLoader__IsEnabled(void)
{
return PluginLoaderCtx.isEnabled;
}
void PluginLoader__MenuCallback(void)
{
PluginLoaderCtx.isEnabled = !PluginLoaderCtx.isEnabled;
LumaConfig_RequestSaveSettings();
PluginLoader__UpdateMenu();
}
void PluginLoader__UpdateMenu(void)
{
static const char *status[2] =
{
"Plugin Loader: [Disabled]",
"Plugin Loader: [Enabled]"
};
rosalinaMenu.items[3].title = status[PluginLoaderCtx.isEnabled];
}
static ControlApplicationMemoryModeOverrideConfig g_memorymodeoverridebackup = { 0 };
Result PluginLoader__SetMode3AppMode(bool enable)
{
Handle loaderHandle;
Result res = srvGetServiceHandle(&loaderHandle, "Loader");
if (R_FAILED(res)) return res;
u32 *cmdbuf = getThreadCommandBuffer();
if (enable) {
ControlApplicationMemoryModeOverrideConfig* mode = (ControlApplicationMemoryModeOverrideConfig*)&cmdbuf[1];
memset(mode, 0, sizeof(ControlApplicationMemoryModeOverrideConfig));
mode->query = true;
cmdbuf[0] = IPC_MakeHeader(0x101, 1, 0); // ControlApplicationMemoryModeOverride
if (R_SUCCEEDED((res = svcSendSyncRequest(loaderHandle))) && R_SUCCEEDED(res = cmdbuf[1]))
{
memcpy(&g_memorymodeoverridebackup, &cmdbuf[2], sizeof(ControlApplicationMemoryModeOverrideConfig));
memset(mode, 0, sizeof(ControlApplicationMemoryModeOverrideConfig));
mode->enable_o3ds = true;
mode->o3ds_mode = SYSMODE_DEV2;
cmdbuf[0] = IPC_MakeHeader(0x101, 1, 0); // ControlApplicationMemoryModeOverride
if (R_SUCCEEDED((res = svcSendSyncRequest(loaderHandle)))) {
res = cmdbuf[1];
}
}
} else {
ControlApplicationMemoryModeOverrideConfig* mode = (ControlApplicationMemoryModeOverrideConfig*)&cmdbuf[1];
*mode = g_memorymodeoverridebackup;
cmdbuf[0] = IPC_MakeHeader(0x101, 1, 0); // ControlApplicationMemoryModeOverride
if (R_SUCCEEDED((res = svcSendSyncRequest(loaderHandle)))) {
res = cmdbuf[1];
}
}
svcCloseHandle(loaderHandle);
return res;
}
static void j_PluginLoader__SetMode3AppMode(void* arg) {(void)arg; PluginLoader__SetMode3AppMode(false);}
void CheckMemory(void);
void PLG__NotifyEvent(PLG_Event event, bool signal);
void PluginLoader__HandleCommands(void *_ctx)
{
(void)_ctx;
u32 *cmdbuf = getThreadCommandBuffer();
PluginLoaderContext *ctx = &PluginLoaderCtx;
switch (cmdbuf[0] >> 16)
{
case 1: // Load plugin
{
if (cmdbuf[0] != IPC_MakeHeader(1, 1, 0))
{
error(cmdbuf, 0xD9001830);
break;
}
ctx->plgEvent = PLG_OK;
svcOpenProcess(&ctx->target, cmdbuf[1]);
if (ctx->useUserLoadParameters && ctx->userLoadParameters.pluginMemoryStrategy == PLG_STRATEGY_MODE3)
TaskRunner_RunTask(j_PluginLoader__SetMode3AppMode, NULL, 0);
bool flash = !(ctx->useUserLoadParameters && ctx->userLoadParameters.noFlash);
if (ctx->isEnabled && TryToLoadPlugin(ctx->target))
{
if (flash)
{
// A little flash to notify the user that the plugin is loaded
for (u32 i = 0; i < 64; i++)
{
REG32(0x10202204) = 0x01FF9933;
svcSleepThread(5000000);
}
REG32(0x10202204) = 0;
}
//if (!ctx->userLoadParameters.noIRPatch)
// IR__Patch();
PLG__SetConfigMemoryStatus(PLG_CFG_RUNNING);
}
else
{
svcCloseHandle(ctx->target);
ctx->target = 0;
}
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)ctx->isEnabled;
break;
}
case 3: // Enable / Disable plugin loader
{
if (cmdbuf[0] != IPC_MakeHeader(3, 1, 0))
{
error(cmdbuf, 0xD9001830);
break;
}
if (cmdbuf[1] != ctx->isEnabled)
{
ctx->isEnabled = cmdbuf[1];
LumaConfig_RequestSaveSettings();
PluginLoader__UpdateMenu();
}
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;
}
PluginLoadParameters *params = &ctx->userLoadParameters;
ctx->useUserLoadParameters = true;
params->noFlash = cmdbuf[1] & 0xFF;
params->pluginMemoryStrategy = (cmdbuf[1] >> 8) & 0xFF;
params->lowTitleId = cmdbuf[2];
strncpy(params->path, (const char *)cmdbuf[4], 255);
memcpy(params->config, (void *)cmdbuf[6], 32 * sizeof(u32));
if (params->pluginMemoryStrategy == PLG_STRATEGY_MODE3)
cmdbuf[1] = PluginLoader__SetMode3AppMode(true);
else
cmdbuf[1] = 0;
cmdbuf[0] = IPC_MakeHeader(4, 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;
}
case 8: // Get PLGLDR Version
{
if (cmdbuf[0] != IPC_MakeHeader(8, 0, 0))
{
error(cmdbuf, 0xD9001830);
break;
}
cmdbuf[0] = IPC_MakeHeader(8, 2, 0);
cmdbuf[1] = 0;
cmdbuf[2] = PLGLDR_VERSION;
break;
}
case 9: // Get the arbiter (events)
{
if (cmdbuf[0] != IPC_MakeHeader(9, 0, 0))
{
error(cmdbuf, 0xD9001830);
break;
}
cmdbuf[0] = IPC_MakeHeader(9, 1, 2);
cmdbuf[1] = 0;
cmdbuf[2] = IPC_Desc_SharedHandles(1);
cmdbuf[3] = ctx->arbiter;
break;
}
case 10: // Get plugin path
{
if (cmdbuf[0] != IPC_MakeHeader(10, 0, 2))
{
error(cmdbuf, 0xD9001830);
break;
}
char *path = (char *)cmdbuf[2];
strncpy(path, ctx->pluginPath, 255);
cmdbuf[0] = IPC_MakeHeader(10, 1, 2);
cmdbuf[1] = 0;
cmdbuf[2] = IPC_Desc_Buffer(255, IPC_BUFFER_RW);
cmdbuf[3] = (u32)path;
break;
}
case 11: // Set rosalina menu block
{
if (cmdbuf[0] != IPC_MakeHeader(11, 1, 0))
{
error(cmdbuf, 0xD9001830);
break;
}
g_blockMenuOpen = cmdbuf[1];
cmdbuf[0] = IPC_MakeHeader(11, 1, 0);
cmdbuf[1] = 0;
break;
}
case 12: // Set swap settings
{
if (cmdbuf[0] != IPC_MakeHeader(12, 2, 4))
{
error(cmdbuf, 0xD9001830);
break;
}
cmdbuf[0] = IPC_MakeHeader(12, 1, 0);
MemoryBlock__ResetSwapSettings();
if (!cmdbuf[1] || !cmdbuf[2]) {
cmdbuf[1] = MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_LDR, RD_INVALID_ADDRESS);
break;
}
u32* remoteSavePhysAddr = (u32*)(cmdbuf[1] | (1 << 31));
u32* remoteLoadPhysAddr = (u32*)(cmdbuf[2] | (1 << 31));
Result ret = MemoryBlock__SetSwapSettings(remoteSavePhysAddr, false, (u32*)cmdbuf[4]);
if (!ret) ret = MemoryBlock__SetSwapSettings(remoteLoadPhysAddr, true, (u32*)cmdbuf[4]);
if (ret) {
cmdbuf[1] = MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_LDR, RD_TOO_LARGE);
MemoryBlock__ResetSwapSettings();
break;
}
ctx->isSwapFunctionset = true;
if (((char*)cmdbuf[6])[0] != '\0') strncpy(g_swapFileName, (char*)cmdbuf[6], 255);
svcInvalidateEntireInstructionCache(); // Could use the range one
cmdbuf[1] = 0;
break;
}
case 13: // Set plugin exe load func
{
if (cmdbuf[0] != IPC_MakeHeader(13, 1, 2))
{
error(cmdbuf, 0xD9001830);
break;
}
cmdbuf[0] = IPC_MakeHeader(13, 1, 0);
Reset_3gx_LoadParams();
if (!cmdbuf[1]) {
cmdbuf[1] = MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_LDR, RD_INVALID_ADDRESS);
break;
}
u32* remoteLoadExeFuncAddr = (u32*)(cmdbuf[1] | (1 << 31));
Result ret = Set_3gx_LoadParams(remoteLoadExeFuncAddr, (u32*)cmdbuf[3]);
if (ret)
{
cmdbuf[1] = MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_LDR, RD_TOO_LARGE);
Reset_3gx_LoadParams();
break;
}
ctx->isExeLoadFunctionset = true;
svcInvalidateEntireInstructionCache(); // Could use the range one
cmdbuf[1] = 0;
break;
}
default: // Unknown command
{
error(cmdbuf, 0xD900182F);
break;
}
}
if (ctx->error.message)
{
PluginLoader__Error(ctx->error.message, ctx->error.code);
ctx->error.message = NULL;
ctx->error.code = 0;
}
}
static bool ThreadPredicate(u32 *kthread)
{
// Check if the thread is part of the plugin
u32 *tls = (u32 *)kthread[0x26];
return *tls == THREADVARS_MAGIC;
}
static void __strex__(s32 *addr, s32 val)
{
do
__ldrex(addr);
while (__strex(addr, val));
}
void PLG__NotifyEvent(PLG_Event event, bool signal)
{
if (!PluginLoaderCtx.plgEventPA) return;
__strex__(PluginLoaderCtx.plgEventPA, event);
if (signal)
svcArbitrateAddress(PluginLoaderCtx.arbiter, (u32)PluginLoaderCtx.plgEventPA, ARBITRATION_SIGNAL, 1, 0);
}
void PLG__WaitForReply(void)
{
__strex__(PluginLoaderCtx.plgReplyPA, PLG_WAIT);
svcArbitrateAddress(PluginLoaderCtx.arbiter, (u32)PluginLoaderCtx.plgReplyPA, ARBITRATION_WAIT_IF_LESS_THAN_TIMEOUT, PLG_OK, 10000000000ULL);
}
void PLG__SetConfigMemoryStatus(u32 status)
{
*(vu32 *)PA_FROM_VA_PTR(0x1FF800F0) = status;
}
u32 PLG__GetConfigMemoryStatus(void)
{
return (*(vu32 *)PA_FROM_VA_PTR((u32 *)0x1FF800F0)) & 0xFFFF;
}
u32 PLG__GetConfigMemoryEvent(void)
{
return (*(vu32 *)PA_FROM_VA_PTR(0x1FF800F0)) & ~0xFFFF;
}
static void WaitForProcessTerminated(void *arg)
{
(void)arg;
PluginLoaderContext *ctx = &PluginLoaderCtx;
// Wait until all threads of the process have finished (svcWaitSynchronization == 0) or 5 seconds have passed.
for (u32 i = 0; svcWaitSynchronization(ctx->target, 0) != 0 && i < 100; i++) svcSleepThread(50000000); // 50ms
// Unmap plugin's memory before closing the process
if (!ctx->pluginIsSwapped) {
MemoryBlock__UnmountFromProcess();
MemoryBlock__Free();
}
// Terminate process
svcCloseHandle(ctx->target);
// Reset plugin loader state
PLG__SetConfigMemoryStatus(PLG_CFG_NONE);
ctx->pluginIsSwapped = false;
ctx->pluginIsHome = false;
ctx->target = 0;
ctx->isExeLoadFunctionset = false;
ctx->isSwapFunctionset = false;
ctx->pluginMemoryStrategy = PLG_STRATEGY_SWAP;
g_blockMenuOpen = 0;
MemoryBlock__ResetSwapSettings();
//if (!ctx->userLoadParameters.noIRPatch)
// IR__Unpatch();
}
void PluginLoader__HandleKernelEvent(u32 notifId)
{
(void)notifId;
if (PLG__GetConfigMemoryStatus() == PLG_CFG_EXITING)
{
srvPublishToSubscriber(0x1002, 0);
return;
}
PluginLoaderContext *ctx = &PluginLoaderCtx;
u32 event = PLG__GetConfigMemoryEvent();
if (event == PLG_CFG_EXIT_EVENT)
{
PLG__SetConfigMemoryStatus(PLG_CFG_EXITING);
if (!ctx->pluginIsSwapped)
{
// Signal the plugin that the game is exiting
PLG__NotifyEvent(PLG_ABOUT_TO_EXIT, false);
// Wait for plugin reply
PLG__WaitForReply();
}
// Start a task to wait for process to be terminated
TaskRunner_RunTask(WaitForProcessTerminated, NULL, 0);
}
else if (event == PLG_CFG_HOME_EVENT)
{
if ((ctx->pluginMemoryStrategy == PLG_STRATEGY_SWAP) && !isN3DS) {
if (ctx->pluginIsSwapped)
{
// Reload data from swap file
MemoryBlock__IsReady();
MemoryBlock__FromSwapFile();
MemoryBlock__MountInProcess();
// Unlock plugin threads
svcControlProcess(ctx->target, PROCESSOP_SCHEDULE_THREADS, 0, (u32)ThreadPredicate);
// Resume plugin execution
PLG__NotifyEvent(PLG_OK, true);
PLG__SetConfigMemoryStatus(PLG_CFG_RUNNING);
}
else
{
// Signal plugin that it's about to be swapped
PLG__NotifyEvent(PLG_ABOUT_TO_SWAP, false);
// Wait for plugin reply
PLG__WaitForReply();
// Lock plugin threads
svcControlProcess(ctx->target, PROCESSOP_SCHEDULE_THREADS, 1, (u32)ThreadPredicate);
// Put data into file and release memory
MemoryBlock__UnmountFromProcess();
MemoryBlock__ToSwapFile();
MemoryBlock__Free();
PLG__SetConfigMemoryStatus(PLG_CFG_INHOME);
}
ctx->pluginIsSwapped = !ctx->pluginIsSwapped;
} else {
// Needed for compatibility with old plugins that don't expect the PLG_HOME events.
volatile PluginHeader* mappedHeader = PA_FROM_VA_PTR(MemoryBlock__GetMappedPluginHeader());
bool doNotification = mappedHeader ? mappedHeader->notifyHomeEvent : false;
if (ctx->pluginIsHome)
{
if (doNotification) {
// Signal plugin that it's about to exit home menu
PLG__NotifyEvent(PLG_HOME_EXIT, false);
// Wait for plugin reply
PLG__WaitForReply();
}
PLG__SetConfigMemoryStatus(PLG_CFG_RUNNING);
}
else
{
if (doNotification) {
// Signal plugin that it's about to enter home menu
PLG__NotifyEvent(PLG_HOME_ENTER, false);
// Wait for plugin reply
PLG__WaitForReply();
}
PLG__SetConfigMemoryStatus(PLG_CFG_INHOME);
}
ctx->pluginIsHome = !ctx->pluginIsHome;
}
}
srvPublishToSubscriber(0x1002, 0);
}

View File

@ -0,0 +1,36 @@
.section .data
.balign 4
.arm
.global gamePatchFunc
.type gamePatchFunc, %function
gamePatchFunc:
stmfd sp!, {r0-r12}
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:
adr r0, g_savedGameInstr
push {r0}
ldr r5, =0x07000100
blx r5
add sp, sp, #4
exit:
ldmfd sp!, {r0}
msr cpsr, r0
ldmfd sp!, {r0-r12}
ldr lr, =0x00100000
mov pc, lr
.global g_savedGameInstr
g_savedGameInstr:
.word 0, 0

View File

@ -0,0 +1,111 @@
.section .text
.balign 4
.arm
.global saveSwapFunc
.type saveSwapFunc, %function
saveSwapFunc:
BX LR
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
.global loadSwapFunc
.type loadSwapFunc, %function
loadSwapFunc:
BX LR
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
.global loadExeFunc
.type loadExeFunc, %function
loadExeFunc:
BX LR
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP

View File

@ -77,7 +77,7 @@ Result OperateOnProcessByName(const char *name, OperateOnProcessCb func)
// NOTE: we suppose .text, .rodata, .data+.bss are contiguous & in that order // NOTE: we suppose .text, .rodata, .data+.bss are contiguous & in that order
u32 totalSize = (u32)(textSize + roSize + rwSize); u32 totalSize = (u32)(textSize + roSize + rwSize);
if (R_FAILED(res = svcMapProcessMemoryEx(processHandle, 0x00100000, (u32) startAddress, totalSize))) if (R_FAILED(res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, processHandle, (u32) startAddress, totalSize)))
{ {
svcCloseHandle(processHandle); svcCloseHandle(processHandle);
return res; return res;
@ -85,6 +85,6 @@ Result OperateOnProcessByName(const char *name, OperateOnProcessCb func)
res = func(processHandle, (u32)textSize, (u32)roSize, (u32)rwSize); res = func(processHandle, (u32)textSize, (u32)roSize, (u32)rwSize);
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize); svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
return res; return res;
} }

View File

@ -0,0 +1,76 @@
/*
* 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>
#include "plugin/plgldr.h"
#include "plugin/plgloader.h"
static bool g_isSleeping = false;
static LightEvent g_onWakeUpEvent;
void IR__HandleSleep(bool isSleeping); ///< See in input_redirection.c
void Sleep__Init(void)
{
// No need anymore, handled by ServiceManager
// 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;
// IR patch creates sleep issue, so we need to handle it
// IR__HandleSleep(g_isSleeping);
// Plugins do not receive 0x214 notifications, so we send it via our custom service
if (PLG__GetConfigMemoryStatus() == PLG_CFG_RUNNING)
PLG__NotifyEvent(PLG_SLEEP_ENTRY, false);
}
else if (notifId == 0x213) ///< Sleep exit
{
g_isSleeping = false;
LightEvent_Signal(&g_onWakeUpEvent);
// IR patch creates sleep issue, so we need to handle it
// IR__HandleSleep(g_isSleeping);
// Plugins actually receives 0x213 notifications, but since we send sleep entry, let's do sleep exit as well
if (PLG__GetConfigMemoryStatus() == PLG_CFG_RUNNING)
PLG__NotifyEvent(PLG_SLEEP_EXIT, false);
}
}
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/result.h>
#include <3ds/svc.h> #include <3ds/svc.h>
#include <3ds/synchronization.h> #include <3ds/synchronization.h>
#include <3ds/services/ac.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include "memory.h" #include "memory.h"
#include "minisoc.h" #include "minisoc.h"
#include "sock_util.h" #include "sock_util.h"
#include "sleep.h"
extern Handle preTerminationEvent; extern Handle preTerminationEvent;
extern bool preTerminationRequested; extern bool preTerminationRequested;
@ -185,6 +187,14 @@ void server_run(struct sock_server *serv)
for(nfds_t i = 0; i < serv->nfds; i++) for(nfds_t i = 0; i < serv->nfds; i++)
fds[i].revents = 0; fds[i].revents = 0;
if (Sleep__Status())
{
while (!Wifi__IsConnected()
&& serv->running && !preTerminationRequested)
svcSleepThread(1000000000ULL);
}
int pollres = socPoll(fds, serv->nfds, 50); int pollres = socPoll(fds, serv->nfds, 50);
if(server_should_exit(serv) || pollres < -10000) if(server_should_exit(serv) || pollres < -10000)
@ -321,3 +331,13 @@ void server_finalize(struct sock_server *serv)
svcClearEvent(serv->started_event); svcClearEvent(serv->started_event);
svcCloseHandle(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;
}