Compare commits
25 Commits
v10.1.3-be
...
v10.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb07a7334f | ||
|
|
748b771618 | ||
|
|
d6e72080d9 | ||
|
|
70109fed2c | ||
|
|
cf36d21daf | ||
|
|
781cd85b00 | ||
|
|
514537a983 | ||
|
|
184f4587fb | ||
|
|
e096aaabc4 | ||
|
|
ba26ae0f1c | ||
|
|
786adf0268 | ||
|
|
2af05220c2 | ||
|
|
362c4ffff1 | ||
|
|
95fd4e763b | ||
|
|
768e587b76 | ||
|
|
e3bb1c1b63 | ||
|
|
4c01bb453c | ||
|
|
dc67d438dc | ||
|
|
2d58ec4c86 | ||
|
|
555286ea47 | ||
|
|
b17eb66d55 | ||
|
|
9ca52054cf | ||
|
|
991f51831d | ||
|
|
e69f89a0d4 | ||
|
|
9411a8c186 |
17
.github/ISSUE_TEMPLATE/bug-report.md
vendored
17
.github/ISSUE_TEMPLATE/bug-report.md
vendored
@@ -7,13 +7,13 @@ about: Use this to report bugs you encounter with Luma3DS. Make sure you upload
|
|||||||
-- THIS IS NOT A SUPPORT FORUM! For support go here:
|
-- THIS IS NOT A SUPPORT FORUM! For support go here:
|
||||||
-- Nintendo Homebrew: https://discord.gg/MjzatM8
|
-- Nintendo Homebrew: https://discord.gg/MjzatM8
|
||||||
--
|
--
|
||||||
-- Rosalina feature requests go here: https://github.com/AuroraWright/Luma3DS/issues/752
|
-- Rosalina feature requests go here: https://github.com/LumaTeam/Luma3DS/issues/752
|
||||||
--
|
--
|
||||||
-- Also check the Wiki (https://github.com/AuroraWright/Luma3DS/wiki) before making an issue.
|
-- Also check the Wiki (https://github.com/LumaTeam/Luma3DS/wiki) before making an issue.
|
||||||
--
|
--
|
||||||
-- For GBA/DSiWare/DS/AGB_FIRM/TWL_FIRM problems: use https://github.com/MechanicalDragon0687/TWLFix-CFW and update your system.
|
-- 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.
|
-- 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).
|
-- Please make sure to read "Enable game patching" https://github.com/LumaTeam/Luma3DS/wiki/Options-and-usage before posting any issues about the "Enable game patching" option(s).
|
||||||
--
|
--
|
||||||
-- Luma updaters that don't support Boot9Strap/Sighax won't work.
|
-- Luma updaters that don't support Boot9Strap/Sighax won't work.
|
||||||
-- This is due to support for non-B9S/Sighax entrypoints being dropped.
|
-- This is due to support for non-B9S/Sighax entrypoints being dropped.
|
||||||
@@ -21,7 +21,7 @@ about: Use this to report bugs you encounter with Luma3DS. Make sure you upload
|
|||||||
-- Please fill in the placeholders.-->
|
-- Please fill in the placeholders.-->
|
||||||
**System model:**
|
**System model:**
|
||||||
|
|
||||||
[e.g. 2DS, New 3DS, Old 3DS]
|
[New 2DS XL, New 3DS XL, New 3DS, Old 2DS, Old 3DS XL, Old 3DS]
|
||||||
|
|
||||||
**SysNAND version (+emu/redNAND version if applicable):**
|
**SysNAND version (+emu/redNAND version if applicable):**
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ about: Use this to report bugs you encounter with Luma3DS. Make sure you upload
|
|||||||
|
|
||||||
**Luma3DS version:**
|
**Luma3DS version:**
|
||||||
|
|
||||||
[e.g. v10.1.3 stable or if using non-releases specify the commit like this https://github.com/AuroraWright/Luma3DS/commit/0543c208fd154e6326ea5da8cbf66ffcbdef010c]
|
[e.g. v10.2 stable or if using non-releases specify the commit like this https://github.com/LumaTeam/Luma3DS/commit/0543c208fd154e6326ea5da8cbf66ffcbdef010c]
|
||||||
|
|
||||||
**Luma3DS configuration/options:**
|
**Luma3DS configuration/options:**
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ Splash duration: ( )
|
|||||||
PIN lock: ( )
|
PIN lock: ( )
|
||||||
|
|
||||||
New 3DS CPU: ( )
|
New 3DS CPU: ( )
|
||||||
<!--This option is only available for New 3DS/2DS.-->
|
<!--This option is only available on New 3DS (XL)/New 2DS XL.-->
|
||||||
|
|
||||||
--
|
--
|
||||||
|
|
||||||
@@ -70,12 +70,13 @@ Show NAND or user string in System Settings: ( )
|
|||||||
|
|
||||||
Show GBA boot screen in patched AGB_FIRM: ( )
|
Show GBA boot screen in patched AGB_FIRM: ( )
|
||||||
|
|
||||||
Patch Arm9 access: ( )
|
|
||||||
|
|
||||||
Set developer UNITINFO: ( )
|
Set developer UNITINFO: ( )
|
||||||
|
|
||||||
Disable Arm11 exception handlers: ( )
|
Disable Arm11 exception handlers: ( )
|
||||||
|
|
||||||
|
Enable Rosalina on SAFE_FIRM: ( )
|
||||||
|
<!--This option is only available on New 3DS (XL)/New 2DS XL.-->
|
||||||
|
|
||||||
--
|
--
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -13,8 +13,11 @@ exceptions/arm11/build
|
|||||||
*.d
|
*.d
|
||||||
*.elf
|
*.elf
|
||||||
*.cxi
|
*.cxi
|
||||||
|
*.3dsx
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.dmp
|
*.dmp
|
||||||
.project
|
.project
|
||||||
.cproject
|
.cproject
|
||||||
.settings
|
.settings
|
||||||
|
|
||||||
|
Luma3DS*.zip
|
||||||
|
|||||||
9
Makefile
9
Makefile
@@ -15,9 +15,10 @@ release: $(NAME)$(REVISION).zip
|
|||||||
|
|
||||||
clean:
|
clean:
|
||||||
@$(foreach dir, $(SUBFOLDERS), $(MAKE) -C $(dir) clean &&) true
|
@$(foreach dir, $(SUBFOLDERS), $(MAKE) -C $(dir) clean &&) true
|
||||||
@rm -rf *.firm *.zip
|
@rm -rf *.firm *.zip *.3dsx
|
||||||
|
|
||||||
$(NAME)$(REVISION).zip: boot.firm exception_dump_parser
|
# boot.3dsx comes from https://github.com/fincs/new-hbmenu/releases
|
||||||
|
$(NAME)$(REVISION).zip: boot.firm boot.3dsx
|
||||||
@zip -r $@ $^ -x "*.DS_Store*" "*__MACOSX*"
|
@zip -r $@ $^ -x "*.DS_Store*" "*__MACOSX*"
|
||||||
|
|
||||||
boot.firm: $(SUBFOLDERS)
|
boot.firm: $(SUBFOLDERS)
|
||||||
@@ -25,5 +26,9 @@ boot.firm: $(SUBFOLDERS)
|
|||||||
-A 0x18180000 -C XDMA XDMA NDMA XDMA
|
-A 0x18180000 -C XDMA XDMA NDMA XDMA
|
||||||
@echo built... $(notdir $@)
|
@echo built... $(notdir $@)
|
||||||
|
|
||||||
|
boot.3dsx:
|
||||||
|
@curl -sSL "https://github.com/fincs/new-hbmenu/releases/latest/download/boot.3dsx" -o "$@"
|
||||||
|
@echo downloaded... $(notdir $@)
|
||||||
|
|
||||||
$(SUBFOLDERS):
|
$(SUBFOLDERS):
|
||||||
@$(MAKE) -C $@ all
|
@$(MAKE) -C $@ all
|
||||||
|
|||||||
42
README.md
42
README.md
@@ -1,41 +1,7 @@
|
|||||||
# Luma3DS-3GX Plugin Edition
|
# Luma3DS
|
||||||
*Noob-proof (N)3DS "Custom Firmware", with 3GX plugins support*
|
*Noob-proof (N)3DS "Custom Firmware"*
|
||||||
|
|
||||||
### 3GX Plugin Edition
|
### What it is
|
||||||
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.
|
**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.
|
It also allows you to run unauthorized ("homebrew") content by removing signature checks.
|
||||||
@@ -50,7 +16,7 @@ Since v8.0, Luma3DS has its own in-game menu, triggerable by <kbd>L+Down+Select<
|
|||||||
2. [makerom](https://github.com/jakcron/Project_CTR) in PATH
|
2. [makerom](https://github.com/jakcron/Project_CTR) in PATH
|
||||||
3. [firmtool](https://github.com/TuxSH/firmtool)
|
3. [firmtool](https://github.com/TuxSH/firmtool)
|
||||||
4. Up-to-date devkitARM+libctru
|
4. Up-to-date devkitARM+libctru
|
||||||
1. Clone the repository with `git clone https://github.com/mind-overflow/Luma3DS-3GX.git`
|
1. Clone the repository with `git clone https://github.com/LumaTeam/Luma3DS.git`
|
||||||
2. Run `make`.
|
2. Run `make`.
|
||||||
|
|
||||||
The produced `boot.firm` is meant to be copied to the root of your SD card for usage with Boot9Strap.
|
The produced `boot.firm` is meant to be copied to the root of your SD card for usage with Boot9Strap.
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ $(OUTPUT).elf : $(OFILES)
|
|||||||
$(OFILES_SRC) : $(HFILES_BIN)
|
$(OFILES_SRC) : $(HFILES_BIN)
|
||||||
|
|
||||||
memory.o strings.o: CFLAGS += -O3
|
memory.o strings.o: CFLAGS += -O3
|
||||||
config.o: CFLAGS += -DCONFIG_TITLE="\"$(APP_TITLE) $(REVISION)_3gx_beta configuration\""
|
config.o: CFLAGS += -DCONFIG_TITLE="\"$(APP_TITLE) $(REVISION) configuration\""
|
||||||
patches.o: CFLAGS += -DVERSION_MAJOR="$(VERSION_MAJOR)" -DVERSION_MINOR="$(VERSION_MINOR)"\
|
patches.o: CFLAGS += -DVERSION_MAJOR="$(VERSION_MAJOR)" -DVERSION_MINOR="$(VERSION_MINOR)"\
|
||||||
-DVERSION_BUILD="$(VERSION_BUILD)" -DISRELEASE="$(IS_RELEASE)" -DCOMMIT_HASH="0x$(COMMIT)"
|
-DVERSION_BUILD="$(VERSION_BUILD)" -DISRELEASE="$(IS_RELEASE)" -DCOMMIT_HASH="0x$(COMMIT)"
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -34,6 +34,8 @@
|
|||||||
#include "buttons.h"
|
#include "buttons.h"
|
||||||
#include "arm9_exception_handlers.h"
|
#include "arm9_exception_handlers.h"
|
||||||
|
|
||||||
|
// See https://github.com/LumaTeam/luma3ds_exception_dump_parser
|
||||||
|
|
||||||
void installArm9Handlers(void)
|
void installArm9Handlers(void)
|
||||||
{
|
{
|
||||||
vu32 *dstVeneers = (vu32 *)0x08000000;
|
vu32 *dstVeneers = (vu32 *)0x08000000;
|
||||||
|
|||||||
@@ -131,7 +131,6 @@ u32 installK11Extension(u8 *pos, u32 size, bool needToInitSd, u32 baseK11VA, u32
|
|||||||
u32 config, multiConfig, bootConfig;
|
u32 config, multiConfig, bootConfig;
|
||||||
u64 hbldr3dsxTitleId;
|
u64 hbldr3dsxTitleId;
|
||||||
u32 rosalinaMenuCombo;
|
u32 rosalinaMenuCombo;
|
||||||
u32 rosalinaFlags;
|
|
||||||
} info;
|
} info;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -204,7 +203,6 @@ u32 installK11Extension(u8 *pos, u32 size, bool needToInitSd, u32 baseK11VA, u32
|
|||||||
info->bootConfig = configData.bootConfig;
|
info->bootConfig = configData.bootConfig;
|
||||||
info->hbldr3dsxTitleId = configData.hbldr3dsxTitleId;
|
info->hbldr3dsxTitleId = configData.hbldr3dsxTitleId;
|
||||||
info->rosalinaMenuCombo = configData.rosalinaMenuCombo;
|
info->rosalinaMenuCombo = configData.rosalinaMenuCombo;
|
||||||
info->rosalinaFlags = configData.rosalinaFlags;
|
|
||||||
info->versionMajor = VERSION_MAJOR;
|
info->versionMajor = VERSION_MAJOR;
|
||||||
info->versionMinor = VERSION_MINOR;
|
info->versionMinor = VERSION_MINOR;
|
||||||
info->versionBuild = VERSION_BUILD;
|
info->versionBuild = VERSION_BUILD;
|
||||||
@@ -231,10 +229,6 @@ u32 patchKernel11(u8 *pos, u32 size, u32 baseK11VA, u32 *arm11SvcTable, u32 *arm
|
|||||||
u8 *ControlMemoryPos = instrPos + 8 + displ;
|
u8 *ControlMemoryPos = instrPos + 8 + displ;
|
||||||
u32 *off;
|
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.
|
Here we replace currentProcess->processID == 1 by additionnalParameter == 1.
|
||||||
This patch should be generic enough to work even on firmware version 5.0.
|
This patch should be generic enough to work even on firmware version 5.0.
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ typedef struct __attribute__((packed, aligned(4)))
|
|||||||
u32 config, multiConfig, bootConfig;
|
u32 config, multiConfig, bootConfig;
|
||||||
u64 hbldr3dsxTitleId;
|
u64 hbldr3dsxTitleId;
|
||||||
u32 rosalinaMenuCombo;
|
u32 rosalinaMenuCombo;
|
||||||
u32 rosalinaFlags;
|
|
||||||
} CfgData;
|
} CfgData;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
|
|||||||
@@ -1,190 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# Requires Python >= 3.2 or >= 2.7
|
|
||||||
|
|
||||||
# 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 of GPLv3 applies 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.
|
|
||||||
|
|
||||||
__author__ = "TuxSH"
|
|
||||||
__copyright__ = "Copyright (c) 2016-2020 TuxSH"
|
|
||||||
__license__ = "GPLv3"
|
|
||||||
__version__ = "v1.2"
|
|
||||||
|
|
||||||
"""
|
|
||||||
Parses Luma3DS exception dumps
|
|
||||||
"""
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
from struct import unpack_from
|
|
||||||
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
# Source of hexdump: https://gist.github.com/ImmortalPC/c340564823f283fe530b
|
|
||||||
# Credits for hexdump go to the original authors
|
|
||||||
# Slightly edited by TuxSH
|
|
||||||
|
|
||||||
def hexdump(addr, src, length=16, sep='.' ):
|
|
||||||
'''
|
|
||||||
@brief Return {src} in hex dump.
|
|
||||||
@param[in] length {Int} Nb Bytes by row.
|
|
||||||
@param[in] sep {Char} For the text part, {sep} will be used for non ASCII char.
|
|
||||||
@return {Str} The hexdump
|
|
||||||
@note Full support for python2 and python3 !
|
|
||||||
'''
|
|
||||||
result = []
|
|
||||||
|
|
||||||
# Python3 support
|
|
||||||
try:
|
|
||||||
xrange(0,1)
|
|
||||||
except NameError:
|
|
||||||
xrange = range
|
|
||||||
|
|
||||||
for i in xrange(0, len(src), length):
|
|
||||||
subSrc = src[i:i+length]
|
|
||||||
hexa = ''
|
|
||||||
isMiddle = False
|
|
||||||
for h in xrange(0,len(subSrc)):
|
|
||||||
if h == length/2:
|
|
||||||
hexa += ' '
|
|
||||||
h = subSrc[h]
|
|
||||||
if not isinstance(h, int):
|
|
||||||
h = ord(h)
|
|
||||||
h = hex(h).replace('0x','')
|
|
||||||
if len(h) == 1:
|
|
||||||
h = '0'+h
|
|
||||||
hexa += h+' '
|
|
||||||
hexa = hexa.strip(' ')
|
|
||||||
text = ''
|
|
||||||
for c in subSrc:
|
|
||||||
if not isinstance(c, int):
|
|
||||||
c = ord(c)
|
|
||||||
if 0x20 <= c < 0x7F:
|
|
||||||
text += chr(c)
|
|
||||||
else:
|
|
||||||
text += sep
|
|
||||||
result.append(('%08x: %-'+str(length*(2+1)+1)+'s |%s|') % (addr + i, hexa, text))
|
|
||||||
|
|
||||||
return '\n'.join(result)
|
|
||||||
|
|
||||||
|
|
||||||
def makeRegisterLine(A, rA, B, rB):
|
|
||||||
return "{0:<15}{1:<20}{2:<15}{3:<20}".format(A, "{0:08x}".format(rA), B, "{0:08x}".format(rB))
|
|
||||||
|
|
||||||
handledExceptionNames = ("FIQ", "undefined instruction", "prefetch abort", "data abort")
|
|
||||||
registerNames = tuple("r{0}".format(i) for i in range(13)) + ("sp", "lr", "pc", "cpsr") + ("dfsr", "ifsr", "far") + ("fpexc", "fpinst", "fpinst2")
|
|
||||||
svcBreakReasons = ("(svcBreak: panic)", "(svcBreak: assertion failed)", "(svcBreak: user-related)")
|
|
||||||
faultStatusSources = {
|
|
||||||
0b1:'Alignment', 0b100:'Instruction cache maintenance operation fault',
|
|
||||||
0b1100:'External Abort on translation - First-level', 0b1110:'External Abort on translation - Second-level',
|
|
||||||
0b101:'Translation - Section', 0b111:'Translation - Page', 0b11:'Access bit - Section', 0b110:'Access bit - Page',
|
|
||||||
0b1001:'Domain - Section', 0b1011:'Domain - Page', 0b1101:'Permission - Section', 0b1111:'Permission - Page',
|
|
||||||
0b1000:'Precise External Abort', 0b10110:'Imprecise External Abort', 0b10:'Debug event'
|
|
||||||
}
|
|
||||||
|
|
||||||
def main(args=None):
|
|
||||||
parser = argparse.ArgumentParser(description="Parse Luma3DS exception dumps")
|
|
||||||
parser.add_argument("filename")
|
|
||||||
args = parser.parse_args()
|
|
||||||
data = b""
|
|
||||||
with open(args.filename, "rb") as f: data = f.read()
|
|
||||||
if unpack_from("<2I", data) != (0xdeadc0de, 0xdeadcafe):
|
|
||||||
raise SystemExit("Invalid file format")
|
|
||||||
|
|
||||||
version, processor, exceptionType, _, nbRegisters, codeDumpSize, stackDumpSize, additionalDataSize = unpack_from("<8I", data, 8)
|
|
||||||
nbRegisters //= 4
|
|
||||||
|
|
||||||
if version < (1 << 16) | 2:
|
|
||||||
raise SystemExit("Incompatible format version, please use the appropriate parser.")
|
|
||||||
|
|
||||||
registers = unpack_from("<{0}I".format(nbRegisters), data, 40)
|
|
||||||
codeOffset = 40 + 4 * nbRegisters
|
|
||||||
codeDump = data[codeOffset : codeOffset + codeDumpSize]
|
|
||||||
stackOffset = codeOffset + codeDumpSize
|
|
||||||
stackDump = data[stackOffset : stackOffset + stackDumpSize]
|
|
||||||
addtionalDataOffset = stackOffset + stackDumpSize
|
|
||||||
additionalData = data[addtionalDataOffset : addtionalDataOffset + additionalDataSize]
|
|
||||||
|
|
||||||
if processor == 9: print("Processor: Arm9")
|
|
||||||
else: print("Processor: Arm11 (core {0})".format(processor >> 16))
|
|
||||||
|
|
||||||
typeDetailsStr = ""
|
|
||||||
if exceptionType == 2:
|
|
||||||
if (registers[16] & 0x20) == 0 and codeDumpSize >= 4:
|
|
||||||
instr = unpack_from("<I", codeDump[-4:])[0]
|
|
||||||
if instr == 0xe12fff7e:
|
|
||||||
typeDetailsStr = " (kernel panic)"
|
|
||||||
elif instr == 0xef00003c:
|
|
||||||
typeDetailsStr = " " + (svcBreakReasons[registers[0]] if registers[0] < 3 else "(svcBreak)")
|
|
||||||
elif (registers[16] & 0x20) == 1 and codeDumpSize >= 2:
|
|
||||||
instr = unpack_from("<I", codeDump[-4:])[0]
|
|
||||||
if instr == 0xdf3c:
|
|
||||||
typeDetailsStr = " " + (svcBreakReasons[registers[0]] if registers[0] < 3 else "(svcBreak)")
|
|
||||||
|
|
||||||
elif processor != 9 and (registers[20] & 0x80000000) != 0:
|
|
||||||
typeDetailsStr = " (VFP exception)"
|
|
||||||
|
|
||||||
print("Exception type: {0}{1}".format("unknown" if exceptionType >= len(handledExceptionNames) else handledExceptionNames[exceptionType], typeDetailsStr))
|
|
||||||
|
|
||||||
if processor == 11 and exceptionType >= 2:
|
|
||||||
xfsr = registers[18] if exceptionType == 2 else registers[17]
|
|
||||||
print("Fault status: " + faultStatusSources[xfsr & 0xf])
|
|
||||||
|
|
||||||
if additionalDataSize != 0:
|
|
||||||
print("Current process: {0} ({1:016x})".format(additionalData[:8].decode("ascii"), unpack_from("<Q", additionalData, 8)[0]))
|
|
||||||
|
|
||||||
print("\nRegister dump:\n")
|
|
||||||
for i in range(0, nbRegisters - (nbRegisters % 2), 2):
|
|
||||||
if i == 16: print("")
|
|
||||||
print(makeRegisterLine(registerNames[i], registers[i], registerNames[i+1], registers[i+1]))
|
|
||||||
if nbRegisters % 2 == 1: print("{0:<15}{1:<20}".format(registerNames[nbRegisters - 1], "{0:08x}".format(registers[nbRegisters - 1])))
|
|
||||||
|
|
||||||
if processor == 11 and exceptionType == 3:
|
|
||||||
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 + (2 if thumb else 4)
|
|
||||||
|
|
||||||
print("\nCode dump:\n")
|
|
||||||
|
|
||||||
objdump_res = ""
|
|
||||||
try:
|
|
||||||
path = os.path.join(os.environ["DEVKITARM"], "bin", "arm-none-eabi-objdump")
|
|
||||||
|
|
||||||
|
|
||||||
if os.name == "nt" and path[0] == '/':
|
|
||||||
path = ''.join(('c:', path[0], path[5:]))
|
|
||||||
|
|
||||||
objdump_res = subprocess.check_output((
|
|
||||||
path, "-marm", "-b", "binary",
|
|
||||||
"--adjust-vma="+hex(addr - codeOffset), "--start-address="+hex(addr),
|
|
||||||
"--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 = ""
|
|
||||||
|
|
||||||
print(objdump_res if objdump_res != "" else hexdump(addr, codeDump))
|
|
||||||
|
|
||||||
print("\nStack dump:\n")
|
|
||||||
print(hexdump(registers[13], stackDump))
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
from setuptools import setup, find_packages
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name='luma3ds_exception_dump_parser',
|
|
||||||
version='1.2',
|
|
||||||
url='https://github.com/AuroraWright/Luma3DS',
|
|
||||||
author='TuxSH',
|
|
||||||
license='GPLv3',
|
|
||||||
description='Parses Luma3DS exception dumps',
|
|
||||||
install_requires=[''],
|
|
||||||
packages=find_packages(),
|
|
||||||
entry_points={'console_scripts': ['luma3ds_exception_dump_parser=luma3ds_exception_dump_parser.__main__:main']},
|
|
||||||
)
|
|
||||||
@@ -44,12 +44,8 @@ extern KAutoObject * (*KProcessHandleTable__ToKAutoObject)(KProcessHandleTable *
|
|||||||
extern void (*KSynchronizationObject__Signal)(KSynchronizationObject *this, bool isPulse);
|
extern void (*KSynchronizationObject__Signal)(KSynchronizationObject *this, bool isPulse);
|
||||||
extern Result (*WaitSynchronization1)(void *this_unused, KThread *thread, KSynchronizationObject *syncObject, s64 timeout);
|
extern Result (*WaitSynchronization1)(void *this_unused, KThread *thread, KSynchronizationObject *syncObject, s64 timeout);
|
||||||
extern Result (*KProcessHandleTable__CreateHandle)(KProcessHandleTable *this, Handle *out, KAutoObject *obj, u8 token);
|
extern Result (*KProcessHandleTable__CreateHandle)(KProcessHandleTable *this, Handle *out, KAutoObject *obj, u8 token);
|
||||||
extern Result (*KProcessHwInfo__QueryMemory)(KProcessHwInfo *this, MemoryInfo *memoryInfo, PageInfo *pageInfo, void *address);
|
|
||||||
extern Result (*KProcessHwInfo__MapProcessMemory)(KProcessHwInfo *this, KProcessHwInfo *other, void *dst, void *src, u32 nbPages);
|
extern Result (*KProcessHwInfo__MapProcessMemory)(KProcessHwInfo *this, KProcessHwInfo *other, void *dst, void *src, u32 nbPages);
|
||||||
extern Result (*KProcessHwInfo__UnmapProcessMemory)(KProcessHwInfo *this, void *addr, u32 nbPages);
|
extern Result (*KProcessHwInfo__UnmapProcessMemory)(KProcessHwInfo *this, void *addr, u32 nbPages);
|
||||||
extern Result (*KProcessHwInfo__CheckVaState)(KProcessHwInfo *hwInfo, u32 va, u32 size, u32 state, u32 perm);
|
|
||||||
extern Result (*KProcessHwInfo__GetListOfKBlockInfoForVA)(KProcessHwInfo *hwInfo, KLinkedList *list, u32 va, u32 sizeInPage);
|
|
||||||
extern Result (*KProcessHwInfo__MapListOfKBlockInfo)(KProcessHwInfo *this, u32 va, KLinkedList *list, u32 state, u32 perm, u32 sbz);
|
|
||||||
extern Result (*KEvent__Clear)(KEvent *this);
|
extern Result (*KEvent__Clear)(KEvent *this);
|
||||||
extern void (*KObjectMutex__WaitAndAcquire)(KObjectMutex *this);
|
extern void (*KObjectMutex__WaitAndAcquire)(KObjectMutex *this);
|
||||||
extern void (*KObjectMutex__ErrorOccured)(void);
|
extern void (*KObjectMutex__ErrorOccured)(void);
|
||||||
@@ -57,11 +53,8 @@ extern void (*KObjectMutex__ErrorOccured)(void);
|
|||||||
extern void (*KScheduler__AdjustThread)(KScheduler *this, KThread *thread, u32 oldSchedulingMask);
|
extern void (*KScheduler__AdjustThread)(KScheduler *this, KThread *thread, u32 oldSchedulingMask);
|
||||||
extern void (*KScheduler__AttemptSwitchingThreadContext)(KScheduler *this);
|
extern void (*KScheduler__AttemptSwitchingThreadContext)(KScheduler *this);
|
||||||
|
|
||||||
extern void (*KLinkedList_KBlockInfo__Clear)(KLinkedList *list);
|
|
||||||
|
|
||||||
extern Result (*ControlMemory)(u32 *addrOut, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader);
|
extern Result (*ControlMemory)(u32 *addrOut, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader);
|
||||||
extern void (*SleepThread)(s64 ns);
|
extern void (*SleepThread)(s64 ns);
|
||||||
extern Result (*CreateEvent)(Handle *out, ResetType resetType);
|
|
||||||
extern Result (*CloseHandle)(Handle handle);
|
extern Result (*CloseHandle)(Handle handle);
|
||||||
extern Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type);
|
extern Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type);
|
||||||
extern Result (*GetSystemInfo)(s64 *out, s32 type, s32 param);
|
extern Result (*GetSystemInfo)(s64 *out, s32 type, s32 param);
|
||||||
@@ -72,7 +65,6 @@ extern Result (*SendSyncRequest)(Handle handle);
|
|||||||
extern Result (*OpenProcess)(Handle *out, u32 processId);
|
extern Result (*OpenProcess)(Handle *out, u32 processId);
|
||||||
extern Result (*GetProcessId)(u32 *out, Handle process);
|
extern Result (*GetProcessId)(u32 *out, Handle process);
|
||||||
extern Result (*DebugActiveProcess)(Handle *out, u32 processId);
|
extern Result (*DebugActiveProcess)(Handle *out, u32 processId);
|
||||||
extern Result (*SignalEvent)(Handle event);
|
|
||||||
extern Result (*UnmapProcessMemory)(Handle processHandle, void *dst, u32 size);
|
extern Result (*UnmapProcessMemory)(Handle processHandle, void *dst, u32 size);
|
||||||
extern Result (*KernelSetState)(u32 type, u32 varg1, u32 varg2, u32 varg3);
|
extern Result (*KernelSetState)(u32 type, u32 varg1, u32 varg2, u32 varg3);
|
||||||
|
|
||||||
@@ -137,7 +129,6 @@ typedef struct CfwInfo
|
|||||||
u32 config, multiConfig, bootConfig;
|
u32 config, multiConfig, bootConfig;
|
||||||
u64 hbldr3dsxTitleId;
|
u64 hbldr3dsxTitleId;
|
||||||
u32 rosalinaMenuCombo;
|
u32 rosalinaMenuCombo;
|
||||||
u32 rosalinaFlags;
|
|
||||||
} CfwInfo;
|
} CfwInfo;
|
||||||
|
|
||||||
extern CfwInfo cfwInfo;
|
extern CfwInfo cfwInfo;
|
||||||
@@ -146,5 +137,3 @@ extern u32 stolenSystemMemRegionSize;
|
|||||||
|
|
||||||
extern vu32 rosalinaState;
|
extern vu32 rosalinaState;
|
||||||
extern bool hasStartedRosalinaNetworkFuncsOnce;
|
extern bool hasStartedRosalinaNetworkFuncsOnce;
|
||||||
|
|
||||||
KLinkedList* KLinkedList__Initialize(KLinkedList *list);
|
|
||||||
|
|||||||
@@ -105,14 +105,6 @@ typedef struct ALIGN(4) KMutex
|
|||||||
union KProcess *owner;
|
union KProcess *owner;
|
||||||
} KMutex;
|
} KMutex;
|
||||||
|
|
||||||
typedef struct KAddressArbiter
|
|
||||||
{
|
|
||||||
KAutoObject autoObject;
|
|
||||||
struct KThread *first;
|
|
||||||
struct KThread *last;
|
|
||||||
union KProcess *owner;
|
|
||||||
} KAddressArbiter;
|
|
||||||
|
|
||||||
/* 92 */
|
/* 92 */
|
||||||
typedef struct KMutexLinkedList
|
typedef struct KMutexLinkedList
|
||||||
{
|
{
|
||||||
@@ -120,30 +112,6 @@ typedef struct KMutexLinkedList
|
|||||||
KMutex *last;
|
KMutex *last;
|
||||||
} KMutexLinkedList;
|
} KMutexLinkedList;
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
TOKEN_KAUTOOBJECT = 0,
|
|
||||||
TOKEN_KSYNCHRONIZATIONOBJECT = 1,
|
|
||||||
TOKEN_KEVENT = 0x1F,
|
|
||||||
TOKEN_KSEMAPHORE = 0x2F,
|
|
||||||
TOKEN_KTIMER = 0x35,
|
|
||||||
TOKEN_KMUTEX = 0x39,
|
|
||||||
TOKEN_KDEBUG = 0x4D,
|
|
||||||
TOKEN_KSERVERPORT = 0x55,
|
|
||||||
TOKEN_KDMAOBJECT = 0x59,
|
|
||||||
TOKEN_KCLIENTPORT = 0x65,
|
|
||||||
TOKEN_KCODESET = 0x68,
|
|
||||||
TOKEN_KSESSION = 0x70,
|
|
||||||
TOKEN_KTHREAD = 0x8D,
|
|
||||||
TOKEN_KSERVERSESSION = 0x95,
|
|
||||||
TOKEN_KADDRESSARBITER = 0x98,
|
|
||||||
TOKEN_KCLIENTSESSION = 0xA5,
|
|
||||||
TOKEN_KPORT = 0xA8,
|
|
||||||
TOKEN_KSHAREDMEMORY = 0xB0,
|
|
||||||
TOKEN_KPROCESS = 0xC5,
|
|
||||||
TOKEN_KRESOURCELIMIT = 0xC8
|
|
||||||
};
|
|
||||||
|
|
||||||
/* 45 */
|
/* 45 */
|
||||||
typedef struct KClassToken
|
typedef struct KClassToken
|
||||||
{
|
{
|
||||||
@@ -572,20 +540,6 @@ typedef struct KBlockInfo
|
|||||||
u32 pageCount;
|
u32 pageCount;
|
||||||
} KBlockInfo;
|
} KBlockInfo;
|
||||||
|
|
||||||
typedef struct KSharedMemory
|
|
||||||
{
|
|
||||||
KAutoObject autoObject;
|
|
||||||
KLinkedList ownedKBlockInfo;
|
|
||||||
union KProcess *owner;
|
|
||||||
u32 ownerPermissions;
|
|
||||||
u32 otherPermissions;
|
|
||||||
u8 isBlockInfoGenerated;
|
|
||||||
s8 allBlockInfoGenerated;
|
|
||||||
u8 unknown_1;
|
|
||||||
u8 unknown_2;
|
|
||||||
u32 address;
|
|
||||||
} KSharedMemory;
|
|
||||||
|
|
||||||
/* 25 */
|
/* 25 */
|
||||||
typedef struct KMemoryBlock
|
typedef struct KMemoryBlock
|
||||||
{
|
{
|
||||||
@@ -1083,26 +1037,10 @@ typedef struct KProcess##sys\
|
|||||||
KThread *mainThread;\
|
KThread *mainThread;\
|
||||||
u32 interruptEnabledFlags[4];\
|
u32 interruptEnabledFlags[4];\
|
||||||
KProcessHandleTable handleTable;\
|
KProcessHandleTable handleTable;\
|
||||||
/* Custom fields for plugin system
|
u8 gap234[52];\
|
||||||
{ */ \
|
|
||||||
u32 customFlags; /* see KProcess_CustomFlags enum below */ \
|
|
||||||
Handle onMemoryLayoutChangeEvent;\
|
|
||||||
Handle onProcessExitEvent;\
|
|
||||||
Handle resumeProcessExitEvent;\
|
|
||||||
/* } */ \
|
|
||||||
u8 gap234[36];\
|
|
||||||
u64 unused;\
|
u64 unused;\
|
||||||
} KProcess##sys;
|
} KProcess##sys;
|
||||||
|
|
||||||
enum KProcess_CustomFlags
|
|
||||||
{
|
|
||||||
ForceRWXPages = 1 << 0,
|
|
||||||
SignalOnMemLayoutChanges = 1 << 1,
|
|
||||||
SignalOnExit = 1 << 2,
|
|
||||||
|
|
||||||
MemLayoutChanged = 1 << 16
|
|
||||||
};
|
|
||||||
|
|
||||||
INSTANCIATE_KPROCESS(N3DS);
|
INSTANCIATE_KPROCESS(N3DS);
|
||||||
INSTANCIATE_KPROCESS(O3DS8x);
|
INSTANCIATE_KPROCESS(O3DS8x);
|
||||||
INSTANCIATE_KPROCESS(O3DSPre8x);
|
INSTANCIATE_KPROCESS(O3DSPre8x);
|
||||||
|
|||||||
@@ -1,129 +0,0 @@
|
|||||||
#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);
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
#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,5 +30,4 @@
|
|||||||
#include "kernel.h"
|
#include "kernel.h"
|
||||||
#include "svc.h"
|
#include "svc.h"
|
||||||
|
|
||||||
Result MapProcessMemoryEx(Handle dstProcessHandle, u32 vaDst, Handle srcProcessHandle, u32 vaSrc, u32 size);
|
Result MapProcessMemoryEx(Handle processHandle, void *dst, void *src, u32 size);
|
||||||
Result MapProcessMemoryExWrapper(Handle dstProcessHandle, u32 vaDst, Handle srcProcessHandle, u32 vaSrc, u32 size);
|
|
||||||
|
|||||||
@@ -36,11 +36,12 @@ void executeFunctionOnCores(SGI0Handler_t func, u8 targetList, u8 targetListFilt
|
|||||||
|
|
||||||
void KScheduler__TriggerCrossCoreInterrupt(KScheduler *this);
|
void KScheduler__TriggerCrossCoreInterrupt(KScheduler *this);
|
||||||
void KThread__DebugReschedule(KThread *this, bool lock);
|
void KThread__DebugReschedule(KThread *this, bool lock);
|
||||||
bool rosalinaThreadLockPredicate(KThread *thread);
|
|
||||||
|
bool rosalinaThreadLockPredicate(KThread *thread, u32 mask);
|
||||||
void rosalinaRescheduleThread(KThread *thread, bool lock);
|
void rosalinaRescheduleThread(KThread *thread, bool lock);
|
||||||
void rosalinaLockThread(KThread *thread);
|
|
||||||
void rosalinaLockAllThreads(void);
|
void rosalinaLockThreads(u32 mask);
|
||||||
void rosalinaUnlockAllThreads(void);
|
void rosalinaUnlockThreads(u32 mask);
|
||||||
|
|
||||||
// Taken from ctrulib:
|
// Taken from ctrulib:
|
||||||
|
|
||||||
@@ -49,6 +50,11 @@ static inline void __dsb(void)
|
|||||||
__asm__ __volatile__("mcr p15, 0, %[val], c7, c10, 4" :: [val] "r" (0) : "memory");
|
__asm__ __volatile__("mcr p15, 0, %[val], c7, c10, 4" :: [val] "r" (0) : "memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void __dmb(void)
|
||||||
|
{
|
||||||
|
__asm__ __volatile__("mcr p15, 0, %[val], c7, c10, 5" :: [val] "r" (0) : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
static inline void __clrex(void)
|
static inline void __clrex(void)
|
||||||
{
|
{
|
||||||
__asm__ __volatile__("clrex" ::: "memory");
|
__asm__ __volatile__("clrex" ::: "memory");
|
||||||
|
|||||||
@@ -28,43 +28,10 @@
|
|||||||
#include "fatalExceptionHandlers.h"
|
#include "fatalExceptionHandlers.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "kernel.h"
|
#include "kernel.h"
|
||||||
#include "memory.h"
|
|
||||||
#include "mmu.h"
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
|
|
||||||
#define REG_DUMP_SIZE 4 * 23
|
#define REG_DUMP_SIZE 4 * 23
|
||||||
#define CODE_DUMP_SIZE 96
|
#define CODE_DUMP_SIZE 48
|
||||||
|
|
||||||
// Return true if parameters are invalid
|
|
||||||
static bool checkExceptionHandlerValidity(KProcess *process, vu32 *threadLocalStorage)
|
|
||||||
{
|
|
||||||
if (process == NULL)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
u32 stackBottom = threadLocalStorage[0x11];
|
|
||||||
u32 exceptionBuf = threadLocalStorage[0x12];
|
|
||||||
MemoryInfo memInfo;
|
|
||||||
PageInfo pageInfo;
|
|
||||||
KProcessHwInfo *hwInfo = hwInfoOfProcess(process);
|
|
||||||
|
|
||||||
u32 perm = KProcessHwInfo__GetAddressUserPerm(hwInfo, threadLocalStorage[0x10]);
|
|
||||||
|
|
||||||
if (stackBottom != 1)
|
|
||||||
{
|
|
||||||
if (KProcessHwInfo__QueryMemory(hwInfo, &memInfo, &pageInfo, (void *)stackBottom)
|
|
||||||
|| (memInfo.permissions & MEMPERM_RW) != MEMPERM_RW)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exceptionBuf > 1)
|
|
||||||
{
|
|
||||||
if (KProcessHwInfo__QueryMemory(hwInfo, &memInfo, &pageInfo, (void *)exceptionBuf)
|
|
||||||
|| (memInfo.permissions & MEMPERM_RW) != MEMPERM_RW)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (perm & MEMPERM_RX) != MEMPERM_RX;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isExceptionFatal(u32 spsr, u32 *regs, u32 index)
|
bool isExceptionFatal(u32 spsr, u32 *regs, u32 index)
|
||||||
{
|
{
|
||||||
@@ -76,7 +43,7 @@ bool isExceptionFatal(u32 spsr, u32 *regs, u32 index)
|
|||||||
KProcess *currentProcess = currentCoreContext->objectContext.currentProcess;
|
KProcess *currentProcess = currentCoreContext->objectContext.currentProcess;
|
||||||
|
|
||||||
if(thread != NULL && thread->threadLocalStorage != NULL && *((vu32 *)thread->threadLocalStorage + 0x10) != 0)
|
if(thread != NULL && thread->threadLocalStorage != NULL && *((vu32 *)thread->threadLocalStorage + 0x10) != 0)
|
||||||
return checkExceptionHandlerValidity(currentProcess, (vu32 *)thread->threadLocalStorage);
|
return false;
|
||||||
|
|
||||||
if(currentProcess != NULL)
|
if(currentProcess != NULL)
|
||||||
{
|
{
|
||||||
@@ -85,7 +52,7 @@ bool isExceptionFatal(u32 spsr, u32 *regs, u32 index)
|
|||||||
|
|
||||||
thread = KPROCESS_GET_RVALUE(currentProcess, mainThread);
|
thread = KPROCESS_GET_RVALUE(currentProcess, mainThread);
|
||||||
if(thread != NULL && thread->threadLocalStorage != NULL && *((vu32 *)thread->threadLocalStorage + 0x10) != 0)
|
if(thread != NULL && thread->threadLocalStorage != NULL && *((vu32 *)thread->threadLocalStorage + 0x10) != 0)
|
||||||
return checkExceptionHandlerValidity(currentProcess, thread->threadLocalStorage);
|
return false;
|
||||||
|
|
||||||
if(index == 3 && strcmp(codeSetOfProcess(currentProcess)->processName, "menu") == 0 && // workaround a Home Menu bug leading to a dabort
|
if(index == 3 && strcmp(codeSetOfProcess(currentProcess)->processName, "menu") == 0 && // workaround a Home Menu bug leading to a dabort
|
||||||
regs[0] == 0x3FFF && regs[2] == 0 && regs[5] == 2 && regs[7] == 1)
|
regs[0] == 0x3FFF && regs[2] == 0 && regs[5] == 2 && regs[7] == 1)
|
||||||
@@ -103,7 +70,6 @@ bool isDataAbortExceptionRangeControlled(u32 spsr, u32 addr)
|
|||||||
((u32)safecpy <= addr && addr < (u32)safecpy + safecpy_sz)
|
((u32)safecpy <= addr && addr < (u32)safecpy + safecpy_sz)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fatalExceptionHandlersMain(u32 *registerDump, u32 type, u32 cpuId)
|
void fatalExceptionHandlersMain(u32 *registerDump, u32 type, u32 cpuId)
|
||||||
{
|
{
|
||||||
ExceptionDumpHeader dumpHeader;
|
ExceptionDumpHeader dumpHeader;
|
||||||
@@ -130,7 +96,7 @@ void fatalExceptionHandlersMain(u32 *registerDump, u32 type, u32 cpuId)
|
|||||||
registerDump[15] = pc;
|
registerDump[15] = pc;
|
||||||
|
|
||||||
//Dump code
|
//Dump code
|
||||||
u8 *instr = (u8 *)pc + ((cpsr & 0x20) ? 2 : 4) - (dumpHeader.codeDumpSize >> 1) ; //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; //wouldn't work well on 32-bit Thumb instructions, but it isn't much of a problem
|
||||||
dumpHeader.codeDumpSize = ((u32)instr & (((cpsr & 0x20) != 0) ? 1 : 3)) != 0 ? 0 : safecpy(codeDump, instr, dumpHeader.codeDumpSize);
|
dumpHeader.codeDumpSize = ((u32)instr & (((cpsr & 0x20) != 0) ? 1 : 3)) != 0 ? 0 : safecpy(codeDump, instr, dumpHeader.codeDumpSize);
|
||||||
|
|
||||||
//Copy register dump and code dump
|
//Copy register dump and code dump
|
||||||
|
|||||||
@@ -40,12 +40,8 @@ KAutoObject * (*KProcessHandleTable__ToKAutoObject)(KProcessHandleTable *this, H
|
|||||||
void (*KSynchronizationObject__Signal)(KSynchronizationObject *this, bool isPulse);
|
void (*KSynchronizationObject__Signal)(KSynchronizationObject *this, bool isPulse);
|
||||||
Result (*WaitSynchronization1)(void *this_unused, KThread *thread, KSynchronizationObject *syncObject, s64 timeout);
|
Result (*WaitSynchronization1)(void *this_unused, KThread *thread, KSynchronizationObject *syncObject, s64 timeout);
|
||||||
Result (*KProcessHandleTable__CreateHandle)(KProcessHandleTable *this, Handle *out, KAutoObject *obj, u8 token);
|
Result (*KProcessHandleTable__CreateHandle)(KProcessHandleTable *this, Handle *out, KAutoObject *obj, u8 token);
|
||||||
Result (*KProcessHwInfo__QueryMemory)(KProcessHwInfo *this, MemoryInfo *memoryInfo, PageInfo *pageInfo, void *address);
|
|
||||||
Result (*KProcessHwInfo__MapProcessMemory)(KProcessHwInfo *this, KProcessHwInfo *other, void *dst, void *src, u32 nbPages);
|
Result (*KProcessHwInfo__MapProcessMemory)(KProcessHwInfo *this, KProcessHwInfo *other, void *dst, void *src, u32 nbPages);
|
||||||
Result (*KProcessHwInfo__UnmapProcessMemory)(KProcessHwInfo *this, void *addr, u32 nbPages);
|
Result (*KProcessHwInfo__UnmapProcessMemory)(KProcessHwInfo *this, void *addr, u32 nbPages);
|
||||||
Result (*KProcessHwInfo__CheckVaState)(KProcessHwInfo *hwInfo, u32 va, u32 size, u32 state, u32 perm);
|
|
||||||
Result (*KProcessHwInfo__GetListOfKBlockInfoForVA)(KProcessHwInfo *hwInfo, KLinkedList *list, u32 va, u32 sizeInPage);
|
|
||||||
Result (*KProcessHwInfo__MapListOfKBlockInfo)(KProcessHwInfo *this, u32 va, KLinkedList *list, u32 state, u32 perm, u32 sbz);
|
|
||||||
Result (*KEvent__Clear)(KEvent *this);
|
Result (*KEvent__Clear)(KEvent *this);
|
||||||
void (*KObjectMutex__WaitAndAcquire)(KObjectMutex *this);
|
void (*KObjectMutex__WaitAndAcquire)(KObjectMutex *this);
|
||||||
void (*KObjectMutex__ErrorOccured)(void);
|
void (*KObjectMutex__ErrorOccured)(void);
|
||||||
@@ -53,11 +49,8 @@ void (*KObjectMutex__ErrorOccured)(void);
|
|||||||
void (*KScheduler__AdjustThread)(KScheduler *this, KThread *thread, u32 oldSchedulingMask);
|
void (*KScheduler__AdjustThread)(KScheduler *this, KThread *thread, u32 oldSchedulingMask);
|
||||||
void (*KScheduler__AttemptSwitchingThreadContext)(KScheduler *this);
|
void (*KScheduler__AttemptSwitchingThreadContext)(KScheduler *this);
|
||||||
|
|
||||||
void (*KLinkedList_KBlockInfo__Clear)(KLinkedList *list);
|
|
||||||
|
|
||||||
Result (*ControlMemory)(u32 *addrOut, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader);
|
Result (*ControlMemory)(u32 *addrOut, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader);
|
||||||
void (*SleepThread)(s64 ns);
|
void (*SleepThread)(s64 ns);
|
||||||
Result (*CreateEvent)(Handle *out, ResetType resetType);
|
|
||||||
Result (*CloseHandle)(Handle handle);
|
Result (*CloseHandle)(Handle handle);
|
||||||
Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type);
|
Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type);
|
||||||
Result (*GetSystemInfo)(s64 *out, s32 type, s32 param);
|
Result (*GetSystemInfo)(s64 *out, s32 type, s32 param);
|
||||||
@@ -68,7 +61,6 @@ Result (*SendSyncRequest)(Handle handle);
|
|||||||
Result (*OpenProcess)(Handle *out, u32 processId);
|
Result (*OpenProcess)(Handle *out, u32 processId);
|
||||||
Result (*GetProcessId)(u32 *out, Handle process);
|
Result (*GetProcessId)(u32 *out, Handle process);
|
||||||
Result (*DebugActiveProcess)(Handle *out, u32 processId);
|
Result (*DebugActiveProcess)(Handle *out, u32 processId);
|
||||||
Result (*SignalEvent)(Handle event);
|
|
||||||
Result (*UnmapProcessMemory)(Handle processHandle, void *dst, u32 size);
|
Result (*UnmapProcessMemory)(Handle processHandle, void *dst, u32 size);
|
||||||
Result (*KernelSetState)(u32 type, u32 varg1, u32 varg2, u32 varg3);
|
Result (*KernelSetState)(u32 type, u32 varg1, u32 varg2, u32 varg3);
|
||||||
|
|
||||||
@@ -121,10 +113,3 @@ u32 stolenSystemMemRegionSize;
|
|||||||
|
|
||||||
vu32 rosalinaState;
|
vu32 rosalinaState;
|
||||||
bool hasStartedRosalinaNetworkFuncsOnce;
|
bool hasStartedRosalinaNetworkFuncsOnce;
|
||||||
|
|
||||||
KLinkedList* KLinkedList__Initialize(KLinkedList *list)
|
|
||||||
{
|
|
||||||
list->size = 0;
|
|
||||||
list->nodes.first = list->nodes.last = (KLinkedListNode *)&list->nodes;
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -123,61 +123,11 @@ void configHook(vu8 *cfgPage)
|
|||||||
*isDevUnit = true; // enable debug features
|
*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)
|
static void findUsefulSymbols(void)
|
||||||
{
|
{
|
||||||
u32 *off;
|
u32 *off;
|
||||||
|
|
||||||
// Patch ERRF__DumpException
|
for(off = (u32 *)0xFFFF0000; *off != 0xE1A0D002; off++);
|
||||||
for(off = (u32 *)0xFFFF0000; *off != 0xE1A04005; ++off);
|
|
||||||
++off;
|
|
||||||
*(u32 *)PA_FROM_VA_PTR(off) = makeArmBranch(off, off + 51, false);
|
|
||||||
|
|
||||||
for(; *off != 0xE2100102; ++off);
|
|
||||||
KProcessHwInfo__QueryMemory = (Result (*)(KProcessHwInfo *, MemoryInfo *, PageInfo *, void *))decodeArmBranch(off - 1);
|
|
||||||
|
|
||||||
for(; *off != 0xE1A0D002; off++);
|
|
||||||
off += 3;
|
off += 3;
|
||||||
initFPU = (void (*) (void))off;
|
initFPU = (void (*) (void))off;
|
||||||
|
|
||||||
@@ -240,18 +190,6 @@ static void findUsefulSymbols(void)
|
|||||||
for(off = (u32 *)officialSVCs[0x72]; *off != 0xE2041102; off++);
|
for(off = (u32 *)officialSVCs[0x72]; *off != 0xE2041102; off++);
|
||||||
KProcessHwInfo__UnmapProcessMemory = (Result (*)(KProcessHwInfo *, void *, u32))decodeArmBranch(off - 1);
|
KProcessHwInfo__UnmapProcessMemory = (Result (*)(KProcessHwInfo *, void *, u32))decodeArmBranch(off - 1);
|
||||||
|
|
||||||
for (off = (u32 *)officialSVCs[0x70]; *off != 0xE8881200 && *off != 0xE8891900; ++off);
|
|
||||||
for (off = (u32 *)decodeArmBranch(off + 1); *off != 0xE2101102; ++off);
|
|
||||||
KProcessHwInfo__CheckVaState = (Result (*)(KProcessHwInfo *, u32, u32, u32, u32))decodeArmBranch(off - 1);
|
|
||||||
for (; *off != 0xE28D1008; ++off);
|
|
||||||
KProcessHwInfo__GetListOfKBlockInfoForVA = (Result (*)(KProcessHwInfo*, KLinkedList*, u32, u32))decodeArmBranch(off + 1);
|
|
||||||
|
|
||||||
for (; *off != 0xE2000102; ++off);
|
|
||||||
KProcessHwInfo__MapListOfKBlockInfo = (Result (*)(KProcessHwInfo*, u32, KLinkedList*, u32, u32, u32))decodeArmBranch(off - 1);
|
|
||||||
|
|
||||||
for (; *off != 0xE8BD8FF0; ++off);
|
|
||||||
KLinkedList_KBlockInfo__Clear = (void (*)(KLinkedList *))decodeArmBranch(off - 6);
|
|
||||||
|
|
||||||
for(off = (u32 *)officialSVCs[0x7C]; *off != 0x03530000; off++);
|
for(off = (u32 *)officialSVCs[0x7C]; *off != 0x03530000; off++);
|
||||||
KObjectMutex__WaitAndAcquire = (void (*)(KObjectMutex *))decodeArmBranch(++off);
|
KObjectMutex__WaitAndAcquire = (void (*)(KObjectMutex *))decodeArmBranch(++off);
|
||||||
for(; *off != 0xE320F000; off++);
|
for(; *off != 0xE320F000; off++);
|
||||||
@@ -297,7 +235,6 @@ static void findUsefulSymbols(void)
|
|||||||
ControlMemory = (Result (*)(u32 *, u32, u32, u32, MemOp, MemPerm, bool))
|
ControlMemory = (Result (*)(u32 *, u32, u32, u32, MemOp, MemPerm, bool))
|
||||||
decodeArmBranch((u32 *)officialSVCs[0x01] + 5);
|
decodeArmBranch((u32 *)officialSVCs[0x01] + 5);
|
||||||
SleepThread = (void (*)(s64))officialSVCs[0x0A];
|
SleepThread = (void (*)(s64))officialSVCs[0x0A];
|
||||||
CreateEvent = (Result (*)(Handle *, ResetType))decodeArmBranch((u32 *)officialSVCs[0x17] + 3);
|
|
||||||
CloseHandle = (Result (*)(Handle))officialSVCs[0x23];
|
CloseHandle = (Result (*)(Handle))officialSVCs[0x23];
|
||||||
GetHandleInfo = (Result (*)(s64 *, Handle, u32))decodeArmBranch((u32 *)officialSVCs[0x29] + 3);
|
GetHandleInfo = (Result (*)(s64 *, Handle, u32))decodeArmBranch((u32 *)officialSVCs[0x29] + 3);
|
||||||
GetSystemInfo = (Result (*)(s64 *, s32, s32))decodeArmBranch((u32 *)officialSVCs[0x2A] + 3);
|
GetSystemInfo = (Result (*)(s64 *, s32, s32))decodeArmBranch((u32 *)officialSVCs[0x2A] + 3);
|
||||||
@@ -308,7 +245,6 @@ static void findUsefulSymbols(void)
|
|||||||
OpenProcess = (Result (*)(Handle *, u32))decodeArmBranch((u32 *)officialSVCs[0x33] + 3);
|
OpenProcess = (Result (*)(Handle *, u32))decodeArmBranch((u32 *)officialSVCs[0x33] + 3);
|
||||||
GetProcessId = (Result (*)(u32 *, Handle))decodeArmBranch((u32 *)officialSVCs[0x35] + 3);
|
GetProcessId = (Result (*)(u32 *, Handle))decodeArmBranch((u32 *)officialSVCs[0x35] + 3);
|
||||||
DebugActiveProcess = (Result (*)(Handle *, u32))decodeArmBranch((u32 *)officialSVCs[0x60] + 3);
|
DebugActiveProcess = (Result (*)(Handle *, u32))decodeArmBranch((u32 *)officialSVCs[0x60] + 3);
|
||||||
SignalEvent = (Result (*)(Handle event))officialSVCs[0x18];
|
|
||||||
UnmapProcessMemory = (Result (*)(Handle, void *, u32))officialSVCs[0x72];
|
UnmapProcessMemory = (Result (*)(Handle, void *, u32))officialSVCs[0x72];
|
||||||
KernelSetState = (Result (*)(u32, u32, u32, u32))((u32 *)officialSVCs[0x7C] + 1);
|
KernelSetState = (Result (*)(u32, u32, u32, u32))((u32 *)officialSVCs[0x7C] + 1);
|
||||||
|
|
||||||
@@ -340,8 +276,6 @@ static void findUsefulSymbols(void)
|
|||||||
invalidateInstructionCacheRange = (void (*)(void *, u32))off2;
|
invalidateInstructionCacheRange = (void (*)(void *, u32))off2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
installMmuHooks();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void main(FcramLayout *layout, KCoreContext *ctxs)
|
void main(FcramLayout *layout, KCoreContext *ctxs)
|
||||||
|
|||||||
@@ -1,315 +0,0 @@
|
|||||||
#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,7 +44,6 @@
|
|||||||
#include "svc/MapProcessMemoryEx.h"
|
#include "svc/MapProcessMemoryEx.h"
|
||||||
#include "svc/UnmapProcessMemoryEx.h"
|
#include "svc/UnmapProcessMemoryEx.h"
|
||||||
#include "svc/ControlService.h"
|
#include "svc/ControlService.h"
|
||||||
#include "svc/ControlProcess.h"
|
|
||||||
#include "svc/CopyHandle.h"
|
#include "svc/CopyHandle.h"
|
||||||
#include "svc/TranslateHandle.h"
|
#include "svc/TranslateHandle.h"
|
||||||
|
|
||||||
@@ -60,16 +59,13 @@ void signalSvcEntry(u8 *pageEnd)
|
|||||||
|
|
||||||
// Since DBGEVENT_SYSCALL_ENTRY is non blocking, we'll cheat using EXCEVENT_UNDEFINED_SYSCALL (debug->svcId is fortunately an u16!)
|
// 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))
|
if(debugOfProcess(currentProcess) != NULL && shouldSignalSyscallDebugEvent(currentProcess, svcId))
|
||||||
{
|
|
||||||
SignalDebugEvent(DBGEVENT_OUTPUT_STRING, 0xFFFFFFFE, svcId);
|
SignalDebugEvent(DBGEVENT_OUTPUT_STRING, 0xFFFFFFFE, svcId);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void signalSvcReturn(u8 *pageEnd)
|
void signalSvcReturn(u8 *pageEnd)
|
||||||
{
|
{
|
||||||
u32 svcId = (u32) *(u8 *)(pageEnd - 0xB5);
|
u32 svcId = (u32) *(u8 *)(pageEnd - 0xB5);
|
||||||
KProcess *currentProcess = currentCoreContext->objectContext.currentProcess;
|
KProcess *currentProcess = currentCoreContext->objectContext.currentProcess;
|
||||||
u32 flags = KPROCESS_GET_RVALUE(currentProcess, customFlags);
|
|
||||||
|
|
||||||
if(svcId == 0xFE)
|
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
|
svcId = *(u32 *)(pageEnd - 0x110 + 8 * 4); // r12 ; note: max theortical SVC atm: 0x1FFFFFFF. We don't support catching svcIds >= 0x100 atm either
|
||||||
@@ -77,19 +73,12 @@ void signalSvcReturn(u8 *pageEnd)
|
|||||||
// Since DBGEVENT_SYSCALL_RETURN is non blocking, we'll cheat using EXCEVENT_UNDEFINED_SYSCALL (debug->svcId is fortunately an u16!)
|
// Since DBGEVENT_SYSCALL_RETURN is non blocking, we'll cheat using EXCEVENT_UNDEFINED_SYSCALL (debug->svcId is fortunately an u16!)
|
||||||
if(debugOfProcess(currentProcess) != NULL && shouldSignalSyscallDebugEvent(currentProcess, svcId))
|
if(debugOfProcess(currentProcess) != NULL && shouldSignalSyscallDebugEvent(currentProcess, svcId))
|
||||||
SignalDebugEvent(DBGEVENT_OUTPUT_STRING, 0xFFFFFFFF, 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)
|
void postprocessSvc(void)
|
||||||
{
|
{
|
||||||
KThread *currentThread = currentCoreContext->objectContext.currentThread;
|
KThread *currentThread = currentCoreContext->objectContext.currentThread;
|
||||||
if(!currentThread->shallTerminate && rosalinaThreadLockPredicate(currentThread))
|
if(!currentThread->shallTerminate && rosalinaThreadLockPredicate(currentThread, rosalinaState & 5))
|
||||||
rosalinaRescheduleThread(currentThread, true);
|
rosalinaRescheduleThread(currentThread, true);
|
||||||
|
|
||||||
officialPostProcessSvc();
|
officialPostProcessSvc();
|
||||||
@@ -102,27 +91,10 @@ void *svcHook(u8 *pageEnd)
|
|||||||
u32 svcId = *(u8 *)(pageEnd - 0xB5);
|
u32 svcId = *(u8 *)(pageEnd - 0xB5);
|
||||||
if(svcId == 0xFE)
|
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
|
svcId = *(u32 *)(pageEnd - 0x110 + 8 * 4); // r12 ; note: max theortical SVC atm: 0x3FFFFFFF. We don't support catching svcIds >= 0x100 atm either
|
||||||
|
|
||||||
switch(svcId)
|
switch(svcId)
|
||||||
{
|
{
|
||||||
case 0x01:
|
case 0x01:
|
||||||
return ControlMemoryHookWrapper;
|
return ControlMemoryHookWrapper;
|
||||||
case 0x03: /* svcExitProcess */
|
|
||||||
{
|
|
||||||
// Signal that the process is about to be terminated
|
|
||||||
u32 flags = KPROCESS_GET_RVALUE(currentProcess, customFlags);
|
|
||||||
|
|
||||||
if (flags & SignalOnExit)
|
|
||||||
{
|
|
||||||
SignalEvent(KPROCESS_GET_RVALUE(currentProcess, onProcessExitEvent));
|
|
||||||
|
|
||||||
KEvent* event = (KEvent *)KProcessHandleTable__ToKAutoObject(handleTableOfProcess(currentProcess),
|
|
||||||
KPROCESS_GET_RVALUE(currentProcess, resumeProcessExitEvent));
|
|
||||||
WaitSynchronization1(NULL, currentCoreContext->objectContext.currentThread, (KSynchronizationObject *)event, 10000000000ULL);
|
|
||||||
((KAutoObject *)event)->vtable->DecrementReferenceCount((KAutoObject *)event);
|
|
||||||
}
|
|
||||||
return officialSVCs[0x3];
|
|
||||||
}
|
|
||||||
case 0x29:
|
case 0x29:
|
||||||
return GetHandleInfoHookWrapper;
|
return GetHandleInfoHookWrapper;
|
||||||
case 0x2A:
|
case 0x2A:
|
||||||
@@ -164,7 +136,7 @@ void *svcHook(u8 *pageEnd)
|
|||||||
return invalidateEntireInstructionCache;
|
return invalidateEntireInstructionCache;
|
||||||
|
|
||||||
case 0xA0:
|
case 0xA0:
|
||||||
return MapProcessMemoryExWrapper;
|
return MapProcessMemoryEx;
|
||||||
case 0xA1:
|
case 0xA1:
|
||||||
return UnmapProcessMemoryEx;
|
return UnmapProcessMemoryEx;
|
||||||
case 0xA2:
|
case 0xA2:
|
||||||
@@ -176,8 +148,6 @@ void *svcHook(u8 *pageEnd)
|
|||||||
return CopyHandleWrapper;
|
return CopyHandleWrapper;
|
||||||
case 0xB2:
|
case 0xB2:
|
||||||
return TranslateHandleWrapper;
|
return TranslateHandleWrapper;
|
||||||
case 0xB3:
|
|
||||||
return ControlProcess;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return (svcId <= 0x7D) ? officialSVCs[svcId] : NULL;
|
return (svcId <= 0x7D) ? officialSVCs[svcId] : NULL;
|
||||||
|
|||||||
@@ -1,215 +0,0 @@
|
|||||||
#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,14 +29,11 @@
|
|||||||
|
|
||||||
Result GetHandleInfoHook(s64 *out, Handle handle, u32 type)
|
Result GetHandleInfoHook(s64 *out, Handle handle, u32 type)
|
||||||
{
|
{
|
||||||
Result res = 0;
|
if(type == 0x10000) // KDebug and KProcess: get context ID
|
||||||
|
|
||||||
if(type >= 0x10000)
|
|
||||||
{
|
{
|
||||||
KProcessHwInfo *hwInfo;
|
KProcessHwInfo *hwInfo;
|
||||||
KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess);
|
KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess);
|
||||||
KAutoObject *obj;
|
KAutoObject *obj;
|
||||||
|
|
||||||
if(handle == CUR_PROCESS_HANDLE)
|
if(handle == CUR_PROCESS_HANDLE)
|
||||||
{
|
{
|
||||||
obj = (KAutoObject *)(currentCoreContext->objectContext.currentProcess);
|
obj = (KAutoObject *)(currentCoreContext->objectContext.currentProcess);
|
||||||
@@ -48,82 +45,18 @@ Result GetHandleInfoHook(s64 *out, Handle handle, u32 type)
|
|||||||
if(obj == NULL)
|
if(obj == NULL)
|
||||||
return 0xD8E007F7;
|
return 0xD8E007F7;
|
||||||
|
|
||||||
switch (type)
|
if(strcmp(classNameOfAutoObject(obj), "KDebug") == 0)
|
||||||
{
|
hwInfo = hwInfoOfProcess(((KDebug *)obj)->owner);
|
||||||
case 0x10000: ///< Get ctx id (should probably move it to GetProcessInfo)
|
else if(strcmp(classNameOfAutoObject(obj), "KProcess") == 0)
|
||||||
{
|
hwInfo = hwInfoOfProcess((KProcess *)obj);
|
||||||
if(strcmp(classNameOfAutoObject(obj), "KDebug") == 0)
|
else
|
||||||
hwInfo = hwInfoOfProcess(((KDebug *)obj)->owner);
|
hwInfo = NULL;
|
||||||
else if(strcmp(classNameOfAutoObject(obj), "KProcess") == 0)
|
|
||||||
hwInfo = hwInfoOfProcess((KProcess *)obj);
|
|
||||||
else
|
|
||||||
hwInfo = NULL;
|
|
||||||
|
|
||||||
*out = hwInfo != NULL ? KPROCESSHWINFO_GET_RVALUE(hwInfo, contextId) : -1;
|
*out = hwInfo != NULL ? KPROCESSHWINFO_GET_RVALUE(hwInfo, contextId) : -1;
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 0x10001: ///< Get referenced object flags (token)
|
|
||||||
{
|
|
||||||
KClassToken token;
|
|
||||||
|
|
||||||
obj->vtable->GetClassToken(&token, obj);
|
|
||||||
*out = token.flags;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 0x10002: ///< Get object owner
|
|
||||||
{
|
|
||||||
Handle hOut;
|
|
||||||
KClassToken token;
|
|
||||||
KProcess * owner = NULL;
|
|
||||||
|
|
||||||
obj->vtable->GetClassToken(&token, obj);
|
|
||||||
switch(token.flags)
|
|
||||||
{
|
|
||||||
case TOKEN_KEVENT:
|
|
||||||
owner = ((KEvent *)obj)->owner;
|
|
||||||
break;
|
|
||||||
case TOKEN_KSEMAPHORE:
|
|
||||||
owner = ((KSemaphore *)obj)->owner;
|
|
||||||
break;
|
|
||||||
case TOKEN_KTIMER:
|
|
||||||
owner = ((KTimer *)obj)->owner;
|
|
||||||
break;
|
|
||||||
case TOKEN_KMUTEX:
|
|
||||||
owner = ((KMutex *)obj)->owner;
|
|
||||||
break;
|
|
||||||
case TOKEN_KDEBUG:
|
|
||||||
owner = ((KDebug *)obj)->owner;
|
|
||||||
break;
|
|
||||||
case TOKEN_KTHREAD:
|
|
||||||
owner = ((KThread *)obj)->ownerProcess;
|
|
||||||
break;
|
|
||||||
case TOKEN_KADDRESSARBITER:
|
|
||||||
owner = ((KAddressArbiter *)obj)->owner;
|
|
||||||
break;
|
|
||||||
case TOKEN_KSHAREDMEMORY:
|
|
||||||
owner = ((KSharedMemory *)obj)->owner;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (owner == NULL)
|
|
||||||
res = 0xD8E007F7;
|
|
||||||
|
|
||||||
res = createHandleForThisProcess(&hOut, (KAutoObject *)owner);
|
|
||||||
*out = hOut;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
res = 0xF8C007F4;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
obj->vtable->DecrementReferenceCount(obj);
|
obj->vtable->DecrementReferenceCount(obj);
|
||||||
return res;
|
return 0;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
return GetHandleInfo(out, handle, type);
|
return GetHandleInfo(out, handle, type);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,14 +79,6 @@ Result GetProcessInfoHook(s64 *out, Handle processHandle, u32 type)
|
|||||||
*out = ttb & ~((1 << (14 - TTBCR)) - 1);
|
*out = ttb & ~((1 << (14 - TTBCR)) - 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 0x10009:
|
|
||||||
{
|
|
||||||
KProcessHwInfo *hwInfo = hwInfoOfProcess(process);
|
|
||||||
u32 mmusize = KPROCESSHWINFO_GET_RVALUE(hwInfo, mmuTableSize);
|
|
||||||
u32 mmupa = (u32)PA_FROM_VA_PTR(KPROCESSHWINFO_GET_RVALUE(hwInfo, mmuTableVA));
|
|
||||||
*out = (s64)(mmusize | ((s64)mmupa << 32));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
res = 0xD8E007ED; // invalid enum value
|
res = 0xD8E007ED; // invalid enum value
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -64,9 +64,6 @@ Result GetSystemInfoHook(s64 *out, s32 type, s32 param)
|
|||||||
case 0x101:
|
case 0x101:
|
||||||
*out = cfwInfo.rosalinaMenuCombo;
|
*out = cfwInfo.rosalinaMenuCombo;
|
||||||
break;
|
break;
|
||||||
case 0x102:
|
|
||||||
*out = cfwInfo.rosalinaFlags;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x200: // isRelease
|
case 0x200: // isRelease
|
||||||
*out = cfwInfo.flags & 1;
|
*out = cfwInfo.flags & 1;
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
static u32 nbEnabled = 0;
|
static u32 nbEnabled = 0;
|
||||||
static u32 maskedPids[MAX_DEBUG];
|
static u32 maskedPids[MAX_DEBUG];
|
||||||
static u32 masks[MAX_DEBUG][8] = {0};
|
static u32 masks[MAX_DEBUG][8] = {0};
|
||||||
static u32 *homeBtnPressed = NULL;
|
|
||||||
|
|
||||||
bool shouldSignalSyscallDebugEvent(KProcess *process, u8 svcId)
|
bool shouldSignalSyscallDebugEvent(KProcess *process, u8 svcId)
|
||||||
{
|
{
|
||||||
@@ -105,14 +104,22 @@ Result KernelSetStateHook(u32 type, u32 varg1, u32 varg2, u32 varg3)
|
|||||||
__ldrex((s32 *)&rosalinaState);
|
__ldrex((s32 *)&rosalinaState);
|
||||||
}
|
}
|
||||||
while(__strex((s32 *)&rosalinaState, (s32)(rosalinaState ^ varg1)));
|
while(__strex((s32 *)&rosalinaState, (s32)(rosalinaState ^ varg1)));
|
||||||
|
__dmb();
|
||||||
|
|
||||||
if(rosalinaState & 2)
|
if(rosalinaState & 0x10)
|
||||||
hasStartedRosalinaNetworkFuncsOnce = true;
|
hasStartedRosalinaNetworkFuncsOnce = true;
|
||||||
|
|
||||||
if(rosalinaState & 1)
|
// 1: all applet/app/dsp/csnd... threads 2: gsp 4: hid/ir
|
||||||
rosalinaLockAllThreads();
|
for (u32 v = 4; v != 0; v >>= 1)
|
||||||
else if(varg1 & 1)
|
{
|
||||||
rosalinaUnlockAllThreads();
|
if (varg1 & v)
|
||||||
|
{
|
||||||
|
if (rosalinaState & v)
|
||||||
|
rosalinaLockThreads(v);
|
||||||
|
else
|
||||||
|
rosalinaUnlockThreads(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -179,15 +186,6 @@ Result KernelSetStateHook(u32 type, u32 varg1, u32 varg2, u32 varg3)
|
|||||||
KRecursiveLock__Unlock(&dbgParamsLock);
|
KRecursiveLock__Unlock(&dbgParamsLock);
|
||||||
break;
|
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:
|
default:
|
||||||
{
|
{
|
||||||
res = KernelSetState(type, varg1, varg2, varg3);
|
res = KernelSetState(type, varg1, varg2, varg3);
|
||||||
|
|||||||
@@ -26,61 +26,19 @@
|
|||||||
|
|
||||||
#include "svc/MapProcessMemoryEx.h"
|
#include "svc/MapProcessMemoryEx.h"
|
||||||
|
|
||||||
Result MapProcessMemoryEx(Handle dstProcessHandle, u32 vaDst, Handle srcProcessHandle, u32 vaSrc, u32 size)
|
Result MapProcessMemoryEx(Handle processHandle, void *dst, void *src, u32 size)
|
||||||
{
|
{
|
||||||
Result res = 0;
|
|
||||||
u32 sizeInPage = size >> 12;
|
|
||||||
KLinkedList list;
|
|
||||||
KProcess *srcProcess;
|
|
||||||
KProcess *dstProcess;
|
|
||||||
KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess);
|
KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess);
|
||||||
|
KProcessHwInfo *currentHwInfo = hwInfoOfProcess(currentCoreContext->objectContext.currentProcess);
|
||||||
|
KProcess *process = KProcessHandleTable__ToKProcess(handleTable, processHandle);
|
||||||
|
|
||||||
if (dstProcessHandle == CUR_PROCESS_HANDLE)
|
if(process == NULL)
|
||||||
{
|
|
||||||
dstProcess = currentCoreContext->objectContext.currentProcess;
|
|
||||||
KAutoObject__AddReference((KAutoObject *)dstProcess);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
dstProcess = KProcessHandleTable__ToKProcess(handleTable, dstProcessHandle);
|
|
||||||
|
|
||||||
if (dstProcess == NULL)
|
|
||||||
return 0xD8E007F7;
|
return 0xD8E007F7;
|
||||||
|
|
||||||
if (srcProcessHandle == CUR_PROCESS_HANDLE)
|
Result res = KProcessHwInfo__MapProcessMemory(currentHwInfo, hwInfoOfProcess(process), dst, src, size >> 12);
|
||||||
{
|
|
||||||
srcProcess = currentCoreContext->objectContext.currentProcess;
|
|
||||||
KAutoObject__AddReference((KAutoObject *)srcProcess);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
srcProcess = KProcessHandleTable__ToKProcess(handleTable, srcProcessHandle);
|
|
||||||
|
|
||||||
if (srcProcess == NULL)
|
KAutoObject *obj = (KAutoObject *)process;
|
||||||
{
|
obj->vtable->DecrementReferenceCount(obj);
|
||||||
res = 0xD8E007F7;
|
|
||||||
goto exit1;
|
|
||||||
}
|
|
||||||
|
|
||||||
KLinkedList__Initialize(&list);
|
|
||||||
|
|
||||||
res = KProcessHwInfo__GetListOfKBlockInfoForVA(hwInfoOfProcess(srcProcess), &list, vaSrc, sizeInPage);
|
|
||||||
|
|
||||||
if (res >= 0)
|
|
||||||
{
|
|
||||||
// Check if the destination address is free and large enough
|
|
||||||
res = KProcessHwInfo__CheckVaState(hwInfoOfProcess(dstProcess), vaDst, size, 0, 0);
|
|
||||||
if (res == 0)
|
|
||||||
res = KProcessHwInfo__MapListOfKBlockInfo(hwInfoOfProcess(dstProcess), vaDst, &list, 0x5806, MEMPERM_RW | 0x18, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
KLinkedList_KBlockInfo__Clear(&list);
|
|
||||||
|
|
||||||
((KAutoObject *)srcProcess)->vtable->DecrementReferenceCount((KAutoObject *)srcProcess);
|
|
||||||
|
|
||||||
exit1:
|
|
||||||
((KAutoObject *)dstProcess)->vtable->DecrementReferenceCount((KAutoObject *)dstProcess);
|
|
||||||
|
|
||||||
invalidateEntireInstructionCache();
|
|
||||||
flushEntireDataCache();
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,9 +28,16 @@
|
|||||||
#include "svc/SendSyncRequest.h"
|
#include "svc/SendSyncRequest.h"
|
||||||
#include "ipc.h"
|
#include "ipc.h"
|
||||||
|
|
||||||
|
static inline bool isNdmuWorkaround(const SessionInfo *info, u32 pid)
|
||||||
|
{
|
||||||
|
return info != NULL && strcmp(info->name, "ndm:u") == 0 && hasStartedRosalinaNetworkFuncsOnce && pid >= nbSection0Modules;
|
||||||
|
}
|
||||||
|
|
||||||
Result SendSyncRequestHook(Handle handle)
|
Result SendSyncRequestHook(Handle handle)
|
||||||
{
|
{
|
||||||
KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess);
|
KProcess *currentProcess = currentCoreContext->objectContext.currentProcess;
|
||||||
|
KProcessHandleTable *handleTable = handleTableOfProcess(currentProcess);
|
||||||
|
u32 pid = idOfProcess(currentProcess);
|
||||||
KClientSession *clientSession = (KClientSession *)KProcessHandleTable__ToKAutoObject(handleTable, handle);
|
KClientSession *clientSession = (KClientSession *)KProcessHandleTable__ToKAutoObject(handleTable, handle);
|
||||||
|
|
||||||
u32 *cmdbuf = (u32 *)((u8 *)currentCoreContext->objectContext.currentThread->threadLocalStorage + 0x80);
|
u32 *cmdbuf = (u32 *)((u8 *)currentCoreContext->objectContext.currentThread->threadLocalStorage + 0x80);
|
||||||
@@ -47,7 +54,7 @@ Result SendSyncRequestHook(Handle handle)
|
|||||||
case 0x10042:
|
case 0x10042:
|
||||||
{
|
{
|
||||||
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
|
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
|
||||||
if(info != NULL && strcmp(info->name, "ndm:u") == 0 && hasStartedRosalinaNetworkFuncsOnce)
|
if(isNdmuWorkaround(info, pid))
|
||||||
{
|
{
|
||||||
cmdbuf[0] = 0x10040;
|
cmdbuf[0] = 0x10040;
|
||||||
cmdbuf[1] = 0;
|
cmdbuf[1] = 0;
|
||||||
@@ -87,7 +94,7 @@ Result SendSyncRequestHook(Handle handle)
|
|||||||
case 0x20002:
|
case 0x20002:
|
||||||
{
|
{
|
||||||
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
|
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
|
||||||
if(info != NULL && strcmp(info->name, "ndm:u") == 0 && hasStartedRosalinaNetworkFuncsOnce)
|
if(isNdmuWorkaround(info, pid))
|
||||||
{
|
{
|
||||||
cmdbuf[0] = 0x20040;
|
cmdbuf[0] = 0x20040;
|
||||||
cmdbuf[1] = 0;
|
cmdbuf[1] = 0;
|
||||||
@@ -129,7 +136,7 @@ Result SendSyncRequestHook(Handle handle)
|
|||||||
if(!hasStartedRosalinaNetworkFuncsOnce)
|
if(!hasStartedRosalinaNetworkFuncsOnce)
|
||||||
break;
|
break;
|
||||||
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
|
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
|
||||||
skip = info != NULL && strcmp(info->name, "ndm:u") == 0; // SuspendScheduler
|
skip = isNdmuWorkaround(info, pid); // SuspendScheduler
|
||||||
if(skip)
|
if(skip)
|
||||||
cmdbuf[1] = 0;
|
cmdbuf[1] = 0;
|
||||||
break;
|
break;
|
||||||
@@ -140,7 +147,7 @@ Result SendSyncRequestHook(Handle handle)
|
|||||||
if(!hasStartedRosalinaNetworkFuncsOnce)
|
if(!hasStartedRosalinaNetworkFuncsOnce)
|
||||||
break;
|
break;
|
||||||
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
|
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
|
||||||
if(info != NULL && strcmp(info->name, "ndm:u") == 0) // ResumeScheduler
|
if(isNdmuWorkaround(info, pid)) // ResumeScheduler
|
||||||
{
|
{
|
||||||
cmdbuf[0] = 0x90040;
|
cmdbuf[0] = 0x90040;
|
||||||
cmdbuf[1] = 0;
|
cmdbuf[1] = 0;
|
||||||
|
|||||||
@@ -29,45 +29,12 @@
|
|||||||
|
|
||||||
Result UnmapProcessMemoryEx(Handle processHandle, void *dst, u32 size)
|
Result UnmapProcessMemoryEx(Handle processHandle, void *dst, u32 size)
|
||||||
{
|
{
|
||||||
if (GET_VERSION_MINOR(kernelVersion) < 37) // < 6.x
|
if(GET_VERSION_MINOR(kernelVersion) < 37) // < 6.x
|
||||||
return UnmapProcessMemory(processHandle, dst, size); // equivalent when size <= 64MB
|
return UnmapProcessMemory(processHandle, dst, size); // equivalent when size <= 64MB
|
||||||
|
|
||||||
Result res = 0;
|
KProcessHwInfo *currentHwInfo = hwInfoOfProcess(currentCoreContext->objectContext.currentProcess);
|
||||||
u32 sizeInPage = size >> 12;
|
|
||||||
KLinkedList list;
|
|
||||||
KProcess *process;
|
|
||||||
KProcessHwInfo *hwInfo;
|
|
||||||
KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess);
|
|
||||||
|
|
||||||
if (processHandle == CUR_PROCESS_HANDLE)
|
Result res = KProcessHwInfo__UnmapProcessMemory(currentHwInfo, dst, size >> 12);
|
||||||
{
|
|
||||||
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();
|
invalidateEntireInstructionCache();
|
||||||
flushEntireDataCache();
|
flushEntireDataCache();
|
||||||
|
|||||||
@@ -84,12 +84,3 @@ ControlMemoryEx:
|
|||||||
ldr r1, [sp, #12]
|
ldr r1, [sp, #12]
|
||||||
add sp, #20
|
add sp, #20
|
||||||
pop {pc}
|
pop {pc}
|
||||||
|
|
||||||
.global MapProcessMemoryExWrapper
|
|
||||||
.type MapProcessMemoryExWrapper, %function
|
|
||||||
MapProcessMemoryExWrapper:
|
|
||||||
push {lr}
|
|
||||||
str r4, [sp, #-4]!
|
|
||||||
bl MapProcessMemoryEx
|
|
||||||
add sp, #4
|
|
||||||
pop {pc}
|
|
||||||
|
|||||||
@@ -66,17 +66,12 @@ void KThread__DebugReschedule(KThread *this, bool lock)
|
|||||||
KRecursiveLock__Unlock(criticalSectionLock);
|
KRecursiveLock__Unlock(criticalSectionLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rosalinaThreadLockPredicate(KThread *thread)
|
static void rosalinaLockThread(KThread *thread)
|
||||||
{
|
{
|
||||||
KProcess *process = thread->ownerProcess;
|
KThread *syncThread = synchronizationMutex->owner;
|
||||||
if(process == NULL)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
u64 titleId = codeSetOfProcess(process)->titleId;
|
if(syncThread == NULL || syncThread != thread)
|
||||||
u32 highTitleId = (u32)(titleId >> 32), lowTitleId = (u32)(titleId & ~0xF0000001); // clear N3DS and SAFE_FIRM bits
|
rosalinaRescheduleThread(thread, true);
|
||||||
return
|
|
||||||
((rosalinaState & 1) && idOfProcess(process) >= nbSection0Modules &&
|
|
||||||
(highTitleId != 0x00040130 || (highTitleId == 0x00040130 && (lowTitleId == 0x1A02 || lowTitleId == 0x1C02))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void rosalinaRescheduleThread(KThread *thread, bool lock)
|
void rosalinaRescheduleThread(KThread *thread, bool lock)
|
||||||
@@ -89,20 +84,45 @@ void rosalinaRescheduleThread(KThread *thread, bool lock)
|
|||||||
else
|
else
|
||||||
thread->schedulingMask &= ~0x40;
|
thread->schedulingMask &= ~0x40;
|
||||||
|
|
||||||
KScheduler__AdjustThread(currentCoreContext->objectContext.currentScheduler, thread, oldSchedulingMask);
|
if (oldSchedulingMask != thread->schedulingMask)
|
||||||
|
KScheduler__AdjustThread(currentCoreContext->objectContext.currentScheduler, thread, oldSchedulingMask);
|
||||||
|
|
||||||
KRecursiveLock__Unlock(criticalSectionLock);
|
KRecursiveLock__Unlock(criticalSectionLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void rosalinaLockThread(KThread *thread)
|
bool rosalinaThreadLockPredicate(KThread *thread, u32 mask)
|
||||||
{
|
{
|
||||||
KThread *syncThread = synchronizationMutex->owner;
|
KProcess *process = thread->ownerProcess;
|
||||||
|
if(process == NULL || idOfProcess(process) < nbSection0Modules)
|
||||||
|
return false;
|
||||||
|
|
||||||
if(syncThread == NULL || syncThread != thread)
|
u64 titleId = codeSetOfProcess(process)->titleId;
|
||||||
rosalinaRescheduleThread(thread, true);
|
u32 highTitleId = (u32)(titleId >> 32), lowTitleId = (u32)(titleId & ~0xF0000001); // clear N3DS and SAFE_FIRM bits
|
||||||
|
|
||||||
|
if (mask & 1)
|
||||||
|
{
|
||||||
|
if (highTitleId != 0x00040130) // non-sysmodules
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return lowTitleId == 0x1A02 || lowTitleId == 0x2702; // dsp, csnd
|
||||||
|
}
|
||||||
|
if (mask & 2)
|
||||||
|
{
|
||||||
|
if (highTitleId != 0x00040130) // non-sysmodules
|
||||||
|
false;
|
||||||
|
return lowTitleId == 0x1C02; // gsp
|
||||||
|
}
|
||||||
|
if (mask & 4)
|
||||||
|
{
|
||||||
|
if (highTitleId != 0x00040130) // non-sysmodules
|
||||||
|
return false;
|
||||||
|
return lowTitleId == 0x1D02 || lowTitleId == 0x3302;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rosalinaLockAllThreads(void)
|
void rosalinaLockThreads(u32 mask)
|
||||||
{
|
{
|
||||||
bool currentThreadsFound = false;
|
bool currentThreadsFound = false;
|
||||||
|
|
||||||
@@ -110,7 +130,7 @@ void rosalinaLockAllThreads(void)
|
|||||||
for(KLinkedListNode *node = threadList->list.nodes.first; node != (KLinkedListNode *)&threadList->list.nodes; node = node->next)
|
for(KLinkedListNode *node = threadList->list.nodes.first; node != (KLinkedListNode *)&threadList->list.nodes; node = node->next)
|
||||||
{
|
{
|
||||||
KThread *thread = (KThread *)node->key;
|
KThread *thread = (KThread *)node->key;
|
||||||
if(!rosalinaThreadLockPredicate(thread))
|
if(!rosalinaThreadLockPredicate(thread, mask))
|
||||||
continue;
|
continue;
|
||||||
if(thread == coreCtxs[thread->coreId].objectContext.currentThread)
|
if(thread == coreCtxs[thread->coreId].objectContext.currentThread)
|
||||||
currentThreadsFound = true;
|
currentThreadsFound = true;
|
||||||
@@ -123,7 +143,7 @@ void rosalinaLockAllThreads(void)
|
|||||||
for(KLinkedListNode *node = threadList->list.nodes.first; node != (KLinkedListNode *)&threadList->list.nodes; node = node->next)
|
for(KLinkedListNode *node = threadList->list.nodes.first; node != (KLinkedListNode *)&threadList->list.nodes; node = node->next)
|
||||||
{
|
{
|
||||||
KThread *thread = (KThread *)node->key;
|
KThread *thread = (KThread *)node->key;
|
||||||
if(!rosalinaThreadLockPredicate(thread))
|
if(!rosalinaThreadLockPredicate(thread, mask))
|
||||||
continue;
|
continue;
|
||||||
if(!(thread->schedulingMask & 0x40))
|
if(!(thread->schedulingMask & 0x40))
|
||||||
{
|
{
|
||||||
@@ -145,7 +165,7 @@ void rosalinaLockAllThreads(void)
|
|||||||
KRecursiveLock__Unlock(criticalSectionLock);
|
KRecursiveLock__Unlock(criticalSectionLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void rosalinaUnlockAllThreads(void)
|
void rosalinaUnlockThreads(u32 mask)
|
||||||
{
|
{
|
||||||
for(KLinkedListNode *node = threadList->list.nodes.first; node != (KLinkedListNode *)&threadList->list.nodes; node = node->next)
|
for(KLinkedListNode *node = threadList->list.nodes.first; node != (KLinkedListNode *)&threadList->list.nodes; node = node->next)
|
||||||
{
|
{
|
||||||
@@ -154,7 +174,7 @@ void rosalinaUnlockAllThreads(void)
|
|||||||
if((thread->schedulingMask & 0xF) == 2) // thread is terminating
|
if((thread->schedulingMask & 0xF) == 2) // thread is terminating
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if(thread->schedulingMask & 0x40)
|
if((thread->schedulingMask & 0x40) && rosalinaThreadLockPredicate(thread, mask))
|
||||||
rosalinaRescheduleThread(thread, false);
|
rosalinaRescheduleThread(thread, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,36 +96,6 @@ KObjectMutex__Release:
|
|||||||
blx r12
|
blx r12
|
||||||
bx lr
|
bx lr
|
||||||
|
|
||||||
.global KProcessHwInfo__MapL1Section_Hook
|
|
||||||
.type KProcessHwInfo__MapL1Section_Hook, %function
|
|
||||||
KProcessHwInfo__MapL1Section_Hook:
|
|
||||||
@r0 => hwInfo
|
|
||||||
@sp + 0x34 => our ptr to state
|
|
||||||
add r1, sp, #0x34
|
|
||||||
str lr, [sp, #-4]!
|
|
||||||
bl PatchDescriptorAccessControl
|
|
||||||
ldr lr, [sp], #4
|
|
||||||
ldmfd sp, {r0-r4}
|
|
||||||
sub sp, sp, #0x14
|
|
||||||
add r4, sp, #0x48
|
|
||||||
mov r11, #0
|
|
||||||
mov pc, lr
|
|
||||||
|
|
||||||
.global KProcessHwInfo__MapL2Section_Hook
|
|
||||||
.type KProcessHwInfo__MapL2Section_Hook, %function
|
|
||||||
KProcessHwInfo__MapL2Section_Hook:
|
|
||||||
@r0 => hwInfo
|
|
||||||
@sp + 0x34 => our ptr to state
|
|
||||||
add r1, sp, #0x34
|
|
||||||
str lr, [sp, #-4]!
|
|
||||||
bl PatchDescriptorAccessControl
|
|
||||||
ldr lr, [sp], #4
|
|
||||||
ldmfd sp, {r0-r4}
|
|
||||||
sub sp, sp, #0x4C
|
|
||||||
mov r4, r1
|
|
||||||
mov r6, r2
|
|
||||||
mov pc, lr
|
|
||||||
|
|
||||||
.global safecpy
|
.global safecpy
|
||||||
.type safecpy, %function
|
.type safecpy, %function
|
||||||
safecpy:
|
safecpy:
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
#include <3ds/exheader.h>
|
#include <3ds/exheader.h>
|
||||||
|
|
||||||
#define HBLDR_3DSX_TID (*(vu64 *)0x1FF81100)
|
|
||||||
|
|
||||||
Result hbldrInit(void);
|
Result hbldrInit(void);
|
||||||
void hbldrExit(void);
|
void hbldrExit(void);
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "ifile.h"
|
#include "ifile.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "hbldr.h"
|
#include "hbldr.h"
|
||||||
|
#include "luma_shared_config.h"
|
||||||
|
|
||||||
extern u32 config, multiConfig, bootConfig;
|
extern u32 config, multiConfig, bootConfig;
|
||||||
extern bool isN3DS, isSdMode;
|
extern bool isN3DS, isSdMode;
|
||||||
@@ -91,6 +92,11 @@ static int lzss_decompress(u8 *end)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool hbldrIs3dsxTitle(u64 tid)
|
||||||
|
{
|
||||||
|
return Luma_SharedConfig->use_hbldr && tid == Luma_SharedConfig->hbldr_3dsx_tid;
|
||||||
|
}
|
||||||
|
|
||||||
static Result allocateSharedMem(prog_addrs_t *shared, prog_addrs_t *vaddr, int flags)
|
static Result allocateSharedMem(prog_addrs_t *shared, prog_addrs_t *vaddr, int flags)
|
||||||
{
|
{
|
||||||
u32 dummy;
|
u32 dummy;
|
||||||
@@ -148,21 +154,6 @@ static Result loadCode(u64 titleId, prog_addrs_t *shared, u64 programHandle, int
|
|||||||
return 0;
|
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)
|
static Result GetProgramInfo(ExHeader_Info *exheaderInfo, u64 programHandle)
|
||||||
{
|
{
|
||||||
Result res = 0;
|
Result res = 0;
|
||||||
@@ -184,11 +175,8 @@ static Result GetProgramInfo(ExHeader_Info *exheaderInfo, u64 programHandle)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s64 nbSection0Modules;
|
|
||||||
svcGetSystemInfo(&nbSection0Modules, 26, 0);
|
|
||||||
|
|
||||||
// Tweak 3dsx placeholder title exheaderInfo
|
// Tweak 3dsx placeholder title exheaderInfo
|
||||||
if (nbSection0Modules == 6 && exheaderInfo->aci.local_caps.title_id == HBLDR_3DSX_TID)
|
if (hbldrIs3dsxTitle(exheaderInfo->aci.local_caps.title_id))
|
||||||
{
|
{
|
||||||
assertSuccess(hbldrInit());
|
assertSuccess(hbldrInit());
|
||||||
HBLDR_PatchExHeaderInfo(exheaderInfo);
|
HBLDR_PatchExHeaderInfo(exheaderInfo);
|
||||||
@@ -219,7 +207,7 @@ static Result LoadProcess(Handle *process, u64 programHandle)
|
|||||||
u64 titleId;
|
u64 titleId;
|
||||||
|
|
||||||
// make sure the cached info corrosponds to the current programHandle
|
// make sure the cached info corrosponds to the current programHandle
|
||||||
if (g_cached_programHandle != programHandle || g_exheaderInfo.aci.local_caps.title_id == HBLDR_3DSX_TID)
|
if (g_cached_programHandle != programHandle || hbldrIs3dsxTitle(g_exheaderInfo.aci.local_caps.title_id))
|
||||||
{
|
{
|
||||||
res = GetProgramInfo(&g_exheaderInfo, programHandle);
|
res = GetProgramInfo(&g_exheaderInfo, programHandle);
|
||||||
g_cached_programHandle = programHandle;
|
g_cached_programHandle = programHandle;
|
||||||
@@ -245,7 +233,7 @@ static Result LoadProcess(Handle *process, u64 programHandle)
|
|||||||
titleId = g_exheaderInfo.aci.local_caps.title_id;
|
titleId = g_exheaderInfo.aci.local_caps.title_id;
|
||||||
ExHeader_CodeSetInfo *csi = &g_exheaderInfo.sci.codeset_info;
|
ExHeader_CodeSetInfo *csi = &g_exheaderInfo.sci.codeset_info;
|
||||||
|
|
||||||
if (titleId == HBLDR_3DSX_TID)
|
if (hbldrIs3dsxTitle(titleId))
|
||||||
{
|
{
|
||||||
assertSuccess(hbldrInit());
|
assertSuccess(hbldrInit());
|
||||||
assertSuccess(HBLDR_LoadProcess(&codeset, csi->text.address, flags & 0xF00, titleId, csi->name));
|
assertSuccess(HBLDR_LoadProcess(&codeset, csi->text.address, flags & 0xF00, titleId, csi->name));
|
||||||
@@ -269,9 +257,6 @@ static Result LoadProcess(Handle *process, u64 programHandle)
|
|||||||
// load code
|
// load code
|
||||||
if (R_SUCCEEDED(res = loadCode(titleId, &sharedAddr, programHandle, csi->flags.compress_exefs_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);
|
memcpy(&codesetinfo.name, csi->name, 8);
|
||||||
codesetinfo.program_id = titleId;
|
codesetinfo.program_id = titleId;
|
||||||
codesetinfo.text_addr = vaddr.text_addr;
|
codesetinfo.text_addr = vaddr.text_addr;
|
||||||
@@ -288,37 +273,7 @@ static Result LoadProcess(Handle *process, u64 programHandle)
|
|||||||
{
|
{
|
||||||
res = svcCreateProcess(process, codeset, g_exheaderInfo.aci.kernel_caps.descriptors, count);
|
res = svcCreateProcess(process, codeset, g_exheaderInfo.aci.kernel_caps.descriptors, count);
|
||||||
svcCloseHandle(codeset);
|
svcCloseHandle(codeset);
|
||||||
if (res >= 0)
|
res = R_SUCCEEDED(res) ? 0 : res;
|
||||||
{
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,7 +380,7 @@ void loaderHandleCommands(void *ctx)
|
|||||||
break;
|
break;
|
||||||
case 4: // GetProgramInfo
|
case 4: // GetProgramInfo
|
||||||
memcpy(&programHandle, &cmdbuf[1], 8);
|
memcpy(&programHandle, &cmdbuf[1], 8);
|
||||||
if (programHandle != g_cached_programHandle || g_exheaderInfo.aci.local_caps.title_id == HBLDR_3DSX_TID)
|
if (programHandle != g_cached_programHandle || hbldrIs3dsxTitle(g_exheaderInfo.aci.local_caps.title_id))
|
||||||
{
|
{
|
||||||
res = GetProgramInfo(&g_exheaderInfo, programHandle);
|
res = GetProgramInfo(&g_exheaderInfo, programHandle);
|
||||||
g_cached_programHandle = R_SUCCEEDED(res) ? programHandle : 0;
|
g_cached_programHandle = R_SUCCEEDED(res) ? programHandle : 0;
|
||||||
|
|||||||
28
sysmodules/loader/source/luma_shared_config.h
Normal file
28
sysmodules/loader/source/luma_shared_config.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/* This paricular file is licensed under the following terms: */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable
|
||||||
|
* for any damages arising from the use of this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it
|
||||||
|
* and redistribute it freely, subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
|
||||||
|
* If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||||
|
*
|
||||||
|
* Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||||
|
* This notice may not be removed or altered from any source distribution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <3ds/types.h>
|
||||||
|
|
||||||
|
/// Luma shared config type.
|
||||||
|
typedef struct LumaSharedConfig {
|
||||||
|
u64 hbldr_3dsx_tid; ///< Title ID to use for 3DSX loading.
|
||||||
|
bool use_hbldr; ///< Whether or not Loader should use hb:ldr (Rosalina writes 1).
|
||||||
|
} LumaSharedConfig;
|
||||||
|
|
||||||
|
/// Luma shared config.
|
||||||
|
#define Luma_SharedConfig ((volatile LumaSharedConfig *)(OS_SHAREDCFG_VADDR + 0x800))
|
||||||
@@ -3,7 +3,7 @@ Open source replacement of the Arm11 PM system module.
|
|||||||
This is licensed under the MIT license.
|
This is licensed under the MIT license.
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
To run this system module, use a recent release or commit of [Luma3DS](https://github.com/AuroraWright/Luma3DS/), build this project and copy the generated CXI file to `/luma/sysmodules/pm.cxi`.
|
To run this system module, use a recent release or commit of [Luma3DS](https://github.com/LumaTeam/Luma3DS/), build this project and copy the generated CXI file to `/luma/sysmodules/pm.cxi`.
|
||||||
|
|
||||||
# Credits
|
# Credits
|
||||||
@fincs
|
@fincs
|
||||||
|
|||||||
@@ -218,8 +218,9 @@ static Result launchTitleImpl(Handle *debug, ProcessData **outProcessData, const
|
|||||||
programInfoUpdate = (launchFlags & PMLAUNCHFLAG_USE_UPDATE_TITLE) ? programInfoUpdate : programInfo;
|
programInfoUpdate = (launchFlags & PMLAUNCHFLAG_USE_UPDATE_TITLE) ? programInfoUpdate : programInfo;
|
||||||
TRY(registerProgram(&programHandle, programInfo, programInfoUpdate));
|
TRY(registerProgram(&programHandle, programInfo, programInfoUpdate));
|
||||||
|
|
||||||
|
u32 coreVer = OS_KernelConfig->kernel_syscore_ver;
|
||||||
res = LOADER_GetProgramInfo(exheaderInfo, programHandle);
|
res = LOADER_GetProgramInfo(exheaderInfo, programHandle);
|
||||||
res = R_SUCCEEDED(res) && SYSCOREVER == 2 && exheaderInfo->aci.local_caps.core_info.core_version != SYSCOREVER ? (Result)0xC8A05800 : res;
|
res = R_SUCCEEDED(res) && coreVer == 2 && exheaderInfo->aci.local_caps.core_info.core_version != coreVer ? (Result)0xC8A05800 : res;
|
||||||
|
|
||||||
if (R_FAILED(res)) {
|
if (R_FAILED(res)) {
|
||||||
LOADER_UnregisterProgram(programHandle);
|
LOADER_UnregisterProgram(programHandle);
|
||||||
@@ -227,7 +228,7 @@ static Result launchTitleImpl(Handle *debug, ProcessData **outProcessData, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Change APPMEMALLOC if needed
|
// Change APPMEMALLOC if needed
|
||||||
if (IS_N3DS && APPMEMTYPE == 6 && (launchFlags & PMLAUNCHFLAG_NORMAL_APPLICATION) != 0) {
|
if (IS_N3DS && OS_KernelConfig->app_memtype == 6 && (launchFlags & PMLAUNCHFLAG_NORMAL_APPLICATION) != 0) {
|
||||||
u32 limitMb;
|
u32 limitMb;
|
||||||
SystemMode n3dsSystemMode = exheaderInfo->aci.local_caps.core_info.n3ds_system_mode;
|
SystemMode n3dsSystemMode = exheaderInfo->aci.local_caps.core_info.n3ds_system_mode;
|
||||||
bool forceO3dsAppMem = (launchFlags & PMLAUNCHFLAG_FORCE_USE_O3DS_APP_MEM) != 0;
|
bool forceO3dsAppMem = (launchFlags & PMLAUNCHFLAG_FORCE_USE_O3DS_APP_MEM) != 0;
|
||||||
@@ -332,9 +333,11 @@ Result LaunchTitle(u32 *outPid, const FS_ProgramInfo *programInfo, u32 launchFla
|
|||||||
|
|
||||||
u32 tidh = (u32)(programInfo->programId >> 32);
|
u32 tidh = (u32)(programInfo->programId >> 32);
|
||||||
u32 tidl = (u32)programInfo->programId;
|
u32 tidl = (u32)programInfo->programId;
|
||||||
if ((tidh == 0x00040030 || tidh == 0x00040130) && (tidl & 0xFF) != SYSCOREVER) {
|
u32 coreVer = OS_KernelConfig->kernel_syscore_ver;
|
||||||
|
if (coreVer == 2 && (tidh == 0x00040030 || tidh == 0x00040130) && (tidl & 0xFF) != coreVer) {
|
||||||
// Panic if launching SAFE_MODE sysmodules or applets (note: exheader syscorever check above only done for applications in official PM)
|
// Panic if launching SAFE_MODE sysmodules or applets (note: exheader syscorever check above only done for applications in official PM)
|
||||||
// Official PM also hardcodes SYSCOREVER = 2 here.
|
// Official PM also hardcodes SYSCOREVER = 2 here.
|
||||||
|
// NATIVE_FIRM-only.
|
||||||
panic(4);
|
panic(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -520,8 +523,8 @@ Result autolaunchSysmodules(void)
|
|||||||
FS_ProgramInfo programInfo = { .mediaType = MEDIATYPE_NAND };
|
FS_ProgramInfo programInfo = { .mediaType = MEDIATYPE_NAND };
|
||||||
|
|
||||||
// Launch NS
|
// Launch NS
|
||||||
if (NSTID != 0) {
|
if (OS_KernelConfig->ns_tid != 0) {
|
||||||
programInfo.programId = NSTID;
|
programInfo.programId = OS_KernelConfig->ns_tid;
|
||||||
TRY(launchTitleImplWrapper(NULL, NULL, &programInfo, &programInfo, PMLAUNCHFLAG_LOAD_DEPENDENCIES));
|
TRY(launchTitleImplWrapper(NULL, NULL, &programInfo, &programInfo, PMLAUNCHFLAG_LOAD_DEPENDENCIES));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ static void cleanupProcess(ProcessData *process)
|
|||||||
|
|
||||||
ProcessList_Lock(&g_manager.processList);
|
ProcessList_Lock(&g_manager.processList);
|
||||||
if (g_manager.runningApplicationData != NULL && process->handle == g_manager.runningApplicationData->handle) {
|
if (g_manager.runningApplicationData != NULL && process->handle == g_manager.runningApplicationData->handle) {
|
||||||
if (IS_N3DS && APPMEMTYPE == 6) {
|
if (IS_N3DS && OS_KernelConfig->app_memtype == 6) {
|
||||||
assertSuccess(resetAppMemLimit());
|
assertSuccess(resetAppMemLimit());
|
||||||
}
|
}
|
||||||
g_manager.runningApplicationData = NULL;
|
g_manager.runningApplicationData = NULL;
|
||||||
|
|||||||
@@ -252,7 +252,8 @@ static ReslimitValues *fixupReslimitValues(void)
|
|||||||
|
|
||||||
// Note: we lie in the reslimit and make as if neither KExt nor Roslina existed, to avoid breakage
|
// Note: we lie in the reslimit and make as if neither KExt nor Roslina existed, to avoid breakage
|
||||||
|
|
||||||
u32 sysmemalloc = SYSMEMALLOC + (hasKExt() ? getStolenSystemMemRegionSize() : 0);
|
u32 appmemalloc = OS_KernelConfig->memregion_sz[0];
|
||||||
|
u32 sysmemalloc = OS_KernelConfig->memregion_sz[1] + (hasKExt() ? getStolenSystemMemRegionSize() : 0);
|
||||||
ReslimitValues *values = !IS_N3DS ? g_o3dsReslimitValues : g_n3dsReslimitValues;
|
ReslimitValues *values = !IS_N3DS ? g_o3dsReslimitValues : g_n3dsReslimitValues;
|
||||||
|
|
||||||
static const u32 minAppletMemAmount = 0x1200000;
|
static const u32 minAppletMemAmount = 0x1200000;
|
||||||
@@ -261,7 +262,7 @@ static ReslimitValues *fixupReslimitValues(void)
|
|||||||
u32 baseRegionSize = !IS_N3DS ? 0x1400000 : 0x2000000;
|
u32 baseRegionSize = !IS_N3DS ? 0x1400000 : 0x2000000;
|
||||||
|
|
||||||
if (sysmemalloc < minAppletMemAmount) {
|
if (sysmemalloc < minAppletMemAmount) {
|
||||||
values[1][0] = SYSMEMALLOC - minAppletMemAmount / 3;
|
values[1][0] = sysmemalloc - minAppletMemAmount / 3;
|
||||||
values[2][0] = 0;
|
values[2][0] = 0;
|
||||||
values[3][0] = baseRegionSize + otherMinOvercommitAmount;
|
values[3][0] = baseRegionSize + otherMinOvercommitAmount;
|
||||||
} else {
|
} else {
|
||||||
@@ -271,8 +272,8 @@ static ReslimitValues *fixupReslimitValues(void)
|
|||||||
values[3][0] = baseRegionSize + (otherMinOvercommitAmount + excess / 4);
|
values[3][0] = baseRegionSize + (otherMinOvercommitAmount + excess / 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
values[0][0] = APPMEMALLOC;
|
values[0][0] = appmemalloc;
|
||||||
g_defaultAppMemLimit = APPMEMALLOC;
|
g_defaultAppMemLimit = appmemalloc;
|
||||||
|
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,13 +8,7 @@
|
|||||||
#define REG32(reg) (*(vu32 *)reg)
|
#define REG32(reg) (*(vu32 *)reg)
|
||||||
#define REG64(reg) (*(vu64 *)reg)
|
#define REG64(reg) (*(vu64 *)reg)
|
||||||
|
|
||||||
#define NSTID REG64(0x1FF80008)
|
#define IS_N3DS (OS_KernelConfig->app_memtype >= 6) // APPMEMTYPE. Hacky but doesn't use APT
|
||||||
#define SYSCOREVER REG32(0x1FF80010)
|
|
||||||
#define APPMEMTYPE REG32(0x1FF80030)
|
|
||||||
#define APPMEMALLOC REG32(0x1FF80040)
|
|
||||||
#define SYSMEMALLOC REG32(0x1FF80044)
|
|
||||||
|
|
||||||
#define IS_N3DS (*(vu32 *)0x1FF80030 >= 6) // APPMEMTYPE. Hacky but doesn't use APT
|
|
||||||
#define N3DS_TID_MASK 0xF0000000ULL
|
#define N3DS_TID_MASK 0xF0000000ULL
|
||||||
#define N3DS_TID_BIT 0x20000000ULL
|
#define N3DS_TID_BIT 0x20000000ULL
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ Open source replacement of the Arm11 PXI system module.
|
|||||||
This is licensed under the MIT license.
|
This is licensed under the MIT license.
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
To run this system module, use a recent release or commit of [Luma3DS](https://github.com/AuroraWright/Luma3DS/) and copy pxi.cxi to /luma/sysmodules/.
|
To run this system module, use a recent release or commit of [Luma3DS](https://github.com/LumaTeam/Luma3DS/) and copy pxi.cxi to /luma/sysmodules/.
|
||||||
|
|
||||||
# Credits
|
# Credits
|
||||||
This list is not complete at all:
|
This list is not complete at all:
|
||||||
|
|||||||
@@ -28,9 +28,8 @@ INCLUDES := include include/gdb include/menus include/redshift
|
|||||||
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
|
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
|
||||||
DEFINES := -DARM11 -D_3DS
|
DEFINES := -DARM11 -D_3DS
|
||||||
|
|
||||||
|
CFLAGS := -g -std=gnu11 -Wall -Wextra -Werror -Wno-unused-value -Os -mword-relocations \
|
||||||
CFLAGS := -g -std=gnu11 -Wall -Wextra -Wno-unused-value -O2 -mword-relocations \
|
-fomit-frame-pointer -ffunction-sections -fdata-sections -fno-math-errno \
|
||||||
-fomit-frame-pointer -ffunction-sections -fdata-sections \
|
|
||||||
$(ARCH) $(DEFINES)
|
$(ARCH) $(DEFINES)
|
||||||
|
|
||||||
CFLAGS += $(INCLUDE)
|
CFLAGS += $(INCLUDE)
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
#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,20 +71,18 @@ void svcInvalidateEntireInstructionCache(void);
|
|||||||
///@{
|
///@{
|
||||||
/**
|
/**
|
||||||
* @brief Maps a block of process memory.
|
* @brief Maps a block of process memory.
|
||||||
* @param dstProcessHandle Handle of the process to map the memory in (destination)
|
* @param process Handle of the process.
|
||||||
* @param destAddress Address of the mapped block in the current process.
|
* @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 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).
|
* @param size Size of the block of the memory to map (truncated to a multiple of 0x1000 bytes).
|
||||||
*/
|
*/
|
||||||
Result svcMapProcessMemoryEx(Handle dstProcessHandle, u32 destAddress, Handle srcProcessHandle, u32 vaSrc, u32 size);
|
Result svcMapProcessMemoryEx(Handle process, u32 destAddr, u32 srcAddr, u32 size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Unmaps a block of process memory.
|
* @brief Unmaps a block of process memory.
|
||||||
* @param process Handle of the process to unmap the memory from
|
* @param process Handle of the process.
|
||||||
* @param destAddress Address of the block of memory to unmap
|
* @param destAddress Address of the block of memory to unmap, in the current (destination) process.
|
||||||
* @param size Size of the block of memory to unmap (truncated to a multiple of 0x1000 bytes).
|
* @param size Size of the block of memory to unmap (truncated to a multiple of 0x1000 bytes).
|
||||||
* This function should only be used to unmap memory mapped with svcMapProcessMemoryEx
|
|
||||||
*/
|
*/
|
||||||
Result svcUnmapProcessMemoryEx(Handle process, u32 destAddress, u32 size);
|
Result svcUnmapProcessMemoryEx(Handle process, u32 destAddress, u32 size);
|
||||||
|
|
||||||
@@ -136,19 +134,4 @@ Result svcCopyHandle(Handle *out, Handle outProcess, Handle in, Handle inProcess
|
|||||||
* @param in The input handle.
|
* @param in The input handle.
|
||||||
*/
|
*/
|
||||||
Result svcTranslateHandle(u32 *outKAddr, char *outClassName, Handle in);
|
Result svcTranslateHandle(u32 *outKAddr, char *outClassName, Handle in);
|
||||||
|
|
||||||
/// Operations for svcControlProcess
|
|
||||||
typedef enum ProcessOp
|
|
||||||
{
|
|
||||||
PROCESSOP_GET_ALL_HANDLES, ///< List all handles of the process, varg3 can be either 0 to fetch all handles, or token of the type to fetch
|
|
||||||
///< 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);
|
|
||||||
///@}
|
///@}
|
||||||
|
|||||||
@@ -73,7 +73,6 @@
|
|||||||
#define COLOR_WHITE RGB565(0x1F, 0x3F, 0x1F)
|
#define COLOR_WHITE RGB565(0x1F, 0x3F, 0x1F)
|
||||||
#define COLOR_RED RGB565(0x1F, 0x00, 0x00)
|
#define COLOR_RED RGB565(0x1F, 0x00, 0x00)
|
||||||
#define COLOR_GREEN RGB565(0x00, 0x1F, 0x00)
|
#define COLOR_GREEN RGB565(0x00, 0x1F, 0x00)
|
||||||
#define COLOR_LIME RGB565(0x00, 0xFF, 0x00)
|
|
||||||
#define COLOR_BLACK RGB565(0x00, 0x00, 0x00)
|
#define COLOR_BLACK RGB565(0x00, 0x00, 0x00)
|
||||||
|
|
||||||
#define DRAW_MAX_FORMATTED_STRING_SIZE 512
|
#define DRAW_MAX_FORMATTED_STRING_SIZE 512
|
||||||
@@ -91,7 +90,8 @@ u32 Draw_DrawFormattedString(u32 posX, u32 posY, u32 color, const char *fmt, ...
|
|||||||
|
|
||||||
void Draw_FillFramebuffer(u32 value);
|
void Draw_FillFramebuffer(u32 value);
|
||||||
void Draw_ClearFramebuffer(void);
|
void Draw_ClearFramebuffer(void);
|
||||||
u32 Draw_AllocateFramebufferCache(void);
|
Result Draw_AllocateFramebufferCache(u32 size);
|
||||||
|
Result Draw_AllocateFramebufferCacheForScreenshot(u32 size);
|
||||||
void Draw_FreeFramebufferCache(void);
|
void Draw_FreeFramebufferCache(void);
|
||||||
void *Draw_GetFramebufferCache(void);
|
void *Draw_GetFramebufferCache(void);
|
||||||
u32 Draw_GetFramebufferCacheSize(void);
|
u32 Draw_GetFramebufferCacheSize(void);
|
||||||
@@ -99,6 +99,8 @@ u32 Draw_SetupFramebuffer(void);
|
|||||||
void Draw_RestoreFramebuffer(void);
|
void Draw_RestoreFramebuffer(void);
|
||||||
void Draw_FlushFramebuffer(void);
|
void Draw_FlushFramebuffer(void);
|
||||||
u32 Draw_GetCurrentFramebufferAddress(bool top, bool left);
|
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_CreateBitmapHeader(u8 *dst, u32 width, u32 heigth);
|
||||||
void Draw_ConvertFrameBufferLines(u8 *buf, u32 startingLine, u32 numLines, bool top, bool left);
|
void Draw_ConvertFrameBufferLines(u8 *buf, u32 width, u32 startingLine, u32 numLines, bool top, bool left);
|
||||||
|
|||||||
@@ -12,14 +12,11 @@
|
|||||||
#define GDB_REMOTE_COMMAND_HANDLER(name) GDB_HANDLER(RemoteCommand##name)
|
#define GDB_REMOTE_COMMAND_HANDLER(name) GDB_HANDLER(RemoteCommand##name)
|
||||||
#define GDB_DECLARE_REMOTE_COMMAND_HANDLER(name) GDB_DECLARE_HANDLER(RemoteCommand##name)
|
#define GDB_DECLARE_REMOTE_COMMAND_HANDLER(name) GDB_DECLARE_HANDLER(RemoteCommand##name)
|
||||||
|
|
||||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ConvertVAToPA);
|
|
||||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(SyncRequestInfo);
|
GDB_DECLARE_REMOTE_COMMAND_HANDLER(SyncRequestInfo);
|
||||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle);
|
GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle);
|
||||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ListAllHandles);
|
|
||||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig);
|
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig);
|
||||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMemRegions);
|
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMemRegions);
|
||||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(FlushCaches);
|
GDB_DECLARE_REMOTE_COMMAND_HANDLER(FlushCaches);
|
||||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess);
|
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess);
|
||||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(CatchSvc);
|
|
||||||
|
|
||||||
GDB_DECLARE_QUERY_HANDLER(Rcmd);
|
GDB_DECLARE_QUERY_HANDLER(Rcmd);
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
#include "MyThread.h"
|
#include "MyThread.h"
|
||||||
|
|
||||||
#define HBLDR_DEFAULT_3DSX_TID 0x000400000D921E00ULL
|
#define HBLDR_DEFAULT_3DSX_TID 0x000400000D921E00ULL
|
||||||
#define HBLDR_3DSX_TID (*(vu64 *)0x1FF81100)
|
|
||||||
|
|
||||||
void HBLDR_RestartHbApplication(void *p);
|
void HBLDR_RestartHbApplication(void *p);
|
||||||
void HBLDR_HandleCommands(void *ctx);
|
void HBLDR_HandleCommands(void *ctx);
|
||||||
|
|||||||
30
sysmodules/rosalina/include/luma_shared_config.h
Normal file
30
sysmodules/rosalina/include/luma_shared_config.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/* This paricular file is licensed under the following terms: */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable
|
||||||
|
* for any damages arising from the use of this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it
|
||||||
|
* and redistribute it freely, subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
|
||||||
|
* If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||||
|
*
|
||||||
|
* Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||||
|
* This notice may not be removed or altered from any source distribution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <3ds/types.h>
|
||||||
|
|
||||||
|
#include <3ds/types.h>
|
||||||
|
|
||||||
|
/// Luma shared config type.
|
||||||
|
typedef struct LumaSharedConfig {
|
||||||
|
u64 hbldr_3dsx_tid; ///< Title ID to use for 3DSX loading.
|
||||||
|
bool use_hbldr; ///< Whether or not Loader should use hb:ldr (Rosalina writes 1).
|
||||||
|
} LumaSharedConfig;
|
||||||
|
|
||||||
|
/// Luma shared config.
|
||||||
|
#define Luma_SharedConfig ((volatile LumaSharedConfig *)(OS_SHAREDCFG_VADDR + 0x800))
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* This file is part of Luma3DS
|
* This file is part of Luma3DS
|
||||||
* Copyright (C) 2016-2018 Aurora Wright, TuxSH
|
* Copyright (C) 2016-2020 Aurora Wright, TuxSH
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -26,6 +26,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
void Sleep__Init(void);
|
#include <3ds/types.h>
|
||||||
void Sleep__HandleNotification(u32 notifId);
|
|
||||||
bool Sleep__Status(void);
|
u32 getMinLuminancePreset(void);
|
||||||
|
u32 getMaxLuminancePreset(void);
|
||||||
|
u32 getCurrentLuminance(bool top);
|
||||||
@@ -30,8 +30,6 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize);
|
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);
|
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 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);
|
unsigned long long int xstrtoull(const char *nptr, char **endptr, int base, bool allowPrefix, bool *ok);
|
||||||
|
|||||||
@@ -81,10 +81,7 @@ bool menuCheckN3ds(void);
|
|||||||
u32 menuCountItems(const Menu *menu);
|
u32 menuCountItems(const Menu *menu);
|
||||||
|
|
||||||
MyThread *menuCreateThread(void);
|
MyThread *menuCreateThread(void);
|
||||||
void menuEnter(void);
|
void menuEnter(void);
|
||||||
void menuLeave(void);
|
void menuLeave(void);
|
||||||
void menuThreadMain(void);
|
void menuThreadMain(void);
|
||||||
void menuShow(Menu *root);
|
void menuShow(Menu *root);
|
||||||
void DispMessage(const char *title, const char *message);
|
|
||||||
u32 DispErrMessage(const char *title, const char *message, const Result error);
|
|
||||||
void DisplayPluginMenu(u32 *cmdbuf);
|
|
||||||
|
|||||||
@@ -32,10 +32,16 @@ extern Menu screenFiltersMenu;
|
|||||||
|
|
||||||
extern int screenFiltersCurrentTemperature;
|
extern int screenFiltersCurrentTemperature;
|
||||||
|
|
||||||
void screenFiltersSetDisabled(void);
|
void ScreenFiltersMenu_RestoreCct(void);
|
||||||
void screenFiltersReduceBlueLevel1(void);
|
|
||||||
void screenFiltersReduceBlueLevel2(void);
|
void ScreenFiltersMenu_SetDefault(void); // 6500K (default)
|
||||||
void screenFiltersReduceBlueLevel3(void);
|
|
||||||
void screenFiltersReduceBlueLevel4(void);
|
void ScreenFiltersMenu_SetAquarium(void); // 10000K
|
||||||
void screenFiltersReduceBlueLevel5(void);
|
void ScreenFiltersMenu_SetOvercastSky(void); // 7500K
|
||||||
void screenFiltersSetTemperature(int temperature);
|
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
|
||||||
|
|||||||
@@ -21,6 +21,10 @@
|
|||||||
extern bool miniSocEnabled;
|
extern bool miniSocEnabled;
|
||||||
|
|
||||||
Result miniSocInit(void);
|
Result miniSocInit(void);
|
||||||
|
|
||||||
|
void miniSocLockState(void);
|
||||||
|
void miniSocUnlockState(bool force);
|
||||||
|
|
||||||
Result miniSocExitDirect(void);
|
Result miniSocExitDirect(void);
|
||||||
Result miniSocExit(void);
|
Result miniSocExit(void);
|
||||||
|
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
#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);
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#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);
|
|
||||||
@@ -77,4 +77,3 @@ void server_run(struct sock_server *serv);
|
|||||||
void server_kill_connections(struct sock_server *serv);
|
void server_kill_connections(struct sock_server *serv);
|
||||||
void server_set_should_close_all(struct sock_server *serv);
|
void server_set_should_close_all(struct sock_server *serv);
|
||||||
void server_finalize(struct sock_server *serv);
|
void server_finalize(struct sock_server *serv);
|
||||||
bool Wifi__IsConnected(void);
|
|
||||||
|
|||||||
@@ -29,8 +29,8 @@
|
|||||||
#include <3ds/svc.h>
|
#include <3ds/svc.h>
|
||||||
#include <3ds/srv.h>
|
#include <3ds/srv.h>
|
||||||
#include <3ds/result.h>
|
#include <3ds/result.h>
|
||||||
#include <3ds/ipc.h>
|
|
||||||
#include "csvc.h"
|
#include "csvc.h"
|
||||||
|
#include "luma_shared_config.h"
|
||||||
|
|
||||||
// For accessing physmem uncached (and directly)
|
// For accessing physmem uncached (and directly)
|
||||||
#define PA_PTR(addr) (void *)((u32)(addr) | 1 << 31)
|
#define PA_PTR(addr) (void *)((u32)(addr) | 1 << 31)
|
||||||
@@ -58,22 +58,6 @@ static inline void *decodeArmBranch(const void *src)
|
|||||||
return (void *)((const u8 *)src + 8 + off);
|
return (void *)((const u8 *)src + 8 + off);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void assertSuccess(Result res)
|
|
||||||
{
|
|
||||||
if(R_FAILED(res))
|
|
||||||
svcBreak(USERBREAK_PANIC);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void error(u32* cmdbuf, Result rc)
|
|
||||||
{
|
|
||||||
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);
|
|
||||||
cmdbuf[1] = rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern bool isN3DS;
|
|
||||||
|
|
||||||
Result OpenProcessByName(const char *name, Handle *h);
|
|
||||||
Result SaveSettings(void);
|
|
||||||
static inline bool isServiceUsable(const char *name)
|
static inline bool isServiceUsable(const char *name)
|
||||||
{
|
{
|
||||||
bool r;
|
bool r;
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
#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,10 +59,7 @@ SVC_BEGIN svcInvalidateEntireInstructionCache
|
|||||||
SVC_END
|
SVC_END
|
||||||
|
|
||||||
SVC_BEGIN svcMapProcessMemoryEx
|
SVC_BEGIN svcMapProcessMemoryEx
|
||||||
str r4, [sp, #-4]!
|
|
||||||
ldr r4, [sp, #4]
|
|
||||||
svc 0xA0
|
svc 0xA0
|
||||||
ldr r4, [sp], #4
|
|
||||||
bx lr
|
bx lr
|
||||||
SVC_END
|
SVC_END
|
||||||
|
|
||||||
@@ -102,8 +99,3 @@ SVC_BEGIN svcTranslateHandle
|
|||||||
str r1, [r2]
|
str r1, [r2]
|
||||||
bx lr
|
bx lr
|
||||||
SVC_END
|
SVC_END
|
||||||
|
|
||||||
SVC_BEGIN svcControlProcess
|
|
||||||
svc 0xB3
|
|
||||||
bx lr
|
|
||||||
SVC_END
|
|
||||||
|
|||||||
@@ -129,18 +129,19 @@ void Draw_ClearFramebuffer(void)
|
|||||||
Draw_FillFramebuffer(0);
|
Draw_FillFramebuffer(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 Draw_AllocateFramebufferCache(void)
|
Result Draw_AllocateFramebufferCache(u32 size)
|
||||||
{
|
{
|
||||||
// Try to see how much we can allocate...
|
|
||||||
// Can't use fbs in FCRAM when Home Menu is active (AXI config related maybe?)
|
// Can't use fbs in FCRAM when Home Menu is active (AXI config related maybe?)
|
||||||
u32 addr = 0x0D000000;
|
u32 addr = 0x0D000000;
|
||||||
u32 tmp;
|
u32 tmp;
|
||||||
u32 minSize = (FB_BOTTOM_SIZE + 0xFFF) & ~0xFFF;
|
|
||||||
u32 maxSize = (FB_SCREENSHOT_SIZE + 0xFFF) & ~0xFFF;
|
|
||||||
u32 remaining = (u32)osGetMemRegionFree(MEMREGION_SYSTEM);
|
|
||||||
u32 size = remaining < maxSize ? remaining : maxSize;
|
|
||||||
|
|
||||||
if (size < minSize || R_FAILED(svcControlMemoryEx(&tmp, addr, 0, size, MEMOP_ALLOC, MEMREGION_SYSTEM | MEMPERM_READ | MEMPERM_WRITE, true)))
|
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;
|
framebufferCache = NULL;
|
||||||
framebufferCacheSize = 0;
|
framebufferCacheSize = 0;
|
||||||
@@ -151,13 +152,21 @@ u32 Draw_AllocateFramebufferCache(void)
|
|||||||
framebufferCacheSize = size;
|
framebufferCacheSize = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return framebufferCacheSize;
|
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)
|
void Draw_FreeFramebufferCache(void)
|
||||||
{
|
{
|
||||||
u32 tmp;
|
u32 tmp;
|
||||||
svcControlMemory(&tmp, (u32)framebufferCache, 0, framebufferCacheSize, MEMOP_FREE, 0);
|
if (framebufferCache != NULL)
|
||||||
|
svcControlMemory(&tmp, (u32)framebufferCache, 0, framebufferCacheSize, MEMOP_FREE, 0);
|
||||||
framebufferCacheSize = 0;
|
framebufferCacheSize = 0;
|
||||||
framebufferCache = NULL;
|
framebufferCache = NULL;
|
||||||
}
|
}
|
||||||
@@ -180,13 +189,19 @@ u32 Draw_SetupFramebuffer(void)
|
|||||||
memcpy(framebufferCache, FB_BOTTOM_VRAM_ADDR, FB_BOTTOM_SIZE);
|
memcpy(framebufferCache, FB_BOTTOM_VRAM_ADDR, FB_BOTTOM_SIZE);
|
||||||
Draw_ClearFramebuffer();
|
Draw_ClearFramebuffer();
|
||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
|
|
||||||
|
u32 format = GPU_FB_BOTTOM_FMT;
|
||||||
|
|
||||||
gpuSavedFramebufferAddr1 = GPU_FB_BOTTOM_ADDR_1;
|
gpuSavedFramebufferAddr1 = GPU_FB_BOTTOM_ADDR_1;
|
||||||
gpuSavedFramebufferAddr2 = GPU_FB_BOTTOM_ADDR_2;
|
gpuSavedFramebufferAddr2 = GPU_FB_BOTTOM_ADDR_2;
|
||||||
gpuSavedFramebufferFormat = GPU_FB_BOTTOM_FMT;
|
gpuSavedFramebufferFormat = format;
|
||||||
gpuSavedFramebufferStride = GPU_FB_BOTTOM_STRIDE;
|
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_ADDR_1 = GPU_FB_BOTTOM_ADDR_2 = FB_BOTTOM_VRAM_PA;
|
||||||
GPU_FB_BOTTOM_FMT = (GPU_FB_BOTTOM_FMT & ~7) | GSP_RGB565_OES;
|
GPU_FB_BOTTOM_FMT = format;
|
||||||
GPU_FB_BOTTOM_STRIDE = 240 * 2;
|
GPU_FB_BOTTOM_STRIDE = 240 * 2;
|
||||||
|
|
||||||
return framebufferCacheSize;
|
return framebufferCacheSize;
|
||||||
@@ -205,7 +220,7 @@ void Draw_RestoreFramebuffer(void)
|
|||||||
|
|
||||||
void Draw_FlushFramebuffer(void)
|
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)
|
u32 Draw_GetCurrentFramebufferAddress(bool top, bool left)
|
||||||
@@ -226,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)
|
static inline void Draw_WriteUnaligned(u8 *dst, u32 tmp, u32 size)
|
||||||
{
|
{
|
||||||
memcpy(dst, &tmp, size);
|
memcpy(dst, &tmp, size);
|
||||||
@@ -247,7 +277,7 @@ void Draw_CreateBitmapHeader(u8 *dst, u32 width, u32 heigth)
|
|||||||
Draw_WriteUnaligned(dst + 0x22, 3 * width * heigth, 4);
|
Draw_WriteUnaligned(dst + 0x22, 3 * width * heigth, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void Draw_ConvertPixelToBGR8(u8 *dst, const u8 *src, GSPGPU_FramebufferFormats srcFormat)
|
static inline void Draw_ConvertPixelToBGR8(u8 *dst, const u8 *src, GSPGPU_FramebufferFormat srcFormat)
|
||||||
{
|
{
|
||||||
u8 red, green, blue;
|
u8 red, green, blue;
|
||||||
switch(srcFormat)
|
switch(srcFormat)
|
||||||
@@ -313,6 +343,7 @@ static inline void Draw_ConvertPixelToBGR8(u8 *dst, const u8 *src, GSPGPU_Frameb
|
|||||||
|
|
||||||
typedef struct FrameBufferConvertArgs {
|
typedef struct FrameBufferConvertArgs {
|
||||||
u8 *buf;
|
u8 *buf;
|
||||||
|
u32 width;
|
||||||
u8 startingLine;
|
u8 startingLine;
|
||||||
u8 numLines;
|
u8 numLines;
|
||||||
bool top;
|
bool top;
|
||||||
@@ -323,8 +354,8 @@ static void Draw_ConvertFrameBufferLinesKernel(const FrameBufferConvertArgs *arg
|
|||||||
{
|
{
|
||||||
static const u8 formatSizes[] = { 4, 3, 2, 2, 2 };
|
static const u8 formatSizes[] = { 4, 3, 2, 2, 2 };
|
||||||
|
|
||||||
GSPGPU_FramebufferFormats fmt = args->top ? (GSPGPU_FramebufferFormats)(GPU_FB_TOP_FMT & 7) : (GSPGPU_FramebufferFormats)(GPU_FB_BOTTOM_FMT & 7);
|
GSPGPU_FramebufferFormat fmt = args->top ? (GSPGPU_FramebufferFormat)(GPU_FB_TOP_FMT & 7) : (GSPGPU_FramebufferFormat)(GPU_FB_BOTTOM_FMT & 7);
|
||||||
u32 width = args->top ? 400 : 320;
|
u32 width = args->width;
|
||||||
u32 stride = args->top ? GPU_FB_TOP_STRIDE : GPU_FB_BOTTOM_STRIDE;
|
u32 stride = args->top ? GPU_FB_TOP_STRIDE : GPU_FB_BOTTOM_STRIDE;
|
||||||
|
|
||||||
u32 pa = Draw_GetCurrentFramebufferAddress(args->top, args->left);
|
u32 pa = Draw_GetCurrentFramebufferAddress(args->top, args->left);
|
||||||
@@ -340,8 +371,8 @@ static void Draw_ConvertFrameBufferLinesKernel(const FrameBufferConvertArgs *arg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Draw_ConvertFrameBufferLines(u8 *buf, u32 startingLine, u32 numLines, bool top, bool left)
|
void Draw_ConvertFrameBufferLines(u8 *buf, u32 width, u32 startingLine, u32 numLines, bool top, bool left)
|
||||||
{
|
{
|
||||||
FrameBufferConvertArgs args = { buf, (u8)startingLine, (u8)numLines, top, left };
|
FrameBufferConvertArgs args = { buf, width, (u8)startingLine, (u8)numLines, top, left };
|
||||||
svcCustomBackdoor(Draw_ConvertFrameBufferLinesKernel, &args);
|
svcCustomBackdoor(Draw_ConvertFrameBufferLinesKernel, &args);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,13 @@
|
|||||||
#include "ifile.h"
|
#include "ifile.h"
|
||||||
|
|
||||||
extern Handle preTerminationEvent;
|
extern Handle preTerminationEvent;
|
||||||
|
|
||||||
|
static inline void assertSuccess(Result res)
|
||||||
|
{
|
||||||
|
if(R_FAILED(res))
|
||||||
|
svcBreak(USERBREAK_PANIC);
|
||||||
|
}
|
||||||
|
|
||||||
static MyThread errDispThread;
|
static MyThread errDispThread;
|
||||||
static u8 ALIGN(8) errDispThreadStack[0xD00];
|
static u8 ALIGN(8) errDispThreadStack[0xD00];
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
#include "csvc.h"
|
#include "csvc.h"
|
||||||
#include "fmt.h"
|
#include "fmt.h"
|
||||||
#include "gdb/breakpoints.h"
|
#include "gdb/breakpoints.h"
|
||||||
#include "utils.h"
|
|
||||||
|
|
||||||
#include "../utils.h"
|
#include "../utils.h"
|
||||||
|
|
||||||
@@ -20,15 +19,12 @@ struct
|
|||||||
GDBCommandHandler handler;
|
GDBCommandHandler handler;
|
||||||
} remoteCommandHandlers[] =
|
} remoteCommandHandlers[] =
|
||||||
{
|
{
|
||||||
{ "convertvatopa" , GDB_REMOTE_COMMAND_HANDLER(ConvertVAToPA) },
|
|
||||||
{ "syncrequestinfo" , GDB_REMOTE_COMMAND_HANDLER(SyncRequestInfo) },
|
{ "syncrequestinfo" , GDB_REMOTE_COMMAND_HANDLER(SyncRequestInfo) },
|
||||||
{ "translatehandle" , GDB_REMOTE_COMMAND_HANDLER(TranslateHandle) },
|
{ "translatehandle" , GDB_REMOTE_COMMAND_HANDLER(TranslateHandle) },
|
||||||
{ "listallhandles" , GDB_REMOTE_COMMAND_HANDLER(ListAllHandles) },
|
|
||||||
{ "getmmuconfig" , GDB_REMOTE_COMMAND_HANDLER(GetMmuConfig) },
|
{ "getmmuconfig" , GDB_REMOTE_COMMAND_HANDLER(GetMmuConfig) },
|
||||||
{ "getmemregions" , GDB_REMOTE_COMMAND_HANDLER(GetMemRegions) },
|
{ "getmemregions" , GDB_REMOTE_COMMAND_HANDLER(GetMemRegions) },
|
||||||
{ "flushcaches" , GDB_REMOTE_COMMAND_HANDLER(FlushCaches) },
|
{ "flushcaches" , GDB_REMOTE_COMMAND_HANDLER(FlushCaches) },
|
||||||
{ "toggleextmemaccess", GDB_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess) },
|
{ "toggleextmemaccess", GDB_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess) },
|
||||||
{ "catchsvc" , GDB_REMOTE_COMMAND_HANDLER(CatchSvc) },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *GDB_SkipSpaces(const char *pos)
|
static const char *GDB_SkipSpaces(const char *pos)
|
||||||
@@ -38,50 +34,6 @@ static const char *GDB_SkipSpaces(const char *pos)
|
|||||||
return nextpos;
|
return nextpos;
|
||||||
}
|
}
|
||||||
|
|
||||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ConvertVAToPA)
|
|
||||||
{
|
|
||||||
bool ok;
|
|
||||||
int n;
|
|
||||||
u32 val;
|
|
||||||
u32 pa;
|
|
||||||
char * end;
|
|
||||||
char outbuf[GDB_BUF_LEN / 2 + 1];
|
|
||||||
|
|
||||||
if(ctx->commandData[0] == 0)
|
|
||||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
|
||||||
|
|
||||||
val = xstrtoul(ctx->commandData, &end, 0, true, &ok);
|
|
||||||
|
|
||||||
if(!ok)
|
|
||||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
|
||||||
|
|
||||||
if (val >= 0x40000000)
|
|
||||||
pa = svcConvertVAToPA((const void *)val, false);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Handle process;
|
|
||||||
Result r = svcOpenProcess(&process, ctx->pid);
|
|
||||||
if(R_FAILED(r))
|
|
||||||
{
|
|
||||||
n = sprintf(outbuf, "Invalid process (wtf?)\n");
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
r = svcControlProcess(process, PROCESSOP_GET_PA_FROM_VA, (u32)&pa, val);
|
|
||||||
svcCloseHandle(process);
|
|
||||||
|
|
||||||
if (R_FAILED(r))
|
|
||||||
{
|
|
||||||
n = sprintf(outbuf, "An error occured: %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)
|
GDB_DECLARE_REMOTE_COMMAND_HANDLER(SyncRequestInfo)
|
||||||
{
|
{
|
||||||
char outbuf[GDB_BUF_LEN / 2 + 1];
|
char outbuf[GDB_BUF_LEN / 2 + 1];
|
||||||
@@ -166,29 +118,6 @@ end:
|
|||||||
return GDB_SendHexPacket(ctx, outbuf, n);
|
return GDB_SendHexPacket(ctx, outbuf, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
TOKEN_KAUTOOBJECT = 0,
|
|
||||||
TOKEN_KSYNCHRONIZATIONOBJECT = 1,
|
|
||||||
TOKEN_KEVENT = 0x1F,
|
|
||||||
TOKEN_KSEMAPHORE = 0x2F,
|
|
||||||
TOKEN_KTIMER = 0x35,
|
|
||||||
TOKEN_KMUTEX = 0x39,
|
|
||||||
TOKEN_KDEBUG = 0x4D,
|
|
||||||
TOKEN_KSERVERPORT = 0x55,
|
|
||||||
TOKEN_KDMAOBJECT = 0x59,
|
|
||||||
TOKEN_KCLIENTPORT = 0x65,
|
|
||||||
TOKEN_KCODESET = 0x68,
|
|
||||||
TOKEN_KSESSION = 0x70,
|
|
||||||
TOKEN_KTHREAD = 0x8D,
|
|
||||||
TOKEN_KSERVERSESSION = 0x95,
|
|
||||||
TOKEN_KCLIENTSESSION = 0xA5,
|
|
||||||
TOKEN_KPORT = 0xA8,
|
|
||||||
TOKEN_KSHAREDMEMORY = 0xB0,
|
|
||||||
TOKEN_KPROCESS = 0xC5,
|
|
||||||
TOKEN_KRESOURCELIMIT = 0xC8
|
|
||||||
};
|
|
||||||
|
|
||||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle)
|
GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle)
|
||||||
{
|
{
|
||||||
bool ok;
|
bool ok;
|
||||||
@@ -197,11 +126,10 @@ GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle)
|
|||||||
int n;
|
int n;
|
||||||
Result r;
|
Result r;
|
||||||
u32 kernelAddr;
|
u32 kernelAddr;
|
||||||
s64 token;
|
|
||||||
Handle handle, process;
|
Handle handle, process;
|
||||||
s64 refcountRaw;
|
s64 refcountRaw;
|
||||||
u32 refcount;
|
u32 refcount;
|
||||||
char classBuf[32], serviceBuf[12] = { 0 }, ownerBuf[50] = { 0 };
|
char classBuf[32], serviceBuf[12] = { 0 };
|
||||||
char outbuf[GDB_BUF_LEN / 2 + 1];
|
char outbuf[GDB_BUF_LEN / 2 + 1];
|
||||||
|
|
||||||
if(ctx->commandData[0] == 0)
|
if(ctx->commandData[0] == 0)
|
||||||
@@ -233,28 +161,12 @@ GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle)
|
|||||||
|
|
||||||
svcTranslateHandle(&kernelAddr, classBuf, handle);
|
svcTranslateHandle(&kernelAddr, classBuf, handle);
|
||||||
svcGetHandleInfo(&refcountRaw, handle, 1);
|
svcGetHandleInfo(&refcountRaw, handle, 1);
|
||||||
svcGetHandleInfo(&token, handle, 0x10001);
|
|
||||||
svcControlService(SERVICEOP_GET_NAME, serviceBuf, handle);
|
svcControlService(SERVICEOP_GET_NAME, serviceBuf, handle);
|
||||||
refcount = (u32)(refcountRaw - 1);
|
refcount = (u32)(refcountRaw - 1);
|
||||||
if(serviceBuf[0] != 0)
|
if(serviceBuf[0] != 0)
|
||||||
n = sprintf(outbuf, "(%s *)0x%08lx /* %s handle, %lu %s */\n", classBuf, kernelAddr, serviceBuf, refcount, refcount == 1 ? "reference" : "references");
|
n = sprintf(outbuf, "(%s *)0x%08lx /* %s handle, %lu %s */\n", classBuf, kernelAddr, serviceBuf, refcount, refcount == 1 ? "reference" : "references");
|
||||||
else if (token == TOKEN_KPROCESS)
|
|
||||||
{
|
|
||||||
svcGetProcessInfo((s64 *)serviceBuf, handle, 0x10000);
|
|
||||||
n = sprintf(outbuf, "(%s *)0x%08x /* process: %s, %u %s */\n", classBuf, kernelAddr, serviceBuf, refcount, refcount == 1 ? "reference" : "references");
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
n = sprintf(outbuf, "(%s *)0x%08lx /* %lu %s */\n", classBuf, kernelAddr, refcount, refcount == 1 ? "reference" : "references");
|
||||||
s64 owner;
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(svcGetHandleInfo(&owner, handle, 0x10002)))
|
|
||||||
{
|
|
||||||
svcGetProcessInfo((s64 *)serviceBuf, (u32)owner, 0x10000);
|
|
||||||
svcCloseHandle((u32)owner);
|
|
||||||
sprintf(ownerBuf, " owner: %s", serviceBuf);
|
|
||||||
}
|
|
||||||
n = sprintf(outbuf, "(%s *)0x%08x /* %u %s%s */\n", classBuf, kernelAddr, refcount, refcount == 1 ? "reference" : "references", ownerBuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
end:
|
end:
|
||||||
svcCloseHandle(handle);
|
svcCloseHandle(handle);
|
||||||
@@ -262,69 +174,6 @@ end:
|
|||||||
return GDB_SendHexPacket(ctx, outbuf, n);
|
return GDB_SendHexPacket(ctx, outbuf, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ListAllHandles)
|
|
||||||
{
|
|
||||||
bool ok;
|
|
||||||
u32 val;
|
|
||||||
char *end;
|
|
||||||
int n = 0;
|
|
||||||
Result r;
|
|
||||||
s32 count = 0;
|
|
||||||
Handle process, procHandles[0x100];
|
|
||||||
char outbuf[GDB_BUF_LEN / 2 + 1];
|
|
||||||
|
|
||||||
if(ctx->commandData[0] == 0)
|
|
||||||
val = 0; ///< All handles
|
|
||||||
else
|
|
||||||
{ // Get handles of specified type
|
|
||||||
val = xstrtoul(ctx->commandData, &end, 0, true, &ok);
|
|
||||||
|
|
||||||
if(!ok)
|
|
||||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
|
||||||
|
|
||||||
end = (char *)GDB_SkipSpaces(end);
|
|
||||||
|
|
||||||
if(*end != 0)
|
|
||||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
|
||||||
}
|
|
||||||
|
|
||||||
r = svcOpenProcess(&process, ctx->pid);
|
|
||||||
if(R_FAILED(r))
|
|
||||||
{
|
|
||||||
n = sprintf(outbuf, "Invalid process (wtf?)\n");
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (R_FAILED(count = svcControlProcess(process, PROCESSOP_GET_ALL_HANDLES, (u32)procHandles, val)))
|
|
||||||
n = sprintf(outbuf, "An error occured: %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;
|
extern bool isN3DS;
|
||||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig)
|
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig)
|
||||||
{
|
{
|
||||||
@@ -400,41 +249,6 @@ GDB_DECLARE_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess)
|
|||||||
return GDB_SendHexPacket(ctx, outbuf, n);
|
return GDB_SendHexPacket(ctx, outbuf, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(CatchSvc)
|
|
||||||
{
|
|
||||||
if(ctx->commandData[0] == '0')
|
|
||||||
{
|
|
||||||
memset(ctx->svcMask, 0, 32);
|
|
||||||
return R_SUCCEEDED(svcKernelSetState(0x10002, ctx->pid, false)) ? GDB_ReplyOk(ctx) : GDB_ReplyErrno(ctx, EPERM);
|
|
||||||
}
|
|
||||||
else if(ctx->commandData[0] == '1')
|
|
||||||
{
|
|
||||||
if(ctx->commandData[1] == ';')
|
|
||||||
{
|
|
||||||
u32 id;
|
|
||||||
const char *pos = ctx->commandData + 1;
|
|
||||||
memset(ctx->svcMask, 0, 32);
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
pos = GDB_ParseHexIntegerList(&id, pos + 1, 1, ';');
|
|
||||||
if(pos == NULL)
|
|
||||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
|
||||||
|
|
||||||
if(id < 0xFE)
|
|
||||||
ctx->svcMask[id / 32] |= 1 << (31 - (id % 32));
|
|
||||||
}
|
|
||||||
while(*pos != 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
memset(ctx->svcMask, 0xFF, 32);
|
|
||||||
|
|
||||||
return R_SUCCEEDED(svcKernelSetState(0x10002, ctx->pid, true, ctx->svcMask)) ? GDB_ReplyOk(ctx) : GDB_ReplyErrno(ctx, EPERM);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
|
||||||
}
|
|
||||||
|
|
||||||
GDB_DECLARE_QUERY_HANDLER(Rcmd)
|
GDB_DECLARE_QUERY_HANDLER(Rcmd)
|
||||||
{
|
{
|
||||||
char commandData[GDB_BUF_LEN / 2 + 1];
|
char commandData[GDB_BUF_LEN / 2 + 1];
|
||||||
|
|||||||
@@ -36,9 +36,6 @@
|
|||||||
#include "gdb/server.h"
|
#include "gdb/server.h"
|
||||||
#include "pmdbgext.h"
|
#include "pmdbgext.h"
|
||||||
|
|
||||||
#define SYSCOREVER (*(vu32 *)0x1FF80010)
|
|
||||||
#define APPMEMTYPE (*(vu32 *)0x1FF80030)
|
|
||||||
|
|
||||||
extern GDBContext *nextApplicationGdbCtx;
|
extern GDBContext *nextApplicationGdbCtx;
|
||||||
extern GDBServer gdbServer;
|
extern GDBServer gdbServer;
|
||||||
|
|
||||||
@@ -148,12 +145,24 @@ static const u32 kernelCaps[] =
|
|||||||
0xFF81FF78, // RW static mapping: 0x1FF78000
|
0xFF81FF78, // RW static mapping: 0x1FF78000
|
||||||
0xFF91F000, // RO static mapping: 0x1F000000
|
0xFF91F000, // RO static mapping: 0x1F000000
|
||||||
0xFF91F600, // RO static mapping: 0x1F600000
|
0xFF91F600, // RO static mapping: 0x1F600000
|
||||||
0xFF002101, // Exflags: APPLICATION memtype + "Allow debug" + "Access core2"
|
0xFF002109, // Exflags: APPLICATION memtype + "Shared page writing" + "Allow debug" + "Access core2"
|
||||||
0xFE000200, // Handle table size: 0x200
|
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 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)
|
static u16 *u16_strncpy(u16 *dest, const u16 *src, u32 size)
|
||||||
{
|
{
|
||||||
u32 i;
|
u32 i;
|
||||||
@@ -310,21 +319,22 @@ void HBLDR_HandleCommands(void *ctx)
|
|||||||
memcpy(&exhi->sci.codeset_info.stack_size, &stacksize, 4);
|
memcpy(&exhi->sci.codeset_info.stack_size, &stacksize, 4);
|
||||||
memset(&exhi->sci.dependencies, 0, sizeof(exhi->sci.dependencies));
|
memset(&exhi->sci.dependencies, 0, sizeof(exhi->sci.dependencies));
|
||||||
|
|
||||||
if (SYSCOREVER == 2)
|
u32 coreVer = OS_KernelConfig->kernel_syscore_ver;
|
||||||
|
if (coreVer == 2)
|
||||||
memcpy(exhi->sci.dependencies, dependencyListNativeFirm, sizeof(dependencyListNativeFirm));
|
memcpy(exhi->sci.dependencies, dependencyListNativeFirm, sizeof(dependencyListNativeFirm));
|
||||||
else if (SYSCOREVER == 3)
|
else if (coreVer == 3)
|
||||||
memcpy(exhi->sci.dependencies, dependencyListSafeFirm, sizeof(dependencyListSafeFirm));
|
memcpy(exhi->sci.dependencies, dependencyListSafeFirm, sizeof(dependencyListSafeFirm));
|
||||||
|
|
||||||
ExHeader_Arm11SystemLocalCapabilities* localcaps0 = &exhi->aci.local_caps;
|
ExHeader_Arm11SystemLocalCapabilities* localcaps0 = &exhi->aci.local_caps;
|
||||||
|
|
||||||
localcaps0->core_info.core_version = SYSCOREVER;
|
localcaps0->core_info.core_version = coreVer;
|
||||||
localcaps0->core_info.use_cpu_clockrate_804MHz = false;
|
localcaps0->core_info.use_cpu_clockrate_804MHz = false;
|
||||||
localcaps0->core_info.enable_l2c = false;
|
localcaps0->core_info.enable_l2c = false;
|
||||||
localcaps0->core_info.ideal_processor = 0;
|
localcaps0->core_info.ideal_processor = 0;
|
||||||
localcaps0->core_info.affinity_mask = BIT(0);
|
localcaps0->core_info.affinity_mask = BIT(0);
|
||||||
localcaps0->core_info.priority = 0x30;
|
localcaps0->core_info.priority = 0x30;
|
||||||
|
|
||||||
u32 appmemtype = APPMEMTYPE;
|
u32 appmemtype = OS_KernelConfig->app_memtype;
|
||||||
localcaps0->core_info.o3ds_system_mode = appmemtype < 6 ? (SystemMode)appmemtype : SYSMODE_O3DS_PROD;
|
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;
|
localcaps0->core_info.n3ds_system_mode = appmemtype >= 6 ? (SystemMode)(appmemtype - 6 + 1) : SYSMODE_N3DS_PROD;
|
||||||
|
|
||||||
@@ -334,7 +344,7 @@ void HBLDR_HandleCommands(void *ctx)
|
|||||||
// See the big comment in sysmodules/pm/source/reslimit.c for technical details.
|
// See the big comment in sysmodules/pm/source/reslimit.c for technical details.
|
||||||
localcaps0->reslimits[0] = BIT(7) | 89;
|
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.no_romfs = true;
|
||||||
localcaps0->storage_info.use_extended_savedata_access = true; // Whatever
|
localcaps0->storage_info.use_extended_savedata_access = true; // Whatever
|
||||||
|
|
||||||
@@ -351,7 +361,7 @@ void HBLDR_HandleCommands(void *ctx)
|
|||||||
// Set kernel release version to the current kernel version
|
// Set kernel release version to the current kernel version
|
||||||
kcaps0->descriptors[0] = 0xFC000000 | (osGetKernelVersion() >> 16);
|
kcaps0->descriptors[0] = 0xFC000000 | (osGetKernelVersion() >> 16);
|
||||||
|
|
||||||
if (GET_VERSION_MINOR(osGetKernelVersion()) >= 50 && SYSCOREVER == 2) // 9.6+ NFIRM
|
if (GET_VERSION_MINOR(osGetKernelVersion()) >= 50 && coreVer == 2) // 9.6+ NFIRM
|
||||||
{
|
{
|
||||||
u64 lastdep = sizeof(dependencyListNativeFirm)/8;
|
u64 lastdep = sizeof(dependencyListNativeFirm)/8;
|
||||||
exhi->sci.dependencies[lastdep++] = 0x0004013000004002ULL; // nfc
|
exhi->sci.dependencies[lastdep++] = 0x0004013000004002ULL; // nfc
|
||||||
|
|||||||
@@ -32,8 +32,6 @@
|
|||||||
#include "process_patches.h"
|
#include "process_patches.h"
|
||||||
#include "menus.h"
|
#include "menus.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "sleep.h"
|
|
||||||
#include "sock_util.h"
|
|
||||||
|
|
||||||
bool inputRedirectionEnabled = false;
|
bool inputRedirectionEnabled = false;
|
||||||
Handle inputRedirectionThreadStartedEvent;
|
Handle inputRedirectionThreadStartedEvent;
|
||||||
@@ -123,13 +121,6 @@ void inputRedirectionThreadMain(void)
|
|||||||
pfd.events = POLLIN;
|
pfd.events = POLLIN;
|
||||||
pfd.revents = 0;
|
pfd.revents = 0;
|
||||||
|
|
||||||
if (Sleep__Status())
|
|
||||||
{
|
|
||||||
while (!Wifi__IsConnected()
|
|
||||||
&& inputRedirectionEnabled && !preTerminationRequested)
|
|
||||||
svcSleepThread(1000000000ULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
int pollres = socPoll(&pfd, 1, 10);
|
int pollres = socPoll(&pfd, 1, 10);
|
||||||
if(pollres > 0 && (pfd.revents & POLLIN))
|
if(pollres > 0 && (pfd.revents & POLLIN))
|
||||||
{
|
{
|
||||||
@@ -177,6 +168,246 @@ void inputRedirectionThreadMain(void)
|
|||||||
void hidCodePatchFunc(void);
|
void hidCodePatchFunc(void);
|
||||||
void irCodePatchFunc(void);
|
void irCodePatchFunc(void);
|
||||||
|
|
||||||
|
static Result InputRedirection_DoUndoIrPatches(Handle processHandle, bool doPatch)
|
||||||
|
{
|
||||||
|
static u32* hookLoc = NULL;
|
||||||
|
static u32* syncLoc = NULL;
|
||||||
|
static u32* cppFlagLoc = NULL;
|
||||||
|
static u32 origIrSync = 0;
|
||||||
|
static u32 origCppFlag = 0;
|
||||||
|
|
||||||
|
static bool patchPrepared = false;
|
||||||
|
|
||||||
|
static u32 irOrigReadingCode[5] = {
|
||||||
|
0xE5940000, // ldr r0, [r4]
|
||||||
|
0xE1A01005, // mov r1, r5
|
||||||
|
0xE3A03005, // mov r3, #5
|
||||||
|
0xE3A02011, // mov r2, #17
|
||||||
|
0x00000000 // (bl i2c_read_raw goes here)
|
||||||
|
};
|
||||||
|
|
||||||
|
static u32 irHook[] = {
|
||||||
|
0xE5940000, // ldr r0, [r4]
|
||||||
|
0xE1A01005, // mov r1, r5
|
||||||
|
0xE59FC000, // ldr r12, [pc] (actually +8)
|
||||||
|
0xE12FFF3C, // blx r12
|
||||||
|
0x00000000 // irCodePhys goes here
|
||||||
|
};
|
||||||
|
|
||||||
|
static u32 syncHookCode[] = {
|
||||||
|
0xE5900000, // ldr r0, [r0]
|
||||||
|
0xEF000024, // svc 0x24
|
||||||
|
0xE3A00000, // mov r0, #0
|
||||||
|
0xE51FF004, // ldr pc, [pc, #-4]
|
||||||
|
0x00000000, // (return address goes here)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Find offsets for required patches
|
||||||
|
s64 startAddress, textTotalRoundedSize, rodataTotalRoundedSize, dataTotalRoundedSize;
|
||||||
|
u32 totalSize;
|
||||||
|
Result res;
|
||||||
|
|
||||||
|
svcGetProcessInfo(&textTotalRoundedSize, processHandle, 0x10002); // only patch .text + .data
|
||||||
|
svcGetProcessInfo(&rodataTotalRoundedSize, processHandle, 0x10003);
|
||||||
|
svcGetProcessInfo(&dataTotalRoundedSize, processHandle, 0x10004);
|
||||||
|
|
||||||
|
totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize);
|
||||||
|
|
||||||
|
svcGetProcessInfo(&startAddress, processHandle, 0x10005);
|
||||||
|
res = svcMapProcessMemoryEx(processHandle, 0x00100000, (u32) startAddress, totalSize);
|
||||||
|
|
||||||
|
if(R_SUCCEEDED(res) && !patchPrepared)
|
||||||
|
{
|
||||||
|
static const u32 irOrigWaitSyncCode[] = {
|
||||||
|
0xEF000024, // svc 0x24 (WaitSynchronization)
|
||||||
|
0xE1B01FA0, // movs r1, r0, lsr#31
|
||||||
|
0xE1A0A000, // mov r10, r0
|
||||||
|
}, irOrigWaitSyncCodeOld[] = {
|
||||||
|
0xE0AC6000, // adc r6, r12, r0
|
||||||
|
0xE5D70000, // ldrb r0, [r7]
|
||||||
|
}; // pattern for 8.1
|
||||||
|
|
||||||
|
static const u32 irOrigCppFlagCode[] = {
|
||||||
|
0xE3550000, // cmp r5, #0
|
||||||
|
0xE3A0B080, // mov r11, #0x80
|
||||||
|
};
|
||||||
|
|
||||||
|
u32 irDataPhys = (u32)PA_FROM_VA_PTR(irData);
|
||||||
|
u32 irCodePhys = (u32)PA_FROM_VA_PTR(&irCodePatchFunc);
|
||||||
|
|
||||||
|
u32 *off = (u32 *)memsearch((u8 *)0x00100000, &irOrigReadingCode, totalSize, sizeof(irOrigReadingCode) - 4);
|
||||||
|
if(off == NULL)
|
||||||
|
{
|
||||||
|
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 *off2 = (u32 *)memsearch((u8 *)0x00100000, &irOrigWaitSyncCode, totalSize, sizeof(irOrigWaitSyncCode));
|
||||||
|
if(off2 == NULL)
|
||||||
|
{
|
||||||
|
off2 = (u32 *)memsearch((u8 *)0x00100000, &irOrigWaitSyncCodeOld, totalSize, sizeof(irOrigWaitSyncCodeOld));
|
||||||
|
if(off2 == NULL)
|
||||||
|
{
|
||||||
|
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 *off3 = (u32 *)memsearch((u8 *)0x00100000, &irOrigCppFlagCode, totalSize, sizeof(irOrigCppFlagCode));
|
||||||
|
if(off3 == NULL)
|
||||||
|
{
|
||||||
|
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
origIrSync = *off2;
|
||||||
|
origCppFlag = *off3;
|
||||||
|
|
||||||
|
*(void **)(irCodePhys + 8) = decodeArmBranch(off + 4);
|
||||||
|
*(void **)(irCodePhys + 12) = (void*)irDataPhys;
|
||||||
|
|
||||||
|
irHook[4] = irCodePhys;
|
||||||
|
irOrigReadingCode[4] = off[4]; // Copy the branch.
|
||||||
|
syncHookCode[4] = (u32)off2 + 4; // Hook return address
|
||||||
|
|
||||||
|
hookLoc = PA_FROM_VA_PTR(off);
|
||||||
|
syncLoc = PA_FROM_VA_PTR(off2);
|
||||||
|
cppFlagLoc = PA_FROM_VA_PTR(off3);
|
||||||
|
|
||||||
|
patchPrepared = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(res))
|
||||||
|
{
|
||||||
|
if (doPatch)
|
||||||
|
{
|
||||||
|
memcpy(hookLoc, &irHook, sizeof(irHook));
|
||||||
|
|
||||||
|
// We keep the WaitSynchronization1 to avoid general slowdown because of the high cpu load
|
||||||
|
if (*syncLoc == 0xEF000024) // svc 0x24 (WaitSynchronization)
|
||||||
|
{
|
||||||
|
syncLoc[-1] = 0xE51FF004;
|
||||||
|
syncLoc[0] = (u32)PA_FROM_VA_PTR(&syncHookCode);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// This "NOP"s out a WaitSynchronisation1 (on the event bound to the 'IR' interrupt) or the check of a previous one
|
||||||
|
*syncLoc = 0xE3A00000; // mov r0, #0
|
||||||
|
}
|
||||||
|
|
||||||
|
// This NOPs out a flag check in ir:user's CPP emulation
|
||||||
|
*cppFlagLoc = 0xE3150000; // tst r5, #0
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(hookLoc, irOrigReadingCode, sizeof(irOrigReadingCode));
|
||||||
|
|
||||||
|
if (*syncLoc == 0xE3A00000)
|
||||||
|
*syncLoc = origIrSync;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
syncLoc[-1] = 0xE5900000; // ldr r0, [r0]
|
||||||
|
syncLoc[0] = 0xEF000024; // svc 0x24
|
||||||
|
}
|
||||||
|
|
||||||
|
*cppFlagLoc = origCppFlag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
svcInvalidateEntireInstructionCache();
|
||||||
|
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Result InputRedirection_DoUndoHidPatches(Handle processHandle, bool doPatches)
|
||||||
|
{
|
||||||
|
static const u32 hidOrigRegisterAndValue[] = { 0x1EC46000, 0x4001 };
|
||||||
|
static const u32 hidOrigCode[] = {
|
||||||
|
0xE92D4070, // push {r4-r6, lr}
|
||||||
|
0xE1A05001, // mov r5, r1
|
||||||
|
0xEE1D4F70, // mrc p15, 0, r4, c13, c0, 3
|
||||||
|
0xE3A01801, // mov r1, #0x10000
|
||||||
|
0xE5A41080, // str r1, [r4,#0x80]!
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool patchPrepared = false;
|
||||||
|
static u32 *hidRegPatchOffsets[2];
|
||||||
|
static u32 *hidPatchJumpLoc;
|
||||||
|
|
||||||
|
// Find offsets for required patches
|
||||||
|
s64 startAddress, textTotalRoundedSize, rodataTotalRoundedSize, dataTotalRoundedSize;
|
||||||
|
u32 totalSize;
|
||||||
|
Result res;
|
||||||
|
|
||||||
|
svcGetProcessInfo(&textTotalRoundedSize, processHandle, 0x10002); // only patch .text + .data
|
||||||
|
svcGetProcessInfo(&rodataTotalRoundedSize, processHandle, 0x10003);
|
||||||
|
svcGetProcessInfo(&dataTotalRoundedSize, processHandle, 0x10004);
|
||||||
|
|
||||||
|
totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize);
|
||||||
|
|
||||||
|
svcGetProcessInfo(&startAddress, processHandle, 0x10005);
|
||||||
|
res = svcMapProcessMemoryEx(processHandle, 0x00100000, (u32) startAddress, totalSize);
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(res) && !patchPrepared)
|
||||||
|
{
|
||||||
|
u32 *off = (u32 *)memsearch((u8 *)0x00100000, &hidOrigRegisterAndValue, totalSize, sizeof(hidOrigRegisterAndValue));
|
||||||
|
if(off == NULL)
|
||||||
|
{
|
||||||
|
svcUnmapProcessMemoryEx(processHandle, 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);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 *off3 = (u32 *)memsearch((u8 *)0x00100000, &hidOrigCode, totalSize, sizeof(hidOrigCode));
|
||||||
|
if(off3 == NULL)
|
||||||
|
{
|
||||||
|
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
hidRegPatchOffsets[0] = off;
|
||||||
|
hidRegPatchOffsets[1] = off2;
|
||||||
|
hidPatchJumpLoc = off3;
|
||||||
|
|
||||||
|
patchPrepared = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(R_SUCCEEDED(res))
|
||||||
|
{
|
||||||
|
if (doPatches)
|
||||||
|
{
|
||||||
|
u32 hidDataPhys = (u32)PA_FROM_VA_PTR(hidData);
|
||||||
|
u32 hidCodePhys = (u32)PA_FROM_VA_PTR(&hidCodePatchFunc);
|
||||||
|
u32 hidHook[] = {
|
||||||
|
0xE59F3004, // ldr r3, [pc, #4]
|
||||||
|
0xE59FC004, // ldr r12, [pc, #4]
|
||||||
|
0xE12FFF1C, // bx r12
|
||||||
|
hidDataPhys,
|
||||||
|
hidCodePhys,
|
||||||
|
};
|
||||||
|
|
||||||
|
*hidRegPatchOffsets[0] = *hidRegPatchOffsets[1] = hidDataPhys;
|
||||||
|
memcpy(hidPatchJumpLoc, &hidHook, sizeof(hidHook));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(hidRegPatchOffsets[0], &hidOrigRegisterAndValue, sizeof(hidOrigRegisterAndValue));
|
||||||
|
memcpy(hidRegPatchOffsets[1], &hidOrigRegisterAndValue, sizeof(hidOrigRegisterAndValue));
|
||||||
|
memcpy(hidPatchJumpLoc, &hidOrigCode, sizeof(hidOrigCode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
Result InputRedirection_Disable(s64 timeout)
|
Result InputRedirection_Disable(s64 timeout)
|
||||||
{
|
{
|
||||||
if(!inputRedirectionEnabled)
|
if(!inputRedirectionEnabled)
|
||||||
@@ -195,209 +426,46 @@ Result InputRedirection_Disable(s64 timeout)
|
|||||||
|
|
||||||
Result InputRedirection_DoOrUndoPatches(void)
|
Result InputRedirection_DoOrUndoPatches(void)
|
||||||
{
|
{
|
||||||
s64 startAddress, textTotalRoundedSize, rodataTotalRoundedSize, dataTotalRoundedSize;
|
|
||||||
u32 totalSize;
|
|
||||||
Handle processHandle;
|
|
||||||
|
|
||||||
Result res = OpenProcessByName("hid", &processHandle);
|
|
||||||
static bool hidPatched = false;
|
static bool hidPatched = false;
|
||||||
static bool irPatched = false;
|
static bool irPatched = false;
|
||||||
|
|
||||||
|
Handle hidProcHandle = 0, irProcHandle = 0;
|
||||||
|
|
||||||
|
// Prevent hid and ir from running, in any case
|
||||||
|
|
||||||
|
svcKernelSetState(0x10000, 4);
|
||||||
|
|
||||||
|
Result res = OpenProcessByName("hid", &hidProcHandle);
|
||||||
|
if (R_FAILED(res))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
res = OpenProcessByName("ir", &irProcHandle);
|
||||||
|
if (R_FAILED(res))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
if(R_SUCCEEDED(res))
|
if(R_SUCCEEDED(res))
|
||||||
{
|
{
|
||||||
svcGetProcessInfo(&textTotalRoundedSize, processHandle, 0x10002); // only patch .text + .data
|
res = InputRedirection_DoUndoHidPatches(hidProcHandle, !hidPatched);
|
||||||
svcGetProcessInfo(&rodataTotalRoundedSize, processHandle, 0x10003);
|
if (R_SUCCEEDED(res))
|
||||||
svcGetProcessInfo(&dataTotalRoundedSize, processHandle, 0x10004);
|
hidPatched = !hidPatched;
|
||||||
|
|
||||||
totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize);
|
|
||||||
|
|
||||||
svcGetProcessInfo(&startAddress, processHandle, 0x10005);
|
|
||||||
res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, processHandle, (u32) startAddress, totalSize);
|
|
||||||
|
|
||||||
if(R_SUCCEEDED(res))
|
|
||||||
{
|
|
||||||
static const u32 hidOrigRegisterAndValue[] = { 0x1EC46000, 0x4001 };
|
|
||||||
static const u32 hidOrigCode[] = {
|
|
||||||
0xE92D4070, // push {r4-r6, lr}
|
|
||||||
0xE1A05001, // mov r5, r1
|
|
||||||
0xEE1D4F70, // mrc p15, 0, r4, c13, c0, 3
|
|
||||||
0xE3A01801, // mov r1, #0x10000
|
|
||||||
0xE5A41080, // str r1, [r4,#0x80]!
|
|
||||||
};
|
|
||||||
|
|
||||||
static u32 *hidRegPatchOffsets[2];
|
|
||||||
static u32 *hidPatchJumpLoc;
|
|
||||||
|
|
||||||
if(hidPatched)
|
|
||||||
{
|
|
||||||
memcpy(hidRegPatchOffsets[0], &hidOrigRegisterAndValue, sizeof(hidOrigRegisterAndValue));
|
|
||||||
memcpy(hidRegPatchOffsets[1], &hidOrigRegisterAndValue, sizeof(hidOrigRegisterAndValue));
|
|
||||||
memcpy(hidPatchJumpLoc, &hidOrigCode, sizeof(hidOrigCode));
|
|
||||||
hidPatched = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
u32 hidDataPhys = (u32)PA_FROM_VA_PTR(hidData);
|
|
||||||
u32 hidCodePhys = (u32)PA_FROM_VA_PTR(&hidCodePatchFunc);
|
|
||||||
u32 hidHook[] = {
|
|
||||||
0xE59F3004, // ldr r3, [pc, #4]
|
|
||||||
0xE59FC004, // ldr r12, [pc, #4]
|
|
||||||
0xE12FFF1C, // bx r12
|
|
||||||
hidDataPhys,
|
|
||||||
hidCodePhys,
|
|
||||||
};
|
|
||||||
|
|
||||||
u32 *off = (u32 *)memsearch((u8 *)0x00100000, &hidOrigRegisterAndValue, totalSize, sizeof(hidOrigRegisterAndValue));
|
|
||||||
if(off == NULL)
|
|
||||||
{
|
|
||||||
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(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 *off3 = (u32 *)memsearch((u8 *)0x00100000, &hidOrigCode, totalSize, sizeof(hidOrigCode));
|
|
||||||
if(off3 == NULL)
|
|
||||||
{
|
|
||||||
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
hidRegPatchOffsets[0] = off;
|
|
||||||
hidRegPatchOffsets[1] = off2;
|
|
||||||
hidPatchJumpLoc = off3;
|
|
||||||
|
|
||||||
*off = *off2 = hidDataPhys;
|
|
||||||
memcpy(off3, &hidHook, sizeof(hidHook));
|
|
||||||
hidPatched = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res = svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
|
|
||||||
}
|
}
|
||||||
svcCloseHandle(processHandle);
|
|
||||||
|
|
||||||
res = OpenProcessByName("ir", &processHandle);
|
|
||||||
if(R_SUCCEEDED(res) && GET_VERSION_MINOR(osGetKernelVersion()) >= 44)
|
if(R_SUCCEEDED(res) && GET_VERSION_MINOR(osGetKernelVersion()) >= 44)
|
||||||
{
|
{
|
||||||
svcGetProcessInfo(&textTotalRoundedSize, processHandle, 0x10002); // only patch .text + .data
|
res = InputRedirection_DoUndoIrPatches(irProcHandle, !irPatched);
|
||||||
svcGetProcessInfo(&rodataTotalRoundedSize, processHandle, 0x10003);
|
if (R_SUCCEEDED(res))
|
||||||
svcGetProcessInfo(&dataTotalRoundedSize, processHandle, 0x10004);
|
irPatched = !irPatched;
|
||||||
|
else if (!irPatched)
|
||||||
totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize);
|
|
||||||
|
|
||||||
svcGetProcessInfo(&startAddress, processHandle, 0x10005);
|
|
||||||
res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, processHandle, (u32) startAddress, totalSize);
|
|
||||||
|
|
||||||
if(R_SUCCEEDED(res))
|
|
||||||
{
|
{
|
||||||
static bool useOldSyncCode;
|
InputRedirection_DoUndoHidPatches(hidProcHandle, false);
|
||||||
static u32 irOrigReadingCode[5] = {
|
hidPatched = false;
|
||||||
0xE5940000, // ldr r0, [r4]
|
|
||||||
0xE1A01005, // mov r1, r5
|
|
||||||
0xE3A03005, // mov r3, #5
|
|
||||||
0xE3A02011, // mov r2, #17
|
|
||||||
0x00000000 // (bl i2c_read_raw goes here)
|
|
||||||
};
|
|
||||||
|
|
||||||
static const u32 irOrigWaitSyncCode[] = {
|
|
||||||
0xEF000024, // svc 0x24 (WaitSynchronization)
|
|
||||||
0xE1B01FA0, // movs r1, r0, lsr#31
|
|
||||||
0xE1A0A000, // mov r10, r0
|
|
||||||
}, irOrigWaitSyncCodeOld[] = {
|
|
||||||
0xE0AC6000, // adc r6, r12, r0
|
|
||||||
0xE5D70000, // ldrb r0, [r7]
|
|
||||||
}; // pattern for 8.1
|
|
||||||
|
|
||||||
static const u32 irOrigCppFlagCode[] = {
|
|
||||||
0xE3550000, // cmp r5, #0
|
|
||||||
0xE3A0B080, // mov r11, #0x80
|
|
||||||
};
|
|
||||||
|
|
||||||
static u32 *irHookLoc, *irWaitSyncLoc, *irCppFlagLoc;
|
|
||||||
|
|
||||||
if(irPatched)
|
|
||||||
{
|
|
||||||
memcpy(irHookLoc, &irOrigReadingCode, sizeof(irOrigReadingCode));
|
|
||||||
if(useOldSyncCode)
|
|
||||||
memcpy(irWaitSyncLoc, &irOrigWaitSyncCodeOld, sizeof(irOrigWaitSyncCodeOld));
|
|
||||||
else
|
|
||||||
memcpy(irWaitSyncLoc, &irOrigWaitSyncCode, sizeof(irOrigWaitSyncCode));
|
|
||||||
memcpy(irCppFlagLoc, &irOrigCppFlagCode, sizeof(irOrigCppFlagCode));
|
|
||||||
|
|
||||||
irPatched = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
u32 irDataPhys = (u32)PA_FROM_VA_PTR(irData);
|
|
||||||
u32 irCodePhys = (u32)PA_FROM_VA_PTR(&irCodePatchFunc);
|
|
||||||
|
|
||||||
u32 irHook[] = {
|
|
||||||
0xE5940000, // ldr r0, [r4]
|
|
||||||
0xE1A01005, // mov r1, r5
|
|
||||||
0xE59FC000, // ldr r12, [pc] (actually +8)
|
|
||||||
0xE12FFF3C, // blx r12
|
|
||||||
irCodePhys,
|
|
||||||
};
|
|
||||||
|
|
||||||
u32 *off = (u32 *)memsearch((u8 *)0x00100000, &irOrigReadingCode, totalSize, sizeof(irOrigReadingCode) - 4);
|
|
||||||
if(off == NULL)
|
|
||||||
{
|
|
||||||
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 *off2 = (u32 *)memsearch((u8 *)0x00100000, &irOrigWaitSyncCode, totalSize, sizeof(irOrigWaitSyncCode));
|
|
||||||
if(off2 == NULL)
|
|
||||||
{
|
|
||||||
off2 = (u32 *)memsearch((u8 *)0x00100000, &irOrigWaitSyncCodeOld, totalSize, sizeof(irOrigWaitSyncCodeOld));
|
|
||||||
if(off2 == NULL)
|
|
||||||
{
|
|
||||||
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
|
|
||||||
return -5;
|
|
||||||
}
|
|
||||||
|
|
||||||
useOldSyncCode = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
useOldSyncCode = false;
|
|
||||||
|
|
||||||
u32 *off3 = (u32 *)memsearch((u8 *)0x00100000, &irOrigCppFlagCode, totalSize, sizeof(irOrigCppFlagCode));
|
|
||||||
if(off3 == NULL)
|
|
||||||
{
|
|
||||||
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
|
|
||||||
return -6;
|
|
||||||
}
|
|
||||||
|
|
||||||
*(void **)(irCodePhys + 8) = decodeArmBranch(off + 4);
|
|
||||||
*(void **)(irCodePhys + 12) = (void*)irDataPhys;
|
|
||||||
|
|
||||||
irHookLoc = off;
|
|
||||||
irWaitSyncLoc = off2;
|
|
||||||
irCppFlagLoc = off3;
|
|
||||||
|
|
||||||
irOrigReadingCode[4] = off[4]; // Copy the branch.
|
|
||||||
|
|
||||||
memcpy(irHookLoc, &irHook, sizeof(irHook));
|
|
||||||
|
|
||||||
// This "NOP"s out a WaitSynchronisation1 (on the event bound to the 'IR' interrupt) or the check of a previous one
|
|
||||||
*irWaitSyncLoc = 0xE3A00000; // mov r0, #0
|
|
||||||
|
|
||||||
// This NOPs out a flag check in ir:user's CPP emulation
|
|
||||||
*irCppFlagLoc = 0xE3150000; // tst r5, #0
|
|
||||||
|
|
||||||
irPatched = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res = svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
|
|
||||||
}
|
}
|
||||||
svcCloseHandle(processHandle);
|
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
svcKernelSetState(0x10000, 4);
|
||||||
|
|
||||||
|
svcCloseHandle(hidProcHandle);
|
||||||
|
svcCloseHandle(irProcHandle);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
122
sysmodules/rosalina/source/luminance.c
Normal file
122
sysmodules/rosalina/source/luminance.c
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* 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 <3ds.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include "luminance.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
extern bool isN3DS;
|
||||||
|
|
||||||
|
typedef struct BlPwmData
|
||||||
|
{
|
||||||
|
float coeffs[3][3];
|
||||||
|
u8 numLevels;
|
||||||
|
u8 unk;
|
||||||
|
u16 luminanceLevels[7];
|
||||||
|
u16 brightnessMax;
|
||||||
|
u16 brightnessMin;
|
||||||
|
} BlPwmData;
|
||||||
|
|
||||||
|
// Calibration, with (dubious) default values as fallback
|
||||||
|
static BlPwmData s_blPwmData = {
|
||||||
|
.coeffs = {
|
||||||
|
{ 0.00111639f, 1.41412f, 0.07178809f },
|
||||||
|
{ 0.000418169f, 0.66567f, 0.06098654f },
|
||||||
|
{ 0.00208543f, 1.55639f, 0.0385939f }
|
||||||
|
},
|
||||||
|
.numLevels = 5,
|
||||||
|
.unk = 0,
|
||||||
|
.luminanceLevels = { 20, 43, 73, 95, 117, 172, 172 },
|
||||||
|
.brightnessMax = 512,
|
||||||
|
.brightnessMin = 13,
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline float getPwmRatio(u32 brightnessMax, u32 pwmCnt)
|
||||||
|
{
|
||||||
|
u32 val = (pwmCnt & 0x10000) ? pwmCnt & 0x3FF : 511; // check pwm enabled flag
|
||||||
|
return (float)brightnessMax / (val + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// nn's asm has rounding errors (originally at 10^-3)
|
||||||
|
static inline u32 luminanceToBrightness(u32 luminance, const float coeffs[3], u32 minLuminance, float pwmRatio)
|
||||||
|
{
|
||||||
|
float x = (float)luminance;
|
||||||
|
float y = coeffs[0]*x*x + coeffs[1]*x + coeffs[2];
|
||||||
|
y = (y <= minLuminance ? (float)minLuminance : y) / pwmRatio;
|
||||||
|
|
||||||
|
return (u32)(y + 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 brightnessToLuminance(u32 brightness, const float coeffs[3], float pwmRatio)
|
||||||
|
{
|
||||||
|
// Find polynomial root of ax^2 + bx + c = y
|
||||||
|
|
||||||
|
float y = (float)brightness * pwmRatio;
|
||||||
|
float a = coeffs[0];
|
||||||
|
float b = coeffs[1];
|
||||||
|
float c = coeffs[2] - y;
|
||||||
|
|
||||||
|
float x0 = (-b + sqrtf(b*b - 4.0f*a*c)) / (a + a);
|
||||||
|
|
||||||
|
return (u32)(x0 + 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void readCalibration(void)
|
||||||
|
{
|
||||||
|
static bool calibRead = false;
|
||||||
|
|
||||||
|
if (!calibRead) {
|
||||||
|
cfguInit();
|
||||||
|
calibRead = R_SUCCEEDED(CFG_GetConfigInfoBlk8(sizeof(BlPwmData), 0x50002, &s_blPwmData));
|
||||||
|
cfguExit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 getMinLuminancePreset(void)
|
||||||
|
{
|
||||||
|
readCalibration();
|
||||||
|
return s_blPwmData.luminanceLevels[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 getMaxLuminancePreset(void)
|
||||||
|
{
|
||||||
|
readCalibration();
|
||||||
|
return s_blPwmData.luminanceLevels[s_blPwmData.numLevels - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 getCurrentLuminance(bool top)
|
||||||
|
{
|
||||||
|
u32 regbase = top ? 0x10202200 : 0x10202A00;
|
||||||
|
|
||||||
|
readCalibration();
|
||||||
|
|
||||||
|
const float *coeffs = s_blPwmData.coeffs[top ? (isN3DS ? 2 : 1) : 0];
|
||||||
|
u32 brightness = REG32(regbase + 0x40);
|
||||||
|
float ratio = getPwmRatio(s_blPwmData.brightnessMax, REG32(regbase + 0x44));
|
||||||
|
|
||||||
|
return brightnessToLuminance(brightness, coeffs, ratio);
|
||||||
|
}
|
||||||
@@ -34,7 +34,6 @@
|
|||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "MyThread.h"
|
#include "MyThread.h"
|
||||||
#include "menus/miscellaneous.h"
|
#include "menus/miscellaneous.h"
|
||||||
#include "plgloader.h"
|
|
||||||
#include "menus/debugger.h"
|
#include "menus/debugger.h"
|
||||||
#include "menus/screen_filters.h"
|
#include "menus/screen_filters.h"
|
||||||
#include "menus/cheats.h"
|
#include "menus/cheats.h"
|
||||||
@@ -83,13 +82,15 @@ void initSystem(void)
|
|||||||
isN3DS = svcGetSystemInfo(&out, 0x10001, 0) == 0;
|
isN3DS = svcGetSystemInfo(&out, 0x10001, 0) == 0;
|
||||||
|
|
||||||
svcGetSystemInfo(&out, 0x10000, 0x100);
|
svcGetSystemInfo(&out, 0x10000, 0x100);
|
||||||
HBLDR_3DSX_TID = out == 0 ? HBLDR_DEFAULT_3DSX_TID : (u64)out;
|
Luma_SharedConfig->hbldr_3dsx_tid = out == 0 ? HBLDR_DEFAULT_3DSX_TID : (u64)out;
|
||||||
|
Luma_SharedConfig->use_hbldr = true;
|
||||||
|
|
||||||
svcGetSystemInfo(&out, 0x10000, 0x101);
|
svcGetSystemInfo(&out, 0x10000, 0x101);
|
||||||
menuCombo = out == 0 ? DEFAULT_MENU_COMBO : (u32)out;
|
menuCombo = out == 0 ? DEFAULT_MENU_COMBO : (u32)out;
|
||||||
|
|
||||||
miscellaneousMenu.items[0].title = HBLDR_3DSX_TID == HBLDR_DEFAULT_3DSX_TID ? "Switch the hb. title to the current app." :
|
miscellaneousMenu.items[0].title = Luma_SharedConfig->hbldr_3dsx_tid == HBLDR_DEFAULT_3DSX_TID ?
|
||||||
"Switch the hb. title to hblauncher_loader";
|
"Switch the hb. title to the current app." :
|
||||||
|
"Switch the hb. title to hblauncher_loader";
|
||||||
|
|
||||||
for(res = 0xD88007FA; res == (Result)0xD88007FA; svcSleepThread(500 * 1000LL))
|
for(res = 0xD88007FA; res == (Result)0xD88007FA; svcSleepThread(500 * 1000LL))
|
||||||
{
|
{
|
||||||
@@ -104,6 +105,9 @@ void initSystem(void)
|
|||||||
if (R_FAILED(fsInit()))
|
if (R_FAILED(fsInit()))
|
||||||
svcBreak(USERBREAK_PANIC);
|
svcBreak(USERBREAK_PANIC);
|
||||||
|
|
||||||
|
if (R_FAILED(FSUSER_SetPriority(-16)))
|
||||||
|
svcBreak(USERBREAK_PANIC);
|
||||||
|
|
||||||
// **** DO NOT init services that don't come from KIPs here ****
|
// **** DO NOT init services that don't come from KIPs here ****
|
||||||
// Instead, init the service only where it's actually init (then deinit it).
|
// Instead, init the service only where it's actually init (then deinit it).
|
||||||
|
|
||||||
@@ -131,11 +135,53 @@ static void handleTermNotification(u32 notificationId)
|
|||||||
(void)notificationId;
|
(void)notificationId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void handleSleepNotification(u32 notificationId)
|
||||||
|
{
|
||||||
|
ptmSysmInit();
|
||||||
|
s32 ackValue = ptmSysmGetNotificationAckValue(notificationId);
|
||||||
|
switch (notificationId)
|
||||||
|
{
|
||||||
|
case PTMNOTIFID_SLEEP_REQUESTED:
|
||||||
|
menuShouldExit = true;
|
||||||
|
PTMSYSM_ReplyToSleepQuery(miniSocEnabled); // deny sleep request if we have network stuff running
|
||||||
|
break;
|
||||||
|
case PTMNOTIFID_GOING_TO_SLEEP:
|
||||||
|
case PTMNOTIFID_SLEEP_ALLOWED:
|
||||||
|
case PTMNOTIFID_FULLY_WAKING_UP:
|
||||||
|
case PTMNOTIFID_HALF_AWAKE:
|
||||||
|
PTMSYSM_NotifySleepPreparationComplete(ackValue);
|
||||||
|
break;
|
||||||
|
case PTMNOTIFID_SLEEP_DENIED:
|
||||||
|
case PTMNOTIFID_FULLY_AWAKE:
|
||||||
|
menuShouldExit = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ptmSysmExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleShellNotification(u32 notificationId)
|
||||||
|
{
|
||||||
|
if (notificationId == 0x213) {
|
||||||
|
// Shell opened
|
||||||
|
// Note that this notification is fired on system init
|
||||||
|
ScreenFiltersMenu_RestoreCct();
|
||||||
|
menuShouldExit = false;
|
||||||
|
} else {
|
||||||
|
// Shell closed
|
||||||
|
menuShouldExit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
static void handlePreTermNotification(u32 notificationId)
|
static void handlePreTermNotification(u32 notificationId)
|
||||||
{
|
{
|
||||||
(void)notificationId;
|
(void)notificationId;
|
||||||
// Might be subject to a race condition, but heh.
|
// Might be subject to a race condition, but heh.
|
||||||
|
|
||||||
|
miniSocUnlockState(true);
|
||||||
|
|
||||||
// Disable input redirection
|
// Disable input redirection
|
||||||
InputRedirection_Disable(100 * 1000 * 1000LL);
|
InputRedirection_Disable(100 * 1000 * 1000LL);
|
||||||
|
|
||||||
@@ -180,11 +226,19 @@ static const ServiceManagerServiceEntry services[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const ServiceManagerNotificationEntry notifications[] = {
|
static const ServiceManagerNotificationEntry notifications[] = {
|
||||||
{ 0x100 , handleTermNotification },
|
{ 0x100 , handleTermNotification },
|
||||||
//{ 0x103 , handlePreTermNotification }, // Sleep mode entry <=== causes issues
|
{ PTMNOTIFID_SLEEP_REQUESTED, handleSleepNotification },
|
||||||
{ 0x1000, handleNextApplicationDebuggedByForce },
|
{ PTMNOTIFID_SLEEP_DENIED, handleSleepNotification },
|
||||||
{ 0x2000, handlePreTermNotification },
|
{ PTMNOTIFID_SLEEP_ALLOWED, handleSleepNotification },
|
||||||
{ 0x3000, handleRestartHbAppNotification },
|
{ PTMNOTIFID_GOING_TO_SLEEP, handleSleepNotification },
|
||||||
|
{ PTMNOTIFID_FULLY_WAKING_UP, handleSleepNotification },
|
||||||
|
{ PTMNOTIFID_FULLY_AWAKE, handleSleepNotification },
|
||||||
|
{ PTMNOTIFID_HALF_AWAKE, handleSleepNotification },
|
||||||
|
{ 0x213, handleShellNotification },
|
||||||
|
{ 0x214, handleShellNotification },
|
||||||
|
{ 0x1000, handleNextApplicationDebuggedByForce },
|
||||||
|
{ 0x2000, handlePreTermNotification },
|
||||||
|
{ 0x3000, handleRestartHbAppNotification },
|
||||||
{ 0x000, NULL },
|
{ 0x000, NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -208,14 +262,17 @@ int main(void)
|
|||||||
|
|
||||||
MyThread *menuThread = menuCreateThread();
|
MyThread *menuThread = menuCreateThread();
|
||||||
MyThread *taskRunnerThread = taskRunnerCreateThread();
|
MyThread *taskRunnerThread = taskRunnerCreateThread();
|
||||||
MyThread *plgloaderThread = PluginLoader__CreateThread();
|
MyThread *errDispThread = errDispCreateThread();
|
||||||
|
|
||||||
if (R_FAILED(ServiceManager_Run(services, notifications, NULL)))
|
if (R_FAILED(ServiceManager_Run(services, notifications, NULL)))
|
||||||
svcBreak(USERBREAK_PANIC);
|
svcBreak(USERBREAK_PANIC);
|
||||||
|
|
||||||
|
TaskRunner_Terminate();
|
||||||
|
|
||||||
MyThread_Join(menuThread, -1LL);
|
MyThread_Join(menuThread, -1LL);
|
||||||
|
|
||||||
MyThread_Join(taskRunnerThread, -1LL);
|
MyThread_Join(taskRunnerThread, -1LL);
|
||||||
MyThread_Join(plgloaderThread, -1LL);
|
MyThread_Join(errDispThread, -1LL);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,15 +51,6 @@ u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize)
|
|||||||
return NULL;
|
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)
|
void hexItoa(u64 number, char *out, u32 digits, bool uppercase)
|
||||||
{
|
{
|
||||||
const char hexDigits[] = "0123456789ABCDEF";
|
const char hexDigits[] = "0123456789ABCDEF";
|
||||||
|
|||||||
@@ -32,7 +32,6 @@
|
|||||||
#include "ifile.h"
|
#include "ifile.h"
|
||||||
#include "menus.h"
|
#include "menus.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "plgloader.h"
|
|
||||||
#include "menus/n3ds.h"
|
#include "menus/n3ds.h"
|
||||||
#include "menus/cheats.h"
|
#include "menus/cheats.h"
|
||||||
#include "minisoc.h"
|
#include "minisoc.h"
|
||||||
@@ -144,7 +143,6 @@ u32 waitCombo(void)
|
|||||||
static MyThread menuThread;
|
static MyThread menuThread;
|
||||||
static u8 ALIGN(8) menuThreadStack[0x1000];
|
static u8 ALIGN(8) menuThreadStack[0x1000];
|
||||||
static u8 batteryLevel = 255;
|
static u8 batteryLevel = 255;
|
||||||
static u32 homeBtnPressed = 0;
|
|
||||||
|
|
||||||
static inline u32 menuAdvanceCursor(u32 pos, u32 numItems, s32 displ)
|
static inline u32 menuAdvanceCursor(u32 pos, u32 numItems, s32 displ)
|
||||||
{
|
{
|
||||||
@@ -170,17 +168,14 @@ u32 menuCountItems(const Menu *menu)
|
|||||||
|
|
||||||
MyThread *menuCreateThread(void)
|
MyThread *menuCreateThread(void)
|
||||||
{
|
{
|
||||||
svcKernelSetState(0x10007, &homeBtnPressed);
|
if(R_FAILED(MyThread_Create(&menuThread, menuThreadMain, menuThreadStack, 0x1000, 52, CORE_SYSTEM)))
|
||||||
if(R_FAILED(MyThread_Create(&menuThread, menuThreadMain, menuThreadStack, 0x3000, 52, CORE_SYSTEM)))
|
|
||||||
svcBreak(USERBREAK_PANIC);
|
svcBreak(USERBREAK_PANIC);
|
||||||
return &menuThread;
|
return &menuThread;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 menuCombo;
|
u32 menuCombo;
|
||||||
|
|
||||||
u32 DispWarningOnHome(void);
|
void menuThreadMain(void)
|
||||||
|
|
||||||
void menuThreadMain(void)
|
|
||||||
{
|
{
|
||||||
if(isN3DS)
|
if(isN3DS)
|
||||||
N3DSMenu_UpdateStatus();
|
N3DSMenu_UpdateStatus();
|
||||||
@@ -193,29 +188,19 @@ void menuThreadMain(void)
|
|||||||
|
|
||||||
while(!preTerminationRequested)
|
while(!preTerminationRequested)
|
||||||
{
|
{
|
||||||
|
svcSleepThread(50 * 1000 * 1000LL);
|
||||||
if (menuShouldExit)
|
if (menuShouldExit)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ((scanHeldKeys() & menuCombo) == menuCombo)
|
Cheat_ApplyCheats();
|
||||||
|
|
||||||
|
if((scanHeldKeys() & menuCombo) == menuCombo)
|
||||||
{
|
{
|
||||||
menuEnter();
|
menuEnter();
|
||||||
if(isN3DS) N3DSMenu_UpdateStatus();
|
if(isN3DS) N3DSMenu_UpdateStatus();
|
||||||
PluginLoader__UpdateMenu();
|
|
||||||
menuShow(&rosalinaMenu);
|
menuShow(&rosalinaMenu);
|
||||||
menuLeave();
|
menuLeave();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Check for home button on O3DS Mode3 with plugin loaded
|
|
||||||
if (homeBtnPressed != 0)
|
|
||||||
{
|
|
||||||
if (DispWarningOnHome())
|
|
||||||
svcKernelSetState(7); ///< reboot is fine since exiting a mode3 game reboot anyway
|
|
||||||
|
|
||||||
homeBtnPressed = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
svcSleepThread(50 * 1000 * 1000LL);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,17 +211,17 @@ void menuEnter(void)
|
|||||||
if(!menuShouldExit && menuRefCount == 0)
|
if(!menuShouldExit && menuRefCount == 0)
|
||||||
{
|
{
|
||||||
menuRefCount++;
|
menuRefCount++;
|
||||||
svcKernelSetState(0x10000, 1);
|
svcKernelSetState(0x10000, 2 | 1);
|
||||||
svcSleepThread(5 * 1000 * 100LL);
|
svcSleepThread(5 * 1000 * 100LL);
|
||||||
if (Draw_AllocateFramebufferCache() == 0)
|
if (R_FAILED(Draw_AllocateFramebufferCache(FB_BOTTOM_SIZE)))
|
||||||
{
|
{
|
||||||
// Oops
|
// Oops
|
||||||
menuRefCount = 0;
|
menuRefCount = 0;
|
||||||
svcKernelSetState(0x10000, 1);
|
svcKernelSetState(0x10000, 2 | 1);
|
||||||
svcSleepThread(5 * 1000 * 100LL);
|
svcSleepThread(5 * 1000 * 100LL);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
Draw_SetupFramebuffer();
|
Draw_SetupFramebuffer();
|
||||||
}
|
}
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
}
|
}
|
||||||
@@ -250,7 +235,7 @@ void menuLeave(void)
|
|||||||
{
|
{
|
||||||
Draw_RestoreFramebuffer();
|
Draw_RestoreFramebuffer();
|
||||||
Draw_FreeFramebufferCache();
|
Draw_FreeFramebufferCache();
|
||||||
svcKernelSetState(0x10000, 1);
|
svcKernelSetState(0x10000, 2 | 1);
|
||||||
}
|
}
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
}
|
}
|
||||||
@@ -421,158 +406,3 @@ void menuShow(Menu *root)
|
|||||||
}
|
}
|
||||||
while(!menuShouldExit);
|
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();
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -35,18 +35,17 @@
|
|||||||
#include "menus/miscellaneous.h"
|
#include "menus/miscellaneous.h"
|
||||||
#include "menus/sysconfig.h"
|
#include "menus/sysconfig.h"
|
||||||
#include "menus/screen_filters.h"
|
#include "menus/screen_filters.h"
|
||||||
#include "plgloader.h"
|
|
||||||
#include "ifile.h"
|
#include "ifile.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "fmt.h"
|
#include "fmt.h"
|
||||||
#include "process_patches.h"
|
#include "process_patches.h"
|
||||||
|
#include "luminance.h"
|
||||||
|
|
||||||
Menu rosalinaMenu = {
|
Menu rosalinaMenu = {
|
||||||
"Rosalina menu",
|
"Rosalina menu",
|
||||||
{
|
{
|
||||||
{ "Take screenshot", METHOD, .method = &RosalinaMenu_TakeScreenshot },
|
{ "Take screenshot", METHOD, .method = &RosalinaMenu_TakeScreenshot },
|
||||||
{ "Change screen brightness", METHOD, .method = &RosalinaMenu_ChangeScreenBrightness },
|
{ "Change screen brightness", METHOD, .method = &RosalinaMenu_ChangeScreenBrightness },
|
||||||
{ "", METHOD, .method = PluginLoader__MenuCallback},
|
|
||||||
{ "Cheats...", METHOD, .method = &RosalinaMenu_Cheats },
|
{ "Cheats...", METHOD, .method = &RosalinaMenu_Cheats },
|
||||||
{ "Process list", METHOD, .method = &RosalinaMenu_ProcessList },
|
{ "Process list", METHOD, .method = &RosalinaMenu_ProcessList },
|
||||||
{ "Debugger options...", MENU, .menu = &debuggerMenu },
|
{ "Debugger options...", MENU, .menu = &debuggerMenu },
|
||||||
@@ -64,7 +63,11 @@ Menu rosalinaMenu = {
|
|||||||
|
|
||||||
bool rosalinaMenuShouldShowDebugInfo(void)
|
bool rosalinaMenuShouldShowDebugInfo(void)
|
||||||
{
|
{
|
||||||
return true;
|
// Don't show on release builds
|
||||||
|
|
||||||
|
s64 out;
|
||||||
|
svcGetSystemInfo(&out, 0x10000, 0x200);
|
||||||
|
return out == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RosalinaMenu_ShowDebugInfo(void)
|
void RosalinaMenu_ShowDebugInfo(void)
|
||||||
@@ -109,7 +112,6 @@ void RosalinaMenu_ShowCredits(void)
|
|||||||
|
|
||||||
u32 posY = Draw_DrawString(10, 30, COLOR_WHITE, "Luma3DS (c) 2016-2020 AuroraWright, TuxSH") + SPACING_Y;
|
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, "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, "Networking code & basic GDB functionality by Stary");
|
||||||
posY = Draw_DrawString(10, posY + SPACING_Y, COLOR_WHITE, "InputRedirection by Stary (PoC by ShinyQuagsire)");
|
posY = Draw_DrawString(10, posY + SPACING_Y, COLOR_WHITE, "InputRedirection by Stary (PoC by ShinyQuagsire)");
|
||||||
@@ -119,8 +121,8 @@ void RosalinaMenu_ShowCredits(void)
|
|||||||
Draw_DrawString(10, posY, COLOR_WHITE,
|
Draw_DrawString(10, posY, COLOR_WHITE,
|
||||||
(
|
(
|
||||||
"Special thanks to:\n"
|
"Special thanks to:\n"
|
||||||
" Bond697, WinterMute, piepie62, yifanlu\n"
|
" fincs, WinterMute, mtheall, piepie62,\n"
|
||||||
" Luma3DS contributors, ctrulib contributors,\n"
|
" Luma3DS contributors, libctru contributors,\n"
|
||||||
" other people"
|
" other people"
|
||||||
));
|
));
|
||||||
|
|
||||||
@@ -140,7 +142,7 @@ void RosalinaMenu_Reboot(void)
|
|||||||
do
|
do
|
||||||
{
|
{
|
||||||
Draw_Lock();
|
Draw_Lock();
|
||||||
Draw_DrawString(10, 10, COLOR_TITLE, "Rosalina menu");
|
Draw_DrawString(10, 10, COLOR_TITLE, "Reboot");
|
||||||
Draw_DrawString(10, 30, COLOR_WHITE, "Press A to reboot, press B to go back.");
|
Draw_DrawString(10, 30, COLOR_WHITE, "Press A to reboot, press B to go back.");
|
||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
@@ -158,107 +160,102 @@ void RosalinaMenu_Reboot(void)
|
|||||||
while(!menuShouldExit);
|
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)
|
void RosalinaMenu_ChangeScreenBrightness(void)
|
||||||
{
|
{
|
||||||
Result patchResult = 0;
|
|
||||||
if (isN3DS && !gspPatchDoneN3ds)
|
|
||||||
{
|
|
||||||
patchResult = PatchProcessByName("gsp", RosalinaMenu_PatchN3dsGspForBrightness);
|
|
||||||
gspPatchDoneN3ds = R_SUCCEEDED(patchResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
Draw_Lock();
|
Draw_Lock();
|
||||||
Draw_ClearFramebuffer();
|
Draw_ClearFramebuffer();
|
||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
|
|
||||||
|
// gsp:LCD GetLuminance is stubbed on O3DS so we have to implement it ourselves... damn it.
|
||||||
|
// Assume top and bottom screen luminances are the same (should be; if not, we'll set them to the same values).
|
||||||
|
u32 luminance = getCurrentLuminance(false);
|
||||||
|
u32 minLum = getMinLuminancePreset();
|
||||||
|
u32 maxLum = getMaxLuminancePreset();
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
// Assume the current brightness for both screens are the same.
|
|
||||||
s32 brightness = (s32)(LCD_TOP_BRIGHTNESS & 0xFF);
|
|
||||||
|
|
||||||
Draw_Lock();
|
Draw_Lock();
|
||||||
Draw_DrawString(10, 10, COLOR_TITLE, "Rosalina menu");
|
Draw_DrawString(10, 10, COLOR_TITLE, "Screen brightness");
|
||||||
u32 posY = 30;
|
u32 posY = 30;
|
||||||
posY = Draw_DrawFormattedString(10, posY, COLOR_WHITE, "Current brightness (0..255): %3lu\n\n", brightness);
|
posY = Draw_DrawFormattedString(
|
||||||
if (R_SUCCEEDED(patchResult))
|
10,
|
||||||
{
|
posY,
|
||||||
posY = Draw_DrawString(10, posY, COLOR_WHITE, "Press Up/Down for +-1, Right/Left for +-10.\n");
|
COLOR_WHITE,
|
||||||
posY = Draw_DrawString(10, posY, COLOR_WHITE, "Press Y to revert the GSP patch and exit.\n\n");
|
"Current luminance: %lu (min. %lu, max. %lu)\n\n",
|
||||||
|
luminance,
|
||||||
posY = Draw_DrawString(10, posY, COLOR_RED, "WARNING: \n");
|
minLum,
|
||||||
posY = Draw_DrawString(10, posY, COLOR_WHITE, " * avoid using values far higher than the presets.\n");
|
maxLum
|
||||||
posY = Draw_DrawString(10, posY, COLOR_WHITE, " * normal brightness mngmt. is now broken on N3DS.\nYou'll need to press Y to revert");
|
);
|
||||||
}
|
posY = Draw_DrawString(10, posY, COLOR_WHITE, "Controls: Up/Down for +-1, Right/Left for +-10.\n");
|
||||||
else
|
posY = Draw_DrawString(10, posY, COLOR_WHITE, "Press A to start, B to exit.\n\n");
|
||||||
Draw_DrawFormattedString(10, posY, COLOR_WHITE, "Failed to patch GSP (0x%08lx).", (u32)patchResult);
|
|
||||||
|
|
||||||
|
posY = Draw_DrawString(10, posY, COLOR_RED, "WARNING: \n");
|
||||||
|
posY = Draw_DrawString(10, posY, COLOR_WHITE, " * value will be limited by the presets.\n");
|
||||||
|
posY = Draw_DrawString(10, posY, COLOR_WHITE, " * bottom framebuffer will be restored until\nyou exit.");
|
||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
|
|
||||||
u32 pressed = waitInputWithTimeout(1000);
|
u32 pressed = waitInputWithTimeout(1000);
|
||||||
|
|
||||||
if ((pressed & DIRECTIONAL_KEYS) && R_SUCCEEDED(patchResult))
|
if (pressed & KEY_A)
|
||||||
{
|
break;
|
||||||
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;
|
if (pressed & KEY_B)
|
||||||
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;
|
return;
|
||||||
}
|
}
|
||||||
while (!menuShouldExit);
|
while (!menuShouldExit);
|
||||||
|
|
||||||
|
Draw_Lock();
|
||||||
|
|
||||||
|
Draw_RestoreFramebuffer();
|
||||||
|
Draw_FreeFramebufferCache();
|
||||||
|
|
||||||
|
svcKernelSetState(0x10000, 2); // unblock gsp
|
||||||
|
gspLcdInit(); // assume it doesn't fail. If it does, brightness won't change, anyway.
|
||||||
|
|
||||||
|
// gsp:LCD will normalize the brightness between top/bottom screen, handle PWM, etc.
|
||||||
|
|
||||||
|
s32 lum = (s32)luminance;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
u32 pressed = waitInputWithTimeout(1000);
|
||||||
|
if (pressed & DIRECTIONAL_KEYS)
|
||||||
|
{
|
||||||
|
if (pressed & KEY_UP)
|
||||||
|
lum += 1;
|
||||||
|
else if (pressed & KEY_DOWN)
|
||||||
|
lum -= 1;
|
||||||
|
else if (pressed & KEY_RIGHT)
|
||||||
|
lum += 10;
|
||||||
|
else if (pressed & KEY_LEFT)
|
||||||
|
lum -= 10;
|
||||||
|
|
||||||
|
lum = lum < 0 ? 0 : lum;
|
||||||
|
|
||||||
|
// We need to call gsp here because updating the active duty LUT is a bit tedious (plus, GSP has internal state).
|
||||||
|
// This is actually SetLuminance:
|
||||||
|
GSPLCD_SetBrightnessRaw(BIT(GSP_SCREEN_TOP) | BIT(GSP_SCREEN_BOTTOM), lum);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pressed & KEY_B)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
while (!menuShouldExit);
|
||||||
|
|
||||||
|
gspLcdExit();
|
||||||
|
svcKernelSetState(0x10000, 2); // block gsp again
|
||||||
|
|
||||||
|
if (R_FAILED(Draw_AllocateFramebufferCache(FB_BOTTOM_SIZE)))
|
||||||
|
{
|
||||||
|
// Shouldn't happen
|
||||||
|
__builtin_trap();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Draw_SetupFramebuffer();
|
||||||
|
|
||||||
|
Draw_Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RosalinaMenu_PowerOff(void) // Soft shutdown.
|
void RosalinaMenu_PowerOff(void) // Soft shutdown.
|
||||||
@@ -271,7 +268,7 @@ void RosalinaMenu_PowerOff(void) // Soft shutdown.
|
|||||||
do
|
do
|
||||||
{
|
{
|
||||||
Draw_Lock();
|
Draw_Lock();
|
||||||
Draw_DrawString(10, 10, COLOR_TITLE, "Rosalina menu");
|
Draw_DrawString(10, 10, COLOR_TITLE, "Power off");
|
||||||
Draw_DrawString(10, 30, COLOR_WHITE, "Press A to power off, press B to go back.");
|
Draw_DrawString(10, 30, COLOR_WHITE, "Press A to power off, press B to go back.");
|
||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
@@ -296,18 +293,20 @@ void RosalinaMenu_PowerOff(void) // Soft shutdown.
|
|||||||
static s64 timeSpentConvertingScreenshot = 0;
|
static s64 timeSpentConvertingScreenshot = 0;
|
||||||
static s64 timeSpentWritingScreenshot = 0;
|
static s64 timeSpentWritingScreenshot = 0;
|
||||||
|
|
||||||
static Result RosalinaMenu_WriteScreenshot(IFile *file, bool top, bool left)
|
static Result RosalinaMenu_WriteScreenshot(IFile *file, u32 width, bool top, bool left)
|
||||||
{
|
{
|
||||||
u64 total;
|
u64 total;
|
||||||
Result res = 0;
|
Result res = 0;
|
||||||
u32 dimX = top ? 400 : 320;
|
u32 lineSize = 3 * width;
|
||||||
u32 lineSize = 3 * dimX;
|
|
||||||
u32 remaining = lineSize * 240;
|
u32 remaining = lineSize * 240;
|
||||||
|
|
||||||
|
TRY(Draw_AllocateFramebufferCacheForScreenshot(remaining));
|
||||||
|
|
||||||
u8 *framebufferCache = (u8 *)Draw_GetFramebufferCache();
|
u8 *framebufferCache = (u8 *)Draw_GetFramebufferCache();
|
||||||
u8 *framebufferCacheEnd = framebufferCache + Draw_GetFramebufferCacheSize();
|
u8 *framebufferCacheEnd = framebufferCache + Draw_GetFramebufferCacheSize();
|
||||||
|
|
||||||
u8 *buf = framebufferCache;
|
u8 *buf = framebufferCache;
|
||||||
Draw_CreateBitmapHeader(framebufferCache, dimX, 240);
|
Draw_CreateBitmapHeader(framebufferCache, width, 240);
|
||||||
buf += 54;
|
buf += 54;
|
||||||
|
|
||||||
u32 y = 0;
|
u32 y = 0;
|
||||||
@@ -318,7 +317,7 @@ static Result RosalinaMenu_WriteScreenshot(IFile *file, bool top, bool left)
|
|||||||
u32 available = (u32)(framebufferCacheEnd - buf);
|
u32 available = (u32)(framebufferCacheEnd - buf);
|
||||||
u32 size = available < remaining ? available : remaining;
|
u32 size = available < remaining ? available : remaining;
|
||||||
u32 nlines = size / lineSize;
|
u32 nlines = size / lineSize;
|
||||||
Draw_ConvertFrameBufferLines(buf, y, nlines, top, left);
|
Draw_ConvertFrameBufferLines(buf, width, y, nlines, top, left);
|
||||||
|
|
||||||
s64 t1 = svcGetSystemTick();
|
s64 t1 = svcGetSystemTick();
|
||||||
timeSpentConvertingScreenshot += t1 - t0;
|
timeSpentConvertingScreenshot += t1 - t0;
|
||||||
@@ -329,7 +328,10 @@ static Result RosalinaMenu_WriteScreenshot(IFile *file, bool top, bool left)
|
|||||||
remaining -= lineSize * nlines;
|
remaining -= lineSize * nlines;
|
||||||
buf = framebufferCache;
|
buf = framebufferCache;
|
||||||
}
|
}
|
||||||
end: return res;
|
end:
|
||||||
|
|
||||||
|
Draw_FreeFramebufferCache();
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RosalinaMenu_TakeScreenshot(void)
|
void RosalinaMenu_TakeScreenshot(void)
|
||||||
@@ -353,9 +355,16 @@ void RosalinaMenu_TakeScreenshot(void)
|
|||||||
archiveId = isSdMode ? ARCHIVE_SDMC : ARCHIVE_NAND_RW;
|
archiveId = isSdMode ? ARCHIVE_SDMC : ARCHIVE_NAND_RW;
|
||||||
Draw_Lock();
|
Draw_Lock();
|
||||||
Draw_RestoreFramebuffer();
|
Draw_RestoreFramebuffer();
|
||||||
|
Draw_FreeFramebufferCache();
|
||||||
|
|
||||||
svcFlushEntireDataCache();
|
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, ""));
|
res = FSUSER_OpenArchive(&archive, archiveId, fsMakePath(PATH_EMPTY, ""));
|
||||||
if(R_SUCCEEDED(res))
|
if(R_SUCCEEDED(res))
|
||||||
{
|
{
|
||||||
@@ -410,24 +419,28 @@ void RosalinaMenu_TakeScreenshot(void)
|
|||||||
|
|
||||||
sprintf(filename, "/luma/screenshots/%04lu-%02lu-%02lu_%02lu-%02lu-%02lu.%03llu_top.bmp", year, month, days, hours, minutes, seconds, milliseconds);
|
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));
|
TRY(IFile_Open(&file, archiveId, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, filename), FS_OPEN_CREATE | FS_OPEN_WRITE));
|
||||||
TRY(RosalinaMenu_WriteScreenshot(&file, true, true));
|
TRY(RosalinaMenu_WriteScreenshot(&file, topWidth, true, true));
|
||||||
TRY(IFile_Close(&file));
|
TRY(IFile_Close(&file));
|
||||||
|
|
||||||
sprintf(filename, "/luma/screenshots/%04lu-%02lu-%02lu_%02lu-%02lu-%02lu.%03llu_bot.bmp", year, month, days, hours, minutes, seconds, milliseconds);
|
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));
|
TRY(IFile_Open(&file, archiveId, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, filename), FS_OPEN_CREATE | FS_OPEN_WRITE));
|
||||||
TRY(RosalinaMenu_WriteScreenshot(&file, false, true));
|
TRY(RosalinaMenu_WriteScreenshot(&file, bottomWidth, false, true));
|
||||||
TRY(IFile_Close(&file));
|
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);
|
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));
|
TRY(IFile_Open(&file, archiveId, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, filename), FS_OPEN_CREATE | FS_OPEN_WRITE));
|
||||||
TRY(RosalinaMenu_WriteScreenshot(&file, true, false));
|
TRY(RosalinaMenu_WriteScreenshot(&file, topWidth, true, false));
|
||||||
TRY(IFile_Close(&file));
|
TRY(IFile_Close(&file));
|
||||||
}
|
}
|
||||||
|
|
||||||
end:
|
end:
|
||||||
IFile_Close(&file);
|
IFile_Close(&file);
|
||||||
|
|
||||||
|
if (R_FAILED(Draw_AllocateFramebufferCache(FB_BOTTOM_SIZE)))
|
||||||
|
__builtin_trap(); // We're f***ed if this happens
|
||||||
|
|
||||||
svcFlushEntireDataCache();
|
svcFlushEntireDataCache();
|
||||||
Draw_SetupFramebuffer();
|
Draw_SetupFramebuffer();
|
||||||
Draw_Unlock();
|
Draw_Unlock();
|
||||||
|
|||||||
@@ -102,7 +102,6 @@ Result debuggerDisable(s64 timeout)
|
|||||||
svcCloseHandle(dummy);
|
svcCloseHandle(dummy);
|
||||||
PMDBG_DebugNextApplicationByForce(false);
|
PMDBG_DebugNextApplicationByForce(false);
|
||||||
nextApplicationGdbCtx = NULL;
|
nextApplicationGdbCtx = NULL;
|
||||||
svcKernelSetState(0x10000, 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "draw.h"
|
#include "draw.h"
|
||||||
#include "hbloader.h"
|
#include "hbloader.h"
|
||||||
#include "plgloader.h"
|
|
||||||
#include "fmt.h"
|
#include "fmt.h"
|
||||||
#include "utils.h" // for makeArmBranch
|
#include "utils.h" // for makeArmBranch
|
||||||
#include "minisoc.h"
|
#include "minisoc.h"
|
||||||
@@ -55,7 +54,7 @@ void MiscellaneousMenu_SwitchBoot3dsxTargetTitle(void)
|
|||||||
Result res;
|
Result res;
|
||||||
char failureReason[64];
|
char failureReason[64];
|
||||||
|
|
||||||
if(HBLDR_3DSX_TID == HBLDR_DEFAULT_3DSX_TID)
|
if(Luma_SharedConfig->hbldr_3dsx_tid == HBLDR_DEFAULT_3DSX_TID)
|
||||||
{
|
{
|
||||||
FS_ProgramInfo progInfo;
|
FS_ProgramInfo progInfo;
|
||||||
u32 pid;
|
u32 pid;
|
||||||
@@ -63,7 +62,7 @@ void MiscellaneousMenu_SwitchBoot3dsxTargetTitle(void)
|
|||||||
res = PMDBG_GetCurrentAppInfo(&progInfo, &pid, &launchFlags);
|
res = PMDBG_GetCurrentAppInfo(&progInfo, &pid, &launchFlags);
|
||||||
if(R_SUCCEEDED(res))
|
if(R_SUCCEEDED(res))
|
||||||
{
|
{
|
||||||
HBLDR_3DSX_TID = progInfo.programId;
|
Luma_SharedConfig->hbldr_3dsx_tid = progInfo.programId;
|
||||||
miscellaneousMenu.items[0].title = "Switch the hb. title to hblauncher_loader";
|
miscellaneousMenu.items[0].title = "Switch the hb. title to hblauncher_loader";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -75,7 +74,7 @@ void MiscellaneousMenu_SwitchBoot3dsxTargetTitle(void)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
res = 0;
|
res = 0;
|
||||||
HBLDR_3DSX_TID = HBLDR_DEFAULT_3DSX_TID;
|
Luma_SharedConfig->hbldr_3dsx_tid = HBLDR_DEFAULT_3DSX_TID;
|
||||||
miscellaneousMenu.items[0].title = "Switch the hb. title to the current app.";
|
miscellaneousMenu.items[0].title = "Switch the hb. title to the current app.";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +164,7 @@ void MiscellaneousMenu_ChangeMenuCombo(void)
|
|||||||
while(!(waitInput() & KEY_B) && !menuShouldExit);
|
while(!(waitInput() & KEY_B) && !menuShouldExit);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result SaveSettings(void)
|
void MiscellaneousMenu_SaveSettings(void)
|
||||||
{
|
{
|
||||||
Result res;
|
Result res;
|
||||||
|
|
||||||
@@ -180,14 +179,12 @@ Result SaveSettings(void)
|
|||||||
u32 config, multiConfig, bootConfig;
|
u32 config, multiConfig, bootConfig;
|
||||||
u64 hbldr3dsxTitleId;
|
u64 hbldr3dsxTitleId;
|
||||||
u32 rosalinaMenuCombo;
|
u32 rosalinaMenuCombo;
|
||||||
u32 rosalinaFlags;
|
|
||||||
} configData;
|
} configData;
|
||||||
|
|
||||||
u32 formatVersion;
|
u32 formatVersion;
|
||||||
u32 config, multiConfig, bootConfig;
|
u32 config, multiConfig, bootConfig;
|
||||||
s64 out;
|
s64 out;
|
||||||
bool isSdMode;
|
bool isSdMode;
|
||||||
|
|
||||||
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 2))) svcBreak(USERBREAK_ASSERT);
|
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 2))) svcBreak(USERBREAK_ASSERT);
|
||||||
formatVersion = (u32)out;
|
formatVersion = (u32)out;
|
||||||
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 3))) svcBreak(USERBREAK_ASSERT);
|
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 3))) svcBreak(USERBREAK_ASSERT);
|
||||||
@@ -205,9 +202,8 @@ Result SaveSettings(void)
|
|||||||
configData.config = config;
|
configData.config = config;
|
||||||
configData.multiConfig = multiConfig;
|
configData.multiConfig = multiConfig;
|
||||||
configData.bootConfig = bootConfig;
|
configData.bootConfig = bootConfig;
|
||||||
configData.hbldr3dsxTitleId = HBLDR_3DSX_TID;
|
configData.hbldr3dsxTitleId = Luma_SharedConfig->hbldr_3dsx_tid;
|
||||||
configData.rosalinaMenuCombo = menuCombo;
|
configData.rosalinaMenuCombo = menuCombo;
|
||||||
configData.rosalinaFlags = PluginLoader__IsEnabled();
|
|
||||||
|
|
||||||
FS_ArchiveID archiveId = isSdMode ? ARCHIVE_SDMC : ARCHIVE_NAND_RW;
|
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);
|
res = IFile_Open(&file, archiveId, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, "/luma/config.bin"), FS_OPEN_CREATE | FS_OPEN_WRITE);
|
||||||
@@ -215,14 +211,6 @@ Result SaveSettings(void)
|
|||||||
if(R_SUCCEEDED(res))
|
if(R_SUCCEEDED(res))
|
||||||
res = IFile_Write(&file, &total, &configData, sizeof(configData), 0);
|
res = IFile_Write(&file, &total, &configData, sizeof(configData), 0);
|
||||||
|
|
||||||
IFile_Close(&file);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MiscellaneousMenu_SaveSettings(void)
|
|
||||||
{
|
|
||||||
Result res = SaveSettings();
|
|
||||||
|
|
||||||
Draw_Lock();
|
Draw_Lock();
|
||||||
Draw_ClearFramebuffer();
|
Draw_ClearFramebuffer();
|
||||||
Draw_FlushFramebuffer();
|
Draw_FlushFramebuffer();
|
||||||
@@ -331,7 +319,21 @@ void MiscellaneousMenu_InputRedirection(void)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(res == 0)
|
if(res == 0)
|
||||||
Draw_DrawString(10, 30, COLOR_WHITE, "InputRedirection stopped successfully.");
|
{
|
||||||
|
u32 posY = 30;
|
||||||
|
posY = Draw_DrawString(10, posY, COLOR_WHITE, "InputRedirection stopped successfully.\n\n");
|
||||||
|
if (isN3DS)
|
||||||
|
{
|
||||||
|
posY = Draw_DrawString(
|
||||||
|
10,
|
||||||
|
posY,
|
||||||
|
COLOR_WHITE,
|
||||||
|
"This might cause a key press to be repeated in\n"
|
||||||
|
"Home Menu for no reason.\n\n"
|
||||||
|
"Just pressing ZL/ZR on the console is enough to fix\nthis.\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
Draw_DrawString(10, 30, COLOR_WHITE, buf);
|
Draw_DrawString(10, 30, COLOR_WHITE, buf);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -233,8 +233,8 @@ static void ProcessListMenu_MemoryViewer(const ProcessInfo *info)
|
|||||||
svcQueryProcessMemory(&mem, &out, processHandle, heapStartAddress);
|
svcQueryProcessMemory(&mem, &out, processHandle, heapStartAddress);
|
||||||
heapTotalSize = mem.size;
|
heapTotalSize = mem.size;
|
||||||
|
|
||||||
Result codeRes = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, codeDestAddress, processHandle, codeStartAddress, codeTotalSize);
|
Result codeRes = svcMapProcessMemoryEx(processHandle, codeDestAddress, codeStartAddress, codeTotalSize);
|
||||||
Result heapRes = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, heapDestAddress, processHandle, heapStartAddress, heapTotalSize);
|
Result heapRes = svcMapProcessMemoryEx(processHandle, heapDestAddress, heapStartAddress, heapTotalSize);
|
||||||
|
|
||||||
bool codeAvailable = R_SUCCEEDED(codeRes);
|
bool codeAvailable = R_SUCCEEDED(codeRes);
|
||||||
bool heapAvailable = R_SUCCEEDED(heapRes);
|
bool heapAvailable = R_SUCCEEDED(heapRes);
|
||||||
@@ -575,9 +575,9 @@ static void ProcessListMenu_MemoryViewer(const ProcessInfo *info)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(codeAvailable)
|
if(codeAvailable)
|
||||||
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, codeDestAddress, codeTotalSize);
|
svcUnmapProcessMemoryEx(processHandle, codeDestAddress, codeTotalSize);
|
||||||
if(heapAvailable)
|
if(heapAvailable)
|
||||||
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, heapDestAddress, heapTotalSize);
|
svcUnmapProcessMemoryEx(processHandle, heapDestAddress, heapTotalSize);
|
||||||
|
|
||||||
svcCloseHandle(processHandle);
|
svcCloseHandle(processHandle);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,33 +32,34 @@
|
|||||||
#include "redshift/redshift.h"
|
#include "redshift/redshift.h"
|
||||||
#include "redshift/colorramp.h"
|
#include "redshift/colorramp.h"
|
||||||
|
|
||||||
#define TEMP_DEFAULT NEUTRAL_TEMP
|
typedef union {
|
||||||
|
struct {
|
||||||
int screenFiltersCurrentTemperature = TEMP_DEFAULT;
|
u8 r;
|
||||||
|
u8 g;
|
||||||
void writeLut(u32* lut)
|
u8 b;
|
||||||
{
|
u8 z;
|
||||||
GPU_FB_TOP_COL_LUT_INDEX = 0;
|
};
|
||||||
GPU_FB_BOTTOM_COL_LUT_INDEX = 0;
|
u32 raw;
|
||||||
|
|
||||||
for (int i = 0; i <= 255; i++) {
|
|
||||||
GPU_FB_TOP_COL_LUT_ELEM = *lut;
|
|
||||||
GPU_FB_BOTTOM_COL_LUT_ELEM = *lut;
|
|
||||||
lut++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
u8 r;
|
|
||||||
u8 g;
|
|
||||||
u8 b;
|
|
||||||
u8 z;
|
|
||||||
} Pixel;
|
} Pixel;
|
||||||
|
|
||||||
static u16 g_c[0x600];
|
static u16 g_c[0x600];
|
||||||
static Pixel g_px[0x400];
|
static Pixel g_px[0x400];
|
||||||
|
|
||||||
void applyColorSettings(color_setting_t* cs)
|
int screenFiltersCurrentTemperature = -1;
|
||||||
|
|
||||||
|
static void ScreenFiltersMenu_WriteLut(const Pixel* lut)
|
||||||
|
{
|
||||||
|
GPU_FB_TOP_COL_LUT_INDEX = 0;
|
||||||
|
GPU_FB_BOTTOM_COL_LUT_INDEX = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i <= 255; i++) {
|
||||||
|
GPU_FB_TOP_COL_LUT_ELEM = lut->raw;
|
||||||
|
GPU_FB_BOTTOM_COL_LUT_ELEM = lut->raw;
|
||||||
|
lut++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ScreenFiltersMenu_ApplyColorSettings(color_setting_t* cs)
|
||||||
{
|
{
|
||||||
u8 i = 0;
|
u8 i = 0;
|
||||||
|
|
||||||
@@ -86,68 +87,69 @@ void applyColorSettings(color_setting_t* cs)
|
|||||||
g_px[i].b = *(g_c + i + 0x200) >> 8;
|
g_px[i].b = *(g_c + i + 0x200) >> 8;
|
||||||
} while(++i);
|
} while(++i);
|
||||||
|
|
||||||
writeLut((u32*)g_px);
|
ScreenFiltersMenu_WriteLut(g_px);
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu screenFiltersMenu = {
|
static void ScreenFiltersMenu_SetCct(int cct)
|
||||||
"Screen filters menu",
|
|
||||||
{
|
|
||||||
{ "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)
|
|
||||||
{
|
{
|
||||||
color_setting_t cs;
|
color_setting_t cs;
|
||||||
memset(&cs, 0, sizeof(cs));
|
memset(&cs, 0, sizeof(cs));
|
||||||
|
|
||||||
cs.temperature = temperature;
|
cs.temperature = cct;
|
||||||
/*cs.gamma[0] = 1.0F;
|
/*cs.gamma[0] = 1.0F;
|
||||||
cs.gamma[1] = 1.0F;
|
cs.gamma[1] = 1.0F;
|
||||||
cs.gamma[2] = 1.0F;
|
cs.gamma[2] = 1.0F;
|
||||||
cs.brightness = 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);\
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenFiltersMenu_RestoreCct(void)
|
||||||
|
{
|
||||||
|
// Not initialized: return
|
||||||
|
if (screenFiltersCurrentTemperature == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Wait for GSP to restore the CCT table
|
||||||
|
while (GPU_FB_TOP_COL_LUT_ELEM != g_px[0].raw)
|
||||||
|
svcSleepThread(10 * 1000 * 1000LL);
|
||||||
|
|
||||||
|
svcSleepThread(10 * 1000 * 1000LL);
|
||||||
|
ScreenFiltersMenu_WriteLut(g_px);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|||||||
@@ -35,10 +35,10 @@
|
|||||||
Menu sysconfigMenu = {
|
Menu sysconfigMenu = {
|
||||||
"System configuration menu",
|
"System configuration menu",
|
||||||
{
|
{
|
||||||
|
{ "Control Wireless connection", METHOD, .method = &SysConfigMenu_ControlWifi },
|
||||||
{ "Toggle LEDs", METHOD, .method = &SysConfigMenu_ToggleLEDs },
|
{ "Toggle LEDs", METHOD, .method = &SysConfigMenu_ToggleLEDs },
|
||||||
{ "Toggle Wireless", METHOD, .method = &SysConfigMenu_ToggleWireless },
|
{ "Toggle Wireless", METHOD, .method = &SysConfigMenu_ToggleWireless },
|
||||||
{ "Toggle Power Button", METHOD, .method=&SysConfigMenu_TogglePowerButton },
|
{ "Toggle Power Button", METHOD, .method=&SysConfigMenu_TogglePowerButton },
|
||||||
{ "Control Wireless connection", METHOD, .method = &SysConfigMenu_ControlWifi },
|
|
||||||
{},
|
{},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* This file is part of Luma3DS.
|
* This file is part of Luma3DS.
|
||||||
* Copyright (C) 2016-2020 Aurora Wright, TuxSH
|
* Copyright (C) 2016-2020 Aurora Wright, TuxSH
|
||||||
@@ -7,18 +8,16 @@
|
|||||||
|
|
||||||
#include "minisoc.h"
|
#include "minisoc.h"
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <3ds/ipc.h>
|
#include <3ds.h>
|
||||||
#include <3ds/os.h>
|
|
||||||
#include <3ds/synchronization.h>
|
|
||||||
#include <3ds/result.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "csvc.h"
|
#include "utils.h"
|
||||||
|
|
||||||
s32 miniSocRefCount = 0;
|
s32 miniSocRefCount = 0;
|
||||||
static u32 socContextAddr = 0x08000000;
|
static u32 socContextAddr = 0x08000000;
|
||||||
static u32 socContextSize = 0x60000;
|
static u32 socContextSize = 0x60000;
|
||||||
static Handle miniSocHandle;
|
static Handle miniSocHandle;
|
||||||
static Handle miniSocMemHandle;
|
static Handle miniSocMemHandle;
|
||||||
|
static bool exclusiveStateEntered = false;
|
||||||
|
|
||||||
bool miniSocEnabled = false;
|
bool miniSocEnabled = false;
|
||||||
|
|
||||||
@@ -56,6 +55,41 @@ static Result SOCU_Shutdown(void)
|
|||||||
return cmdbuf[1];
|
return cmdbuf[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// unsafe but what can I do?
|
||||||
|
void miniSocLockState(void)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
__dmb();
|
||||||
|
if (!exclusiveStateEntered && isServiceUsable("ndm:u"))
|
||||||
|
{
|
||||||
|
ndmuInit();
|
||||||
|
res = NDMU_EnterExclusiveState(NDM_EXCLUSIVE_STATE_INFRASTRUCTURE);
|
||||||
|
if (R_SUCCEEDED(res))
|
||||||
|
res = NDMU_LockState(); // prevents ndm from switching to StreetPass when the lid is closed
|
||||||
|
exclusiveStateEntered = R_SUCCEEDED(res);
|
||||||
|
__dmb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void miniSocUnlockState(bool force)
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
|
||||||
|
__dmb();
|
||||||
|
if (exclusiveStateEntered)
|
||||||
|
{
|
||||||
|
if (!force)
|
||||||
|
{
|
||||||
|
res = NDMU_UnlockState();
|
||||||
|
if (R_SUCCEEDED(res))
|
||||||
|
res = NDMU_LeaveExclusiveState();
|
||||||
|
}
|
||||||
|
ndmuExit();
|
||||||
|
exclusiveStateEntered = R_FAILED(res);
|
||||||
|
__dmb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Result miniSocInit(void)
|
Result miniSocInit(void)
|
||||||
{
|
{
|
||||||
if(AtomicPostIncrement(&miniSocRefCount))
|
if(AtomicPostIncrement(&miniSocRefCount))
|
||||||
@@ -89,8 +123,11 @@ Result miniSocInit(void)
|
|||||||
ret = SOCU_Initialize(miniSocMemHandle, socContextSize);
|
ret = SOCU_Initialize(miniSocMemHandle, socContextSize);
|
||||||
if(ret != 0) goto cleanup;
|
if(ret != 0) goto cleanup;
|
||||||
|
|
||||||
svcKernelSetState(0x10000, 2);
|
miniSocLockState();
|
||||||
|
|
||||||
|
svcKernelSetState(0x10000, 0x10);
|
||||||
miniSocEnabled = true;
|
miniSocEnabled = true;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
@@ -117,7 +154,6 @@ cleanup:
|
|||||||
|
|
||||||
Result miniSocExitDirect(void)
|
Result miniSocExitDirect(void)
|
||||||
{
|
{
|
||||||
//if (miniSocRefCount != 0) __builtin_trap();
|
|
||||||
Result ret = 0;
|
Result ret = 0;
|
||||||
u32 tmp;
|
u32 tmp;
|
||||||
|
|
||||||
@@ -132,8 +168,10 @@ Result miniSocExitDirect(void)
|
|||||||
svcControlMemory(&tmp, socContextAddr, socContextAddr, socContextSize, MEMOP_FREE, MEMPERM_DONTCARE);
|
svcControlMemory(&tmp, socContextAddr, socContextAddr, socContextSize, MEMOP_FREE, MEMPERM_DONTCARE);
|
||||||
if(ret == 0)
|
if(ret == 0)
|
||||||
{
|
{
|
||||||
svcKernelSetState(0x10000, 2);
|
miniSocUnlockState(false);
|
||||||
|
|
||||||
miniSocEnabled = false;
|
miniSocEnabled = false;
|
||||||
|
svcKernelSetState(0x10000, 0x10);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,143 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
||||||
@@ -1,599 +0,0 @@
|
|||||||
#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);
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
.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
|
|
||||||
@@ -68,11 +68,11 @@ Result PatchProcessByName(const char *name, Result (*func)(u32 size))
|
|||||||
s64 textTotalRoundedSize = 0, startAddress = 0;
|
s64 textTotalRoundedSize = 0, startAddress = 0;
|
||||||
svcGetProcessInfo(&textTotalRoundedSize, processHandle, 0x10002); // only patch .text
|
svcGetProcessInfo(&textTotalRoundedSize, processHandle, 0x10002); // only patch .text
|
||||||
svcGetProcessInfo(&startAddress, processHandle, 0x10005);
|
svcGetProcessInfo(&startAddress, processHandle, 0x10005);
|
||||||
if(R_FAILED(res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, processHandle, (u32) startAddress, textTotalRoundedSize)))
|
if(R_FAILED(res = svcMapProcessMemoryEx(processHandle, 0x00100000, (u32) startAddress, textTotalRoundedSize)))
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
res = func(textTotalRoundedSize);
|
res = func(textTotalRoundedSize);
|
||||||
|
|
||||||
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, textTotalRoundedSize);
|
svcUnmapProcessMemoryEx(processHandle, 0x00100000, textTotalRoundedSize);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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,12 +9,10 @@
|
|||||||
#include <3ds/result.h>
|
#include <3ds/result.h>
|
||||||
#include <3ds/svc.h>
|
#include <3ds/svc.h>
|
||||||
#include <3ds/synchronization.h>
|
#include <3ds/synchronization.h>
|
||||||
#include <3ds/services/ac.h>
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "minisoc.h"
|
#include "minisoc.h"
|
||||||
#include "sock_util.h"
|
#include "sock_util.h"
|
||||||
#include "sleep.h"
|
|
||||||
|
|
||||||
extern Handle preTerminationEvent;
|
extern Handle preTerminationEvent;
|
||||||
extern bool preTerminationRequested;
|
extern bool preTerminationRequested;
|
||||||
@@ -187,14 +185,6 @@ void server_run(struct sock_server *serv)
|
|||||||
|
|
||||||
for(nfds_t i = 0; i < serv->nfds; i++)
|
for(nfds_t i = 0; i < serv->nfds; i++)
|
||||||
fds[i].revents = 0;
|
fds[i].revents = 0;
|
||||||
|
|
||||||
if (Sleep__Status())
|
|
||||||
{
|
|
||||||
while (!Wifi__IsConnected()
|
|
||||||
&& serv->running && !preTerminationRequested)
|
|
||||||
svcSleepThread(1000000000ULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
int pollres = socPoll(fds, serv->nfds, 50);
|
int pollres = socPoll(fds, serv->nfds, 50);
|
||||||
|
|
||||||
if(server_should_exit(serv) || pollres < -10000)
|
if(server_should_exit(serv) || pollres < -10000)
|
||||||
@@ -331,13 +321,3 @@ void server_finalize(struct sock_server *serv)
|
|||||||
svcClearEvent(serv->started_event);
|
svcClearEvent(serv->started_event);
|
||||||
svcCloseHandle(serv->started_event);
|
svcCloseHandle(serv->started_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Wifi__IsConnected(void)
|
|
||||||
{
|
|
||||||
u32 status = 0;
|
|
||||||
u32 wifistatus = 0;
|
|
||||||
|
|
||||||
acInit();
|
|
||||||
return R_SUCCEEDED(ACU_GetWifiStatus(&wifistatus)) && wifistatus > 0
|
|
||||||
&& R_SUCCEEDED(ACU_GetStatus(&status)) && status != 1;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ Open source replacement of the Arm11 SM system module.
|
|||||||
This is licensed under the MIT license.
|
This is licensed under the MIT license.
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
To run this system module, use a recent release or commit of [Luma3DS](https://github.com/AuroraWright/Luma3DS/), build this project and copy sm.cxi to /luma/sysmodules/.
|
To run this system module, use a recent release or commit of [Luma3DS](https://github.com/LumaTeam/Luma3DS/), build this project and copy sm.cxi to /luma/sysmodules/.
|
||||||
|
|
||||||
# Credits
|
# Credits
|
||||||
Everyone that helped me fix some of stupid bugs I had been making: @fincs, @Hikari-chin, etc.
|
Everyone that helped me fix some of stupid bugs I had been making: @fincs, @Hikari-chin, etc.
|
||||||
|
|||||||
@@ -8,6 +8,18 @@ This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for
|
|||||||
#include "notifications.h"
|
#include "notifications.h"
|
||||||
#include "processes.h"
|
#include "processes.h"
|
||||||
|
|
||||||
|
#include <stdatomic.h>
|
||||||
|
|
||||||
|
static bool isNotificationInhibited(const ProcessData *processData, u32 notificationId)
|
||||||
|
{
|
||||||
|
(void)processData;
|
||||||
|
switch(notificationId)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool doPublishNotification(ProcessData *processData, u32 notificationId, u32 flags)
|
static bool doPublishNotification(ProcessData *processData, u32 notificationId, u32 flags)
|
||||||
{
|
{
|
||||||
if((flags & 1) && processData->nbPendingNotifications != 0) // only send if not already pending
|
if((flags & 1) && processData->nbPendingNotifications != 0) // only send if not already pending
|
||||||
@@ -19,14 +31,6 @@ 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)
|
if(processData->nbPendingNotifications < 0x10)
|
||||||
{
|
{
|
||||||
s32 count;
|
s32 count;
|
||||||
@@ -126,7 +130,7 @@ Result PublishToSubscriber(u32 notificationId, u32 flags)
|
|||||||
{
|
{
|
||||||
for(ProcessData *node = processDataInUseList.first; node != NULL; node = node->next)
|
for(ProcessData *node = processDataInUseList.first; node != NULL; node = node->next)
|
||||||
{
|
{
|
||||||
if(!node->notificationEnabled)
|
if(!node->notificationEnabled || isNotificationInhibited(node, notificationId))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
u16 i;
|
u16 i;
|
||||||
@@ -146,7 +150,7 @@ Result PublishAndGetSubscriber(u32 *pidCount, u32 *pidList, u32 notificationId,
|
|||||||
u32 nb = 0;
|
u32 nb = 0;
|
||||||
for(ProcessData *node = processDataInUseList.first; node != NULL; node = node->next)
|
for(ProcessData *node = processDataInUseList.first; node != NULL; node = node->next)
|
||||||
{
|
{
|
||||||
if(!node->notificationEnabled)
|
if(!node->notificationEnabled || isNotificationInhibited(node, notificationId))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
u16 i;
|
u16 i;
|
||||||
|
|||||||
@@ -17,3 +17,5 @@ Result PublishToSubscriber(u32 notificationId, u32 flags);
|
|||||||
Result PublishAndGetSubscriber(u32 *pidCount, u32 *pidList, u32 notificationId, u32 flags);
|
Result PublishAndGetSubscriber(u32 *pidCount, u32 *pidList, u32 notificationId, u32 flags);
|
||||||
Result PublishToProcess(Handle process, u32 notificationId);
|
Result PublishToProcess(Handle process, u32 notificationId);
|
||||||
Result PublishToAll(u32 notificationId);
|
Result PublishToAll(u32 notificationId);
|
||||||
|
|
||||||
|
Result AddToNdmuWorkaroundCount(s32 count);
|
||||||
|
|||||||
Reference in New Issue
Block a user