Compare commits

..

68 Commits

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

# Conflicts:
#	k11_extension/source/main.c
#	k11_extension/source/svc/UnmapProcessMemoryEx.c
#	sysmodules/rosalina/Makefile
#	sysmodules/rosalina/include/menu.h
#	sysmodules/rosalina/include/utils.h
#	sysmodules/rosalina/source/errdisp.c
#	sysmodules/rosalina/source/main.c
#	sysmodules/rosalina/source/menu.c
#	sysmodules/rosalina/source/menus.c
2020-07-04 02:43:27 +02:00
TuxSH
dc4de4ce6f rosalina: fix for latest libctru changes 2020-06-26 09:43:42 +01:00
TuxSH
4e12453fff pm: fix critical bugs where 1.0(?) titles not in the list have scheduling mode misconfigured
Also fix the comments.
Thanks @fincs
2020-06-26 01:04:12 +01:00
Aurora Wright
3a0418e279 loader: revert to use the NS patch due to a Nintendo bug: https://www.3dbrew.org/wiki/NCCH/Extended_Header#Flag1 2020-05-29 23:31:05 +02:00
Aurora Wright
1899bf377b loader: replace NS N3DS CPU patch with exheader override, fix overriding exheader with homebrew 2020-05-29 20:57:05 +02:00
TuxSH
0471002d4c rosalina: ntp: use PTMSYSM_SetRtcTime 2020-05-18 22:43:00 +01:00
TuxSH
704e08dc23 revert the memory map to the old one (mostly) 2020-05-18 22:15:34 +01:00
TuxSH
905837468c fix module loading 2020-05-18 21:05:36 +01:00
TuxSH
19d95782e1 kext: fix outer memory cacheability on newer versions 2020-05-18 20:48:54 +01:00
TuxSH
adda19ecb2 so bascially rosalina's image...
was in BASE (hardcoded by kernel) while its heaps were in SYSTEM. Fix this; also put the kext where BASE was.
2020-05-18 01:15:44 +01:00
TuxSH
b02d0346fd rosalina: add hidden debug info menu 2020-05-17 22:48:26 +01:00
TuxSH
9097276a06 rosalina: refactor menu handling 2020-05-17 16:42:44 +01:00
TuxSH
e99ab11c6f rosalina: rephrase brightness warning 2020-05-16 18:31:30 +01:00
TuxSH
a564d8536a rosalina: add brightness control menu 2020-05-16 02:37:47 +01:00
TuxSH
a21eee9207 rosalina/pm: remove fs patch, use pm instead 2020-05-15 22:15:55 +01:00
TuxSH
71cddef78f rosalina: cleanup variable names 2020-05-15 20:00:13 +01:00
TuxSH
9ae913064c rosalina: reorder menus 2020-05-15 02:29:17 +01:00
TuxSH
a2313d1c03 Fix latest commit 2020-05-15 02:06:52 +01:00
TuxSH
22db3445a0 rosalina menu: add scrolling, cpad and inputredir support (note: no ZL/ZR due to technical reasons) 2020-05-14 21:05:27 +01:00
TuxSH
6417720d7d stuff 2020-05-11 10:25:33 +01:00
TuxSH
8b10906d90 newlib... 2020-05-10 18:54:51 +01:00
TuxSH
0c55324d11 rosalina shutdown/reboot: fix a few things 2020-05-10 15:35:39 +01:00
TuxSH
0b4fdc6e66 Revert "Shutdown via rosalina menu is now much faster; similar thing for reboot"
This reverts commit 9942e8b299.
2020-05-10 15:08:13 +01:00
TuxSH
d3e62df769 rosalina: implement dirty hb chainload 2020-05-10 02:58:21 +01:00
Aurora
04bd881cfa Update README.md
Reflect move to the Organization
2020-05-09 20:07:07 +02:00
TuxSH
96799455cb rosalina: allow booting homebrew w/o having to reboot if using a different memory mode 2020-05-09 02:25:33 +01:00
TuxSH
814792eb91 pm: fix handling of PMLAUNCHFLAG_FORCE_USE_O3DS_MAX_APP_MEM 2020-05-09 01:37:09 +01:00
TuxSH
2834bae318 rosalina screenshots: improve conversion time by another 10% 2020-05-08 20:51:41 +01:00
TuxSH
037fae99d6 rosalina: use kernel cached RAM mapping for pixel conversion
Conversion time goes down by 90% with this
2020-05-08 20:20:51 +01:00
TuxSH
49c8888948 rosalina: put screenshot loop inside same TU 2020-05-08 18:19:17 +01:00
TuxSH
1875556f81 Merge pull request #1417 from mtheall/master
Add printf attrtibute to Draw_DrawFormattedString
2020-05-08 16:52:52 +01:00
TuxSH
00850bf691 rosalina: fast screenshots (10s -> 0.3s on my end) 2020-05-08 16:44:30 +01:00
TuxSH
09fd199487 rosalina: Dynamically alloc/free fb cache, exempt rosalina from reslimiting 2020-05-08 01:17:46 +01:00
Michael Theall
32c53578e0 Add printf attrtibute to Draw_DrawFormattedString 2020-05-06 22:34:09 -05:00
TuxSH
0da90f61fc Fix ODR bugs 2020-05-05 18:13:32 +01:00
TuxSH
9942e8b299 Shutdown via rosalina menu is now much faster; similar thing for reboot 2020-05-05 02:01:30 +01:00
TuxSH
daaeb97834 Don't do firmlaunch patches on safe_firm (because of USM) 2020-05-03 01:53:35 +01:00
TuxSH
92da214066 loader: add dsp patch for safe_firm 2020-05-02 23:50:04 +01:00
TuxSH
0f05dd5c0a Revert "rosalina draw.c: remove wait for cmd, gpu processing engine is prone to crashes"
This reverts commit 7dc2b7123b.
2020-05-02 23:36:27 +01:00
TuxSH
166bdbeb7d Add option to run rosalina on N3DS SAFE_FIRM.
Also enables a qtm error bypass
2020-05-02 23:32:21 +01:00
TuxSH
7dc2b7123b rosalina draw.c: remove wait for cmd, gpu processing engine is prone to crashes 2020-05-02 22:38:03 +01:00
TuxSH
3d0ec9b785 Make 3dsx feature compatible with n3ds safe_firm, HOWEVER apps that actually use the GPU won't work 2020-05-02 22:37:15 +01:00
TuxSH
85cfa5cba6 loader: enable secureinfo patch on safe_firm 2020-05-02 18:26:58 +01:00
TuxSH
fdc1eaa16c pm: quick safe_firm fixes 2020-05-02 18:08:24 +01:00
TuxSH
d4dcf1a3e9 k11ext: support SAFE_FIRM in rosalinaThreadLockPredicate 2020-05-02 14:44:39 +01:00
TuxSH
43fd137d55 Use kernel version minor 2020-05-02 12:35:44 +01:00
TuxSH
6931eadc34 update bug report template again 2020-04-29 23:09:20 +01:00
PabloMK7
f1b787c7d9 Merge pull request #3 from AuroraWright/master
Merge 10.0.1
2019-07-06 09:37:41 +02:00
PabloMK7
6b13b5d06b Merge branch 'AuroraWright-master' 2019-06-29 23:35:07 +02:00
PabloMK7
9705083b65 Fix conflicting files 2019-06-29 23:34:45 +02:00
PabloMK7
2423d1802e Add plgldr string to config menu 2019-06-29 17:16:17 +02:00
PabloMK7
0b68baa0dd Merge branch 'Nanquitas-master' 2019-06-29 16:26:26 +02:00
PabloMK7
8168d2c2f9 Manually merge files 2019-06-29 16:26:03 +02:00
Nanquitas
4b341e039a Merge branch 'master' of https://github.com/AuroraWright/Luma3DS 2018-11-15 13:49:35 +01:00
Nanquitas
1ae01c2406 GDB: add 'catchsvc' command to catch svc with IDA
Usage:
   - 'catchsvc 0' : Don't catch svcs
   - 'catchsvc 1' : Catch all svcs
   - 'catchsvc 1;19;24;32;' : Only catch svc 0x19, svc 0x24 and svc 0x32
2018-11-15 13:44:45 +01:00
Nanquitas
2182742708 Implement plugin loader 2018-11-15 13:38:19 +01:00
Nanquitas
2520079536 Increase code dump on exception 2018-08-04 16:22:16 +02:00
Aurora Wright
d7095ce37d Fix patchKernel9Panic on 11.8 NATIVE_FIRM (pattern tested down to 3.0) 2018-08-04 16:11:14 +02:00
Nanquitas
0a87e41c66 socAccept: Fix an omitted comment, which masked a condition 2018-08-04 16:10:35 +02:00
Nanquitas
bec8daf028 Fix sleep issue (freeze) when InputRedirection is enabled 2018-06-23 23:06:18 +02:00
100 changed files with 3994 additions and 784 deletions

View File

@@ -11,7 +11,7 @@ about: Use this to report bugs you encounter with Luma3DS. Make sure you upload
--
-- Also check the Wiki (https://github.com/AuroraWright/Luma3DS/wiki) before making an issue.
--
-- For GBA/DSiWare/DS/AGB_FIRM/TWL_FIRM problems: https://3ds.hacks.guide/troubleshooting
-- For GBA/DSiWare/DS/AGB_FIRM/TWL_FIRM problems: use https://github.com/MechanicalDragon0687/TWLFix-CFW and update your system.
-- If you're using an emu/redNAND try installing anything on it to sysNAND.
-- Please make sure to read "Enable game patching" https://github.com/AuroraWright/Luma3DS/wiki/Options-and-usage before posting any issues about the "Enable game patching" option(s).
--
@@ -34,7 +34,7 @@ about: Use this to report bugs you encounter with Luma3DS. Make sure you upload
**Luma3DS version:**
[e.g. v10.1.2 stable or if using nightly/hourly specify the commit like this https://github.com/AuroraWright/Luma3DS/commit/0543c208fd154e6326ea5da8cbf66ffcbdef010c]
[e.g. v10.1.3 stable or if using non-releases specify the commit like this https://github.com/AuroraWright/Luma3DS/commit/0543c208fd154e6326ea5da8cbf66ffcbdef010c]
**Luma3DS configuration/options:**

View File

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

View File

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

View File

@@ -95,6 +95,7 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
"( ) Show GBA boot screen in patched AGB_FIRM",
"( ) Set developer UNITINFO",
"( ) Disable Arm11 exception handlers",
"( ) Enable Rosalina on SAFE_FIRM",
};
static const char *optionsDescription[] = { "Select the default EmuNAND.\n\n"
@@ -193,7 +194,16 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
"Note: Disabling the exception handlers\n"
"will disqualify you from submitting\n"
"issues or bug reports to the Luma3DS\n"
"GitHub repository!"
"GitHub repository!",
"Enables Rosalina, the kernel ext.\n"
"and sysmodule reimplementations on\n"
"SAFE_FIRM (New 3DS only).\n\n"
"Also suppresses QTM error 0xF96183FE,\n"
"allowing to use 8.1-11.3 N3DS on\n"
"New 2DS XL consoles.\n\n"
"Only select this if you know what you\n"
"are doing!",
};
FirmwareSource nandType = FIRMWARE_SYSNAND;
@@ -229,7 +239,8 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
{ .visible = true },
{ .visible = true },
{ .visible = true },
{ .visible = true }
{ .visible = true },
{ .visible = ISN3DS },
};
//Calculate the amount of the various kinds of options and pre-select the first single one

View File

@@ -34,7 +34,7 @@
#define CONFIG_FILE "config.bin"
#define CONFIG_VERSIONMAJOR 2
#define CONFIG_VERSIONMINOR 3
#define CONFIG_VERSIONMINOR 4
#define BOOTCFG_NAND BOOTCONFIG(0, 7)
#define BOOTCFG_FIRM BOOTCONFIG(3, 7)
@@ -60,7 +60,8 @@ enum singleOptions
PATCHVERSTRING,
SHOWGBABOOT,
PATCHUNITINFO,
DISABLEARM11EXCHANDLERS
DISABLEARM11EXCHANDLERS,
ENABLESAFEFIRMROSALINA,
};
typedef enum ConfigurationStatus

View File

