Compare commits
73 Commits
release
...
v10.1.3-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53ab41e6d0 | ||
|
|
1176fa8f28 | ||
|
|
e69f89a0d4 | ||
|
|
9411a8c186 | ||
|
|
aa577d0e39 | ||
|
|
241dd35000 | ||
|
|
ca48641a7e | ||
|
|
1fce207bcf | ||
|
|
c58cb2d916 | ||
|
|
d734e36a3a | ||
|
|
28d84f30bb | ||
|
|
0bb56031d7 | ||
|
|
cc9977774e | ||
|
|
a39053c3c3 | ||
|
|
dc4de4ce6f | ||
|
|
4e12453fff | ||
|
|
3a0418e279 | ||
|
|
1899bf377b | ||
|
|
0471002d4c | ||
|
|
704e08dc23 | ||
|
|
905837468c | ||
|
|
19d95782e1 | ||
|
|
adda19ecb2 | ||
|
|
b02d0346fd | ||
|
|
9097276a06 | ||
|
|
e99ab11c6f | ||
|
|
a564d8536a | ||
|
|
a21eee9207 | ||
|
|
71cddef78f | ||
|
|
9ae913064c | ||
|
|
a2313d1c03 | ||
|
|
22db3445a0 | ||
|
|
6417720d7d | ||
|
|
8b10906d90 | ||
|
|
0c55324d11 | ||
|
|
0b4fdc6e66 | ||
|
|
d3e62df769 | ||
|
|
04bd881cfa | ||
|
|
96799455cb | ||
|
|
814792eb91 | ||
|
|
2834bae318 | ||
|
|
037fae99d6 | ||
|
|
49c8888948 | ||
|
|
1875556f81 | ||
|
|
00850bf691 | ||
|
|
09fd199487 | ||
|
|
32c53578e0 | ||
|
|
0da90f61fc | ||
|
|
9942e8b299 | ||
|
|
daaeb97834 | ||
|
|
92da214066 | ||
|
|
0f05dd5c0a | ||
|
|
166bdbeb7d | ||
|
|
7dc2b7123b | ||
|
|
3d0ec9b785 | ||
|
|
85cfa5cba6 | ||
|
|
fdc1eaa16c | ||
|
|
d4dcf1a3e9 | ||
|
|
43fd137d55 | ||
|
|
6931eadc34 | ||
|
|
f1b787c7d9 | ||
|
|
6b13b5d06b | ||
|
|
9705083b65 | ||
|
|
2423d1802e | ||
|
|
0b68baa0dd | ||
|
|
8168d2c2f9 | ||
|
|
4b341e039a | ||
|
|
1ae01c2406 | ||
|
|
2182742708 | ||
|
|
2520079536 | ||
|
|
d7095ce37d | ||
|
|
0a87e41c66 | ||
|
|
bec8daf028 |
4
.github/ISSUE_TEMPLATE/bug-report.md
vendored
4
.github/ISSUE_TEMPLATE/bug-report.md
vendored
@@ -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:**
|
||||
|
||||
|
||||
54
README.md
54
README.md
@@ -1,13 +1,51 @@
|
||||
# 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 +54,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
|
||||
|
||||
@@ -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)"
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ typedef struct __attribute__((packed, aligned(4)))
|
||||
u32 config, multiConfig, bootConfig;
|
||||
u64 hbldr3dsxTitleId;
|
||||
u32 rosalinaMenuCombo;
|
||||
u32 rosalinaFlags;
|
||||
} CfgData;
|
||||
|
||||
typedef struct
|
||||
|
||||
@@ -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 = ""
|
||||
|
||||
|
||||
@@ -33,5 +33,6 @@ enum singleOptions
|
||||
PATCHVERSTRING,
|
||||
SHOWGBABOOT,
|
||||
PATCHUNITINFO,
|
||||
DISABLEARM11EXCHANDLERS
|
||||
DISABLEARM11EXCHANDLERS,
|
||||
ENABLESAFEFIRMROSALINA,
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
129
k11_extension/include/mmu.h
Normal 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);
|
||||
21
k11_extension/include/svc/ControlProcess.h
Normal file
21
k11_extension/include/svc/ControlProcess.h
Normal 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);
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
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
315
k11_extension/source/mmu.c
Normal 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;
|
||||
}
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "svc/MapProcessMemoryEx.h"
|
||||
#include "svc/UnmapProcessMemoryEx.h"
|
||||
#include "svc/ControlService.h"
|
||||
#include "svc/ControlProcess.h"
|
||||
#include "svc/CopyHandle.h"
|
||||
#include "svc/TranslateHandle.h"
|
||||
|
||||
@@ -59,13 +60,16 @@ void signalSvcEntry(u8 *pageEnd)
|
||||
|
||||
// Since DBGEVENT_SYSCALL_ENTRY is non blocking, we'll cheat using EXCEVENT_UNDEFINED_SYSCALL (debug->svcId is fortunately an u16!)
|
||||
if(debugOfProcess(currentProcess) != NULL && shouldSignalSyscallDebugEvent(currentProcess, svcId))
|
||||
{
|
||||
SignalDebugEvent(DBGEVENT_OUTPUT_STRING, 0xFFFFFFFE, svcId);
|
||||
}
|
||||
}
|
||||
|
||||
void signalSvcReturn(u8 *pageEnd)
|
||||
{
|
||||
u32 svcId = (u32) *(u8 *)(pageEnd - 0xB5);
|
||||
KProcess *currentProcess = currentCoreContext->objectContext.currentProcess;
|
||||
u32 flags = KPROCESS_GET_RVALUE(currentProcess, customFlags);
|
||||
|
||||
if(svcId == 0xFE)
|
||||
svcId = *(u32 *)(pageEnd - 0x110 + 8 * 4); // r12 ; note: max theortical SVC atm: 0x1FFFFFFF. We don't support catching svcIds >= 0x100 atm either
|
||||
@@ -73,6 +77,13 @@ void signalSvcReturn(u8 *pageEnd)
|
||||
// Since DBGEVENT_SYSCALL_RETURN is non blocking, we'll cheat using EXCEVENT_UNDEFINED_SYSCALL (debug->svcId is fortunately an u16!)
|
||||
if(debugOfProcess(currentProcess) != NULL && shouldSignalSyscallDebugEvent(currentProcess, svcId))
|
||||
SignalDebugEvent(DBGEVENT_OUTPUT_STRING, 0xFFFFFFFF, svcId);
|
||||
|
||||
// Signal if the memory layout of the process changed
|
||||
if (flags & SignalOnMemLayoutChanges && flags & MemLayoutChanged)
|
||||
{
|
||||
*KPROCESS_GET_PTR(currentProcess, customFlags) = flags & ~MemLayoutChanged;
|
||||
SignalEvent(KPROCESS_GET_RVALUE(currentProcess, onMemoryLayoutChangeEvent));
|
||||
}
|
||||
}
|
||||
|
||||
void postprocessSvc(void)
|
||||
@@ -91,10 +102,26 @@ void *svcHook(u8 *pageEnd)
|
||||
u32 svcId = *(u8 *)(pageEnd - 0xB5);
|
||||
if(svcId == 0xFE)
|
||||
svcId = *(u32 *)(pageEnd - 0x110 + 8 * 4); // r12 ; note: max theortical SVC atm: 0x3FFFFFFF. We don't support catching svcIds >= 0x100 atm either
|
||||
|
||||
switch(svcId)
|
||||
{
|
||||
case 0x01:
|
||||
return ControlMemoryHookWrapper;
|
||||
case 0x03: /* svcExitProcess */
|
||||
{
|
||||
// Signal that the process is about to be terminated
|
||||
u32 flags = KPROCESS_GET_RVALUE(currentProcess, customFlags);
|
||||
|
||||
if (flags & SignalOnExit)
|
||||
{
|
||||
SignalEvent(KPROCESS_GET_RVALUE(currentProcess, onProcessExitEvent));
|
||||
|
||||
KEvent* event = (KEvent *)KProcessHandleTable__ToKAutoObject(handleTableOfProcess(currentProcess),
|
||||
KPROCESS_GET_RVALUE(currentProcess, resumeProcessExitEvent));
|
||||
((KAutoObject *)event)->vtable->DecrementReferenceCount((KAutoObject *)event);
|
||||
}
|
||||
return officialSVCs[0x3];
|
||||
}
|
||||
case 0x29:
|
||||
return GetHandleInfoHookWrapper;
|
||||
case 0x2A:
|
||||
@@ -136,7 +163,7 @@ void *svcHook(u8 *pageEnd)
|
||||
return invalidateEntireInstructionCache;
|
||||
|
||||
case 0xA0:
|
||||
return MapProcessMemoryEx;
|
||||
return MapProcessMemoryExWrapper;
|
||||
case 0xA1:
|
||||
return UnmapProcessMemoryEx;
|
||||
case 0xA2:
|
||||
@@ -148,6 +175,8 @@ void *svcHook(u8 *pageEnd)
|
||||
return CopyHandleWrapper;
|
||||
case 0xB2:
|
||||
return TranslateHandleWrapper;
|
||||
case 0xB3:
|
||||
return ControlProcess;
|
||||
|
||||
default:
|
||||
return (svcId <= 0x7D) ? officialSVCs[svcId] : NULL;
|
||||
|
||||
215
k11_extension/source/svc/ControlProcess.c
Normal file
215
k11_extension/source/svc/ControlProcess.c
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess);
|
||||
KAutoObject *obj;
|
||||
|
||||
if(handle == CUR_PROCESS_HANDLE)
|
||||
{
|
||||
obj = (KAutoObject *)(currentCoreContext->objectContext.currentProcess);
|
||||
@@ -45,6 +48,10 @@ Result GetHandleInfoHook(s64 *out, Handle handle, u32 type)
|
||||
if(obj == NULL)
|
||||
return 0xD8E007F7;
|
||||
|
||||
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)
|
||||
@@ -53,10 +60,70 @@ Result GetHandleInfoHook(s64 *out, Handle handle, u32 type)
|
||||
hwInfo = NULL;
|
||||
|
||||
*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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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))));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -35,7 +35,8 @@ enum singleOptions
|
||||
PATCHVERSTRING,
|
||||
SHOWGBABOOT,
|
||||
PATCHUNITINFO,
|
||||
DISABLEARM11EXCHANDLERS
|
||||
DISABLEARM11EXCHANDLERS,
|
||||
ENABLESAFEFIRMROSALINA,
|
||||
};
|
||||
|
||||
extern u32 config, multiConfig, bootConfig;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -2,5 +2,7 @@
|
||||
|
||||
#include <3ds/types.h>
|
||||
|
||||
bool hasKExt(void);
|
||||
u32 getKExtSize(void);
|
||||
u32 getStolenSystemMemRegionSize(void);
|
||||
bool isTitleLaunchPrevented(u64 titleId);
|
||||
@@ -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 },
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
37
sysmodules/rosalina/include/3gx.h
Normal file
37
sysmodules/rosalina/include/3gx.h
Normal 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);
|
||||
@@ -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);
|
||||
///@}
|
||||
|
||||
@@ -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,35 @@
|
||||
#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);
|
||||
Result Draw_AllocateFramebufferCache(u32 size);
|
||||
Result Draw_AllocateFramebufferCacheForScreenshot(u32 size);
|
||||
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);
|
||||
// Width is actually height as the 3ds screen is rotated 90 degrees
|
||||
void Draw_GetCurrentScreenInfo(u32 *width, bool *is3d, bool top);
|
||||
|
||||
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 width, u32 startingLine, u32 numLines, bool top, bool left);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 DispMessage(const char *title, const char *message);
|
||||
u32 DispErrMessage(const char *title, const char *message, const Result error);
|
||||
void DisplayPluginMenu(u32 *cmdbuf);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -30,12 +30,16 @@
|
||||
|
||||
extern Menu screenFiltersMenu;
|
||||
|
||||
int screenFiltersCurrentTemperature;
|
||||
extern int screenFiltersCurrentTemperature;
|
||||
|
||||
void screenFiltersSetDisabled(void);
|
||||
void screenFiltersReduceBlueLevel1(void);
|
||||
void screenFiltersReduceBlueLevel2(void);
|
||||
void screenFiltersReduceBlueLevel3(void);
|
||||
void screenFiltersReduceBlueLevel4(void);
|
||||
void screenFiltersReduceBlueLevel5(void);
|
||||
void screenFiltersSetTemperature(int temperature);
|
||||
void ScreenFiltersMenu_SetDefault(void); // 6500K (default)
|
||||
|
||||
void ScreenFiltersMenu_SetAquarium(void); // 10000K
|
||||
void ScreenFiltersMenu_SetOvercastSky(void); // 7500K
|
||||
void ScreenFiltersMenu_SetDaylight(void); // 5500K
|
||||
void ScreenFiltersMenu_SetFluorescent(void); // 4200K
|
||||
void ScreenFiltersMenu_SetHalogen(void); // 3400K
|
||||
void ScreenFiltersMenu_SetIncandescent(void); // 2700K
|
||||
void ScreenFiltersMenu_SetWarmIncandescent(void); // 2300K
|
||||
void ScreenFiltersMenu_SetCandle(void); // 1900K
|
||||
void ScreenFiltersMenu_SetEmber(void); // 1200K
|
||||
|
||||
@@ -30,4 +30,4 @@
|
||||
#include <time.h>
|
||||
|
||||
Result ntpGetTimeStamp(time_t *outTimestamp);
|
||||
Result ntpSetTimeDate(const struct tm *localt);
|
||||
Result ntpSetTimeDate(time_t timestamp);
|
||||
|
||||
32
sysmodules/rosalina/include/plgldr.h
Normal file
32
sysmodules/rosalina/include/plgldr.h
Normal 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);
|
||||
9
sysmodules/rosalina/include/plgloader.h
Normal file
9
sysmodules/rosalina/include/plgloader.h
Normal 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);
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
31
sysmodules/rosalina/include/sleep.h
Normal file
31
sysmodules/rosalina/include/sleep.h
Normal 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);
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -37,7 +37,7 @@ AccessControlInfo:
|
||||
CoreVersion : 2
|
||||
DescVersion : 2
|
||||
|
||||
MemoryType : System # Application / System / Base
|
||||
MemoryType : Base # Application / System / Base
|
||||
HandleTableSize: 150
|
||||
|
||||
MemoryMapping:
|
||||
|
||||
62
sysmodules/rosalina/source/3gx.c
Normal file
62
sysmodules/rosalina/source/3gx.c
Normal 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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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,39 +129,98 @@ void Draw_ClearFramebuffer(void)
|
||||
Draw_FillFramebuffer(0);
|
||||
}
|
||||
|
||||
void Draw_SetupFramebuffer(void)
|
||||
Result Draw_AllocateFramebufferCache(u32 size)
|
||||
{
|
||||
// Can't use fbs in FCRAM when Home Menu is active (AXI config related maybe?)
|
||||
u32 addr = 0x0D000000;
|
||||
u32 tmp;
|
||||
|
||||
size = (size + 0xFFF) >> 12 << 12; // round-up
|
||||
|
||||
if (framebufferCache != NULL)
|
||||
__builtin_trap();
|
||||
|
||||
Result res = svcControlMemoryEx(&tmp, addr, 0, size, MEMOP_ALLOC, MEMREGION_SYSTEM | MEMPERM_READWRITE, true);
|
||||
if (R_FAILED(res))
|
||||
{
|
||||
framebufferCache = NULL;
|
||||
framebufferCacheSize = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
framebufferCache = (u32 *)addr;
|
||||
framebufferCacheSize = size;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
Result Draw_AllocateFramebufferCacheForScreenshot(u32 size)
|
||||
{
|
||||
u32 remaining = (u32)osGetMemRegionFree(MEMREGION_SYSTEM);
|
||||
u32 sz = remaining < size ? remaining : size;
|
||||
return Draw_AllocateFramebufferCache(sz);
|
||||
}
|
||||
|
||||
void Draw_FreeFramebufferCache(void)
|
||||
{
|
||||
u32 tmp;
|
||||
if (framebufferCache != NULL)
|
||||
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();
|
||||
|
||||
u32 format = GPU_FB_BOTTOM_FMT;
|
||||
|
||||
gpuSavedFramebufferAddr1 = GPU_FB_BOTTOM_ADDR_1;
|
||||
gpuSavedFramebufferAddr2 = GPU_FB_BOTTOM_ADDR_2;
|
||||
gpuSavedFramebufferFormat = GPU_FB_BOTTOM_FMT;
|
||||
gpuSavedFramebufferFormat = format;
|
||||
gpuSavedFramebufferStride = GPU_FB_BOTTOM_STRIDE;
|
||||
|
||||
format = (format & ~7) | GSP_RGB565_OES;
|
||||
format |= 3 << 8; // set VRAM bits
|
||||
|
||||
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 = format;
|
||||
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)
|
||||
{
|
||||
svcFlushProcessDataCache(CUR_PROCESS_HANDLE, FB_BOTTOM_VRAM_ADDR, FB_BOTTOM_SIZE);
|
||||
svcFlushProcessDataCache(CUR_PROCESS_HANDLE, (u32)FB_BOTTOM_VRAM_ADDR, FB_BOTTOM_SIZE);
|
||||
}
|
||||
|
||||
u32 Draw_GetCurrentFramebufferAddress(bool top, bool left)
|
||||
@@ -182,6 +241,21 @@ u32 Draw_GetCurrentFramebufferAddress(bool top, bool left)
|
||||
}
|
||||
}
|
||||
|
||||
void Draw_GetCurrentScreenInfo(u32 *width, bool *is3d, bool top)
|
||||
{
|
||||
if (top)
|
||||
{
|
||||
bool isNormal2d = (GPU_FB_TOP_FMT & BIT(6)) != 0;
|
||||
*is3d = (GPU_FB_TOP_FMT & BIT(5)) != 0;
|
||||
*width = !(*is3d) && !isNormal2d ? 800 : 400;
|
||||
}
|
||||
else
|
||||
{
|
||||
*is3d = false;
|
||||
*width = 320;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void Draw_WriteUnaligned(u8 *dst, u32 tmp, u32 size)
|
||||
{
|
||||
memcpy(dst, &tmp, size);
|
||||
@@ -267,16 +341,38 @@ 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;
|
||||
u32 width;
|
||||
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->width;
|
||||
u32 stride = args->top ? GPU_FB_TOP_STRIDE : GPU_FB_BOTTOM_STRIDE;
|
||||
|
||||
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++)
|
||||
Draw_ConvertPixelToBGR8(line + x * 3 , addr + x * stride + y * formatSizes[(u8)fmt], fmt);
|
||||
{
|
||||
__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 width, u32 startingLine, u32 numLines, bool top, bool left)
|
||||
{
|
||||
FrameBufferConvertArgs args = { buf, width, (u8)startingLine, (u8)numLines, top, left };
|
||||
svcCustomBackdoor(Draw_ConvertFrameBufferLinesKernel, &args);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,12 +233,28 @@ 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);
|
||||
@@ -172,6 +262,69 @@ end:
|
||||
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];
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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 pressedKey = false;
|
||||
u32 key = 0;
|
||||
u32 n = 0;
|
||||
bool isHidInitialized = false;
|
||||
|
||||
//Wait for no keys to be pressed
|
||||
while(HID_PAD && !terminationRequest && (msec == 0 || n < msec))
|
||||
// libctru redefinition:
|
||||
|
||||
bool hidShouldUseIrrst(void)
|
||||
{
|
||||
svcSleepThread(1 * 1000 * 1000LL);
|
||||
n++;
|
||||
// ir:rst exposes only two sessions :(
|
||||
return false;
|
||||
}
|
||||
|
||||
if(terminationRequest || (msec != 0 && n >= msec))
|
||||
return 0;
|
||||
static inline u32 convertHidKeys(u32 keys)
|
||||
{
|
||||
// Nothing to do yet
|
||||
return keys;
|
||||
}
|
||||
|
||||
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);
|
||||
n++;
|
||||
}
|
||||
|
||||
if(terminationRequest || (msec != 0 && n >= msec))
|
||||
return 0;
|
||||
|
||||
key = HID_PAD;
|
||||
|
||||
//Make sure it's pressed
|
||||
for(u32 i = 0x26000; i > 0; i --)
|
||||
Draw_Lock();
|
||||
if (!isHidInitialized || menuShouldExit)
|
||||
{
|
||||
if(key != HID_PAD) break;
|
||||
if(i == 1) pressedKey = true;
|
||||
keys = 0;
|
||||
Draw_Unlock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
while(!pressedKey);
|
||||
n++;
|
||||
|
||||
return key;
|
||||
hidScanInput();
|
||||
keys = convertHidKeys(hidKeysDown()) | (convertHidKeys(hidKeysDownRepeat()) & DIRECTIONAL_KEYS);
|
||||
Draw_Unlock();
|
||||
} while (keys == 0 && !menuShouldExit && isHidInitialized && (msec < 0 || n < msec));
|
||||
|
||||
|
||||
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;
|
||||
|
||||
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)
|
||||
{
|
||||
if((HID_PAD & menuCombo) == menuCombo)
|
||||
{
|
||||
if (!isAcURegistered)
|
||||
isAcURegistered = R_SUCCEEDED(srvIsServiceRegistered(&isAcURegistered, "ac:u"))
|
||||
&& isAcURegistered;
|
||||
hidInit(); // assume this doesn't fail
|
||||
isHidInitialized = true;
|
||||
|
||||
if (isAcURegistered)
|
||||
while(!preTerminationRequested)
|
||||
{
|
||||
if (menuShouldExit)
|
||||
continue;
|
||||
|
||||
if ((scanHeldKeys() & menuCombo) == menuCombo)
|
||||
{
|
||||
menuEnter();
|
||||
if(isN3DS) N3DSMenu_UpdateStatus();
|
||||
PluginLoader__UpdateMenu();
|
||||
menuShow(&rosalinaMenu);
|
||||
menuLeave();
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
|
||||
// Check for home button on O3DS Mode3 with plugin loaded
|
||||
if (homeBtnPressed != 0)
|
||||
{
|
||||
Cheat_ApplyCheats();
|
||||
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 (R_FAILED(Draw_AllocateFramebufferCache(FB_BOTTOM_SIZE)))
|
||||
{
|
||||
// Oops
|
||||
menuRefCount = 0;
|
||||
svcKernelSetState(0x10000, 1);
|
||||
svcSleepThread(5 * 1000 * 100LL);
|
||||
Draw_SetupFramebuffer();
|
||||
Draw_ClearFramebuffer();
|
||||
}
|
||||
else
|
||||
Draw_SetupFramebuffer();
|
||||
}
|
||||
Draw_Unlock();
|
||||
}
|
||||
|
||||
void menuLeave(void)
|
||||
{
|
||||
svcSleepThread(50 * 1000 * 1000);
|
||||
|
||||
if(AtomicDecrement(&menuRefCount) == 0)
|
||||
{
|
||||
Draw_Lock();
|
||||
Draw_FlushFramebuffer();
|
||||
if(--menuRefCount == 0)
|
||||
{
|
||||
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(¤tMenu->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(¤tMenu->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(¤tMenu->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();
|
||||
}
|
||||
|
||||
@@ -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,69 @@ void RosalinaMenu_PowerOff(void) // Soft shutdown.
|
||||
|
||||
u32 pressed = waitInputWithTimeout(1000);
|
||||
|
||||
if(pressed & BUTTON_A)
|
||||
if(pressed & KEY_A)
|
||||
{
|
||||
menuLeave();
|
||||
srvPublishToSubscriber(0x203, 0);
|
||||
}
|
||||
else if(pressed & BUTTON_B)
|
||||
return;
|
||||
}
|
||||
while(!terminationRequest);
|
||||
else if(pressed & KEY_B)
|
||||
return;
|
||||
}
|
||||
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, u32 width, bool top, bool left)
|
||||
{
|
||||
u64 total;
|
||||
Result res = 0;
|
||||
u32 lineSize = 3 * width;
|
||||
u32 remaining = lineSize * 240;
|
||||
|
||||
TRY(Draw_AllocateFramebufferCacheForScreenshot(remaining));
|
||||
|
||||
u8 *framebufferCache = (u8 *)Draw_GetFramebufferCache();
|
||||
u8 *framebufferCacheEnd = framebufferCache + Draw_GetFramebufferCacheSize();
|
||||
|
||||
u8 *buf = framebufferCache;
|
||||
Draw_CreateBitmapHeader(framebufferCache, width, 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, width, 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:
|
||||
|
||||
Draw_FreeFramebufferCache();
|
||||
return res;
|
||||
}
|
||||
|
||||
void RosalinaMenu_TakeScreenshot(void)
|
||||
{
|
||||
IFile file;
|
||||
Result res;
|
||||
Result res = 0;
|
||||
|
||||
char filename[64];
|
||||
|
||||
@@ -163,15 +349,25 @@ 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;
|
||||
|
||||
archiveId = isSdMode ? ARCHIVE_SDMC : ARCHIVE_NAND_RW;
|
||||
Draw_Lock();
|
||||
Draw_RestoreFramebuffer();
|
||||
Draw_FreeFramebufferCache();
|
||||
|
||||
svcFlushEntireDataCache();
|
||||
|
||||
bool is3d;
|
||||
u32 topWidth, bottomWidth; // actually Y-dim
|
||||
|
||||
Draw_GetCurrentScreenInfo(&bottomWidth, &is3d, false);
|
||||
Draw_GetCurrentScreenInfo(&topWidth, &is3d, true);
|
||||
|
||||
res = FSUSER_OpenArchive(&archive, archiveId, fsMakePath(PATH_EMPTY, ""));
|
||||
if(R_SUCCEEDED(res))
|
||||
{
|
||||
@@ -226,58 +422,30 @@ 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, topWidth, 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, bottomWidth, false, true));
|
||||
TRY(IFile_Close(&file));
|
||||
|
||||
if((GPU_FB_TOP_FMT & 0x20) && (Draw_GetCurrentFramebufferAddress(true, true) != Draw_GetCurrentFramebufferAddress(true, false)))
|
||||
if(is3d && (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, topWidth, true, false));
|
||||
TRY(IFile_Close(&file));
|
||||
}
|
||||
|
||||
end:
|
||||
IFile_Close(&file);
|
||||
|
||||
if (R_FAILED(Draw_AllocateFramebufferCache(FB_BOTTOM_SIZE)))
|
||||
__builtin_trap(); // We're f***ed if this happens
|
||||
|
||||
svcFlushEntireDataCache();
|
||||
Draw_SetupFramebuffer();
|
||||
Draw_ClearFramebuffer();
|
||||
Draw_FlushFramebuffer();
|
||||
Draw_Unlock();
|
||||
|
||||
do
|
||||
@@ -287,12 +455,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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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 },
|
||||
{},
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -32,11 +32,19 @@
|
||||
#include "redshift/redshift.h"
|
||||
#include "redshift/colorramp.h"
|
||||
|
||||
#define TEMP_DEFAULT NEUTRAL_TEMP
|
||||
typedef struct {
|
||||
u8 r;
|
||||
u8 g;
|
||||
u8 b;
|
||||
u8 z;
|
||||
} Pixel;
|
||||
|
||||
int screenFiltersCurrentTemperature = TEMP_DEFAULT;
|
||||
static u16 g_c[0x600];
|
||||
static Pixel g_px[0x400];
|
||||
|
||||
void writeLut(u32* lut)
|
||||
int screenFiltersCurrentTemperature = 6500;
|
||||
|
||||
static void ScreenFiltersMenu_WriteLut(const u32* lut)
|
||||
{
|
||||
GPU_FB_TOP_COL_LUT_INDEX = 0;
|
||||
GPU_FB_BOTTOM_COL_LUT_INDEX = 0;
|
||||
@@ -48,17 +56,7 @@ void writeLut(u32* lut)
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
u8 r;
|
||||
u8 g;
|
||||
u8 b;
|
||||
u8 z;
|
||||
} Pixel;
|
||||
|
||||
static u16 g_c[0x600];
|
||||
static Pixel g_px[0x400];
|
||||
|
||||
void applyColorSettings(color_setting_t* cs)
|
||||
static void ScreenFiltersMenu_ApplyColorSettings(color_setting_t* cs)
|
||||
{
|
||||
u8 i = 0;
|
||||
|
||||
@@ -86,68 +84,56 @@ void applyColorSettings(color_setting_t* cs)
|
||||
g_px[i].b = *(g_c + i + 0x200) >> 8;
|
||||
} while(++i);
|
||||
|
||||
writeLut((u32*)g_px);
|
||||
ScreenFiltersMenu_WriteLut((u32*)g_px);
|
||||
}
|
||||
|
||||
Menu screenFiltersMenu = {
|
||||
"Screen filters menu",
|
||||
.nbItems = 6,
|
||||
{
|
||||
{ "Disable", METHOD, .method = &screenFiltersSetDisabled },
|
||||
{ "Reduce blue light (level 1)", METHOD, .method = &screenFiltersReduceBlueLevel1 },
|
||||
{ "Reduce blue light (level 2)", METHOD, .method = &screenFiltersReduceBlueLevel2 },
|
||||
{ "Reduce blue light (level 3)", METHOD, .method = &screenFiltersReduceBlueLevel3 },
|
||||
{ "Reduce blue light (level 4)", METHOD, .method = &screenFiltersReduceBlueLevel4 },
|
||||
{ "Reduce blue light (level 5)", METHOD, .method = &screenFiltersReduceBlueLevel5 },
|
||||
}
|
||||
};
|
||||
|
||||
void screenFiltersSetDisabled(void)
|
||||
{
|
||||
screenFiltersCurrentTemperature = TEMP_DEFAULT;
|
||||
screenFiltersSetTemperature(screenFiltersCurrentTemperature);
|
||||
}
|
||||
|
||||
void screenFiltersReduceBlueLevel1(void)
|
||||
{
|
||||
screenFiltersCurrentTemperature = 4300;
|
||||
screenFiltersSetTemperature(screenFiltersCurrentTemperature);
|
||||
}
|
||||
|
||||
void screenFiltersReduceBlueLevel2(void)
|
||||
{
|
||||
screenFiltersCurrentTemperature = 3200;
|
||||
screenFiltersSetTemperature(screenFiltersCurrentTemperature);
|
||||
}
|
||||
|
||||
void screenFiltersReduceBlueLevel3(void)
|
||||
{
|
||||
screenFiltersCurrentTemperature = 2100;
|
||||
screenFiltersSetTemperature(screenFiltersCurrentTemperature);
|
||||
}
|
||||
|
||||
void screenFiltersReduceBlueLevel4(void)
|
||||
{
|
||||
screenFiltersCurrentTemperature = 1550;
|
||||
screenFiltersSetTemperature(screenFiltersCurrentTemperature);
|
||||
}
|
||||
|
||||
void screenFiltersReduceBlueLevel5(void)
|
||||
{
|
||||
screenFiltersCurrentTemperature = 1000;
|
||||
screenFiltersSetTemperature(screenFiltersCurrentTemperature);
|
||||
}
|
||||
|
||||
void screenFiltersSetTemperature(int temperature)
|
||||
static void ScreenFiltersMenu_SetCct(int cct)
|
||||
{
|
||||
color_setting_t cs;
|
||||
memset(&cs, 0, sizeof(cs));
|
||||
|
||||
cs.temperature = temperature;
|
||||
cs.temperature = cct;
|
||||
/*cs.gamma[0] = 1.0F;
|
||||
cs.gamma[1] = 1.0F;
|
||||
cs.gamma[2] = 1.0F;
|
||||
cs.brightness = 1.0F;*/
|
||||
|
||||
applyColorSettings(&cs);
|
||||
ScreenFiltersMenu_ApplyColorSettings(&cs);
|
||||
}
|
||||
|
||||
|
||||
Menu screenFiltersMenu = {
|
||||
"Screen filters menu",
|
||||
{
|
||||
{ "[6500K] Default", METHOD, .method = &ScreenFiltersMenu_SetDefault },
|
||||
{ "[10000K] Aquarium", METHOD, .method = &ScreenFiltersMenu_SetAquarium },
|
||||
{ "[7500K] Overcast Sky", METHOD, .method = &ScreenFiltersMenu_SetOvercastSky },
|
||||
{ "[5500K] Daylight", METHOD, .method = &ScreenFiltersMenu_SetDaylight },
|
||||
{ "[4200K] Fluorescent", METHOD, .method = &ScreenFiltersMenu_SetFluorescent },
|
||||
{ "[3400K] Halogen", METHOD, .method = &ScreenFiltersMenu_SetHalogen },
|
||||
{ "[2700K] Incandescent", METHOD, .method = &ScreenFiltersMenu_SetIncandescent },
|
||||
{ "[2300K] Warm Incandescent", METHOD, .method = &ScreenFiltersMenu_SetWarmIncandescent },
|
||||
{ "[1900K] Candle", METHOD, .method = &ScreenFiltersMenu_SetCandle },
|
||||
{ "[2700K] Ember", METHOD, .method = &ScreenFiltersMenu_SetEmber },
|
||||
{},
|
||||
}
|
||||
};
|
||||
|
||||
#define DEF_CCT_SETTER(temp, name)\
|
||||
void ScreenFiltersMenu_Set##name(void)\
|
||||
{\
|
||||
screenFiltersCurrentTemperature = temp;\
|
||||
ScreenFiltersMenu_SetCct(temp);\
|
||||
}
|
||||
|
||||
DEF_CCT_SETTER(6500, Default)
|
||||
|
||||
DEF_CCT_SETTER(10000, Aquarium)
|
||||
DEF_CCT_SETTER(7500, OvercastSky)
|
||||
DEF_CCT_SETTER(5500, Daylight)
|
||||
DEF_CCT_SETTER(4200, Fluorescent)
|
||||
DEF_CCT_SETTER(3400, Halogen)
|
||||
DEF_CCT_SETTER(2700, Incandescent)
|
||||
DEF_CCT_SETTER(2300, WarmIncandescent)
|
||||
DEF_CCT_SETTER(1900, Candle)
|
||||
DEF_CCT_SETTER(1200, Ember)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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, <);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
143
sysmodules/rosalina/source/plgldr.c
Normal file
143
sysmodules/rosalina/source/plgldr.c
Normal 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;
|
||||
}
|
||||
599
sysmodules/rosalina/source/plgloader.c
Normal file
599
sysmodules/rosalina/source/plgloader.c
Normal 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);
|
||||
}
|
||||
33
sysmodules/rosalina/source/pluginLoader.s
Normal file
33
sysmodules/rosalina/source/pluginLoader.s
Normal 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
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
61
sysmodules/rosalina/source/sleep.c
Normal file
61
sysmodules/rosalina/source/sleep.c
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
110
sysmodules/rosalina/source/utils.c
Normal file
110
sysmodules/rosalina/source/utils.c
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user