diff --git a/arm9/data/config_template.ini b/arm9/data/config_template.ini index 3e7158b..9b521fa 100644 Binary files a/arm9/data/config_template.ini and b/arm9/data/config_template.ini differ diff --git a/arm9/source/config.c b/arm9/source/config.c index d488112..ab2befc 100644 --- a/arm9/source/config.c +++ b/arm9/source/config.c @@ -62,6 +62,7 @@ static const char *singleOptionIniNamesBoot[] = { "use_emunand_firm_if_r_pressed", "enable_external_firm_and_modules", "enable_game_patching", + "app_syscore_threads_on_core_2", "show_system_settings_string", "show_gba_boot_screen", }; @@ -544,7 +545,8 @@ static size_t saveLumaIniConfigToStr(char *out) (int)CONFIG_VERSIONMAJOR, (int)CONFIG_VERSIONMINOR, (int)CONFIG(AUTOBOOTEMU), (int)CONFIG(USEEMUFIRM), (int)CONFIG(LOADEXTFIRMSANDMODULES), (int)CONFIG(PATCHGAMES), - (int)CONFIG(PATCHVERSTRING), (int)CONFIG(SHOWGBABOOT), + (int)CONFIG(REDIRECTAPPTHREADS), (int)CONFIG(PATCHVERSTRING), + (int)CONFIG(SHOWGBABOOT), 1 + (int)MULTICONFIG(DEFAULTEMU), 4 - (int)MULTICONFIG(BRIGHTNESS), splashPosStr, (unsigned int)cfg->splashDurationMsec, @@ -716,6 +718,7 @@ void configMenu(bool oldPinStatus, u32 oldPinMode) "( ) Use EmuNAND FIRM if booting with R", "( ) Enable loading external FIRMs and modules", "( ) Enable game patching", + "( ) Redirect app. syscore threads to core2", "( ) Show NAND or user string in System Settings", "( ) Show GBA boot screen in patched AGB_FIRM", }; @@ -793,6 +796,15 @@ void configMenu(bool oldPinStatus, u32 oldPinMode) "for out-of-region games work.\n\n" "Refer to the wiki for instructions.", + "Redirect app. threads that would spawn\n" + "on core1, to core2 (which is an extra\n" + "CPU core for applications that usually\n" + "remains unused).\n\n" + "This improves the performance of very\n" + "demanding games (like Pok\x82mon US/UM)\n" // CP437 + "by about 10%. Can break some games\n" + "and other applications.\n", + "Enable showing the current NAND/FIRM:\n\n" "\t* Sys = SysNAND\n" "\t* Emu = EmuNAND 1\n" @@ -839,6 +851,7 @@ void configMenu(bool oldPinStatus, u32 oldPinMode) { .visible = nandType == FIRMWARE_EMUNAND }, { .visible = true }, { .visible = true }, + { .visible = ISN3DS }, { .visible = true }, { .visible = true }, }; diff --git a/arm9/source/config.h b/arm9/source/config.h index 7a7f162..0d79e62 100644 --- a/arm9/source/config.h +++ b/arm9/source/config.h @@ -36,7 +36,7 @@ #define CONFIG_FILE "config.bin" #define CONFIG_VERSIONMAJOR 3 -#define CONFIG_VERSIONMINOR 2 +#define CONFIG_VERSIONMINOR 3 #define BOOTCFG_NAND BOOTCONFIG(0, 7) #define BOOTCFG_FIRM BOOTCONFIG(3, 7) @@ -59,6 +59,7 @@ enum singleOptions USEEMUFIRM, LOADEXTFIRMSANDMODULES, PATCHGAMES, + REDIRECTAPPTHREADS, PATCHVERSTRING, SHOWGBABOOT, PATCHUNITINFO, diff --git a/k11_extension/include/config.h b/k11_extension/include/config.h index 26ee057..07d12de 100644 --- a/k11_extension/include/config.h +++ b/k11_extension/include/config.h @@ -30,6 +30,7 @@ enum singleOptions USEEMUFIRM, LOADEXTFIRMSANDMODULES, PATCHGAMES, + REDIRECTAPPTHREADS, PATCHVERSTRING, SHOWGBABOOT, PATCHUNITINFO, diff --git a/k11_extension/include/globals.h b/k11_extension/include/globals.h index 163a8cd..3e5ed24 100644 --- a/k11_extension/include/globals.h +++ b/k11_extension/include/globals.h @@ -54,6 +54,7 @@ extern void (*KScheduler__AdjustThread)(KScheduler *this, KThread *thread, u32 o extern void (*KScheduler__AttemptSwitchingThreadContext)(KScheduler *this); extern Result (*ControlMemory)(u32 *addrOut, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader); +extern Result (*CreateThread)(Handle *outThreadHandle, u32 ep, u32 arg, u32 stackTop, s32 priority, s32 processorId); extern void (*SleepThread)(s64 ns); extern Result (*CloseHandle)(Handle handle); extern Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type); diff --git a/k11_extension/include/kernel.h b/k11_extension/include/kernel.h index fb1bed8..a0596c3 100644 --- a/k11_extension/include/kernel.h +++ b/k11_extension/include/kernel.h @@ -1162,7 +1162,7 @@ offsetof(classname##O3DSPre8x, field))) #define KPROCESSHWINFO_GET_RVALUE(obj, field) *(KPROCESSHWINFO_GET_PTR(obj, field)) #define KPROCESSHWINFO_GET_RVALUE_TYPE(type, obj, field) *(KPROCESSHWINFO_GET_PTR_TYPE(type, obj, field)) -extern u32 pidOffsetKProcess, hwInfoOffsetKProcess, codeSetOffsetKProcess, handleTableOffsetKProcess, debugOffsetKProcess; +extern u32 pidOffsetKProcess, hwInfoOffsetKProcess, codeSetOffsetKProcess, handleTableOffsetKProcess, debugOffsetKProcess, flagsKProcess; static inline u32 idOfProcess(KProcess *process) { @@ -1195,6 +1195,13 @@ static inline KDebug *debugOfProcess(KProcess *process) return debug; } +static inline u32 flagsOfProcess(KProcess *process) +{ + u32 flags; + memcpy(&flags, (const u8 *)process + flagsKProcess, 4); + return flags; +} + static inline const char *classNameOfAutoObject(KAutoObject *object) { const char *name; diff --git a/k11_extension/include/svc/CreateThread.h b/k11_extension/include/svc/CreateThread.h new file mode 100644 index 0000000..e4f7de5 --- /dev/null +++ b/k11_extension/include/svc/CreateThread.h @@ -0,0 +1,32 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016-2023 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#include "utils.h" +#include "kernel.h" +#include "svc.h" + +Result CreateThreadHookWrapper(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); diff --git a/k11_extension/source/globals.c b/k11_extension/source/globals.c index 26584d6..47918aa 100644 --- a/k11_extension/source/globals.c +++ b/k11_extension/source/globals.c @@ -50,6 +50,7 @@ void (*KScheduler__AdjustThread)(KScheduler *this, KThread *thread, u32 oldSched void (*KScheduler__AttemptSwitchingThreadContext)(KScheduler *this); Result (*ControlMemory)(u32 *addrOut, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader); +Result (*CreateThread)(Handle *outThreadHandle, u32 ep, u32 arg, u32 stackTop, s32 priority, s32 processorId); void (*SleepThread)(s64 ns); Result (*CloseHandle)(Handle handle); Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type); @@ -114,4 +115,4 @@ u32 stolenSystemMemRegionSize; vu32 rosalinaState; bool hasStartedRosalinaNetworkFuncsOnce; -u32 pidOffsetKProcess, hwInfoOffsetKProcess, codeSetOffsetKProcess, handleTableOffsetKProcess, debugOffsetKProcess; +u32 pidOffsetKProcess, hwInfoOffsetKProcess, codeSetOffsetKProcess, handleTableOffsetKProcess, debugOffsetKProcess, flagsKProcess; diff --git a/k11_extension/source/main.c b/k11_extension/source/main.c index e0a0052..c4cce09 100644 --- a/k11_extension/source/main.c +++ b/k11_extension/source/main.c @@ -127,6 +127,7 @@ void configHook(vu8 *cfgPage) codeSetOffsetKProcess = KPROCESS_OFFSETOF(codeSet); handleTableOffsetKProcess = KPROCESS_OFFSETOF(handleTable); debugOffsetKProcess = KPROCESS_OFFSETOF(debug); + flagsKProcess = KPROCESS_OFFSETOF(kernelFlags); } static void findUsefulSymbols(void) @@ -240,6 +241,7 @@ static void findUsefulSymbols(void) // The official prototype of ControlMemory doesn't have that extra param' ControlMemory = (Result (*)(u32 *, u32, u32, u32, MemOp, MemPerm, bool)) decodeArmBranch((u32 *)officialSVCs[0x01] + 5); + CreateThread = (Result (*)(Handle *, u32, u32, u32, s32, s32))decodeArmBranch((u32 *)officialSVCs[0x08] + 5); SleepThread = (void (*)(s64))officialSVCs[0x0A]; CloseHandle = (Result (*)(Handle))officialSVCs[0x23]; GetHandleInfo = (Result (*)(s64 *, Handle, u32))decodeArmBranch((u32 *)officialSVCs[0x29] + 3); diff --git a/k11_extension/source/svc.c b/k11_extension/source/svc.c index 27a064a..516098f 100644 --- a/k11_extension/source/svc.c +++ b/k11_extension/source/svc.c @@ -28,6 +28,7 @@ #include "synchronization.h" #include "svc.h" #include "svc/ControlMemory.h" +#include "svc/CreateThread.h" #include "svc/GetHandleInfo.h" #include "svc/GetSystemInfo.h" #include "svc/GetProcessInfo.h" @@ -63,6 +64,8 @@ void buildAlteredSvcTable(void) alteredSvcTable[0x01] = ControlMemoryHookWrapper; + if (isN3DS) + alteredSvcTable[0x08] = CreateThreadHookWrapper; alteredSvcTable[0x29] = GetHandleInfoHookWrapper; alteredSvcTable[0x2A] = GetSystemInfoHookWrapper; alteredSvcTable[0x2B] = GetProcessInfoHookWrapper; diff --git a/k11_extension/source/svc/CreateThread.c b/k11_extension/source/svc/CreateThread.c new file mode 100644 index 0000000..da748e9 --- /dev/null +++ b/k11_extension/source/svc/CreateThread.c @@ -0,0 +1,37 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016-2023 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#include "svc/CreateThread.h" + +Result CreateThreadHook(Handle *outThreadHandle, u32 ep, u32 arg, u32 stackTop, s32 priority, s32 processorId) +{ + u32 flags = flagsOfProcess(currentCoreContext->objectContext.currentProcess); + if (isN3DS && CONFIG(REDIRECTAPPTHREADS) && processorId == 1 && (flags & 0xF00) == 0x100) + processorId = 2; + + return CreateThread(outThreadHandle, ep, arg, stackTop, priority, processorId); +} + diff --git a/k11_extension/source/svc/wrappers.s b/k11_extension/source/svc/wrappers.s index c0953ea..68fc1d8 100644 --- a/k11_extension/source/svc/wrappers.s +++ b/k11_extension/source/svc/wrappers.s @@ -84,3 +84,16 @@ ControlMemoryEx: ldr r1, [sp, #12] add sp, #20 pop {pc} + +.global CreateThreadHookWrapper +.type CreateThreadHookWrapper, %function +CreateThreadHookWrapper: + push {lr} + sub sp, #12 + str r4, [sp, #4] + str r0, [sp] + add r0, sp, #8 + bl CreateThreadHook + ldr r1, [sp, #8] + add sp, sp, #12 + pop {pc} diff --git a/sysmodules/loader/source/patcher.h b/sysmodules/loader/source/patcher.h index 0ff267f..86b32eb 100644 --- a/sysmodules/loader/source/patcher.h +++ b/sysmodules/loader/source/patcher.h @@ -33,6 +33,7 @@ enum singleOptions USEEMUFIRM, LOADEXTFIRMSANDMODULES, PATCHGAMES, + REDIRECTAPPTHREADS, PATCHVERSTRING, SHOWGBABOOT, PATCHUNITINFO, diff --git a/sysmodules/pm/source/luma.c b/sysmodules/pm/source/luma.c index eb14d26..81d0b00 100644 --- a/sysmodules/pm/source/luma.c +++ b/sysmodules/pm/source/luma.c @@ -3,6 +3,23 @@ #include "luma.h" #include "util.h" +u32 config, multiConfig, bootConfig; + +void readLumaConfig(void) +{ + s64 out = 0; + bool isLumaWithKext = svcGetSystemInfo(&out, 0x20000, 0) == 1; + if (isLumaWithKext) + { + svcGetSystemInfo(&out, 0x10000, 3); + config = (u32)out; + svcGetSystemInfo(&out, 0x10000, 4); + multiConfig = (u32)out; + svcGetSystemInfo(&out, 0x10000, 5); + bootConfig = (u32)out; + } +} + bool hasKExt(void) { s64 val; diff --git a/sysmodules/pm/source/luma.h b/sysmodules/pm/source/luma.h index 80b80ef..54e6c9e 100644 --- a/sysmodules/pm/source/luma.h +++ b/sysmodules/pm/source/luma.h @@ -2,6 +2,42 @@ #include <3ds/types.h> +#define CONFIG(a) (((config >> (a)) & 1) != 0) +#define MULTICONFIG(a) ((multiConfig >> (2 * (a))) & 3) +#define BOOTCONFIG(a, b) ((bootConfig >> (a)) & (b)) + +#define BOOTCFG_NAND BOOTCONFIG(0, 7) +#define BOOTCFG_FIRM BOOTCONFIG(3, 7) +#define BOOTCFG_NOFORCEFLAG BOOTCONFIG(6, 1) +#define BOOTCFG_NTRCARDBOOT BOOTCONFIG(7, 1) + +enum multiOptions +{ + DEFAULTEMU = 0, + BRIGHTNESS, + SPLASH, + PIN, + NEWCPU, + AUTOBOOTMODE, +}; + +enum singleOptions +{ + AUTOBOOTEMU = 0, + USEEMUFIRM, + LOADEXTFIRMSANDMODULES, + PATCHGAMES, + REDIRECTAPPTHREADS, + PATCHVERSTRING, + SHOWGBABOOT, + PATCHUNITINFO, + DISABLEARM11EXCHANDLERS, + ENABLESAFEFIRMROSALINA, +}; + +extern u32 config, multiConfig, bootConfig; + +void readLumaConfig(void); bool hasKExt(void); u32 getKExtSize(void); u32 getStolenSystemMemRegionSize(void); diff --git a/sysmodules/pm/source/main.c b/sysmodules/pm/source/main.c index 9b7fedc..78f4914 100644 --- a/sysmodules/pm/source/main.c +++ b/sysmodules/pm/source/main.c @@ -13,6 +13,7 @@ #include "util.h" #include "my_thread.h" #include "service_manager.h" +#include "luma.h" static MyThread processMonitorThread, taskRunnerThread; static u8 ALIGN(8) processDataBuffer[0x40 * sizeof(ProcessData)] = {0}; @@ -34,6 +35,7 @@ Result __sync_init(void); void initSystem() { __sync_init(); + readLumaConfig(); //__libc_init_array(); // Wait for sm diff --git a/sysmodules/pm/source/reslimit.c b/sysmodules/pm/source/reslimit.c index 4dad971..b057257 100644 --- a/sysmodules/pm/source/reslimit.c +++ b/sysmodules/pm/source/reslimit.c @@ -308,6 +308,11 @@ Result resetAppMemLimit(void) Result setAppCpuTimeLimit(s64 limit) { + // Prevent apps from enabling preemption on core1 (and kernel will + // redirect apps threads from core 1 to 2). + if (IS_N3DS && CONFIG(REDIRECTAPPTHREADS)) + return 0; + ResourceLimitType category = RESLIMIT_CPUTIME; return svcSetResourceLimitValues(g_manager.reslimits[0], &category, &limit, 1); } diff --git a/sysmodules/rosalina/data/config_template.ini b/sysmodules/rosalina/data/config_template.ini index 3e7158b..9b521fa 100644 Binary files a/sysmodules/rosalina/data/config_template.ini and b/sysmodules/rosalina/data/config_template.ini differ diff --git a/sysmodules/rosalina/source/menus/miscellaneous.c b/sysmodules/rosalina/source/menus/miscellaneous.c index 7697779..70055dd 100644 --- a/sysmodules/rosalina/source/menus/miscellaneous.c +++ b/sysmodules/rosalina/source/menus/miscellaneous.c @@ -49,6 +49,7 @@ enum singleOptions USEEMUFIRM, LOADEXTFIRMSANDMODULES, PATCHGAMES, + REDIRECTAPPTHREADS, PATCHVERSTRING, SHOWGBABOOT, PATCHUNITINFO, @@ -315,7 +316,8 @@ static size_t saveLumaIniConfigToStr(char *out, const CfgData *cfg) (int)cfg->formatVersionMajor, (int)cfg->formatVersionMinor, (int)CONFIG(AUTOBOOTEMU), (int)CONFIG(USEEMUFIRM), (int)CONFIG(LOADEXTFIRMSANDMODULES), (int)CONFIG(PATCHGAMES), - (int)CONFIG(PATCHVERSTRING), (int)CONFIG(SHOWGBABOOT), + (int)CONFIG(REDIRECTAPPTHREADS), (int)CONFIG(PATCHVERSTRING), + (int)CONFIG(SHOWGBABOOT), 1 + (int)MULTICONFIG(DEFAULTEMU), 4 - (int)MULTICONFIG(BRIGHTNESS), splashPosStr, (unsigned int)cfg->splashDurationMsec,