@@ -278,7 +278,8 @@ static inline void mergeSection0(FirmwareType firmType, u32 firmVersion, bool lo
srcModuleSize = moduleList[nbModules].size = ((Cxi *)src)->ncch.contentSize * 0x200;
}
if(firmType == NATIVE_FIRM && (ISN3DS || firmVersion >= 0x1D))
// SAFE_FIRM only for N3DS and only if ENABLESAFEFIRMROSALINA is on
if((firmType == NATIVE_FIRM || firmType == SAFE_FIRM) && (ISN3DS || firmVersion >= 0x1D))
{
//2) Merge that info with our own modules'
for(u8 *src = (u8 *)0x18180000; memcmp(((Cxi *)src)->ncch.magic, "NCCH", 4) == 0; src += srcModuleSize)
@@ -303,7 +304,9 @@ static inline void mergeSection0(FirmwareType firmType, u32 firmVersion, bool lo
//3) Read or copy the modules
u8 *dst = firm->section[0].address;
const char *extModuleSizeError = "The external FIRM modules are too large.";
for(u32 i = 0, dstModuleSize, maxModuleSize = firmType == NATIVE_FIRM ? 0x80000 : 0x600000; i < nbModules; i++, dst += dstModuleSize, maxModuleSize -= dstModuleSize)
// SAFE_FIRM only for N3DS and only if ENABLESAFEFIRMROSALINA is on
u32 maxModuleSize = (firmType == NATIVE_FIRM || firmType == SAFE_FIRM) ? 0x80000 : 0x600000;
for(u32 i = 0, dstModuleSize; i < nbModules; i++, dst += dstModuleSize, maxModuleSize -= dstModuleSize)
{
if(loadFromStorage)
{
@@ -335,7 +338,7 @@ static inline void mergeSection0(FirmwareType firmType, u32 firmVersion, bool lo
memcpy(dst, moduleList[i].src, dstModuleSize);
}
//4) Patch NATIVE_FIRM if necessary
//4) Patch NATIVE_FIRM/SAFE_FIRM (N3DS) if necessary
if(nbModules == 6)
{
if(patchK11ModuleLoading(firm->section[0].size, dst - firm->section[0].address, (u8 *)firm + firm->section[1].offset, firm->section[1].size) != 0)
@@ -522,6 +525,32 @@ u32 patch1x2xNativeAndSafeFirm(void)
ret += patchArm9ExceptionHandlersInstall(arm9Section, kernel9Size);
ret += patchSvcBreak9(arm9Section, kernel9Size, (u32)firm->section[2].address);
if(ISN3DS && CONFIG(ENABLESAFEFIRMROSALINA))
{
u8 *arm11Section1 = (u8 *)firm + firm->section[1].offset;
//Find the Kernel11 SVC table and handler, exceptions page and free space locations
u32 baseK11VA;
u8 *freeK11Space;
u32 *arm11SvcHandler,
*arm11ExceptionsPage,
*arm11SvcTable = getKernel11Info(arm11Section1, firm->section[1].size, &baseK11VA, &freeK11Space, &arm11SvcHandler, &arm11ExceptionsPage);
ret += installK11Extension(arm11Section1, firm->section[1].size, false, baseK11VA, arm11ExceptionsPage, &freeK11Space);
ret += patchKernel11(arm11Section1, firm->section[1].size, baseK11VA, arm11SvcTable, arm11ExceptionsPage);
// Add some other patches to the mix, as we can now launch homebrew on SAFE_FIRM:
//Apply firmlaunch patches
//Or don't, this makes usm not work
//ret += patchFirmlaunches(process9Offset, process9Size, process9MemAddr);
ret += patchKernel9Panic(arm9Section, kernel9Size);
ret += patchP9AccessChecks(process9Offset, process9Size);
mergeSection0(NATIVE_FIRM, 0x45, false); // may change in the future
firm->section[0].size = 0;
}
return ret;
}

View File

@@ -110,9 +110,12 @@ u32 installK11Extension(u8 *pos, u32 size, bool needToInitSd, u32 baseK11VA, u32
struct KExtParameters
{
u32 basePA;
u32 stolenSystemMemRegionSize;
void *originalHandlers[4];
u32 L1MMUTableAddrs[4];
volatile bool done;
struct CfwInfo
{
char magic[4];
@@ -128,6 +131,7 @@ u32 installK11Extension(u8 *pos, u32 size, bool needToInitSd, u32 baseK11VA, u32
u32 config, multiConfig, bootConfig;
u64 hbldr3dsxTitleId;
u32 rosalinaMenuCombo;
u32 rosalinaFlags;
} info;
};
@@ -137,7 +141,8 @@ u32 installK11Extension(u8 *pos, u32 size, bool needToInitSd, u32 baseK11VA, u32
//Our kernel11 extension is initially loaded in VRAM
u32 kextTotalSize = *(u32 *)0x18000020 - K11EXT_VA;
u32 dstKextPA = (ISN3DS ? 0x2E000000 : 0x26C00000) - kextTotalSize;
u32 stolenSystemMemRegionSize = kextTotalSize; // no need to steal any more mem on N3DS. Currently, everything fits in BASE on O3DS too (?)
u32 dstKextPA = (ISN3DS ? 0x2E000000 : 0x26C00000) - stolenSystemMemRegionSize; // start of BASE memregion (note: linear heap ---> <--- the rest)
u32 *hookVeneers = (u32 *)*freeK11Space;
u32 relocBase = 0xFFFF0000 + (*freeK11Space - (u8 *)arm11ExceptionsPage);
@@ -179,6 +184,8 @@ u32 installK11Extension(u8 *pos, u32 size, bool needToInitSd, u32 baseK11VA, u32
struct KExtParameters *p = (struct KExtParameters *)(*(u32 *)0x18000024 - K11EXT_VA + 0x18000000);
p->basePA = dstKextPA;
p->done = false;
p->stolenSystemMemRegionSize = stolenSystemMemRegionSize;
for(u32 i = 0; i < 4; i++)
{
@@ -197,6 +204,7 @@ u32 installK11Extension(u8 *pos, u32 size, bool needToInitSd, u32 baseK11VA, u32
info->bootConfig = configData.bootConfig;
info->hbldr3dsxTitleId = configData.hbldr3dsxTitleId;
info->rosalinaMenuCombo = configData.rosalinaMenuCombo;
info->rosalinaFlags = configData.rosalinaFlags;
info->versionMajor = VERSION_MAJOR;
info->versionMinor = VERSION_MINOR;
info->versionBuild = VERSION_BUILD;
@@ -223,6 +231,10 @@ u32 patchKernel11(u8 *pos, u32 size, u32 baseK11VA, u32 *arm11SvcTable, u32 *arm
u8 *ControlMemoryPos = instrPos + 8 + displ;
u32 *off;
// Patch ControlMemory bounds checks for mem mapping
for (off = (u32 *)ControlMemoryPos; *off != 0xE0E01BF5; ++off);
*off = 0;
/*
Here we replace currentProcess->processID == 1 by additionnalParameter == 1.
This patch should be generic enough to work even on firmware version 5.0.
@@ -436,7 +448,7 @@ u32 patchK11ModuleLoading(u32 section0size, u32 modulesSize, u8 *pos, u32 size)
off32 += 2;
off32[1] = off32[0] + modulesSize;
for(; *off32 != section0size; off32++);
*off32 += ((modulesSize + 0x1FF) >> 9) << 9;
*off32 = ((modulesSize + 0x1FF) >> 9) << 9;
off = memsearch(pos, modulePidPattern, size, 4);

View File

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

View File

@@ -159,7 +159,7 @@ def main(args=None):
print("{0:<15}{1:<20}Access type: {2}".format("FAR", "{0:08x}".format(registers[19]), "Write" if registers[17] & (1 << 11) != 0 else "Read"))
thumb = registers[16] & 0x20 != 0
addr = registers[15] - codeDumpSize + (2 if thumb else 4)
addr = registers[15] - codeDumpSize / 2 + (2 if thumb else 4)
print("\nCode dump:\n")
@@ -167,8 +167,9 @@ def main(args=None):
try:
path = os.path.join(os.environ["DEVKITARM"], "bin", "arm-none-eabi-objdump")
if os.name == "nt" and path[0] == '/':
path = ''.join((path[1], ':', path[2:]))
path = ''.join(('c:', path[0], path[5:]))
objdump_res = subprocess.check_output((
path, "-marm", "-b", "binary",
@@ -176,6 +177,7 @@ def main(args=None):
"--stop-address="+hex(addr + codeDumpSize), "-D", "-z", "-M",
"reg-names-std" + (",force-thumb" if thumb else ""), args.filename
)).decode("utf-8")
objdump_res = '\n'.join(objdump_res[objdump_res.find('<.data+'):].split('\n')[1:])
except: objdump_res = ""

View File

@@ -33,5 +33,6 @@ enum singleOptions
PATCHVERSTRING,
SHOWGBABOOT,
PATCHUNITINFO,
DISABLEARM11EXCHANDLERS
DISABLEARM11EXCHANDLERS,
ENABLESAFEFIRMROSALINA,
};

View File

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

View File

@@ -105,6 +105,14 @@ typedef struct ALIGN(4) KMutex
union KProcess *owner;
} KMutex;
typedef struct KAddressArbiter
{
KAutoObject autoObject;
struct KThread *first;
struct KThread *last;
union KProcess *owner;
} KAddressArbiter;
/* 92 */
typedef struct KMutexLinkedList
{
@@ -112,6 +120,30 @@ typedef struct KMutexLinkedList
KMutex *last;
} KMutexLinkedList;
enum
{
TOKEN_KAUTOOBJECT = 0,
TOKEN_KSYNCHRONIZATIONOBJECT = 1,
TOKEN_KEVENT = 0x1F,
TOKEN_KSEMAPHORE = 0x2F,
TOKEN_KTIMER = 0x35,
TOKEN_KMUTEX = 0x39,
TOKEN_KDEBUG = 0x4D,
TOKEN_KSERVERPORT = 0x55,
TOKEN_KDMAOBJECT = 0x59,
TOKEN_KCLIENTPORT = 0x65,
TOKEN_KCODESET = 0x68,
TOKEN_KSESSION = 0x70,
TOKEN_KTHREAD = 0x8D,
TOKEN_KSERVERSESSION = 0x95,
TOKEN_KADDRESSARBITER = 0x98,
TOKEN_KCLIENTSESSION = 0xA5,
TOKEN_KPORT = 0xA8,
TOKEN_KSHAREDMEMORY = 0xB0,
TOKEN_KPROCESS = 0xC5,
TOKEN_KRESOURCELIMIT = 0xC8
};
/* 45 */
typedef struct KClassToken
{
@@ -540,6 +572,20 @@ typedef struct KBlockInfo
u32 pageCount;
} KBlockInfo;
typedef struct KSharedMemory
{
KAutoObject autoObject;
KLinkedList ownedKBlockInfo;
union KProcess *owner;
u32 ownerPermissions;
u32 otherPermissions;
u8 isBlockInfoGenerated;
s8 allBlockInfoGenerated;
u8 unknown_1;
u8 unknown_2;
u32 address;
} KSharedMemory;
/* 25 */
typedef struct KMemoryBlock
{
@@ -1037,10 +1083,26 @@ typedef struct KProcess##sys\
KThread *mainThread;\
u32 interruptEnabledFlags[4];\
KProcessHandleTable handleTable;\
u8 gap234[52];\
/* Custom fields for plugin system
{ */ \
u32 customFlags; /* see KProcess_CustomFlags enum below */ \
Handle onMemoryLayoutChangeEvent;\
Handle onProcessExitEvent;\
Handle resumeProcessExitEvent;\
/* } */ \
u8 gap234[36];\
u64 unused;\
} KProcess##sys;
enum KProcess_CustomFlags
{
ForceRWXPages = 1 << 0,
SignalOnMemLayoutChanges = 1 << 1,
SignalOnExit = 1 << 2,
MemLayoutChanged = 1 << 16
};
INSTANCIATE_KPROCESS(N3DS);
INSTANCIATE_KPROCESS(O3DS8x);
INSTANCIATE_KPROCESS(O3DSPre8x);
@@ -1126,11 +1188,11 @@ typedef union KCacheMaintenanceInterruptEvent
typedef struct FcramLayout
{
void *applicationAddr;
u32 applicationAddr;
u32 applicationSize;
void *systemAddr;
u32 systemAddr;
u32 systemSize;
void *baseAddr;
u32 baseAddr;
u32 baseSize;
} FcramLayout;
@@ -1138,15 +1200,15 @@ extern bool isN3DS;
extern void *officialSVCs[0x7E];
#define KPROCESSRELATED_OFFSETOFF(classname, field) (isN3DS ? offsetof(classname##N3DS, field) :\
((kernelVersion >= SYSTEM_VERSION(2, 44, 6)) ? offsetof(classname##O3DS8x, field) :\
((GET_VERSION_MINOR(kernelVersion) >= 44) ? offsetof(classname##O3DS8x, field) :\
offsetof(classname##O3DSPre8x, field)))
#define KPROCESSRELATED_GET_PTR(obj, field) (isN3DS ? &(obj)->N3DS.field :\
((kernelVersion >= SYSTEM_VERSION(2, 44, 6)) ? &(obj)->O3DS8x.field :\
((GET_VERSION_MINOR(kernelVersion) >= 44) ? &(obj)->O3DS8x.field :\
&(obj)->O3DSPre8x.field))
#define KPROCESSRELATED_GET_PTR_TYPE(type, obj, field) (isN3DS ? (type *)(&(obj)->N3DS.field) :\
((kernelVersion >= SYSTEM_VERSION(2, 44, 6)) ? (type *)(&(obj)->O3DS8x.field) :\
((GET_VERSION_MINOR(kernelVersion) >= 44) ? (type *)(&(obj)->O3DS8x.field) :\
(type *)(&(obj)->O3DSPre8x.field)))
#define KPROCESS_OFFSETOF(field) KPROCESSRELATED_OFFSETOFF(KProcess, field)
@@ -1189,7 +1251,7 @@ static inline KDebug *debugOfProcess(KProcess *process)
static inline const char *classNameOfAutoObject(KAutoObject *object)
{
const char *name;
if(kernelVersion >= SYSTEM_VERSION(2, 46, 0))
if(GET_VERSION_MINOR(kernelVersion) >= 46)
{
KClassToken tok;
object->vtable->GetClassToken(&tok, object);
@@ -1205,7 +1267,7 @@ extern Result (*KProcessHandleTable__CreateHandle)(KProcessHandleTable *this, Ha
static inline Result createHandleForProcess(Handle *out, KProcess *process, KAutoObject *obj)
{
u8 token;
if(kernelVersion >= SYSTEM_VERSION(2, 46, 0))
if(GET_VERSION_MINOR(kernelVersion) >= 46)
{
KClassToken tok;
obj->vtable->GetClassToken(&tok, obj);

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

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

View File

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

View File

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

View File

@@ -78,6 +78,15 @@ typedef s32 Result; ///< Function result.
#define SYSTEM_VERSION(major, minor, revision) \
(((major)<<24)|((minor)<<16)|((revision)<<8))
/// Retrieves the major version from a packed system version.
#define GET_VERSION_MAJOR(version) ((version) >>24)
/// Retrieves the minor version from a packed system version.
#define GET_VERSION_MINOR(version) (((version)>>16)&0xFF)
/// Retrieves the revision version from a packed system version.
#define GET_VERSION_REVISION(version) (((version)>> 8)&0xFF)
#define CUR_THREAD_HANDLE 0xFFFF8000
#define CUR_PROCESS_HANDLE 0xFFFF8001

View File

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

View File

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

View File

@@ -246,24 +246,24 @@ bool doErrfThrowHook(u32 *cmdbuf)
u8 *srcerrbuf = (u8 *)r0_to_r7_r12_usr[(spsr & 0x20) ? 4 : 6];
const char *pname = codeSetOfProcess(currentCoreContext->objectContext.currentProcess)->processName;
static const struct
const struct
{
const char *name;
Result errCode;
bool enabled;
} errorCodesToIgnore[] =
{
/*
If you're getting this error, you have broken your head-tracking hardware,
and should uncomment the following line:
If you're getting this error, you may have broken your head-tracking hardware,
and you need to enable the qtm error bypass below:
*/
//{ "qtm", (Result)0xF96183FE },
{ "", 0 }, // impossible case to ensure the array has at least 1 element
{ "qtm", 0xF96183FEu, CONFIG(ENABLESAFEFIRMROSALINA)},
{ "", 0, false}, // impossible case to ensure the array has at least 1 element
};
for(u32 i = 0; i < sizeof(errorCodesToIgnore) / sizeof(errorCodesToIgnore[0]); i++)
{
if(strcmp(pname, errorCodesToIgnore[i].name) == 0 && (Result)cmdbuf[2] == errorCodesToIgnore[i].errCode)
if(errorCodesToIgnore[i].enabled && strcmp(pname, errorCodesToIgnore[i].name) == 0 && (Result)cmdbuf[2] == errorCodesToIgnore[i].errCode)
{
srcerrbuf[0] = 5;
cmdbuf[0] = 0x10040;

View File

@@ -37,9 +37,12 @@
struct KExtParameters
{
u32 basePA;
u32 stolenSystemMemRegionSize;
void *originalHandlers[4];
u32 L1MMUTableAddrs[4];
volatile bool done;
CfwInfo cfwInfo;
} kExtParameters = { .basePA = 0x12345678 }; // place this in .data
@@ -59,15 +62,31 @@ void relocateAndSetupMMU(u32 coreId, u32 *L1Table)
memset((u32 *)(p0->basePA + (__bss_start__ - __start__)), 0, __bss_end__ - __bss_start__);
// Map the kernel ext at K11EXT_VA
// 4KB extended small pages: [SYS:RW USR:-- X TYP:NORMAL SHARED OUTER NOCACHE, INNER CACHED WB WA]
// 4KB extended small pages:
// Outer Write-Through cached, No Allocate on Write, Buffered
// Inner Cached Write-Back Write-Allocate, Buffered
// This was changed at some point (8.0 maybe?), it was outer noncached before
for(u32 offset = 0; offset < (u32)(__end__ - __start__); offset += 0x1000)
L2Table[offset >> 12] = (p0->basePA + offset) | 0x516;
L2Table[offset >> 12] = (p0->basePA + offset) | 0x596;
p0->done = true;
// DSB, Flush Prefetch Buffer (more or less "isb")
__asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 4" :: "r" (0) : "memory");
__asm__ __volatile__ ("mcr p15, 0, %0, c7, c5, 4" :: "r" (0) : "memory");
__asm__ __volatile__ ("sev");
}
else
__asm__ __volatile__ ("wfe");
else {
do
{
__asm__ __volatile__ ("wfe");
} while(!p0->done);
// DSB, Flush Prefetch Buffer (more or less "isb")
__asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 4" :: "r" (0) : "memory");
__asm__ __volatile__ ("mcr p15, 0, %0, c7, c5, 4" :: "r" (0) : "memory");
}
// bit31 idea thanks to SALT
// Maps physmem so that, if addr is in physmem(0, 0x30000000), it can be accessed uncached&rwx as addr|(1<<31)
u32 attribs = 0x40C02; // supersection (rwx for all) of strongly ordered memory, shared
@@ -81,6 +100,10 @@ void relocateAndSetupMMU(u32 coreId, u32 *L1Table)
L1Table[K11EXT_VA >> 20] = (u32)L2Table | 1;
p->L1MMUTableAddrs[coreId] = (u32)L1Table;
// DSB, Flush Prefetch Buffer (more or less "isb")
__asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 4" :: "r" (0) : "memory");
__asm__ __volatile__ ("mcr p15, 0, %0, c7, c5, 4" :: "r" (0) : "memory");
}
void bindSGI0Hook(void)
@@ -100,11 +123,61 @@ void configHook(vu8 *cfgPage)
*isDevUnit = true; // enable debug features
}
void KProcessHwInfo__MapL1Section_Hook(void);
void KProcessHwInfo__MapL2Section_Hook(void);
static void installMmuHooks(void)
{
u32 *mapL1Section = NULL;
u32 *mapL2Section = NULL;
u32 *off;
for(off = (u32 *)officialSVCs[0x1F]; *off != 0xE1CD60F0; ++off);
off = decodeArmBranch(off + 1);
for (; *off != 0xE58D5000; ++off);
off = decodeArmBranch(off + 1);
for (; *off != 0xE58DC000; ++off);
off = decodeArmBranch(off + 1);
for (; *off != 0xE1A0000B; ++off);
off = decodeArmBranch(off + 1);
for (; *off != 0xE59D2030; ++off);
off = decodeArmBranch(off + 1);
for (; *off != 0xE88D1100; ++off);
mapL2Section = (u32 *)PA_FROM_VA_PTR(decodeArmBranch(off + 1));
do
{
for (; *off != 0xE58D8000; ++off);
u32 *loc = (u32 *)PA_FROM_VA_PTR(decodeArmBranch(++off));
if (loc != mapL2Section)
mapL1Section = loc;
} while (mapL1Section == NULL);
mapL1Section[1] = 0xE28FE004; // add lr, pc, #4
mapL1Section[2] = 0xE51FF004; // ldr pc, [pc, #-4]
mapL1Section[3] = (u32)KProcessHwInfo__MapL1Section_Hook;
mapL2Section[1] = 0xE28FE004; // add lr, pc, #4
mapL2Section[2] = 0xE51FF004; // ldr pc, [pc, #-4]
mapL2Section[3] = (u32)KProcessHwInfo__MapL2Section_Hook;
}
static void findUsefulSymbols(void)
{
u32 *off;
for(off = (u32 *)0xFFFF0000; *off != 0xE1A0D002; off++);
// Patch ERRF__DumpException
for(off = (u32 *)0xFFFF0000; *off != 0xE1A04005; ++off);
++off;
*(u32 *)PA_FROM_VA_PTR(off) = makeArmBranch(off, off + 51, false);
for(; *off != 0xE2100102; ++off);
KProcessHwInfo__QueryMemory = (Result (*)(KProcessHwInfo *, MemoryInfo *, PageInfo *, void *))decodeArmBranch(off - 1);
for(; *off != 0xE1A0D002; off++);
off += 3;
initFPU = (void (*) (void))off;
@@ -167,6 +240,18 @@ static void findUsefulSymbols(void)
for(off = (u32 *)officialSVCs[0x72]; *off != 0xE2041102; off++);
KProcessHwInfo__UnmapProcessMemory = (Result (*)(KProcessHwInfo *, void *, u32))decodeArmBranch(off - 1);
for (off = (u32 *)officialSVCs[0x70]; *off != 0xE8881200 && *off != 0xE8891900; ++off);
for (off = (u32 *)decodeArmBranch(off + 1); *off != 0xE2101102; ++off);
KProcessHwInfo__CheckVaState = (Result (*)(KProcessHwInfo *, u32, u32, u32, u32))decodeArmBranch(off - 1);
for (; *off != 0xE28D1008; ++off);
KProcessHwInfo__GetListOfKBlockInfoForVA = (Result (*)(KProcessHwInfo*, KLinkedList*, u32, u32))decodeArmBranch(off + 1);
for (; *off != 0xE2000102; ++off);
KProcessHwInfo__MapListOfKBlockInfo = (Result (*)(KProcessHwInfo*, u32, KLinkedList*, u32, u32, u32))decodeArmBranch(off - 1);
for (; *off != 0xE8BD8FF0; ++off);
KLinkedList_KBlockInfo__Clear = (void (*)(KLinkedList *))decodeArmBranch(off - 6);
for(off = (u32 *)officialSVCs[0x7C]; *off != 0x03530000; off++);
KObjectMutex__WaitAndAcquire = (void (*)(KObjectMutex *))decodeArmBranch(++off);
for(; *off != 0xE320F000; off++);
@@ -212,6 +297,7 @@ static void findUsefulSymbols(void)
ControlMemory = (Result (*)(u32 *, u32, u32, u32, MemOp, MemPerm, bool))
decodeArmBranch((u32 *)officialSVCs[0x01] + 5);
SleepThread = (void (*)(s64))officialSVCs[0x0A];
CreateEvent = (Result (*)(Handle *, ResetType))decodeArmBranch((u32 *)officialSVCs[0x17] + 3);
CloseHandle = (Result (*)(Handle))officialSVCs[0x23];
GetHandleInfo = (Result (*)(s64 *, Handle, u32))decodeArmBranch((u32 *)officialSVCs[0x29] + 3);
GetSystemInfo = (Result (*)(s64 *, s32, s32))decodeArmBranch((u32 *)officialSVCs[0x2A] + 3);
@@ -222,6 +308,7 @@ static void findUsefulSymbols(void)
OpenProcess = (Result (*)(Handle *, u32))decodeArmBranch((u32 *)officialSVCs[0x33] + 3);
GetProcessId = (Result (*)(u32 *, Handle))decodeArmBranch((u32 *)officialSVCs[0x35] + 3);
DebugActiveProcess = (Result (*)(Handle *, u32))decodeArmBranch((u32 *)officialSVCs[0x60] + 3);
SignalEvent = (Result (*)(Handle event))officialSVCs[0x18];
UnmapProcessMemory = (Result (*)(Handle, void *, u32))officialSVCs[0x72];
KernelSetState = (Result (*)(u32, u32, u32, u32))((u32 *)officialSVCs[0x7C] + 1);
@@ -253,6 +340,8 @@ static void findUsefulSymbols(void)
invalidateInstructionCacheRange = (void (*)(void *, u32))off2;
}
}
installMmuHooks();
}
void main(FcramLayout *layout, KCoreContext *ctxs)
@@ -261,7 +350,11 @@ void main(FcramLayout *layout, KCoreContext *ctxs)
u32 TTBCR_;
s64 nb;
layout->systemSize -= __end__ - __start__;
cfwInfo = p->cfwInfo;
kextBasePa = p->basePA;
stolenSystemMemRegionSize = p->stolenSystemMemRegionSize;
layout->systemSize -= stolenSystemMemRegionSize;
fcramLayout = *layout;
coreCtxs = ctxs;
@@ -270,7 +363,6 @@ void main(FcramLayout *layout, KCoreContext *ctxs)
isN3DS = getNumberOfCores() == 4;
memcpy(L1MMUTableAddrs, (const void *)p->L1MMUTableAddrs, 16);
exceptionStackTop = (u32 *)0xFFFF2000 + (1 << (32 - TTBCR - 20));
cfwInfo = p->cfwInfo;
memcpy(originalHandlers + 1, p->originalHandlers, 16);
void **arm11SvcTable = (void**)originalHandlers[2];
@@ -284,4 +376,8 @@ void main(FcramLayout *layout, KCoreContext *ctxs)
rosalinaState = 0;
hasStartedRosalinaNetworkFuncsOnce = false;
// DSB, Flush Prefetch Buffer (more or less "isb")
__asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 4" :: "r" (0) : "memory");
__asm__ __volatile__ ("mcr p15, 0, %0, c7, c5, 4" :: "r" (0) : "memory");
}

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

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -64,6 +64,9 @@ Result GetSystemInfoHook(s64 *out, s32 type, s32 param)
case 0x101:
*out = cfwInfo.rosalinaMenuCombo;
break;
case 0x102:
*out = cfwInfo.rosalinaFlags;
break;
case 0x200: // isRelease
*out = cfwInfo.flags & 1;
@@ -79,7 +82,11 @@ Result GetSystemInfoHook(s64 *out, s32 type, s32 param)
break;
case 0x300: // K11Ext size
*out = (s64)(__end__ - __start__);
*out = (s64)(((u64)kextBasePa << 32) | (u64)(__end__ - __start__));
break;
case 0x301: // stolen SYSTEM memory size
*out = stolenSystemMemRegionSize;
break;
default:

View File

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

View File

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

View File

@@ -100,7 +100,7 @@ Result SendSyncRequestHook(Handle handle)
case 0x50100:
{
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
if(info != NULL && (strcmp(info->name, "srv:") == 0 || (kernelVersion < SYSTEM_VERSION(2, 39, 4) && strcmp(info->name, "srv:pm") == 0)))
if(info != NULL && (strcmp(info->name, "srv:") == 0 || (GET_VERSION_MINOR(kernelVersion) < 39 && strcmp(info->name, "srv:pm") == 0)))
{
char name[9] = { 0 };
memcpy(name, cmdbuf + 1, 8);

View File

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

View File

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

View File

@@ -73,7 +73,7 @@ bool rosalinaThreadLockPredicate(KThread *thread)
return false;
u64 titleId = codeSetOfProcess(process)->titleId;
u32 highTitleId = (u32)(titleId >> 32), lowTitleId = (u32)titleId;
u32 highTitleId = (u32)(titleId >> 32), lowTitleId = (u32)(titleId & ~0xF0000001); // clear N3DS and SAFE_FIRM bits
return
((rosalinaState & 1) && idOfProcess(process) >= nbSection0Modules &&
(highTitleId != 0x00040130 || (highTitleId == 0x00040130 && (lowTitleId == 0x1A02 || lowTitleId == 0x1C02))));

View File

@@ -96,6 +96,36 @@ KObjectMutex__Release:
blx r12
bx lr
.global KProcessHwInfo__MapL1Section_Hook
.type KProcessHwInfo__MapL1Section_Hook, %function
KProcessHwInfo__MapL1Section_Hook:
@r0 => hwInfo
@sp + 0x34 => our ptr to state
add r1, sp, #0x34
str lr, [sp, #-4]!
bl PatchDescriptorAccessControl
ldr lr, [sp], #4
ldmfd sp, {r0-r4}
sub sp, sp, #0x14
add r4, sp, #0x48
mov r11, #0
mov pc, lr
.global KProcessHwInfo__MapL2Section_Hook
.type KProcessHwInfo__MapL2Section_Hook, %function
KProcessHwInfo__MapL2Section_Hook:
@r0 => hwInfo
@sp + 0x34 => our ptr to state
add r1, sp, #0x34
str lr, [sp, #-4]!
bl PatchDescriptorAccessControl
ldr lr, [sp], #4
ldmfd sp, {r0-r4}
sub sp, sp, #0x4C
mov r4, r1
mov r6, r2
mov pc, lr
.global safecpy
.type safecpy, %function
safecpy:
@@ -121,12 +151,14 @@ _safecpy_end:
.section .rodata.safecpy_sz, "a", %progbits
.global safecpy_sz
.type safecpy_sz, %object
safecpy_sz: .word _safecpy_end - safecpy
.section .bss.SGI0Handler, "aw", %nobits
.balign 4
.global SGI0Handler
.type safecpy_sz, %object
SGI0Handler: .skip 4 @ see synchronization.c
.balign 4
@@ -136,4 +168,5 @@ SGI0Handler: .skip 4 @ see synchronization.c
_customInterruptEventObj: .word SGI0Handler
.global customInterruptEvent
.type safecpy_sz, %object
customInterruptEvent: .word _customInterruptEventObj

View File

@@ -148,6 +148,21 @@ static Result loadCode(u64 titleId, prog_addrs_t *shared, u64 programHandle, int
return 0;
}
static Result PLGLDR_Init(Handle *session)
{
Result res;
while (1)
{
res = svcConnectToPort(session, "plg:ldr");
if (R_LEVEL(res) != RL_PERMANENT ||
R_SUMMARY(res) != RS_NOTFOUND ||
R_DESCRIPTION(res) != RD_NOT_FOUND
) break;
svcSleepThread(500000);
}
return res;
}
static Result GetProgramInfo(ExHeader_Info *exheaderInfo, u64 programHandle)
{
Result res = 0;
@@ -172,9 +187,6 @@ static Result GetProgramInfo(ExHeader_Info *exheaderInfo, u64 programHandle)
s64 nbSection0Modules;
svcGetSystemInfo(&nbSection0Modules, 26, 0);
// Force always having sdmc:/ and nand:/rw permission
exheaderInfo->aci.local_caps.storage_info.fs_access_info |= FSACCESS_NANDRW | FSACCESS_SDMC_RW;
// Tweak 3dsx placeholder title exheaderInfo
if (nbSection0Modules == 6 && exheaderInfo->aci.local_caps.title_id == HBLDR_3DSX_TID)
{
@@ -182,10 +194,12 @@ static Result GetProgramInfo(ExHeader_Info *exheaderInfo, u64 programHandle)
HBLDR_PatchExHeaderInfo(exheaderInfo);
hbldrExit();
}
u64 originaltitleId = exheaderInfo->aci.local_caps.title_id;
if(CONFIG(PATCHGAMES) && loadTitleExheaderInfo(exheaderInfo->aci.local_caps.title_id, exheaderInfo))
exheaderInfo->aci.local_caps.title_id = originaltitleId;
else
{
u64 originaltitleId = exheaderInfo->aci.local_caps.title_id;
if(CONFIG(PATCHGAMES) && loadTitleExheaderInfo(exheaderInfo->aci.local_caps.title_id, exheaderInfo))
exheaderInfo->aci.local_caps.title_id = originaltitleId;
}
return res;
}
@@ -255,6 +269,9 @@ static Result LoadProcess(Handle *process, u64 programHandle)
// load code
if (R_SUCCEEDED(res = loadCode(titleId, &sharedAddr, programHandle, csi->flags.compress_exefs_code)))
{
u32 *code = (u32 *)sharedAddr.text_addr;
bool isHomebrew = code[0] == 0xEA000006 && code[8] == 0xE1A0400E;
memcpy(&codesetinfo.name, csi->name, 8);
codesetinfo.program_id = titleId;
codesetinfo.text_addr = vaddr.text_addr;
@@ -271,7 +288,37 @@ static Result LoadProcess(Handle *process, u64 programHandle)
{
res = svcCreateProcess(process, codeset, g_exheaderInfo.aci.kernel_caps.descriptors, count);
svcCloseHandle(codeset);
res = R_SUCCEEDED(res) ? 0 : res;
if (res >= 0)
{
// Try to load a plugin for the game
if (!isHomebrew && ((u32)((titleId >> 0x20) & 0xFFFFFFEDULL) == 0x00040000))
{
// Special case handling: games rebooting the 3DS on old models
if (!isN3DS && g_exheaderInfo.aci.local_caps.core_info.o3ds_system_mode > 0)
{
// Check if the plugin loader is enabled, otherwise skip the loading part
s64 out;
svcGetSystemInfo(&out, 0x10000, 0x102);
if ((out & 1) == 0)
return 0;
}
Handle plgldr = 0;
if (R_SUCCEEDED(PLGLDR_Init(&plgldr)))
{
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(1, 0, 2);
cmdbuf[1] = IPC_Desc_SharedHandles(1);
cmdbuf[2] = *process;
svcSendSyncRequest(plgldr);
svcCloseHandle(plgldr);
}
}
return 0;
}
}
}

View File

@@ -740,7 +740,7 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 ro
}
}
else if(progId == 0x0004013000001702LL) //CFG
else if((progId & ~0xF0000001ULL) == 0x0004013000001702LL) //CFG, SAFE_FIRM CFG
{
static const u8 pattern[] = {
0x06, 0x46, 0x10, 0x48
@@ -826,7 +826,7 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 ro
)) goto error;
}
else if(progId == 0x0004013000001A02LL) //DSP
else if((progId & ~0xF0000001ULL) == 0x0004013000001A02LL) //DSP, SAFE_FIRM DSP
{
static const u8 pattern[] = {
0xE3, 0x10, 0x10, 0x80, 0xE2

View File

@@ -35,7 +35,8 @@ enum singleOptions
PATCHVERSTRING,
SHOWGBABOOT,
PATCHUNITINFO,
DISABLEARM11EXCHANDLERS
DISABLEARM11EXCHANDLERS,
ENABLESAFEFIRMROSALINA,
};
extern u32 config, multiConfig, bootConfig;

View File

@@ -129,16 +129,21 @@ Result GetTitleExHeaderFlags(ExHeader_Arm11CoreInfo *outCoreInfo, ExHeader_Syste
return res;
}
Result GetCurrentAppTitleIdAndPid(u64 *outTitleId, u32 *outPid)
Result GetCurrentAppInfo(FS_ProgramInfo *outProgramInfo, u32 *outPid, u32 *outLaunchFlags)
{
ProcessList_Lock(&g_manager.processList);
Result res;
memset(outProgramInfo, 0, sizeof(FS_ProgramInfo));
if (g_manager.runningApplicationData != NULL) {
*outTitleId = g_manager.runningApplicationData->titleId;
*outPid = g_manager.runningApplicationData->pid;
ProcessData *app = g_manager.runningApplicationData;
outProgramInfo->programId = app->titleId;
outProgramInfo->mediaType = app->mediaType;
*outPid = app->pid;
*outLaunchFlags = app->launchFlags;
res = 0;
} else {
*outTitleId = 0;
*outPid = 0;
res = MAKERESULT(RL_TEMPORARY, RS_NOTFOUND, RM_PM, 0x100);
}
ProcessList_Unlock(&g_manager.processList);

View File

@@ -12,4 +12,4 @@ Result listMergeUniqueDependencies(ProcessData **procs, u64 *dependencies, u32 *
Result GetTitleExHeaderFlags(ExHeader_Arm11CoreInfo *outCoreInfo, ExHeader_SystemInfoFlags *outSiFlags, const FS_ProgramInfo *programInfo);
// Custom
Result GetCurrentAppTitleIdAndPid(u64 *outTitleId, u32 *outPid);
Result GetCurrentAppInfo(FS_ProgramInfo *outProgramInfo, u32 *outPid, u32 *outLaunchFlags);

View File

@@ -56,10 +56,12 @@ static Result loadWithoutDependencies(Handle *outDebug, ProcessData **outProcess
process->pid = pid;
process->titleId = exheaderInfo->aci.local_caps.title_id;;
process->programHandle = programHandle;
process->launchFlags = launchFlags; // not in official PM
process->flags = 0; // will be filled later
process->terminatedNotificationVariation = (launchFlags & 0xF0) >> 4;
process->terminationStatus = TERMSTATUS_RUNNING;
process->refcount = 1;
process->mediaType = programInfo->mediaType; // not in official PM
ProcessList_Unlock(&g_manager.processList);
svcSignalEvent(g_manager.newProcessEvent);
@@ -71,7 +73,11 @@ static Result loadWithoutDependencies(Handle *outDebug, ProcessData **outProcess
u32 serviceCount;
for(serviceCount = 0; serviceCount < 34 && *(u64 *)localcaps->service_access[serviceCount] != 0; serviceCount++);
TRY(FSREG_Register(pid, programHandle, programInfo, &localcaps->storage_info));
// Not in official PM: patch local caps to give access to everything
ExHeader_Arm11StorageInfo storageInfo = localcaps->storage_info;
storageInfo.fs_access_info = 0xFFFFFFFF;
TRY(FSREG_Register(pid, programHandle, programInfo, &storageInfo));
TRY(SRVPM_RegisterProcess(pid, serviceCount, localcaps->service_access));
if (localcaps->reslimit_category <= RESLIMIT_CATEGORY_OTHER) {
@@ -135,6 +141,11 @@ static Result loadWithDependencies(Handle *outDebug, ProcessData **outProcessDat
process->flags |= PROCESSFLAG_DEPENDENCIES_LOADED;
}
if (launchFlags & PMLAUNCHFLAGEXT_FAKE_DEPENDENCY_LOADING) {
// See no evil
numUnique = 0;
}
/*
Official pm does this:
for each dependency:
@@ -208,7 +219,7 @@ static Result launchTitleImpl(Handle *debug, ProcessData **outProcessData, const
TRY(registerProgram(&programHandle, programInfo, programInfoUpdate));
res = LOADER_GetProgramInfo(exheaderInfo, programHandle);
res = R_SUCCEEDED(res) && exheaderInfo->aci.local_caps.core_info.core_version != SYSCOREVER ? (Result)0xC8A05800 : res;
res = R_SUCCEEDED(res) && SYSCOREVER == 2 && exheaderInfo->aci.local_caps.core_info.core_version != SYSCOREVER ? (Result)0xC8A05800 : res;
if (R_FAILED(res)) {
LOADER_UnregisterProgram(programHandle);
@@ -219,18 +230,18 @@ static Result launchTitleImpl(Handle *debug, ProcessData **outProcessData, const
if (IS_N3DS && APPMEMTYPE == 6 && (launchFlags & PMLAUNCHFLAG_NORMAL_APPLICATION) != 0) {
u32 limitMb;
SystemMode n3dsSystemMode = exheaderInfo->aci.local_caps.core_info.n3ds_system_mode;
if ((launchFlags & PMLAUNCHFLAG_FORCE_USE_O3DS_APP_MEM) || n3dsSystemMode == SYSMODE_O3DS_PROD) {
if ((launchFlags & PMLAUNCHFLAG_FORCE_USE_O3DS_APP_MEM) & PMLAUNCHFLAG_FORCE_USE_O3DS_MAX_APP_MEM) {
limitMb = 96;
} else {
switch (exheaderInfo->aci.local_caps.core_info.o3ds_system_mode) {
case SYSMODE_O3DS_PROD: limitMb = 64; break;
case SYSMODE_DEV1: limitMb = 96; break;
case SYSMODE_DEV2: limitMb = 80; break;
default: limitMb = 0; break;
}
bool forceO3dsAppMem = (launchFlags & PMLAUNCHFLAG_FORCE_USE_O3DS_APP_MEM) != 0;
if (forceO3dsAppMem && (launchFlags & PMLAUNCHFLAG_FORCE_USE_O3DS_MAX_APP_MEM) != 0) {
setAppMemLimit(96 << 20);
} else if (forceO3dsAppMem || n3dsSystemMode == SYSMODE_O3DS_PROD) {
switch (exheaderInfo->aci.local_caps.core_info.o3ds_system_mode) {
case SYSMODE_O3DS_PROD: limitMb = 64; break;
case SYSMODE_DEV1: limitMb = 96; break;
case SYSMODE_DEV2: limitMb = 80; break;
default: limitMb = 0; break;
}
// Can be 0:
setAppMemLimit(limitMb << 20);
}
}

View File

@@ -4,6 +4,11 @@
#include <3ds/services/fs.h>
#include "process_data.h"
/// Custom launch flags for PM launch commands.
enum {
PMLAUNCHFLAGEXT_FAKE_DEPENDENCY_LOADING = BIT(24),
};
Result LaunchTitle(u32 *outPid, const FS_ProgramInfo *programInfo, u32 launchFlags);
Result LaunchTitleUpdate(const FS_ProgramInfo *programInfo, const FS_ProgramInfo *programInfoUpdate, u32 launchFlags);
Result LaunchApp(const FS_ProgramInfo *programInfo, u32 launchFlags);

View File

@@ -3,11 +3,24 @@
#include "luma.h"
#include "util.h"
bool hasKExt(void)
{
s64 val;
return svcGetSystemInfo(&val, 0x20000, 0) == 1;
}
u32 getKExtSize(void)
{
s64 val;
Result res = svcGetSystemInfo(&val, 0x10000, 0x300);
return R_FAILED(res) ? 0 : (u32)val;
svcGetSystemInfo(&val, 0x10000, 0x300);
return (u32)val;
}
u32 getStolenSystemMemRegionSize(void)
{
s64 val;
svcGetSystemInfo(&val, 0x10000, 0x301);
return (u32)val;
}
bool isTitleLaunchPrevented(u64 titleId)
@@ -15,5 +28,5 @@ bool isTitleLaunchPrevented(u64 titleId)
s64 numKips = 0;
svcGetSystemInfo(&numKips, 26, 0);
return numKips >= 6 && (titleId & ~N3DS_TID_BIT) == 0x0004003000008A02ULL; // ErrDisp
}
return numKips >= 6 && (titleId & ~(N3DS_TID_MASK | 1)) == 0x0004003000008A02ULL; // ErrDisp
}

View File

@@ -2,5 +2,7 @@
#include <3ds/types.h>
bool hasKExt(void);
u32 getKExtSize(void);
bool isTitleLaunchPrevented(u64 titleId);
u32 getStolenSystemMemRegionSize(void);
bool isTitleLaunchPrevented(u64 titleId);

View File

@@ -52,7 +52,7 @@ void initSystem()
}
static const ServiceManagerServiceEntry services[] = {
{ "pm:app", 3, pmAppHandleCommands, false },
{ "pm:app", 4, pmAppHandleCommands, false },
{ "pm:dbg", 2, pmDbgHandleCommands, false },
{ NULL },
};

View File

@@ -7,6 +7,21 @@
Manager g_manager;
static void giveAllFsArchiveAccessToKip(u32 pid, u64 tid)
{
static const ExHeader_Arm11StorageInfo storageInfo = {
.fs_access_info = 0xFFFFFFFF,
};
static const u64 programHandle = 0xFFFF000000000000LL;
FS_ProgramInfo info = {
.programId = tid,
.mediaType = MEDIATYPE_NAND,
};
assertSuccess(FSREG_Register(pid, programHandle, &info, &storageInfo));
}
void Manager_Init(void *procBuf, size_t numProc)
{
memset(&g_manager, 0, sizeof(Manager));
@@ -34,14 +49,23 @@ void Manager_RegisterKips(void)
process->handle = processHandle;
process->pid = i;
process->refcount = 1;
process->titleId = 0x0004000100001000ULL; // note: same TID for all builtins
process->titleId = 0x0004000100001000ULL; // note: same internal TID for all builtins
process->flags = PROCESSFLAG_KIP;
process->terminationStatus = TERMSTATUS_RUNNING;
assertSuccess(svcSetProcessResourceLimits(processHandle, g_manager.reslimits[RESLIMIT_CATEGORY_OTHER]));
if (i < 5) {
// Exempt rosalina from being resource-limited at all
assertSuccess(svcSetProcessResourceLimits(processHandle, g_manager.reslimits[RESLIMIT_CATEGORY_OTHER]));
}
}
ProcessList_Unlock(&g_manager.processList);
// Give full archive access to us (PM) and Rosalina (real PIDs don't matter, they just have to be unique (?))
// Loader doesn't depend on PM and has its own fs:REG handle so it must do it itself.
giveAllFsArchiveAccessToKip(2, 0x0004013000001202LL); // PM
if (numKips > 5) {
giveAllFsArchiveAccessToKip(5, 0x0004013000006902LL); // Rosalina
}
}
Result UnregisterProcess(u64 titleId)
@@ -66,3 +90,27 @@ Result UnregisterProcess(u64 titleId)
ProcessList_Unlock(&g_manager.processList);
return 0;
}
Result PrepareToChainloadHomebrew(u64 titleId)
{
// Note: I'm allowing this command to be called for non-applications, maybe that'll be useful
// in the future...
ProcessData *foundProcess = NULL;
Result res;
ProcessList_Lock(&g_manager.processList);
foundProcess = ProcessList_FindProcessByTitleId(&g_manager.processList, titleId & ~N3DS_TID_MASK);
if (foundProcess != NULL) {
// Clear the "notify on termination, don't cleanup" flag, so that for ex. APT isn't notified & no need for UnregisterProcess,
// and the "dependencies loaded" flag, so that the dependencies aren't killed (for ex. when
// booting hbmenu instead of Home Menu, in which case the same title is going to be launched...)
foundProcess->flags &= ~(PROCESSFLAG_DEPENDENCIES_LOADED | PROCESSFLAG_NOTIFY_TERMINATION);
res = 0;
} else {
res = MAKERESULT(RL_TEMPORARY, RS_NOTFOUND, RM_PM, 0x100);
}
ProcessList_Unlock(&g_manager.processList);
return res;
}

View File

@@ -21,3 +21,4 @@ extern Manager g_manager;
void Manager_Init(void *procBuf, size_t numProc);
void Manager_RegisterKips(void);
Result UnregisterProcess(u64 titleId);
Result PrepareToChainloadHomebrew(u64 titleId);

View File

@@ -3,6 +3,7 @@
#include "launch.h"
#include "info.h"
#include "util.h"
#include "manager.h"
void pmDbgHandleCommands(void *ctx)
{
@@ -11,10 +12,10 @@ void pmDbgHandleCommands(void *ctx)
u32 cmdhdr = cmdbuf[0];
FS_ProgramInfo programInfo;
Handle debug;
u64 titleId;
Handle debug;
u32 pid;
u32 launchFlags;
switch (cmdhdr >> 16) {
case 1:
@@ -40,12 +41,11 @@ void pmDbgHandleCommands(void *ctx)
// Custom
case 0x100:
titleId = 0;
pid = 0xFFFFFFFF;
cmdbuf[1] = GetCurrentAppTitleIdAndPid(&titleId, &pid);
cmdbuf[0] = IPC_MakeHeader(0x100, 4, 0);
memcpy(cmdbuf + 2, &titleId, 8);
cmdbuf[4] = pid;
cmdbuf[1] = GetCurrentAppInfo(&programInfo, &pid, &launchFlags);
cmdbuf[0] = IPC_MakeHeader(0x100, 7, 0);
memcpy(cmdbuf + 2, &programInfo, sizeof(FS_ProgramInfo));
cmdbuf[6] = pid;
cmdbuf[7] = launchFlags;
break;
case 0x101:
cmdbuf[1] = DebugNextApplicationByForce(cmdbuf[1] != 0);
@@ -59,7 +59,11 @@ void pmDbgHandleCommands(void *ctx)
cmdbuf[2] = IPC_Desc_MoveHandles(1);
cmdbuf[3] = debug;
break;
case 0x103:
memcpy(&titleId, cmdbuf + 1, 8);
cmdbuf[1] = PrepareToChainloadHomebrew(titleId);
cmdbuf[0] = IPC_MakeHeader(0x103, 1, 0);
break;
default:
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);
cmdbuf[1] = 0xD900182F;

View File

@@ -29,10 +29,12 @@ typedef struct ProcessData {
u32 pid;
u64 titleId;
u64 programHandle;
u32 launchFlags;
u8 flags;
u8 terminatedNotificationVariation;
TerminationStatus terminationStatus;
u8 refcount;
FS_MediaType mediaType;
} ProcessData;
typedef struct ProcessList {

View File

@@ -4,6 +4,9 @@
#include "manager.h"
#include "luma.h"
#define CPUTIME_MULTI_MASK BIT(7)
#define CPUTIME_SINGLE_MASK 0
typedef s64 ReslimitValues[10];
static const ResourceLimitType g_reslimitInitOrder[10] = {
@@ -189,20 +192,23 @@ static ReslimitValues g_n3dsReslimitValues[4] = {
Both modes pause threads they don't want to run in thread selection, and unpause them when needed.
If the threads that are intended to be paused is running an SVC, the pause will happen *after* SVC return.
Mode0 (unsure)
Mode0 "multi"
Starting by "sysmodule" threads, alternatively allow (if preemptible) only sysmodule threads,
and then only application threads to run.
The latter has an exception; if "sysmodule" threads have run for less than 2usec, they
The latter has an exception; if "sysmodule" threads have run for less than 8usec (value is a kernel bug), they
are unpaused an allowed to run instead.
This happens at a rate of 1ms * (cpuTime/100).
This happens at a rate of 2ms * (cpuTime/100).
Mode1
Mode1 "single"
This mode is half-broken due to a kernel bug (when "current thread" is the priority 0 kernel thread).
When this mode is enabled, only one application thread is allowed to be created on core1.
This divides the core1 time into slices of 12.5ms.
This divides the core1 time into slices of 25ms.
The "application" thread is given cpuTime% of the slice.
The "sysmodules" threads are given a total of (90 - cpuTime)% of the slice.
@@ -243,7 +249,10 @@ static ReslimitValues *fixupReslimitValues(void)
{
// In order: APPLICATION, SYS_APPLET, LIB_APPLET, OTHER
// Fixup "commit" reslimit
u32 sysmemalloc = SYSMEMALLOC + getKExtSize();
// Note: we lie in the reslimit and make as if neither KExt nor Roslina existed, to avoid breakage
u32 sysmemalloc = SYSMEMALLOC + (hasKExt() ? getStolenSystemMemRegionSize() : 0);
ReslimitValues *values = !IS_N3DS ? g_o3dsReslimitValues : g_n3dsReslimitValues;
static const u32 minAppletMemAmount = 0x1200000;
@@ -315,22 +324,23 @@ void setAppCpuTimeLimitAndSchedModeFromDescriptor(u64 titleId, u16 descriptor)
- app has a non-0 cputime descriptor in exhdr: maximum core1 cputime reslimit and scheduling
mode are set according to it. Current reslimit is set to 0. SetAppResourceLimit *is* needed
to use core1.
- app has a 0 cputime descriptor: maximum is set to 80.
Current reslimit is set to 0, and SetAppResourceLimit *is* needed
- app has a 0 cputime descriptor: maximum is set to 80, scheduling mode to "single" (broken).
Current reslimit is set to 0, and SetAppResourceLimit *is* also needed
to use core1, **EXCEPT** for an hardcoded set of titles.
*/
u8 cpuTime = (u8)descriptor;
assertSuccess(setAppCpuTimeLimit(0)); // remove preemption first.
g_manager.cpuTimeBase = 0;
u32 currentValueToSet = g_manager.cpuTimeBase; // 0
if (cpuTime != 0) {
// Set core1 scheduling mode
g_manager.maxAppCpuTime = cpuTime & 0x7F;
assertSuccess(svcKernelSetState(6, 3, (cpuTime & 0x80) ? 0LL : 1LL));
} else {
if (cpuTime == 0) {
// 2.0 apps have this exheader field correctly filled, very often to 0x9E (1.0 titles don't).
u32 titleUid = ((u32)titleId >> 8) & 0xFFFFF;
g_manager.maxAppCpuTime = 80;
// Default setting is 80% max "single", with a current value of 0
cpuTime = CPUTIME_SINGLE_MASK | 80;
static const u32 numOverrides = sizeof(g_startCpuTimeOverrides) / sizeof(g_startCpuTimeOverrides[0]);
if (titleUid >= g_startCpuTimeOverrides[0].titleUid && titleUid <= g_startCpuTimeOverrides[numOverrides - 1].titleUid) {
@@ -338,15 +348,26 @@ void setAppCpuTimeLimitAndSchedModeFromDescriptor(u64 titleId, u16 descriptor)
for (u32 i = 0; i < numOverrides && titleUid < g_startCpuTimeOverrides[i].titleUid; i++);
if (i < numOverrides) {
if (g_startCpuTimeOverrides[i].value > 100 && g_startCpuTimeOverrides[i].value < 200) {
assertSuccess(svcKernelSetState(6, 3, 0LL));
assertSuccess(setAppCpuTimeLimit(g_startCpuTimeOverrides[i].value - 100));
cpuTime = CPUTIME_MULTI_MASK | 80; // "multi", max 80%
currentValueToSet = g_startCpuTimeOverrides[i].value - 100;
} else {
assertSuccess(svcKernelSetState(6, 3, 1LL));
assertSuccess(setAppCpuTimeLimit(g_startCpuTimeOverrides[i].value));
cpuTime = CPUTIME_SINGLE_MASK | 80; // "single", max 80%
currentValueToSet = g_startCpuTimeOverrides[i].value;
}
}
}
}
// Set core1 scheduling mode
assertSuccess(svcKernelSetState(6, 3, (cpuTime & CPUTIME_MULTI_MASK) ? 0LL : 1LL));
// Set max value (limit)
g_manager.maxAppCpuTime = cpuTime & 0x7F;
// Set current value (for 1.0 apps)
if (currentValueToSet != 0) {
assertSuccess(setAppCpuTimeLimit(currentValueToSet));
}
}
Result SetAppResourceLimit(u32 mbz, ResourceLimitType category, u32 value, u64 mbz2)

View File

@@ -6,30 +6,10 @@
#include "exheader_info_heap.h"
#include "task_runner.h"
static Result fsRegSetupPermissions(void)
{
u32 pid;
Result res;
FS_ProgramInfo info;
ExHeader_Arm11StorageInfo storageInfo = {
.fs_access_info = FSACCESS_SDMC_RW,
};
info.programId = 0x0004013000001202LL; // PM's TID
info.mediaType = MEDIATYPE_NAND;
if(R_SUCCEEDED(res = svcGetProcessId(&pid, CUR_PROCESS_HANDLE)))
res = FSREG_Register(pid, 0xFFFF000000000000LL, &info, &storageInfo);
return res;
}
void forceMountSdCard(void)
{
FS_Archive sdmcArchive;
assertSuccess(fsRegSetupPermissions());
assertSuccess(fsInit());
assertSuccess(FSUSER_OpenArchive(&sdmcArchive, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")));
// No need to clean up things as we will firmlaunch straight away

View File

@@ -28,8 +28,9 @@ INCLUDES := include include/gdb include/menus include/redshift
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
DEFINES := -DARM11 -D_3DS
CFLAGS := -g -std=gnu11 -Wall -Wextra -Werror -Wno-unused-value -Os -mword-relocations \
-fomit-frame-pointer -ffunction-sections -fdata-sections -fno-math-errno \
CFLAGS := -g -std=gnu11 -Wall -Wextra -Wno-unused-value -O2 -mword-relocations \
-fomit-frame-pointer -ffunction-sections -fdata-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE)
@@ -130,6 +131,8 @@ $(OUTPUT).elf : $(OFILES)
@$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
@$(NM) -CSn $@ > $(notdir $*.lst)
draw.o: CFLAGS += -O3
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------

View File

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

View File

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

View File

@@ -54,9 +54,14 @@
#define GPU_TRANSFER_CNT REG32(0x10400C18)
#define GPU_CMDLIST_CNT REG32(0x104018F0)
#define LCD_TOP_BRIGHTNESS REG32(0x10202240)
#define LCD_BOT_BRIGHTNESS REG32(0x10202A40)
#define FB_BOTTOM_VRAM_ADDR ((void *)0x1F48F000) // cached
#define FB_BOTTOM_VRAM_PA 0x1848F000
#define FB_BOTTOM_SIZE (320 * 240 * 2)
#define FB_SCREENSHOT_SIZE (52 + 400 * 240 * 3)
#define SCREEN_BOT_WIDTH 320
#define SCREEN_BOT_HEIGHT 240
@@ -68,23 +73,32 @@
#define COLOR_WHITE RGB565(0x1F, 0x3F, 0x1F)
#define COLOR_RED RGB565(0x1F, 0x00, 0x00)
#define COLOR_GREEN RGB565(0x00, 0x1F, 0x00)
#define COLOR_LIME RGB565(0x00, 0xFF, 0x00)
#define COLOR_BLACK RGB565(0x00, 0x00, 0x00)
#define DRAW_MAX_FORMATTED_STRING_SIZE 512
void Draw_Init(void);
void Draw_Lock(void);
void Draw_Unlock(void);
void Draw_DrawCharacter(u32 posX, u32 posY, u32 color, char character);
u32 Draw_DrawString(u32 posX, u32 posY, u32 color, const char *string);
__attribute__((format(printf,4,5)))
u32 Draw_DrawFormattedString(u32 posX, u32 posY, u32 color, const char *fmt, ...);
void Draw_FillFramebuffer(u32 value);
void Draw_ClearFramebuffer(void);
void Draw_SetupFramebuffer(void);
u32 Draw_AllocateFramebufferCache(void);
void Draw_FreeFramebufferCache(void);
void *Draw_GetFramebufferCache(void);
u32 Draw_GetFramebufferCacheSize(void);
u32 Draw_SetupFramebuffer(void);
void Draw_RestoreFramebuffer(void);
void Draw_FlushFramebuffer(void);
u32 Draw_GetCurrentFramebufferAddress(bool top, bool left);
void Draw_CreateBitmapHeader(u8 *dst, u32 width, u32 heigth);
void Draw_ConvertFrameBufferLine(u8 *line, bool top, bool left, u32 y);
void Draw_ConvertFrameBufferLines(u8 *buf, u32 startingLine, u32 numLines, bool top, bool left);

View File

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

View File

@@ -33,4 +33,5 @@
#define HBLDR_DEFAULT_3DSX_TID 0x000400000D921E00ULL
#define HBLDR_3DSX_TID (*(vu64 *)0x1FF81100)
void HBLDR_RestartHbApplication(void *p);
void HBLDR_HandleCommands(void *ctx);

View File

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

View File

@@ -27,33 +27,25 @@
#pragma once
#include <3ds/types.h>
#include <3ds/services/hid.h>
#include "MyThread.h"
#include "utils.h"
#define HID_PAD (REG32(0x10146000) ^ 0xFFF)
#define BUTTON_A (1 << 0)
#define BUTTON_B (1 << 1)
#define BUTTON_SELECT (1 << 2)
#define BUTTON_START (1 << 3)
#define BUTTON_RIGHT (1 << 4)
#define BUTTON_LEFT (1 << 5)
#define BUTTON_UP (1 << 6)
#define BUTTON_DOWN (1 << 7)
#define BUTTON_R1 (1 << 8)
#define BUTTON_L1 (1 << 9)
#define BUTTON_X (1 << 10)
#define BUTTON_Y (1 << 11)
#define DEFAULT_MENU_COMBO (BUTTON_L1 | BUTTON_DOWN | BUTTON_SELECT)
#define DEFAULT_MENU_COMBO (KEY_L | KEY_DDOWN | KEY_SELECT)
#define DIRECTIONAL_KEYS (KEY_DOWN | KEY_UP | KEY_LEFT | KEY_RIGHT)
#define CORE_APPLICATION 0
#define CORE_SYSTEM 1
typedef enum MenuItemAction {
METHOD,
MENU
MENU_END = 0,
METHOD = 1,
MENU = 2,
} MenuItemAction;
typedef struct MenuItem {
const char *title;
@@ -62,27 +54,37 @@ typedef struct MenuItem {
struct Menu *menu;
void (*method)(void);
};
bool (*visibility)(void);
} MenuItem;
typedef struct Menu {
const char *title;
u32 nbItems;
MenuItem items[0x40];
MenuItem items[16];
} Menu;
extern bool terminationRequest;
extern Handle terminationRequestEvent;
extern bool isN3DS;
extern bool menuShouldExit;
extern bool preTerminationRequested;
extern Handle preTerminationEvent;
extern u32 menuCombo;
u32 waitInputWithTimeout(u32 msec);
u32 waitInputWithTimeout(s32 msec);
u32 waitInput(void);
u32 waitComboWithTimeout(u32 msec);
u32 waitComboWithTimeout(s32 msec);
u32 waitCombo(void);
bool menuCheckN3ds(void);
u32 menuCountItems(const Menu *menu);
MyThread *menuCreateThread(void);
void menuEnter(void);
void menuLeave(void);
void menuThreadMain(void);
void menuShow(Menu *root);
void menuEnter(void);
void menuLeave(void);
void menuThreadMain(void);
void menuShow(Menu *root);
void DispMessage(const char *title, const char *message);
u32 DispErrMessage(const char *title, const char *message, const Result error);
void DisplayPluginMenu(u32 *cmdbuf);

View File

@@ -33,8 +33,12 @@
extern Menu rosalinaMenu;
void RosalinaMenu_TakeScreenshot(void);
void RosalinaMenu_ChangeScreenBrightness(void);
void RosalinaMenu_ShowCredits(void);
void RosalinaMenu_ProcessList(void);
void RosalinaMenu_PowerOff(void);
void RosalinaMenu_Reboot(void);
void RosalinaMenu_Cheats(void);
bool rosalinaMenuShouldShowDebugInfo(void);
void RosalinaMenu_ShowDebugInfo(void);

View File

@@ -30,7 +30,7 @@
extern Menu screenFiltersMenu;
int screenFiltersCurrentTemperature;
extern int screenFiltersCurrentTemperature;
void screenFiltersSetDisabled(void);
void screenFiltersReduceBlueLevel1(void);

View File

@@ -30,4 +30,4 @@
#include <time.h>
Result ntpGetTimeStamp(time_t *outTimestamp);
Result ntpSetTimeDate(const struct tm *localt);
Result ntpSetTimeDate(time_t timestamp);

View File

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

View File

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

View File

@@ -6,6 +6,12 @@
#include <3ds/services/pmapp.h>
#include <3ds/services/pmdbg.h>
Result PMDBG_GetCurrentAppTitleIdAndPid(u64 *outTitleId, u32 *outPid);
/// Custom launch flags for PM launch commands.
enum {
PMLAUNCHFLAGEXT_FAKE_DEPENDENCY_LOADING = BIT(24),
};
Result PMDBG_GetCurrentAppInfo(FS_ProgramInfo *outProgramInfo, u32 *outPid, u32 *outLaunchFlags);
Result PMDBG_DebugNextApplicationByForce(bool debug);
Result PMDBG_LaunchTitleDebug(Handle *outDebug, const FS_ProgramInfo *programInfo, u32 launchFlags);
Result PMDBG_PrepareToChainloadHomebrew(u64 titleId);

View File

@@ -29,4 +29,5 @@
#include <3ds/types.h>
#include "menu.h"
void ProcessPatchesMenu_PatchUnpatchFSDirectly(void);
Result OpenProcessByName(const char *name, Handle *h);
Result PatchProcessByName(const char *name, Result (*func)(u32 size));

View File

@@ -0,0 +1,31 @@
/*
* This file is part of Luma3DS
* Copyright (C) 2016-2018 Aurora Wright, TuxSH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#pragma once
void Sleep__Init(void);
void Sleep__HandleNotification(u32 notifId);
bool Sleep__Status(void);

View File

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

View File

@@ -27,7 +27,9 @@
#pragma once
#include <3ds/svc.h>
#include <3ds/srv.h>
#include <3ds/result.h>
#include <3ds/ipc.h>
#include "csvc.h"
// For accessing physmem uncached (and directly)
@@ -56,4 +58,28 @@ static inline void *decodeArmBranch(const void *src)
return (void *)((const u8 *)src + 8 + off);
}
static inline void assertSuccess(Result res)
{
if(R_FAILED(res))
svcBreak(USERBREAK_PANIC);
}
static inline void error(u32* cmdbuf, Result rc)
{
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);
cmdbuf[1] = rc;
}
extern bool isN3DS;
Result OpenProcessByName(const char *name, Handle *h);
Result SaveSettings(void);
static inline bool isServiceUsable(const char *name)
{
bool r;
return R_SUCCEEDED(srvIsServiceRegistered(&r, name)) && r;
}
void formatMemoryPermission(char *outbuf, MemPerm perm);
void formatUserMemoryState(char *outbuf, MemState state);
u32 formatMemoryMapOfProcess(char *outbuf, u32 bufLen, Handle handle);

View File

@@ -37,7 +37,7 @@ AccessControlInfo:
CoreVersion : 2
DescVersion : 2
MemoryType : System # Application / System / Base
MemoryType : Base # Application / System / Base
HandleTableSize: 150
MemoryMapping:

View File

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

View File

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

View File

@@ -32,22 +32,22 @@
#include "memory.h"
#include "menu.h"
#include "utils.h"
#include "csvc.h"
u8 framebufferCache[FB_BOTTOM_SIZE];
#define KERNPA2VA(a) ((a) + (GET_VERSION_MINOR(osGetKernelVersion()) < 44 ? 0xD0000000 : 0xC0000000))
static u32 gpuSavedFramebufferAddr1, gpuSavedFramebufferAddr2, gpuSavedFramebufferFormat, gpuSavedFramebufferStride;
static u32 framebufferCacheSize;
static void *framebufferCache;
static RecursiveLock lock;
void Draw_Init(void)
{
RecursiveLock_Init(&lock);
}
void Draw_Lock(void)
{
static bool lockInitialized = false;
if(!lockInitialized)
{
RecursiveLock_Init(&lock);
lockInitialized = true;
}
RecursiveLock_Lock(&lock);
}
@@ -58,7 +58,7 @@ void Draw_Unlock(void)
void Draw_DrawCharacter(u32 posX, u32 posY, u32 color, char character)
{
volatile u16 *const fb = (volatile u16 *const)FB_BOTTOM_VRAM_ADDR;
u16 *const fb = (u16 *)FB_BOTTOM_VRAM_ADDR;
s32 y;
for(y = 0; y < 10; y++)
@@ -129,34 +129,78 @@ void Draw_ClearFramebuffer(void)
Draw_FillFramebuffer(0);
}
void Draw_SetupFramebuffer(void)
u32 Draw_AllocateFramebufferCache(void)
{
// Try to see how much we can allocate...
// Can't use fbs in FCRAM when Home Menu is active (AXI config related maybe?)
u32 addr = 0x0D000000;
u32 tmp;
u32 minSize = (FB_BOTTOM_SIZE + 0xFFF) & ~0xFFF;
u32 maxSize = (FB_SCREENSHOT_SIZE + 0xFFF) & ~0xFFF;
u32 remaining = (u32)osGetMemRegionFree(MEMREGION_SYSTEM);
u32 size = remaining < maxSize ? remaining : maxSize;
if (size < minSize || R_FAILED(svcControlMemoryEx(&tmp, addr, 0, size, MEMOP_ALLOC, MEMREGION_SYSTEM | MEMPERM_READ | MEMPERM_WRITE, true)))
{
framebufferCache = NULL;
framebufferCacheSize = 0;
}
else
{
framebufferCache = (u32 *)addr;
framebufferCacheSize = size;
}
return framebufferCacheSize;
}
void Draw_FreeFramebufferCache(void)
{
u32 tmp;
svcControlMemory(&tmp, (u32)framebufferCache, 0, framebufferCacheSize, MEMOP_FREE, 0);
framebufferCacheSize = 0;
framebufferCache = NULL;
}
void *Draw_GetFramebufferCache(void)
{
return framebufferCache;
}
u32 Draw_GetFramebufferCacheSize(void)
{
return framebufferCacheSize;
}
u32 Draw_SetupFramebuffer(void)
{
while((GPU_PSC0_CNT | GPU_PSC1_CNT | GPU_TRANSFER_CNT | GPU_CMDLIST_CNT) & 1);
svcFlushEntireDataCache();
Draw_FlushFramebuffer();
memcpy(framebufferCache, FB_BOTTOM_VRAM_ADDR, FB_BOTTOM_SIZE);
Draw_ClearFramebuffer();
Draw_FlushFramebuffer();
gpuSavedFramebufferAddr1 = GPU_FB_BOTTOM_ADDR_1;
gpuSavedFramebufferAddr2 = GPU_FB_BOTTOM_ADDR_2;
gpuSavedFramebufferFormat = GPU_FB_BOTTOM_FMT;
gpuSavedFramebufferStride = GPU_FB_BOTTOM_STRIDE;
GPU_FB_BOTTOM_ADDR_1 = GPU_FB_BOTTOM_ADDR_2 = FB_BOTTOM_VRAM_PA;
GPU_FB_BOTTOM_FMT = (GPU_FB_BOTTOM_FMT & ~7) | 2;
GPU_FB_BOTTOM_FMT = (GPU_FB_BOTTOM_FMT & ~7) | GSP_RGB565_OES;
GPU_FB_BOTTOM_STRIDE = 240 * 2;
Draw_FlushFramebuffer();
return framebufferCacheSize;
}
void Draw_RestoreFramebuffer(void)
{
memcpy(FB_BOTTOM_VRAM_ADDR, framebufferCache, FB_BOTTOM_SIZE);
Draw_FlushFramebuffer();
GPU_FB_BOTTOM_ADDR_1 = gpuSavedFramebufferAddr1;
GPU_FB_BOTTOM_ADDR_2 = gpuSavedFramebufferAddr2;
GPU_FB_BOTTOM_FMT = gpuSavedFramebufferFormat;
GPU_FB_BOTTOM_STRIDE = gpuSavedFramebufferStride;
Draw_FlushFramebuffer();
}
void Draw_FlushFramebuffer(void)
@@ -267,16 +311,37 @@ static inline void Draw_ConvertPixelToBGR8(u8 *dst, const u8 *src, GSPGPU_Frameb
}
}
void Draw_ConvertFrameBufferLine(u8 *line, bool top, bool left, u32 y)
typedef struct FrameBufferConvertArgs {
u8 *buf;
u8 startingLine;
u8 numLines;
bool top;
bool left;
} FrameBufferConvertArgs;
static void Draw_ConvertFrameBufferLinesKernel(const FrameBufferConvertArgs *args)
{
GSPGPU_FramebufferFormats fmt = top ? (GSPGPU_FramebufferFormats)(GPU_FB_TOP_FMT & 7) : (GSPGPU_FramebufferFormats)(GPU_FB_BOTTOM_FMT & 7);
u32 width = top ? 400 : 320;
u8 formatSizes[] = { 4, 3, 2, 2, 2 };
u32 stride = top ? GPU_FB_TOP_STRIDE : GPU_FB_BOTTOM_STRIDE;
static const u8 formatSizes[] = { 4, 3, 2, 2, 2 };
u32 pa = Draw_GetCurrentFramebufferAddress(top, left);
u8 *addr = (u8 *)PA_PTR(pa);
GSPGPU_FramebufferFormats fmt = args->top ? (GSPGPU_FramebufferFormats)(GPU_FB_TOP_FMT & 7) : (GSPGPU_FramebufferFormats)(GPU_FB_BOTTOM_FMT & 7);
u32 width = args->top ? 400 : 320;
u32 stride = args->top ? GPU_FB_TOP_STRIDE : GPU_FB_BOTTOM_STRIDE;
for(u32 x = 0; x < width; x++)
Draw_ConvertPixelToBGR8(line + x * 3 , addr + x * stride + y * formatSizes[(u8)fmt], fmt);
u32 pa = Draw_GetCurrentFramebufferAddress(args->top, args->left);
u8 *addr = (u8 *)KERNPA2VA(pa);
for (u32 y = args->startingLine; y < args->startingLine + args->numLines; y++)
{
for(u32 x = 0; x < width; x++)
{
__builtin_prefetch(addr + x * stride + y * formatSizes[fmt], 0, 3);
Draw_ConvertPixelToBGR8(args->buf + (x + width * y) * 3 , addr + x * stride + y * formatSizes[fmt], fmt);
}
}
}
void Draw_ConvertFrameBufferLines(u8 *buf, u32 startingLine, u32 numLines, bool top, bool left)
{
FrameBufferConvertArgs args = { buf, (u8)startingLine, (u8)numLines, top, left };
svcCustomBackdoor(Draw_ConvertFrameBufferLinesKernel, &args);
}

View File

@@ -32,14 +32,7 @@
#include "fmt.h"
#include "ifile.h"
extern Handle terminationRequestEvent;
static inline void assertSuccess(Result res)
{
if(R_FAILED(res))
svcBreak(USERBREAK_PANIC);
}
extern Handle preTerminationEvent;
static MyThread errDispThread;
static u8 ALIGN(8) errDispThreadStack[0xD00];
@@ -254,7 +247,7 @@ void ERRF_HandleCommands(void)
{
ERRF_GetErrInfo(&info, (cmdbuf + 1), sizeof(ERRF_FatalErrInfo));
ERRF_SaveErrorToFile(&info);
if(info.type != ERRF_ERRTYPE_LOGGED || info.procId == 0)
if(!menuShouldExit && (info.type != ERRF_ERRTYPE_LOGGED || info.procId == 0))
{
menuEnter();
@@ -321,7 +314,7 @@ void errDispThreadMain(void)
do
{
handles[0] = terminationRequestEvent;
handles[0] = preTerminationEvent;
handles[1] = serverHandle;
handles[2] = sessionHandle;
@@ -365,7 +358,7 @@ void errDispThreadMain(void)
}
}
}
while(!terminationRequest);
while(!preTerminationRequested);
svcCloseHandle(sessionHandle);
svcCloseHandle(clientHandle);

View File

@@ -9,15 +9,15 @@
#include "gdb/net.h"
#include "gdb/debug.h"
extern Handle terminationRequestEvent;
extern bool terminationRequest;
extern Handle preTerminationEvent;
extern bool preTerminationRequested;
void GDB_RunMonitor(GDBServer *server)
{
Handle handles[3 + MAX_DEBUG];
Result r = 0;
handles[0] = terminationRequestEvent;
handles[0] = preTerminationEvent;
handles[1] = server->super.shall_terminate_event;
handles[2] = server->statusUpdated;
@@ -81,5 +81,5 @@ void GDB_RunMonitor(GDBServer *server)
RecursiveLock_Unlock(&ctx->lock);
}
}
while(!terminationRequest && server->super.running);
while(!preTerminationRequested && server->super.running);
}

View File

@@ -10,6 +10,9 @@
#include "csvc.h"
#include "fmt.h"
#include "gdb/breakpoints.h"
#include "utils.h"
#include "../utils.h"
struct
{
@@ -17,12 +20,15 @@ struct
GDBCommandHandler handler;
} remoteCommandHandlers[] =
{
{ "convertvatopa" , GDB_REMOTE_COMMAND_HANDLER(ConvertVAToPA) },
{ "syncrequestinfo" , GDB_REMOTE_COMMAND_HANDLER(SyncRequestInfo) },
{ "translatehandle" , GDB_REMOTE_COMMAND_HANDLER(TranslateHandle) },
{ "listallhandles" , GDB_REMOTE_COMMAND_HANDLER(ListAllHandles) },
{ "getmmuconfig" , GDB_REMOTE_COMMAND_HANDLER(GetMmuConfig) },
{ "getmemregions" , GDB_REMOTE_COMMAND_HANDLER(GetMemRegions) },
{ "flushcaches" , GDB_REMOTE_COMMAND_HANDLER(FlushCaches) },
{ "toggleextmemaccess", GDB_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess) },
{ "catchsvc" , GDB_REMOTE_COMMAND_HANDLER(CatchSvc) },
};
static const char *GDB_SkipSpaces(const char *pos)
@@ -32,6 +38,50 @@ static const char *GDB_SkipSpaces(const char *pos)
return nextpos;
}
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ConvertVAToPA)
{
bool ok;
int n;
u32 val;
u32 pa;
char * end;
char outbuf[GDB_BUF_LEN / 2 + 1];
if(ctx->commandData[0] == 0)
return GDB_ReplyErrno(ctx, EILSEQ);
val = xstrtoul(ctx->commandData, &end, 0, true, &ok);
if(!ok)
return GDB_ReplyErrno(ctx, EILSEQ);
if (val >= 0x40000000)
pa = svcConvertVAToPA((const void *)val, false);
else
{
Handle process;
Result r = svcOpenProcess(&process, ctx->pid);
if(R_FAILED(r))
{
n = sprintf(outbuf, "Invalid process (wtf?)\n");
goto end;
}
r = svcControlProcess(process, PROCESSOP_GET_PA_FROM_VA, (u32)&pa, val);
svcCloseHandle(process);
if (R_FAILED(r))
{
n = sprintf(outbuf, "An error occured: %08X\n", r);
goto end;
}
}
n = sprintf(outbuf, "va: 0x%08X, pa: 0x%08X, b31: 0x%08X\n", val, pa, pa | (1 << 31));
end:
return GDB_SendHexPacket(ctx, outbuf, n);
}
GDB_DECLARE_REMOTE_COMMAND_HANDLER(SyncRequestInfo)
{
char outbuf[GDB_BUF_LEN / 2 + 1];
@@ -116,6 +166,29 @@ end:
return GDB_SendHexPacket(ctx, outbuf, n);
}
enum
{
TOKEN_KAUTOOBJECT = 0,
TOKEN_KSYNCHRONIZATIONOBJECT = 1,
TOKEN_KEVENT = 0x1F,
TOKEN_KSEMAPHORE = 0x2F,
TOKEN_KTIMER = 0x35,
TOKEN_KMUTEX = 0x39,
TOKEN_KDEBUG = 0x4D,
TOKEN_KSERVERPORT = 0x55,
TOKEN_KDMAOBJECT = 0x59,
TOKEN_KCLIENTPORT = 0x65,
TOKEN_KCODESET = 0x68,
TOKEN_KSESSION = 0x70,
TOKEN_KTHREAD = 0x8D,
TOKEN_KSERVERSESSION = 0x95,
TOKEN_KCLIENTSESSION = 0xA5,
TOKEN_KPORT = 0xA8,
TOKEN_KSHAREDMEMORY = 0xB0,
TOKEN_KPROCESS = 0xC5,
TOKEN_KRESOURCELIMIT = 0xC8
};
GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle)
{
bool ok;
@@ -124,10 +197,11 @@ GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle)
int n;
Result r;
u32 kernelAddr;
s64 token;
Handle handle, process;
s64 refcountRaw;
u32 refcount;
char classBuf[32], serviceBuf[12] = { 0 };
char classBuf[32], serviceBuf[12] = { 0 }, ownerBuf[50] = { 0 };
char outbuf[GDB_BUF_LEN / 2 + 1];
if(ctx->commandData[0] == 0)
@@ -159,19 +233,98 @@ GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle)
svcTranslateHandle(&kernelAddr, classBuf, handle);
svcGetHandleInfo(&refcountRaw, handle, 1);
svcGetHandleInfo(&token, handle, 0x10001);
svcControlService(SERVICEOP_GET_NAME, serviceBuf, handle);
refcount = (u32)(refcountRaw - 1);
if(serviceBuf[0] != 0)
n = sprintf(outbuf, "(%s *)0x%08lx /* %s handle, %lu %s */\n", classBuf, kernelAddr, serviceBuf, refcount, refcount == 1 ? "reference" : "references");
else if (token == TOKEN_KPROCESS)
{
svcGetProcessInfo((s64 *)serviceBuf, handle, 0x10000);
n = sprintf(outbuf, "(%s *)0x%08x /* process: %s, %u %s */\n", classBuf, kernelAddr, serviceBuf, refcount, refcount == 1 ? "reference" : "references");
}
else
n = sprintf(outbuf, "(%s *)0x%08lx /* %lu %s */\n", classBuf, kernelAddr, refcount, refcount == 1 ? "reference" : "references");
{
s64 owner;
if (R_SUCCEEDED(svcGetHandleInfo(&owner, handle, 0x10002)))
{
svcGetProcessInfo((s64 *)serviceBuf, (u32)owner, 0x10000);
svcCloseHandle((u32)owner);
sprintf(ownerBuf, " owner: %s", serviceBuf);
}
n = sprintf(outbuf, "(%s *)0x%08x /* %u %s%s */\n", classBuf, kernelAddr, refcount, refcount == 1 ? "reference" : "references", ownerBuf);
}
end:
svcCloseHandle(handle);
svcCloseHandle(process);
return GDB_SendHexPacket(ctx, outbuf, n);
}
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ListAllHandles)
{
bool ok;
u32 val;
char *end;
int n = 0;
Result r;
s32 count = 0;
Handle process, procHandles[0x100];
char outbuf[GDB_BUF_LEN / 2 + 1];
if(ctx->commandData[0] == 0)
val = 0; ///< All handles
else
{ // Get handles of specified type
val = xstrtoul(ctx->commandData, &end, 0, true, &ok);
if(!ok)
return GDB_ReplyErrno(ctx, EILSEQ);
end = (char *)GDB_SkipSpaces(end);
if(*end != 0)
return GDB_ReplyErrno(ctx, EILSEQ);
}
r = svcOpenProcess(&process, ctx->pid);
if(R_FAILED(r))
{
n = sprintf(outbuf, "Invalid process (wtf?)\n");
goto end;
}
if (R_FAILED(count = svcControlProcess(process, PROCESSOP_GET_ALL_HANDLES, (u32)procHandles, val)))
n = sprintf(outbuf, "An error occured: %08X\n", count);
else if (count == 0)
n = sprintf(outbuf, "Process has no handles ?\n");
else
{
n = sprintf(outbuf, "Found %d handles.\n", count);
const char *comma = "";
for (s32 i = 0; i < count && n < (GDB_BUF_LEN >> 1) - 20; ++i)
{
Handle handle = procHandles[i];
n += sprintf(outbuf + n, "%s0x%08X", comma, handle);
if (((i + 1) % 8) == 0)
{
outbuf[n++] = '\n';
comma = "";
}
else
comma = ", ";
}
}
end:
svcCloseHandle(process);
return GDB_SendHexPacket(ctx, outbuf, n);
}
extern bool isN3DS;
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig)
{
@@ -208,84 +361,19 @@ GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig)
return GDB_SendHexPacket(ctx, outbuf, n);
}
static const char *FormatMemPerm(u32 perm)
{
if (perm == MEMPERM_DONTCARE)
return "???";
static char buf[4] = {0};
buf[0] = perm & MEMPERM_READ ? 'r' : '-';
buf[1] = perm & MEMPERM_WRITE ? 'w' : '-';
buf[2] = perm & MEMPERM_EXECUTE ? 'x' : '-';
return buf;
}
static const char *FormatMemState(u32 state)
{
if (state > 11)
return "Unknown";
static const char *states[12] =
{
"Free",
"Reserved",
"IO",
"Static",
"Code",
"Private",
"Shared",
"Continuous",
"Aliased",
"Alias",
"AliasCode",
"Locked"
};
return states[state];
}
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMemRegions)
{
u32 address = 0;
u32 posInBuffer = 0;
u32 maxPosInBuffer = GDB_BUF_LEN / 2 - 35; ///< 35 is the maximum length of a formatted region
Handle handle;
MemInfo memi;
PageInfo pagei;
char outbuf[GDB_BUF_LEN / 2 + 1];
if(R_FAILED(svcOpenProcess(&handle, ctx->pid)))
{
posInBuffer = sprintf(outbuf, "Invalid process (wtf?)\n");
goto end;
return GDB_SendHexPacket(ctx, outbuf, posInBuffer);
}
s64 TTBCR;
svcGetSystemInfo(&TTBCR, 0x10002, 0);
while (address < (1u << (32 - (u32)TTBCR)) ///< Limit to check for regions
&& posInBuffer < maxPosInBuffer
&& R_SUCCEEDED(svcQueryProcessMemory(&memi, &pagei, handle, address)))
{
// Update the address for next region
address = memi.base_addr + memi.size;
// If region isn't FREE then add it to the list
if (memi.state != MEMSTATE_FREE)
{
const char *perm = FormatMemPerm(memi.perm);
const char *state = FormatMemState(memi.state);
posInBuffer += sprintf(outbuf + posInBuffer, "%08lx - %08lx %s %s\n",
memi.base_addr, address, perm, state);
}
}
svcCloseHandle(handle);
end:
posInBuffer = formatMemoryMapOfProcess(outbuf, GDB_BUF_LEN / 2, handle);
return GDB_SendHexPacket(ctx, outbuf, posInBuffer);
}
@@ -312,6 +400,41 @@ GDB_DECLARE_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess)
return GDB_SendHexPacket(ctx, outbuf, n);
}
GDB_DECLARE_REMOTE_COMMAND_HANDLER(CatchSvc)
{
if(ctx->commandData[0] == '0')
{
memset(ctx->svcMask, 0, 32);
return R_SUCCEEDED(svcKernelSetState(0x10002, ctx->pid, false)) ? GDB_ReplyOk(ctx) : GDB_ReplyErrno(ctx, EPERM);
}
else if(ctx->commandData[0] == '1')
{
if(ctx->commandData[1] == ';')
{
u32 id;
const char *pos = ctx->commandData + 1;
memset(ctx->svcMask, 0, 32);
do
{
pos = GDB_ParseHexIntegerList(&id, pos + 1, 1, ';');
if(pos == NULL)
return GDB_ReplyErrno(ctx, EILSEQ);
if(id < 0xFE)
ctx->svcMask[id / 32] |= 1 << (31 - (id % 32));
}
while(*pos != 0);
}
else
memset(ctx->svcMask, 0xFF, 32);
return R_SUCCEEDED(svcKernelSetState(0x10002, ctx->pid, true, ctx->svcMask)) ? GDB_ReplyOk(ctx) : GDB_ReplyErrno(ctx, EPERM);
}
else
return GDB_ReplyErrno(ctx, EILSEQ);
}
GDB_DECLARE_QUERY_HANDLER(Rcmd)
{
char commandData[GDB_BUF_LEN / 2 + 1];

View File

@@ -85,38 +85,38 @@
typedef u32 gdbhio_time_t;
typedef int gdbhio_mode_t;
struct gdbhio_stat {
unsigned int st_dev; /* device */
unsigned int st_ino; /* inode */
gdbhio_mode_t st_mode; /* protection */
unsigned int st_nlink; /* number of hard links */
unsigned int st_uid; /* user ID of owner */
unsigned int st_gid; /* group ID of owner */
unsigned int st_rdev; /* device type (if inode device) */
u64 st_size; /* total size, in bytes */
u64 st_blksize; /* blocksize for filesystem I/O */
u64 st_blocks; /* number of blocks allocated */
gdbhio_time_t st_atime; /* time of last access */
gdbhio_time_t st_mtime; /* time of last modification */
gdbhio_time_t st_ctime; /* time of last change */
struct PACKED ALIGN(4) gdbhio_stat {
u32 gst_dev; /* device */
u32 gst_ino; /* inode */
gdbhio_mode_t gst_mode; /* protection */
u32 gst_nlink; /* number of hard links */
u32 gst_uid; /* user ID of owner */
u32 gst_gid; /* group ID of owner */
u32 gst_rdev; /* device type (if inode device) */
u64 gst_size; /* total size, in bytes */
u64 gst_blksize; /* blocksize for filesystem I/O */
u64 gst_blocks; /* number of blocks allocated */
gdbhio_time_t gst_atime; /* time of last access */
gdbhio_time_t gst_mtime; /* time of last modification */
gdbhio_time_t gst_ctime; /* time of last change */
};
static void GDB_TioMakeStructStat(struct gdbhio_stat *out, const struct gdbhio_stat *in)
{
memset(out, 0, sizeof(struct gdbhio_stat));
out->st_dev = __builtin_bswap32(in->st_dev);
out->st_ino = __builtin_bswap32(in->st_ino);
out->st_mode = __builtin_bswap32(in->st_dev);
out->st_nlink = __builtin_bswap32(in->st_nlink);
out->st_uid = __builtin_bswap32(in->st_uid);
out->st_gid = __builtin_bswap32(in->st_gid);
out->st_rdev = __builtin_bswap32(in->st_rdev);
out->st_size = __builtin_bswap64(in->st_size);
out->st_blksize = __builtin_bswap64(in->st_blksize);
out->st_blocks = __builtin_bswap64(in->st_blocks);
out->st_atime = __builtin_bswap32(in->st_atime);
out->st_mtime = __builtin_bswap32(in->st_mtime);
out->st_ctime = __builtin_bswap32(in->st_ctime);
out->gst_dev = __builtin_bswap32(in->gst_dev);
out->gst_ino = __builtin_bswap32(in->gst_ino);
out->gst_mode = __builtin_bswap32(in->gst_dev);
out->gst_nlink = __builtin_bswap32(in->gst_nlink);
out->gst_uid = __builtin_bswap32(in->gst_uid);
out->gst_gid = __builtin_bswap32(in->gst_gid);
out->gst_rdev = __builtin_bswap32(in->gst_rdev);
out->gst_size = __builtin_bswap64(in->gst_size);
out->gst_blksize = __builtin_bswap64(in->gst_blksize);
out->gst_blocks = __builtin_bswap64(in->gst_blocks);
out->gst_atime = __builtin_bswap32(in->gst_atime);
out->gst_mtime = __builtin_bswap32(in->gst_mtime);
out->gst_ctime = __builtin_bswap32(in->gst_ctime);
}
// Inspired from https://github.com/smealum/ctrulib/blob/master/libctru/source/sdmc_dev.c
@@ -408,8 +408,8 @@ GDB_DECLARE_TIO_HANDLER(Stat)
if (err == 0)
{
gdbSt.st_nlink = 1;
gdbSt.st_mode = GDBHIO_S_IFREG | GDBHIO_S_IRUSR | GDBHIO_S_IWUSR |
gdbSt.gst_nlink = 1;
gdbSt.gst_mode = GDBHIO_S_IFREG | GDBHIO_S_IRUSR | GDBHIO_S_IWUSR |
GDBHIO_S_IRGRP | GDBHIO_S_IWGRP | GDBHIO_S_IROTH | GDBHIO_S_IWOTH;
}
}
@@ -419,8 +419,8 @@ GDB_DECLARE_TIO_HANDLER(Stat)
if (err == 0)
{
FSDIR_Close(dirHandle);
gdbSt.st_nlink = 1;
gdbSt.st_mode = GDBHIO_S_IFDIR | GDBHIO_S_IRWXU | GDBHIO_S_IRWXG | GDBHIO_S_IRWXO;
gdbSt.gst_nlink = 1;
gdbSt.gst_mode = GDBHIO_S_IFDIR | GDBHIO_S_IRWXU | GDBHIO_S_IRWXG | GDBHIO_S_IRWXO;
}
}

View File

@@ -36,7 +36,8 @@
#include "gdb/server.h"
#include "pmdbgext.h"
#define MAP_BASE 0x10000000
#define SYSCOREVER (*(vu32 *)0x1FF80010)
#define APPMEMTYPE (*(vu32 *)0x1FF80030)
extern GDBContext *nextApplicationGdbCtx;
extern GDBServer gdbServer;
@@ -77,16 +78,13 @@ static const char serviceList[32][8] =
"y2r:u",
};
static const u64 dependencyList[] =
static const u64 dependencyListNativeFirm[] =
{
0x0004013000002402LL, //ac
0x0004013000001502LL, //am
0x0004013000003402LL, //boss
0x0004013000001602LL, //camera
0x0004013000001702LL, //cfg
0x0004013000001802LL, //codec
0x0004013000002702LL, //csnd
0x0004013000002802LL, //dlp
0x0004013000001A02LL, //dsp
0x0004013000001B02LL, //gpio
0x0004013000001C02LL, //gsp
@@ -95,18 +93,50 @@ static const u64 dependencyList[] =
0x0004013000001E02LL, //i2c
0x0004013000003302LL, //ir
0x0004013000001F02LL, //mcu
0x0004013000002002LL, //mic
0x0004013000002B02LL, //ndm
0x0004013000003502LL, //news
0x0004013000002C02LL, //nim
0x0004013000002D02LL, //nwm
0x0004013000002102LL, //pdn
0x0004013000003102LL, //ps
0x0004013000002202LL, //ptm
0x0004013000003702LL, //ro
0x0004013000002E02LL, //socket
0x0004013000002302LL, //spi
0x0004013000002F02LL, //ssl
// Not present on SAFE_FIRM:
0x0004013000003402LL, //boss
0x0004013000001602LL, //camera
0x0004013000002802LL, //dlp
0x0004013000002002LL, //mic
0x0004013000002B02LL, //ndm
0x0004013000003502LL, //news
0x0004013000003702LL, //ro
};
static const u64 dependencyListSafeFirm[] =
{
0x0004013000002403LL, //ac
0x0004013000001503LL, //am
0x0004013000001703LL, //cfg
0x0004013000001803LL, //codec
0x0004013000002703LL, //csnd
0x0004013000001A03LL, //dsp
0x0004013000001B03LL, //gpio
0x0004013000001C03LL, //gsp
0x0004013000001D03LL, //hid
0x0004013000002903LL, //http
0x0004013000001E03LL, //i2c
0x0004013000003303LL, //ir
0x0004013000001F03LL, //mcu
0x0004013000002C03LL, //nim
0x0004013000002D03LL, //nwm
0x0004013000002103LL, //pdn
0x0004013000003103LL, //ps
0x0004013000002203LL, //ptm
0x0004013000002E03LL, //socket
0x0004013000002303LL, //spi
0x0004013000002F03LL, //ssl
0x0004013000003203LL, //friends (wouldn't be launched otherwise)
};
static const u32 kernelCaps[] =
@@ -122,20 +152,8 @@ static const u32 kernelCaps[] =
0xFE000200, // Handle table size: 0x200
};
static inline void assertSuccess(Result res)
{
if(R_FAILED(res))
svcBreak(USERBREAK_PANIC);
}
static u16 hbldrTarget[PATH_MAX+1];
static inline void error(u32* cmdbuf, Result rc)
{
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);
cmdbuf[1] = rc;
}
static u16 *u16_strncpy(u16 *dest, const u16 *src, u32 size)
{
u32 i;
@@ -147,6 +165,31 @@ static u16 *u16_strncpy(u16 *dest, const u16 *src, u32 size)
return dest;
}
void HBLDR_RestartHbApplication(void *p)
{
(void)p;
// Don't crash if we fail
FS_ProgramInfo programInfo;
u32 pid;
u32 launchFlags;
Result res = PMDBG_GetCurrentAppInfo(&programInfo, &pid, &launchFlags);
if (R_FAILED(res)) return;
res = PMDBG_PrepareToChainloadHomebrew(programInfo.programId);
if (R_FAILED(res)) return;
res = PMAPP_TerminateCurrentApplication(3 * 1000 * 1000 *1000LL); // 3s, like what NS uses
if (R_FAILED(res)) return;
if (R_SUCCEEDED(res))
{
do
{
svcSleepThread(100 * 1000 * 1000LL);
res = PMAPP_LaunchTitle(&programInfo, PMLAUNCHFLAGEXT_FAKE_DEPENDENCY_LOADING | launchFlags);
} while (res == (Result)0xC8A05BF0);
}
}
void HBLDR_HandleCommands(void *ctx)
{
(void)ctx;
@@ -193,8 +236,10 @@ void HBLDR_HandleCommands(void *ctx)
break;
}
// note: mappableFree doesn't do anything
u32 tmp = 0;
res = svcControlMemoryEx(&tmp, MAP_BASE, 0, totalSize, MEMOP_ALLOC | flags, MEMPERM_READ | MEMPERM_WRITE, true);
u32 *addr = mappableAlloc(totalSize);
res = svcControlMemoryEx(&tmp, (u32)addr, 0, totalSize, MEMOP_ALLOC | flags, MEMPERM_READ | MEMPERM_WRITE, true);
if (R_FAILED(res))
{
IFile_Close(&file);
@@ -202,12 +247,12 @@ void HBLDR_HandleCommands(void *ctx)
break;
}
Handle hCodeset = Ldr_CodesetFrom3dsx(name, (u32*)MAP_BASE, baseAddr, &file, tid);
Handle hCodeset = Ldr_CodesetFrom3dsx(name, addr, baseAddr, &file, tid);
IFile_Close(&file);
if (!hCodeset)
{
svcControlMemory(&tmp, MAP_BASE, 0, totalSize, MEMOP_FREE, 0);
svcControlMemory(&tmp, (u32)addr, 0, totalSize, MEMOP_FREE, 0);
error(cmdbuf, MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_LDR, RD_NOT_FOUND));
break;
}
@@ -264,30 +309,36 @@ void HBLDR_HandleCommands(void *ctx)
memcpy(exhi->sci.codeset_info.name, "3dsx_app", 8);
memcpy(&exhi->sci.codeset_info.stack_size, &stacksize, 4);
memset(&exhi->sci.dependencies, 0, sizeof(exhi->sci.dependencies));
memcpy(exhi->sci.dependencies, dependencyList, sizeof(dependencyList));
if (SYSCOREVER == 2)
memcpy(exhi->sci.dependencies, dependencyListNativeFirm, sizeof(dependencyListNativeFirm));
else if (SYSCOREVER == 3)
memcpy(exhi->sci.dependencies, dependencyListSafeFirm, sizeof(dependencyListSafeFirm));
ExHeader_Arm11SystemLocalCapabilities* localcaps0 = &exhi->aci.local_caps;
localcaps0->core_info.core_version = 2;
localcaps0->core_info.core_version = SYSCOREVER;
localcaps0->core_info.use_cpu_clockrate_804MHz = false;
localcaps0->core_info.enable_l2c = false;
localcaps0->core_info.n3ds_system_mode = SYSMODE_N3DS_PROD;
localcaps0->core_info.ideal_processor = 0;
localcaps0->core_info.affinity_mask = BIT(0);
localcaps0->core_info.o3ds_system_mode = SYSMODE_O3DS_PROD;
localcaps0->core_info.priority = 0x30;
u32 appmemtype = APPMEMTYPE;
localcaps0->core_info.o3ds_system_mode = appmemtype < 6 ? (SystemMode)appmemtype : SYSMODE_O3DS_PROD;
localcaps0->core_info.n3ds_system_mode = appmemtype >= 6 ? (SystemMode)(appmemtype - 6 + 1) : SYSMODE_N3DS_PROD;
memset(localcaps0->reslimits, 0, sizeof(localcaps0->reslimits));
// Set mode1 preemption mode for core1, max. 89% of CPU time (default 0, requires a APT_SetAppCpuTimeLimit call)
// See the big comment in sysmodules/pm/source/reslimit.c for technical details.
localcaps0->reslimits[0] = BIT(7) | 89;
localcaps0->storage_info.fs_access_info = 0xFFFFFFFF; // Give access to everything
//localcaps0->storage_info.fs_access_info = 0xFFFFFFFF; // Give access to everything
localcaps0->storage_info.no_romfs = true;
localcaps0->storage_info.use_extended_savedata_access = true; // Whatever
/* We have a patched SM, so whatever... */
// We have a patched SM, so whatever...
memset(localcaps0->service_access, 0, sizeof(localcaps0->service_access));
memcpy(localcaps0->service_access, serviceList, sizeof(serviceList));
@@ -300,9 +351,9 @@ void HBLDR_HandleCommands(void *ctx)
// Set kernel release version to the current kernel version
kcaps0->descriptors[0] = 0xFC000000 | (osGetKernelVersion() >> 16);
u64 lastdep = sizeof(dependencyList)/8;
if (osGetFirmVersion() >= SYSTEM_VERSION(2,50,0)) // 9.6+ FIRM
if (GET_VERSION_MINOR(osGetKernelVersion()) >= 50 && SYSCOREVER == 2) // 9.6+ NFIRM
{
u64 lastdep = sizeof(dependencyListNativeFirm)/8;
exhi->sci.dependencies[lastdep++] = 0x0004013000004002ULL; // nfc
strncpy((char*)&localcaps0->service_access[0x20], "nfc:u", 8);
s64 dummy = 0;

View File

@@ -29,8 +29,11 @@
#include "utils.h" // for makeArmBranch
#include "minisoc.h"
#include "input_redirection.h"
#include "process_patches.h"
#include "menus.h"
#include "memory.h"
#include "sleep.h"
#include "sock_util.h"
bool inputRedirectionEnabled = false;
Handle inputRedirectionThreadStartedEvent;
@@ -113,13 +116,20 @@ void inputRedirectionThreadMain(void)
char buf[20];
u32 oldSpecialButtons = 0, specialButtons = 0;
while(inputRedirectionEnabled && !terminationRequest)
while(inputRedirectionEnabled && !preTerminationRequested)
{
struct pollfd pfd;
pfd.fd = sock;
pfd.events = POLLIN;
pfd.revents = 0;
if (Sleep__Status())
{
while (!Wifi__IsConnected()
&& inputRedirectionEnabled && !preTerminationRequested)
svcSleepThread(1000000000ULL);
}
int pollres = socPoll(&pfd, 1, 10);
if(pollres > 0 && (pfd.revents & POLLIN))
{
@@ -202,7 +212,7 @@ Result InputRedirection_DoOrUndoPatches(void)
totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize);
svcGetProcessInfo(&startAddress, processHandle, 0x10005);
res = svcMapProcessMemoryEx(processHandle, 0x00100000, (u32) startAddress, totalSize);
res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, processHandle, (u32) startAddress, totalSize);
if(R_SUCCEEDED(res))
{
@@ -240,21 +250,21 @@ Result InputRedirection_DoOrUndoPatches(void)
u32 *off = (u32 *)memsearch((u8 *)0x00100000, &hidOrigRegisterAndValue, totalSize, sizeof(hidOrigRegisterAndValue));
if(off == NULL)
{
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
return -1;
}
u32 *off2 = (u32 *)memsearch((u8 *)off + sizeof(hidOrigRegisterAndValue), &hidOrigRegisterAndValue, totalSize - ((u32)off - 0x00100000), sizeof(hidOrigRegisterAndValue));
if(off2 == NULL)
{
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
return -2;
}
u32 *off3 = (u32 *)memsearch((u8 *)0x00100000, &hidOrigCode, totalSize, sizeof(hidOrigCode));
if(off3 == NULL)
{
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
return -3;
}
@@ -268,12 +278,12 @@ Result InputRedirection_DoOrUndoPatches(void)
}
}
res = svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
res = svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
}
svcCloseHandle(processHandle);
res = OpenProcessByName("ir", &processHandle);
if(R_SUCCEEDED(res) && osGetKernelVersion() >= SYSTEM_VERSION(2, 44, 6))
if(R_SUCCEEDED(res) && GET_VERSION_MINOR(osGetKernelVersion()) >= 44)
{
svcGetProcessInfo(&textTotalRoundedSize, processHandle, 0x10002); // only patch .text + .data
svcGetProcessInfo(&rodataTotalRoundedSize, processHandle, 0x10003);
@@ -282,7 +292,7 @@ Result InputRedirection_DoOrUndoPatches(void)
totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize);
svcGetProcessInfo(&startAddress, processHandle, 0x10005);
res = svcMapProcessMemoryEx(processHandle, 0x00100000, (u32) startAddress, totalSize);
res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, processHandle, (u32) startAddress, totalSize);
if(R_SUCCEEDED(res))
{
@@ -338,7 +348,7 @@ Result InputRedirection_DoOrUndoPatches(void)
u32 *off = (u32 *)memsearch((u8 *)0x00100000, &irOrigReadingCode, totalSize, sizeof(irOrigReadingCode) - 4);
if(off == NULL)
{
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
return -4;
}
@@ -348,7 +358,7 @@ Result InputRedirection_DoOrUndoPatches(void)
off2 = (u32 *)memsearch((u8 *)0x00100000, &irOrigWaitSyncCodeOld, totalSize, sizeof(irOrigWaitSyncCodeOld));
if(off2 == NULL)
{
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
return -5;
}
@@ -360,7 +370,7 @@ Result InputRedirection_DoOrUndoPatches(void)
u32 *off3 = (u32 *)memsearch((u8 *)0x00100000, &irOrigCppFlagCode, totalSize, sizeof(irOrigCppFlagCode));
if(off3 == NULL)
{
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
return -6;
}
@@ -385,7 +395,7 @@ Result InputRedirection_DoOrUndoPatches(void)
}
}
res = svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
res = svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
}
svcCloseHandle(processHandle);

View File

@@ -33,52 +33,20 @@
#include "3dsx.h"
#include "utils.h"
#include "MyThread.h"
#include "menus/process_patches.h"
#include "menus/miscellaneous.h"
#include "plgloader.h"
#include "menus/debugger.h"
#include "menus/screen_filters.h"
#include "menus/cheats.h"
#include "menus/sysconfig.h"
#include "input_redirection.h"
#include "minisoc.h"
#include "draw.h"
#include "task_runner.h"
bool isN3DS;
static Result stealFsReg(void)
{
Result ret = 0;
ret = svcControlService(SERVICEOP_STEAL_CLIENT_SESSION, fsRegGetSessionHandle(), "fs:REG");
while(ret == 0x9401BFE)
{
svcSleepThread(500 * 1000LL);
ret = svcControlService(SERVICEOP_STEAL_CLIENT_SESSION, fsRegGetSessionHandle(), "fs:REG");
}
return ret;
}
static Result fsRegSetupPermissions(void)
{
u32 pid;
Result res;
FS_ProgramInfo info;
ExHeader_Arm11StorageInfo storageInfo = {
.fs_access_info = FSACCESS_NANDRO_RW | FSACCESS_NANDRW | FSACCESS_SDMC_RW,
};
info.programId = 0x0004013000006902LL; // Rosalina TID
info.mediaType = MEDIATYPE_NAND;
if(R_SUCCEEDED(res = svcGetProcessId(&pid, CUR_PROCESS_HANDLE)))
res = FSREG_Register(pid, 0xFFFF000000000000LL, &info, &storageInfo);
return res;
}
Result __sync_init(void);
Result __sync_fini(void);
void __libc_init_array(void);
@@ -110,6 +78,7 @@ void initSystem(void)
s64 out;
Result res;
__sync_init();
mappableInit(0x10000000, 0x14000000);
isN3DS = svcGetSystemInfo(&out, 0x10001, 0) == 0;
@@ -122,8 +91,6 @@ void initSystem(void)
miscellaneousMenu.items[0].title = HBLDR_3DSX_TID == HBLDR_DEFAULT_3DSX_TID ? "Switch the hb. title to the current app." :
"Switch the hb. title to hblauncher_loader";
ProcessPatchesMenu_PatchUnpatchFSDirectly();
for(res = 0xD88007FA; res == (Result)0xD88007FA; svcSleepThread(500 * 1000LL))
{
res = srvInit();
@@ -131,10 +98,10 @@ void initSystem(void)
svcBreak(USERBREAK_PANIC);
}
if (R_FAILED(stealFsReg()) || R_FAILED(fsRegSetupPermissions()) || R_FAILED(fsInit()))
if (R_FAILED(pmAppInit()) || R_FAILED(pmDbgInit()))
svcBreak(USERBREAK_PANIC);
if (R_FAILED(pmDbgInit()))
if (R_FAILED(fsInit()))
svcBreak(USERBREAK_PANIC);
// **** DO NOT init services that don't come from KIPs here ****
@@ -142,29 +109,29 @@ void initSystem(void)
__libc_init_array();
// ROSALINA HACKJOB BEGIN
// NORMAL APPS SHOULD NOT DO THIS, EVER
u32 *tls = (u32 *)getThreadLocalStorage();
memset(tls, 0, 0x80);
tls[0] = 0x21545624;
// ROSALINA HACKJOB END
// Rosalina specific:
u32 *tls = (u32 *)getThreadLocalStorage();
memset(tls, 0, 0x80);
tls[0] = 0x21545624;
// ROSALINA HACKJOB BEGIN
// NORMAL APPS SHOULD NOT DO THIS, EVER
srvSetBlockingPolicy(true); // GetServiceHandle nonblocking if service port is full
}
bool terminationRequest = false;
Handle terminationRequestEvent;
bool menuShouldExit = false;
bool preTerminationRequested = false;
Handle preTerminationEvent;
extern bool isHidInitialized;
static void handleTermNotification(u32 notificationId)
{
(void)notificationId;
// Termination request
terminationRequest = true;
svcSignalEvent(terminationRequestEvent);
}
static void relinquishConnectionSessions(u32 notificationId)
static void handlePreTermNotification(u32 notificationId)
{
(void)notificationId;
// Might be subject to a race condition, but heh.
@@ -182,6 +149,16 @@ static void relinquishConnectionSessions(u32 notificationId)
isConnectionForced = false;
SysConfigMenu_UpdateStatus(true);
}
Draw_Lock();
if (isHidInitialized)
hidExit();
// Termination request
menuShouldExit = true;
preTerminationRequested = true;
svcSignalEvent(preTerminationEvent);
Draw_Unlock();
}
static void handleNextApplicationDebuggedByForce(u32 notificationId)
@@ -191,6 +168,12 @@ static void handleNextApplicationDebuggedByForce(u32 notificationId)
TaskRunner_RunTask(debuggerFetchAndSetNextApplicationDebugHandleTask, NULL, 0);
}
static void handleRestartHbAppNotification(u32 notificationId)
{
(void)notificationId;
TaskRunner_RunTask(HBLDR_RestartHbApplication, NULL, 0);
}
static const ServiceManagerServiceEntry services[] = {
{ "hb:ldr", 2, HBLDR_HandleCommands, true },
{ NULL },
@@ -198,9 +181,10 @@ static const ServiceManagerServiceEntry services[] = {
static const ServiceManagerNotificationEntry notifications[] = {
{ 0x100 , handleTermNotification },
//{ 0x103 , relinquishConnectionSessions }, // Sleep mode entry <=== causes issues
//{ 0x103 , handlePreTermNotification }, // Sleep mode entry <=== causes issues
{ 0x1000, handleNextApplicationDebuggedByForce },
{ 0x2000, relinquishConnectionSessions },
{ 0x2000, handlePreTermNotification },
{ 0x3000, handleRestartHbAppNotification },
{ 0x000, NULL },
};
@@ -216,23 +200,22 @@ int main(void)
bufPtrs[2] = IPC_Desc_StaticBuffer(sizeof(ldrArgvBuf), 1);
bufPtrs[3] = (u32)ldrArgvBuf;
if(R_FAILED(svcCreateEvent(&terminationRequestEvent, RESET_STICKY)))
if(R_FAILED(svcCreateEvent(&preTerminationEvent, RESET_STICKY)))
svcBreak(USERBREAK_ASSERT);
Draw_Init();
Cheat_SeedRng(svcGetSystemTick());
MyThread *menuThread = menuCreateThread();
MyThread *taskRunnerThread = taskRunnerCreateThread();
MyThread *errDispThread = errDispCreateThread();
MyThread *plgloaderThread = PluginLoader__CreateThread();
if (R_FAILED(ServiceManager_Run(services, notifications, NULL)))
svcBreak(USERBREAK_PANIC);
TaskRunner_Terminate();
MyThread_Join(menuThread, -1LL);
MyThread_Join(taskRunnerThread, -1LL);
MyThread_Join(errDispThread, -1LL);
MyThread_Join(plgloaderThread, -1LL);
return 0;
}

View File

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

View File

@@ -32,70 +32,90 @@
#include "ifile.h"
#include "menus.h"
#include "utils.h"
#include "plgloader.h"
#include "menus/n3ds.h"
#include "menus/cheats.h"
#include "minisoc.h"
u32 waitInputWithTimeout(u32 msec)
bool isHidInitialized = false;
// libctru redefinition:
bool hidShouldUseIrrst(void)
{
bool pressedKey = false;
u32 key = 0;
u32 n = 0;
// ir:rst exposes only two sessions :(
return false;
}
//Wait for no keys to be pressed
while(HID_PAD && !terminationRequest && (msec == 0 || n < msec))
{
svcSleepThread(1 * 1000 * 1000LL);
n++;
}
static inline u32 convertHidKeys(u32 keys)
{
// Nothing to do yet
return keys;
}
if(terminationRequest || (msec != 0 && n >= msec))
return 0;
u32 waitInputWithTimeout(s32 msec)
{
s32 n = 0;
u32 keys;
do
{
//Wait for a key to be pressed
while(!HID_PAD && !terminationRequest && (msec == 0 || n < msec))
svcSleepThread(1 * 1000 * 1000LL);
Draw_Lock();
if (!isHidInitialized || menuShouldExit)
{
svcSleepThread(1 * 1000 * 1000LL);
n++;
keys = 0;
Draw_Unlock();
break;
}
n++;
if(terminationRequest || (msec != 0 && n >= msec))
return 0;
hidScanInput();
keys = convertHidKeys(hidKeysDown()) | (convertHidKeys(hidKeysDownRepeat()) & DIRECTIONAL_KEYS);
Draw_Unlock();
} while (keys == 0 && !menuShouldExit && isHidInitialized && (msec < 0 || n < msec));
key = HID_PAD;
//Make sure it's pressed
for(u32 i = 0x26000; i > 0; i --)
{
if(key != HID_PAD) break;
if(i == 1) pressedKey = true;
}
}
while(!pressedKey);
return key;
return keys;
}
u32 waitInput(void)
{
return waitInputWithTimeout(0);
return waitInputWithTimeout(-1);
}
u32 waitComboWithTimeout(u32 msec)
static u32 scanHeldKeys(void)
{
u32 key = 0;
u32 n = 0;
u32 keys;
//Wait for no keys to be pressed
while(HID_PAD && !terminationRequest && (msec == 0 || n < msec))
Draw_Lock();
if (!isHidInitialized || menuShouldExit)
keys = 0;
else
{
hidScanInput();
keys = convertHidKeys(hidKeysHeld());
}
Draw_Unlock();
return keys;
}
u32 waitComboWithTimeout(s32 msec)
{
s32 n = 0;
u32 keys = 0;
u32 tempKeys = 0;
// Wait for nothing to be pressed
while (scanHeldKeys() != 0 && !menuShouldExit && isHidInitialized && (msec < 0 || n < msec))
{
svcSleepThread(1 * 1000 * 1000LL);
n++;
}
if(terminationRequest || (msec != 0 && n >= msec))
if (menuShouldExit || !isHidInitialized || !(msec < 0 || n < msec))
return 0;
do
@@ -103,74 +123,98 @@ u32 waitComboWithTimeout(u32 msec)
svcSleepThread(1 * 1000 * 1000LL);
n++;
u32 tempKey = HID_PAD;
tempKeys = scanHeldKeys();
for(u32 i = 0x26000; i > 0; i--)
for (u32 i = 0x10000; i > 0; i--)
{
if(tempKey != HID_PAD) break;
if(i == 1) key = tempKey;
if (tempKeys != scanHeldKeys()) break;
if (i == 1) keys = tempKeys;
}
}
while((!key || HID_PAD) && !terminationRequest && (msec == 0 || n < msec));
while((keys == 0 || scanHeldKeys() != 0) && !menuShouldExit && isHidInitialized && (msec < 0 || n < msec));
if(terminationRequest || (msec != 0 && n >= msec))
return 0;
return key;
return keys;
}
u32 waitCombo(void)
{
return waitComboWithTimeout(0);
return waitComboWithTimeout(-1);
}
static MyThread menuThread;
static u8 ALIGN(8) menuThreadStack[0x1000];
static u8 batteryLevel = 255;
static u32 homeBtnPressed = 0;
static inline u32 menuAdvanceCursor(u32 pos, u32 numItems, s32 displ)
{
return (pos + numItems + displ) % numItems;
}
static inline bool menuItemIsHidden(const MenuItem *item)
{
return item->visibility != NULL && !item->visibility();
}
bool menuCheckN3ds(void)
{
return isN3DS;
}
u32 menuCountItems(const Menu *menu)
{
u32 n;
for (n = 0; menu->items[n].action_type != MENU_END; n++);
return n;
}
MyThread *menuCreateThread(void)
{
if(R_FAILED(MyThread_Create(&menuThread, menuThreadMain, menuThreadStack, 0x1000, 52, CORE_SYSTEM)))
svcKernelSetState(0x10007, &homeBtnPressed);
if(R_FAILED(MyThread_Create(&menuThread, menuThreadMain, menuThreadStack, 0x3000, 52, CORE_SYSTEM)))
svcBreak(USERBREAK_PANIC);
return &menuThread;
}
extern bool isN3DS;
u32 menuCombo;
void menuThreadMain(void)
u32 DispWarningOnHome(void);
void menuThreadMain(void)
{
if(!isN3DS)
{
rosalinaMenu.nbItems--;
for(u32 i = 0; i <= rosalinaMenu.nbItems; i++)
rosalinaMenu.items[i] = rosalinaMenu.items[i+1];
}
else
if(isN3DS)
N3DSMenu_UpdateStatus();
bool isAcURegistered = false;
while (!isServiceUsable("ac:u") || !isServiceUsable("hid:USER"))
svcSleepThread(500 * 1000 * 1000LL);
while(!terminationRequest)
hidInit(); // assume this doesn't fail
isHidInitialized = true;
while(!preTerminationRequested)
{
if((HID_PAD & menuCombo) == menuCombo)
{
if (!isAcURegistered)
isAcURegistered = R_SUCCEEDED(srvIsServiceRegistered(&isAcURegistered, "ac:u"))
&& isAcURegistered;
if (menuShouldExit)
continue;
if (isAcURegistered)
{
menuEnter();
if(isN3DS) N3DSMenu_UpdateStatus();
menuShow(&rosalinaMenu);
menuLeave();
}
}
else
if ((scanHeldKeys() & menuCombo) == menuCombo)
{
Cheat_ApplyCheats();
menuEnter();
if(isN3DS) N3DSMenu_UpdateStatus();
PluginLoader__UpdateMenu();
menuShow(&rosalinaMenu);
menuLeave();
}
// Check for home button on O3DS Mode3 with plugin loaded
if (homeBtnPressed != 0)
{
if (DispWarningOnHome())
svcKernelSetState(7); ///< reboot is fine since exiting a mode3 game reboot anyway
homeBtnPressed = 0;
}
svcSleepThread(50 * 1000 * 1000LL);
}
}
@@ -178,27 +222,37 @@ void menuThreadMain(void)
static s32 menuRefCount = 0;
void menuEnter(void)
{
if(AtomicPostIncrement(&menuRefCount) == 0)
Draw_Lock();
if(!menuShouldExit && menuRefCount == 0)
{
menuRefCount++;
svcKernelSetState(0x10000, 1);
svcSleepThread(5 * 1000 * 100LL);
if (Draw_AllocateFramebufferCache() == 0)
{
// Oops
menuRefCount = 0;
svcKernelSetState(0x10000, 1);
svcSleepThread(5 * 1000 * 100LL);
}
Draw_SetupFramebuffer();
Draw_ClearFramebuffer();
}
Draw_Unlock();
}
void menuLeave(void)
{
svcSleepThread(50 * 1000 * 1000);
if(AtomicDecrement(&menuRefCount) == 0)
Draw_Lock();
if(--menuRefCount == 0)
{
Draw_Lock();
Draw_FlushFramebuffer();
Draw_RestoreFramebuffer();
Draw_Unlock();
Draw_FreeFramebufferCache();
svcKernelSetState(0x10000, 1);
}
Draw_Unlock();
}
static void menuDraw(Menu *menu, u32 selected)
@@ -233,13 +287,17 @@ static void menuDraw(Menu *menu, u32 selected)
sprintf(versionString, "v%lu.%lu.%lu", GET_VERSION_MAJOR(version), GET_VERSION_MINOR(version), GET_VERSION_REVISION(version));
Draw_DrawString(10, 10, COLOR_TITLE, menu->title);
u32 numItems = menuCountItems(menu);
u32 dispY = 0;
for(u32 i = 0; i < 15; i++)
for(u32 i = 0; i < numItems; i++)
{
if(i >= menu->nbItems)
break;
Draw_DrawString(30, 30 + i * SPACING_Y, COLOR_WHITE, menu->items[i].title);
Draw_DrawCharacter(10, 30 + i * SPACING_Y, COLOR_TITLE, i == selected ? '>' : ' ');
if (menuItemIsHidden(&menu->items[i]))
continue;
Draw_DrawString(30, 30 + dispY, COLOR_WHITE, menu->items[i].title);
Draw_DrawCharacter(10, 30 + dispY, COLOR_TITLE, i == selected ? '>' : ' ');
dispY += SPACING_Y;
}
if(miniSocEnabled)
@@ -274,17 +332,33 @@ void menuShow(Menu *root)
Menu *previousMenus[0x80];
u32 previousSelectedItems[0x80];
u32 numItems = menuCountItems(currentMenu);
if (menuItemIsHidden(&currentMenu->items[selectedItem]))
selectedItem = menuAdvanceCursor(selectedItem, numItems, 1);
Draw_Lock();
Draw_ClearFramebuffer();
Draw_FlushFramebuffer();
hidSetRepeatParameters(0, 0);
menuDraw(currentMenu, selectedItem);
Draw_Unlock();
bool menuComboReleased = false;
do
{
u32 pressed = waitInputWithTimeout(1000);
numItems = menuCountItems(currentMenu);
if(pressed & BUTTON_A)
if(!menuComboReleased && (scanHeldKeys() & menuCombo) != menuCombo)
{
menuComboReleased = true;
Draw_Lock();
hidSetRepeatParameters(200, 100);
Draw_Unlock();
}
if(pressed & KEY_A)
{
Draw_Lock();
Draw_ClearFramebuffer();
@@ -303,6 +377,9 @@ void menuShow(Menu *root)
currentMenu = currentMenu->items[selectedItem].menu;
selectedItem = 0;
break;
default:
__builtin_trap(); // oops
break;
}
Draw_Lock();
@@ -310,7 +387,7 @@ void menuShow(Menu *root)
Draw_FlushFramebuffer();
Draw_Unlock();
}
else if(pressed & BUTTON_B)
else if(pressed & KEY_B)
{
Draw_Lock();
Draw_ClearFramebuffer();
@@ -325,20 +402,177 @@ void menuShow(Menu *root)
else
break;
}
else if(pressed & BUTTON_DOWN)
else if(pressed & KEY_DOWN)
{
if(++selectedItem >= currentMenu->nbItems)
selectedItem = 0;
selectedItem = menuAdvanceCursor(selectedItem, numItems, 1);
if (menuItemIsHidden(&currentMenu->items[selectedItem]))
selectedItem = menuAdvanceCursor(selectedItem, numItems, 1);
}
else if(pressed & BUTTON_UP)
else if(pressed & KEY_UP)
{
if(selectedItem-- <= 0)
selectedItem = currentMenu->nbItems - 1;
selectedItem = menuAdvanceCursor(selectedItem, numItems, -1);
if (menuItemIsHidden(&currentMenu->items[selectedItem]))
selectedItem = menuAdvanceCursor(selectedItem, numItems, -1);
}
Draw_Lock();
menuDraw(currentMenu, selectedItem);
Draw_Unlock();
}
while(!terminationRequest);
while(!menuShouldExit);
}
static const char *__press_b_to_close = "Press [B] to close";
void DispMessage(const char *title, const char *message)
{
menuEnter();
Draw_Lock();
Draw_ClearFramebuffer();
Draw_FlushFramebuffer();
Draw_DrawString(10, 10, COLOR_TITLE, title);
Draw_DrawString(30, 30, COLOR_WHITE, message);
Draw_DrawString(200, 220, COLOR_TITLE, __press_b_to_close);
u32 keys = 0;
do
{
keys = waitComboWithTimeout(1000);
}while (!preTerminationRequested && !(keys & KEY_B));
Draw_Unlock(); ///< Keep it locked until we exit the message
menuLeave();
}
u32 DispErrMessage(const char *title, const char *message, const Result error)
{
char buf[100];
sprintf(buf, "Error code: 0x%08X", (unsigned int)error);
menuEnter();
Draw_Lock();
Draw_ClearFramebuffer();
Draw_FlushFramebuffer();
Draw_DrawString(10, 10, COLOR_TITLE, title);
u32 posY = Draw_DrawString(30, 30, COLOR_WHITE, message);
Draw_DrawString(30, posY + 20, COLOR_RED, buf);
Draw_DrawString(200, 220, COLOR_TITLE, __press_b_to_close);
u32 keys = 0;
do
{
keys = waitComboWithTimeout(1000);
}while (!preTerminationRequested && !(keys & KEY_B));
Draw_Unlock(); ///< Keep it locked until we exit the message
menuLeave();
return error;
}
u32 DispWarningOnHome(void)
{
menuEnter();
Draw_Lock();
Draw_ClearFramebuffer();
Draw_FlushFramebuffer();
Draw_DrawString(10, 10, COLOR_TITLE, "Warning");
u32 posY = Draw_DrawString(30, 40, COLOR_WHITE, "Due to memory shortage the home button\nis disabled.");
Draw_DrawString(30, posY + 20, COLOR_WHITE, "Press [DPAD UP + B] to exit the application.");
Draw_DrawString(200, 220, COLOR_TITLE, __press_b_to_close);
u32 keys = 0;
do
{
keys = waitComboWithTimeout(1000);
}while (!preTerminationRequested && !(keys & KEY_B));
Draw_Unlock(); ///< Keep it locked until we exit the message
menuLeave();
return (keys & KEY_UP) > 0;
}
typedef char string[50];
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
void DisplayPluginMenu(u32 *cmdbuf)
{
u32 cursor = 0;
u32 nbItems = cmdbuf[1];
u8 *states = (u8 *)cmdbuf[3];
char buffer[60];
const char *title = (const char *)cmdbuf[5];
const string *items = (const string *)cmdbuf[7];
const string *hints = (const string *)cmdbuf[9];
menuEnter();
Draw_Lock();
do
{
// Draw the menu
{
// Clear screen
Draw_ClearFramebuffer();
Draw_FlushFramebuffer();
// Draw title
Draw_DrawString(10, 10, COLOR_TITLE, title);
// Draw items
u32 i = MAX(0, (int)cursor - 7);
u32 end = MIN(nbItems, i + 16);
u32 posY = 30;
for (; i < end; ++i, posY += 10)
{
sprintf(buffer, "[ ] %s", items[i]);
Draw_DrawString(30, posY, COLOR_WHITE, buffer);
if (i == cursor) Draw_DrawCharacter(10, posY, COLOR_TITLE, '>');
if (states[i]) Draw_DrawCharacter(36, posY, COLOR_LIME, 'x');
}
// Draw hint
if (hints[cursor])
Draw_DrawString(10, 200, COLOR_TITLE, hints[cursor]);
}
// Wait for input
u32 pressed = waitInput();
if (pressed & KEY_A)
states[cursor] = !states[cursor];
if (pressed & KEY_B)
break;
if (pressed & KEY_DOWN)
if (++cursor >= nbItems)
cursor = 0;
if (pressed & KEY_UP)
if (--cursor >= nbItems)
cursor = nbItems - 1;
} while (true);
Draw_Unlock();
menuLeave();
}

View File

@@ -30,34 +30,71 @@
#include "menu.h"
#include "draw.h"
#include "menus/process_list.h"
#include "menus/process_patches.h"
#include "menus/n3ds.h"
#include "menus/debugger.h"
#include "menus/miscellaneous.h"
#include "menus/sysconfig.h"
#include "menus/screen_filters.h"
#include "plgloader.h"
#include "ifile.h"
#include "memory.h"
#include "fmt.h"
#include "process_patches.h"
Menu rosalinaMenu = {
"Rosalina menu",
.nbItems = 11,
{
{ "New 3DS menu...", MENU, .menu = &N3DSMenu },
{ "Take screenshot", METHOD, .method = &RosalinaMenu_TakeScreenshot },
{ "Change screen brightness", METHOD, .method = &RosalinaMenu_ChangeScreenBrightness },
{ "", METHOD, .method = PluginLoader__MenuCallback},
{ "Cheats...", METHOD, .method = &RosalinaMenu_Cheats },
{ "Process list", METHOD, .method = &RosalinaMenu_ProcessList },
{ "Take screenshot (slow!)", METHOD, .method = &RosalinaMenu_TakeScreenshot },
{ "Debugger options...", MENU, .menu = &debuggerMenu },
{ "System configuration...", MENU, .menu = &sysconfigMenu },
{ "Screen filters...", MENU, .menu = &screenFiltersMenu },
{ "New 3DS menu...", MENU, .menu = &N3DSMenu, .visibility = &menuCheckN3ds },
{ "Miscellaneous options...", MENU, .menu = &miscellaneousMenu },
{ "Power off", METHOD, .method = &RosalinaMenu_PowerOff },
{ "Reboot", METHOD, .method = &RosalinaMenu_Reboot },
{ "Credits", METHOD, .method = &RosalinaMenu_ShowCredits }
{ "Credits", METHOD, .method = &RosalinaMenu_ShowCredits },
{ "Debug info", METHOD, .method = &RosalinaMenu_ShowDebugInfo, .visibility = &rosalinaMenuShouldShowDebugInfo },
{},
}
};
bool rosalinaMenuShouldShowDebugInfo(void)
{
return true;
}
void RosalinaMenu_ShowDebugInfo(void)
{
Draw_Lock();
Draw_ClearFramebuffer();
Draw_FlushFramebuffer();
Draw_Unlock();
char memoryMap[512];
formatMemoryMapOfProcess(memoryMap, 511, CUR_PROCESS_HANDLE);
s64 kextAddrSize;
svcGetSystemInfo(&kextAddrSize, 0x10000, 0x300);
u32 kextPa = (u32)((u64)kextAddrSize >> 32);
u32 kextSize = (u32)kextAddrSize;
do
{
Draw_Lock();
Draw_DrawString(10, 10, COLOR_TITLE, "Rosalina -- Debug info");
u32 posY = Draw_DrawString(10, 30, COLOR_WHITE, memoryMap);
Draw_DrawFormattedString(10, posY, COLOR_WHITE, "Kernel ext PA: %08lx - %08lx\n", kextPa, kextPa + kextSize);
Draw_FlushFramebuffer();
Draw_Unlock();
}
while(!(waitInput() & KEY_B) && !menuShouldExit);
}
void RosalinaMenu_ShowCredits(void)
{
Draw_Lock();
@@ -72,6 +109,7 @@ void RosalinaMenu_ShowCredits(void)
u32 posY = Draw_DrawString(10, 30, COLOR_WHITE, "Luma3DS (c) 2016-2020 AuroraWright, TuxSH") + SPACING_Y;
posY = Draw_DrawString(10, posY + SPACING_Y, COLOR_WHITE, "3gx implementation update by mind_overflow");
posY = Draw_DrawString(10, posY + SPACING_Y, COLOR_WHITE, "3DSX loading code by fincs");
posY = Draw_DrawString(10, posY + SPACING_Y, COLOR_WHITE, "Networking code & basic GDB functionality by Stary");
posY = Draw_DrawString(10, posY + SPACING_Y, COLOR_WHITE, "InputRedirection by Stary (PoC by ShinyQuagsire)");
@@ -89,7 +127,7 @@ void RosalinaMenu_ShowCredits(void)
Draw_FlushFramebuffer();
Draw_Unlock();
}
while(!(waitInput() & BUTTON_B) && !terminationRequest);
while(!(waitInput() & KEY_B) && !menuShouldExit);
}
void RosalinaMenu_Reboot(void)
@@ -109,14 +147,118 @@ void RosalinaMenu_Reboot(void)
u32 pressed = waitInputWithTimeout(1000);
if(pressed & BUTTON_A)
if(pressed & KEY_A)
{
APT_HardwareResetAsync();
menuLeave();
} else if(pressed & BUTTON_B)
APT_HardwareResetAsync();
return;
} else if(pressed & KEY_B)
return;
}
while(!terminationRequest);
while(!menuShouldExit);
}
static u32 gspPatchAddrN3ds, gspPatchValuesN3ds[2];
static bool gspPatchDoneN3ds;
static Result RosalinaMenu_PatchN3dsGspForBrightness(u32 size)
{
u32 *off = (u32 *)0x00100000;
u32 *end = (u32 *)(0x00100000 + size);
for (; off < end && (off[0] != 0xE92D4030 || off[1] != 0xE1A04000 || off[2] != 0xE2805C01 || off[3] != 0xE5D0018C); off++);
if (off >= end) {
return -1;
}
gspPatchAddrN3ds = (u32)off;
gspPatchValuesN3ds[0] = off[26];
gspPatchValuesN3ds[1] = off[50];
// NOP brightness changing in GSP
off[26] = 0xE1A00000;
off[50] = 0xE1A00000;
return 0;
}
static Result RosalinaMenu_RevertN3dsGspPatch(u32 size)
{
(void)size;
u32 *off = (u32 *)gspPatchAddrN3ds;
off[26] = gspPatchValuesN3ds[0];
off[50] = gspPatchValuesN3ds[1];
return 0;
}
void RosalinaMenu_ChangeScreenBrightness(void)
{
Result patchResult = 0;
if (isN3DS && !gspPatchDoneN3ds)
{
patchResult = PatchProcessByName("gsp", RosalinaMenu_PatchN3dsGspForBrightness);
gspPatchDoneN3ds = R_SUCCEEDED(patchResult);
}
Draw_Lock();
Draw_ClearFramebuffer();
Draw_FlushFramebuffer();
Draw_Unlock();
do
{
// Assume the current brightness for both screens are the same.
s32 brightness = (s32)(LCD_TOP_BRIGHTNESS & 0xFF);
Draw_Lock();
Draw_DrawString(10, 10, COLOR_TITLE, "Rosalina menu");
u32 posY = 30;
posY = Draw_DrawFormattedString(10, posY, COLOR_WHITE, "Current brightness (0..255): %3lu\n\n", brightness);
if (R_SUCCEEDED(patchResult))
{
posY = Draw_DrawString(10, posY, COLOR_WHITE, "Press Up/Down for +-1, Right/Left for +-10.\n");
posY = Draw_DrawString(10, posY, COLOR_WHITE, "Press Y to revert the GSP patch and exit.\n\n");
posY = Draw_DrawString(10, posY, COLOR_RED, "WARNING: \n");
posY = Draw_DrawString(10, posY, COLOR_WHITE, " * avoid using values far higher than the presets.\n");
posY = Draw_DrawString(10, posY, COLOR_WHITE, " * normal brightness mngmt. is now broken on N3DS.\nYou'll need to press Y to revert");
}
else
Draw_DrawFormattedString(10, posY, COLOR_WHITE, "Failed to patch GSP (0x%08lx).", (u32)patchResult);
Draw_FlushFramebuffer();
Draw_Unlock();
u32 pressed = waitInputWithTimeout(1000);
if ((pressed & DIRECTIONAL_KEYS) && R_SUCCEEDED(patchResult))
{
if (pressed & KEY_UP)
brightness += 1;
else if (pressed & KEY_DOWN)
brightness -= 1;
else if (pressed & KEY_RIGHT)
brightness += 10;
else if (pressed & KEY_LEFT)
brightness -= 10;
brightness = brightness < 0 ? 0 : brightness;
brightness = brightness > 255 ? 255 : brightness;
LCD_TOP_BRIGHTNESS = (u32)brightness;
LCD_BOT_BRIGHTNESS = (u32)brightness;
}
else if ((pressed & KEY_Y) && gspPatchDoneN3ds)
{
patchResult = PatchProcessByName("gsp", RosalinaMenu_RevertN3dsGspPatch);
gspPatchDoneN3ds = !R_SUCCEEDED(patchResult);
return;
}
else if (pressed & KEY_B)
return;
}
while (!menuShouldExit);
}
void RosalinaMenu_PowerOff(void) // Soft shutdown.
@@ -136,25 +278,64 @@ void RosalinaMenu_PowerOff(void) // Soft shutdown.
u32 pressed = waitInputWithTimeout(1000);
if(pressed & BUTTON_A)
if(pressed & KEY_A)
{
menuLeave();
srvPublishToSubscriber(0x203, 0);
return;
}
else if(pressed & BUTTON_B)
else if(pressed & KEY_B)
return;
}
while(!terminationRequest);
while(!menuShouldExit);
}
extern u8 framebufferCache[FB_BOTTOM_SIZE];
void RosalinaMenu_TakeScreenshot(void)
{
#define TRY(expr) if(R_FAILED(res = (expr))) goto end;
static s64 timeSpentConvertingScreenshot = 0;
static s64 timeSpentWritingScreenshot = 0;
static Result RosalinaMenu_WriteScreenshot(IFile *file, bool top, bool left)
{
u64 total;
Result res = 0;
u32 dimX = top ? 400 : 320;
u32 lineSize = 3 * dimX;
u32 remaining = lineSize * 240;
u8 *framebufferCache = (u8 *)Draw_GetFramebufferCache();
u8 *framebufferCacheEnd = framebufferCache + Draw_GetFramebufferCacheSize();
u8 *buf = framebufferCache;
Draw_CreateBitmapHeader(framebufferCache, dimX, 240);
buf += 54;
u32 y = 0;
// Our buffer might be smaller than the size of the screenshot...
while (remaining != 0)
{
s64 t0 = svcGetSystemTick();
u32 available = (u32)(framebufferCacheEnd - buf);
u32 size = available < remaining ? available : remaining;
u32 nlines = size / lineSize;
Draw_ConvertFrameBufferLines(buf, y, nlines, top, left);
s64 t1 = svcGetSystemTick();
timeSpentConvertingScreenshot += t1 - t0;
TRY(IFile_Write(file, &total, framebufferCache, (y == 0 ? 54 : 0) + lineSize * nlines, 0)); // don't forget to write the header
timeSpentWritingScreenshot += svcGetSystemTick() - t1;
y += nlines;
remaining -= lineSize * nlines;
buf = framebufferCache;
}
end: return res;
}
void RosalinaMenu_TakeScreenshot(void)
{
IFile file;
Result res;
Result res = 0;
char filename[64];
@@ -163,6 +344,9 @@ void RosalinaMenu_TakeScreenshot(void)
s64 out;
bool isSdMode;
timeSpentConvertingScreenshot = 0;
timeSpentWritingScreenshot = 0;
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 0x203))) svcBreak(USERBREAK_ASSERT);
isSdMode = (bool)out;
@@ -226,49 +410,19 @@ void RosalinaMenu_TakeScreenshot(void)
sprintf(filename, "/luma/screenshots/%04lu-%02lu-%02lu_%02lu-%02lu-%02lu.%03llu_top.bmp", year, month, days, hours, minutes, seconds, milliseconds);
TRY(IFile_Open(&file, archiveId, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, filename), FS_OPEN_CREATE | FS_OPEN_WRITE));
Draw_CreateBitmapHeader(framebufferCache, 400, 240);
for(u32 y = 0; y < 120; y++)
Draw_ConvertFrameBufferLine(framebufferCache + 54 + 3 * 400 * y, true, true, y);
TRY(IFile_Write(&file, &total, framebufferCache, 54 + 3 * 400 * 120, 0));
for(u32 y = 120; y < 240; y++)
Draw_ConvertFrameBufferLine(framebufferCache + 3 * 400 * (y - 120), true, true, y);
TRY(IFile_Write(&file, &total, framebufferCache, 3 * 400 * 120, 0));
TRY(RosalinaMenu_WriteScreenshot(&file, true, true));
TRY(IFile_Close(&file));
sprintf(filename, "/luma/screenshots/%04lu-%02lu-%02lu_%02lu-%02lu-%02lu.%03llu_bot.bmp", year, month, days, hours, minutes, seconds, milliseconds);
TRY(IFile_Open(&file, archiveId, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, filename), FS_OPEN_CREATE | FS_OPEN_WRITE));
Draw_CreateBitmapHeader(framebufferCache, 320, 240);
for(u32 y = 0; y < 120; y++)
Draw_ConvertFrameBufferLine(framebufferCache + 54 + 3 * 320 * y, false, true, y);
TRY(IFile_Write(&file, &total, framebufferCache, 54 + 3 * 320 * 120, 0));
for(u32 y = 120; y < 240; y++)
Draw_ConvertFrameBufferLine(framebufferCache + 3 * 320 * (y - 120), false, true, y);
TRY(IFile_Write(&file, &total, framebufferCache, 3 * 320 * 120, 0));
TRY(RosalinaMenu_WriteScreenshot(&file, false, true));
TRY(IFile_Close(&file));
if((GPU_FB_TOP_FMT & 0x20) && (Draw_GetCurrentFramebufferAddress(true, true) != Draw_GetCurrentFramebufferAddress(true, false)))
{
sprintf(filename, "/luma/screenshots/%04lu-%02lu-%02lu_%02lu-%02lu-%02lu.%03llu_top_right.bmp", year, month, days, hours, minutes, seconds, milliseconds);
TRY(IFile_Open(&file, archiveId, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, filename), FS_OPEN_CREATE | FS_OPEN_WRITE));
Draw_CreateBitmapHeader(framebufferCache, 400, 240);
for(u32 y = 0; y < 120; y++)
Draw_ConvertFrameBufferLine(framebufferCache + 54 + 3 * 400 * y, true, false, y);
TRY(IFile_Write(&file, &total, framebufferCache, 54 + 3 * 400 * 120, 0));
for(u32 y = 120; y < 240; y++)
Draw_ConvertFrameBufferLine(framebufferCache + 3 * 400 * (y - 120), true, false, y);
TRY(IFile_Write(&file, &total, framebufferCache, 3 * 400 * 120, 0));
TRY(RosalinaMenu_WriteScreenshot(&file, true, false));
TRY(IFile_Close(&file));
}
@@ -276,8 +430,6 @@ end:
IFile_Close(&file);
svcFlushEntireDataCache();
Draw_SetupFramebuffer();
Draw_ClearFramebuffer();
Draw_FlushFramebuffer();
Draw_Unlock();
do
@@ -287,12 +439,19 @@ end:
if(R_FAILED(res))
Draw_DrawFormattedString(10, 30, COLOR_WHITE, "Operation failed (0x%08lx).", (u32)res);
else
Draw_DrawString(10, 30, COLOR_WHITE, "Operation succeeded.");
{
u32 t1 = (u32)(1000 * timeSpentConvertingScreenshot / SYSCLOCK_ARM11);
u32 t2 = (u32)(1000 * timeSpentWritingScreenshot / SYSCLOCK_ARM11);
u32 posY = 30;
posY = Draw_DrawString(10, posY, COLOR_WHITE, "Operation succeeded.\n\n");
posY = Draw_DrawFormattedString(10, posY, COLOR_WHITE, "Time spent converting: %5lums\n", t1);
posY = Draw_DrawFormattedString(10, posY, COLOR_WHITE, "Time spent writing files: %5lums\n", t2);
}
Draw_FlushFramebuffer();
Draw_Unlock();
}
while(!(waitInput() & BUTTON_B) && !terminationRequest);
while(!(waitInput() & KEY_B) && !menuShouldExit);
#undef TRY
}

View File

@@ -1860,12 +1860,16 @@ static void Cheat_LoadCheatsIntoMemory(u64 titleId)
static u32 Cheat_GetCurrentProcessAndTitleId(u64* titleId)
{
FS_ProgramInfo programInfo;
u32 pid;
Result res = PMDBG_GetCurrentAppTitleIdAndPid(titleId, &pid);
u32 launchFlags;
Result res = PMDBG_GetCurrentAppInfo(&programInfo, &pid, &launchFlags);
if (R_FAILED(res)) {
*titleId = 0;
return 0xFFFFFFFF;
}
*titleId = programInfo.programId;
return pid;
}
@@ -1940,7 +1944,7 @@ void RosalinaMenu_Cheats(void)
Draw_FlushFramebuffer();
Draw_Unlock();
} while (!(waitInput() & BUTTON_B) && !terminationRequest);
} while (!(waitInput() & KEY_B) && !menuShouldExit);
}
else
{
@@ -1978,18 +1982,18 @@ void RosalinaMenu_Cheats(void)
Draw_FlushFramebuffer();
Draw_Unlock();
if (terminationRequest) break;
if (menuShouldExit) break;
u32 pressed;
do
{
pressed = waitInputWithTimeout(50);
if (pressed != 0) break;
} while (pressed == 0 && !terminationRequest);
} while (pressed == 0 && !menuShouldExit);
if (pressed & BUTTON_B)
if (pressed & KEY_B)
break;
else if ((pressed & BUTTON_A) && R_SUCCEEDED(r))
else if ((pressed & KEY_A) && R_SUCCEEDED(r))
{
if (cheats[selected]->active)
{
@@ -2000,13 +2004,13 @@ void RosalinaMenu_Cheats(void)
r = Cheat_MapMemoryAndApplyCheat(pid, cheats[selected]);
}
}
else if (pressed & BUTTON_DOWN)
else if (pressed & KEY_DOWN)
selected++;
else if (pressed & BUTTON_UP)
else if (pressed & KEY_UP)
selected--;
else if (pressed & BUTTON_LEFT)
else if (pressed & KEY_LEFT)
selected -= CHEATS_PER_MENU_PAGE;
else if (pressed & BUTTON_RIGHT)
else if (pressed & KEY_RIGHT)
{
if (selected + CHEATS_PER_MENU_PAGE < cheatCount)
selected += CHEATS_PER_MENU_PAGE;
@@ -2021,7 +2025,7 @@ void RosalinaMenu_Cheats(void)
pagePrev = page;
page = selected / CHEATS_PER_MENU_PAGE;
} while (!terminationRequest);
} while (!menuShouldExit);
}
}

View File

@@ -38,11 +38,11 @@
Menu debuggerMenu = {
"Debugger options menu",
.nbItems = 3,
{
{ "Enable debugger", METHOD, .method = &DebuggerMenu_EnableDebugger },
{ "Disable debugger", METHOD, .method = &DebuggerMenu_DisableDebugger },
{ "Force-debug next application at launch", METHOD, .method = &DebuggerMenu_DebugNextApplicationByForce },
{},
}
};
@@ -139,13 +139,13 @@ void DebuggerMenu_EnableDebugger(void)
if(!done)
{
res = GDB_InitializeServer(&gdbServer);
Handle handles[3] = { gdbServer.super.started_event, gdbServer.super.shall_terminate_event, terminationRequestEvent };
Handle handles[3] = { gdbServer.super.started_event, gdbServer.super.shall_terminate_event, preTerminationEvent };
s32 idx;
if(R_SUCCEEDED(res))
{
debuggerCreateSocketThread();
debuggerCreateDebugThread();
res = svcWaitSynchronizationN(&idx, handles, 3, false, 10 * 1000 * 1000 * 1000LL);
res = svcWaitSynchronizationN(&idx, handles, 3, false, 5 * 1000 * 1000 * 1000LL);
if(res == 0) res = gdbServer.super.init_result;
}
@@ -162,7 +162,7 @@ void DebuggerMenu_EnableDebugger(void)
Draw_FlushFramebuffer();
Draw_Unlock();
}
while(!(waitInput() & BUTTON_B) && !terminationRequest);
while(!(waitInput() & KEY_B) && !menuShouldExit);
}
void DebuggerMenu_DisableDebugger(void)
@@ -183,7 +183,7 @@ void DebuggerMenu_DisableDebugger(void)
Draw_FlushFramebuffer();
Draw_Unlock();
}
while(!(waitInput() & BUTTON_B) && !terminationRequest);
while(!(waitInput() & KEY_B) && !menuShouldExit);
}
void DebuggerMenu_DebugNextApplicationByForce(void)
@@ -232,7 +232,7 @@ void DebuggerMenu_DebugNextApplicationByForce(void)
Draw_FlushFramebuffer();
Draw_Unlock();
}
while(!(waitInput() & BUTTON_B) && !terminationRequest);
while(!(waitInput() & KEY_B) && !menuShouldExit);
}
void debuggerSocketThreadMain(void)

View File

@@ -31,6 +31,7 @@
#include "memory.h"
#include "draw.h"
#include "hbloader.h"
#include "plgloader.h"
#include "fmt.h"
#include "utils.h" // for makeArmBranch
#include "minisoc.h"
@@ -39,29 +40,30 @@
Menu miscellaneousMenu = {
"Miscellaneous options menu",
.nbItems = 5,
{
{ "Switch the hb. title to the current app.", METHOD, .method = &MiscellaneousMenu_SwitchBoot3dsxTargetTitle },
{ "Change the menu combo", METHOD, .method = &MiscellaneousMenu_ChangeMenuCombo },
{ "Start InputRedirection", METHOD, .method = &MiscellaneousMenu_InputRedirection },
{ "Sync time and date via NTP", METHOD, .method = &MiscellaneousMenu_SyncTimeDate },
{ "Save settings", METHOD, .method = &MiscellaneousMenu_SaveSettings },
{},
}
};
void MiscellaneousMenu_SwitchBoot3dsxTargetTitle(void)
{
Result res;
u64 titleId = 0;
char failureReason[64];
if(HBLDR_3DSX_TID == HBLDR_DEFAULT_3DSX_TID)
{
FS_ProgramInfo progInfo;
u32 pid;
res = PMDBG_GetCurrentAppTitleIdAndPid(&titleId, &pid);
u32 launchFlags;
res = PMDBG_GetCurrentAppInfo(&progInfo, &pid, &launchFlags);
if(R_SUCCEEDED(res))
{
HBLDR_3DSX_TID = titleId;
HBLDR_3DSX_TID = progInfo.programId;
miscellaneousMenu.items[0].title = "Switch the hb. title to hblauncher_loader";
}
else
@@ -94,13 +96,25 @@ void MiscellaneousMenu_SwitchBoot3dsxTargetTitle(void)
Draw_FlushFramebuffer();
Draw_Unlock();
}
while(!(waitInput() & BUTTON_B) && !terminationRequest);
while(!(waitInput() & KEY_B) && !menuShouldExit);
}
static void MiscellaneousMenu_ConvertComboToString(char *out, u32 combo)
{
static const char *keys[] = { "A", "B", "Select", "Start", "Right", "Left", "Up", "Down", "R", "L", "X", "Y" };
for(s32 i = 11; i >= 0; i--)
static const char *keys[] = {
"A", "B", "Select", "Start", "Right", "Left", "Up", "Down", "R", "L", "X", "Y",
"?", "?",
"ZL", "ZR",
"?", "?", "?", "?",
"Touch",
"?", "?", "?",
"CStick Right", "CStick Left", "CStick Up", "CStick Down",
"CPad Right", "CPad Left", "CPad Up", "CPad Down",
};
char *outOrig = out;
out[0] = 0;
for(s32 i = 31; i >= 0; i--)
{
if(combo & (1 << i))
{
@@ -110,12 +124,13 @@ static void MiscellaneousMenu_ConvertComboToString(char *out, u32 combo)
}
}
out[-1] = 0;
if (out != outOrig)
out[-1] = 0;
}
void MiscellaneousMenu_ChangeMenuCombo(void)
{
char comboStrOrig[64], comboStr[64];
char comboStrOrig[128], comboStr[128];
u32 posY;
Draw_Lock();
@@ -131,9 +146,6 @@ void MiscellaneousMenu_ChangeMenuCombo(void)
posY = Draw_DrawFormattedString(10, 30, COLOR_WHITE, "The current menu combo is: %s", comboStrOrig);
posY = Draw_DrawString(10, posY + SPACING_Y, COLOR_WHITE, "Please enter the new combo:");
Draw_FlushFramebuffer();
Draw_Unlock();
menuCombo = waitCombo();
MiscellaneousMenu_ConvertComboToString(comboStr, menuCombo);
@@ -150,10 +162,10 @@ void MiscellaneousMenu_ChangeMenuCombo(void)
Draw_FlushFramebuffer();
Draw_Unlock();
}
while(!(waitInput() & BUTTON_B) && !terminationRequest);
while(!(waitInput() & KEY_B) && !menuShouldExit);
}
void MiscellaneousMenu_SaveSettings(void)
Result SaveSettings(void)
{
Result res;
@@ -168,12 +180,14 @@ void MiscellaneousMenu_SaveSettings(void)
u32 config, multiConfig, bootConfig;
u64 hbldr3dsxTitleId;
u32 rosalinaMenuCombo;
u32 rosalinaFlags;
} configData;
u32 formatVersion;
u32 config, multiConfig, bootConfig;
s64 out;
bool isSdMode;
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 2))) svcBreak(USERBREAK_ASSERT);
formatVersion = (u32)out;
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 3))) svcBreak(USERBREAK_ASSERT);
@@ -193,6 +207,7 @@ void MiscellaneousMenu_SaveSettings(void)
configData.bootConfig = bootConfig;
configData.hbldr3dsxTitleId = HBLDR_3DSX_TID;
configData.rosalinaMenuCombo = menuCombo;
configData.rosalinaFlags = PluginLoader__IsEnabled();
FS_ArchiveID archiveId = isSdMode ? ARCHIVE_SDMC : ARCHIVE_NAND_RW;
res = IFile_Open(&file, archiveId, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, "/luma/config.bin"), FS_OPEN_CREATE | FS_OPEN_WRITE);
@@ -200,6 +215,14 @@ void MiscellaneousMenu_SaveSettings(void)
if(R_SUCCEEDED(res))
res = IFile_Write(&file, &total, &configData, sizeof(configData), 0);
IFile_Close(&file);
return res;
}
void MiscellaneousMenu_SaveSettings(void)
{
Result res = SaveSettings();
Draw_Lock();
Draw_ClearFramebuffer();
Draw_FlushFramebuffer();
@@ -216,7 +239,7 @@ void MiscellaneousMenu_SaveSettings(void)
Draw_FlushFramebuffer();
Draw_Unlock();
}
while(!(waitInput() & BUTTON_B) && !terminationRequest);
while(!(waitInput() & KEY_B) && !menuShouldExit);
}
void MiscellaneousMenu_InputRedirection(void)
@@ -316,7 +339,7 @@ void MiscellaneousMenu_InputRedirection(void)
Draw_FlushFramebuffer();
Draw_Unlock();
}
while(!(waitInput() & BUTTON_B) && !terminationRequest);
while(!(waitInput() & KEY_B) && !menuShouldExit);
}
void MiscellaneousMenu_SyncTimeDate(void)
@@ -330,7 +353,6 @@ void MiscellaneousMenu_SyncTimeDate(void)
bool isSocURegistered;
time_t t;
struct tm localt = {0};
res = srvIsServiceRegistered(&isSocURegistered, "soc:U");
cantStart = R_FAILED(res) || !isSocURegistered;
@@ -350,16 +372,16 @@ void MiscellaneousMenu_SyncTimeDate(void)
input = waitInput();
if(input & BUTTON_LEFT) utcOffset = (24 + utcOffset - 1) % 24; // ensure utcOffset >= 0
if(input & BUTTON_RIGHT) utcOffset = (utcOffset + 1) % 24;
if(input & BUTTON_UP) utcOffsetMinute = (utcOffsetMinute + 1) % 60;
if(input & BUTTON_DOWN) utcOffsetMinute = (60 + utcOffsetMinute - 1) % 60;
if(input & KEY_LEFT) utcOffset = (24 + utcOffset - 1) % 24; // ensure utcOffset >= 0
if(input & KEY_RIGHT) utcOffset = (utcOffset + 1) % 24;
if(input & KEY_UP) utcOffsetMinute = (utcOffsetMinute + 1) % 60;
if(input & KEY_DOWN) utcOffsetMinute = (60 + utcOffsetMinute - 1) % 60;
Draw_FlushFramebuffer();
Draw_Unlock();
}
while(!(input & (BUTTON_A | BUTTON_B)) && !terminationRequest);
while(!(input & (KEY_A | KEY_B)) && !menuShouldExit);
if (input & BUTTON_B)
if (input & KEY_B)
return;
utcOffset -= 12;
@@ -374,8 +396,7 @@ void MiscellaneousMenu_SyncTimeDate(void)
{
t += 3600 * utcOffset;
t += 60 * utcOffsetMinute;
gmtime_r(&t, &localt);
res = ntpSetTimeDate(&localt);
res = ntpSetTimeDate(t);
}
}
@@ -399,6 +420,6 @@ void MiscellaneousMenu_SyncTimeDate(void)
Draw_FlushFramebuffer();
Draw_Unlock();
}
while(!(input & BUTTON_B) && !terminationRequest);
while(!(input & KEY_B) && !menuShouldExit);
}

View File

@@ -34,10 +34,10 @@ static char clkRateBuf[128 + 1];
Menu N3DSMenu = {
"New 3DS menu",
.nbItems = 2,
{
{ "Enable L2 cache", METHOD, .method = &N3DSMenu_EnableDisableL2Cache },
{ clkRateBuf, METHOD, .method = &N3DSMenu_ChangeClockRate }
{ clkRateBuf, METHOD, .method = &N3DSMenu_ChangeClockRate },
{},
}
};

View File

@@ -26,6 +26,7 @@
#include <3ds.h>
#include "menus/process_list.h"
#include "process_patches.h"
#include "memory.h"
#include "csvc.h"
#include "draw.h"
@@ -197,7 +198,7 @@ end:
Draw_FlushFramebuffer();
Draw_Unlock();
}
while(!(waitInput() & BUTTON_B) && !terminationRequest);
while(!(waitInput() & KEY_B) && !menuShouldExit);
#undef TRY
}
@@ -232,8 +233,8 @@ static void ProcessListMenu_MemoryViewer(const ProcessInfo *info)
svcQueryProcessMemory(&mem, &out, processHandle, heapStartAddress);
heapTotalSize = mem.size;
Result codeRes = svcMapProcessMemoryEx(processHandle, codeDestAddress, codeStartAddress, codeTotalSize);
Result heapRes = svcMapProcessMemoryEx(processHandle, heapDestAddress, heapStartAddress, heapTotalSize);
Result codeRes = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, codeDestAddress, processHandle, codeStartAddress, codeTotalSize);
Result heapRes = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, heapDestAddress, processHandle, heapStartAddress, heapTotalSize);
bool codeAvailable = R_SUCCEEDED(codeRes);
bool heapAvailable = R_SUCCEEDED(heapRes);
@@ -493,21 +494,21 @@ static void ProcessListMenu_MemoryViewer(const ProcessInfo *info)
u32 pressed = waitInputWithTimeout(1000);
if(pressed & BUTTON_A)
if(pressed & KEY_A)
editing = !editing;
else if(pressed & BUTTON_X)
else if(pressed & KEY_X)
{
if(checkMode(MENU_MODE_GOTO))
finishJumping();
else
gotoAddress = __builtin_bswap32(((u32)menus[MENU_MODE_NORMAL].buf) + menus[MENU_MODE_NORMAL].selected);
}
else if(pressed & BUTTON_Y)
else if(pressed & KEY_Y)
{
if(checkMode(MENU_MODE_SEARCH))
finishSearching();
}
else if(pressed & BUTTON_SELECT)
else if(pressed & KEY_SELECT)
{
clearMenu();
ProcessListMenu_DumpMemory(info->name, menus[MENU_MODE_NORMAL].buf, menus[MENU_MODE_NORMAL].max);
@@ -517,35 +518,35 @@ static void ProcessListMenu_MemoryViewer(const ProcessInfo *info)
if(editing)
{
// Edit the highlighted byte
if(pressed & BUTTON_LEFT)
if(pressed & KEY_LEFT)
selectedByteAdd0x10();
else if(pressed & BUTTON_RIGHT)
else if(pressed & KEY_RIGHT)
selectedByteSub0x10();
else if(pressed & BUTTON_UP)
else if(pressed & KEY_UP)
selectedByteIncrement();
else if(pressed & BUTTON_DOWN)
else if(pressed & KEY_DOWN)
selectedByteDecrement();
}
else
{
// Move the cursor
if(pressed & BUTTON_LEFT)
if(pressed & KEY_LEFT)
selectedMoveLeft();
else if(pressed & BUTTON_RIGHT)
else if(pressed & KEY_RIGHT)
selectedMoveRight();
else if(pressed & BUTTON_UP)
else if(pressed & KEY_UP)
selectedMoveUp();
else if(pressed & BUTTON_DOWN)
else if(pressed & KEY_DOWN)
selectedMoveDown();
else if(pressed & BUTTON_L1)
else if(pressed & KEY_L)
{
if(menuMode == MENU_MODE_NORMAL)
viewHeap();
else if(menuMode == MENU_MODE_SEARCH)
searchPatternReduce();
}
else if(pressed & BUTTON_R1)
else if(pressed & KEY_R)
{
if(menuMode == MENU_MODE_NORMAL)
viewCode();
@@ -554,7 +555,7 @@ static void ProcessListMenu_MemoryViewer(const ProcessInfo *info)
}
}
if(pressed & BUTTON_B) // go back to the list, or the simple viewer
if(pressed & KEY_B) // go back to the list, or the simple viewer
{
if(menuMode != MENU_MODE_NORMAL)
{
@@ -568,15 +569,15 @@ static void ProcessListMenu_MemoryViewer(const ProcessInfo *info)
if(menus[menuMode].selected >= menus[menuMode].max)
menus[menuMode].selected = menus[menuMode].max - 1;
}
while(!terminationRequest);
while(!menuShouldExit);
clearMenu();
}
if(codeAvailable)
svcUnmapProcessMemoryEx(processHandle, codeDestAddress, codeTotalSize);
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, codeDestAddress, codeTotalSize);
if(heapAvailable)
svcUnmapProcessMemoryEx(processHandle, heapDestAddress, heapTotalSize);
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, heapDestAddress, heapTotalSize);
svcCloseHandle(processHandle);
}
@@ -685,7 +686,7 @@ void RosalinaMenu_ProcessList(void)
Draw_FlushFramebuffer();
Draw_Unlock();
if(terminationRequest)
if(menuShouldExit)
break;
u32 pressed;
@@ -698,19 +699,19 @@ void RosalinaMenu_ProcessList(void)
if(memcmp(infos, infosPrev, sizeof(infos)) != 0)
break;
}
while(pressed == 0 && !terminationRequest);
while(pressed == 0 && !menuShouldExit);
if(pressed & BUTTON_B)
if(pressed & KEY_B)
break;
else if(pressed & BUTTON_A)
else if(pressed & KEY_A)
ProcessListMenu_HandleSelected(&infos[selected]);
else if(pressed & BUTTON_DOWN)
else if(pressed & KEY_DOWN)
selected++;
else if(pressed & BUTTON_UP)
else if(pressed & KEY_UP)
selected--;
else if(pressed & BUTTON_LEFT)
else if(pressed & KEY_LEFT)
selected -= PROCESSES_PER_MENU_PAGE;
else if(pressed & BUTTON_RIGHT)
else if(pressed & KEY_RIGHT)
{
if(selected + PROCESSES_PER_MENU_PAGE < processAmount)
selected += PROCESSES_PER_MENU_PAGE;
@@ -728,5 +729,5 @@ void RosalinaMenu_ProcessList(void)
pagePrev = page;
page = selected / PROCESSES_PER_MENU_PAGE;
}
while(!terminationRequest);
while(!menuShouldExit);
}

View File

@@ -91,7 +91,6 @@ void applyColorSettings(color_setting_t* cs)
Menu screenFiltersMenu = {
"Screen filters menu",
.nbItems = 6,
{
{ "Disable", METHOD, .method = &screenFiltersSetDisabled },
{ "Reduce blue light (level 1)", METHOD, .method = &screenFiltersReduceBlueLevel1 },
@@ -99,6 +98,7 @@ Menu screenFiltersMenu = {
{ "Reduce blue light (level 3)", METHOD, .method = &screenFiltersReduceBlueLevel3 },
{ "Reduce blue light (level 4)", METHOD, .method = &screenFiltersReduceBlueLevel4 },
{ "Reduce blue light (level 5)", METHOD, .method = &screenFiltersReduceBlueLevel5 },
{},
}
};

View File

@@ -34,12 +34,12 @@
Menu sysconfigMenu = {
"System configuration menu",
.nbItems = 4,
{
{ "Toggle LEDs", METHOD, .method = &SysConfigMenu_ToggleLEDs },
{ "Toggle Wireless", METHOD, .method = &SysConfigMenu_ToggleWireless },
{ "Toggle Power Button", METHOD, .method=&SysConfigMenu_TogglePowerButton },
{ "Control Wireless connection", METHOD, .method = &SysConfigMenu_ControlWifi },
{},
}
};
@@ -66,7 +66,7 @@ void SysConfigMenu_ToggleLEDs(void)
u32 pressed = waitInputWithTimeout(1000);
if(pressed & BUTTON_A)
if(pressed & KEY_A)
{
mcuHwcInit();
u8 result;
@@ -75,10 +75,10 @@ void SysConfigMenu_ToggleLEDs(void)
MCUHWC_WriteRegister(0x28, &result, 1);
mcuHwcExit();
}
else if(pressed & BUTTON_B)
else if(pressed & KEY_B)
return;
}
while(!terminationRequest);
while(!menuShouldExit);
}
void SysConfigMenu_ToggleWireless(void)
@@ -139,16 +139,16 @@ void SysConfigMenu_ToggleWireless(void)
u32 pressed = waitInputWithTimeout(1000);
if(pressed & BUTTON_A && nwmRunning)
if(pressed & KEY_A && nwmRunning)
{
nwmExtInit();
NWMEXT_ControlWirelessEnabled(!wireless);
nwmExtExit();
}
else if(pressed & BUTTON_B)
else if(pressed & KEY_B)
return;
}
while(!terminationRequest);
while(!menuShouldExit);
}
void SysConfigMenu_UpdateStatus(bool control)
@@ -224,10 +224,10 @@ static bool SysConfigMenu_ForceWifiConnection(int slot)
u32 pressed = waitInputWithTimeout(1000);
if(pressed & BUTTON_B)
if(pressed & KEY_B)
break;
}
while(!terminationRequest);
while(!menuShouldExit);
return forcedConnection;
}
@@ -259,7 +259,7 @@ void SysConfigMenu_TogglePowerButton(void)
u32 pressed = waitInputWithTimeout(1000);
if(pressed & BUTTON_A)
if(pressed & KEY_A)
{
mcuHwcInit();
MCUHWC_ReadRegister(0x18, (u8*)&mcuIRQMask, 4);
@@ -267,10 +267,10 @@ void SysConfigMenu_TogglePowerButton(void)
MCUHWC_WriteRegister(0x18, (u8*)&mcuIRQMask, 4);
mcuHwcExit();
}
else if(pressed & BUTTON_B)
else if(pressed & KEY_B)
return;
}
while(!terminationRequest);
while(!menuShouldExit);
}
void SysConfigMenu_ControlWifi(void)
@@ -296,7 +296,7 @@ void SysConfigMenu_ControlWifi(void)
u32 pressed = waitInputWithTimeout(1000);
if(pressed & BUTTON_A)
if(pressed & KEY_A)
{
if(SysConfigMenu_ForceWifiConnection(slot))
{
@@ -309,7 +309,7 @@ void SysConfigMenu_ControlWifi(void)
Draw_FlushFramebuffer();
Draw_Unlock();
}
else if(pressed & BUTTON_LEFT)
else if(pressed & KEY_LEFT)
{
slotString[slot * 4] = ' ';
slotString[(slot * 4) + 2] = ' ';
@@ -319,7 +319,7 @@ void SysConfigMenu_ControlWifi(void)
slotString[slot * 4] = '>';
slotString[(slot * 4) + 2] = '<';
}
else if(pressed & BUTTON_RIGHT)
else if(pressed & KEY_RIGHT)
{
slotString[slot * 4] = ' ';
slotString[(slot * 4) + 2] = ' ';
@@ -329,10 +329,10 @@ void SysConfigMenu_ControlWifi(void)
slotString[slot * 4] = '>';
slotString[(slot * 4) + 2] = '<';
}
else if(pressed & BUTTON_B)
else if(pressed & KEY_B)
return;
}
while(!terminationRequest);
while(!menuShouldExit);
}
void SysConfigMenu_DisableForcedWifiConnection(void)
@@ -352,8 +352,8 @@ void SysConfigMenu_DisableForcedWifiConnection(void)
Draw_DrawString(10, 30, COLOR_WHITE, "Forced connection successfully disabled.\nNote: auto-connection may remain broken.");
u32 pressed = waitInputWithTimeout(1000);
if(pressed & BUTTON_B)
if(pressed & KEY_B)
return;
}
while(!terminationRequest);
while(!menuShouldExit);
}

View File

@@ -12,6 +12,7 @@
#include <3ds/synchronization.h>
#include <3ds/result.h>
#include <string.h>
#include "csvc.h"
s32 miniSocRefCount = 0;
static u32 socContextAddr = 0x08000000;
@@ -75,7 +76,7 @@ Result miniSocInit(void)
ret = srvGetServiceHandle(&miniSocHandle, "soc:U");
if(ret != 0) goto cleanup;
ret = svcControlMemory(&tmp, socContextAddr, 0, socContextSize, MEMOP_ALLOC, MEMPERM_READ | MEMPERM_WRITE);
ret = svcControlMemoryEx(&tmp, socContextAddr, 0, socContextSize, MEMOP_ALLOC, MEMREGION_SYSTEM | MEMPERM_READ | MEMPERM_WRITE, true);
if(ret != 0) goto cleanup;
socContextAddr = tmp;

View File

@@ -40,18 +40,6 @@
#define NTP_IP MAKE_IPV4(51, 137, 137, 111) // time.windows.com
#endif
typedef struct RtcTime {
// From 3dbrew
u8 seconds;
u8 minutes;
u8 hours;
u8 dayofweek;
u8 dayofmonth;
u8 month;
u8 year;
u8 leapcount;
} RtcTime;
// From https://github.com/lettier/ntpclient/blob/master/source/c/main.c
typedef struct NtpPacket
@@ -84,17 +72,6 @@ typedef struct NtpPacket
} NtpPacket; // Total: 384 bits or 48 bytes.
void rtcToBcd(u8 *out, const RtcTime *in)
{
memcpy(out, in, 8);
for (u32 i = 0; i < 8; i++)
{
u8 units = out[i] % 10;
u8 tens = (out[i] - units) / 10;
out[i] = (tens << 4) | units;
}
}
Result ntpGetTimeStamp(time_t *outTimestamp)
{
Result res = 0;
@@ -162,42 +139,33 @@ cleanup:
return res;
}
Result ntpSetTimeDate(const struct tm *localt)
Result ntpSetTimeDate(time_t timestamp)
{
Result res = mcuHwcInit();
Result res = ptmSysmInit();
if (R_FAILED(res)) return res;
res = cfguInit();
if (R_FAILED(res)) goto cleanup;
if (R_FAILED(res))
{
ptmSysmExit();
return res;
}
// First, set the config RTC offset to 0
u8 rtcOff[8] = {0};
res = CFG_SetConfigInfoBlk4(8, 0x30001, rtcOff);
if (R_FAILED(res)) goto cleanup;
u8 yr = (u8)(localt->tm_year - 100);
// Update the RTC
u8 bcd[8];
RtcTime lt = {
.seconds = (u8)localt->tm_sec,
.minutes = (u8)localt->tm_min,
.hours = (u8)localt->tm_hour,
.dayofweek = (u8)localt->tm_wday,
.dayofmonth = (u8)localt->tm_mday,
.month = (u8)(localt->tm_mon + 1),
.year = yr,
.leapcount = 0,
};
rtcToBcd(bcd, &lt);
res = MCUHWC_WriteRegister(0x30, bcd, 7);
// 946684800 is the timestamp of 01/01/2000 00:00 relative to the Unix Epoch
s64 msY2k = (timestamp - 946684800) * 1000;
res = PTMSYSM_SetRtcTime(msY2k);
if (R_FAILED(res)) goto cleanup;
// Save the config changes
res = CFG_UpdateConfigSavegame();
cleanup:
mcuHwcExit();
ptmSysmExit();
cfguExit();
return res;
}

View File

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

View File

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

View File

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

View File

@@ -10,15 +10,16 @@
#include <3ds/services/pmdbg.h>
#include <3ds/ipc.h>
Result PMDBG_GetCurrentAppTitleIdAndPid(u64 *outTitleId, u32 *outPid)
Result PMDBG_GetCurrentAppInfo(FS_ProgramInfo *outProgramInfo, u32 *outPid, u32 *outLaunchFlags)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x100, 0, 0);
if(R_FAILED(ret = svcSendSyncRequest(*pmDbgGetSessionHandle()))) return ret;
memcpy(outTitleId, cmdbuf + 2, 8);
*outPid = cmdbuf[4];
memcpy(outProgramInfo, cmdbuf + 2, sizeof(FS_ProgramInfo));
*outPid = cmdbuf[6];
*outLaunchFlags = cmdbuf[7];
return cmdbuf[1];
}
@@ -47,3 +48,16 @@ Result PMDBG_LaunchTitleDebug(Handle *outDebug, const FS_ProgramInfo *programInf
*outDebug = cmdbuf[3];
return (Result)cmdbuf[1];
}
Result PMDBG_PrepareToChainloadHomebrew(u64 titleId)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x103, 2, 0);
memcpy(&cmdbuf[1], &titleId, 8);
if(R_FAILED(ret = svcSendSyncRequest(*pmDbgGetSessionHandle()))) return ret;
return (Result)cmdbuf[1];
}

View File

@@ -25,48 +25,11 @@
*/
#include <3ds.h>
#include <string.h>
#include "csvc.h"
#include "menus/process_patches.h"
#include "memory.h"
#include "draw.h"
#include "hbloader.h"
#include "fmt.h"
#include "process_patches.h"
#include "utils.h"
static Result ProcessPatchesMenu_DoPatchUnpatchFS(u32 textTotalRoundedSize)
{
static bool patched = false;
static u16 *off;
static u16 origData[2];
static const u16 pattern[2] = {
0x7401, // strb r1, [r0, #16]
0x2000, // movs r0, #0
};
if(patched)
{
memcpy(off, &origData, sizeof(origData));
patched = false;
}
else
{
off = (u16 *)memsearch((u8 *)0x00100000, &pattern, textTotalRoundedSize, sizeof(pattern));
if(off == NULL)
return -1;
for(; (*off & 0xFF00) != 0xB500; off++); // Find function start
memcpy(origData, off, 4);
off[0] = 0x2001; // mov r0, #1
off[1] = 0x4770; // bx lr
patched = true;
}
//processPatchesMenu.items[1].title = patched ? "Unpatch FS for the archive checks" : "Patch FS for the archive checks";
return 0;
}
Result OpenProcessByName(const char *name, Handle *h)
{
u32 pidList[0x40];
@@ -96,7 +59,7 @@ Result OpenProcessByName(const char *name, Handle *h)
return 0;
}
static u32 ProcessPatchesMenu_PatchUnpatchProcessByName(const char *name, Result (*func)(u32 size))
Result PatchProcessByName(const char *name, Result (*func)(u32 size))
{
Result res;
Handle processHandle;
@@ -105,16 +68,11 @@ static u32 ProcessPatchesMenu_PatchUnpatchProcessByName(const char *name, Result
s64 textTotalRoundedSize = 0, startAddress = 0;
svcGetProcessInfo(&textTotalRoundedSize, processHandle, 0x10002); // only patch .text
svcGetProcessInfo(&startAddress, processHandle, 0x10005);
if(R_FAILED(res = svcMapProcessMemoryEx(processHandle, 0x00100000, (u32) startAddress, textTotalRoundedSize)))
if(R_FAILED(res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, processHandle, (u32) startAddress, textTotalRoundedSize)))
return res;
res = func(textTotalRoundedSize);
svcUnmapProcessMemoryEx(processHandle, 0x00100000, textTotalRoundedSize);
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, textTotalRoundedSize);
return res;
}
void ProcessPatchesMenu_PatchUnpatchFSDirectly(void)
{
ProcessPatchesMenu_PatchUnpatchProcessByName("fs", &ProcessPatchesMenu_DoPatchUnpatchFS);
}

View File

@@ -0,0 +1,61 @@
/*
* This file is part of Luma3DS
* Copyright (C) 2016-2018 Aurora Wright, TuxSH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include <3ds.h>
static bool g_isSleeping = false;
static LightEvent g_onWakeUpEvent;
void Sleep__Init(void)
{
srvSubscribe(0x214); ///< Sleep entry
srvSubscribe(0x213); ///< Sleep exit
LightEvent_Init(&g_onWakeUpEvent, RESET_STICKY);
}
void Sleep__HandleNotification(u32 notifId)
{
if (notifId == 0x214) ///< Sleep entry
{
LightEvent_Clear(&g_onWakeUpEvent);
g_isSleeping = true;
}
else if (notifId == 0x213) ///< Sleep exit
{
g_isSleeping = false;
LightEvent_Signal(&g_onWakeUpEvent);
}
}
bool Sleep__Status(void)
{
if (g_isSleeping)
{
LightEvent_Wait(&g_onWakeUpEvent);
return true;
}
return false;
}

View File

@@ -9,13 +9,15 @@
#include <3ds/result.h>
#include <3ds/svc.h>
#include <3ds/synchronization.h>
#include <3ds/services/ac.h>
#include <arpa/inet.h>
#include "memory.h"
#include "minisoc.h"
#include "sock_util.h"
#include "sleep.h"
extern Handle terminationRequestEvent;
extern bool terminationRequest;
extern Handle preTerminationEvent;
extern bool preTerminationRequested;
// soc's poll function is odd, and doesn't like -1 as fd.
// so this compacts everything together
@@ -104,7 +106,7 @@ Result server_init(struct sock_server *serv)
Result server_bind(struct sock_server *serv, u16 port)
{
int server_sockfd;
Handle handles[2] = { terminationRequestEvent, serv->shall_terminate_event };
Handle handles[2] = { preTerminationEvent, serv->shall_terminate_event };
s32 idx = -1;
server_sockfd = socSocket(AF_INET, SOCK_STREAM, 0);
@@ -163,7 +165,7 @@ Result server_bind(struct sock_server *serv, u16 port)
static bool server_should_exit(struct sock_server *serv)
{
return svcWaitSynchronization(serv->shall_terminate_event, 0) == 0 || svcWaitSynchronization(terminationRequestEvent, 0) == 0;
return svcWaitSynchronization(serv->shall_terminate_event, 0) == 0 || svcWaitSynchronization(preTerminationEvent, 0) == 0;
}
void server_run(struct sock_server *serv)
@@ -172,7 +174,7 @@ void server_run(struct sock_server *serv)
serv->running = true;
svcSignalEvent(serv->started_event);
while(serv->running && !terminationRequest)
while(serv->running && !preTerminationRequested)
{
if(server_should_exit(serv))
goto abort_connections;
@@ -185,6 +187,14 @@ void server_run(struct sock_server *serv)
for(nfds_t i = 0; i < serv->nfds; i++)
fds[i].revents = 0;
if (Sleep__Status())
{
while (!Wifi__IsConnected()
&& serv->running && !preTerminationRequested)
svcSleepThread(1000000000ULL);
}
int pollres = socPoll(fds, serv->nfds, 50);
if(server_should_exit(serv) || pollres < -10000)
@@ -321,3 +331,13 @@ void server_finalize(struct sock_server *serv)
svcClearEvent(serv->started_event);
svcCloseHandle(serv->started_event);
}
bool Wifi__IsConnected(void)
{
u32 status = 0;
u32 wifistatus = 0;
acInit();
return R_SUCCEEDED(ACU_GetWifiStatus(&wifistatus)) && wifistatus > 0
&& R_SUCCEEDED(ACU_GetStatus(&status)) && status != 1;
}

View File

@@ -0,0 +1,110 @@
/*
* This file is part of Luma3DS
* Copyright (C) 2016-2020 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 "utils.h"
#include "csvc.h"
#include <string.h>
#include <stdio.h>
void formatMemoryPermission(char *outbuf, MemPerm perm)
{
if (perm == MEMPERM_DONTCARE)
{
strcpy(outbuf, "???");
return;
}
outbuf[0] = perm & MEMPERM_READ ? 'r' : '-';
outbuf[1] = perm & MEMPERM_WRITE ? 'w' : '-';
outbuf[2] = perm & MEMPERM_EXECUTE ? 'x' : '-';
outbuf[3] = '\0';
}
void formatUserMemoryState(char *outbuf, MemState state)
{
static const char *states[12] =
{
"Free",
"Reserved",
"IO",
"Static",
"Code",
"Private",
"Shared",
"Continuous",
"Aliased",
"Alias",
"AliasCode",
"Locked"
};
strcpy(outbuf, state > 11 ? "Unknown" : states[state]);
}
u32 formatMemoryMapOfProcess(char *outbuf, u32 bufLen, Handle handle)
{
u32 maxLineSize = 35 + (handle == CUR_PROCESS_HANDLE ? 15 : 0);
u32 address = 0;
u32 posInBuffer = 0;
u32 maxPosInBuffer = bufLen - maxLineSize; // 35 is the maximum length of a formatted region
MemInfo memi;
PageInfo pagei;
char pabuf[32];
char permbuf[8];
char statebuf[16];
s64 TTBCR;
svcGetSystemInfo(&TTBCR, 0x10002, 0);
while (address < (1u << (32 - (u32)TTBCR)) // Limit to check for regions
&& posInBuffer < maxPosInBuffer
&& R_SUCCEEDED(svcQueryProcessMemory(&memi, &pagei, handle, address)))
{
// Update the address for next region
address = memi.base_addr + memi.size;
// If region isn't FREE then add it to the list
if (memi.state != MEMSTATE_FREE)
{
if (handle == CUR_PROCESS_HANDLE)
{
u32 pa = svcConvertVAToPA((void *)memi.base_addr, false);
sprintf(pabuf, " (PA %08lx)", pa);
}
else
pabuf[0] = '\0';
formatMemoryPermission(permbuf, memi.perm);
formatUserMemoryState(statebuf, memi.state);
posInBuffer += sprintf(outbuf + posInBuffer, "%08lx - %08lx%s %s %s\n",
memi.base_addr, address, pabuf, permbuf, statebuf);
}
}
svcCloseHandle(handle);
return posInBuffer;
}

View File

@@ -10,8 +10,10 @@ This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for
#include <3ds.h>
#include <string.h>
#define IS_PRE_7X (osGetFirmVersion() < SYSTEM_VERSION(2, 39, 4))
#define IS_PRE_93 (osGetFirmVersion() < SYSTEM_VERSION(2, 48, 3))
#define KERNEL_VERSION_MINOR (GET_VERSION_MINOR(osGetKernelVersion()))
#define IS_PRE_7X (KERNEL_VERSION_MINOR < 39)
#define IS_PRE_93 (KERNEL_VERSION_MINOR < 48)
extern u32 nbSection0Modules;
extern Handle resumeGetServiceHandleOrPortRegisteredSemaphore;

View File

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