Compare commits
90 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e6ff1f327 | ||
|
|
ea49c2ccd1 | ||
|
|
d484872a73 | ||
|
|
6ee525201c | ||
|
|
5ac7ae7edb | ||
|
|
0c590eb768 | ||
|
|
bd40d4f237 | ||
|
|
788436b4a3 | ||
|
|
2bb77237bc | ||
|
|
33827fe3a3 | ||
|
|
48b0b2fc46 | ||
|
|
4f29eedfe9 | ||
|
|
66372e2b2e | ||
|
|
2bae1ad116 | ||
|
|
bbdc643b6d | ||
|
|
a66fcde0ee | ||
|
|
7835486a4d | ||
|
|
90367aea0d | ||
|
|
16c638756c | ||
|
|
b08a97d883 | ||
|
|
746dbfe018 | ||
|
|
733f2b3cdd | ||
|
|
ad64cb5212 | ||
|
|
b1a9e8d0df | ||
|
|
4f9838df3c | ||
|
|
f4ca2c02a7 | ||
|
|
e1391d4162 | ||
|
|
8d9c51f204 | ||
|
|
c10ba67973 | ||
|
|
55610694c8 | ||
|
|
8764d94fd9 | ||
|
|
5228768841 | ||
|
|
9c68bea16c | ||
|
|
5484740cab | ||
|
|
421324b498 | ||
|
|
37e065fa2d | ||
|
|
b758e3fc55 | ||
|
|
393596ef9a | ||
|
|
02d4c97c6d | ||
|
|
2c5ef434f0 | ||
|
|
122f3e4403 | ||
|
|
ea3ebbaa7d | ||
|
|
1636668762 | ||
|
|
c6b9a0c4bf | ||
|
|
93a218abeb | ||
|
|
d7a06057eb | ||
|
|
8cb77ac136 | ||
|
|
4059dc6187 | ||
|
|
15773e4755 | ||
|
|
4f455dacf4 | ||
|
|
0b22af1206 | ||
|
|
ae2fa2fa60 | ||
|
|
6abd756e0c | ||
|
|
535e49a38d | ||
|
|
fbb5730369 | ||
|
|
e5d62025d3 | ||
|
|
89c6fc6437 | ||
|
|
799c158b86 | ||
|
|
8d16d2152b | ||
|
|
635ae4e3da | ||
|
|
59140d8dfa | ||
|
|
2cb8aadafc | ||
|
|
aa0826bb70 | ||
|
|
8bd2a9a23b | ||
|
|
2f959785e1 | ||
|
|
88a86a3363 | ||
|
|
0c7827104f | ||
|
|
c8ed190e5c | ||
|
|
f4dcd1db9b | ||
|
|
bd341d5c00 | ||
|
|
add18d868f | ||
|
|
609a302e16 | ||
|
|
e07011be32 | ||
|
|
bb223eb5ae | ||
|
|
73d904036d | ||
|
|
542833866e | ||
|
|
89c484414b | ||
|
|
fe2dd671fb | ||
|
|
8abee1bdaa | ||
|
|
78a730ddf6 | ||
|
|
a750e55f75 | ||
|
|
c62c4846fc | ||
|
|
8db5b01507 | ||
|
|
a6e405c988 | ||
|
|
6613fda4b1 | ||
|
|
a18a6e87df | ||
|
|
93d83c5bb9 | ||
|
|
6ee8720028 | ||
|
|
600d68bd1a | ||
|
|
0c3a294cbe |
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
common/defaults/hbl_html/accessible-urls/accessible-urls.txt text eol=lf
|
||||||
38
Makefile
38
Makefile
@@ -50,12 +50,15 @@ dist: all
|
|||||||
mkdir atmosphere-$(AMSVER)/atmosphere
|
mkdir atmosphere-$(AMSVER)/atmosphere
|
||||||
mkdir atmosphere-$(AMSVER)/sept
|
mkdir atmosphere-$(AMSVER)/sept
|
||||||
mkdir atmosphere-$(AMSVER)/switch
|
mkdir atmosphere-$(AMSVER)/switch
|
||||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/010000000000000D
|
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000008
|
||||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032
|
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/010000000000000D
|
||||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034
|
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000032
|
||||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036
|
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000034
|
||||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037
|
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000036
|
||||||
|
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000037
|
||||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/fatal_errors
|
mkdir -p atmosphere-$(AMSVER)/atmosphere/fatal_errors
|
||||||
|
mkdir -p atmosphere-$(AMSVER)/atmosphere/config_templates
|
||||||
|
mkdir -p atmosphere-$(AMSVER)/atmosphere/config
|
||||||
cp fusee/fusee-primary/fusee-primary.bin atmosphere-$(AMSVER)/atmosphere/reboot_payload.bin
|
cp fusee/fusee-primary/fusee-primary.bin atmosphere-$(AMSVER)/atmosphere/reboot_payload.bin
|
||||||
cp fusee/fusee-mtc/fusee-mtc.bin atmosphere-$(AMSVER)/atmosphere/fusee-mtc.bin
|
cp fusee/fusee-mtc/fusee-mtc.bin atmosphere-$(AMSVER)/atmosphere/fusee-mtc.bin
|
||||||
cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/atmosphere/fusee-secondary.bin
|
cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/atmosphere/fusee-secondary.bin
|
||||||
@@ -64,20 +67,21 @@ dist: all
|
|||||||
cp sept/sept-secondary/sept-secondary.bin atmosphere-$(AMSVER)/sept/sept-secondary.bin
|
cp sept/sept-secondary/sept-secondary.bin atmosphere-$(AMSVER)/sept/sept-secondary.bin
|
||||||
cp sept/sept-secondary/sept-secondary_00.enc atmosphere-$(AMSVER)/sept/sept-secondary_00.enc
|
cp sept/sept-secondary/sept-secondary_00.enc atmosphere-$(AMSVER)/sept/sept-secondary_00.enc
|
||||||
cp sept/sept-secondary/sept-secondary_01.enc atmosphere-$(AMSVER)/sept/sept-secondary_01.enc
|
cp sept/sept-secondary/sept-secondary_01.enc atmosphere-$(AMSVER)/sept/sept-secondary_01.enc
|
||||||
cp common/defaults/BCT.ini atmosphere-$(AMSVER)/atmosphere/BCT.ini
|
cp common/defaults/BCT.ini atmosphere-$(AMSVER)/atmosphere/config/BCT.ini
|
||||||
cp common/defaults/loader.ini atmosphere-$(AMSVER)/atmosphere/loader.ini
|
cp common/defaults/override_config.ini atmosphere-$(AMSVER)/atmosphere/config_templates/override_config.ini
|
||||||
cp common/defaults/system_settings.ini atmosphere-$(AMSVER)/atmosphere/system_settings.ini
|
cp common/defaults/system_settings.ini atmosphere-$(AMSVER)/atmosphere/config_templates/system_settings.ini
|
||||||
cp -r common/defaults/kip_patches atmosphere-$(AMSVER)/atmosphere/kip_patches
|
cp -r common/defaults/kip_patches atmosphere-$(AMSVER)/atmosphere/kip_patches
|
||||||
cp -r common/defaults/hbl_html atmosphere-$(AMSVER)/atmosphere/hbl_html
|
cp -r common/defaults/hbl_html atmosphere-$(AMSVER)/atmosphere/hbl_html
|
||||||
cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/atmosphere/titles/010000000000000D/exefs.nsp
|
cp stratosphere/boot2/boot2.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000008/exefs.nsp
|
||||||
cp stratosphere/eclct.stub/eclct.stub.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/exefs.nsp
|
cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/atmosphere/contents/010000000000000D/exefs.nsp
|
||||||
cp stratosphere/fatal/fatal.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034/exefs.nsp
|
cp stratosphere/eclct.stub/eclct.stub.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000032/exefs.nsp
|
||||||
cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036/exefs.nsp
|
cp stratosphere/fatal/fatal.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000034/exefs.nsp
|
||||||
cp stratosphere/ro/ro.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037/exefs.nsp
|
cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000036/exefs.nsp
|
||||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/flags
|
cp stratosphere/ro/ro.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000037/exefs.nsp
|
||||||
touch atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/flags/boot2.flag
|
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000032/flags
|
||||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037/flags
|
touch atmosphere-$(AMSVER)/atmosphere/contents/0100000000000032/flags/boot2.flag
|
||||||
touch atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037/flags/boot2.flag
|
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000037/flags
|
||||||
|
touch atmosphere-$(AMSVER)/atmosphere/contents/0100000000000037/flags/boot2.flag
|
||||||
cp troposphere/reboot_to_payload/reboot_to_payload.nro atmosphere-$(AMSVER)/switch/reboot_to_payload.nro
|
cp troposphere/reboot_to_payload/reboot_to_payload.nro atmosphere-$(AMSVER)/switch/reboot_to_payload.nro
|
||||||
cd atmosphere-$(AMSVER); zip -r ../atmosphere-$(AMSVER).zip ./*; cd ../;
|
cd atmosphere-$(AMSVER); zip -r ../atmosphere-$(AMSVER).zip ./*; cd ../;
|
||||||
rm -r atmosphere-$(AMSVER)
|
rm -r atmosphere-$(AMSVER)
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ debugmode_user = 0
|
|||||||
; Note: Disabling usermode exception handlers will cause atmosphere to not fail gracefully under error conditions.
|
; Note: Disabling usermode exception handlers will cause atmosphere to not fail gracefully under error conditions.
|
||||||
; Support will not be provided to users who disable these. If you do not know what you are doing, leave them on.
|
; Support will not be provided to users who disable these. If you do not know what you are doing, leave them on.
|
||||||
disable_user_exception_handlers = 0
|
disable_user_exception_handlers = 0
|
||||||
|
; Note: It's currently unknown what effects enabling the usermode PMU register access may have on official code.
|
||||||
|
enable_user_pmu_access = 0
|
||||||
|
|
||||||
[stratosphere]
|
[stratosphere]
|
||||||
; To force-enable nogc, add nogc = 1
|
; To force-enable nogc, add nogc = 1
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,9 +0,0 @@
|
|||||||
[hbl_config]
|
|
||||||
title_id=010000000000100D
|
|
||||||
override_any_app=true
|
|
||||||
path=atmosphere/hbl.nsp
|
|
||||||
override_key=R
|
|
||||||
|
|
||||||
[default_config]
|
|
||||||
override_key=!L
|
|
||||||
cheat_enable_key=!L
|
|
||||||
10
common/defaults/override_config.ini
Normal file
10
common/defaults/override_config.ini
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[hbl_config]
|
||||||
|
; title_id=010000000000100D
|
||||||
|
; override_any_app=true
|
||||||
|
; path=atmosphere/hbl.nsp
|
||||||
|
; override_key=!R
|
||||||
|
; override_any_app_key=R
|
||||||
|
|
||||||
|
[default_config]
|
||||||
|
; override_key=!L
|
||||||
|
; cheat_enable_key=!L
|
||||||
@@ -1,40 +1,43 @@
|
|||||||
; Disable uploading error reports to Nintendo
|
; Disable uploading error reports to Nintendo
|
||||||
[eupld]
|
[eupld]
|
||||||
upload_enabled = u8!0x0
|
; upload_enabled = u8!0x0
|
||||||
; Enable USB 3.0 superspeed for homebrew
|
|
||||||
[usb]
|
|
||||||
usb30_force_enabled = u8!0x0
|
|
||||||
; Control whether RO should ease its validation of NROs.
|
; Control whether RO should ease its validation of NROs.
|
||||||
; (note: this is normally not necessary, and ips patches can be used.)
|
; (note: this is normally not necessary, and ips patches can be used.)
|
||||||
[ro]
|
[ro]
|
||||||
ease_nro_restriction = u8!0x0
|
; ease_nro_restriction = u8!0x0
|
||||||
; Atmosphere custom settings
|
; Atmosphere custom settings
|
||||||
[atmosphere]
|
[atmosphere]
|
||||||
; Reboot from fatal automatically after some number of milliseconds.
|
; Reboot from fatal automatically after some number of milliseconds.
|
||||||
; If field is not present or 0, fatal will wait indefinitely for user input.
|
; If field is not present or 0, fatal will wait indefinitely for user input.
|
||||||
fatal_auto_reboot_interval = u64!0x0
|
; fatal_auto_reboot_interval = u64!0x0
|
||||||
; Make the power menu's "reboot" button reboot to payload.
|
; Make the power menu's "reboot" button reboot to payload.
|
||||||
; Set to "normal" for normal reboot, "rcm" for rcm reboot.
|
; Set to "normal" for normal reboot, "rcm" for rcm reboot.
|
||||||
power_menu_reboot_function = str!payload
|
; power_menu_reboot_function = str!payload
|
||||||
; Controls whether dmnt cheats should be toggled on or off by
|
; Controls whether dmnt cheats should be toggled on or off by
|
||||||
; default. 1 = toggled on by default, 0 = toggled off by default.
|
; default. 1 = toggled on by default, 0 = toggled off by default.
|
||||||
dmnt_cheats_enabled_by_default = u8!0x1
|
; dmnt_cheats_enabled_by_default = u8!0x1
|
||||||
; Controls whether dmnt should always save cheat toggle state
|
; Controls whether dmnt should always save cheat toggle state
|
||||||
; for restoration on new game launch. 1 = always save toggles,
|
; for restoration on new game launch. 1 = always save toggles,
|
||||||
; 0 = only save toggles if toggle file exists.
|
; 0 = only save toggles if toggle file exists.
|
||||||
dmnt_always_save_cheat_toggles = u8!0x0
|
; dmnt_always_save_cheat_toggles = u8!0x0
|
||||||
|
; Enable writing to BIS partitions for HBL.
|
||||||
|
; This is probably undesirable for normal usage.
|
||||||
|
; enable_hbl_bis_write = u8!0x0
|
||||||
|
; Enable reading the CAL0 partition for HBL.
|
||||||
|
; This is probably undesirable for normal usage.
|
||||||
|
; enable_hbl_cal_read = u8!0x0
|
||||||
; Controls whether fs.mitm should redirect save files
|
; Controls whether fs.mitm should redirect save files
|
||||||
; to directories on the sd card.
|
; to directories on the sd card.
|
||||||
; 0 = Do not redirect, 1 = Redirect.
|
; 0 = Do not redirect, 1 = Redirect.
|
||||||
; NOTE: EXPERIMENTAL
|
; NOTE: EXPERIMENTAL
|
||||||
; If you do not know what you are doing, do not touch this yet.
|
; If you do not know what you are doing, do not touch this yet.
|
||||||
fsmitm_redirect_saves_to_sd = u8!0x0
|
; fsmitm_redirect_saves_to_sd = u8!0x0
|
||||||
[hbloader]
|
[hbloader]
|
||||||
; Controls the size of the homebrew heap when running as applet.
|
; Controls the size of the homebrew heap when running as applet.
|
||||||
; If set to zero, all available applet memory is used as heap.
|
; If set to zero, all available applet memory is used as heap.
|
||||||
; The default is zero.
|
; The default is zero.
|
||||||
applet_heap_size = u64!0x0
|
; applet_heap_size = u64!0x0
|
||||||
; Controls the amount of memory to reserve when running as applet
|
; Controls the amount of memory to reserve when running as applet
|
||||||
; for usage by other applets. This setting has no effect if
|
; for usage by other applets. This setting has no effect if
|
||||||
; applet_heap_size is non-zero. The default is zero.
|
; applet_heap_size is non-zero. The default is 0x8600000.
|
||||||
applet_heap_reservation_size = u64!0x8000000
|
; applet_heap_reservation_size = u64!0x8600000
|
||||||
@@ -27,11 +27,13 @@
|
|||||||
#define ATMOSPHERE_TARGET_FIRMWARE_700 8
|
#define ATMOSPHERE_TARGET_FIRMWARE_700 8
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_800 9
|
#define ATMOSPHERE_TARGET_FIRMWARE_800 9
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_810 10
|
#define ATMOSPHERE_TARGET_FIRMWARE_810 10
|
||||||
|
#define ATMOSPHERE_TARGET_FIRMWARE_900 11
|
||||||
|
#define ATMOSPHERE_TARGET_FIRMWARE_910 12
|
||||||
|
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_810
|
#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_910
|
||||||
|
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_MIN ATMOSPHERE_TARGET_FIRMWARE_100
|
#define ATMOSPHERE_TARGET_FIRMWARE_MIN ATMOSPHERE_TARGET_FIRMWARE_100
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_810
|
#define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_910
|
||||||
|
|
||||||
/* TODO: What should this be, for release? */
|
/* TODO: What should this be, for release? */
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_DEFAULT_FOR_DEBUG ATMOSPHERE_TARGET_FIRMWARE_CURRENT
|
#define ATMOSPHERE_TARGET_FIRMWARE_DEFAULT_FOR_DEBUG ATMOSPHERE_TARGET_FIRMWARE_CURRENT
|
||||||
|
|||||||
@@ -18,10 +18,10 @@
|
|||||||
#define ATMOSPHERE_VERSION_H
|
#define ATMOSPHERE_VERSION_H
|
||||||
|
|
||||||
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 0
|
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 0
|
||||||
#define ATMOSPHERE_RELEASE_VERSION_MINOR 9
|
#define ATMOSPHERE_RELEASE_VERSION_MINOR 10
|
||||||
#define ATMOSPHERE_RELEASE_VERSION_MICRO 3
|
#define ATMOSPHERE_RELEASE_VERSION_MICRO 0
|
||||||
|
|
||||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 8
|
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 9
|
||||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 1
|
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 1
|
||||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 0
|
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 0
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,87 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
## 0.10.0
|
||||||
|
+ Support was added for 9.1.0
|
||||||
|
+ **Please note**: The temporary hid-mitm added in Atmosphere 0.9.0 will be removed in Atmosphere 0.10.1.
|
||||||
|
+ Please ensure your homebrew is updated.
|
||||||
|
+ The Stratosphere rewrite was completed.
|
||||||
|
+ libstratosphere was rewritten as part of Stratosphere's refactor.
|
||||||
|
+ Code responsible for providing and managing IPC services was greatly improved.
|
||||||
|
+ The new code is significantly more accurate (it is bug-for-bug compatible with Nintendo's code), and significantly faster.
|
||||||
|
+ ams.mitm was rewritten as part of Stratosphere's refactor.
|
||||||
|
+ Saves redirected to the SD card are now separated for sysmmc vs emummc.
|
||||||
|
+ **Please note**: If you find any bugs, please report them so they can be fixed.
|
||||||
|
+ Thanks to the rewrite, Atmosphere now uses significantly less memory.
|
||||||
|
+ Roughly 10 additional megabytes are now available for custom system modules to use.
|
||||||
|
+ This means you can potentially run more custom system modules simultaneously.
|
||||||
|
+ If system modules are incompatible, please ask their authors to reduce their memory footprints.
|
||||||
|
+ Atmosphere's configuration layout has had major changes.
|
||||||
|
+ Configuration now lives inside /atmosphere/config/.
|
||||||
|
+ Atmosphere code now knows what default values should be, and includes them in code.
|
||||||
|
+ It is no longer an error if configuration inis are not present.
|
||||||
|
+ Correspondingly, Atmosphere no longer distributes default configuration inis.
|
||||||
|
+ Templates are provided in /atmosphere/config_templates.
|
||||||
|
+ loader.ini was renamed to override_config.ini.
|
||||||
|
+ This fixes the longstanding problem that atmosphere updates overwrote user configuration when extracted.
|
||||||
|
+ Atmosphere's process override layout was changed.
|
||||||
|
+ Atmosphere now uses the /atmosphere/contents directory, instead of /atmosphere/titles.
|
||||||
|
+ This goes along with a refactoring to remove all reference to "title id" from code, as Nintendo does not use the term.
|
||||||
|
+ To make this transition easier, a temporary functionality has been added that migrates folders to the new directory.
|
||||||
|
+ When booting into 0.10.0, Atmosphere will rename /atmosphere/titles/<program id> to /atmosphere/contents/<program id>.
|
||||||
|
+ This functionality may or may not be removed in some future update.
|
||||||
|
+ This should solve any transition difficulties for the typical user.
|
||||||
|
+ Please make sure that any future mods you install extract to the correct directory.
|
||||||
|
+ Support for configuring override keys for hbl was improved.
|
||||||
|
+ The key used to override applications versus a specific program can now be different.
|
||||||
|
+ The key to override a specific program can be managed via override_key.
|
||||||
|
+ The key to override any app can be managed via override_any_app_key.
|
||||||
|
+ Default override behavior was changed.
|
||||||
|
+ By default, atmosphere will now override the album applet with hbl unless R is held.
|
||||||
|
+ By default, atmosphere will now override any application with hbl only if R is held.
|
||||||
|
+ The default amount of applet memory reserved has been slightly increased.
|
||||||
|
+ This allows the profile selector applet to work by default in applet mode.
|
||||||
|
+ The way process override status is captured was changed.
|
||||||
|
+ Process override keys are now captured exactly once, when the process is created.
|
||||||
|
+ This fixes the longstanding issue where letting go of the override button partway into the process launch could cause problems.
|
||||||
|
+ The Mitm API was changed to pass around override status.
|
||||||
|
+ Mitm services now know what keys were held when the client was created, as well as whether the client is HBL/should override contents.
|
||||||
|
+ An extension was added to pm:info to allow querying a process's override status.
|
||||||
|
+ Thanks to process override capture improvements, hbl html behavior has been greatly improved.
|
||||||
|
+ Web applets launched by hbl will now always see the /atmosphere/hbl_html filesystem
|
||||||
|
+ Support was added to exosphere for enabling usermode access to the PMU registers.
|
||||||
|
+ This can be controlled via exosphere!enable_user_pmu_access in BCT.ini.
|
||||||
|
+ An enormous number of minor bugs were fixed.
|
||||||
|
+ dmnt's cheat VM had a fix for an inversion in opcode behavior.
|
||||||
|
+ An issue was fixed in fs.mitm's management of domain object IDs that could lead to system corruption in rare cases.
|
||||||
|
+ The Mitm API no longer silently fails when attempting to handle commands passing C descriptors.
|
||||||
|
+ On previous atmosphere versions, certain commands to FS would silently fail due to this...
|
||||||
|
+ No users reported any visible errors, but it was definitely a problem behind the scenes.
|
||||||
|
+ These commands are now handled correctly.
|
||||||
|
+ Atmosphere can now display a fatal error screen significantly earlier in the boot process, if things go wrong early on.
|
||||||
|
+ The temporary hid mitm will no longer sometimes cause games to fail to detect input.
|
||||||
|
+ Mitm Domain object ID management no longer desynchronizes from the host process.
|
||||||
|
+ An issue was fixed that could cause service acquisition to hang forever if certain sm commands were called in a precise order.
|
||||||
|
+ An off-by-one was fixed that could cause memory corruption in server memory management.
|
||||||
|
+ ... and too many more bugs fixed to reasonably list them all :)
|
||||||
|
+ General system stability improvements to enhance the user's experience.
|
||||||
|
## 0.9.4
|
||||||
|
+ Support was added for 9.0.0.
|
||||||
|
+ **Please note**: 9.0.0 made a number of changes that may cause some issues with homebrew. Details:
|
||||||
|
+ 9.0.0 changed HID in a way that causes libnx to be unable to detect button input.
|
||||||
|
+ Homebrew should be recompiled with newest libnx to fix this.
|
||||||
|
+ Atmosphere now provides a temporary hid-mitm that will cause homebrew to continue to work as expected.
|
||||||
|
+ This mitm will be removed in a future Atmosphere revision once homebrew has been updated, to allow users to use a custom hid mitm again if they desire.
|
||||||
|
+ 9.0.0 introduced an dependency in FS on the USB system module in order to launch the SD card.
|
||||||
|
+ This means the USB system module must now be launched before the SD card is initialized.
|
||||||
|
+ Correspondingly, the USB system module can no longer be IPS patched, and its settings cannot be reliably mitm'd.
|
||||||
|
+ We know this is frustrating, so we'll be looking into whether there is some way of addressing this in the future.
|
||||||
|
+ An off-by-one error was fixed in `boot` system module's pinmux initialization.
|
||||||
|
+ This could theoretically have caused issues with HdmiCec communication.
|
||||||
|
+ No users reported issues, so it's unclear if this was a problem in practice.
|
||||||
|
+ A bug was fixed that could cause webapplet launching homebrew to improperly set the accessible url whitelist.
|
||||||
|
+ BIS key generation has been fixed for newer hardware.
|
||||||
|
+ Newer hardware uses new, per-firmware device key to generate BIS keys instead of the first device key, so previously the wrong keys were generated as backup.
|
||||||
|
+ This only affects units manufactured after ~5.0.0.
|
||||||
|
+ General system stability improvements to enhance the user's experience.
|
||||||
## 0.9.3
|
## 0.9.3
|
||||||
+ Thanks to hexkyz, fusee's boot sequence has been greatly optimized.
|
+ Thanks to hexkyz, fusee's boot sequence has been greatly optimized.
|
||||||
+ Memory training is now managed by a separate binary (`fusee-mtc`, loaded by fusee-primary before fusee-secondary).
|
+ Memory training is now managed by a separate binary (`fusee-mtc`, loaded by fusee-primary before fusee-secondary).
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
[subrepo]
|
[subrepo]
|
||||||
remote = https://github.com/m4xw/emuMMC
|
remote = https://github.com/m4xw/emuMMC
|
||||||
branch = develop
|
branch = develop
|
||||||
commit = 5f51fa3b81d2b14b348f6e8579454007019fc7a6
|
commit = bd81a674a946c30b566e1732a95c18f19b701558
|
||||||
parent = e871a754a87631c3036ca985ff1c223e00ef4dda
|
parent = 6ee525201ccef107c61d81ba73c891e3eb5f0215
|
||||||
method = rebase
|
method = rebase
|
||||||
cmdver = 0.4.0
|
cmdver = 0.4.0
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
*A SDMMC driver replacement for Nintendo's Filesystem Services, by **m4xw***
|
*A SDMMC driver replacement for Nintendo's Filesystem Services, by **m4xw***
|
||||||
|
|
||||||
### Supported Horizon Versions
|
### Supported Horizon Versions
|
||||||
**1.0.0 - 8.1.0**
|
**1.0.0 - 9.1.0**
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
* Arbitrary SDMMC backend selection
|
* Arbitrary SDMMC backend selection
|
||||||
|
|||||||
@@ -41,6 +41,10 @@
|
|||||||
#include "offsets/800_exfat.h"
|
#include "offsets/800_exfat.h"
|
||||||
#include "offsets/810.h"
|
#include "offsets/810.h"
|
||||||
#include "offsets/810_exfat.h"
|
#include "offsets/810_exfat.h"
|
||||||
|
#include "offsets/900.h"
|
||||||
|
#include "offsets/900_exfat.h"
|
||||||
|
#include "offsets/910.h"
|
||||||
|
#include "offsets/910_exfat.h"
|
||||||
#include "../utils/fatal.h"
|
#include "../utils/fatal.h"
|
||||||
|
|
||||||
#define GET_OFFSET_STRUCT_NAME(vers) g_offsets##vers
|
#define GET_OFFSET_STRUCT_NAME(vers) g_offsets##vers
|
||||||
@@ -92,6 +96,10 @@ DEFINE_OFFSET_STRUCT(_800);
|
|||||||
DEFINE_OFFSET_STRUCT(_800_EXFAT);
|
DEFINE_OFFSET_STRUCT(_800_EXFAT);
|
||||||
DEFINE_OFFSET_STRUCT(_810);
|
DEFINE_OFFSET_STRUCT(_810);
|
||||||
DEFINE_OFFSET_STRUCT(_810_EXFAT);
|
DEFINE_OFFSET_STRUCT(_810_EXFAT);
|
||||||
|
DEFINE_OFFSET_STRUCT(_900);
|
||||||
|
DEFINE_OFFSET_STRUCT(_900_EXFAT);
|
||||||
|
DEFINE_OFFSET_STRUCT(_910);
|
||||||
|
DEFINE_OFFSET_STRUCT(_910_EXFAT);
|
||||||
|
|
||||||
const fs_offsets_t *get_fs_offsets(enum FS_VER version) {
|
const fs_offsets_t *get_fs_offsets(enum FS_VER version) {
|
||||||
switch (version) {
|
switch (version) {
|
||||||
@@ -145,6 +153,14 @@ const fs_offsets_t *get_fs_offsets(enum FS_VER version) {
|
|||||||
return &(GET_OFFSET_STRUCT_NAME(_810));
|
return &(GET_OFFSET_STRUCT_NAME(_810));
|
||||||
case FS_VER_8_1_0_EXFAT:
|
case FS_VER_8_1_0_EXFAT:
|
||||||
return &(GET_OFFSET_STRUCT_NAME(_810_EXFAT));
|
return &(GET_OFFSET_STRUCT_NAME(_810_EXFAT));
|
||||||
|
case FS_VER_9_0_0:
|
||||||
|
return &(GET_OFFSET_STRUCT_NAME(_900));
|
||||||
|
case FS_VER_9_0_0_EXFAT:
|
||||||
|
return &(GET_OFFSET_STRUCT_NAME(_900_EXFAT));
|
||||||
|
case FS_VER_9_1_0:
|
||||||
|
return &(GET_OFFSET_STRUCT_NAME(_910));
|
||||||
|
case FS_VER_9_1_0_EXFAT:
|
||||||
|
return &(GET_OFFSET_STRUCT_NAME(_910_EXFAT));
|
||||||
default:
|
default:
|
||||||
fatal_abort(Fatal_UnknownVersion);
|
fatal_abort(Fatal_UnknownVersion);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,12 @@ enum FS_VER
|
|||||||
FS_VER_8_1_0,
|
FS_VER_8_1_0,
|
||||||
FS_VER_8_1_0_EXFAT,
|
FS_VER_8_1_0_EXFAT,
|
||||||
|
|
||||||
|
FS_VER_9_0_0,
|
||||||
|
FS_VER_9_0_0_EXFAT,
|
||||||
|
|
||||||
|
FS_VER_9_1_0,
|
||||||
|
FS_VER_9_1_0_EXFAT,
|
||||||
|
|
||||||
FS_VER_MAX,
|
FS_VER_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
59
emummc/source/FS/offsets/900.h
Normal file
59
emummc/source/FS/offsets/900.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
|
||||||
|
* Copyright (c) 2019 Atmosphere-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope 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/>.
|
||||||
|
*/
|
||||||
|
#ifndef __FS_900_H__
|
||||||
|
#define __FS_900_H__
|
||||||
|
|
||||||
|
// Accessor vtable getters
|
||||||
|
#define FS_OFFSET_900_SDMMC_ACCESSOR_GC 0x1430F0
|
||||||
|
#define FS_OFFSET_900_SDMMC_ACCESSOR_SD 0x141200
|
||||||
|
#define FS_OFFSET_900_SDMMC_ACCESSOR_NAND 0x13C080
|
||||||
|
|
||||||
|
// Hooks
|
||||||
|
#define FS_OFFSET_900_SDMMC_WRAPPER_READ 0x1377E0
|
||||||
|
#define FS_OFFSET_900_SDMMC_WRAPPER_WRITE 0x1378C0
|
||||||
|
#define FS_OFFSET_900_RTLD 0x454
|
||||||
|
#define FS_OFFSET_900_RTLD_DESTINATION 0x9C
|
||||||
|
|
||||||
|
#define FS_OFFSET_900_CLKRST_SET_MIN_V_CLK_RATE 0x136A00
|
||||||
|
|
||||||
|
// Misc funcs
|
||||||
|
#define FS_OFFSET_900_LOCK_MUTEX 0x25280
|
||||||
|
#define FS_OFFSET_900_UNLOCK_MUTEX 0x252D0
|
||||||
|
|
||||||
|
#define FS_OFFSET_900_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x137740
|
||||||
|
|
||||||
|
// Misc Data
|
||||||
|
#define FS_OFFSET_900_SD_MUTEX 0xE1D3E8
|
||||||
|
#define FS_OFFSET_900_NAND_MUTEX 0xE18258
|
||||||
|
#define FS_OFFSET_900_ACTIVE_PARTITION 0xE18298
|
||||||
|
#define FS_OFFSET_900_SDMMC_DAS_HANDLE 0xDFEFA0
|
||||||
|
|
||||||
|
// NOPs
|
||||||
|
#define FS_OFFSET_900_SD_DAS_INIT 0x1472BC
|
||||||
|
|
||||||
|
// Nintendo Paths
|
||||||
|
#define FS_OFFSET_900_NINTENDO_PATHS \
|
||||||
|
{ \
|
||||||
|
{.opcode_reg = 3, .adrp_offset = 0x00068A60, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 3, .adrp_offset = 0x00070A40, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 3, .adrp_offset = 0x00081CB4, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 3, .adrp_offset = 0x00081EF4, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 4, .adrp_offset = 0x0008211C, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __FS_900_H__
|
||||||
59
emummc/source/FS/offsets/900_exfat.h
Normal file
59
emummc/source/FS/offsets/900_exfat.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
|
||||||
|
* Copyright (c) 2019 Atmosphere-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope 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/>.
|
||||||
|
*/
|
||||||
|
#ifndef __FS_900_EXFAT_H__
|
||||||
|
#define __FS_900_EXFAT_H__
|
||||||
|
|
||||||
|
// Accessor vtable getters
|
||||||
|
#define FS_OFFSET_900_EXFAT_SDMMC_ACCESSOR_GC 0x1430F0
|
||||||
|
#define FS_OFFSET_900_EXFAT_SDMMC_ACCESSOR_SD 0x141200
|
||||||
|
#define FS_OFFSET_900_EXFAT_SDMMC_ACCESSOR_NAND 0x13C080
|
||||||
|
|
||||||
|
// Hooks
|
||||||
|
#define FS_OFFSET_900_EXFAT_SDMMC_WRAPPER_READ 0x1377E0
|
||||||
|
#define FS_OFFSET_900_EXFAT_SDMMC_WRAPPER_WRITE 0x1378C0
|
||||||
|
#define FS_OFFSET_900_EXFAT_RTLD 0x454
|
||||||
|
#define FS_OFFSET_900_EXFAT_RTLD_DESTINATION 0x9C
|
||||||
|
|
||||||
|
#define FS_OFFSET_900_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x136A00
|
||||||
|
|
||||||
|
// Misc funcs
|
||||||
|
#define FS_OFFSET_900_EXFAT_LOCK_MUTEX 0x25280
|
||||||
|
#define FS_OFFSET_900_EXFAT_UNLOCK_MUTEX 0x252D0
|
||||||
|
|
||||||
|
#define FS_OFFSET_900_EXFAT_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x137740
|
||||||
|
|
||||||
|
// Misc Data
|
||||||
|
#define FS_OFFSET_900_EXFAT_SD_MUTEX 0xE2B3E8
|
||||||
|
#define FS_OFFSET_900_EXFAT_NAND_MUTEX 0xE26258
|
||||||
|
#define FS_OFFSET_900_EXFAT_ACTIVE_PARTITION 0xE26298
|
||||||
|
#define FS_OFFSET_900_EXFAT_SDMMC_DAS_HANDLE 0xE0CFA0
|
||||||
|
|
||||||
|
// NOPs
|
||||||
|
#define FS_OFFSET_900_EXFAT_SD_DAS_INIT 0x1472BC
|
||||||
|
|
||||||
|
// Nintendo Paths
|
||||||
|
#define FS_OFFSET_900_EXFAT_NINTENDO_PATHS \
|
||||||
|
{ \
|
||||||
|
{.opcode_reg = 3, .adrp_offset = 0x00068A60, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 3, .adrp_offset = 0x00070A40, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 3, .adrp_offset = 0x00081CB4, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 3, .adrp_offset = 0x00081EF4, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 4, .adrp_offset = 0x0008211C, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __FS_900_EXFAT_H__
|
||||||
59
emummc/source/FS/offsets/910.h
Normal file
59
emummc/source/FS/offsets/910.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
|
||||||
|
* Copyright (c) 2019 Atmosphere-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope 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/>.
|
||||||
|
*/
|
||||||
|
#ifndef __FS_910_H__
|
||||||
|
#define __FS_910_H__
|
||||||
|
|
||||||
|
// Accessor vtable getters
|
||||||
|
#define FS_OFFSET_910_SDMMC_ACCESSOR_GC 0x143100
|
||||||
|
#define FS_OFFSET_910_SDMMC_ACCESSOR_SD 0x141210
|
||||||
|
#define FS_OFFSET_910_SDMMC_ACCESSOR_NAND 0x13C090
|
||||||
|
|
||||||
|
// Hooks
|
||||||
|
#define FS_OFFSET_910_SDMMC_WRAPPER_READ 0x1377F0
|
||||||
|
#define FS_OFFSET_910_SDMMC_WRAPPER_WRITE 0x1378D0
|
||||||
|
#define FS_OFFSET_910_RTLD 0x454
|
||||||
|
#define FS_OFFSET_910_RTLD_DESTINATION 0x9C
|
||||||
|
|
||||||
|
#define FS_OFFSET_910_CLKRST_SET_MIN_V_CLK_RATE 0x136A10
|
||||||
|
|
||||||
|
// Misc funcs
|
||||||
|
#define FS_OFFSET_910_LOCK_MUTEX 0x25280
|
||||||
|
#define FS_OFFSET_910_UNLOCK_MUTEX 0x252D0
|
||||||
|
|
||||||
|
#define FS_OFFSET_910_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x137750
|
||||||
|
|
||||||
|
// Misc Data
|
||||||
|
#define FS_OFFSET_910_SD_MUTEX 0xE1D3E8
|
||||||
|
#define FS_OFFSET_910_NAND_MUTEX 0xE18258
|
||||||
|
#define FS_OFFSET_910_ACTIVE_PARTITION 0xE18298
|
||||||
|
#define FS_OFFSET_910_SDMMC_DAS_HANDLE 0xDFEFA0
|
||||||
|
|
||||||
|
// NOPs
|
||||||
|
#define FS_OFFSET_910_SD_DAS_INIT 0x1472CC
|
||||||
|
|
||||||
|
// Nintendo Paths
|
||||||
|
#define FS_OFFSET_910_NINTENDO_PATHS \
|
||||||
|
{ \
|
||||||
|
{.opcode_reg = 3, .adrp_offset = 0x00068A70, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 3, .adrp_offset = 0x00070A50, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 3, .adrp_offset = 0x00081CC4, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 3, .adrp_offset = 0x00081F04, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 4, .adrp_offset = 0x0008212C, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __FS_910_H__
|
||||||
59
emummc/source/FS/offsets/910_exfat.h
Normal file
59
emummc/source/FS/offsets/910_exfat.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
|
||||||
|
* Copyright (c) 2019 Atmosphere-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope 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/>.
|
||||||
|
*/
|
||||||
|
#ifndef __FS_910_EXFAT_H__
|
||||||
|
#define __FS_910_EXFAT_H__
|
||||||
|
|
||||||
|
// Accessor vtable getters
|
||||||
|
#define FS_OFFSET_910_EXFAT_SDMMC_ACCESSOR_GC 0x143100
|
||||||
|
#define FS_OFFSET_910_EXFAT_SDMMC_ACCESSOR_SD 0x141210
|
||||||
|
#define FS_OFFSET_910_EXFAT_SDMMC_ACCESSOR_NAND 0x13C090
|
||||||
|
|
||||||
|
// Hooks
|
||||||
|
#define FS_OFFSET_910_EXFAT_SDMMC_WRAPPER_READ 0x1377F0
|
||||||
|
#define FS_OFFSET_910_EXFAT_SDMMC_WRAPPER_WRITE 0x1378D0
|
||||||
|
#define FS_OFFSET_910_EXFAT_RTLD 0x454
|
||||||
|
#define FS_OFFSET_910_EXFAT_RTLD_DESTINATION 0x9C
|
||||||
|
|
||||||
|
#define FS_OFFSET_910_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x136A10
|
||||||
|
|
||||||
|
// Misc funcs
|
||||||
|
#define FS_OFFSET_910_EXFAT_LOCK_MUTEX 0x25280
|
||||||
|
#define FS_OFFSET_910_EXFAT_UNLOCK_MUTEX 0x252D0
|
||||||
|
|
||||||
|
#define FS_OFFSET_910_EXFAT_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x137750
|
||||||
|
|
||||||
|
// Misc Data
|
||||||
|
#define FS_OFFSET_910_EXFAT_SD_MUTEX 0xE2B3E8
|
||||||
|
#define FS_OFFSET_910_EXFAT_NAND_MUTEX 0xE26258
|
||||||
|
#define FS_OFFSET_910_EXFAT_ACTIVE_PARTITION 0xE26298
|
||||||
|
#define FS_OFFSET_910_EXFAT_SDMMC_DAS_HANDLE 0xE0CFA0
|
||||||
|
|
||||||
|
// NOPs
|
||||||
|
#define FS_OFFSET_910_EXFAT_SD_DAS_INIT 0x1472CC
|
||||||
|
|
||||||
|
// Nintendo Paths
|
||||||
|
#define FS_OFFSET_910_EXFAT_NINTENDO_PATHS \
|
||||||
|
{ \
|
||||||
|
{.opcode_reg = 3, .adrp_offset = 0x00068A70, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 3, .adrp_offset = 0x00070A50, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 3, .adrp_offset = 0x00081CC4, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 3, .adrp_offset = 0x00081F04, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 4, .adrp_offset = 0x0008212C, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __FS_910_EXFAT_H__
|
||||||
@@ -15,13 +15,26 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
#include "fatal.h"
|
#include "fatal.h"
|
||||||
|
|
||||||
void __attribute__((noreturn)) fatal_abort(enum FatalReason abortReason)
|
void __attribute__((noreturn)) fatal_abort(enum FatalReason abortReason)
|
||||||
{
|
{
|
||||||
|
atmosphere_fatal_error_ctx error_ctx;
|
||||||
|
memset(&error_ctx, 0, sizeof(atmosphere_fatal_error_ctx));
|
||||||
|
|
||||||
|
// Basic error storage for Atmosphere
|
||||||
|
// TODO: Maybe include a small reboot2payload stub?
|
||||||
|
error_ctx.magic = ATMOSPHERE_REBOOT_TO_FATAL_MAGIC;
|
||||||
|
error_ctx.title_id = 0x0100000000000000; // FS
|
||||||
|
error_ctx.error_desc = abortReason;
|
||||||
|
|
||||||
|
// Copy fatal context
|
||||||
|
smcCopyToIram(ATMOSPHERE_FATAL_ERROR_ADDR, &error_ctx, sizeof(atmosphere_fatal_error_ctx));
|
||||||
|
|
||||||
// Reboot to RCM
|
// Reboot to RCM
|
||||||
smcRebootToRcm();
|
smcRebootToRcm();
|
||||||
|
|
||||||
while(true)
|
while (true)
|
||||||
; // Should never be reached
|
; // Should never be reached
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "../nx/smc.h"
|
#include "../nx/smc.h"
|
||||||
|
|
||||||
enum FatalReason {
|
enum FatalReason
|
||||||
|
{
|
||||||
Fatal_InitMMC = 0,
|
Fatal_InitMMC = 0,
|
||||||
Fatal_InitSD,
|
Fatal_InitSD,
|
||||||
Fatal_InvalidAccessor,
|
Fatal_InvalidAccessor,
|
||||||
@@ -32,4 +33,45 @@ enum FatalReason {
|
|||||||
Fatal_Max
|
Fatal_Max
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define AMS_FATAL_ERROR_MAX_STACKTRACE 0x20
|
||||||
|
#define AMS_FATAL_ERROR_MAX_STACKDUMP 0x100
|
||||||
|
|
||||||
|
/* Atmosphere reboot-to-fatal-error. */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t magic;
|
||||||
|
uint32_t error_desc;
|
||||||
|
uint64_t title_id;
|
||||||
|
union {
|
||||||
|
uint64_t gprs[32];
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint64_t _gprs[29];
|
||||||
|
uint64_t fp;
|
||||||
|
uint64_t lr;
|
||||||
|
uint64_t sp;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
uint64_t pc;
|
||||||
|
uint64_t module_base;
|
||||||
|
uint32_t pstate;
|
||||||
|
uint32_t afsr0;
|
||||||
|
uint32_t afsr1;
|
||||||
|
uint32_t esr;
|
||||||
|
uint64_t far;
|
||||||
|
uint64_t report_identifier; /* Normally just system tick. */
|
||||||
|
uint64_t stack_trace_size;
|
||||||
|
uint64_t stack_dump_size;
|
||||||
|
uint64_t stack_trace[AMS_FATAL_ERROR_MAX_STACKTRACE];
|
||||||
|
uint8_t stack_dump[AMS_FATAL_ERROR_MAX_STACKDUMP];
|
||||||
|
} atmosphere_fatal_error_ctx;
|
||||||
|
|
||||||
|
/* "AFE1" */
|
||||||
|
#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC 0x31454641
|
||||||
|
/* "AFE0" */
|
||||||
|
#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_0 0x30454641
|
||||||
|
|
||||||
|
#define ATMOSPHERE_FATAL_ERROR_ADDR 0x4003E000
|
||||||
|
#define ATMOSPHERE_FATAL_ERROR_CONTEXT ((volatile atmosphere_fatal_error_ctx *)(ATMOSPHERE_FATAL_ERROR_ADDR))
|
||||||
|
|
||||||
void __attribute__((noreturn)) fatal_abort(enum FatalReason abortReason);
|
void __attribute__((noreturn)) fatal_abort(enum FatalReason abortReason);
|
||||||
|
|||||||
@@ -35,6 +35,14 @@
|
|||||||
#define MC_SMMU_PTB_DATA 0x20
|
#define MC_SMMU_PTB_DATA 0x20
|
||||||
#define MC_SMMU_TLB_FLUSH 0x30
|
#define MC_SMMU_TLB_FLUSH 0x30
|
||||||
#define MC_SMMU_PTC_FLUSH 0x34
|
#define MC_SMMU_PTC_FLUSH 0x34
|
||||||
|
#define MC_SMMU_ASID_SECURITY 0x38
|
||||||
|
#define MC_SMMU_ASID_SECURITY_1 0x3c
|
||||||
|
#define MC_SMMU_ASID_SECURITY_2 0x9e0
|
||||||
|
#define MC_SMMU_ASID_SECURITY_3 0x9e4
|
||||||
|
#define MC_SMMU_ASID_SECURITY_4 0x9e8
|
||||||
|
#define MC_SMMU_ASID_SECURITY_5 0x9ec
|
||||||
|
#define MC_SMMU_ASID_SECURITY_6 0x9f0
|
||||||
|
#define MC_SMMU_ASID_SECURITY_7 0x9f4
|
||||||
#define MC_SMMU_AFI_ASID 0x238
|
#define MC_SMMU_AFI_ASID 0x238
|
||||||
#define MC_SMMU_AVPC_ASID 0x23c
|
#define MC_SMMU_AVPC_ASID 0x23c
|
||||||
#define MC_SMMU_PPCS1_ASID 0x298
|
#define MC_SMMU_PPCS1_ASID 0x298
|
||||||
|
|||||||
@@ -85,6 +85,9 @@ void bootup_misc_mmio(void) {
|
|||||||
setup_dram_magic_numbers();
|
setup_dram_magic_numbers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* On 9.0.0+, Nintendo writes random values to context save scratch here, and locks the SRK scratch. */
|
||||||
|
/* There's no real need for us to do this, so we won't. */
|
||||||
|
|
||||||
/* Mark TMR5, TMR6, TMR7, TMR8, WDT0, WDT1, WDT2 and WDT3 as secure. */
|
/* Mark TMR5, TMR6, TMR7, TMR8, WDT0, WDT1, WDT2 and WDT3 as secure. */
|
||||||
SHARED_TIMER_SECURE_CFG_0 = 0xF1E0;
|
SHARED_TIMER_SECURE_CFG_0 = 0xF1E0;
|
||||||
|
|
||||||
@@ -140,28 +143,26 @@ void bootup_misc_mmio(void) {
|
|||||||
APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG2_0 = sec_disable_2;
|
APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG2_0 = sec_disable_2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reset Translation Enable Registers. */
|
/* Reset Translation Enable registers. */
|
||||||
MAKE_MC_REG(MC_SMMU_TRANSLATION_ENABLE_0) = 0xFFFFFFFF;
|
MAKE_MC_REG(MC_SMMU_TRANSLATION_ENABLE_0) = 0xFFFFFFFF;
|
||||||
MAKE_MC_REG(MC_SMMU_TRANSLATION_ENABLE_1) = 0xFFFFFFFF;
|
MAKE_MC_REG(MC_SMMU_TRANSLATION_ENABLE_1) = 0xFFFFFFFF;
|
||||||
MAKE_MC_REG(MC_SMMU_TRANSLATION_ENABLE_2) = 0xFFFFFFFF;
|
MAKE_MC_REG(MC_SMMU_TRANSLATION_ENABLE_2) = 0xFFFFFFFF;
|
||||||
MAKE_MC_REG(MC_SMMU_TRANSLATION_ENABLE_3) = 0xFFFFFFFF;
|
MAKE_MC_REG(MC_SMMU_TRANSLATION_ENABLE_3) = 0xFFFFFFFF;
|
||||||
MAKE_MC_REG(MC_SMMU_TRANSLATION_ENABLE_4) = 0xFFFFFFFF;
|
MAKE_MC_REG(MC_SMMU_TRANSLATION_ENABLE_4) = 0xFFFFFFFF;
|
||||||
|
|
||||||
/* TODO: What are these MC reg writes? */
|
/* Set SMMU ASID security registers. */
|
||||||
if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) {
|
if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) {
|
||||||
MAKE_MC_REG(0x038) = 0xE;
|
MAKE_MC_REG(MC_SMMU_ASID_SECURITY) = 0xE;
|
||||||
} else {
|
} else {
|
||||||
MAKE_MC_REG(0x038) = 0x0;
|
MAKE_MC_REG(MC_SMMU_ASID_SECURITY) = 0x0;
|
||||||
}
|
}
|
||||||
MAKE_MC_REG(0x03C) = 0;
|
MAKE_MC_REG(MC_SMMU_ASID_SECURITY_1) = 0;
|
||||||
|
MAKE_MC_REG(MC_SMMU_ASID_SECURITY_2) = 0;
|
||||||
/* MISC registers. */
|
MAKE_MC_REG(MC_SMMU_ASID_SECURITY_3) = 0;
|
||||||
MAKE_MC_REG(0x9E0) = 0;
|
MAKE_MC_REG(MC_SMMU_ASID_SECURITY_4) = 0;
|
||||||
MAKE_MC_REG(0x9E4) = 0;
|
MAKE_MC_REG(MC_SMMU_ASID_SECURITY_5) = 0;
|
||||||
MAKE_MC_REG(0x9E8) = 0;
|
MAKE_MC_REG(MC_SMMU_ASID_SECURITY_6) = 0;
|
||||||
MAKE_MC_REG(0x9EC) = 0;
|
MAKE_MC_REG(MC_SMMU_ASID_SECURITY_7) = 0;
|
||||||
MAKE_MC_REG(0x9F0) = 0;
|
|
||||||
MAKE_MC_REG(0x9F4) = 0;
|
|
||||||
|
|
||||||
if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) {
|
if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) {
|
||||||
MAKE_MC_REG(MC_SMMU_PTB_ASID) = 0;
|
MAKE_MC_REG(MC_SMMU_PTB_ASID) = 0;
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
static bool g_hiz_mode_enabled = false;
|
static bool g_hiz_mode_enabled = false;
|
||||||
static bool g_debugmode_override_user = false, g_debugmode_override_priv = false;
|
static bool g_debugmode_override_user = false, g_debugmode_override_priv = false;
|
||||||
static bool g_enable_usermode_exception_handlers = true;
|
static bool g_enable_usermode_exception_handlers = true;
|
||||||
|
static bool g_enable_usermode_pmu_access = false;
|
||||||
|
|
||||||
uint32_t configitem_set(bool privileged, ConfigItem item, uint64_t value) {
|
uint32_t configitem_set(bool privileged, ConfigItem item, uint64_t value) {
|
||||||
switch (item) {
|
switch (item) {
|
||||||
@@ -168,6 +169,10 @@ void configitem_disable_usermode_exception_handlers(void) {
|
|||||||
g_enable_usermode_exception_handlers = false;
|
g_enable_usermode_exception_handlers = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void configitem_enable_usermode_pmu_access(void) {
|
||||||
|
g_enable_usermode_pmu_access = true;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t configitem_get(bool privileged, ConfigItem item, uint64_t *p_outvalue) {
|
uint32_t configitem_get(bool privileged, ConfigItem item, uint64_t *p_outvalue) {
|
||||||
uint32_t result = 0;
|
uint32_t result = 0;
|
||||||
switch (item) {
|
switch (item) {
|
||||||
@@ -222,6 +227,10 @@ uint32_t configitem_get(bool privileged, ConfigItem item, uint64_t *p_outvalue)
|
|||||||
if (g_enable_usermode_exception_handlers) {
|
if (g_enable_usermode_exception_handlers) {
|
||||||
config |= KERNELCONFIGFLAG_ENABLE_USER_EXCEPTION_HANDLERS;
|
config |= KERNELCONFIGFLAG_ENABLE_USER_EXCEPTION_HANDLERS;
|
||||||
}
|
}
|
||||||
|
/* Allow for enabling usermode pmu access. */
|
||||||
|
if (g_enable_usermode_pmu_access) {
|
||||||
|
config |= KERNELCONFIGFLAG_ENABLE_USER_PMU_ACCESS;
|
||||||
|
}
|
||||||
*p_outvalue = config;
|
*p_outvalue = config;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ bool configitem_is_debugmode_priv(void);
|
|||||||
|
|
||||||
void configitem_set_debugmode_override(bool user, bool priv);
|
void configitem_set_debugmode_override(bool user, bool priv);
|
||||||
void configitem_disable_usermode_exception_handlers(void);
|
void configitem_disable_usermode_exception_handlers(void);
|
||||||
|
void configitem_enable_usermode_pmu_access(void);
|
||||||
void configitem_set_hiz_mode_enabled(bool enabled);
|
void configitem_set_hiz_mode_enabled(bool enabled);
|
||||||
|
|
||||||
uint64_t configitem_get_hardware_type(void);
|
uint64_t configitem_get_hardware_type(void);
|
||||||
|
|||||||
@@ -84,6 +84,14 @@ unsigned int exosphere_should_disable_usermode_exception_handlers(void) {
|
|||||||
return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS);
|
return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int exosphere_should_enable_usermode_pmu_access(void) {
|
||||||
|
if (!g_has_loaded_config) {
|
||||||
|
generic_panic();
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS);
|
||||||
|
}
|
||||||
|
|
||||||
const exo_emummc_config_t *exosphere_get_emummc_config(void) {
|
const exo_emummc_config_t *exosphere_get_emummc_config(void) {
|
||||||
if (!g_has_loaded_config) {
|
if (!g_has_loaded_config) {
|
||||||
generic_panic();
|
generic_panic();
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
#define EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV (1 << 1u)
|
#define EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV (1 << 1u)
|
||||||
#define EXOSPHERE_FLAG_IS_DEBUGMODE_USER (1 << 2u)
|
#define EXOSPHERE_FLAG_IS_DEBUGMODE_USER (1 << 2u)
|
||||||
#define EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS (1 << 3u)
|
#define EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS (1 << 3u)
|
||||||
|
#define EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS (1 << 4u)
|
||||||
#define EXOSPHERE_FLAGS_DEFAULT (EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV)
|
#define EXOSPHERE_FLAGS_DEFAULT (EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -58,6 +59,7 @@ unsigned int exosphere_should_perform_620_keygen(void);
|
|||||||
unsigned int exosphere_should_override_debugmode_priv(void);
|
unsigned int exosphere_should_override_debugmode_priv(void);
|
||||||
unsigned int exosphere_should_override_debugmode_user(void);
|
unsigned int exosphere_should_override_debugmode_user(void);
|
||||||
unsigned int exosphere_should_disable_usermode_exception_handlers(void);
|
unsigned int exosphere_should_disable_usermode_exception_handlers(void);
|
||||||
|
unsigned int exosphere_should_enable_usermode_pmu_access(void);
|
||||||
|
|
||||||
const exo_emummc_config_t *exosphere_get_emummc_config(void);
|
const exo_emummc_config_t *exosphere_get_emummc_config(void);
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ void i2c_init(I2CDevice id) {
|
|||||||
|
|
||||||
/* Wait a while until BUS_CLEAR_DONE is set. */
|
/* Wait a while until BUS_CLEAR_DONE is set. */
|
||||||
for (unsigned int i = 0; i < 10; i++) {
|
for (unsigned int i = 0; i < 10; i++) {
|
||||||
wait(20000);
|
wait(25);
|
||||||
if (regs->I2C_INTERRUPT_STATUS_REGISTER_0 & 0x800) {
|
if (regs->I2C_INTERRUPT_STATUS_REGISTER_0 & 0x800) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ static const uint8_t mkey_vectors_dev[MASTERKEY_REVISION_MAX][0x10] =
|
|||||||
{0x6F, 0xD2, 0x84, 0x1D, 0x05, 0xEC, 0x40, 0x94, 0x5F, 0x18, 0xB3, 0x81, 0x09, 0x98, 0x8D, 0x4E}, /* Master key 05 encrypted with Master key 06. */
|
{0x6F, 0xD2, 0x84, 0x1D, 0x05, 0xEC, 0x40, 0x94, 0x5F, 0x18, 0xB3, 0x81, 0x09, 0x98, 0x8D, 0x4E}, /* Master key 05 encrypted with Master key 06. */
|
||||||
{0x37, 0xAF, 0xAB, 0x35, 0x79, 0x09, 0xD9, 0x48, 0x29, 0xD2, 0xDB, 0xA5, 0xA5, 0xF5, 0x30, 0x19}, /* Master key 06 encrypted with Master key 07. */
|
{0x37, 0xAF, 0xAB, 0x35, 0x79, 0x09, 0xD9, 0x48, 0x29, 0xD2, 0xDB, 0xA5, 0xA5, 0xF5, 0x30, 0x19}, /* Master key 06 encrypted with Master key 07. */
|
||||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* TODO: Master key 07 encrypted with Master key 08. */
|
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* TODO: Master key 07 encrypted with Master key 08. */
|
||||||
|
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* TODO: Master key 08 encrypted with Master key 09. */
|
||||||
|
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* TODO: Master key 09 encrypted with Master key 0A. */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Retail unit keys. */
|
/* Retail unit keys. */
|
||||||
@@ -57,6 +59,8 @@ static const uint8_t mkey_vectors[MASTERKEY_REVISION_MAX][0x10] =
|
|||||||
{0x1E, 0x1E, 0x22, 0xC0, 0x5A, 0x33, 0x3C, 0xB9, 0x0B, 0xA9, 0x03, 0x04, 0xBA, 0xDB, 0x07, 0x57}, /* Master key 05 encrypted with Master key 06. */
|
{0x1E, 0x1E, 0x22, 0xC0, 0x5A, 0x33, 0x3C, 0xB9, 0x0B, 0xA9, 0x03, 0x04, 0xBA, 0xDB, 0x07, 0x57}, /* Master key 05 encrypted with Master key 06. */
|
||||||
{0xA4, 0xD4, 0x52, 0x6F, 0xD1, 0xE4, 0x36, 0xAA, 0x9F, 0xCB, 0x61, 0x27, 0x1C, 0x67, 0x65, 0x1F}, /* Master key 06 encrypted with Master key 07. */
|
{0xA4, 0xD4, 0x52, 0x6F, 0xD1, 0xE4, 0x36, 0xAA, 0x9F, 0xCB, 0x61, 0x27, 0x1C, 0x67, 0x65, 0x1F}, /* Master key 06 encrypted with Master key 07. */
|
||||||
{0xEA, 0x60, 0xB3, 0xEA, 0xCE, 0x8F, 0x24, 0x46, 0x7D, 0x33, 0x9C, 0xD1, 0xBC, 0x24, 0x98, 0x29}, /* Master key 07 encrypted with Master key 08. */
|
{0xEA, 0x60, 0xB3, 0xEA, 0xCE, 0x8F, 0x24, 0x46, 0x7D, 0x33, 0x9C, 0xD1, 0xBC, 0x24, 0x98, 0x29}, /* Master key 07 encrypted with Master key 08. */
|
||||||
|
{0x4D, 0xD9, 0x98, 0x42, 0x45, 0x0D, 0xB1, 0x3C, 0x52, 0x0C, 0x9A, 0x44, 0xBB, 0xAD, 0xAF, 0x80}, /* Master key 08 encrypted with Master key 09. */
|
||||||
|
{0xB8, 0x96, 0x9E, 0x4A, 0x00, 0x0D, 0xD6, 0x28, 0xB3, 0xD1, 0xDB, 0x68, 0x5F, 0xFB, 0xE1, 0x2A}, /* Master key 09 encrypted with Master key 0A. */
|
||||||
};
|
};
|
||||||
|
|
||||||
bool check_mkey_revision(unsigned int revision, bool is_retail) {
|
bool check_mkey_revision(unsigned int revision, bool is_retail) {
|
||||||
|
|||||||
@@ -19,8 +19,8 @@
|
|||||||
|
|
||||||
/* This is glue code to enable master key support across versions. */
|
/* This is glue code to enable master key support across versions. */
|
||||||
|
|
||||||
/* TODO: Update to 0xA on release of new master key. */
|
/* TODO: Update to 0xC on release of new master key. */
|
||||||
#define MASTERKEY_REVISION_MAX 0x9
|
#define MASTERKEY_REVISION_MAX 0xB
|
||||||
|
|
||||||
#define MASTERKEY_REVISION_100_230 0x00
|
#define MASTERKEY_REVISION_100_230 0x00
|
||||||
#define MASTERKEY_REVISION_300 0x01
|
#define MASTERKEY_REVISION_300 0x01
|
||||||
@@ -29,8 +29,10 @@
|
|||||||
#define MASTERKEY_REVISION_500_510 0x04
|
#define MASTERKEY_REVISION_500_510 0x04
|
||||||
#define MASTERKEY_REVISION_600_610 0x05
|
#define MASTERKEY_REVISION_600_610 0x05
|
||||||
#define MASTERKEY_REVISION_620 0x06
|
#define MASTERKEY_REVISION_620 0x06
|
||||||
#define MASTERKEY_REVISION_700_800 0x07
|
#define MASTERKEY_REVISION_700_800 0x07
|
||||||
#define MASTERKEY_REVISION_810_CURRENT 0x08
|
#define MASTERKEY_REVISION_810 0x08
|
||||||
|
#define MASTERKEY_REVISION_900 0x09
|
||||||
|
#define MASTERKEY_REVISION_910_CURRENT 0x0A
|
||||||
|
|
||||||
#define MASTERKEY_NUM_NEW_DEVICE_KEYS (MASTERKEY_REVISION_MAX - MASTERKEY_REVISION_400_410)
|
#define MASTERKEY_NUM_NEW_DEVICE_KEYS (MASTERKEY_REVISION_MAX - MASTERKEY_REVISION_400_410)
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,14 @@ static inline uintptr_t get_mc_base(void) {
|
|||||||
#define MC_SMMU_PTB_DATA 0x20
|
#define MC_SMMU_PTB_DATA 0x20
|
||||||
#define MC_SMMU_TLB_FLUSH 0x30
|
#define MC_SMMU_TLB_FLUSH 0x30
|
||||||
#define MC_SMMU_PTC_FLUSH 0x34
|
#define MC_SMMU_PTC_FLUSH 0x34
|
||||||
|
#define MC_SMMU_ASID_SECURITY 0x38
|
||||||
|
#define MC_SMMU_ASID_SECURITY_1 0x3c
|
||||||
|
#define MC_SMMU_ASID_SECURITY_2 0x9e0
|
||||||
|
#define MC_SMMU_ASID_SECURITY_3 0x9e4
|
||||||
|
#define MC_SMMU_ASID_SECURITY_4 0x9e8
|
||||||
|
#define MC_SMMU_ASID_SECURITY_5 0x9ec
|
||||||
|
#define MC_SMMU_ASID_SECURITY_6 0x9f0
|
||||||
|
#define MC_SMMU_ASID_SECURITY_7 0x9f4
|
||||||
#define MC_SMMU_AFI_ASID 0x238
|
#define MC_SMMU_AFI_ASID 0x238
|
||||||
#define MC_SMMU_AVPC_ASID 0x23c
|
#define MC_SMMU_AVPC_ASID 0x23c
|
||||||
#define MC_SMMU_PPCS1_ASID 0x298
|
#define MC_SMMU_PPCS1_ASID 0x298
|
||||||
|
|||||||
@@ -43,7 +43,9 @@ static const uint8_t new_device_key_sources[MASTERKEY_NUM_NEW_DEVICE_KEYS][0x10]
|
|||||||
{0x70, 0x08, 0x1B, 0x97, 0x44, 0x64, 0xF8, 0x91, 0x54, 0x9D, 0xC6, 0x84, 0x8F, 0x1A, 0xB2, 0xE4}, /* 6.x New Device Key Source. */
|
{0x70, 0x08, 0x1B, 0x97, 0x44, 0x64, 0xF8, 0x91, 0x54, 0x9D, 0xC6, 0x84, 0x8F, 0x1A, 0xB2, 0xE4}, /* 6.x New Device Key Source. */
|
||||||
{0x8E, 0x09, 0x1F, 0x7A, 0xBB, 0xCA, 0x6A, 0xFB, 0xB8, 0x9B, 0xD5, 0xC1, 0x25, 0x9C, 0xA9, 0x17}, /* 6.2.0 New Device Key Source. */
|
{0x8E, 0x09, 0x1F, 0x7A, 0xBB, 0xCA, 0x6A, 0xFB, 0xB8, 0x9B, 0xD5, 0xC1, 0x25, 0x9C, 0xA9, 0x17}, /* 6.2.0 New Device Key Source. */
|
||||||
{0x8F, 0x77, 0x5A, 0x96, 0xB0, 0x94, 0xFD, 0x8D, 0x28, 0xE4, 0x19, 0xC8, 0x16, 0x1C, 0xDB, 0x3D}, /* 7.0.0 New Device Key Source. */
|
{0x8F, 0x77, 0x5A, 0x96, 0xB0, 0x94, 0xFD, 0x8D, 0x28, 0xE4, 0x19, 0xC8, 0x16, 0x1C, 0xDB, 0x3D}, /* 7.0.0 New Device Key Source. */
|
||||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* TODO: 8.1.0 New Device Key Source to be added on next change-of-keys. */
|
{0x67, 0x62, 0xD4, 0x8E, 0x55, 0xCF, 0xFF, 0x41, 0x31, 0x15, 0x3B, 0x24, 0x0C, 0x7C, 0x07, 0xAE}, /* 8.1.0 New Device Key Source. */
|
||||||
|
{0x4A, 0xC3, 0x4E, 0x14, 0x8B, 0x96, 0x4A, 0xD5, 0xD4, 0x99, 0x73, 0xC4, 0x45, 0xAB, 0x8B, 0x49}, /* 9.0.0 New Device Key Source. */
|
||||||
|
{0x14, 0xB8, 0x74, 0x12, 0xCB, 0xBD, 0x0B, 0x8F, 0x20, 0xFB, 0x30, 0xDA, 0x27, 0xE4, 0x58, 0x94}, /* 9.1.0 New Device Key Source. */
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint8_t new_device_keygen_sources[MASTERKEY_NUM_NEW_DEVICE_KEYS][0x10] = {
|
static const uint8_t new_device_keygen_sources[MASTERKEY_NUM_NEW_DEVICE_KEYS][0x10] = {
|
||||||
@@ -52,7 +54,9 @@ static const uint8_t new_device_keygen_sources[MASTERKEY_NUM_NEW_DEVICE_KEYS][0x
|
|||||||
{0x99, 0xFA, 0x98, 0xBD, 0x15, 0x1C, 0x72, 0xFD, 0x7D, 0x9A, 0xD5, 0x41, 0x00, 0xFD, 0xB2, 0xEF}, /* 6.x New Device Keygen Source. */
|
{0x99, 0xFA, 0x98, 0xBD, 0x15, 0x1C, 0x72, 0xFD, 0x7D, 0x9A, 0xD5, 0x41, 0x00, 0xFD, 0xB2, 0xEF}, /* 6.x New Device Keygen Source. */
|
||||||
{0x81, 0x3C, 0x6C, 0xBF, 0x5D, 0x21, 0xDE, 0x77, 0x20, 0xD9, 0x6C, 0xE3, 0x22, 0x06, 0xAE, 0xBB}, /* 6.2.0 New Device Keygen Source. */
|
{0x81, 0x3C, 0x6C, 0xBF, 0x5D, 0x21, 0xDE, 0x77, 0x20, 0xD9, 0x6C, 0xE3, 0x22, 0x06, 0xAE, 0xBB}, /* 6.2.0 New Device Keygen Source. */
|
||||||
{0x86, 0x61, 0xB0, 0x16, 0xFA, 0x7A, 0x9A, 0xEA, 0xF6, 0xF5, 0xBE, 0x1A, 0x13, 0x5B, 0x6D, 0x9E}, /* 7.0.0 New Device Keygen Source. */
|
{0x86, 0x61, 0xB0, 0x16, 0xFA, 0x7A, 0x9A, 0xEA, 0xF6, 0xF5, 0xBE, 0x1A, 0x13, 0x5B, 0x6D, 0x9E}, /* 7.0.0 New Device Keygen Source. */
|
||||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* TODO: 8.1.0 New Device Key Source to be added on next change-of-keys. */
|
{0xA6, 0x81, 0x71, 0xE7, 0xB5, 0x23, 0x74, 0xB0, 0x39, 0x8C, 0xB7, 0xFF, 0xA0, 0x62, 0x9F, 0x8D}, /* 8.1.0 New Device Keygen Source. */
|
||||||
|
{0x03, 0xE7, 0xEB, 0x43, 0x1B, 0xCF, 0x5F, 0xB5, 0xED, 0xDC, 0x97, 0xAE, 0x21, 0x8D, 0x19, 0xED}, /* 9.0.0 New Device Keygen Source. */
|
||||||
|
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* TODO: 9.1.0 New Device Keygen Source to be added on next change-of-keys. */
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint8_t new_device_keygen_sources_dev[MASTERKEY_NUM_NEW_DEVICE_KEYS][0x10] = {
|
static const uint8_t new_device_keygen_sources_dev[MASTERKEY_NUM_NEW_DEVICE_KEYS][0x10] = {
|
||||||
@@ -61,7 +65,9 @@ static const uint8_t new_device_keygen_sources_dev[MASTERKEY_NUM_NEW_DEVICE_KEYS
|
|||||||
{0xF6, 0xD8, 0x59, 0x63, 0x8F, 0x47, 0xCB, 0x4A, 0xD8, 0x74, 0x05, 0x7F, 0x88, 0x92, 0x33, 0xA5}, /* 6.x New Device Keygen Source. */
|
{0xF6, 0xD8, 0x59, 0x63, 0x8F, 0x47, 0xCB, 0x4A, 0xD8, 0x74, 0x05, 0x7F, 0x88, 0x92, 0x33, 0xA5}, /* 6.x New Device Keygen Source. */
|
||||||
{0x20, 0xAB, 0xF2, 0x0F, 0x05, 0xE3, 0xDE, 0x2E, 0xA1, 0xFB, 0x37, 0x5E, 0x8B, 0x22, 0x1A, 0x38}, /* 6.2.0 New Device Keygen Source. */
|
{0x20, 0xAB, 0xF2, 0x0F, 0x05, 0xE3, 0xDE, 0x2E, 0xA1, 0xFB, 0x37, 0x5E, 0x8B, 0x22, 0x1A, 0x38}, /* 6.2.0 New Device Keygen Source. */
|
||||||
{0x60, 0xAE, 0x56, 0x68, 0x11, 0xE2, 0x0C, 0x99, 0xDE, 0x05, 0xAE, 0x68, 0x78, 0x85, 0x04, 0xAE}, /* 7.0.0 New Device Keygen Source. */
|
{0x60, 0xAE, 0x56, 0x68, 0x11, 0xE2, 0x0C, 0x99, 0xDE, 0x05, 0xAE, 0x68, 0x78, 0x85, 0x04, 0xAE}, /* 7.0.0 New Device Keygen Source. */
|
||||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* TODO: 8.1.0 New Device Key Source to be added on next change-of-keys. */
|
{0x94, 0xD6, 0xA8, 0xC0, 0x95, 0xAF, 0xD0, 0xA6, 0x27, 0x53, 0x5E, 0xE5, 0x8E, 0x70, 0x1F, 0x87}, /* 8.1.0 New Device Keygen Source. */
|
||||||
|
{0x61, 0x6A, 0x88, 0x21, 0xA3, 0x52, 0xB0, 0x19, 0x16, 0x25, 0xA4, 0xE3, 0x4C, 0x54, 0x02, 0x0F}, /* 9.0.0 New Device Keygen Source. */
|
||||||
|
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* TODO: 9.1.0 New Device Keygen Source to be added on next change-of-keys. */
|
||||||
};
|
};
|
||||||
|
|
||||||
static void derive_new_device_keys(unsigned int keygen_keyslot) {
|
static void derive_new_device_keys(unsigned int keygen_keyslot) {
|
||||||
@@ -141,6 +147,8 @@ static void setup_se(void) {
|
|||||||
case ATMOSPHERE_TARGET_FIRMWARE_700:
|
case ATMOSPHERE_TARGET_FIRMWARE_700:
|
||||||
case ATMOSPHERE_TARGET_FIRMWARE_800:
|
case ATMOSPHERE_TARGET_FIRMWARE_800:
|
||||||
case ATMOSPHERE_TARGET_FIRMWARE_810:
|
case ATMOSPHERE_TARGET_FIRMWARE_810:
|
||||||
|
case ATMOSPHERE_TARGET_FIRMWARE_900:
|
||||||
|
case ATMOSPHERE_TARGET_FIRMWARE_910:
|
||||||
derive_new_device_keys(KEYSLOT_SWITCH_5XNEWDEVICEKEYGENKEY);
|
derive_new_device_keys(KEYSLOT_SWITCH_5XNEWDEVICEKEYGENKEY);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -330,7 +338,7 @@ static bool validate_package2_metadata(package2_meta_t *metadata) {
|
|||||||
|
|
||||||
/* Perform version checks. */
|
/* Perform version checks. */
|
||||||
/* We will be compatible with all package2s released before current, but not newer ones. */
|
/* We will be compatible with all package2s released before current, but not newer ones. */
|
||||||
if (metadata->version_max >= PACKAGE2_MINVER_THEORETICAL && metadata->version_min < PACKAGE2_MAXVER_810_CURRENT) {
|
if (metadata->version_max >= PACKAGE2_MINVER_THEORETICAL && metadata->version_min < PACKAGE2_MAXVER_910_CURRENT) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,6 +464,8 @@ static void copy_warmboot_bin_to_dram() {
|
|||||||
case ATMOSPHERE_TARGET_FIRMWARE_700:
|
case ATMOSPHERE_TARGET_FIRMWARE_700:
|
||||||
case ATMOSPHERE_TARGET_FIRMWARE_800:
|
case ATMOSPHERE_TARGET_FIRMWARE_800:
|
||||||
case ATMOSPHERE_TARGET_FIRMWARE_810:
|
case ATMOSPHERE_TARGET_FIRMWARE_810:
|
||||||
|
case ATMOSPHERE_TARGET_FIRMWARE_900:
|
||||||
|
case ATMOSPHERE_TARGET_FIRMWARE_910:
|
||||||
warmboot_src = (uint8_t *)0x4003E000;
|
warmboot_src = (uint8_t *)0x4003E000;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -502,6 +512,9 @@ void load_package2(coldboot_crt0_reloc_list_t *reloc_list) {
|
|||||||
if (exosphere_should_disable_usermode_exception_handlers() != 0) {
|
if (exosphere_should_disable_usermode_exception_handlers() != 0) {
|
||||||
configitem_disable_usermode_exception_handlers();
|
configitem_disable_usermode_exception_handlers();
|
||||||
}
|
}
|
||||||
|
if (exosphere_should_enable_usermode_pmu_access()) {
|
||||||
|
configitem_enable_usermode_pmu_access();
|
||||||
|
}
|
||||||
|
|
||||||
/* Setup the Security Engine. */
|
/* Setup the Security Engine. */
|
||||||
setup_se();
|
setup_se();
|
||||||
@@ -532,6 +545,12 @@ void load_package2(coldboot_crt0_reloc_list_t *reloc_list) {
|
|||||||
case ATMOSPHERE_TARGET_FIRMWARE_810:
|
case ATMOSPHERE_TARGET_FIRMWARE_810:
|
||||||
MAKE_REG32(PMC_BASE + 0x360) = 0x14A;
|
MAKE_REG32(PMC_BASE + 0x360) = 0x14A;
|
||||||
break;
|
break;
|
||||||
|
case ATMOSPHERE_TARGET_FIRMWARE_900:
|
||||||
|
MAKE_REG32(PMC_BASE + 0x360) = 0x16B;
|
||||||
|
break;
|
||||||
|
case ATMOSPHERE_TARGET_FIRMWARE_910:
|
||||||
|
MAKE_REG32(PMC_BASE + 0x360) = 0x18C;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,9 @@ static inline uintptr_t get_nx_bootloader_mailbox_base(unsigned int targetfw) {
|
|||||||
#define PACKAGE2_MAXVER_600_610 0x8
|
#define PACKAGE2_MAXVER_600_610 0x8
|
||||||
#define PACKAGE2_MAXVER_620 0x9
|
#define PACKAGE2_MAXVER_620 0x9
|
||||||
#define PACKAGE2_MAXVER_700_800 0xA
|
#define PACKAGE2_MAXVER_700_800 0xA
|
||||||
#define PACKAGE2_MAXVER_810_CURRENT 0xB
|
#define PACKAGE2_MAXVER_810 0xB
|
||||||
|
#define PACKAGE2_MAXVER_900 0xC
|
||||||
|
#define PACKAGE2_MAXVER_910_CURRENT 0xD
|
||||||
|
|
||||||
#define PACKAGE2_MINVER_100 0x3
|
#define PACKAGE2_MINVER_100 0x3
|
||||||
#define PACKAGE2_MINVER_200 0x4
|
#define PACKAGE2_MINVER_200 0x4
|
||||||
@@ -82,7 +84,9 @@ static inline uintptr_t get_nx_bootloader_mailbox_base(unsigned int targetfw) {
|
|||||||
#define PACKAGE2_MINVER_600_610 0x9
|
#define PACKAGE2_MINVER_600_610 0x9
|
||||||
#define PACKAGE2_MINVER_620 0xA
|
#define PACKAGE2_MINVER_620 0xA
|
||||||
#define PACKAGE2_MINVER_700_800 0xB
|
#define PACKAGE2_MINVER_700_800 0xB
|
||||||
#define PACKAGE2_MINVER_810_CURRENT 0xC
|
#define PACKAGE2_MINVER_810 0xC
|
||||||
|
#define PACKAGE2_MINVER_900 0xD
|
||||||
|
#define PACKAGE2_MINVER_910_CURRENT 0xE
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
union {
|
union {
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ static void enable_lp0_wake_events(void) {
|
|||||||
|
|
||||||
static void notify_pmic_shutdown(void) {
|
static void notify_pmic_shutdown(void) {
|
||||||
clkrst_reboot(CARDEVICE_I2C5);
|
clkrst_reboot(CARDEVICE_I2C5);
|
||||||
|
i2c_init(I2C_5);
|
||||||
if (fuse_get_bootrom_patch_version() >= 0x7F) {
|
if (fuse_get_bootrom_patch_version() >= 0x7F) {
|
||||||
i2c_send_pmic_cpu_shutdown_cmd();
|
i2c_send_pmic_cpu_shutdown_cmd();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -186,6 +186,8 @@ void set_version_specific_smcs(void) {
|
|||||||
case ATMOSPHERE_TARGET_FIRMWARE_700:
|
case ATMOSPHERE_TARGET_FIRMWARE_700:
|
||||||
case ATMOSPHERE_TARGET_FIRMWARE_800:
|
case ATMOSPHERE_TARGET_FIRMWARE_800:
|
||||||
case ATMOSPHERE_TARGET_FIRMWARE_810:
|
case ATMOSPHERE_TARGET_FIRMWARE_810:
|
||||||
|
case ATMOSPHERE_TARGET_FIRMWARE_900:
|
||||||
|
case ATMOSPHERE_TARGET_FIRMWARE_910:
|
||||||
/* No more LoadSecureExpModKey. */
|
/* No more LoadSecureExpModKey. */
|
||||||
g_smc_user_table[0xE].handler = NULL;
|
g_smc_user_table[0xE].handler = NULL;
|
||||||
g_smc_user_table[0xC].id = 0xC300D60C;
|
g_smc_user_table[0xC].id = 0xC300D60C;
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ static bool is_user_keyslot_valid(unsigned int keyslot) {
|
|||||||
case ATMOSPHERE_TARGET_FIRMWARE_700:
|
case ATMOSPHERE_TARGET_FIRMWARE_700:
|
||||||
case ATMOSPHERE_TARGET_FIRMWARE_800:
|
case ATMOSPHERE_TARGET_FIRMWARE_800:
|
||||||
case ATMOSPHERE_TARGET_FIRMWARE_810:
|
case ATMOSPHERE_TARGET_FIRMWARE_810:
|
||||||
|
case ATMOSPHERE_TARGET_FIRMWARE_900:
|
||||||
|
case ATMOSPHERE_TARGET_FIRMWARE_910:
|
||||||
default:
|
default:
|
||||||
return keyslot <= 5;
|
return keyslot <= 5;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,13 @@
|
|||||||
#define MC_SMMU_TLB_FLUSH 0x30
|
#define MC_SMMU_TLB_FLUSH 0x30
|
||||||
#define MC_SMMU_PTC_FLUSH 0x34
|
#define MC_SMMU_PTC_FLUSH 0x34
|
||||||
#define MC_SMMU_ASID_SECURITY 0x38
|
#define MC_SMMU_ASID_SECURITY 0x38
|
||||||
|
#define MC_SMMU_ASID_SECURITY_1 0x3c
|
||||||
|
#define MC_SMMU_ASID_SECURITY_2 0x9e0
|
||||||
|
#define MC_SMMU_ASID_SECURITY_3 0x9e4
|
||||||
|
#define MC_SMMU_ASID_SECURITY_4 0x9e8
|
||||||
|
#define MC_SMMU_ASID_SECURITY_5 0x9ec
|
||||||
|
#define MC_SMMU_ASID_SECURITY_6 0x9f0
|
||||||
|
#define MC_SMMU_ASID_SECURITY_7 0x9f4
|
||||||
#define MC_SMMU_AFI_ASID 0x238
|
#define MC_SMMU_AFI_ASID 0x238
|
||||||
#define MC_SMMU_AVPC_ASID 0x23c
|
#define MC_SMMU_AVPC_ASID 0x23c
|
||||||
#define MC_SMMU_TSEC_ASID 0x294
|
#define MC_SMMU_TSEC_ASID 0x294
|
||||||
|
|||||||
@@ -37,19 +37,24 @@ static char g_bct0_buffer[BCTO_MAX_SIZE];
|
|||||||
|
|
||||||
#define CONFIG_LOG_LEVEL_KEY "log_level"
|
#define CONFIG_LOG_LEVEL_KEY "log_level"
|
||||||
|
|
||||||
#define DEFAULT_BCT0_FOR_DEBUG \
|
#define DEFAULT_BCT0 \
|
||||||
"BCT0\n"\
|
"BCT0\n"\
|
||||||
"[stage1]\n"\
|
"[stage1]\n"\
|
||||||
"stage2_path = atmosphere/fusee-secondary.bin\n"\
|
"stage2_path = atmosphere/fusee-secondary.bin\n"\
|
||||||
"stage2_mtc_path = atmosphere/fusee-mtc.bin\n"\
|
"stage2_mtc_path = atmosphere/fusee-mtc.bin\n"\
|
||||||
"stage2_addr = 0xF0000000\n"\
|
"stage2_addr = 0xF0000000\n"\
|
||||||
"stage2_entrypoint = 0xF0000000\n"
|
"stage2_entrypoint = 0xF0000000\n"\
|
||||||
|
"[exosphere]\n"\
|
||||||
|
"debugmode = 1\n"\
|
||||||
|
"debugmode_user = 0\n"\
|
||||||
|
"disable_user_exception_handlers = 0\n"\
|
||||||
|
"[stratosphere]\n"
|
||||||
|
|
||||||
static const char *load_config(void) {
|
static const char *load_config(void) {
|
||||||
if (!read_from_file(g_bct0_buffer, BCTO_MAX_SIZE, "atmosphere/BCT.ini")) {
|
if (!read_from_file(g_bct0_buffer, BCTO_MAX_SIZE, "atmosphere/config/BCT.ini")) {
|
||||||
print(SCREEN_LOG_LEVEL_DEBUG, "Failed to read BCT0 from SD!\n");
|
print(SCREEN_LOG_LEVEL_DEBUG, "Failed to read BCT0 from SD!\n");
|
||||||
print(SCREEN_LOG_LEVEL_DEBUG, "Using default BCT0!\n");
|
print(SCREEN_LOG_LEVEL_DEBUG, "Using default BCT0!\n");
|
||||||
memcpy(g_bct0_buffer, DEFAULT_BCT0_FOR_DEBUG, sizeof(DEFAULT_BCT0_FOR_DEBUG));
|
memcpy(g_bct0_buffer, DEFAULT_BCT0, sizeof(DEFAULT_BCT0));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (memcmp(g_bct0_buffer, "BCT0", 4) != 0) {
|
if (memcmp(g_bct0_buffer, "BCT0", 4) != 0) {
|
||||||
|
|||||||
@@ -37,6 +37,13 @@
|
|||||||
#define MC_SMMU_TLB_FLUSH 0x30
|
#define MC_SMMU_TLB_FLUSH 0x30
|
||||||
#define MC_SMMU_PTC_FLUSH 0x34
|
#define MC_SMMU_PTC_FLUSH 0x34
|
||||||
#define MC_SMMU_ASID_SECURITY 0x38
|
#define MC_SMMU_ASID_SECURITY 0x38
|
||||||
|
#define MC_SMMU_ASID_SECURITY_1 0x3c
|
||||||
|
#define MC_SMMU_ASID_SECURITY_2 0x9e0
|
||||||
|
#define MC_SMMU_ASID_SECURITY_3 0x9e4
|
||||||
|
#define MC_SMMU_ASID_SECURITY_4 0x9e8
|
||||||
|
#define MC_SMMU_ASID_SECURITY_5 0x9ec
|
||||||
|
#define MC_SMMU_ASID_SECURITY_6 0x9f0
|
||||||
|
#define MC_SMMU_ASID_SECURITY_7 0x9f4
|
||||||
#define MC_SMMU_AFI_ASID 0x238
|
#define MC_SMMU_AFI_ASID 0x238
|
||||||
#define MC_SMMU_AVPC_ASID 0x23c
|
#define MC_SMMU_AVPC_ASID 0x23c
|
||||||
#define MC_SMMU_TSEC_ASID 0x294
|
#define MC_SMMU_TSEC_ASID 0x294
|
||||||
|
|||||||
@@ -76,6 +76,12 @@ typedef enum {
|
|||||||
FS_VER_8_1_0,
|
FS_VER_8_1_0,
|
||||||
FS_VER_8_1_0_EXFAT,
|
FS_VER_8_1_0_EXFAT,
|
||||||
|
|
||||||
|
FS_VER_9_0_0,
|
||||||
|
FS_VER_9_0_0_EXFAT,
|
||||||
|
|
||||||
|
FS_VER_9_1_0,
|
||||||
|
FS_VER_9_1_0_EXFAT,
|
||||||
|
|
||||||
FS_VER_MAX,
|
FS_VER_MAX,
|
||||||
} emummc_fs_ver_t;
|
} emummc_fs_ver_t;
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#define EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV (1 << 1u)
|
#define EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV (1 << 1u)
|
||||||
#define EXOSPHERE_FLAG_IS_DEBUGMODE_USER (1 << 2u)
|
#define EXOSPHERE_FLAG_IS_DEBUGMODE_USER (1 << 2u)
|
||||||
#define EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS (1 << 3u)
|
#define EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS (1 << 3u)
|
||||||
|
#define EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS (1 << 4u)
|
||||||
#define EXOSPHERE_FLAGS_DEFAULT (EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV)
|
#define EXOSPHERE_FLAGS_DEFAULT (EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -48,5 +49,6 @@ _Static_assert(sizeof(exosphere_config_t) == 0x20 + sizeof(exo_emummc_config_t),
|
|||||||
#define EXOSPHERE_DEBUGMODE_PRIV_KEY "debugmode"
|
#define EXOSPHERE_DEBUGMODE_PRIV_KEY "debugmode"
|
||||||
#define EXOSPHERE_DEBUGMODE_USER_KEY "debugmode_user"
|
#define EXOSPHERE_DEBUGMODE_USER_KEY "debugmode_user"
|
||||||
#define EXOSPHERE_DISABLE_USERMODE_EXCEPTION_HANDLERS_KEY "disable_user_exception_handlers"
|
#define EXOSPHERE_DISABLE_USERMODE_EXCEPTION_HANDLERS_KEY "disable_user_exception_handlers"
|
||||||
|
#define EXOSPHERE_ENABLE_USERMODE_PMU_ACCESS_KEY "enable_user_pmu_access"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -411,6 +411,12 @@ static const uint8_t g_fs_hashes[FS_VER_MAX][0x8] = {
|
|||||||
|
|
||||||
"\x6B\x09\xB6\x7B\x29\xC0\x20\x24", /* FS_VER_8_1_0 */
|
"\x6B\x09\xB6\x7B\x29\xC0\x20\x24", /* FS_VER_8_1_0 */
|
||||||
"\xB4\xCA\xE1\xF2\x49\x65\xD9\x2E", /* FS_VER_8_1_0_EXFAT */
|
"\xB4\xCA\xE1\xF2\x49\x65\xD9\x2E", /* FS_VER_8_1_0_EXFAT */
|
||||||
|
|
||||||
|
"\x46\x87\x40\x76\x1E\x19\x3E\xB7", /* FS_VER_9_0_0 */
|
||||||
|
"\x7C\x95\x13\x76\xE5\xC1\x2D\xF8", /* FS_VER_9_0_0_EXFAT */
|
||||||
|
|
||||||
|
"\xB5\xE7\xA6\x4C\x6F\x5C\x4F\xE3", /* FS_VER_9_1_0 */
|
||||||
|
"\xF1\x96\xD1\x44\xD0\x44\x45\xB6", /* FS_VER_9_1_0_EXFAT */
|
||||||
};
|
};
|
||||||
|
|
||||||
kip1_header_t *apply_kip_ips_patches(kip1_header_t *kip, size_t kip_size, emummc_fs_ver_t *out_fs_ver) {
|
kip1_header_t *apply_kip_ips_patches(kip1_header_t *kip, size_t kip_size, emummc_fs_ver_t *out_fs_ver) {
|
||||||
|
|||||||
@@ -425,12 +425,68 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(800, proc_id_send)[] = {0xA9BF
|
|||||||
static const uint8_t MAKE_KERNEL_PATTERN_NAME(800, proc_id_recv)[] = {0x68, 0x03, 0x40, 0xF9, 0x08, 0x1D, 0x40, 0xF9, 0xE0, 0x03, 0x1B, 0xAA, 0x00, 0x01, 0x3F, 0xD6, 0xA9, 0x83, 0x50, 0xF8, 0xE8, 0x03, 0x16, 0x2A, 0xD6, 0x0A, 0x00, 0x11};
|
static const uint8_t MAKE_KERNEL_PATTERN_NAME(800, proc_id_recv)[] = {0x68, 0x03, 0x40, 0xF9, 0x08, 0x1D, 0x40, 0xF9, 0xE0, 0x03, 0x1B, 0xAA, 0x00, 0x01, 0x3F, 0xD6, 0xA9, 0x83, 0x50, 0xF8, 0xE8, 0x03, 0x16, 0x2A, 0xD6, 0x0A, 0x00, 0x11};
|
||||||
static const instruction_t MAKE_KERNEL_PATCH_NAME(800, proc_id_recv)[] = {0xA9BF2FEA, 0xF9404FEB, 0x2A1603EA, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF9400368, 0xF9401D08, 0xAA1B03E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0};
|
static const instruction_t MAKE_KERNEL_PATCH_NAME(800, proc_id_recv)[] = {0xA9BF2FEA, 0xF9404FEB, 0x2A1603EA, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF9400368, 0xF9401D08, 0xAA1B03E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0};
|
||||||
|
|
||||||
|
/*
|
||||||
|
stp x10, x11, [sp, #-0x10]!
|
||||||
|
ldr x11, [sp, #0x68]
|
||||||
|
mov w10, w22
|
||||||
|
lsl x10, x10, #2
|
||||||
|
ldr x10, [x11, x10]
|
||||||
|
mov x9, #0x0000ffffffffffff
|
||||||
|
and x8, x10, x9
|
||||||
|
mov x9, #0xffff000000000000
|
||||||
|
and x10, x10, x9
|
||||||
|
mov x9, #0xfffe000000000000
|
||||||
|
cmp x10, x9
|
||||||
|
beq #0x20
|
||||||
|
|
||||||
|
stp x8, x9, [sp, #-0x10]!
|
||||||
|
ldr x8, [x23]
|
||||||
|
ldr x8, [x8, #0x38]
|
||||||
|
mov x0, x23
|
||||||
|
blr x8
|
||||||
|
ldp x8, x9, [sp],#0x10
|
||||||
|
mov x8, x0
|
||||||
|
|
||||||
|
ldp x10, x11, [sp],#0x10
|
||||||
|
mov x0, x8
|
||||||
|
*/
|
||||||
|
static const uint8_t MAKE_KERNEL_PATTERN_NAME(900, proc_id_send)[] = {0xE8, 0x02, 0x40, 0xF9, 0x08, 0x1D, 0x40, 0xF9, 0xE0, 0x03, 0x17, 0xAA, 0x00, 0x01, 0x3F, 0xD6, 0xE8, 0x03, 0x16, 0x2A, 0xD6, 0x0A, 0x00, 0x11, 0x08, 0xF5, 0x7E, 0xD3};
|
||||||
|
static const instruction_t MAKE_KERNEL_PATCH_NAME(900, proc_id_send)[] = {0xA9BF2FEA, 0xF94037EB, 0x2A1603EA, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF94002E8, 0xF9401D08, 0xAA1703E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0};
|
||||||
|
/*
|
||||||
|
stp x10, x11, [sp, #-0x10]!
|
||||||
|
ldr x11, [sp, #0x90]
|
||||||
|
mov w10, w23
|
||||||
|
lsl x10, x10, #2
|
||||||
|
ldr x10, [x11, x10]
|
||||||
|
mov x9, #0x0000ffffffffffff
|
||||||
|
and x8, x10, x9
|
||||||
|
mov x9, #0xffff000000000000
|
||||||
|
and x10, x10, x9
|
||||||
|
mov x9, #0xfffe000000000000
|
||||||
|
cmp x10, x9
|
||||||
|
beq #0x20
|
||||||
|
|
||||||
|
stp x8, x9, [sp, #-0x10]!
|
||||||
|
ldr x8, [x27]
|
||||||
|
ldr x8, [x8, #0x38]
|
||||||
|
mov x0, x27
|
||||||
|
blr x8
|
||||||
|
ldp x8, x9, [sp],#0x10
|
||||||
|
mov x8, x0
|
||||||
|
|
||||||
|
ldp x10, x11, [sp],#0x10
|
||||||
|
mov x0, x8
|
||||||
|
*/
|
||||||
|
static const uint8_t MAKE_KERNEL_PATTERN_NAME(900, proc_id_recv)[] = {0x68, 0x03, 0x40, 0xF9, 0x08, 0x1D, 0x40, 0xF9, 0xE0, 0x03, 0x1B, 0xAA, 0x00, 0x01, 0x3F, 0xD6, 0xE8, 0x03, 0x17, 0x2A, 0xF7, 0x0A, 0x00, 0x11, 0x08, 0xF5, 0x7E, 0xD3};
|
||||||
|
static const instruction_t MAKE_KERNEL_PATCH_NAME(900, proc_id_recv)[] = {0xA9BF2FEA, 0xF9404BEB, 0x2A1703EA, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF9400368, 0xF9401D08, 0xAA1B03E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0};
|
||||||
|
|
||||||
/* svcControlCodeMemory Patches */
|
/* svcControlCodeMemory Patches */
|
||||||
/* b.eq -> nop */
|
/* b.eq -> nop */
|
||||||
static const instruction_t MAKE_KERNEL_PATCH_NAME(500, svc_control_codememory)[] = {MAKE_NOP};
|
static const instruction_t MAKE_KERNEL_PATCH_NAME(500, svc_control_codememory)[] = {MAKE_NOP};
|
||||||
static const instruction_t MAKE_KERNEL_PATCH_NAME(600, svc_control_codememory)[] = {MAKE_NOP};
|
static const instruction_t MAKE_KERNEL_PATCH_NAME(600, svc_control_codememory)[] = {MAKE_NOP};
|
||||||
static const instruction_t MAKE_KERNEL_PATCH_NAME(700, svc_control_codememory)[] = {MAKE_NOP};
|
static const instruction_t MAKE_KERNEL_PATCH_NAME(700, svc_control_codememory)[] = {MAKE_NOP};
|
||||||
static const instruction_t MAKE_KERNEL_PATCH_NAME(800, svc_control_codememory)[] = {MAKE_NOP};
|
static const instruction_t MAKE_KERNEL_PATCH_NAME(800, svc_control_codememory)[] = {MAKE_NOP};
|
||||||
|
static const instruction_t MAKE_KERNEL_PATCH_NAME(900, svc_control_codememory)[] = {MAKE_NOP};
|
||||||
|
|
||||||
/* Hook Definitions. */
|
/* Hook Definitions. */
|
||||||
static const kernel_patch_t g_kernel_patches_100[] = {
|
static const kernel_patch_t g_kernel_patches_100[] = {
|
||||||
@@ -592,6 +648,7 @@ static const kernel_patch_t g_kernel_patches_700[] = {
|
|||||||
.patch_offset = 0x3C6E0,
|
.patch_offset = 0x3C6E0,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const kernel_patch_t g_kernel_patches_800[] = {
|
static const kernel_patch_t g_kernel_patches_800[] = {
|
||||||
{ /* Send Message Process ID Patch. */
|
{ /* Send Message Process ID Patch. */
|
||||||
.pattern_size = 0x1C,
|
.pattern_size = 0x1C,
|
||||||
@@ -616,6 +673,30 @@ static const kernel_patch_t g_kernel_patches_800[] = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const kernel_patch_t g_kernel_patches_900[] = {
|
||||||
|
{ /* Send Message Process ID Patch. */
|
||||||
|
.pattern_size = 0x1C,
|
||||||
|
.pattern = MAKE_KERNEL_PATTERN_NAME(900, proc_id_send),
|
||||||
|
.pattern_hook_offset = 0x0,
|
||||||
|
.payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(900, proc_id_send))/sizeof(instruction_t),
|
||||||
|
.branch_back_offset = 0x10,
|
||||||
|
.payload = MAKE_KERNEL_PATCH_NAME(900, proc_id_send)
|
||||||
|
},
|
||||||
|
{ /* Receive Message Process ID Patch. */
|
||||||
|
.pattern_size = 0x1C,
|
||||||
|
.pattern = MAKE_KERNEL_PATTERN_NAME(900, proc_id_recv),
|
||||||
|
.pattern_hook_offset = 0x0,
|
||||||
|
.payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(900, proc_id_recv))/sizeof(instruction_t),
|
||||||
|
.branch_back_offset = 0x10,
|
||||||
|
.payload = MAKE_KERNEL_PATCH_NAME(900, proc_id_recv)
|
||||||
|
},
|
||||||
|
{ /* svcControlCodeMemory Patch. */
|
||||||
|
.payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(900, svc_control_codememory))/sizeof(instruction_t),
|
||||||
|
.payload = MAKE_KERNEL_PATCH_NAME(900, svc_control_codememory),
|
||||||
|
.patch_offset = 0x43DFC,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
#define KERNEL_PATCHES(vers) .num_patches = sizeof(g_kernel_patches_##vers)/sizeof(kernel_patch_t), .patches = g_kernel_patches_##vers,
|
#define KERNEL_PATCHES(vers) .num_patches = sizeof(g_kernel_patches_##vers)/sizeof(kernel_patch_t), .patches = g_kernel_patches_##vers,
|
||||||
|
|
||||||
/* Kernel Infos. */
|
/* Kernel Infos. */
|
||||||
@@ -673,6 +754,15 @@ static const kernel_info_t g_kernel_infos[] = {
|
|||||||
.embedded_ini_ptr = 0x168,
|
.embedded_ini_ptr = 0x168,
|
||||||
.free_code_space_offset = 0x607F0,
|
.free_code_space_offset = 0x607F0,
|
||||||
KERNEL_PATCHES(800)
|
KERNEL_PATCHES(800)
|
||||||
|
},
|
||||||
|
{ /* 9.0.0. */
|
||||||
|
.hash = {0xD7, 0x95, 0x65, 0x3A, 0x49, 0x4C, 0x5A, 0x9E, 0x2E, 0x04, 0xD6, 0x30, 0x7D, 0x79, 0xE1, 0xEE, 0x10, 0x2B, 0x30, 0xE0, 0x3E, 0xDD, 0x9F, 0xB3, 0x8A, 0x3C, 0x5E, 0xD3, 0x9B, 0x30, 0x11, 0x9B},
|
||||||
|
.hash_offset = 0x1C0,
|
||||||
|
.hash_size = 0x90000 - 0x1C0,
|
||||||
|
.embedded_ini_offset = 0x90000,
|
||||||
|
.embedded_ini_ptr = 0x180,
|
||||||
|
.free_code_space_offset = 0x65780,
|
||||||
|
KERNEL_PATCHES(900)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -154,7 +154,13 @@ int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, ui
|
|||||||
desired_keyblob = MASTERKEY_REVISION_700_800;
|
desired_keyblob = MASTERKEY_REVISION_700_800;
|
||||||
break;
|
break;
|
||||||
case ATMOSPHERE_TARGET_FIRMWARE_810:
|
case ATMOSPHERE_TARGET_FIRMWARE_810:
|
||||||
desired_keyblob = MASTERKEY_REVISION_810_CURRENT;
|
desired_keyblob = MASTERKEY_REVISION_810;
|
||||||
|
/* Fallthrough */
|
||||||
|
case ATMOSPHERE_TARGET_FIRMWARE_900:
|
||||||
|
desired_keyblob = MASTERKEY_REVISION_900;
|
||||||
|
/* Fallthrough */
|
||||||
|
case ATMOSPHERE_TARGET_FIRMWARE_910:
|
||||||
|
desired_keyblob = MASTERKEY_REVISION_910_CURRENT;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fatal_error("Unknown target firmware: %02x!", target_firmware);
|
fatal_error("Unknown target firmware: %02x!", target_firmware);
|
||||||
@@ -230,6 +236,7 @@ int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, ui
|
|||||||
case ATMOSPHERE_TARGET_FIRMWARE_700:
|
case ATMOSPHERE_TARGET_FIRMWARE_700:
|
||||||
case ATMOSPHERE_TARGET_FIRMWARE_800:
|
case ATMOSPHERE_TARGET_FIRMWARE_800:
|
||||||
case ATMOSPHERE_TARGET_FIRMWARE_810:
|
case ATMOSPHERE_TARGET_FIRMWARE_810:
|
||||||
|
case ATMOSPHERE_TARGET_FIRMWARE_900:
|
||||||
decrypt_data_into_keyslot(0xA, 0xF, devicekey_4x_seed, 0x10);
|
decrypt_data_into_keyslot(0xA, 0xF, devicekey_4x_seed, 0x10);
|
||||||
decrypt_data_into_keyslot(0xF, 0xF, devicekey_seed, 0x10);
|
decrypt_data_into_keyslot(0xF, 0xF, devicekey_seed, 0x10);
|
||||||
decrypt_data_into_keyslot(0xE, 0xC, masterkey_4x_seed, 0x10);
|
decrypt_data_into_keyslot(0xE, 0xC, masterkey_4x_seed, 0x10);
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ int main(int argc, void **argv) {
|
|||||||
/* Load BCT0 from SD if needed. */
|
/* Load BCT0 from SD if needed. */
|
||||||
if (strcmp(g_stage2_args->bct0, "") == 0) {
|
if (strcmp(g_stage2_args->bct0, "") == 0) {
|
||||||
uint32_t bct_tmp_buf[sizeof(g_stage2_args->bct0) / sizeof(uint32_t)] = {0};
|
uint32_t bct_tmp_buf[sizeof(g_stage2_args->bct0) / sizeof(uint32_t)] = {0};
|
||||||
if (!read_from_file(bct_tmp_buf, sizeof(bct_tmp_buf) - 1, "atmosphere/BCT.ini")) {
|
if (!read_from_file(bct_tmp_buf, sizeof(bct_tmp_buf) - 1, "atmosphere/config/BCT.ini")) {
|
||||||
fatal_error("Failed to read BCT0 from SD!\n");
|
fatal_error("Failed to read BCT0 from SD!\n");
|
||||||
}
|
}
|
||||||
memcpy(g_stage2_args->bct0, bct_tmp_buf, sizeof(bct_tmp_buf));
|
memcpy(g_stage2_args->bct0, bct_tmp_buf, sizeof(bct_tmp_buf));
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ static const uint8_t mkey_vectors_dev[MASTERKEY_REVISION_MAX][0x10] =
|
|||||||
{0x6F, 0xD2, 0x84, 0x1D, 0x05, 0xEC, 0x40, 0x94, 0x5F, 0x18, 0xB3, 0x81, 0x09, 0x98, 0x8D, 0x4E}, /* Master key 05 encrypted with Master key 06. */
|
{0x6F, 0xD2, 0x84, 0x1D, 0x05, 0xEC, 0x40, 0x94, 0x5F, 0x18, 0xB3, 0x81, 0x09, 0x98, 0x8D, 0x4E}, /* Master key 05 encrypted with Master key 06. */
|
||||||
{0x37, 0xAF, 0xAB, 0x35, 0x79, 0x09, 0xD9, 0x48, 0x29, 0xD2, 0xDB, 0xA5, 0xA5, 0xF5, 0x30, 0x19}, /* Master key 06 encrypted with Master key 07. */
|
{0x37, 0xAF, 0xAB, 0x35, 0x79, 0x09, 0xD9, 0x48, 0x29, 0xD2, 0xDB, 0xA5, 0xA5, 0xF5, 0x30, 0x19}, /* Master key 06 encrypted with Master key 07. */
|
||||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* TODO: Master key 07 encrypted with Master key 08. */
|
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* TODO: Master key 07 encrypted with Master key 08. */
|
||||||
|
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* TODO: Master key 08 encrypted with Master key 09. */
|
||||||
|
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* TODO: Master key 09 encrypted with Master key 0A. */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Retail unit keys. */
|
/* Retail unit keys. */
|
||||||
@@ -54,6 +56,8 @@ static const uint8_t mkey_vectors[MASTERKEY_REVISION_MAX][0x10] =
|
|||||||
{0x1E, 0x1E, 0x22, 0xC0, 0x5A, 0x33, 0x3C, 0xB9, 0x0B, 0xA9, 0x03, 0x04, 0xBA, 0xDB, 0x07, 0x57}, /* Master key 05 encrypted with Master key 06. */
|
{0x1E, 0x1E, 0x22, 0xC0, 0x5A, 0x33, 0x3C, 0xB9, 0x0B, 0xA9, 0x03, 0x04, 0xBA, 0xDB, 0x07, 0x57}, /* Master key 05 encrypted with Master key 06. */
|
||||||
{0xA4, 0xD4, 0x52, 0x6F, 0xD1, 0xE4, 0x36, 0xAA, 0x9F, 0xCB, 0x61, 0x27, 0x1C, 0x67, 0x65, 0x1F}, /* Master key 06 encrypted with Master key 07. */
|
{0xA4, 0xD4, 0x52, 0x6F, 0xD1, 0xE4, 0x36, 0xAA, 0x9F, 0xCB, 0x61, 0x27, 0x1C, 0x67, 0x65, 0x1F}, /* Master key 06 encrypted with Master key 07. */
|
||||||
{0xEA, 0x60, 0xB3, 0xEA, 0xCE, 0x8F, 0x24, 0x46, 0x7D, 0x33, 0x9C, 0xD1, 0xBC, 0x24, 0x98, 0x29}, /* Master key 07 encrypted with Master key 08. */
|
{0xEA, 0x60, 0xB3, 0xEA, 0xCE, 0x8F, 0x24, 0x46, 0x7D, 0x33, 0x9C, 0xD1, 0xBC, 0x24, 0x98, 0x29}, /* Master key 07 encrypted with Master key 08. */
|
||||||
|
{0x4D, 0xD9, 0x98, 0x42, 0x45, 0x0D, 0xB1, 0x3C, 0x52, 0x0C, 0x9A, 0x44, 0xBB, 0xAD, 0xAF, 0x80}, /* Master key 08 encrypted with Master key 09. */
|
||||||
|
{0xB8, 0x96, 0x9E, 0x4A, 0x00, 0x0D, 0xD6, 0x28, 0xB3, 0xD1, 0xDB, 0x68, 0x5F, 0xFB, 0xE1, 0x2A}, /* Master key 09 encrypted with Master key 0A. */
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool check_mkey_revision(unsigned int revision, bool is_retail) {
|
static bool check_mkey_revision(unsigned int revision, bool is_retail) {
|
||||||
|
|||||||
@@ -19,8 +19,8 @@
|
|||||||
|
|
||||||
/* This is glue code to enable master key support across versions. */
|
/* This is glue code to enable master key support across versions. */
|
||||||
|
|
||||||
/* TODO: Update to 0xA on release of new master key. */
|
/* TODO: Update to 0xC on release of new master key. */
|
||||||
#define MASTERKEY_REVISION_MAX 0x9
|
#define MASTERKEY_REVISION_MAX 0xB
|
||||||
|
|
||||||
#define MASTERKEY_REVISION_100_230 0x00
|
#define MASTERKEY_REVISION_100_230 0x00
|
||||||
#define MASTERKEY_REVISION_300 0x01
|
#define MASTERKEY_REVISION_300 0x01
|
||||||
@@ -30,7 +30,9 @@
|
|||||||
#define MASTERKEY_REVISION_600_610 0x05
|
#define MASTERKEY_REVISION_600_610 0x05
|
||||||
#define MASTERKEY_REVISION_620 0x06
|
#define MASTERKEY_REVISION_620 0x06
|
||||||
#define MASTERKEY_REVISION_700_800 0x07
|
#define MASTERKEY_REVISION_700_800 0x07
|
||||||
#define MASTERKEY_REVISION_810_CURRENT 0x08
|
#define MASTERKEY_REVISION_810 0x08
|
||||||
|
#define MASTERKEY_REVISION_900 0x09
|
||||||
|
#define MASTERKEY_REVISION_910_CURRENT 0x0A
|
||||||
|
|
||||||
#define MASTERKEY_NUM_NEW_DEVICE_KEYS (MASTERKEY_REVISION_MAX - MASTERKEY_REVISION_400_410)
|
#define MASTERKEY_NUM_NEW_DEVICE_KEYS (MASTERKEY_REVISION_MAX - MASTERKEY_REVISION_400_410)
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,13 @@
|
|||||||
#define MC_SMMU_TLB_FLUSH 0x30
|
#define MC_SMMU_TLB_FLUSH 0x30
|
||||||
#define MC_SMMU_PTC_FLUSH 0x34
|
#define MC_SMMU_PTC_FLUSH 0x34
|
||||||
#define MC_SMMU_ASID_SECURITY 0x38
|
#define MC_SMMU_ASID_SECURITY 0x38
|
||||||
|
#define MC_SMMU_ASID_SECURITY_1 0x3c
|
||||||
|
#define MC_SMMU_ASID_SECURITY_2 0x9e0
|
||||||
|
#define MC_SMMU_ASID_SECURITY_3 0x9e4
|
||||||
|
#define MC_SMMU_ASID_SECURITY_4 0x9e8
|
||||||
|
#define MC_SMMU_ASID_SECURITY_5 0x9ec
|
||||||
|
#define MC_SMMU_ASID_SECURITY_6 0x9f0
|
||||||
|
#define MC_SMMU_ASID_SECURITY_7 0x9f4
|
||||||
#define MC_SMMU_AFI_ASID 0x238
|
#define MC_SMMU_AFI_ASID 0x238
|
||||||
#define MC_SMMU_AVPC_ASID 0x23c
|
#define MC_SMMU_AVPC_ASID 0x23c
|
||||||
#define MC_SMMU_TSEC_ASID 0x294
|
#define MC_SMMU_TSEC_ASID 0x294
|
||||||
|
|||||||
@@ -158,6 +158,13 @@ static int exosphere_ini_handler(void *user, const char *section, const char *na
|
|||||||
} else {
|
} else {
|
||||||
exo_cfg->flags &= ~(EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS);
|
exo_cfg->flags &= ~(EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS);
|
||||||
}
|
}
|
||||||
|
} else if (strcmp(name, EXOSPHERE_ENABLE_USERMODE_PMU_ACCESS_KEY) == 0) {
|
||||||
|
sscanf(value, "%d", &tmp);
|
||||||
|
if (tmp) {
|
||||||
|
exo_cfg->flags |= EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS;
|
||||||
|
} else {
|
||||||
|
exo_cfg->flags &= ~(EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -208,11 +215,15 @@ static uint32_t nxboot_get_target_firmware(const void *package1loader) {
|
|||||||
}
|
}
|
||||||
case 0x0F: /* 7.0.0 - 7.0.1 */
|
case 0x0F: /* 7.0.0 - 7.0.1 */
|
||||||
return ATMOSPHERE_TARGET_FIRMWARE_700;
|
return ATMOSPHERE_TARGET_FIRMWARE_700;
|
||||||
case 0x10: { /* 8.0.0 - 8.1.0 */
|
case 0x10: { /* 8.0.0 - 9.0.0 */
|
||||||
if (memcmp(package1loader_header->build_timestamp, "20190314", 8) == 0) {
|
if (memcmp(package1loader_header->build_timestamp, "20190314", 8) == 0) {
|
||||||
return ATMOSPHERE_TARGET_FIRMWARE_800;
|
return ATMOSPHERE_TARGET_FIRMWARE_800;
|
||||||
} else if (memcmp(package1loader_header->build_timestamp, "20190531", 8) == 0) {
|
} else if (memcmp(package1loader_header->build_timestamp, "20190531", 8) == 0) {
|
||||||
return ATMOSPHERE_TARGET_FIRMWARE_810;
|
return ATMOSPHERE_TARGET_FIRMWARE_810;
|
||||||
|
} else if (memcmp(package1loader_header->build_timestamp, "20190809", 8) == 0) {
|
||||||
|
return ATMOSPHERE_TARGET_FIRMWARE_900;
|
||||||
|
} else if (memcmp(package1loader_header->build_timestamp, "20191021", 8) == 0) {
|
||||||
|
return ATMOSPHERE_TARGET_FIRMWARE_910;
|
||||||
} else {
|
} else {
|
||||||
fatal_error("[NXBOOT] Unable to identify package1!\n");
|
fatal_error("[NXBOOT] Unable to identify package1!\n");
|
||||||
}
|
}
|
||||||
@@ -360,6 +371,10 @@ static void nxboot_configure_stratosphere(uint32_t target_firmware) {
|
|||||||
if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_400 && !(fuse_get_reserved_odm(7) & ~0x0000000F)) {
|
if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_400 && !(fuse_get_reserved_odm(7) & ~0x0000000F)) {
|
||||||
kip_patches_set_enable_nogc();
|
kip_patches_set_enable_nogc();
|
||||||
}
|
}
|
||||||
|
/* Check if the fuses are < 9.0.0, but firmware is >= 9.0.0 */
|
||||||
|
if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_900 && !(fuse_get_reserved_odm(7) & ~0x000003FF)) {
|
||||||
|
kip_patches_set_enable_nogc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -653,7 +653,8 @@ int nxfs_mount_emummc_file(const char *emummc_path, int num_parts, uint64_t part
|
|||||||
is_exfat = (rc == 1);
|
is_exfat = (rc == 1);
|
||||||
|
|
||||||
/* Reject single part in FAT32. */
|
/* Reject single part in FAT32. */
|
||||||
if (!is_exfat && (num_parts <= 1)) {
|
/* NOTE: This check has no effect in the current design. */
|
||||||
|
if (!is_exfat && (num_parts < 1)) {
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ static bool package2_validate_metadata(package2_meta_t *metadata, uint8_t data[]
|
|||||||
|
|
||||||
/* Perform version checks. */
|
/* Perform version checks. */
|
||||||
/* We will be compatible with all package2s released before current, but not newer ones. */
|
/* We will be compatible with all package2s released before current, but not newer ones. */
|
||||||
if (metadata->version_max >= PACKAGE2_MINVER_THEORETICAL && metadata->version_min < PACKAGE2_MAXVER_810_CURRENT) {
|
if (metadata->version_max >= PACKAGE2_MINVER_THEORETICAL && metadata->version_min < PACKAGE2_MAXVER_910_CURRENT) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,9 @@
|
|||||||
#define PACKAGE2_MAXVER_600_610 0x8
|
#define PACKAGE2_MAXVER_600_610 0x8
|
||||||
#define PACKAGE2_MAXVER_620 0x9
|
#define PACKAGE2_MAXVER_620 0x9
|
||||||
#define PACKAGE2_MAXVER_700_800 0xA
|
#define PACKAGE2_MAXVER_700_800 0xA
|
||||||
#define PACKAGE2_MAXVER_810_CURRENT 0xB
|
#define PACKAGE2_MAXVER_810 0xB
|
||||||
|
#define PACKAGE2_MAXVER_900 0xC
|
||||||
|
#define PACKAGE2_MAXVER_910_CURRENT 0xD
|
||||||
|
|
||||||
#define PACKAGE2_MINVER_100 0x3
|
#define PACKAGE2_MINVER_100 0x3
|
||||||
#define PACKAGE2_MINVER_200 0x4
|
#define PACKAGE2_MINVER_200 0x4
|
||||||
@@ -48,7 +50,9 @@
|
|||||||
#define PACKAGE2_MINVER_600_610 0x9
|
#define PACKAGE2_MINVER_600_610 0x9
|
||||||
#define PACKAGE2_MINVER_620 0xA
|
#define PACKAGE2_MINVER_620 0xA
|
||||||
#define PACKAGE2_MINVER_700_800 0xB
|
#define PACKAGE2_MINVER_700_800 0xB
|
||||||
#define PACKAGE2_MINVER_810_CURRENT 0xC
|
#define PACKAGE2_MINVER_810 0xC
|
||||||
|
#define PACKAGE2_MINVER_900 0xD
|
||||||
|
#define PACKAGE2_MINVER_910_CURRENT 0xE
|
||||||
|
|
||||||
#define NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS ((void *)(0xA9800000ull))
|
#define NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS ((void *)(0xA9800000ull))
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,13 @@
|
|||||||
#define MC_SMMU_TLB_FLUSH 0x30
|
#define MC_SMMU_TLB_FLUSH 0x30
|
||||||
#define MC_SMMU_PTC_FLUSH 0x34
|
#define MC_SMMU_PTC_FLUSH 0x34
|
||||||
#define MC_SMMU_ASID_SECURITY 0x38
|
#define MC_SMMU_ASID_SECURITY 0x38
|
||||||
|
#define MC_SMMU_ASID_SECURITY_1 0x3c
|
||||||
|
#define MC_SMMU_ASID_SECURITY_2 0x9e0
|
||||||
|
#define MC_SMMU_ASID_SECURITY_3 0x9e4
|
||||||
|
#define MC_SMMU_ASID_SECURITY_4 0x9e8
|
||||||
|
#define MC_SMMU_ASID_SECURITY_5 0x9ec
|
||||||
|
#define MC_SMMU_ASID_SECURITY_6 0x9f0
|
||||||
|
#define MC_SMMU_ASID_SECURITY_7 0x9f4
|
||||||
#define MC_SMMU_AFI_ASID 0x238
|
#define MC_SMMU_AFI_ASID 0x238
|
||||||
#define MC_SMMU_AVPC_ASID 0x23c
|
#define MC_SMMU_AVPC_ASID 0x23c
|
||||||
#define MC_SMMU_TSEC_ASID 0x294
|
#define MC_SMMU_TSEC_ASID 0x294
|
||||||
|
|||||||
@@ -47,19 +47,29 @@ static const uint8_t AL16 zeroes[0x10] = {
|
|||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Note: 9.0.0 did not change the TSEC firmware. Thus, the root key is the same. */
|
||||||
|
/* To avoid distribution of more and more sept binaries, we will simply derive the 9.0.0 master key */
|
||||||
|
/* on 8.1.0 and 9.0.0. Exosphere supports this already with no issues. */
|
||||||
|
|
||||||
static const uint8_t AL16 master_kek_seeds[DERIVATION_ID_MAX][0x10] = {
|
static const uint8_t AL16 master_kek_seeds[DERIVATION_ID_MAX][0x10] = {
|
||||||
{0x9A, 0x3E, 0xA9, 0xAB, 0xFD, 0x56, 0x46, 0x1C, 0x9B, 0xF6, 0x48, 0x7F, 0x5C, 0xFA, 0x09, 0x5C},
|
{0x9A, 0x3E, 0xA9, 0xAB, 0xFD, 0x56, 0x46, 0x1C, 0x9B, 0xF6, 0x48, 0x7F, 0x5C, 0xFA, 0x09, 0x5C},
|
||||||
{0xDE, 0xDC, 0xE3, 0x39, 0x30, 0x88, 0x16, 0xF8, 0xAE, 0x97, 0xAD, 0xEC, 0x64, 0x2D, 0x41, 0x41},
|
/* 8.1.0: {0xDE, 0xDC, 0xE3, 0x39, 0x30, 0x88, 0x16, 0xF8, 0xAE, 0x97, 0xAD, 0xEC, 0x64, 0x2D, 0x41, 0x41}, */
|
||||||
|
/* 9.0.0: {0x1A, 0xEC, 0x11, 0x82, 0x2B, 0x32, 0x38, 0x7A, 0x2B, 0xED, 0xBA, 0x01, 0x47, 0x7E, 0x3B, 0x67}, */
|
||||||
|
{0x30, 0x3F, 0x02, 0x7E, 0xD8, 0x38, 0xEC, 0xD7, 0x93, 0x25, 0x34, 0xB5, 0x30, 0xEB, 0xCA, 0x7A},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint8_t AL16 master_devkey_seeds[DERIVATION_ID_MAX][0x10] = {
|
static const uint8_t AL16 master_devkey_seeds[DERIVATION_ID_MAX][0x10] = {
|
||||||
{0x8F, 0x77, 0x5A, 0x96, 0xB0, 0x94, 0xFD, 0x8D, 0x28, 0xE4, 0x19, 0xC8, 0x16, 0x1C, 0xDB, 0x3D},
|
{0x8F, 0x77, 0x5A, 0x96, 0xB0, 0x94, 0xFD, 0x8D, 0x28, 0xE4, 0x19, 0xC8, 0x16, 0x1C, 0xDB, 0x3D},
|
||||||
{0x67, 0x62, 0xD4, 0x8E, 0x55, 0xCF, 0xFF, 0x41, 0x31, 0x15, 0x3B, 0x24, 0x0C, 0x7C, 0x07, 0xAE},
|
/* 8.1.0: {0x67, 0x62, 0xD4, 0x8E, 0x55, 0xCF, 0xFF, 0x41, 0x31, 0x15, 0x3B, 0x24, 0x0C, 0x7C, 0x07, 0xAE}, */
|
||||||
|
/* 9.0.0: {0x4A, 0xC3, 0x4E, 0x14, 0x8B, 0x96, 0x4A, 0xD5, 0xD4, 0x99, 0x73, 0xC4, 0x45, 0xAB, 0x8B, 0x49}, */
|
||||||
|
{0x14, 0xB8, 0x74, 0x12, 0xCB, 0xBD, 0x0B, 0x8F, 0x20, 0xFB, 0x30, 0xDA, 0x27, 0xE4, 0x58, 0x94},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint8_t AL16 master_devkey_vectors[DERIVATION_ID_MAX][0x10] = {
|
static const uint8_t AL16 master_devkey_vectors[DERIVATION_ID_MAX][0x10] = {
|
||||||
{0xD8, 0xD3, 0x67, 0x4F, 0xF3, 0xA2, 0xA4, 0x4E, 0xE4, 0x04, 0x37, 0xC2, 0xD9, 0xCF, 0x41, 0x6F},
|
{0xD8, 0xD3, 0x67, 0x4F, 0xF3, 0xA2, 0xA4, 0x4E, 0xE4, 0x04, 0x37, 0xC2, 0xD9, 0xCF, 0x41, 0x6F},
|
||||||
{0x72, 0xD0, 0xAD, 0xEB, 0xE1, 0xF6, 0x35, 0x90, 0xB4, 0x43, 0xCC, 0x4B, 0xC4, 0xDC, 0x88, 0x0A},
|
/* 8.1.0: {0x72, 0xD0, 0xAD, 0xEB, 0xE1, 0xF6, 0x35, 0x90, 0xB4, 0x43, 0xCC, 0x4B, 0xC4, 0xDC, 0x88, 0x0A}, */
|
||||||
|
/* 9.0.0: {0x8B, 0xD6, 0x13, 0x2F, 0xC3, 0x4D, 0x53, 0x2D, 0x10, 0xA1, 0x63, 0x85, 0x49, 0x2B, 0xCF, 0x3F}, */
|
||||||
|
{0xA8, 0x58, 0x05, 0x8D, 0xDD, 0x9A, 0xA0, 0x2F, 0x01, 0x62, 0x4A, 0x6B, 0xC6, 0x77, 0xE9, 0x86},
|
||||||
};
|
};
|
||||||
|
|
||||||
void derive_keys(void) {
|
void derive_keys(void) {
|
||||||
|
|||||||
@@ -37,6 +37,13 @@
|
|||||||
#define MC_SMMU_TLB_FLUSH 0x30
|
#define MC_SMMU_TLB_FLUSH 0x30
|
||||||
#define MC_SMMU_PTC_FLUSH 0x34
|
#define MC_SMMU_PTC_FLUSH 0x34
|
||||||
#define MC_SMMU_ASID_SECURITY 0x38
|
#define MC_SMMU_ASID_SECURITY 0x38
|
||||||
|
#define MC_SMMU_ASID_SECURITY_1 0x3c
|
||||||
|
#define MC_SMMU_ASID_SECURITY_2 0x9e0
|
||||||
|
#define MC_SMMU_ASID_SECURITY_3 0x9e4
|
||||||
|
#define MC_SMMU_ASID_SECURITY_4 0x9e8
|
||||||
|
#define MC_SMMU_ASID_SECURITY_5 0x9ec
|
||||||
|
#define MC_SMMU_ASID_SECURITY_6 0x9f0
|
||||||
|
#define MC_SMMU_ASID_SECURITY_7 0x9f4
|
||||||
#define MC_SMMU_AFI_ASID 0x238
|
#define MC_SMMU_AFI_ASID 0x238
|
||||||
#define MC_SMMU_AVPC_ASID 0x23c
|
#define MC_SMMU_AVPC_ASID 0x23c
|
||||||
#define MC_SMMU_TSEC_ASID 0x294
|
#define MC_SMMU_TSEC_ASID 0x294
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
MODULES := loader pm sm boot ams_mitm spl eclct.stub ro creport fatal dmnt
|
MODULES := loader pm sm boot ams_mitm spl eclct.stub ro creport fatal dmnt boot2
|
||||||
|
|
||||||
SUBFOLDERS := libstratosphere $(MODULES)
|
SUBFOLDERS := libstratosphere $(MODULES)
|
||||||
|
|
||||||
|
|||||||
@@ -26,12 +26,12 @@ endif
|
|||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
TARGET := $(notdir $(CURDIR))
|
TARGET := $(notdir $(CURDIR))
|
||||||
BUILD := build
|
BUILD := build
|
||||||
SOURCES := source source/fs_mitm source/set_mitm source/bpc_mitm source/ns_mitm
|
SOURCES := source source/fs_mitm source/set_mitm source/bpc_mitm source/ns_mitm source/hid_mitm
|
||||||
DATA := data
|
DATA := data
|
||||||
INCLUDES := include ../../common/include
|
INCLUDES := include ../../common/include
|
||||||
EXEFS_SRC := exefs_src
|
EXEFS_SRC := exefs_src
|
||||||
|
|
||||||
DEFINES := -DRESULT_ABORT_ON_ASSERT -DATMOSPHERE_GIT_BRANCH=\"$(AMSBRANCH)\" -DATMOSPHERE_GIT_REV=\"$(AMSREV)\"
|
DEFINES := -DATMOSPHERE_GIT_BRANCH=\"$(AMSBRANCH)\" -DATMOSPHERE_GIT_REV=\"$(AMSREV)\"
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# options for code generation
|
# options for code generation
|
||||||
@@ -45,8 +45,18 @@ CFLAGS += $(INCLUDE) -D__SWITCH__
|
|||||||
|
|
||||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17
|
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17
|
||||||
|
|
||||||
|
CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \
|
||||||
|
-Wl,--wrap,__cxa_throw \
|
||||||
|
-Wl,--wrap,__cxa_rethrow \
|
||||||
|
-Wl,--wrap,__cxa_allocate_exception \
|
||||||
|
-Wl,--wrap,__cxa_begin_catch \
|
||||||
|
-Wl,--wrap,__cxa_end_catch \
|
||||||
|
-Wl,--wrap,__cxa_call_unexpected \
|
||||||
|
-Wl,--wrap,__cxa_call_terminate \
|
||||||
|
-Wl,--wrap,__gxx_personality_v0
|
||||||
|
|
||||||
ASFLAGS := -g $(ARCH)
|
ASFLAGS := -g $(ARCH)
|
||||||
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) $(CXXWRAPS) -Wl,-Map,$(notdir $*.map)
|
||||||
|
|
||||||
LIBS := -lstratosphere -lnx
|
LIBS := -lstratosphere -lnx
|
||||||
|
|
||||||
|
|||||||
54
stratosphere/ams_mitm/source/amsmitm_debug.cpp
Normal file
54
stratosphere/ams_mitm/source/amsmitm_debug.cpp
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope 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/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "amsmitm_debug.hpp"
|
||||||
|
|
||||||
|
namespace ams::mitm {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
os::Mutex g_throw_lock;
|
||||||
|
bool g_threw;
|
||||||
|
Result g_throw_result;
|
||||||
|
|
||||||
|
|
||||||
|
void DebugThrowThreadFunc(void *arg);
|
||||||
|
|
||||||
|
constexpr size_t DebugThrowThreadStackSize = 0x4000;
|
||||||
|
constexpr int DebugThrowThreadPriority = 49;
|
||||||
|
os::StaticThread<DebugThrowThreadStackSize> g_debug_throw_thread(&DebugThrowThreadFunc, nullptr, DebugThrowThreadPriority);
|
||||||
|
|
||||||
|
void DebugThrowThreadFunc(void *arg) {
|
||||||
|
/* TODO: Better heuristic for fatal startup than sleep. */
|
||||||
|
svcSleepThread(10'000'000'000ul);
|
||||||
|
fatalThrow(g_throw_result.GetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThrowResultForDebug(Result res) {
|
||||||
|
std::scoped_lock lk(g_throw_lock);
|
||||||
|
|
||||||
|
if (g_threw) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_throw_result = res;
|
||||||
|
g_threw = true;
|
||||||
|
R_ASSERT(g_debug_throw_thread.Start());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -13,11 +13,11 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <switch.h>
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
class HidManagement {
|
namespace ams::mitm {
|
||||||
public:
|
|
||||||
static Result GetKeysDown(u64 *keys);
|
void ThrowResultForDebug(Result res);
|
||||||
};
|
|
||||||
|
}
|
||||||
216
stratosphere/ams_mitm/source/amsmitm_fs_utils.cpp
Normal file
216
stratosphere/ams_mitm/source/amsmitm_fs_utils.cpp
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope 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/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "amsmitm_initialization.hpp"
|
||||||
|
#include "amsmitm_fs_utils.hpp"
|
||||||
|
|
||||||
|
namespace ams::mitm::fs {
|
||||||
|
|
||||||
|
using namespace ams::fs;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/* Globals. */
|
||||||
|
FsFileSystem g_sd_filesystem;
|
||||||
|
|
||||||
|
/* Helpers. */
|
||||||
|
Result EnsureSdInitialized() {
|
||||||
|
R_UNLESS(serviceIsActive(&g_sd_filesystem.s), ams::fs::ResultSdCardNotPresent());
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, const char *src_path) {
|
||||||
|
if (src_path[0] == '/') {
|
||||||
|
std::snprintf(dst_path, dst_path_size, "/atmosphere%s", src_path);
|
||||||
|
} else {
|
||||||
|
std::snprintf(dst_path, dst_path_size, "/atmosphere/%s", src_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, const char *subdir, const char *src_path) {
|
||||||
|
if (src_path[0] == '/') {
|
||||||
|
std::snprintf(dst_path, dst_path_size, "/atmosphere/%s%s", subdir, src_path);
|
||||||
|
} else {
|
||||||
|
std::snprintf(dst_path, dst_path_size, "/atmosphere/%s/%s", subdir, src_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, ncm::ProgramId program_id, const char *src_path) {
|
||||||
|
if (src_path[0] == '/') {
|
||||||
|
std::snprintf(dst_path, dst_path_size, "/atmosphere/contents/%016lx%s", static_cast<u64>(program_id), src_path);
|
||||||
|
} else {
|
||||||
|
std::snprintf(dst_path, dst_path_size, "/atmosphere/contents/%016lx/%s", static_cast<u64>(program_id), src_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, ncm::ProgramId program_id, const char *subdir, const char *src_path) {
|
||||||
|
if (src_path[0] == '/') {
|
||||||
|
std::snprintf(dst_path, dst_path_size, "/atmosphere/contents/%016lx/%s%s", static_cast<u64>(program_id), subdir, src_path);
|
||||||
|
} else {
|
||||||
|
std::snprintf(dst_path, dst_path_size, "/atmosphere/contents/%016lx/%s/%s", static_cast<u64>(program_id), subdir, src_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormatAtmosphereRomfsPath(char *dst_path, size_t dst_path_size, ncm::ProgramId program_id, const char *src_path) {
|
||||||
|
return FormatAtmosphereSdPath(dst_path, dst_path_size, program_id, "romfs", src_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGlobalSdCardFileSystem() {
|
||||||
|
R_ASSERT(fsOpenSdCardFileSystem(&g_sd_filesystem));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CreateSdFile(const char *path, s64 size, s32 option) {
|
||||||
|
R_TRY(EnsureSdInitialized());
|
||||||
|
return fsFsCreateFile(&g_sd_filesystem, path, size, option);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CreateAtmosphereSdFile(const char *path, s64 size, s32 option) {
|
||||||
|
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||||
|
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
|
||||||
|
return CreateSdFile(fixed_path, size, option);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result OpenSdFile(FsFile *out, const char *path, u32 mode) {
|
||||||
|
R_TRY(EnsureSdInitialized());
|
||||||
|
return fsFsOpenFile(&g_sd_filesystem, path, mode, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result OpenAtmosphereSdFile(FsFile *out, const char *path, u32 mode) {
|
||||||
|
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||||
|
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
|
||||||
|
return OpenSdFile(out, fixed_path, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result OpenAtmosphereSdFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode) {
|
||||||
|
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||||
|
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), program_id, path);
|
||||||
|
return OpenSdFile(out, fixed_path, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result OpenAtmosphereSdRomfsFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode) {
|
||||||
|
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||||
|
FormatAtmosphereRomfsPath(fixed_path, sizeof(fixed_path), program_id, path);
|
||||||
|
return OpenSdFile(out, fixed_path, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result OpenAtmosphereRomfsFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode, FsFileSystem *fs) {
|
||||||
|
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||||
|
FormatAtmosphereRomfsPath(fixed_path, sizeof(fixed_path), program_id, path);
|
||||||
|
return fsFsOpenFile(fs, fixed_path, mode, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CreateSdDirectory(const char *path) {
|
||||||
|
R_TRY(EnsureSdInitialized());
|
||||||
|
return fsFsCreateDirectory(&g_sd_filesystem, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CreateAtmosphereSdDirectory(const char *path) {
|
||||||
|
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||||
|
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
|
||||||
|
return CreateSdDirectory(fixed_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result OpenSdDirectory(FsDir *out, const char *path, u32 mode) {
|
||||||
|
R_TRY(EnsureSdInitialized());
|
||||||
|
return fsFsOpenDirectory(&g_sd_filesystem, path, mode, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result OpenAtmosphereSdDirectory(FsDir *out, const char *path, u32 mode) {
|
||||||
|
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||||
|
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
|
||||||
|
return OpenSdDirectory(out, fixed_path, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result OpenAtmosphereSdDirectory(FsDir *out, ncm::ProgramId program_id, const char *path, u32 mode) {
|
||||||
|
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||||
|
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), program_id, path);
|
||||||
|
return OpenSdDirectory(out, fixed_path, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result OpenAtmosphereSdRomfsDirectory(FsDir *out, ncm::ProgramId program_id, const char *path, u32 mode) {
|
||||||
|
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||||
|
FormatAtmosphereRomfsPath(fixed_path, sizeof(fixed_path), program_id, path);
|
||||||
|
return OpenSdDirectory(out, fixed_path, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result OpenAtmosphereRomfsDirectory(FsDir *out, ncm::ProgramId program_id, const char *path, u32 mode, FsFileSystem *fs) {
|
||||||
|
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||||
|
FormatAtmosphereRomfsPath(fixed_path, sizeof(fixed_path), program_id, path);
|
||||||
|
return fsFsOpenDirectory(fs, fixed_path, mode, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Remove this in Atmosphere 0.10.1. */
|
||||||
|
Result RenameProgramDirectoryForCompatibility(const char *dir_name) {
|
||||||
|
R_TRY(EnsureSdInitialized());
|
||||||
|
char titles_fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||||
|
char contents_fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||||
|
FormatAtmosphereSdPath(titles_fixed_path, sizeof(titles_fixed_path), "titles", dir_name);
|
||||||
|
FormatAtmosphereSdPath(contents_fixed_path, sizeof(contents_fixed_path), "contents", dir_name);
|
||||||
|
return fsFsRenameDirectory(&g_sd_filesystem, titles_fixed_path, contents_fixed_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasSdRomfsContent(ncm::ProgramId program_id) {
|
||||||
|
/* Check if romfs.bin is present. */
|
||||||
|
{
|
||||||
|
FsFile romfs_file;
|
||||||
|
if (R_SUCCEEDED(OpenAtmosphereSdFile(&romfs_file, program_id, "romfs.bin", OpenMode_Read))) {
|
||||||
|
fsFileClose(&romfs_file);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for romfs folder with content. */
|
||||||
|
FsDir romfs_dir;
|
||||||
|
if (R_FAILED(OpenAtmosphereSdRomfsDirectory(&romfs_dir, program_id, "", OpenDirectoryMode_All))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ON_SCOPE_EXIT { fsDirClose(&romfs_dir); };
|
||||||
|
|
||||||
|
/* Verify the folder has at least one entry. */
|
||||||
|
s64 num_entries = 0;
|
||||||
|
return R_SUCCEEDED(fsDirGetEntryCount(&romfs_dir, &num_entries)) && num_entries > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SaveAtmosphereSdFile(FsFile *out, ncm::ProgramId program_id, const char *path, void *data, size_t size) {
|
||||||
|
R_TRY(EnsureSdInitialized());
|
||||||
|
|
||||||
|
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||||
|
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), program_id, path);
|
||||||
|
|
||||||
|
/* Unconditionally create. */
|
||||||
|
/* Don't check error, as a failure here should be okay. */
|
||||||
|
FsFile f;
|
||||||
|
fsFsCreateFile(&g_sd_filesystem, fixed_path, size, 0);
|
||||||
|
|
||||||
|
/* Try to open. */
|
||||||
|
R_TRY(fsFsOpenFile(&g_sd_filesystem, fixed_path, OpenMode_ReadWrite, &f));
|
||||||
|
auto file_guard = SCOPE_GUARD { fsFileClose(&f); };
|
||||||
|
|
||||||
|
/* Try to set the size. */
|
||||||
|
R_TRY(fsFileSetSize(&f, static_cast<s64>(size)));
|
||||||
|
|
||||||
|
/* Try to write data. */
|
||||||
|
R_TRY(fsFileWrite(&f, 0, data, size, FsWriteOption_Flush));
|
||||||
|
|
||||||
|
/* Set output. */
|
||||||
|
file_guard.Cancel();
|
||||||
|
*out = f;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
48
stratosphere/ams_mitm/source/amsmitm_fs_utils.hpp
Normal file
48
stratosphere/ams_mitm/source/amsmitm_fs_utils.hpp
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope 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/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::mitm::fs {
|
||||||
|
|
||||||
|
/* Initialization. */
|
||||||
|
void OpenGlobalSdCardFileSystem();
|
||||||
|
|
||||||
|
/* Utilities. */
|
||||||
|
Result CreateSdFile(const char *path, s64 size, s32 option);
|
||||||
|
Result CreateAtmosphereSdFile(const char *path, s64 size, s32 option);
|
||||||
|
Result OpenSdFile(FsFile *out, const char *path, u32 mode);
|
||||||
|
Result OpenAtmosphereSdFile(FsFile *out, const char *path, u32 mode);
|
||||||
|
Result OpenAtmosphereSdFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode);
|
||||||
|
Result OpenAtmosphereSdRomfsFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode);
|
||||||
|
Result OpenAtmosphereRomfsFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode, FsFileSystem *fs);
|
||||||
|
|
||||||
|
Result CreateSdDirectory(const char *path);
|
||||||
|
Result CreateAtmosphereSdDirectory(const char *path);
|
||||||
|
Result OpenSdDirectory(FsDir *out, const char *path, u32 mode);
|
||||||
|
Result OpenAtmosphereSdDirectory(FsDir *out, const char *path, u32 mode);
|
||||||
|
Result OpenAtmosphereSdDirectory(FsDir *out, ncm::ProgramId program_id, const char *path, u32 mode);
|
||||||
|
Result OpenAtmosphereSdRomfsDirectory(FsDir *out, ncm::ProgramId program_id, const char *path, u32 mode);
|
||||||
|
Result OpenAtmosphereRomfsDirectory(FsDir *out, ncm::ProgramId program_id, const char *path, u32 mode, FsFileSystem *fs);
|
||||||
|
|
||||||
|
/* TODO: Remove this in Atmosphere 0.10.1. */
|
||||||
|
Result RenameProgramDirectoryForCompatibility(const char *dir_name);
|
||||||
|
|
||||||
|
bool HasSdRomfsContent(ncm::ProgramId program_id);
|
||||||
|
|
||||||
|
Result SaveAtmosphereSdFile(FsFile *out, ncm::ProgramId program_id, const char *path, void *data, size_t size);
|
||||||
|
|
||||||
|
}
|
||||||
249
stratosphere/ams_mitm/source/amsmitm_initialization.cpp
Normal file
249
stratosphere/ams_mitm/source/amsmitm_initialization.cpp
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope 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/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "amsmitm_initialization.hpp"
|
||||||
|
#include "amsmitm_fs_utils.hpp"
|
||||||
|
#include "bpc_mitm/bpc_ams_power_utils.hpp"
|
||||||
|
|
||||||
|
namespace ams::mitm {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/* BIS key sources. */
|
||||||
|
constexpr u8 BisKeySources[4][2][0x10] = {
|
||||||
|
{
|
||||||
|
{0xF8, 0x3F, 0x38, 0x6E, 0x2C, 0xD2, 0xCA, 0x32, 0xA8, 0x9A, 0xB9, 0xAA, 0x29, 0xBF, 0xC7, 0x48},
|
||||||
|
{0x7D, 0x92, 0xB0, 0x3A, 0xA8, 0xBF, 0xDE, 0xE1, 0xA7, 0x4C, 0x3B, 0x6E, 0x35, 0xCB, 0x71, 0x06}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{0x41, 0x00, 0x30, 0x49, 0xDD, 0xCC, 0xC0, 0x65, 0x64, 0x7A, 0x7E, 0xB4, 0x1E, 0xED, 0x9C, 0x5F},
|
||||||
|
{0x44, 0x42, 0x4E, 0xDA, 0xB4, 0x9D, 0xFC, 0xD9, 0x87, 0x77, 0x24, 0x9A, 0xDC, 0x9F, 0x7C, 0xA4}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{0x52, 0xC2, 0xE9, 0xEB, 0x09, 0xE3, 0xEE, 0x29, 0x32, 0xA1, 0x0C, 0x1F, 0xB6, 0xA0, 0x92, 0x6C},
|
||||||
|
{0x4D, 0x12, 0xE1, 0x4B, 0x2A, 0x47, 0x4C, 0x1C, 0x09, 0xCB, 0x03, 0x59, 0xF0, 0x15, 0xF4, 0xE4}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{0x52, 0xC2, 0xE9, 0xEB, 0x09, 0xE3, 0xEE, 0x29, 0x32, 0xA1, 0x0C, 0x1F, 0xB6, 0xA0, 0x92, 0x6C},
|
||||||
|
{0x4D, 0x12, 0xE1, 0x4B, 0x2A, 0x47, 0x4C, 0x1C, 0x09, 0xCB, 0x03, 0x59, 0xF0, 0x15, 0xF4, 0xE4}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr u8 BisKekSource[0x10] = {
|
||||||
|
0x34, 0xC1, 0xA0, 0xC4, 0x82, 0x58, 0xF8, 0xB4, 0xFA, 0x9E, 0x5E, 0x6A, 0xDA, 0xFC, 0x7E, 0x4F,
|
||||||
|
};
|
||||||
|
|
||||||
|
void InitializeThreadFunc(void *arg);
|
||||||
|
|
||||||
|
constexpr size_t InitializeThreadStackSize = 0x4000;
|
||||||
|
constexpr int InitializeThreadPriority = 0x15;
|
||||||
|
os::StaticThread<InitializeThreadStackSize> g_initialize_thread(&InitializeThreadFunc, nullptr, InitializeThreadPriority);
|
||||||
|
|
||||||
|
/* Globals. */
|
||||||
|
os::Event g_init_event(false);
|
||||||
|
|
||||||
|
/* Console-unique data backup and protection. */
|
||||||
|
constexpr size_t CalibrationBinarySize = 0x8000;
|
||||||
|
u8 g_calibration_binary_storage_backup[CalibrationBinarySize];
|
||||||
|
u8 g_calibration_binary_file_backup[CalibrationBinarySize];
|
||||||
|
FsFile g_calibration_binary_file;
|
||||||
|
FsFile g_bis_key_file;
|
||||||
|
|
||||||
|
/* Emummc file protection. */
|
||||||
|
FsFile g_emummc_file;
|
||||||
|
|
||||||
|
constexpr inline bool IsHexadecimal(const char *str) {
|
||||||
|
while (*str) {
|
||||||
|
if (std::isxdigit(static_cast<unsigned char>(*str))) {
|
||||||
|
str++;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetBackupFileName(char *dst, size_t dst_size, const char *serial_number, const char *fn) {
|
||||||
|
if (strlen(serial_number) > 0) {
|
||||||
|
std::snprintf(dst, dst_size, "automatic_backups/%s_%s", serial_number, fn);
|
||||||
|
} else {
|
||||||
|
std::snprintf(dst, dst_size, "automatic_backups/%s", fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateAutomaticBackups() {
|
||||||
|
/* Create a backup directory, if one doesn't exist. */
|
||||||
|
mitm::fs::CreateAtmosphereSdDirectory("/automatic_backups");
|
||||||
|
|
||||||
|
/* Read the calibration binary. */
|
||||||
|
{
|
||||||
|
FsStorage calibration_binary_storage;
|
||||||
|
R_ASSERT(fsOpenBisStorage(&calibration_binary_storage, FsBisPartitionId_CalibrationBinary));
|
||||||
|
ON_SCOPE_EXIT { fsStorageClose(&calibration_binary_storage); };
|
||||||
|
|
||||||
|
R_ASSERT(fsStorageRead(&calibration_binary_storage, 0, g_calibration_binary_storage_backup, CalibrationBinarySize));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy serial number from partition. */
|
||||||
|
/* TODO: Define the magic numbers? Structs? */
|
||||||
|
char serial_number[0x40] = {};
|
||||||
|
std::memcpy(serial_number, g_calibration_binary_storage_backup + 0x250, 0x18);
|
||||||
|
|
||||||
|
/* Backup the calibration binary. */
|
||||||
|
{
|
||||||
|
char calibration_binary_backup_name[ams::fs::EntryNameLengthMax + 1];
|
||||||
|
GetBackupFileName(calibration_binary_backup_name, sizeof(calibration_binary_backup_name), serial_number, "PRODINFO.bin");
|
||||||
|
|
||||||
|
mitm::fs::CreateAtmosphereSdFile(calibration_binary_backup_name, CalibrationBinarySize, ams::fs::CreateOption_None);
|
||||||
|
R_ASSERT(mitm::fs::OpenAtmosphereSdFile(&g_calibration_binary_file, calibration_binary_backup_name, ams::fs::OpenMode_ReadWrite));
|
||||||
|
|
||||||
|
s64 file_size = 0;
|
||||||
|
R_ASSERT(fsFileGetSize(&g_calibration_binary_file, &file_size));
|
||||||
|
|
||||||
|
bool is_file_backup_valid = file_size == CalibrationBinarySize;
|
||||||
|
if (is_file_backup_valid) {
|
||||||
|
u64 read_size = 0;
|
||||||
|
R_ASSERT(fsFileRead(&g_calibration_binary_file, 0, g_calibration_binary_file_backup, CalibrationBinarySize, FsReadOption_None, &read_size));
|
||||||
|
AMS_ASSERT(read_size == CalibrationBinarySize);
|
||||||
|
is_file_backup_valid &= std::memcmp(g_calibration_binary_file_backup, "CAL0", 4) == 0;
|
||||||
|
is_file_backup_valid &= std::memcmp(g_calibration_binary_file_backup + 0x250, serial_number, 0x18) == 0;
|
||||||
|
const u32 cal_bin_size = *reinterpret_cast<const u32 *>(g_calibration_binary_file_backup + 0x8);
|
||||||
|
is_file_backup_valid &= cal_bin_size + 0x40 <= CalibrationBinarySize;
|
||||||
|
if (is_file_backup_valid) {
|
||||||
|
u8 calc_hash[SHA256_HASH_SIZE];
|
||||||
|
/* TODO: ams::crypto? */
|
||||||
|
sha256CalculateHash(calc_hash, g_calibration_binary_file_backup + 0x40, cal_bin_size);
|
||||||
|
is_file_backup_valid &= std::memcmp(calc_hash, g_calibration_binary_file_backup + 0x20, sizeof(calc_hash)) == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_file_backup_valid) {
|
||||||
|
R_ASSERT(fsFileSetSize(&g_calibration_binary_file, CalibrationBinarySize));
|
||||||
|
R_ASSERT(fsFileWrite(&g_calibration_binary_file, 0, g_calibration_binary_storage_backup, CalibrationBinarySize, FsWriteOption_Flush));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note: g_calibration_binary_file is intentionally not closed here. This prevents any other process from opening it. */
|
||||||
|
std::memset(g_calibration_binary_file_backup, 0, CalibrationBinarySize);
|
||||||
|
std::memset(g_calibration_binary_storage_backup, 0, CalibrationBinarySize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Backup BIS keys. */
|
||||||
|
{
|
||||||
|
u64 key_generation = 0;
|
||||||
|
if (hos::GetVersion() >= hos::Version_500) {
|
||||||
|
R_ASSERT(splGetConfig(SplConfigItem_NewKeyGeneration, &key_generation));
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 bis_keys[4][2][0x10];
|
||||||
|
std::memset(bis_keys, 0xCC, sizeof(bis_keys));
|
||||||
|
|
||||||
|
/* TODO: Clean this up. */
|
||||||
|
for (size_t partition = 0; partition < 4; partition++) {
|
||||||
|
if (partition == 0) {
|
||||||
|
for (size_t i = 0; i < 2; i++) {
|
||||||
|
R_ASSERT(splFsGenerateSpecificAesKey(BisKeySources[partition][i], key_generation, i, bis_keys[partition][i]));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const u32 option = (partition == 3 && spl::IsRecoveryBoot()) ? 0x4 : 0x1;
|
||||||
|
|
||||||
|
u8 access_key[0x10];
|
||||||
|
R_ASSERT(splCryptoGenerateAesKek(BisKekSource, key_generation, option, access_key));
|
||||||
|
for (size_t i = 0; i < 2; i++) {
|
||||||
|
R_ASSERT(splCryptoGenerateAesKey(access_key, BisKeySources[partition][i], bis_keys[partition][i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char bis_keys_backup_name[ams::fs::EntryNameLengthMax + 1];
|
||||||
|
GetBackupFileName(bis_keys_backup_name, sizeof(bis_keys_backup_name), serial_number, "BISKEYS.bin");
|
||||||
|
|
||||||
|
mitm::fs::CreateAtmosphereSdFile(bis_keys_backup_name, sizeof(bis_keys), ams::fs::CreateOption_None);
|
||||||
|
R_ASSERT(mitm::fs::OpenAtmosphereSdFile(&g_bis_key_file, bis_keys_backup_name, ams::fs::OpenMode_ReadWrite));
|
||||||
|
R_ASSERT(fsFileSetSize(&g_bis_key_file, sizeof(bis_keys)));
|
||||||
|
R_ASSERT(fsFileWrite(&g_bis_key_file, 0, bis_keys, sizeof(bis_keys), FsWriteOption_Flush));
|
||||||
|
/* NOTE: g_bis_key_file is intentionally not closed here. This prevents any other process from opening it. */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenameTitlesDirectoryProgramFoldersForCompatibility() {
|
||||||
|
FsDir titles_dir;
|
||||||
|
if (R_FAILED(mitm::fs::OpenAtmosphereSdDirectory(&titles_dir, "/titles", ams::fs::OpenDirectoryMode_Directory))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ON_SCOPE_EXIT { fsDirClose(&titles_dir); };
|
||||||
|
|
||||||
|
ams::fs::DirectoryEntry dir_entry;
|
||||||
|
s64 read_entries;
|
||||||
|
while (R_SUCCEEDED(fsDirRead(&titles_dir, &read_entries, 1, &dir_entry)) && read_entries == 1) {
|
||||||
|
if (strlen(dir_entry.name) == 2 * sizeof(ncm::ProgramId) && IsHexadecimal(dir_entry.name)) {
|
||||||
|
/* We found a program directory, try to rename it. Failure is allowed. */
|
||||||
|
mitm::fs::RenameProgramDirectoryForCompatibility(dir_entry.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialization implementation */
|
||||||
|
void InitializeThreadFunc(void *arg) {
|
||||||
|
/* Wait for the SD card to be ready. */
|
||||||
|
cfg::WaitSdCardInitialized();
|
||||||
|
|
||||||
|
/* Open global SD card file system, so that other threads can begin using the SD. */
|
||||||
|
mitm::fs::OpenGlobalSdCardFileSystem();
|
||||||
|
|
||||||
|
/* Initialize the reboot manager (load a payload off the SD). */
|
||||||
|
/* Discard result, since it doesn't need to succeed. */
|
||||||
|
mitm::bpc::LoadRebootPayload();
|
||||||
|
|
||||||
|
/* Backup Calibration Binary and BIS keys. */
|
||||||
|
CreateAutomaticBackups();
|
||||||
|
|
||||||
|
/* Rename program folders in the titles directory. */
|
||||||
|
/* TODO: Remove this in Atmosphere 0.10.1. */
|
||||||
|
RenameTitlesDirectoryProgramFoldersForCompatibility();
|
||||||
|
|
||||||
|
/* If we're emummc, persist a write-handle to prevent other processes from touching the image. */
|
||||||
|
if (emummc::IsActive()) {
|
||||||
|
if (const char *emummc_file_path = emummc::GetFilePath(); emummc_file_path != nullptr) {
|
||||||
|
char emummc_path[ams::fs::EntryNameLengthMax + 1];
|
||||||
|
std::snprintf(emummc_path, sizeof(emummc_path), "%s/eMMC", emummc_file_path);
|
||||||
|
mitm::fs::OpenSdFile(&g_emummc_file, emummc_path, ams::fs::OpenMode_Read);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Connect to set:sys. */
|
||||||
|
sm::DoWithSession([]() {
|
||||||
|
R_ASSERT(setsysInitialize());
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Signal to waiters that we are ready. */
|
||||||
|
g_init_event.Signal();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void StartInitialize() {
|
||||||
|
R_ASSERT(g_initialize_thread.Start());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsInitialized() {
|
||||||
|
return g_init_event.TryWait();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaitInitialized() {
|
||||||
|
g_init_event.Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
25
stratosphere/ams_mitm/source/amsmitm_initialization.hpp
Normal file
25
stratosphere/ams_mitm/source/amsmitm_initialization.hpp
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope 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/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::mitm {
|
||||||
|
|
||||||
|
void StartInitialize();
|
||||||
|
bool IsInitialized();
|
||||||
|
void WaitInitialized();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -13,23 +13,16 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
#include "amsmitm_initialization.hpp"
|
||||||
#include <cstdlib>
|
#include "amsmitm_module_management.hpp"
|
||||||
#include <cstdint>
|
#include "bpc_mitm/bpc_ams_power_utils.hpp"
|
||||||
#include <cstring>
|
|
||||||
#include <malloc.h>
|
|
||||||
|
|
||||||
#include <switch.h>
|
|
||||||
#include <atmosphere.h>
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
|
|
||||||
#include "amsmitm_modules.hpp"
|
|
||||||
#include "utils.hpp"
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
extern u32 __start__;
|
extern u32 __start__;
|
||||||
|
|
||||||
u32 __nx_applet_type = AppletType_None;
|
u32 __nx_applet_type = AppletType_None;
|
||||||
|
u32 __nx_fs_num_sessions = 1;
|
||||||
|
u32 __nx_fsdev_direntry_cache_size = 1;
|
||||||
|
|
||||||
#define INNER_HEAP_SIZE 0x1000000
|
#define INNER_HEAP_SIZE 0x1000000
|
||||||
size_t nx_inner_heap_size = INNER_HEAP_SIZE;
|
size_t nx_inner_heap_size = INNER_HEAP_SIZE;
|
||||||
@@ -40,21 +33,33 @@ extern "C" {
|
|||||||
void __appExit(void);
|
void __appExit(void);
|
||||||
|
|
||||||
/* Exception handling. */
|
/* Exception handling. */
|
||||||
alignas(16) u8 __nx_exception_stack[0x1000];
|
alignas(16) u8 __nx_exception_stack[ams::os::MemoryPageSize];
|
||||||
u64 __nx_exception_stack_size = sizeof(__nx_exception_stack);
|
u64 __nx_exception_stack_size = sizeof(__nx_exception_stack);
|
||||||
void __libnx_exception_handler(ThreadExceptionDump *ctx);
|
void __libnx_exception_handler(ThreadExceptionDump *ctx);
|
||||||
void __libstratosphere_exception_handler(AtmosphereFatalErrorContext *ctx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sts::ncm::TitleId __stratosphere_title_id = sts::ncm::TitleId::AtmosphereMitm;
|
namespace ams {
|
||||||
|
|
||||||
|
ncm::ProgramId CurrentProgramId = ncm::ProgramId::AtmosphereMitm;
|
||||||
|
|
||||||
|
namespace result {
|
||||||
|
|
||||||
|
bool CallFatalOnResultAssertion = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Override. */
|
||||||
|
void ExceptionHandler(FatalErrorContext *ctx) {
|
||||||
|
/* We're bpc-mitm (or ams_mitm, anyway), so manually reboot to fatal error. */
|
||||||
|
mitm::bpc::RebootForFatalError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace ams;
|
||||||
|
|
||||||
void __libnx_exception_handler(ThreadExceptionDump *ctx) {
|
void __libnx_exception_handler(ThreadExceptionDump *ctx) {
|
||||||
StratosphereCrashHandler(ctx);
|
ams::CrashHandler(ctx);
|
||||||
}
|
|
||||||
|
|
||||||
void __libstratosphere_exception_handler(AtmosphereFatalErrorContext *ctx) {
|
|
||||||
/* We're bpc-mitm (or ams_mitm, anyway), so manually reboot to fatal error. */
|
|
||||||
Utils::RebootToFatalError(ctx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void __libnx_initheap(void) {
|
void __libnx_initheap(void) {
|
||||||
@@ -70,16 +75,16 @@ void __libnx_initheap(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void __appInit(void) {
|
void __appInit(void) {
|
||||||
SetFirmwareVersionForLibnx();
|
hos::SetVersionForLibnx();
|
||||||
|
|
||||||
DoWithSmSession([&]() {
|
sm::DoWithSession([&]() {
|
||||||
R_ASSERT(fsInitialize());
|
R_ASSERT(fsInitialize());
|
||||||
R_ASSERT(pmdmntInitialize());
|
R_ASSERT(pmdmntInitialize());
|
||||||
R_ASSERT(pminfoInitialize());
|
R_ASSERT(pminfoInitialize());
|
||||||
R_ASSERT(splFsInitialize());
|
R_ASSERT(splFsInitialize());
|
||||||
});
|
});
|
||||||
|
|
||||||
CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION);
|
ams::CheckApiVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
void __appExit(void) {
|
void __appExit(void) {
|
||||||
@@ -90,18 +95,15 @@ void __appExit(void) {
|
|||||||
fsExit();
|
fsExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv) {
|
||||||
{
|
/* Start initialization (sd card init, automatic backups, etc) */
|
||||||
consoleDebugInit(debugDevice_SVC);
|
mitm::StartInitialize();
|
||||||
HosThread initializer_thread;
|
|
||||||
|
|
||||||
LaunchAllMitmModules();
|
/* Launch all mitm modules in sequence. */
|
||||||
|
mitm::LaunchAllModules();
|
||||||
R_ASSERT(initializer_thread.Initialize(&Utils::InitializeThreadFunc, NULL, 0x4000, 0x15));
|
|
||||||
R_ASSERT(initializer_thread.Start());
|
|
||||||
|
|
||||||
/* Wait for all mitm modules to end. */
|
/* Wait for all mitm modules to end. */
|
||||||
WaitAllMitmModules();
|
mitm::WaitAllModules();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
45
stratosphere/ams_mitm/source/amsmitm_module.hpp
Normal file
45
stratosphere/ams_mitm/source/amsmitm_module.hpp
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope 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/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::mitm {
|
||||||
|
|
||||||
|
/* TODO: C++20 Concepts will make this a lot less stupid. */
|
||||||
|
class ModuleBase {};
|
||||||
|
|
||||||
|
#define DEFINE_MITM_MODULE_CLASS(ss, prio) class MitmModule : public ::ams::mitm::ModuleBase { \
|
||||||
|
public: \
|
||||||
|
static constexpr size_t ThreadPriority = prio; \
|
||||||
|
static constexpr size_t StackSize = ss; \
|
||||||
|
alignas(os::MemoryPageSize) static inline u8 Stack[StackSize]; \
|
||||||
|
public: \
|
||||||
|
static void ThreadFunction(void *); \
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class M>
|
||||||
|
struct ModuleTraits {
|
||||||
|
static_assert(std::is_base_of<ModuleBase, M>::value, "Mitm Modules must inherit from ams::mitm::Module");
|
||||||
|
|
||||||
|
static constexpr void *Stack = &M::Stack[0];
|
||||||
|
static constexpr size_t StackSize = M::StackSize;
|
||||||
|
|
||||||
|
static constexpr size_t ThreadPriority = M::ThreadPriority;
|
||||||
|
|
||||||
|
static constexpr ::ThreadFunc ThreadFunction = &M::ThreadFunction;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
91
stratosphere/ams_mitm/source/amsmitm_module_management.cpp
Normal file
91
stratosphere/ams_mitm/source/amsmitm_module_management.cpp
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope 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/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "amsmitm_module_management.hpp"
|
||||||
|
#include "amsmitm_module.hpp"
|
||||||
|
|
||||||
|
#include "fs_mitm/fsmitm_module.hpp"
|
||||||
|
#include "set_mitm/setmitm_module.hpp"
|
||||||
|
#include "bpc_mitm/bpcmitm_module.hpp"
|
||||||
|
#include "ns_mitm/nsmitm_module.hpp"
|
||||||
|
#include "hid_mitm/hidmitm_module.hpp"
|
||||||
|
|
||||||
|
namespace ams::mitm {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
enum ModuleId : u32 {
|
||||||
|
ModuleId_FsMitm,
|
||||||
|
ModuleId_SetMitm,
|
||||||
|
ModuleId_BpcMitm,
|
||||||
|
ModuleId_NsMitm,
|
||||||
|
ModuleId_HidMitm,
|
||||||
|
|
||||||
|
ModuleId_Count,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ModuleDefinition {
|
||||||
|
ThreadFunc main;
|
||||||
|
void *stack_mem;
|
||||||
|
u32 priority;
|
||||||
|
u32 stack_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class M>
|
||||||
|
constexpr ModuleDefinition GetModuleDefinition() {
|
||||||
|
using Traits = ModuleTraits<M>;
|
||||||
|
|
||||||
|
return ModuleDefinition {
|
||||||
|
.main = Traits::ThreadFunction,
|
||||||
|
.stack_mem = Traits::Stack,
|
||||||
|
.priority = Traits::ThreadPriority,
|
||||||
|
.stack_size = Traits::StackSize,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ams::os::Thread g_module_threads[ModuleId_Count];
|
||||||
|
|
||||||
|
constexpr ModuleDefinition g_module_definitions[ModuleId_Count] = {
|
||||||
|
GetModuleDefinition<fs::MitmModule>(),
|
||||||
|
GetModuleDefinition<settings::MitmModule>(),
|
||||||
|
GetModuleDefinition<bpc::MitmModule>(),
|
||||||
|
GetModuleDefinition<ns::MitmModule>(),
|
||||||
|
GetModuleDefinition<hid::MitmModule>(),
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaunchAllModules() {
|
||||||
|
/* Create thread for each module. */
|
||||||
|
for (u32 i = 0; i < static_cast<u32>(ModuleId_Count); i++) {
|
||||||
|
const ModuleDefinition &cur_module = g_module_definitions[i];
|
||||||
|
R_ASSERT(g_module_threads[i].Initialize(cur_module.main, nullptr, cur_module.stack_mem, cur_module.stack_size, cur_module.priority));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start thread for each module. */
|
||||||
|
for (u32 i = 0; i < static_cast<u32>(ModuleId_Count); i++) {
|
||||||
|
R_ASSERT(g_module_threads[i].Start());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaitAllModules() {
|
||||||
|
/* Wait on thread for each module. */
|
||||||
|
for (u32 i = 0; i < static_cast<u32>(ModuleId_Count); i++) {
|
||||||
|
g_module_threads[i].Join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -13,10 +13,12 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
#include "ipc/ipc_service_object.hpp"
|
namespace ams::mitm {
|
||||||
#include "ipc/ipc_serialization.hpp"
|
|
||||||
|
|
||||||
#include "ipc/ipc_service_session.hpp"
|
void LaunchAllModules();
|
||||||
|
void WaitAllModules();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <switch.h>
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
#include <cstring>
|
|
||||||
#include "debug.hpp"
|
|
||||||
|
|
||||||
#include "amsmitm_modules.hpp"
|
|
||||||
|
|
||||||
#include "fs_mitm/fsmitm_main.hpp"
|
|
||||||
#include "set_mitm/setmitm_main.hpp"
|
|
||||||
#include "bpc_mitm/bpcmitm_main.hpp"
|
|
||||||
#include "ns_mitm/nsmitm_main.hpp"
|
|
||||||
|
|
||||||
static HosThread g_module_threads[MitmModuleId_Count];
|
|
||||||
|
|
||||||
static const struct {
|
|
||||||
ThreadFunc main;
|
|
||||||
u32 priority;
|
|
||||||
u32 stack_size;
|
|
||||||
} g_module_definitions[MitmModuleId_Count] = {
|
|
||||||
{ &FsMitmMain, FsMitmPriority, FsMitmStackSize }, /* FsMitm */
|
|
||||||
{ &SetMitmMain, SetMitmPriority, SetMitmStackSize }, /* SetMitm */
|
|
||||||
{ &BpcMitmMain, BpcMitmPriority, BpcMitmStackSize }, /* BpcMitm */
|
|
||||||
{ &NsMitmMain, NsMitmPriority, NsMitmStackSize }, /* NsMitm */
|
|
||||||
};
|
|
||||||
|
|
||||||
void LaunchAllMitmModules() {
|
|
||||||
/* Create thread for each module. */
|
|
||||||
for (u32 i = 0; i < static_cast<u32>(MitmModuleId_Count); i++) {
|
|
||||||
const auto cur_module = &g_module_definitions[i];
|
|
||||||
R_ASSERT(g_module_threads[i].Initialize(cur_module->main, nullptr, cur_module->stack_size, cur_module->priority));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start thread for each module. */
|
|
||||||
for (u32 i = 0; i < static_cast<u32>(MitmModuleId_Count); i++) {
|
|
||||||
R_ASSERT(g_module_threads[i].Start());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WaitAllMitmModules() {
|
|
||||||
/* Wait on thread for each module. */
|
|
||||||
for (u32 i = 0; i < static_cast<u32>(MitmModuleId_Count); i++) {
|
|
||||||
g_module_threads[i].Join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
121
stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp
Normal file
121
stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope 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/>.
|
||||||
|
*/
|
||||||
|
#include "bpc_ams_power_utils.hpp"
|
||||||
|
#include "../amsmitm_fs_utils.hpp"
|
||||||
|
|
||||||
|
namespace ams::mitm::bpc {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/* Convenience definitions. */
|
||||||
|
constexpr uintptr_t IramBase = 0x40000000ull;
|
||||||
|
constexpr uintptr_t IramPayloadBase = 0x40010000ull;
|
||||||
|
constexpr size_t IramSize = 0x40000;
|
||||||
|
constexpr size_t IramPayloadMaxSize = 0x2E000;
|
||||||
|
|
||||||
|
/* Helper enum. */
|
||||||
|
enum class RebootType : u32 {
|
||||||
|
Standard,
|
||||||
|
ToRcm,
|
||||||
|
ToPayload,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Globals. */
|
||||||
|
alignas(os::MemoryPageSize) u8 g_work_page[os::MemoryPageSize];
|
||||||
|
alignas(os::MemoryPageSize) u8 g_reboot_payload[IramPayloadMaxSize];
|
||||||
|
RebootType g_reboot_type = RebootType::ToRcm;
|
||||||
|
|
||||||
|
/* Helpers. */
|
||||||
|
void ClearIram() {
|
||||||
|
/* Make page CCs. */
|
||||||
|
std::memset(g_work_page, 0xCC, sizeof(g_work_page));
|
||||||
|
|
||||||
|
/* Overwrite all of IRAM with CCs. */
|
||||||
|
for (size_t ofs = 0; ofs < IramSize; ofs += sizeof(g_work_page)) {
|
||||||
|
exosphere::CopyToIram(IramBase + ofs, g_work_page, sizeof(g_work_page));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoRebootToPayload(const ams::FatalErrorContext *ctx) {
|
||||||
|
/* Ensure clean IRAM state. */
|
||||||
|
ClearIram();
|
||||||
|
|
||||||
|
/* Copy in payload. */
|
||||||
|
for (size_t ofs = 0; ofs < sizeof(g_reboot_payload); ofs += sizeof(g_work_page)) {
|
||||||
|
std::memcpy(g_work_page, &g_reboot_payload[ofs], std::min(sizeof(g_reboot_payload) - ofs, sizeof(g_work_page)));
|
||||||
|
exosphere::CopyToIram(IramPayloadBase + ofs, g_work_page, sizeof(g_work_page));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy in fatal error context, if relevant. */
|
||||||
|
if (ctx != nullptr) {
|
||||||
|
std::memset(g_work_page, 0xCC, sizeof(g_work_page));
|
||||||
|
std::memcpy(g_work_page, ctx, sizeof(*ctx));
|
||||||
|
exosphere::CopyToIram(IramPayloadBase + IramPayloadMaxSize, g_work_page, sizeof(g_work_page));
|
||||||
|
}
|
||||||
|
|
||||||
|
exosphere::ForceRebootToIramPayload();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Power utilities. */
|
||||||
|
bool IsRebootManaged() {
|
||||||
|
return g_reboot_type != RebootType::Standard;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RebootSystem() {
|
||||||
|
switch (g_reboot_type) {
|
||||||
|
case RebootType::ToRcm:
|
||||||
|
exosphere::ForceRebootToRcm();
|
||||||
|
break;
|
||||||
|
case RebootType::ToPayload:
|
||||||
|
default: /* This should never be called with ::Standard */
|
||||||
|
DoRebootToPayload(nullptr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShutdownSystem() {
|
||||||
|
exosphere::ForceShutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Atmosphere power utilities. */
|
||||||
|
void RebootForFatalError(const ams::FatalErrorContext *ctx) {
|
||||||
|
DoRebootToPayload(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result LoadRebootPayload() {
|
||||||
|
/* Clear payload buffer */
|
||||||
|
std::memset(g_reboot_payload, 0xCC, sizeof(g_reboot_payload));
|
||||||
|
|
||||||
|
/* Open payload file. */
|
||||||
|
FsFile payload_file;
|
||||||
|
R_TRY(fs::OpenAtmosphereSdFile(&payload_file, "/reboot_payload.bin", ams::fs::OpenMode_Read));
|
||||||
|
ON_SCOPE_EXIT { fsFileClose(&payload_file); };
|
||||||
|
|
||||||
|
/* Read payload file. Discard result. */
|
||||||
|
{
|
||||||
|
size_t actual_size;
|
||||||
|
fsFileRead(&payload_file, 0, g_reboot_payload, sizeof(g_reboot_payload), FsReadOption_None, &actual_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Parse reboot type from settings. */
|
||||||
|
g_reboot_type = RebootType::ToPayload;
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -13,18 +13,18 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
#pragma once
|
||||||
#include <switch.h>
|
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
static HosRecursiveMutex g_sm_session_lock;
|
namespace ams::mitm::bpc {
|
||||||
static HosRecursiveMutex g_sm_mitm_session_lock;
|
|
||||||
|
|
||||||
|
/* Power utilities. */
|
||||||
|
bool IsRebootManaged();
|
||||||
|
void RebootSystem();
|
||||||
|
void ShutdownSystem();
|
||||||
|
|
||||||
|
/* Atmosphere power utilities. */
|
||||||
|
Result LoadRebootPayload();
|
||||||
|
void RebootForFatalError(const ams::FatalErrorContext *ctx);
|
||||||
|
|
||||||
HosRecursiveMutex &GetSmSessionMutex() {
|
|
||||||
return g_sm_session_lock;
|
|
||||||
}
|
|
||||||
|
|
||||||
HosRecursiveMutex &GetSmMitmSessionMutex() {
|
|
||||||
return g_sm_mitm_session_lock;
|
|
||||||
}
|
}
|
||||||
@@ -13,20 +13,13 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <mutex>
|
|
||||||
#include <switch.h>
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
#include "bpc_ams_service.hpp"
|
#include "bpc_ams_service.hpp"
|
||||||
#include "bpcmitm_reboot_manager.hpp"
|
#include "bpc_ams_power_utils.hpp"
|
||||||
|
|
||||||
Result BpcAtmosphereService::RebootToFatalError(InBuffer<AtmosphereFatalErrorContext> ctx) {
|
namespace ams::mitm::bpc {
|
||||||
if (ctx.buffer == nullptr || ctx.num_elements != 1) {
|
|
||||||
return ResultKernelConnectionClosed;
|
void AtmosphereService::RebootToFatalError(const ams::FatalErrorContext &ctx) {
|
||||||
|
bpc::RebootForFatalError(&ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reboot to fusee with the input context. */
|
|
||||||
BpcRebootManager::RebootForFatalError(ctx.buffer);
|
|
||||||
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,19 +15,21 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <switch.h>
|
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
#include "../utils.hpp"
|
namespace ams::mitm::bpc {
|
||||||
|
|
||||||
class BpcAtmosphereService : public IServiceObject {
|
class AtmosphereService final : public sf::IServiceObject {
|
||||||
enum class CommandId {
|
private:
|
||||||
RebootToFatalError = 65000,
|
enum class CommandId {
|
||||||
|
RebootToFatalError = 65000,
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
void RebootToFatalError(const ams::FatalErrorContext &ctx);
|
||||||
|
public:
|
||||||
|
DEFINE_SERVICE_DISPATCH_TABLE {
|
||||||
|
MAKE_SERVICE_COMMAND_META(RebootToFatalError),
|
||||||
|
};
|
||||||
};
|
};
|
||||||
private:
|
|
||||||
Result RebootToFatalError(InBuffer<AtmosphereFatalErrorContext> ctx);
|
}
|
||||||
public:
|
|
||||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
|
||||||
MAKE_SERVICE_COMMAND_META(BpcAtmosphereService, RebootToFatalError),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -13,23 +13,20 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <mutex>
|
|
||||||
#include <switch.h>
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
#include "bpc_mitm_service.hpp"
|
#include "bpc_mitm_service.hpp"
|
||||||
#include "bpcmitm_reboot_manager.hpp"
|
#include "bpc_ams_power_utils.hpp"
|
||||||
|
|
||||||
void BpcMitmService::PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx) {
|
namespace ams::mitm::bpc {
|
||||||
/* Nothing to do here */
|
|
||||||
}
|
|
||||||
|
|
||||||
Result BpcMitmService::ShutdownSystem() {
|
Result BpcMitmService::RebootSystem() {
|
||||||
/* Use exosphere + reboot to perform real shutdown, instead of fake shutdown. */
|
R_UNLESS(bpc::IsRebootManaged(), sm::mitm::ResultShouldForwardToSession());
|
||||||
PerformShutdownSmc();
|
bpc::RebootSystem();
|
||||||
return ResultSuccess;
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result BpcMitmService::ShutdownSystem() {
|
||||||
|
bpc::ShutdownSystem();
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
Result BpcMitmService::RebootSystem() {
|
|
||||||
return BpcRebootManager::PerformReboot();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,42 +13,39 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <switch.h>
|
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
#include "../utils.hpp"
|
namespace ams::mitm::bpc {
|
||||||
|
|
||||||
class BpcMitmService : public IMitmServiceObject {
|
class BpcMitmService : public sf::IMitmServiceObject {
|
||||||
private:
|
private:
|
||||||
enum class CommandId {
|
enum class CommandId {
|
||||||
ShutdownSystem = 0,
|
ShutdownSystem = 0,
|
||||||
RebootSystem = 1,
|
RebootSystem = 1,
|
||||||
};
|
};
|
||||||
public:
|
public:
|
||||||
BpcMitmService(std::shared_ptr<Service> s, u64 pid, sts::ncm::TitleId tid) : IMitmServiceObject(s, pid, tid) {
|
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
|
||||||
/* ... */
|
/* We will mitm:
|
||||||
}
|
* - am, to intercept the Reboot/Power buttons in the overlay menu.
|
||||||
|
* - fatal, to simplify payload reboot logic significantly
|
||||||
|
* - hbl, to allow homebrew to take advantage of the feature.
|
||||||
|
*/
|
||||||
|
return client_info.program_id == ncm::ProgramId::Am ||
|
||||||
|
client_info.program_id == ncm::ProgramId::Fatal ||
|
||||||
|
client_info.override_status.IsHbl();
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
SF_MITM_SERVICE_OBJECT_CTOR(BpcMitmService) { /* ... */ }
|
||||||
|
protected:
|
||||||
|
/* Overridden commands. */
|
||||||
|
Result ShutdownSystem();
|
||||||
|
Result RebootSystem();
|
||||||
|
public:
|
||||||
|
DEFINE_SERVICE_DISPATCH_TABLE {
|
||||||
|
MAKE_SERVICE_COMMAND_META(ShutdownSystem),
|
||||||
|
MAKE_SERVICE_COMMAND_META(RebootSystem),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
static bool ShouldMitm(u64 pid, sts::ncm::TitleId tid) {
|
}
|
||||||
/* We will mitm:
|
|
||||||
* - am, to intercept the Reboot/Power buttons in the overlay menu.
|
|
||||||
* - fatal, to simplify payload reboot logic significantly
|
|
||||||
* - applications, to allow homebrew to take advantage of the feature.
|
|
||||||
*/
|
|
||||||
return tid == sts::ncm::TitleId::Am || tid == sts::ncm::TitleId::Fatal || sts::ncm::IsApplicationTitleId(tid) || Utils::IsHblTid(static_cast<u64>(tid));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/* Overridden commands. */
|
|
||||||
Result ShutdownSystem();
|
|
||||||
Result RebootSystem();
|
|
||||||
public:
|
|
||||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
|
||||||
MAKE_SERVICE_COMMAND_META(BpcMitmService, ShutdownSystem),
|
|
||||||
MAKE_SERVICE_COMMAND_META(BpcMitmService, RebootSystem),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstring>
|
|
||||||
#include <malloc.h>
|
|
||||||
|
|
||||||
#include <switch.h>
|
|
||||||
#include <atmosphere.h>
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
|
|
||||||
#include "bpcmitm_main.hpp"
|
|
||||||
#include "bpc_mitm_service.hpp"
|
|
||||||
#include "bpc_ams_service.hpp"
|
|
||||||
#include "bpcmitm_reboot_manager.hpp"
|
|
||||||
|
|
||||||
#include "../utils.hpp"
|
|
||||||
|
|
||||||
void BpcMitmMain(void *arg) {
|
|
||||||
/* Wait for initialization to occur */
|
|
||||||
Utils::WaitSdInitialized();
|
|
||||||
|
|
||||||
/* Load a payload off of the SD */
|
|
||||||
BpcRebootManager::Initialize();
|
|
||||||
|
|
||||||
/* Create server manager */
|
|
||||||
static auto s_server_manager = WaitableManager(2);
|
|
||||||
|
|
||||||
/* Create bpc mitm. */
|
|
||||||
const char *service_name = "bpc";
|
|
||||||
if (GetRuntimeFirmwareVersion() < FirmwareVersion_200) {
|
|
||||||
service_name = "bpc:c";
|
|
||||||
}
|
|
||||||
AddMitmServerToManager<BpcMitmService>(&s_server_manager, service_name, 13);
|
|
||||||
|
|
||||||
/* Extension: Allow for reboot-to-error. */
|
|
||||||
/* Must be managed port in order for sm to be able to access. */
|
|
||||||
s_server_manager.AddWaitable(new ManagedPortServer<BpcAtmosphereService>("bpc:ams", 1));
|
|
||||||
|
|
||||||
/* Loop forever, servicing our services. */
|
|
||||||
s_server_manager.Process();
|
|
||||||
}
|
|
||||||
|
|
||||||
59
stratosphere/ams_mitm/source/bpc_mitm/bpcmitm_module.cpp
Normal file
59
stratosphere/ams_mitm/source/bpc_mitm/bpcmitm_module.cpp
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope 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/>.
|
||||||
|
*/
|
||||||
|
#include "../amsmitm_initialization.hpp"
|
||||||
|
#include "bpcmitm_module.hpp"
|
||||||
|
#include "bpc_mitm_service.hpp"
|
||||||
|
#include "bpc_ams_service.hpp"
|
||||||
|
#include "bpc_ams_power_utils.hpp"
|
||||||
|
|
||||||
|
namespace ams::mitm::bpc {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr sm::ServiceName MitmServiceName = sm::ServiceName::Encode("bpc");
|
||||||
|
constexpr sm::ServiceName DeprecatedMitmServiceName = sm::ServiceName::Encode("bpc:c");
|
||||||
|
constexpr size_t MitmServiceMaxSessions = 13;
|
||||||
|
|
||||||
|
constexpr sm::ServiceName AtmosphereServiceName = sm::ServiceName::Encode("bpc:ams");
|
||||||
|
constexpr size_t AtmosphereMaxSessions = 3;
|
||||||
|
|
||||||
|
constexpr size_t MaxServers = 2;
|
||||||
|
constexpr size_t MaxSessions = MitmServiceMaxSessions + AtmosphereMaxSessions;
|
||||||
|
using ServerOptions = sf::hipc::DefaultServerManagerOptions;
|
||||||
|
sf::hipc::ServerManager<MaxServers, ServerOptions, MaxSessions> g_server_manager;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MitmModule::ThreadFunction(void *arg) {
|
||||||
|
/* Wait until initialization is complete. */
|
||||||
|
mitm::WaitInitialized();
|
||||||
|
|
||||||
|
/* Create bpc:ams. */
|
||||||
|
{
|
||||||
|
Handle bpcams_h;
|
||||||
|
R_ASSERT(svcManageNamedPort(&bpcams_h, AtmosphereServiceName.name, AtmosphereMaxSessions));
|
||||||
|
g_server_manager.RegisterServer<bpc::AtmosphereService>(bpcams_h);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create bpc mitm. */
|
||||||
|
const sm::ServiceName service_name = (hos::GetVersion() >= hos::Version_200) ? MitmServiceName : DeprecatedMitmServiceName;
|
||||||
|
R_ASSERT(g_server_manager.RegisterMitmServer<BpcMitmService>(service_name));
|
||||||
|
|
||||||
|
/* Loop forever, servicing our services. */
|
||||||
|
g_server_manager.LoopProcess();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
24
stratosphere/ams_mitm/source/bpc_mitm/bpcmitm_module.hpp
Normal file
24
stratosphere/ams_mitm/source/bpc_mitm/bpcmitm_module.hpp
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope 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/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "../amsmitm_module.hpp"
|
||||||
|
|
||||||
|
namespace ams::mitm::bpc {
|
||||||
|
|
||||||
|
DEFINE_MITM_MODULE_CLASS(0x8000, 32);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <switch.h>
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
#include <strings.h>
|
|
||||||
#include "bpcmitm_reboot_manager.hpp"
|
|
||||||
#include "../utils.hpp"
|
|
||||||
|
|
||||||
/* TODO: Find a way to pre-populate this with the contents of fusee-primary. */
|
|
||||||
static u8 g_reboot_payload[IRAM_PAYLOAD_MAX_SIZE] __attribute__ ((aligned (0x1000)));
|
|
||||||
static u8 g_work_page[0x1000] __attribute__ ((aligned (0x1000)));
|
|
||||||
static bool g_payload_loaded = false;
|
|
||||||
static BpcRebootType g_reboot_type = BpcRebootType::ToPayload;
|
|
||||||
|
|
||||||
void BpcRebootManager::Initialize() {
|
|
||||||
/* Open payload file. */
|
|
||||||
FsFile payload_file;
|
|
||||||
if (R_FAILED(Utils::OpenSdFile("/atmosphere/reboot_payload.bin", FS_OPEN_READ, &payload_file))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ON_SCOPE_EXIT { fsFileClose(&payload_file); };
|
|
||||||
|
|
||||||
/* Clear payload buffer */
|
|
||||||
std::memset(g_reboot_payload, 0xFF, sizeof(g_reboot_payload));
|
|
||||||
|
|
||||||
/* Read payload file. */
|
|
||||||
size_t actual_size;
|
|
||||||
fsFileRead(&payload_file, 0, g_reboot_payload, IRAM_PAYLOAD_MAX_SIZE, FS_READOPTION_NONE, &actual_size);
|
|
||||||
|
|
||||||
g_payload_loaded = true;
|
|
||||||
|
|
||||||
/* Figure out what kind of reboot we're gonna be doing. */
|
|
||||||
{
|
|
||||||
char reboot_type[0x40] = {0};
|
|
||||||
u64 reboot_type_size = 0;
|
|
||||||
if (R_SUCCEEDED(Utils::GetSettingsItemValue("atmosphere", "power_menu_reboot_function", reboot_type, sizeof(reboot_type)-1, &reboot_type_size))) {
|
|
||||||
if (strcasecmp(reboot_type, "stock") == 0 || strcasecmp(reboot_type, "normal") == 0 || strcasecmp(reboot_type, "standard") == 0) {
|
|
||||||
g_reboot_type = BpcRebootType::Standard;
|
|
||||||
} else if (strcasecmp(reboot_type, "rcm") == 0) {
|
|
||||||
g_reboot_type = BpcRebootType::ToRcm;
|
|
||||||
} else if (strcasecmp(reboot_type, "payload") == 0) {
|
|
||||||
g_reboot_type = BpcRebootType::ToPayload;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ClearIram() {
|
|
||||||
/* Make page FFs. */
|
|
||||||
memset(g_work_page, 0xFF, sizeof(g_work_page));
|
|
||||||
|
|
||||||
/* Overwrite all of IRAM with FFs. */
|
|
||||||
for (size_t ofs = 0; ofs < IRAM_SIZE; ofs += sizeof(g_work_page)) {
|
|
||||||
CopyToIram(IRAM_BASE + ofs, g_work_page, sizeof(g_work_page));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void DoRebootToPayload() {
|
|
||||||
/* If we don't actually have a payload loaded, just go to RCM. */
|
|
||||||
if (!g_payload_loaded) {
|
|
||||||
RebootToRcm();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ensure clean IRAM state. */
|
|
||||||
ClearIram();
|
|
||||||
|
|
||||||
/* Copy in payload. */
|
|
||||||
for (size_t ofs = 0; ofs < sizeof(g_reboot_payload); ofs += 0x1000) {
|
|
||||||
CopyToIram(IRAM_PAYLOAD_BASE + ofs, &g_reboot_payload[ofs], 0x1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
RebootToIramPayload();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result BpcRebootManager::PerformReboot() {
|
|
||||||
switch (g_reboot_type) {
|
|
||||||
case BpcRebootType::Standard:
|
|
||||||
return ResultAtmosphereMitmShouldForwardToSession;
|
|
||||||
case BpcRebootType::ToRcm:
|
|
||||||
RebootToRcm();
|
|
||||||
return ResultSuccess;
|
|
||||||
case BpcRebootType::ToPayload:
|
|
||||||
default:
|
|
||||||
DoRebootToPayload();
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BpcRebootManager::RebootForFatalError(AtmosphereFatalErrorContext *ctx) {
|
|
||||||
/* Ensure clean IRAM state. */
|
|
||||||
ClearIram();
|
|
||||||
|
|
||||||
|
|
||||||
/* Copy in payload. */
|
|
||||||
for (size_t ofs = 0; ofs < sizeof(g_reboot_payload); ofs += 0x1000) {
|
|
||||||
CopyToIram(IRAM_PAYLOAD_BASE + ofs, &g_reboot_payload[ofs], 0x1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(g_work_page, ctx, sizeof(*ctx));
|
|
||||||
CopyToIram(IRAM_PAYLOAD_BASE + IRAM_PAYLOAD_MAX_SIZE, g_work_page, sizeof(g_work_page));
|
|
||||||
|
|
||||||
/* If we don't actually have a payload loaded, just go to RCM. */
|
|
||||||
if (!g_payload_loaded) {
|
|
||||||
RebootToRcm();
|
|
||||||
}
|
|
||||||
|
|
||||||
RebootToIramPayload();
|
|
||||||
}
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope 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/>.
|
|
||||||
*/
|
|
||||||
#include <cstring>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <switch.h>
|
|
||||||
|
|
||||||
#include "fs_dir_utils.hpp"
|
|
||||||
#include "fs_ifile.hpp"
|
|
||||||
|
|
||||||
Result FsDirUtils::CopyFile(IFileSystem *dst_fs, IFileSystem *src_fs, const FsPath &dst_parent_path, const FsPath &src_path, const FsDirectoryEntry *dir_ent, void *work_buf, size_t work_buf_size) {
|
|
||||||
std::unique_ptr<IFile> src_file;
|
|
||||||
std::unique_ptr<IFile> dst_file;
|
|
||||||
const u64 file_size = dir_ent->fileSize;
|
|
||||||
|
|
||||||
/* Open source file for reading. */
|
|
||||||
R_TRY(src_fs->OpenFile(src_file, src_path, OpenMode_Read));
|
|
||||||
|
|
||||||
/* Create and open destination file. */
|
|
||||||
{
|
|
||||||
FsPath dst_path;
|
|
||||||
if (static_cast<size_t>(snprintf(dst_path.str, sizeof(dst_path.str), "%s%s", dst_parent_path.str, dir_ent->name)) >= sizeof(dst_path)) {
|
|
||||||
/* TODO: Error code? N aborts here. */
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
R_TRY(dst_fs->CreateFile(dst_path, file_size));
|
|
||||||
R_TRY(dst_fs->OpenFile(dst_file, dst_path, OpenMode_Write));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read/Write work_buf_size chunks. */
|
|
||||||
u64 offset = 0;
|
|
||||||
while (offset < file_size) {
|
|
||||||
u64 read_size;
|
|
||||||
R_TRY(src_file->Read(&read_size, offset, work_buf, work_buf_size));
|
|
||||||
R_TRY(dst_file->Write(offset, work_buf, read_size));
|
|
||||||
|
|
||||||
offset += read_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result FsDirUtils::CopyDirectoryRecursively(IFileSystem *dst_fs, IFileSystem *src_fs, const FsPath &dst_path, const FsPath &src_path, void *work_buf, size_t work_buf_size) {
|
|
||||||
FsPath work_path = dst_path;
|
|
||||||
|
|
||||||
return IterateDirectoryRecursively(src_fs, src_path,
|
|
||||||
[&](const FsPath &path, const FsDirectoryEntry *dir_ent) -> Result { /* On Enter Directory */
|
|
||||||
/* Update path, create new dir. */
|
|
||||||
strncat(work_path.str, dir_ent->name, sizeof(work_path) - strnlen(work_path.str, sizeof(work_path) - 1) - 1);
|
|
||||||
strncat(work_path.str, "/", sizeof(work_path) - strnlen(work_path.str, sizeof(work_path) - 1) - 1);
|
|
||||||
return dst_fs->CreateDirectory(work_path);
|
|
||||||
},
|
|
||||||
[&](const FsPath &path, const FsDirectoryEntry *dir_ent) -> Result { /* On Exit Directory */
|
|
||||||
/* Check we have a parent directory. */
|
|
||||||
const size_t work_path_len = strnlen(work_path.str, sizeof(work_path));
|
|
||||||
if (work_path_len < 2) {
|
|
||||||
return ResultFsInvalidPathFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find previous separator, add NULL terminator */
|
|
||||||
char *p = &work_path.str[work_path_len - 2];
|
|
||||||
while (*p != '/' && p > work_path.str) {
|
|
||||||
p--;
|
|
||||||
}
|
|
||||||
p[1] = 0;
|
|
||||||
|
|
||||||
return ResultSuccess;
|
|
||||||
},
|
|
||||||
[&](const FsPath &path, const FsDirectoryEntry *dir_ent) -> Result { /* On File */
|
|
||||||
/* Just copy the file to the new fs. */
|
|
||||||
return CopyFile(dst_fs, src_fs, work_path, path, dir_ent, work_buf, work_buf_size);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Result FsDirUtils::EnsureDirectoryExists(IFileSystem *fs, const FsPath &path) {
|
|
||||||
FsPath normal_path;
|
|
||||||
size_t normal_path_len;
|
|
||||||
|
|
||||||
/* Normalize the path. */
|
|
||||||
R_TRY(FsPathUtils::Normalize(normal_path.str, sizeof(normal_path.str) - 1, path.str, &normal_path_len));
|
|
||||||
|
|
||||||
/* Repeatedly call CreateDirectory on each directory leading to the target. */
|
|
||||||
for (size_t i = 1; i < normal_path_len; i++) {
|
|
||||||
/* If we detect a separator, we're done. */
|
|
||||||
if (normal_path.str[i] == '/') {
|
|
||||||
normal_path.str[i] = 0;
|
|
||||||
{
|
|
||||||
R_TRY_CATCH(fs->CreateDirectory(normal_path)) {
|
|
||||||
R_CATCH(ResultFsPathAlreadyExists) {
|
|
||||||
/* If path already exists, there's no problem. */
|
|
||||||
}
|
|
||||||
} R_END_TRY_CATCH;
|
|
||||||
}
|
|
||||||
normal_path.str[i] = '/';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Call CreateDirectory on the final path. */
|
|
||||||
R_TRY_CATCH(fs->CreateDirectory(normal_path)) {
|
|
||||||
R_CATCH(ResultFsPathAlreadyExists) {
|
|
||||||
/* If path already exists, there's no problem. */
|
|
||||||
}
|
|
||||||
} R_END_TRY_CATCH;
|
|
||||||
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
|
|
||||||
#include "fs_path_utils.hpp"
|
|
||||||
#include "fs_ifilesystem.hpp"
|
|
||||||
|
|
||||||
class FsDirUtils {
|
|
||||||
private:
|
|
||||||
template<typename OnEnterDir, typename OnExitDir, typename OnFile>
|
|
||||||
static Result IterateDirectoryRecursivelyInternal(IFileSystem *fs, FsPath &work_path, FsDirectoryEntry *ent_buf, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) {
|
|
||||||
std::unique_ptr<IDirectory> dir;
|
|
||||||
|
|
||||||
/* Open the directory. */
|
|
||||||
R_TRY(fs->OpenDirectory(dir, work_path, DirectoryOpenMode_All));
|
|
||||||
|
|
||||||
const size_t parent_len = strnlen(work_path.str, sizeof(work_path.str) - 1);
|
|
||||||
|
|
||||||
/* Read and handle entries. */
|
|
||||||
while (true) {
|
|
||||||
/* Read a single entry. */
|
|
||||||
u64 read_count;
|
|
||||||
R_TRY(dir->Read(&read_count, ent_buf, 1));
|
|
||||||
|
|
||||||
/* If we're out of entries, we're done. */
|
|
||||||
if (read_count == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t child_name_len = strnlen(ent_buf->name, sizeof(ent_buf->name) - 1);
|
|
||||||
const bool is_dir = ent_buf->type == ENTRYTYPE_DIR;
|
|
||||||
const size_t child_path_len = parent_len + child_name_len + (is_dir ? 1 : 0);
|
|
||||||
|
|
||||||
/* Validate child path size. */
|
|
||||||
if (child_path_len >= sizeof(work_path.str)) {
|
|
||||||
return ResultFsTooLongPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
strncat(work_path.str, ent_buf->name, sizeof(work_path.str) - 1 - parent_len);
|
|
||||||
if (is_dir) {
|
|
||||||
/* Enter directory. */
|
|
||||||
R_TRY(on_enter_dir(work_path, ent_buf));
|
|
||||||
|
|
||||||
/* Append separator, recurse. */
|
|
||||||
strncat(work_path.str, "/", sizeof(work_path.str) - 1 - parent_len - child_name_len);
|
|
||||||
R_TRY(IterateDirectoryRecursivelyInternal(fs, work_path, ent_buf, on_enter_dir, on_exit_dir, on_file));
|
|
||||||
|
|
||||||
/* Exit directory. */
|
|
||||||
R_TRY(on_exit_dir(work_path, ent_buf));
|
|
||||||
} else {
|
|
||||||
/* Call file handler. */
|
|
||||||
R_TRY(on_file(work_path, ent_buf));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Restore parent path. */
|
|
||||||
work_path.str[parent_len] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
template<typename OnEnterDir, typename OnExitDir, typename OnFile>
|
|
||||||
static Result IterateDirectoryRecursively(IFileSystem *fs, const FsPath &root_path, FsPath &work_path, FsDirectoryEntry *ent_buf, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) {
|
|
||||||
/* Ensure valid root path. */
|
|
||||||
size_t root_path_len = strnlen(root_path.str, sizeof(root_path.str));
|
|
||||||
if (root_path_len > FS_MAX_PATH - 1 || ((root_path_len == FS_MAX_PATH - 1) && root_path.str[root_path_len-1] != '/')) {
|
|
||||||
return ResultFsTooLongPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy path, ensure terminating separator. */
|
|
||||||
memcpy(work_path.str, root_path.str, root_path_len);
|
|
||||||
if (work_path.str[root_path_len-1] != '/') {
|
|
||||||
root_path_len++;
|
|
||||||
work_path.str[root_path_len-1] = '/';
|
|
||||||
}
|
|
||||||
work_path.str[root_path_len] = 0;
|
|
||||||
|
|
||||||
/* Actually iterate. */
|
|
||||||
return IterateDirectoryRecursivelyInternal(fs, work_path, ent_buf, on_enter_dir, on_exit_dir, on_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Helper for not specifying work path/entry buffer. */
|
|
||||||
template<typename OnEnterDir, typename OnExitDir, typename OnFile>
|
|
||||||
static Result IterateDirectoryRecursively(IFileSystem *fs, const FsPath &root_path, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) {
|
|
||||||
FsDirectoryEntry dir_ent = {0};
|
|
||||||
FsPath work_path = {0};
|
|
||||||
return IterateDirectoryRecursively(fs, root_path, work_path, &dir_ent, on_enter_dir, on_exit_dir, on_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Helper for iterating over the filesystem root. */
|
|
||||||
template<typename OnEnterDir, typename OnExitDir, typename OnFile>
|
|
||||||
static Result IterateDirectoryRecursively(IFileSystem *fs, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) {
|
|
||||||
return IterateDirectoryRecursively(fs, FsPathUtils::RootPath, on_enter_dir, on_exit_dir, on_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy API. */
|
|
||||||
static Result CopyFile(IFileSystem *dst_fs, IFileSystem *src_fs, const FsPath &dst_parent_path, const FsPath &src_path, const FsDirectoryEntry *dir_ent, void *work_buf, size_t work_buf_size);
|
|
||||||
static Result CopyFile(IFileSystem *fs, const FsPath &dst_parent_path, const FsPath &src_path, const FsDirectoryEntry *dir_ent, void *work_buf, size_t work_buf_size) {
|
|
||||||
return CopyFile(fs, fs, dst_parent_path, src_path, dir_ent, work_buf, work_buf_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Result CopyDirectoryRecursively(IFileSystem *dst_fs, IFileSystem *src_fs, const FsPath &dst_path, const FsPath &src_path, void *work_buf, size_t work_buf_size);
|
|
||||||
static Result CopyDirectoryRecursively(IFileSystem *fs, const FsPath &dst_path, const FsPath &src_path, void *work_buf, size_t work_buf_size) {
|
|
||||||
return CopyDirectoryRecursively(fs, fs, dst_path, src_path, work_buf, work_buf_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ensure directory existence. */
|
|
||||||
static Result EnsureDirectoryExists(IFileSystem *fs, const FsPath &path);
|
|
||||||
|
|
||||||
/* Other Utility. */
|
|
||||||
template<typename F>
|
|
||||||
static Result RetryUntilTargetNotLocked(F f) {
|
|
||||||
const size_t MaxRetries = 10;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < MaxRetries; i++) {
|
|
||||||
R_TRY_CATCH(f()) {
|
|
||||||
R_CATCH(ResultFsTargetLocked) {
|
|
||||||
/* If target is locked, wait 100ms and try again. */
|
|
||||||
svcSleepThread(100'000'000ul);
|
|
||||||
}
|
|
||||||
} R_END_TRY_CATCH;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,188 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <switch.h>
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
|
|
||||||
#include "../utils.hpp"
|
|
||||||
#include "fs_directory_redirection_filesystem.hpp"
|
|
||||||
#include "fs_path_utils.hpp"
|
|
||||||
|
|
||||||
static char *GetNormalizedDirectory(const char *dir_prefix) {
|
|
||||||
/* Normalize the path. */
|
|
||||||
char normal_path[FS_MAX_PATH + 1];
|
|
||||||
size_t normal_path_len;
|
|
||||||
R_ASSERT(FsPathUtils::Normalize(normal_path, sizeof(normal_path), dir_prefix, &normal_path_len));
|
|
||||||
|
|
||||||
/* Ensure terminating '/' */
|
|
||||||
if (normal_path[normal_path_len-1] != '/') {
|
|
||||||
if (normal_path_len + 2 > sizeof(normal_path)) {
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
strncat(normal_path, "/", 2);
|
|
||||||
normal_path[sizeof(normal_path)-1] = 0;
|
|
||||||
normal_path_len++;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *output = static_cast<char *>(malloc(normal_path_len + 1));
|
|
||||||
if (output == nullptr) {
|
|
||||||
std::abort();
|
|
||||||
/* TODO: Result error code? */
|
|
||||||
}
|
|
||||||
|
|
||||||
std::strncpy(output, normal_path, normal_path_len + 1);
|
|
||||||
output[normal_path_len] = 0;
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectoryRedirectionFileSystem::Initialize(const char *before, const char *after) {
|
|
||||||
if (strnlen(before, FS_MAX_PATH) >= FS_MAX_PATH || strnlen(after, FS_MAX_PATH) >= FS_MAX_PATH) {
|
|
||||||
return ResultFsTooLongPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->before_dir = GetNormalizedDirectory(before);
|
|
||||||
this->after_dir = GetNormalizedDirectory(after);
|
|
||||||
this->before_dir_len = strlen(this->before_dir);
|
|
||||||
this->after_dir_len = strlen(this->after_dir);
|
|
||||||
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectoryRedirectionFileSystem::GetFullPath(char *out, size_t out_size, const char *relative_path) {
|
|
||||||
FsPath tmp_rel_path;
|
|
||||||
R_TRY(FsPathUtils::Normalize(tmp_rel_path.str, sizeof(tmp_rel_path), relative_path, nullptr));
|
|
||||||
|
|
||||||
if (std::strncmp(tmp_rel_path.str, this->before_dir, this->before_dir_len) == 0) {
|
|
||||||
if (this->after_dir_len + strnlen(tmp_rel_path.str, FS_MAX_PATH) - this->before_dir_len > out_size) {
|
|
||||||
return ResultFsTooLongPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy after path. */
|
|
||||||
std::strncpy(out, this->after_dir, out_size);
|
|
||||||
out[out_size - 1] = 0;
|
|
||||||
|
|
||||||
/* Normalize it. */
|
|
||||||
return FsPathUtils::Normalize(out + this->after_dir_len - 1, out_size - (this->after_dir_len - 1), relative_path + this->before_dir_len - 1, nullptr);
|
|
||||||
} else if (std::memcmp(tmp_rel_path.str, this->before_dir, this->before_dir_len - 1) == 0) {
|
|
||||||
/* Handling raw directory. */
|
|
||||||
if (this->after_dir_len + 1 > out_size) {
|
|
||||||
return ResultFsTooLongPath;
|
|
||||||
}
|
|
||||||
std::strncpy(out, this->after_dir, out_size);
|
|
||||||
out[out_size - 1] = 0;
|
|
||||||
return ResultSuccess;
|
|
||||||
} else {
|
|
||||||
return FsPathUtils::Normalize(out, out_size, relative_path, nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectoryRedirectionFileSystem::CreateFileImpl(const FsPath &path, uint64_t size, int flags) {
|
|
||||||
FsPath full_path;
|
|
||||||
R_TRY(GetFullPath(full_path, path));
|
|
||||||
return this->base_fs->CreateFile(full_path, size, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectoryRedirectionFileSystem::DeleteFileImpl(const FsPath &path) {
|
|
||||||
FsPath full_path;
|
|
||||||
R_TRY(GetFullPath(full_path, path));
|
|
||||||
return this->base_fs->DeleteFile(full_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectoryRedirectionFileSystem::CreateDirectoryImpl(const FsPath &path) {
|
|
||||||
FsPath full_path;
|
|
||||||
R_TRY(GetFullPath(full_path, path));
|
|
||||||
return this->base_fs->CreateDirectory(full_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectoryRedirectionFileSystem::DeleteDirectoryImpl(const FsPath &path) {
|
|
||||||
FsPath full_path;
|
|
||||||
R_TRY(GetFullPath(full_path, path));
|
|
||||||
return this->base_fs->DeleteDirectory(full_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectoryRedirectionFileSystem::DeleteDirectoryRecursivelyImpl(const FsPath &path) {
|
|
||||||
FsPath full_path;
|
|
||||||
R_TRY(GetFullPath(full_path, path));
|
|
||||||
return this->base_fs->DeleteDirectoryRecursively(full_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectoryRedirectionFileSystem::RenameFileImpl(const FsPath &old_path, const FsPath &new_path) {
|
|
||||||
FsPath full_old_path, full_new_path;
|
|
||||||
R_TRY(GetFullPath(full_old_path, old_path));
|
|
||||||
R_TRY(GetFullPath(full_new_path, new_path));
|
|
||||||
return this->base_fs->RenameFile(full_old_path, full_new_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectoryRedirectionFileSystem::RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) {
|
|
||||||
FsPath full_old_path, full_new_path;
|
|
||||||
R_TRY(GetFullPath(full_old_path, old_path));
|
|
||||||
R_TRY(GetFullPath(full_new_path, new_path));
|
|
||||||
return this->base_fs->RenameDirectory(full_old_path, full_new_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectoryRedirectionFileSystem::GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) {
|
|
||||||
FsPath full_path;
|
|
||||||
R_TRY(GetFullPath(full_path, path));
|
|
||||||
return this->base_fs->GetEntryType(out, full_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectoryRedirectionFileSystem::OpenFileImpl(std::unique_ptr<IFile> &out_file, const FsPath &path, OpenMode mode) {
|
|
||||||
FsPath full_path;
|
|
||||||
R_TRY(GetFullPath(full_path, path));
|
|
||||||
return this->base_fs->OpenFile(out_file, full_path, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectoryRedirectionFileSystem::OpenDirectoryImpl(std::unique_ptr<IDirectory> &out_dir, const FsPath &path, DirectoryOpenMode mode) {
|
|
||||||
FsPath full_path;
|
|
||||||
R_TRY(GetFullPath(full_path, path));
|
|
||||||
return this->base_fs->OpenDirectory(out_dir, full_path, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectoryRedirectionFileSystem::CommitImpl() {
|
|
||||||
return this->base_fs->Commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectoryRedirectionFileSystem::GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) {
|
|
||||||
FsPath full_path;
|
|
||||||
R_TRY(GetFullPath(full_path, path));
|
|
||||||
return this->base_fs->GetFreeSpaceSize(out, full_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectoryRedirectionFileSystem::GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) {
|
|
||||||
FsPath full_path;
|
|
||||||
R_TRY(GetFullPath(full_path, path));
|
|
||||||
return this->base_fs->GetTotalSpaceSize(out, full_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectoryRedirectionFileSystem::CleanDirectoryRecursivelyImpl(const FsPath &path) {
|
|
||||||
FsPath full_path;
|
|
||||||
R_TRY(GetFullPath(full_path, path));
|
|
||||||
return this->base_fs->CleanDirectoryRecursively(full_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectoryRedirectionFileSystem::GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) {
|
|
||||||
FsPath full_path;
|
|
||||||
R_TRY(GetFullPath(full_path, path));
|
|
||||||
return this->base_fs->GetFileTimeStampRaw(out, full_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectoryRedirectionFileSystem::QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) {
|
|
||||||
FsPath full_path;
|
|
||||||
R_TRY(GetFullPath(full_path, path));
|
|
||||||
return this->base_fs->QueryEntry(out, out_size, in, in_size, query, full_path);
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
|
|
||||||
#include "fs_ifilesystem.hpp"
|
|
||||||
#include "fs_path_utils.hpp"
|
|
||||||
|
|
||||||
class DirectoryRedirectionFileSystem : public IFileSystem {
|
|
||||||
private:
|
|
||||||
std::shared_ptr<IFileSystem> base_fs;
|
|
||||||
char *before_dir = nullptr;
|
|
||||||
size_t before_dir_len = 0;
|
|
||||||
char *after_dir = nullptr;
|
|
||||||
size_t after_dir_len = 0;
|
|
||||||
|
|
||||||
public:
|
|
||||||
DirectoryRedirectionFileSystem(IFileSystem *fs, const char *before, const char *after) : base_fs(fs) {
|
|
||||||
R_ASSERT(this->Initialize(before, after));
|
|
||||||
}
|
|
||||||
|
|
||||||
DirectoryRedirectionFileSystem(std::shared_ptr<IFileSystem> fs, const char *before, const char *after) : base_fs(fs) {
|
|
||||||
R_ASSERT(this->Initialize(before, after));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
virtual ~DirectoryRedirectionFileSystem() {
|
|
||||||
if (this->before_dir != nullptr) {
|
|
||||||
free(this->before_dir);
|
|
||||||
}
|
|
||||||
if (this->after_dir != nullptr) {
|
|
||||||
free(this->after_dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Result Initialize(const char *before, const char *after);
|
|
||||||
protected:
|
|
||||||
Result GetFullPath(char *out, size_t out_size, const char *relative_path);
|
|
||||||
Result GetFullPath(FsPath &full_path, const FsPath &relative_path) {
|
|
||||||
return GetFullPath(full_path.str, sizeof(full_path.str), relative_path.str);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual Result CreateFileImpl(const FsPath &path, uint64_t size, int flags) override;
|
|
||||||
virtual Result DeleteFileImpl(const FsPath &path) override;
|
|
||||||
virtual Result CreateDirectoryImpl(const FsPath &path) override;
|
|
||||||
virtual Result DeleteDirectoryImpl(const FsPath &path) override;
|
|
||||||
virtual Result DeleteDirectoryRecursivelyImpl(const FsPath &path) override;
|
|
||||||
virtual Result RenameFileImpl(const FsPath &old_path, const FsPath &new_path) override;
|
|
||||||
virtual Result RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) override;
|
|
||||||
virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) override;
|
|
||||||
virtual Result OpenFileImpl(std::unique_ptr<IFile> &out_file, const FsPath &path, OpenMode mode) override;
|
|
||||||
virtual Result OpenDirectoryImpl(std::unique_ptr<IDirectory> &out_dir, const FsPath &path, DirectoryOpenMode mode) override;
|
|
||||||
virtual Result CommitImpl() override;
|
|
||||||
virtual Result GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) override;
|
|
||||||
virtual Result GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) override;
|
|
||||||
virtual Result CleanDirectoryRecursivelyImpl(const FsPath &path) override;
|
|
||||||
virtual Result GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) override;
|
|
||||||
virtual Result QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) override;
|
|
||||||
};
|
|
||||||
@@ -1,335 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <switch.h>
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
|
|
||||||
#include "../utils.hpp"
|
|
||||||
#include "fs_directory_savedata_filesystem.hpp"
|
|
||||||
#include "fs_dir_utils.hpp"
|
|
||||||
|
|
||||||
class DirectorySaveDataFile : public IFile {
|
|
||||||
private:
|
|
||||||
std::unique_ptr<IFile> base_file;
|
|
||||||
DirectorySaveDataFileSystem *parent_fs; /* TODO: shared_ptr + enabled_shared_from_this? */
|
|
||||||
int open_mode;
|
|
||||||
public:
|
|
||||||
DirectorySaveDataFile(std::unique_ptr<IFile> f, DirectorySaveDataFileSystem *p, int m) : base_file(std::move(f)), parent_fs(p), open_mode(m) {
|
|
||||||
/* ... */
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~DirectorySaveDataFile() {
|
|
||||||
/* Observe closing of writable file. */
|
|
||||||
if (this->open_mode & OpenMode_Write) {
|
|
||||||
this->parent_fs->OnWritableFileClose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
virtual Result ReadImpl(u64 *out, u64 offset, void *buffer, u64 size) override {
|
|
||||||
return this->base_file->Read(out, offset, buffer, size);
|
|
||||||
}
|
|
||||||
virtual Result GetSizeImpl(u64 *out) override {
|
|
||||||
return this->base_file->GetSize(out);
|
|
||||||
}
|
|
||||||
virtual Result FlushImpl() override {
|
|
||||||
return this->base_file->Flush();
|
|
||||||
}
|
|
||||||
virtual Result WriteImpl(u64 offset, void *buffer, u64 size, u32 option) override {
|
|
||||||
return this->base_file->Write(offset, buffer, size, option);
|
|
||||||
}
|
|
||||||
virtual Result SetSizeImpl(u64 size) override {
|
|
||||||
return this->base_file->SetSize(size);
|
|
||||||
}
|
|
||||||
virtual Result OperateRangeImpl(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override {
|
|
||||||
return this->base_file->OperateRange(operation_type, offset, size, out_range_info);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ================================================================================================ */
|
|
||||||
|
|
||||||
Result DirectorySaveDataFileSystem::Initialize() {
|
|
||||||
DirectoryEntryType ent_type;
|
|
||||||
|
|
||||||
/* Check that the working directory exists. */
|
|
||||||
R_TRY_CATCH(this->fs->GetEntryType(&ent_type, WorkingDirectoryPath)) {
|
|
||||||
/* If path isn't found, create working directory and committed directory. */
|
|
||||||
R_CATCH(ResultFsPathNotFound) {
|
|
||||||
R_TRY(this->fs->CreateDirectory(WorkingDirectoryPath));
|
|
||||||
R_TRY(this->fs->CreateDirectory(CommittedDirectoryPath));
|
|
||||||
}
|
|
||||||
} R_END_TRY_CATCH;
|
|
||||||
|
|
||||||
/* Now check for the committed directory. */
|
|
||||||
R_TRY_CATCH(this->fs->GetEntryType(&ent_type, CommittedDirectoryPath)) {
|
|
||||||
/* Committed doesn't exist, so synchronize and rename. */
|
|
||||||
R_CATCH(ResultFsPathNotFound) {
|
|
||||||
R_TRY(this->SynchronizeDirectory(SynchronizingDirectoryPath, WorkingDirectoryPath));
|
|
||||||
return this->fs->RenameDirectory(SynchronizingDirectoryPath, CommittedDirectoryPath);
|
|
||||||
}
|
|
||||||
} R_END_TRY_CATCH;
|
|
||||||
|
|
||||||
/* If committed exists, synchronize it to the working directory. */
|
|
||||||
return this->SynchronizeDirectory(WorkingDirectoryPath, CommittedDirectoryPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectorySaveDataFileSystem::SynchronizeDirectory(const FsPath &dst_dir, const FsPath &src_dir) {
|
|
||||||
/* Delete destination dir and recreate it. */
|
|
||||||
R_TRY_CATCH(this->fs->DeleteDirectoryRecursively(dst_dir)) {
|
|
||||||
R_CATCH(ResultFsPathNotFound) {
|
|
||||||
/* Nintendo returns error unconditionally, but I think that's a bug in their code. */
|
|
||||||
}
|
|
||||||
} R_END_TRY_CATCH;
|
|
||||||
|
|
||||||
R_TRY(this->fs->CreateDirectory(dst_dir));
|
|
||||||
|
|
||||||
/* Get a buffer to work with. */
|
|
||||||
void *work_buf = nullptr;
|
|
||||||
size_t work_buf_size = 0;
|
|
||||||
R_TRY(this->AllocateWorkBuffer(&work_buf, &work_buf_size, IdealWorkBuffersize));
|
|
||||||
ON_SCOPE_EXIT { free(work_buf); };
|
|
||||||
|
|
||||||
return FsDirUtils::CopyDirectoryRecursively(this->fs, dst_dir, src_dir, work_buf, work_buf_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectorySaveDataFileSystem::AllocateWorkBuffer(void **out_buf, size_t *out_size, const size_t ideal_size) {
|
|
||||||
size_t try_size = ideal_size;
|
|
||||||
|
|
||||||
/* Repeatedly try to allocate until success. */
|
|
||||||
while (try_size > 0x200) {
|
|
||||||
void *buf = malloc(try_size);
|
|
||||||
if (buf != nullptr) {
|
|
||||||
*out_buf = buf;
|
|
||||||
*out_size = try_size;
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Divide size by two. */
|
|
||||||
try_size >>= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: Return a result here? Nintendo does not, but they have other allocation failed results. */
|
|
||||||
/* Consider returning ResultFsAllocationFailureInDirectorySaveDataFileSystem? */
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectorySaveDataFileSystem::GetFullPath(char *out, size_t out_size, const char *relative_path) {
|
|
||||||
/* Validate path. */
|
|
||||||
if (1 + strnlen(relative_path, FS_MAX_PATH) >= out_size) {
|
|
||||||
return ResultFsTooLongPath;
|
|
||||||
}
|
|
||||||
if (relative_path[0] != '/') {
|
|
||||||
return ResultFsInvalidPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy working directory path. */
|
|
||||||
std::strncpy(out, WorkingDirectoryPath.str, out_size);
|
|
||||||
out[out_size-1] = 0;
|
|
||||||
|
|
||||||
/* Normalize it. */
|
|
||||||
constexpr size_t working_len = WorkingDirectoryPathLen - 1;
|
|
||||||
return FsPathUtils::Normalize(out + working_len, out_size - working_len, relative_path, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirectorySaveDataFileSystem::OnWritableFileClose() {
|
|
||||||
std::scoped_lock<HosMutex> lk(this->lock);
|
|
||||||
this->open_writable_files--;
|
|
||||||
|
|
||||||
/* TODO: Abort if < 0? N does not. */
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectorySaveDataFileSystem::CopySaveFromProxy() {
|
|
||||||
if (this->proxy_save_fs != nullptr) {
|
|
||||||
/* Get a buffer to work with. */
|
|
||||||
void *work_buf = nullptr;
|
|
||||||
size_t work_buf_size = 0;
|
|
||||||
R_TRY(this->AllocateWorkBuffer(&work_buf, &work_buf_size, IdealWorkBuffersize));
|
|
||||||
ON_SCOPE_EXIT { free(work_buf); };
|
|
||||||
|
|
||||||
R_TRY(FsDirUtils::CopyDirectoryRecursively(this, this->proxy_save_fs.get(), FsPathUtils::RootPath, FsPathUtils::RootPath, work_buf, work_buf_size));
|
|
||||||
return this->Commit();
|
|
||||||
}
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================================================================ */
|
|
||||||
|
|
||||||
Result DirectorySaveDataFileSystem::CreateFileImpl(const FsPath &path, uint64_t size, int flags) {
|
|
||||||
FsPath full_path;
|
|
||||||
R_TRY(GetFullPath(full_path, path));
|
|
||||||
|
|
||||||
std::scoped_lock<HosMutex> lk(this->lock);
|
|
||||||
return this->fs->CreateFile(full_path, size, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectorySaveDataFileSystem::DeleteFileImpl(const FsPath &path) {
|
|
||||||
FsPath full_path;
|
|
||||||
R_TRY(GetFullPath(full_path, path));
|
|
||||||
|
|
||||||
std::scoped_lock<HosMutex> lk(this->lock);
|
|
||||||
return this->fs->DeleteFile(full_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectorySaveDataFileSystem::CreateDirectoryImpl(const FsPath &path) {
|
|
||||||
FsPath full_path;
|
|
||||||
R_TRY(GetFullPath(full_path, path));
|
|
||||||
|
|
||||||
std::scoped_lock<HosMutex> lk(this->lock);
|
|
||||||
return this->fs->CreateDirectory(full_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectorySaveDataFileSystem::DeleteDirectoryImpl(const FsPath &path) {
|
|
||||||
FsPath full_path;
|
|
||||||
R_TRY(GetFullPath(full_path, path));
|
|
||||||
|
|
||||||
std::scoped_lock<HosMutex> lk(this->lock);
|
|
||||||
return this->fs->DeleteDirectory(full_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectorySaveDataFileSystem::DeleteDirectoryRecursivelyImpl(const FsPath &path) {
|
|
||||||
FsPath full_path;
|
|
||||||
R_TRY(GetFullPath(full_path, path));
|
|
||||||
|
|
||||||
std::scoped_lock<HosMutex> lk(this->lock);
|
|
||||||
return this->fs->DeleteDirectoryRecursively(full_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectorySaveDataFileSystem::RenameFileImpl(const FsPath &old_path, const FsPath &new_path) {
|
|
||||||
FsPath full_old_path, full_new_path;
|
|
||||||
R_TRY(GetFullPath(full_old_path, old_path));
|
|
||||||
R_TRY(GetFullPath(full_new_path, new_path));
|
|
||||||
|
|
||||||
std::scoped_lock<HosMutex> lk(this->lock);
|
|
||||||
return this->fs->RenameFile(full_old_path, full_new_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectorySaveDataFileSystem::RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) {
|
|
||||||
FsPath full_old_path, full_new_path;
|
|
||||||
R_TRY(GetFullPath(full_old_path, old_path));
|
|
||||||
R_TRY(GetFullPath(full_new_path, new_path));
|
|
||||||
|
|
||||||
std::scoped_lock<HosMutex> lk(this->lock);
|
|
||||||
return this->fs->RenameDirectory(full_old_path, full_new_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectorySaveDataFileSystem::GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) {
|
|
||||||
FsPath full_path;
|
|
||||||
R_TRY(GetFullPath(full_path, path));
|
|
||||||
|
|
||||||
std::scoped_lock<HosMutex> lk(this->lock);
|
|
||||||
return this->fs->GetEntryType(out, full_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectorySaveDataFileSystem::OpenFileImpl(std::unique_ptr<IFile> &out_file, const FsPath &path, OpenMode mode) {
|
|
||||||
FsPath full_path;
|
|
||||||
R_TRY(GetFullPath(full_path, path));
|
|
||||||
|
|
||||||
std::scoped_lock<HosMutex> lk(this->lock);
|
|
||||||
|
|
||||||
{
|
|
||||||
/* Open the raw file. */
|
|
||||||
std::unique_ptr<IFile> file;
|
|
||||||
R_TRY(this->fs->OpenFile(file, full_path, mode));
|
|
||||||
|
|
||||||
/* Create DirectorySaveDataFile wrapper. */
|
|
||||||
out_file = std::make_unique<DirectorySaveDataFile>(std::move(file), this, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check for allocation failure. */
|
|
||||||
if (out_file == nullptr) {
|
|
||||||
return ResultFsAllocationFailureInDirectorySaveDataFileSystem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Increment open writable files, if needed. */
|
|
||||||
if (mode & OpenMode_Write) {
|
|
||||||
this->open_writable_files++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectorySaveDataFileSystem::OpenDirectoryImpl(std::unique_ptr<IDirectory> &out_dir, const FsPath &path, DirectoryOpenMode mode) {
|
|
||||||
FsPath full_path;
|
|
||||||
R_TRY(GetFullPath(full_path, path));
|
|
||||||
|
|
||||||
std::scoped_lock<HosMutex> lk(this->lock);
|
|
||||||
return this->fs->OpenDirectory(out_dir, full_path, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectorySaveDataFileSystem::CommitImpl() {
|
|
||||||
/* Here, Nintendo does the following (with retries): */
|
|
||||||
/* - Rename Committed -> Synchronizing. */
|
|
||||||
/* - Synchronize Working -> Synchronizing (deleting Synchronizing). */
|
|
||||||
/* - Rename Synchronizing -> Committed. */
|
|
||||||
/* I think this is not the optimal order to do things, as the previous committed directory */
|
|
||||||
/* will be deleted if there is an error during synchronization. */
|
|
||||||
/* Instead, we will synchronize first, then delete committed, then rename. */
|
|
||||||
|
|
||||||
std::scoped_lock<HosMutex> lk(this->lock);
|
|
||||||
|
|
||||||
/* Ensure we don't have any open writable files. */
|
|
||||||
if (this->open_writable_files != 0) {
|
|
||||||
return ResultFsPreconditionViolation;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto SynchronizeWorkingDir = [&]() { return this->SynchronizeDirectory(SynchronizingDirectoryPath, WorkingDirectoryPath); };
|
|
||||||
const auto DeleteCommittedDir = [&]() { return this->fs->DeleteDirectoryRecursively(CommittedDirectoryPath); };
|
|
||||||
const auto RenameSynchDir = [&]() { return this->fs->RenameDirectory(SynchronizingDirectoryPath, CommittedDirectoryPath); };
|
|
||||||
|
|
||||||
/* Synchronize working directory. */
|
|
||||||
R_TRY(FsDirUtils::RetryUntilTargetNotLocked(std::move(SynchronizeWorkingDir)));
|
|
||||||
|
|
||||||
/* Delete committed directory. */
|
|
||||||
R_TRY_CATCH(FsDirUtils::RetryUntilTargetNotLocked(std::move(DeleteCommittedDir))) {
|
|
||||||
R_CATCH(ResultFsPathNotFound) {
|
|
||||||
/* It is okay for us to not have a committed directory here. */
|
|
||||||
}
|
|
||||||
} R_END_TRY_CATCH;
|
|
||||||
|
|
||||||
/* Rename synchronizing directory to committed directory. */
|
|
||||||
R_TRY(FsDirUtils::RetryUntilTargetNotLocked(std::move(RenameSynchDir)));
|
|
||||||
|
|
||||||
/* TODO: Should I call this->fs->Commit()? Nintendo does not. */
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectorySaveDataFileSystem::GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) {
|
|
||||||
/* TODO: How should this work? N returns ResultFsNotImplemented. */
|
|
||||||
return ResultFsNotImplemented;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectorySaveDataFileSystem::GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) {
|
|
||||||
/* TODO: How should this work? N returns ResultFsNotImplemented. */
|
|
||||||
return ResultFsNotImplemented;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectorySaveDataFileSystem::CleanDirectoryRecursivelyImpl(const FsPath &path) {
|
|
||||||
FsPath full_path;
|
|
||||||
R_TRY(GetFullPath(full_path, path));
|
|
||||||
|
|
||||||
std::scoped_lock<HosMutex> lk(this->lock);
|
|
||||||
return this->fs->CleanDirectoryRecursively(full_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectorySaveDataFileSystem::GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) {
|
|
||||||
/* TODO: How should this work? N returns ResultFsNotImplemented. */
|
|
||||||
return ResultFsNotImplemented;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DirectorySaveDataFileSystem::QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) {
|
|
||||||
/* TODO: How should this work? N returns ResultFsNotImplemented. */
|
|
||||||
return ResultFsNotImplemented;
|
|
||||||
}
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
|
|
||||||
#include "fs_ifilesystem.hpp"
|
|
||||||
#include "fs_path_utils.hpp"
|
|
||||||
|
|
||||||
class DirectorySaveDataFileSystem : public IFileSystem {
|
|
||||||
private:
|
|
||||||
static constexpr FsPath CommittedDirectoryPath = EncodeConstantFsPath("/0/");
|
|
||||||
static constexpr FsPath WorkingDirectoryPath = EncodeConstantFsPath("/1/");
|
|
||||||
static constexpr FsPath SynchronizingDirectoryPath = EncodeConstantFsPath("/_/");
|
|
||||||
|
|
||||||
static constexpr size_t CommittedDirectoryPathLen = GetConstantFsPathLen(CommittedDirectoryPath);
|
|
||||||
static constexpr size_t WorkingDirectoryPathLen = GetConstantFsPathLen(WorkingDirectoryPath);
|
|
||||||
static constexpr size_t SynchronizingDirectoryPathLen = GetConstantFsPathLen(SynchronizingDirectoryPath);
|
|
||||||
|
|
||||||
static constexpr size_t IdealWorkBuffersize = 0x100000; /* 1 MB */
|
|
||||||
private:
|
|
||||||
std::shared_ptr<IFileSystem> shared_fs;
|
|
||||||
std::unique_ptr<IFileSystem> unique_fs;
|
|
||||||
std::unique_ptr<IFileSystem> proxy_save_fs;
|
|
||||||
IFileSystem *fs;
|
|
||||||
HosMutex lock;
|
|
||||||
size_t open_writable_files = 0;
|
|
||||||
|
|
||||||
public:
|
|
||||||
DirectorySaveDataFileSystem(IFileSystem *fs, std::unique_ptr<IFileSystem> pfs) : unique_fs(fs), proxy_save_fs(std::move(pfs)) {
|
|
||||||
this->fs = this->unique_fs.get();
|
|
||||||
R_ASSERT(this->Initialize());
|
|
||||||
}
|
|
||||||
|
|
||||||
DirectorySaveDataFileSystem(std::unique_ptr<IFileSystem> fs, std::unique_ptr<IFileSystem> pfs) : unique_fs(std::move(fs)), proxy_save_fs(std::move(pfs)) {
|
|
||||||
this->fs = this->unique_fs.get();
|
|
||||||
R_ASSERT(this->Initialize());
|
|
||||||
}
|
|
||||||
|
|
||||||
DirectorySaveDataFileSystem(std::shared_ptr<IFileSystem> fs, std::unique_ptr<IFileSystem> pfs) : shared_fs(fs), proxy_save_fs(std::move(pfs)) {
|
|
||||||
this->fs = this->shared_fs.get();
|
|
||||||
R_ASSERT(this->Initialize());
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~DirectorySaveDataFileSystem() { }
|
|
||||||
|
|
||||||
private:
|
|
||||||
Result Initialize();
|
|
||||||
protected:
|
|
||||||
Result SynchronizeDirectory(const FsPath &dst_dir, const FsPath &src_dir);
|
|
||||||
Result AllocateWorkBuffer(void **out_buf, size_t *out_size, const size_t ideal_size);
|
|
||||||
|
|
||||||
Result GetFullPath(char *out, size_t out_size, const char *relative_path);
|
|
||||||
Result GetFullPath(FsPath &full_path, const FsPath &relative_path) {
|
|
||||||
return GetFullPath(full_path.str, sizeof(full_path.str), relative_path.str);
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
void OnWritableFileClose();
|
|
||||||
Result CopySaveFromProxy();
|
|
||||||
public:
|
|
||||||
virtual Result CreateFileImpl(const FsPath &path, uint64_t size, int flags) override;
|
|
||||||
virtual Result DeleteFileImpl(const FsPath &path) override;
|
|
||||||
virtual Result CreateDirectoryImpl(const FsPath &path) override;
|
|
||||||
virtual Result DeleteDirectoryImpl(const FsPath &path) override;
|
|
||||||
virtual Result DeleteDirectoryRecursivelyImpl(const FsPath &path) override;
|
|
||||||
virtual Result RenameFileImpl(const FsPath &old_path, const FsPath &new_path) override;
|
|
||||||
virtual Result RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) override;
|
|
||||||
virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) override;
|
|
||||||
virtual Result OpenFileImpl(std::unique_ptr<IFile> &out_file, const FsPath &path, OpenMode mode) override;
|
|
||||||
virtual Result OpenDirectoryImpl(std::unique_ptr<IDirectory> &out_dir, const FsPath &path, DirectoryOpenMode mode) override;
|
|
||||||
virtual Result CommitImpl() override;
|
|
||||||
virtual Result GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) override;
|
|
||||||
virtual Result GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) override;
|
|
||||||
virtual Result CleanDirectoryRecursivelyImpl(const FsPath &path) override;
|
|
||||||
virtual Result GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) override;
|
|
||||||
virtual Result QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) override;
|
|
||||||
};
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <switch.h>
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
|
|
||||||
#include "../utils.hpp"
|
|
||||||
#include "fs_file_storage.hpp"
|
|
||||||
|
|
||||||
Result FileStorage::UpdateSize() {
|
|
||||||
if (this->size == InvalidSize) {
|
|
||||||
return this->file->GetSize(&this->size);
|
|
||||||
}
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result FileStorage::Read(void *buffer, size_t size, u64 offset) {
|
|
||||||
u64 read_size;
|
|
||||||
|
|
||||||
if (size == 0) {
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
if (buffer == nullptr) {
|
|
||||||
return ResultFsNullptrArgument;
|
|
||||||
}
|
|
||||||
R_TRY(this->UpdateSize());
|
|
||||||
if (!IStorage::IsRangeValid(offset, size, this->size)) {
|
|
||||||
return ResultFsOutOfRange;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Nintendo doesn't do check output read size, but we will for safety. */
|
|
||||||
R_TRY(this->file->Read(&read_size, offset, buffer, size));
|
|
||||||
if (read_size != size && read_size) {
|
|
||||||
return this->Read(reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(buffer) + read_size), size - read_size, offset + read_size);
|
|
||||||
}
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result FileStorage::Write(void *buffer, size_t size, u64 offset) {
|
|
||||||
if (size == 0) {
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
if (buffer == nullptr) {
|
|
||||||
return ResultFsNullptrArgument;
|
|
||||||
}
|
|
||||||
R_TRY(this->UpdateSize());
|
|
||||||
if (!IStorage::IsRangeValid(offset, size, this->size)) {
|
|
||||||
return ResultFsOutOfRange;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this->file->Write(offset, buffer, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result FileStorage::Flush() {
|
|
||||||
return this->file->Flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result FileStorage::GetSize(u64 *out_size) {
|
|
||||||
R_TRY(this->UpdateSize());
|
|
||||||
*out_size = this->size;
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result FileStorage::SetSize(u64 size) {
|
|
||||||
this->size = InvalidSize;
|
|
||||||
return this->file->SetSize(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result FileStorage::OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) {
|
|
||||||
switch (operation_type) {
|
|
||||||
case FsOperationId_InvalidateCache:
|
|
||||||
case FsOperationId_QueryRange:
|
|
||||||
if (size == 0) {
|
|
||||||
if (operation_type == FsOperationId_QueryRange) {
|
|
||||||
if (out_range_info == nullptr) {
|
|
||||||
return ResultFsNullptrArgument;
|
|
||||||
}
|
|
||||||
/* N checks for size == sizeof(*out_range_info) here, but that's because their wrapper api is bad. */
|
|
||||||
std::memset(out_range_info, 0, sizeof(*out_range_info));
|
|
||||||
}
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
R_TRY(this->UpdateSize());
|
|
||||||
/* N checks for positivity + signed overflow on offset/size here, but we're using unsigned types... */
|
|
||||||
return this->file->OperateRange(operation_type, offset, size, out_range_info);
|
|
||||||
default:
|
|
||||||
return ResultFsUnsupportedOperation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
#include "fs_shim.h"
|
|
||||||
|
|
||||||
#include "../debug.hpp"
|
|
||||||
#include "fs_istorage.hpp"
|
|
||||||
#include "fs_ifile.hpp"
|
|
||||||
|
|
||||||
class FileStorage : public IStorage {
|
|
||||||
public:
|
|
||||||
static constexpr u64 InvalidSize = UINT64_MAX;
|
|
||||||
private:
|
|
||||||
std::shared_ptr<IFile> base_file;
|
|
||||||
IFile *file;
|
|
||||||
u64 size;
|
|
||||||
public:
|
|
||||||
FileStorage(IFile *f) : base_file(f) {
|
|
||||||
this->file = this->base_file.get();
|
|
||||||
this->size = InvalidSize;
|
|
||||||
};
|
|
||||||
FileStorage(std::shared_ptr<IFile> f) : base_file(f) {
|
|
||||||
this->file = this->base_file.get();
|
|
||||||
this->size = InvalidSize;
|
|
||||||
};
|
|
||||||
virtual ~FileStorage() {
|
|
||||||
/* ... */
|
|
||||||
};
|
|
||||||
protected:
|
|
||||||
Result UpdateSize();
|
|
||||||
public:
|
|
||||||
virtual Result Read(void *buffer, size_t size, u64 offset) override;
|
|
||||||
virtual Result Write(void *buffer, size_t size, u64 offset) override;
|
|
||||||
virtual Result Flush() override;
|
|
||||||
virtual Result GetSize(u64 *out_size) override;
|
|
||||||
virtual Result SetSize(u64 size) override;
|
|
||||||
virtual Result OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override;
|
|
||||||
};
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
|
|
||||||
class IDirectory {
|
|
||||||
public:
|
|
||||||
virtual ~IDirectory() {}
|
|
||||||
|
|
||||||
Result Read(uint64_t *out_count, FsDirectoryEntry *out_entries, uint64_t max_entries) {
|
|
||||||
if (out_count == nullptr) {
|
|
||||||
return ResultFsNullptrArgument;
|
|
||||||
}
|
|
||||||
if (max_entries == 0) {
|
|
||||||
*out_count = 0;
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
if (out_entries == nullptr) {
|
|
||||||
return ResultFsNullptrArgument;
|
|
||||||
}
|
|
||||||
return ReadImpl(out_count, out_entries, max_entries);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result GetEntryCount(uint64_t *count) {
|
|
||||||
if (count == nullptr) {
|
|
||||||
return ResultFsNullptrArgument;
|
|
||||||
}
|
|
||||||
return GetEntryCountImpl(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/* ...? */
|
|
||||||
private:
|
|
||||||
virtual Result ReadImpl(uint64_t *out_count, FsDirectoryEntry *out_entries, uint64_t max_entries) = 0;
|
|
||||||
virtual Result GetEntryCountImpl(uint64_t *count) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class IDirectoryInterface : public IServiceObject {
|
|
||||||
private:
|
|
||||||
enum class CommandId {
|
|
||||||
Read = 0,
|
|
||||||
GetEntryCount = 1,
|
|
||||||
};
|
|
||||||
private:
|
|
||||||
std::unique_ptr<IDirectory> base_dir;
|
|
||||||
public:
|
|
||||||
IDirectoryInterface(IDirectory *d) : base_dir(d) {
|
|
||||||
/* ... */
|
|
||||||
};
|
|
||||||
IDirectoryInterface(std::unique_ptr<IDirectory> d) : base_dir(std::move(d)) {
|
|
||||||
/* ... */
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
/* Actual command API. */
|
|
||||||
virtual Result Read(OutBuffer<FsDirectoryEntry> buffer, Out<u64> out_count) final {
|
|
||||||
return this->base_dir->Read(out_count.GetPointer(), buffer.buffer, buffer.num_elements);
|
|
||||||
};
|
|
||||||
virtual Result GetEntryCount(Out<u64> out_count) final {
|
|
||||||
return this->base_dir->GetEntryCount(out_count.GetPointer());
|
|
||||||
};
|
|
||||||
public:
|
|
||||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
|
||||||
/* 1.0.0- */
|
|
||||||
MAKE_SERVICE_COMMAND_META(IDirectoryInterface, Read),
|
|
||||||
MAKE_SERVICE_COMMAND_META(IDirectoryInterface, GetEntryCount),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class ProxyDirectory : public IDirectory {
|
|
||||||
private:
|
|
||||||
std::unique_ptr<FsDir> base_dir;
|
|
||||||
public:
|
|
||||||
ProxyDirectory(FsDir *d) : base_dir(d) {
|
|
||||||
/* ... */
|
|
||||||
}
|
|
||||||
|
|
||||||
ProxyDirectory(std::unique_ptr<FsDir> d) : base_dir(std::move(d)) {
|
|
||||||
/* ... */
|
|
||||||
}
|
|
||||||
|
|
||||||
ProxyDirectory(FsDir d) {
|
|
||||||
this->base_dir = std::make_unique<FsDir>(d);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~ProxyDirectory() {
|
|
||||||
fsDirClose(this->base_dir.get());
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
virtual Result ReadImpl(uint64_t *out_count, FsDirectoryEntry *out_entries, uint64_t max_entries) {
|
|
||||||
size_t count;
|
|
||||||
R_TRY(fsDirRead(this->base_dir.get(), 0, &count, max_entries, out_entries));
|
|
||||||
|
|
||||||
*out_count = count;
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
virtual Result GetEntryCountImpl(uint64_t *count) {
|
|
||||||
return fsDirGetEntryCount(this->base_dir.get(), count);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,195 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
|
|
||||||
#include "fs_shim.h"
|
|
||||||
|
|
||||||
class IFile {
|
|
||||||
public:
|
|
||||||
virtual ~IFile() {}
|
|
||||||
|
|
||||||
Result Read(uint64_t *out, uint64_t offset, void *buffer, uint64_t size, uint32_t flags) {
|
|
||||||
(void)(flags);
|
|
||||||
if (out == nullptr) {
|
|
||||||
return ResultFsNullptrArgument;
|
|
||||||
}
|
|
||||||
if (size == 0) {
|
|
||||||
*out = 0;
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
if (buffer == nullptr) {
|
|
||||||
return ResultFsNullptrArgument;
|
|
||||||
}
|
|
||||||
return ReadImpl(out, offset, buffer, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result Read(uint64_t *out, uint64_t offset, void *buffer, uint64_t size) {
|
|
||||||
return Read(out, offset, buffer, size, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result GetSize(uint64_t *out) {
|
|
||||||
if (out == nullptr) {
|
|
||||||
return ResultFsNullptrArgument;
|
|
||||||
}
|
|
||||||
return GetSizeImpl(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result Flush() {
|
|
||||||
return FlushImpl();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result Write(uint64_t offset, void *buffer, uint64_t size, uint32_t flags) {
|
|
||||||
if (size == 0) {
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
if (buffer == nullptr) {
|
|
||||||
return ResultFsNullptrArgument;
|
|
||||||
}
|
|
||||||
return WriteImpl(offset, buffer, size, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result Write(uint64_t offset, void *buffer, uint64_t size, bool flush = false) {
|
|
||||||
if (size == 0) {
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
if (buffer == nullptr) {
|
|
||||||
return ResultFsNullptrArgument;
|
|
||||||
}
|
|
||||||
return WriteImpl(offset, buffer, size, flush);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result SetSize(uint64_t size) {
|
|
||||||
return SetSizeImpl(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) {
|
|
||||||
if (operation_type == FsOperationId_QueryRange) {
|
|
||||||
return OperateRangeImpl(operation_type, offset, size, out_range_info);
|
|
||||||
}
|
|
||||||
return ResultFsUnsupportedOperation;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/* ...? */
|
|
||||||
private:
|
|
||||||
virtual Result ReadImpl(u64 *out, u64 offset, void *buffer, u64 size) = 0;
|
|
||||||
virtual Result GetSizeImpl(u64 *out) = 0;
|
|
||||||
virtual Result FlushImpl() = 0;
|
|
||||||
virtual Result WriteImpl(u64 offset, void *buffer, u64 size, u32 option) = 0;
|
|
||||||
virtual Result SetSizeImpl(u64 size) = 0;
|
|
||||||
virtual Result OperateRangeImpl(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class IFileInterface : public IServiceObject {
|
|
||||||
private:
|
|
||||||
enum class CommandId {
|
|
||||||
Read = 0,
|
|
||||||
Write = 1,
|
|
||||||
Flush = 2,
|
|
||||||
SetSize = 3,
|
|
||||||
GetSize = 4,
|
|
||||||
OperateRange = 5,
|
|
||||||
};
|
|
||||||
private:
|
|
||||||
std::unique_ptr<IFile> base_file;
|
|
||||||
public:
|
|
||||||
IFileInterface(IFile *f) : base_file(f) {
|
|
||||||
/* ... */
|
|
||||||
};
|
|
||||||
IFileInterface(std::unique_ptr<IFile> f) : base_file(std::move(f)) {
|
|
||||||
/* ... */
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
/* Actual command API. */
|
|
||||||
virtual Result Read(OutBuffer<u8, BufferType_Type1> buffer, Out<u64> out_read, u64 offset, u64 size, u32 read_flags) final {
|
|
||||||
return this->base_file->Read(out_read.GetPointer(), offset, buffer.buffer, std::min(buffer.num_elements, size), read_flags);
|
|
||||||
};
|
|
||||||
virtual Result Write(InBuffer<u8, BufferType_Type1> buffer, u64 offset, u64 size, u32 write_flags) final {
|
|
||||||
return this->base_file->Write(offset, buffer.buffer, std::min(buffer.num_elements, size), write_flags);
|
|
||||||
};
|
|
||||||
virtual Result Flush() final {
|
|
||||||
return this->base_file->Flush();
|
|
||||||
};
|
|
||||||
virtual Result SetSize(u64 size) final {
|
|
||||||
return this->base_file->SetSize(size);
|
|
||||||
};
|
|
||||||
virtual Result GetSize(Out<u64> size) final {
|
|
||||||
return this->base_file->GetSize(size.GetPointer());
|
|
||||||
};
|
|
||||||
virtual Result OperateRange(Out<FsRangeInfo> range_info, u32 operation_type, u64 offset, u64 size) final {
|
|
||||||
return this->base_file->OperateRange(static_cast<FsOperationId>(operation_type), offset, size, range_info.GetPointer());
|
|
||||||
};
|
|
||||||
public:
|
|
||||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
|
||||||
/* 1.0.0- */
|
|
||||||
MAKE_SERVICE_COMMAND_META(IFileInterface, Read),
|
|
||||||
MAKE_SERVICE_COMMAND_META(IFileInterface, Write),
|
|
||||||
MAKE_SERVICE_COMMAND_META(IFileInterface, Flush),
|
|
||||||
MAKE_SERVICE_COMMAND_META(IFileInterface, SetSize),
|
|
||||||
MAKE_SERVICE_COMMAND_META(IFileInterface, GetSize),
|
|
||||||
|
|
||||||
/* 4.0.0- */
|
|
||||||
MAKE_SERVICE_COMMAND_META(IFileInterface, OperateRange, FirmwareVersion_400),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class ProxyFile : public IFile {
|
|
||||||
private:
|
|
||||||
std::unique_ptr<FsFile> base_file;
|
|
||||||
public:
|
|
||||||
ProxyFile(FsFile *f) : base_file(f) {
|
|
||||||
/* ... */
|
|
||||||
}
|
|
||||||
|
|
||||||
ProxyFile(std::unique_ptr<FsFile> f) : base_file(std::move(f)) {
|
|
||||||
/* ... */
|
|
||||||
}
|
|
||||||
|
|
||||||
ProxyFile(FsFile f) {
|
|
||||||
this->base_file = std::make_unique<FsFile>(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~ProxyFile() {
|
|
||||||
fsFileClose(this->base_file.get());
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
virtual Result ReadImpl(u64 *out, u64 offset, void *buffer, u64 size) override {
|
|
||||||
size_t out_sz;
|
|
||||||
R_TRY(fsFileRead(this->base_file.get(), offset, buffer, size, FS_READOPTION_NONE, &out_sz));
|
|
||||||
|
|
||||||
*out = out_sz;
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
virtual Result GetSizeImpl(u64 *out) override {
|
|
||||||
return fsFileGetSize(this->base_file.get(), out);
|
|
||||||
}
|
|
||||||
virtual Result FlushImpl() override {
|
|
||||||
return fsFileFlush(this->base_file.get());
|
|
||||||
}
|
|
||||||
virtual Result WriteImpl(u64 offset, void *buffer, u64 size, u32 option) override {
|
|
||||||
return fsFileWrite(this->base_file.get(), offset, buffer, size, option);
|
|
||||||
}
|
|
||||||
virtual Result SetSizeImpl(u64 size) override {
|
|
||||||
return fsFileSetSize(this->base_file.get(), size);
|
|
||||||
}
|
|
||||||
virtual Result OperateRangeImpl(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override {
|
|
||||||
return fsFileOperateRange(this->base_file.get(), operation_type, offset, size, out_range_info);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,463 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
|
|
||||||
#include "../utils.hpp"
|
|
||||||
|
|
||||||
#include "fs_filesystem_types.hpp"
|
|
||||||
#include "fs_path_utils.hpp"
|
|
||||||
|
|
||||||
#include "fs_ifile.hpp"
|
|
||||||
#include "fs_idirectory.hpp"
|
|
||||||
|
|
||||||
class IFile;
|
|
||||||
class IDirectory;
|
|
||||||
|
|
||||||
class IFileSystem {
|
|
||||||
public:
|
|
||||||
virtual ~IFileSystem() {}
|
|
||||||
|
|
||||||
Result CreateFile(const FsPath &path, uint64_t size, int flags) {
|
|
||||||
return CreateFileImpl(path, size, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result CreateFile(const FsPath &path, uint64_t size) {
|
|
||||||
return CreateFileImpl(path, size, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DeleteFile(const FsPath &path) {
|
|
||||||
return DeleteFileImpl(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result CreateDirectory(const FsPath &path) {
|
|
||||||
return CreateDirectoryImpl(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DeleteDirectory(const FsPath &path) {
|
|
||||||
return DeleteDirectoryImpl(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DeleteDirectoryRecursively(const FsPath &path) {
|
|
||||||
return DeleteDirectoryRecursivelyImpl(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RenameFile(const FsPath &old_path, const FsPath &new_path) {
|
|
||||||
return RenameFileImpl(old_path, new_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RenameDirectory(const FsPath &old_path, const FsPath &new_path) {
|
|
||||||
return RenameDirectoryImpl(old_path, new_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result GetEntryType(DirectoryEntryType *out, const FsPath &path) {
|
|
||||||
if (out == nullptr) {
|
|
||||||
return ResultFsNullptrArgument;
|
|
||||||
}
|
|
||||||
return GetEntryTypeImpl(out, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result OpenFile(std::unique_ptr<IFile> &out_file, const FsPath &path, OpenMode mode) {
|
|
||||||
if (!(mode & OpenMode_ReadWrite)) {
|
|
||||||
return ResultFsInvalidArgument;
|
|
||||||
}
|
|
||||||
if (mode & ~OpenMode_All) {
|
|
||||||
return ResultFsInvalidArgument;
|
|
||||||
}
|
|
||||||
return OpenFileImpl(out_file, path, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result OpenDirectory(std::unique_ptr<IDirectory> &out_dir, const FsPath &path, DirectoryOpenMode mode) {
|
|
||||||
if (!(mode & DirectoryOpenMode_All)) {
|
|
||||||
return ResultFsInvalidArgument;
|
|
||||||
}
|
|
||||||
if (mode & ~DirectoryOpenMode_All) {
|
|
||||||
return ResultFsInvalidArgument;
|
|
||||||
}
|
|
||||||
return OpenDirectoryImpl(out_dir, path, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result Commit() {
|
|
||||||
return CommitImpl();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result GetFreeSpaceSize(uint64_t *out, const FsPath &path) {
|
|
||||||
if (out == nullptr) {
|
|
||||||
return ResultFsNullptrArgument;
|
|
||||||
}
|
|
||||||
return GetFreeSpaceSizeImpl(out, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result GetTotalSpaceSize(uint64_t *out, const FsPath &path) {
|
|
||||||
if (out == nullptr) {
|
|
||||||
return ResultFsNullptrArgument;
|
|
||||||
}
|
|
||||||
return GetTotalSpaceSizeImpl(out, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result CleanDirectoryRecursively(const FsPath &path) {
|
|
||||||
return CleanDirectoryRecursivelyImpl(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result GetFileTimeStampRaw(FsTimeStampRaw *out, const FsPath &path) {
|
|
||||||
if (out == nullptr) {
|
|
||||||
return ResultFsNullptrArgument;
|
|
||||||
}
|
|
||||||
return GetFileTimeStampRawImpl(out, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result QueryEntry(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) {
|
|
||||||
return QueryEntryImpl(out, out_size, in, in_size, query, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/* ...? */
|
|
||||||
private:
|
|
||||||
virtual Result CreateFileImpl(const FsPath &path, uint64_t size, int flags) = 0;
|
|
||||||
virtual Result DeleteFileImpl(const FsPath &path) = 0;
|
|
||||||
virtual Result CreateDirectoryImpl(const FsPath &path) = 0;
|
|
||||||
virtual Result DeleteDirectoryImpl(const FsPath &path) = 0;
|
|
||||||
virtual Result DeleteDirectoryRecursivelyImpl(const FsPath &path) = 0;
|
|
||||||
virtual Result RenameFileImpl(const FsPath &old_path, const FsPath &new_path) = 0;
|
|
||||||
virtual Result RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) = 0;
|
|
||||||
virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) = 0;
|
|
||||||
virtual Result OpenFileImpl(std::unique_ptr<IFile> &out_file, const FsPath &path, OpenMode mode) = 0;
|
|
||||||
virtual Result OpenDirectoryImpl(std::unique_ptr<IDirectory> &out_dir, const FsPath &path, DirectoryOpenMode mode) = 0;
|
|
||||||
virtual Result CommitImpl() = 0;
|
|
||||||
|
|
||||||
virtual Result GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) {
|
|
||||||
(void)(out);
|
|
||||||
(void)(path);
|
|
||||||
return ResultFsNotImplemented;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) {
|
|
||||||
(void)(out);
|
|
||||||
(void)(path);
|
|
||||||
return ResultFsNotImplemented;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result CleanDirectoryRecursivelyImpl(const FsPath &path) = 0;
|
|
||||||
|
|
||||||
virtual Result GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) {
|
|
||||||
(void)(out);
|
|
||||||
(void)(path);
|
|
||||||
return ResultFsNotImplemented;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) {
|
|
||||||
(void)(out);
|
|
||||||
(void)(out_size);
|
|
||||||
(void)(in);
|
|
||||||
(void)(in_size);
|
|
||||||
(void)(query);
|
|
||||||
(void)(path);
|
|
||||||
return ResultFsNotImplemented;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class IFileSystemInterface : public IServiceObject {
|
|
||||||
private:
|
|
||||||
enum class CommandId {
|
|
||||||
/* 1.0.0+ */
|
|
||||||
CreateFile = 0,
|
|
||||||
DeleteFile = 1,
|
|
||||||
CreateDirectory = 2,
|
|
||||||
DeleteDirectory = 3,
|
|
||||||
DeleteDirectoryRecursively = 4,
|
|
||||||
RenameFile = 5,
|
|
||||||
RenameDirectory = 6,
|
|
||||||
GetEntryType = 7,
|
|
||||||
OpenFile = 8,
|
|
||||||
OpenDirectory = 9,
|
|
||||||
Commit = 10,
|
|
||||||
GetFreeSpaceSize = 11,
|
|
||||||
GetTotalSpaceSize = 12,
|
|
||||||
|
|
||||||
/* 3.0.0+ */
|
|
||||||
CleanDirectoryRecursively = 13,
|
|
||||||
GetFileTimeStampRaw = 14,
|
|
||||||
|
|
||||||
/* 4.0.0+ */
|
|
||||||
QueryEntry = 15,
|
|
||||||
};
|
|
||||||
private:
|
|
||||||
std::unique_ptr<IFileSystem> unique_fs;
|
|
||||||
std::shared_ptr<IFileSystem> shared_fs;
|
|
||||||
IFileSystem *base_fs;
|
|
||||||
public:
|
|
||||||
IFileSystemInterface(IFileSystem *fs) : unique_fs(fs) {
|
|
||||||
this->base_fs = this->unique_fs.get();
|
|
||||||
};
|
|
||||||
IFileSystemInterface(std::unique_ptr<IFileSystem> fs) : unique_fs(std::move(fs)) {
|
|
||||||
this->base_fs = this->unique_fs.get();
|
|
||||||
};
|
|
||||||
IFileSystemInterface(std::shared_ptr<IFileSystem> fs) : shared_fs(fs) {
|
|
||||||
this->base_fs = this->shared_fs.get();
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
/* Actual command API. */
|
|
||||||
virtual Result CreateFile(InPointer<char> in_path, uint64_t size, int flags) final {
|
|
||||||
FsPath path;
|
|
||||||
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
|
|
||||||
|
|
||||||
return this->base_fs->CreateFile(path, size, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result DeleteFile(InPointer<char> in_path) final {
|
|
||||||
FsPath path;
|
|
||||||
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
|
|
||||||
|
|
||||||
return this->base_fs->DeleteFile(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result CreateDirectory(InPointer<char> in_path) final {
|
|
||||||
FsPath path;
|
|
||||||
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
|
|
||||||
|
|
||||||
return this->base_fs->CreateDirectory(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result DeleteDirectory(InPointer<char> in_path) final {
|
|
||||||
FsPath path;
|
|
||||||
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
|
|
||||||
|
|
||||||
return this->base_fs->DeleteDirectory(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result DeleteDirectoryRecursively(InPointer<char> in_path) final {
|
|
||||||
FsPath path;
|
|
||||||
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
|
|
||||||
|
|
||||||
return this->base_fs->DeleteDirectoryRecursively(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result RenameFile(InPointer<char> in_old_path, InPointer<char> in_new_path) final {
|
|
||||||
FsPath old_path;
|
|
||||||
FsPath new_path;
|
|
||||||
R_TRY(FsPathUtils::ConvertPathForServiceObject(&old_path, in_old_path.pointer));
|
|
||||||
R_TRY(FsPathUtils::ConvertPathForServiceObject(&new_path, in_new_path.pointer));
|
|
||||||
|
|
||||||
return this->base_fs->RenameFile(old_path, new_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result RenameDirectory(InPointer<char> in_old_path, InPointer<char> in_new_path) final {
|
|
||||||
FsPath old_path;
|
|
||||||
FsPath new_path;
|
|
||||||
R_TRY(FsPathUtils::ConvertPathForServiceObject(&old_path, in_old_path.pointer));
|
|
||||||
R_TRY(FsPathUtils::ConvertPathForServiceObject(&new_path, in_new_path.pointer));
|
|
||||||
|
|
||||||
return this->base_fs->RenameDirectory(old_path, new_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
virtual Result GetEntryType(Out<u32> out_type, InPointer<char> in_path) final {
|
|
||||||
FsPath path;
|
|
||||||
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
|
|
||||||
|
|
||||||
DirectoryEntryType type;
|
|
||||||
R_TRY(this->base_fs->GetEntryType(&type, path));
|
|
||||||
|
|
||||||
out_type.SetValue(type);
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result OpenFile(Out<std::shared_ptr<IFileInterface>> out_intf, InPointer<char> in_path, uint32_t mode) final {
|
|
||||||
FsPath path;
|
|
||||||
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
|
|
||||||
|
|
||||||
std::unique_ptr<IFile> out_file;
|
|
||||||
R_TRY(this->base_fs->OpenFile(out_file, path, static_cast<OpenMode>(mode)));
|
|
||||||
|
|
||||||
out_intf.SetValue(std::make_shared<IFileInterface>(std::move(out_file)));
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result OpenDirectory(Out<std::shared_ptr<IDirectoryInterface>> out_intf, InPointer<char> in_path, uint32_t mode) final {
|
|
||||||
FsPath path;
|
|
||||||
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
|
|
||||||
|
|
||||||
std::unique_ptr<IDirectory> out_dir;
|
|
||||||
R_TRY(this->base_fs->OpenDirectory(out_dir, path, static_cast<DirectoryOpenMode>(mode)));
|
|
||||||
|
|
||||||
out_intf.SetValue(std::make_shared<IDirectoryInterface>(std::move(out_dir)));
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result Commit() final {
|
|
||||||
return this->base_fs->Commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result GetFreeSpaceSize(Out<uint64_t> out_size, InPointer<char> in_path) final {
|
|
||||||
FsPath path;
|
|
||||||
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
|
|
||||||
|
|
||||||
return this->base_fs->GetFreeSpaceSize(out_size.GetPointer(), path);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result GetTotalSpaceSize(Out<uint64_t> out_size, InPointer<char> in_path) final {
|
|
||||||
FsPath path;
|
|
||||||
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
|
|
||||||
|
|
||||||
return this->base_fs->GetTotalSpaceSize(out_size.GetPointer(), path);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result CleanDirectoryRecursively(InPointer<char> in_path) final {
|
|
||||||
FsPath path;
|
|
||||||
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
|
|
||||||
|
|
||||||
return this->base_fs->CleanDirectoryRecursively(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result GetFileTimeStampRaw(Out<FsTimeStampRaw> out_timestamp, InPointer<char> in_path) final {
|
|
||||||
FsPath path;
|
|
||||||
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
|
|
||||||
|
|
||||||
return this->base_fs->GetFileTimeStampRaw(out_timestamp.GetPointer(), path);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result QueryEntry(OutBuffer<char, BufferType_Type1> out_buffer, InBuffer<char, BufferType_Type1> in_buffer, int query, InPointer<char> in_path) final {
|
|
||||||
FsPath path;
|
|
||||||
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
|
|
||||||
|
|
||||||
return this->base_fs->QueryEntry(out_buffer.buffer, out_buffer.num_elements, in_buffer.buffer, in_buffer.num_elements, query, path);
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
|
||||||
/* 1.0.0- */
|
|
||||||
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, CreateFile),
|
|
||||||
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, DeleteFile),
|
|
||||||
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, CreateDirectory),
|
|
||||||
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, DeleteDirectory),
|
|
||||||
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, DeleteDirectoryRecursively),
|
|
||||||
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, RenameFile),
|
|
||||||
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, RenameDirectory),
|
|
||||||
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, GetEntryType),
|
|
||||||
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, OpenFile),
|
|
||||||
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, OpenDirectory),
|
|
||||||
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, Commit),
|
|
||||||
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, GetFreeSpaceSize),
|
|
||||||
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, GetTotalSpaceSize),
|
|
||||||
|
|
||||||
/* 3.0.0- */
|
|
||||||
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, CleanDirectoryRecursively, FirmwareVersion_300),
|
|
||||||
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, GetFileTimeStampRaw, FirmwareVersion_300),
|
|
||||||
|
|
||||||
/* 4.0.0- */
|
|
||||||
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, QueryEntry, FirmwareVersion_400),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class ProxyFileSystem : public IFileSystem {
|
|
||||||
private:
|
|
||||||
std::unique_ptr<FsFileSystem> base_fs;
|
|
||||||
public:
|
|
||||||
ProxyFileSystem(FsFileSystem *fs) : base_fs(fs) {
|
|
||||||
/* ... */
|
|
||||||
}
|
|
||||||
|
|
||||||
ProxyFileSystem(std::unique_ptr<FsFileSystem> fs) : base_fs(std::move(fs)) {
|
|
||||||
/* ... */
|
|
||||||
}
|
|
||||||
|
|
||||||
ProxyFileSystem(FsFileSystem fs) {
|
|
||||||
this->base_fs = std::make_unique<FsFileSystem>(fs);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~ProxyFileSystem() {
|
|
||||||
fsFsClose(this->base_fs.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual Result CreateFileImpl(const FsPath &path, uint64_t size, int flags) {
|
|
||||||
return fsFsCreateFile(this->base_fs.get(), path.str, size, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result DeleteFileImpl(const FsPath &path) {
|
|
||||||
return fsFsDeleteFile(this->base_fs.get(), path.str);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result CreateDirectoryImpl(const FsPath &path) {
|
|
||||||
return fsFsCreateDirectory(this->base_fs.get(), path.str);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result DeleteDirectoryImpl(const FsPath &path) {
|
|
||||||
return fsFsDeleteDirectory(this->base_fs.get(), path.str);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result DeleteDirectoryRecursivelyImpl(const FsPath &path) {
|
|
||||||
return fsFsDeleteDirectoryRecursively(this->base_fs.get(), path.str);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result RenameFileImpl(const FsPath &old_path, const FsPath &new_path) {
|
|
||||||
return fsFsRenameFile(this->base_fs.get(), old_path.str, new_path.str);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) {
|
|
||||||
return fsFsRenameDirectory(this->base_fs.get(), old_path.str, new_path.str);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) {
|
|
||||||
FsEntryType type;
|
|
||||||
R_TRY(fsFsGetEntryType(this->base_fs.get(), path.str, &type));
|
|
||||||
|
|
||||||
*out = static_cast<DirectoryEntryType>(static_cast<u32>(type));
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
virtual Result OpenFileImpl(std::unique_ptr<IFile> &out_file, const FsPath &path, OpenMode mode) {
|
|
||||||
FsFile f;
|
|
||||||
R_TRY(fsFsOpenFile(this->base_fs.get(), path.str, static_cast<int>(mode), &f));
|
|
||||||
|
|
||||||
out_file = std::make_unique<ProxyFile>(f);
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result OpenDirectoryImpl(std::unique_ptr<IDirectory> &out_dir, const FsPath &path, DirectoryOpenMode mode) {
|
|
||||||
FsDir d;
|
|
||||||
R_TRY(fsFsOpenDirectory(this->base_fs.get(), path.str, static_cast<int>(mode), &d));
|
|
||||||
|
|
||||||
out_dir = std::make_unique<ProxyDirectory>(d);
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result CommitImpl() {
|
|
||||||
return fsFsCommit(this->base_fs.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) {
|
|
||||||
return fsFsGetFreeSpace(this->base_fs.get(), path.str, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) {
|
|
||||||
return fsFsGetTotalSpace(this->base_fs.get(), path.str, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result CleanDirectoryRecursivelyImpl(const FsPath &path) {
|
|
||||||
return fsFsCleanDirectoryRecursively(this->base_fs.get(), path.str);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) {
|
|
||||||
return fsFsGetFileTimeStampRaw(this->base_fs.get(), path.str, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) {
|
|
||||||
return fsFsQueryEntry(this->base_fs.get(), out, out_size, in, in_size, path.str,static_cast<FsFileSystemQueryType>(query));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,175 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
#include "fs_shim.h"
|
|
||||||
|
|
||||||
#include "../debug.hpp"
|
|
||||||
|
|
||||||
class IStorage {
|
|
||||||
public:
|
|
||||||
virtual ~IStorage();
|
|
||||||
|
|
||||||
virtual Result Read(void *buffer, size_t size, u64 offset) = 0;
|
|
||||||
virtual Result Write(void *buffer, size_t size, u64 offset) = 0;
|
|
||||||
virtual Result Flush() = 0;
|
|
||||||
virtual Result SetSize(u64 size) = 0;
|
|
||||||
virtual Result GetSize(u64 *out_size) = 0;
|
|
||||||
virtual Result OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) = 0;
|
|
||||||
|
|
||||||
static inline bool IsRangeValid(uint64_t offset, uint64_t size, uint64_t total_size) {
|
|
||||||
return size <= total_size && offset <= total_size - size;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class IStorageInterface : public IServiceObject {
|
|
||||||
private:
|
|
||||||
enum class CommandId {
|
|
||||||
Read = 0,
|
|
||||||
Write = 1,
|
|
||||||
Flush = 2,
|
|
||||||
SetSize = 3,
|
|
||||||
GetSize = 4,
|
|
||||||
OperateRange = 5,
|
|
||||||
};
|
|
||||||
private:
|
|
||||||
IStorage *base_storage;
|
|
||||||
public:
|
|
||||||
IStorageInterface(IStorage *s) : base_storage(s) {
|
|
||||||
/* ... */
|
|
||||||
};
|
|
||||||
|
|
||||||
~IStorageInterface() {
|
|
||||||
delete base_storage;
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
/* Actual command API. */
|
|
||||||
virtual Result Read(OutBuffer<u8, BufferType_Type1> buffer, u64 offset, u64 size) final {
|
|
||||||
return this->base_storage->Read(buffer.buffer, std::min(buffer.num_elements, size), offset);
|
|
||||||
};
|
|
||||||
virtual Result Write(InBuffer<u8, BufferType_Type1> buffer, u64 offset, u64 size) final {
|
|
||||||
return this->base_storage->Write(buffer.buffer, std::min(buffer.num_elements, size), offset);
|
|
||||||
};
|
|
||||||
virtual Result Flush() final {
|
|
||||||
return this->base_storage->Flush();
|
|
||||||
};
|
|
||||||
virtual Result SetSize(u64 size) final {
|
|
||||||
return this->base_storage->SetSize(size);
|
|
||||||
};
|
|
||||||
virtual Result GetSize(Out<u64> size) final {
|
|
||||||
return this->base_storage->GetSize(size.GetPointer());
|
|
||||||
};
|
|
||||||
virtual Result OperateRange(Out<FsRangeInfo> range_info, u32 operation_type, u64 offset, u64 size) final {
|
|
||||||
return this->base_storage->OperateRange(static_cast<FsOperationId>(operation_type), offset, size, range_info.GetPointer());
|
|
||||||
};
|
|
||||||
public:
|
|
||||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
|
||||||
/* 1.0.0- */
|
|
||||||
MAKE_SERVICE_COMMAND_META(IStorageInterface, Read),
|
|
||||||
MAKE_SERVICE_COMMAND_META(IStorageInterface, Write),
|
|
||||||
MAKE_SERVICE_COMMAND_META(IStorageInterface, Flush),
|
|
||||||
MAKE_SERVICE_COMMAND_META(IStorageInterface, SetSize),
|
|
||||||
MAKE_SERVICE_COMMAND_META(IStorageInterface, GetSize),
|
|
||||||
|
|
||||||
/* 4.0.0- */
|
|
||||||
MAKE_SERVICE_COMMAND_META(IStorageInterface, OperateRange, FirmwareVersion_400),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class IROStorage : public IStorage {
|
|
||||||
public:
|
|
||||||
virtual Result Read(void *buffer, size_t size, u64 offset) = 0;
|
|
||||||
virtual Result Write(void *buffer, size_t size, u64 offset) final {
|
|
||||||
(void)(buffer);
|
|
||||||
(void)(offset);
|
|
||||||
(void)(size);
|
|
||||||
return ResultFsUnsupportedOperation;
|
|
||||||
};
|
|
||||||
virtual Result Flush() final {
|
|
||||||
return ResultSuccess;
|
|
||||||
};
|
|
||||||
virtual Result SetSize(u64 size) final {
|
|
||||||
(void)(size);
|
|
||||||
return ResultFsUnsupportedOperation;
|
|
||||||
};
|
|
||||||
virtual Result GetSize(u64 *out_size) = 0;
|
|
||||||
virtual Result OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class ProxyStorage : public IStorage {
|
|
||||||
private:
|
|
||||||
FsStorage *base_storage;
|
|
||||||
public:
|
|
||||||
ProxyStorage(FsStorage *s) : base_storage(s) {
|
|
||||||
/* ... */
|
|
||||||
};
|
|
||||||
ProxyStorage(FsStorage s) {
|
|
||||||
this->base_storage = new FsStorage(s);
|
|
||||||
};
|
|
||||||
virtual ~ProxyStorage() {
|
|
||||||
fsStorageClose(base_storage);
|
|
||||||
delete base_storage;
|
|
||||||
};
|
|
||||||
public:
|
|
||||||
virtual Result Read(void *buffer, size_t size, u64 offset) override {
|
|
||||||
return fsStorageRead(this->base_storage, offset, buffer, size);
|
|
||||||
};
|
|
||||||
virtual Result Write(void *buffer, size_t size, u64 offset) override {
|
|
||||||
return fsStorageWrite(this->base_storage, offset, buffer, size);
|
|
||||||
};
|
|
||||||
virtual Result Flush() override {
|
|
||||||
return fsStorageFlush(this->base_storage);
|
|
||||||
};
|
|
||||||
virtual Result GetSize(u64 *out_size) override {
|
|
||||||
return fsStorageGetSize(this->base_storage, out_size);
|
|
||||||
};
|
|
||||||
virtual Result SetSize(u64 size) override {
|
|
||||||
return fsStorageSetSize(this->base_storage, size);
|
|
||||||
};
|
|
||||||
virtual Result OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override {
|
|
||||||
return fsStorageOperateRange(this->base_storage, operation_type, offset, size, out_range_info);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class ReadOnlyStorageAdapter : public IROStorage {
|
|
||||||
private:
|
|
||||||
std::shared_ptr<IStorage> base_storage;
|
|
||||||
IStorage *storage;
|
|
||||||
public:
|
|
||||||
ReadOnlyStorageAdapter(IStorage *s) : base_storage(s) {
|
|
||||||
this->storage = this->base_storage.get();
|
|
||||||
}
|
|
||||||
ReadOnlyStorageAdapter(std::shared_ptr<IStorage> s) : base_storage(s) {
|
|
||||||
this->storage = this->base_storage.get();
|
|
||||||
}
|
|
||||||
virtual ~ReadOnlyStorageAdapter() {
|
|
||||||
/* ... */
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
virtual Result Read(void *buffer, size_t size, u64 offset) override {
|
|
||||||
return this->base_storage->Read(buffer, size, offset);
|
|
||||||
};
|
|
||||||
virtual Result GetSize(u64 *out_size) override {
|
|
||||||
return this->base_storage->GetSize(out_size);
|
|
||||||
};
|
|
||||||
virtual Result OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override {
|
|
||||||
return this->base_storage->OperateRange(operation_type, offset, size, out_range_info);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
311
stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp
Normal file
311
stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope 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/>.
|
||||||
|
*/
|
||||||
|
#include "../amsmitm_fs_utils.hpp"
|
||||||
|
#include "fs_shim.h"
|
||||||
|
#include "fs_mitm_service.hpp"
|
||||||
|
#include "fsmitm_boot0storage.hpp"
|
||||||
|
#include "fsmitm_layered_romfs_storage.hpp"
|
||||||
|
#include "fsmitm_save_utils.hpp"
|
||||||
|
|
||||||
|
namespace ams::mitm::fs {
|
||||||
|
|
||||||
|
using namespace ams::fs;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr const char AtmosphereHblWebContentDir[] = "/atmosphere/hbl_html/";
|
||||||
|
|
||||||
|
os::Mutex g_storage_cache_lock;
|
||||||
|
std::unordered_map<u64, std::weak_ptr<IStorageInterface>> g_storage_cache;
|
||||||
|
|
||||||
|
std::shared_ptr<IStorageInterface> GetStorageCacheEntry(ncm::ProgramId program_id) {
|
||||||
|
std::scoped_lock lk(g_storage_cache_lock);
|
||||||
|
|
||||||
|
auto it = g_storage_cache.find(static_cast<u64>(program_id));
|
||||||
|
if (it == g_storage_cache.end()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return it->second.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetStorageCacheEntry(ncm::ProgramId program_id, std::shared_ptr<IStorageInterface> *new_intf) {
|
||||||
|
std::scoped_lock lk(g_storage_cache_lock);
|
||||||
|
|
||||||
|
auto it = g_storage_cache.find(static_cast<u64>(program_id));
|
||||||
|
if (it != g_storage_cache.end()) {
|
||||||
|
auto cur_intf = it->second.lock();
|
||||||
|
if (cur_intf != nullptr) {
|
||||||
|
*new_intf = cur_intf;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_storage_cache[static_cast<u64>(program_id)] = *new_intf;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetSettingsItemBooleanValue(const char *name, const char *key) {
|
||||||
|
u8 tmp = 0;
|
||||||
|
AMS_ASSERT(settings::fwdbg::GetSettingsItemValue(&tmp, sizeof(tmp), name, key) == sizeof(tmp));
|
||||||
|
return (tmp != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result OpenHblWebContentFileSystem(sf::Out<std::shared_ptr<IFileSystemInterface>> &out, ncm::ProgramId client_program_id, ncm::ProgramId program_id, FsFileSystemType filesystem_type) {
|
||||||
|
/* Verify eligibility. */
|
||||||
|
bool is_hbl;
|
||||||
|
R_UNLESS(ncm::IsWebAppletProgramId(client_program_id), sm::mitm::ResultShouldForwardToSession());
|
||||||
|
R_UNLESS(filesystem_type == FsFileSystemType_ContentManual, sm::mitm::ResultShouldForwardToSession());
|
||||||
|
R_UNLESS(R_SUCCEEDED(pm::info::IsHblProgramId(&is_hbl, program_id)), sm::mitm::ResultShouldForwardToSession());
|
||||||
|
R_UNLESS(is_hbl, sm::mitm::ResultShouldForwardToSession());
|
||||||
|
|
||||||
|
/* Hbl html directory must exist. */
|
||||||
|
{
|
||||||
|
FsDir d;
|
||||||
|
R_UNLESS(R_SUCCEEDED(mitm::fs::OpenSdDirectory(&d, AtmosphereHblWebContentDir, fs::OpenDirectoryMode_Directory)), sm::mitm::ResultShouldForwardToSession());
|
||||||
|
fsDirClose(&d);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open the SD card using fs.mitm's session. */
|
||||||
|
FsFileSystem sd_fs;
|
||||||
|
R_TRY(fsOpenSdCardFileSystem(&sd_fs));
|
||||||
|
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(&sd_fs.s)};
|
||||||
|
std::unique_ptr<fs::fsa::IFileSystem> sd_ifs = std::make_unique<fs::RemoteFileSystem>(sd_fs);
|
||||||
|
|
||||||
|
out.SetValue(std::make_shared<IFileSystemInterface>(std::make_shared<fssystem::SubDirectoryFileSystem>(std::move(sd_ifs), AtmosphereHblWebContentDir), false), target_object_id);
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FsMitmService::OpenFileSystemWithPatch(sf::Out<std::shared_ptr<IFileSystemInterface>> out, ncm::ProgramId program_id, u32 _filesystem_type) {
|
||||||
|
return OpenHblWebContentFileSystem(out, this->client_info.program_id, program_id, static_cast<FsFileSystemType>(_filesystem_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FsMitmService::OpenFileSystemWithId(sf::Out<std::shared_ptr<IFileSystemInterface>> out, const fssrv::sf::Path &path, ncm::ProgramId program_id, u32 _filesystem_type) {
|
||||||
|
return OpenHblWebContentFileSystem(out, this->client_info.program_id, program_id, static_cast<FsFileSystemType>(_filesystem_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FsMitmService::OpenSdCardFileSystem(sf::Out<std::shared_ptr<IFileSystemInterface>> out) {
|
||||||
|
/* We only care about redirecting this for NS/emummc. */
|
||||||
|
R_UNLESS(this->client_info.program_id == ncm::ProgramId::Ns, sm::mitm::ResultShouldForwardToSession());
|
||||||
|
R_UNLESS(emummc::IsActive(), sm::mitm::ResultShouldForwardToSession());
|
||||||
|
|
||||||
|
/* Create a new SD card filesystem. */
|
||||||
|
FsFileSystem sd_fs;
|
||||||
|
R_TRY(fsOpenSdCardFileSystemFwd(this->forward_service.get(), &sd_fs));
|
||||||
|
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(&sd_fs.s)};
|
||||||
|
|
||||||
|
/* Return output filesystem. */
|
||||||
|
std::shared_ptr<fs::fsa::IFileSystem> redir_fs = std::make_shared<fssystem::DirectoryRedirectionFileSystem>(std::make_shared<RemoteFileSystem>(sd_fs), "/Nintendo", emummc::GetNintendoDirPath());
|
||||||
|
out.SetValue(std::make_shared<IFileSystemInterface>(std::move(redir_fs), false), target_object_id);
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FsMitmService::OpenSaveDataFileSystem(sf::Out<std::shared_ptr<IFileSystemInterface>> out, u8 _space_id, const FsSaveDataAttribute &attribute) {
|
||||||
|
/* We only want to intercept saves for games, right now. */
|
||||||
|
const bool is_game_or_hbl = this->client_info.override_status.IsHbl() || ncm::IsApplicationProgramId(this->client_info.program_id);
|
||||||
|
R_UNLESS(is_game_or_hbl, sm::mitm::ResultShouldForwardToSession());
|
||||||
|
|
||||||
|
/* Only redirect if the appropriate system setting is set. */
|
||||||
|
R_UNLESS(GetSettingsItemBooleanValue("atmosphere", "fsmitm_redirect_saves_to_sd"), sm::mitm::ResultShouldForwardToSession());
|
||||||
|
|
||||||
|
/* Only redirect if the specific title being accessed has a redirect save flag. */
|
||||||
|
R_UNLESS(cfg::HasContentSpecificFlag(this->client_info.program_id, "redirect_save"), sm::mitm::ResultShouldForwardToSession());
|
||||||
|
|
||||||
|
/* Only redirect account savedata. */
|
||||||
|
R_UNLESS(attribute.save_data_type == FsSaveDataType_Account, sm::mitm::ResultShouldForwardToSession());
|
||||||
|
|
||||||
|
/* Get enum type for space id. */
|
||||||
|
auto space_id = static_cast<FsSaveDataSpaceId>(_space_id);
|
||||||
|
|
||||||
|
/* Verify we can open the save. */
|
||||||
|
FsFileSystem save_fs;
|
||||||
|
R_UNLESS(R_SUCCEEDED(fsOpenSaveDataFileSystemFwd(this->forward_service.get(), &save_fs, space_id, &attribute)), sm::mitm::ResultShouldForwardToSession());
|
||||||
|
std::unique_ptr<fs::fsa::IFileSystem> save_ifs = std::make_unique<fs::RemoteFileSystem>(save_fs);
|
||||||
|
|
||||||
|
/* Mount the SD card using fs.mitm's session. */
|
||||||
|
FsFileSystem sd_fs;
|
||||||
|
R_TRY(fsOpenSdCardFileSystem(&sd_fs));
|
||||||
|
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(&sd_fs.s)};
|
||||||
|
std::shared_ptr<fs::fsa::IFileSystem> sd_ifs = std::make_shared<fs::RemoteFileSystem>(sd_fs);
|
||||||
|
|
||||||
|
/* Verify that we can open the save directory, and that it exists. */
|
||||||
|
const ncm::ProgramId application_id = attribute.application_id == 0 ? this->client_info.program_id : ncm::ProgramId{attribute.application_id};
|
||||||
|
char save_dir_path[fs::EntryNameLengthMax + 1];
|
||||||
|
R_TRY(mitm::fs::SaveUtil::GetDirectorySaveDataPath(save_dir_path, sizeof(save_dir_path), application_id, space_id, attribute));
|
||||||
|
|
||||||
|
/* Check if this is the first time we're making the save. */
|
||||||
|
bool is_new_save = false;
|
||||||
|
{
|
||||||
|
fs::DirectoryEntryType ent;
|
||||||
|
R_TRY_CATCH(sd_ifs->GetEntryType(&ent, save_dir_path)) {
|
||||||
|
R_CATCH(fs::ResultPathNotFound) { is_new_save = true; }
|
||||||
|
R_CATCH_ALL() { /* ... */ }
|
||||||
|
} R_END_TRY_CATCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure the directory exists. */
|
||||||
|
R_TRY(fssystem::EnsureDirectoryExistsRecursively(sd_ifs.get(), save_dir_path));
|
||||||
|
|
||||||
|
/* Create directory savedata filesystem. */
|
||||||
|
std::unique_ptr<fs::fsa::IFileSystem> subdir_fs = std::make_unique<fssystem::SubDirectoryFileSystem>(sd_ifs, save_dir_path);
|
||||||
|
std::shared_ptr<fssystem::DirectorySaveDataFileSystem> dirsave_ifs = std::make_shared<fssystem::DirectorySaveDataFileSystem>(std::move(subdir_fs));
|
||||||
|
|
||||||
|
/* Ensure correct directory savedata filesystem state. */
|
||||||
|
R_TRY(dirsave_ifs->Initialize());
|
||||||
|
|
||||||
|
/* If it's the first time we're making the save, copy existing savedata over. */
|
||||||
|
if (is_new_save) {
|
||||||
|
/* TODO: Check error? */
|
||||||
|
dirsave_ifs->CopySaveFromFileSystem(save_ifs.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set output. */
|
||||||
|
out.SetValue(std::make_shared<IFileSystemInterface>(std::move(dirsave_ifs), false), target_object_id);
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FsMitmService::OpenBisStorage(sf::Out<std::shared_ptr<IStorageInterface>> out, u32 _bis_partition_id) {
|
||||||
|
const ::FsBisPartitionId bis_partition_id = static_cast<::FsBisPartitionId>(_bis_partition_id);
|
||||||
|
|
||||||
|
/* Try to open a storage for the partition. */
|
||||||
|
FsStorage bis_storage;
|
||||||
|
R_TRY(fsOpenBisStorageFwd(this->forward_service.get(), &bis_storage, bis_partition_id));
|
||||||
|
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(&bis_storage.s)};
|
||||||
|
|
||||||
|
const bool is_sysmodule = ncm::IsSystemProgramId(this->client_info.program_id);
|
||||||
|
const bool is_hbl = this->client_info.override_status.IsHbl();
|
||||||
|
const bool can_write_bis = is_sysmodule || (is_hbl && GetSettingsItemBooleanValue("atmosphere", "enable_hbl_bis_write"));
|
||||||
|
const bool can_read_cal = is_sysmodule || (is_hbl && GetSettingsItemBooleanValue("atmosphere", "enable_hbl_cal_read"));
|
||||||
|
|
||||||
|
/* Allow HBL to write to boot1 (safe firm) + package2. */
|
||||||
|
/* This is needed to not break compatibility with ChoiDujourNX, which does not check for write access before beginning an update. */
|
||||||
|
/* TODO: get fixed so that this can be turned off without causing bricks :/ */
|
||||||
|
const bool is_package2 = (FsBisPartitionId_BootConfigAndPackage2Part1 <= bis_partition_id && bis_partition_id <= FsBisPartitionId_BootConfigAndPackage2Part6);
|
||||||
|
const bool is_boot1 = bis_partition_id == FsBisPartitionId_BootPartition2Root;
|
||||||
|
const bool can_write_bis_for_choi_support = is_hbl && (is_package2 || is_boot1);
|
||||||
|
|
||||||
|
/* Set output storage. */
|
||||||
|
if (bis_partition_id == FsBisPartitionId_BootPartition1Root) {
|
||||||
|
out.SetValue(std::make_shared<IStorageInterface>(new Boot0Storage(bis_storage, this->client_info)), target_object_id);
|
||||||
|
} else if (bis_partition_id == FsBisPartitionId_CalibrationBinary) {
|
||||||
|
/* PRODINFO should *never* be writable. */
|
||||||
|
/* If we have permissions, create a read only storage. */
|
||||||
|
if (can_read_cal) {
|
||||||
|
out.SetValue(std::make_shared<IStorageInterface>(new ReadOnlyStorageAdapter(new RemoteStorage(bis_storage))), target_object_id);
|
||||||
|
} else {
|
||||||
|
/* If we can't read cal, return permission denied. */
|
||||||
|
fsStorageClose(&bis_storage);
|
||||||
|
return fs::ResultPermissionDenied();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (can_write_bis || can_write_bis_for_choi_support) {
|
||||||
|
/* We can write, so create a writable storage. */
|
||||||
|
out.SetValue(std::make_shared<IStorageInterface>(new RemoteStorage(bis_storage)), target_object_id);
|
||||||
|
} else {
|
||||||
|
/* We can only read, so create a readable storage. */
|
||||||
|
out.SetValue(std::make_shared<IStorageInterface>(new ReadOnlyStorageAdapter(new RemoteStorage(bis_storage))), target_object_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FsMitmService::OpenDataStorageByCurrentProcess(sf::Out<std::shared_ptr<IStorageInterface>> out) {
|
||||||
|
/* Only mitm if we should override contents for the current process. */
|
||||||
|
R_UNLESS(this->client_info.override_status.IsProgramSpecific(), sm::mitm::ResultShouldForwardToSession());
|
||||||
|
|
||||||
|
/* Only mitm if there is actually an override romfs. */
|
||||||
|
R_UNLESS(mitm::fs::HasSdRomfsContent(this->client_info.program_id), sm::mitm::ResultShouldForwardToSession());
|
||||||
|
|
||||||
|
/* Try to open the process romfs. */
|
||||||
|
FsStorage data_storage;
|
||||||
|
R_TRY(fsOpenDataStorageByCurrentProcessFwd(this->forward_service.get(), &data_storage));
|
||||||
|
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(&data_storage.s)};
|
||||||
|
|
||||||
|
/* Try to get a storage from the cache. */
|
||||||
|
{
|
||||||
|
std::shared_ptr<IStorageInterface> cached_storage = GetStorageCacheEntry(this->client_info.program_id);
|
||||||
|
if (cached_storage != nullptr) {
|
||||||
|
out.SetValue(std::move(cached_storage), target_object_id);
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make a new layered romfs, and cache to storage. */
|
||||||
|
{
|
||||||
|
std::shared_ptr<IStorageInterface> new_storage_intf = nullptr;
|
||||||
|
|
||||||
|
/* Create the layered storage. */
|
||||||
|
FsFile data_file;
|
||||||
|
if (R_SUCCEEDED(OpenAtmosphereSdFile(&data_file, this->client_info.program_id, "romfs.bin", OpenMode_Read))) {
|
||||||
|
auto *layered_storage = new LayeredRomfsStorage(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), std::make_unique<ReadOnlyStorageAdapter>(new FileStorage(new RemoteFile(data_file))), this->client_info.program_id);
|
||||||
|
new_storage_intf = std::make_shared<IStorageInterface>(layered_storage);
|
||||||
|
} else {
|
||||||
|
auto *layered_storage = new LayeredRomfsStorage(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), nullptr, this->client_info.program_id);
|
||||||
|
new_storage_intf = std::make_shared<IStorageInterface>(layered_storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetStorageCacheEntry(this->client_info.program_id, &new_storage_intf);
|
||||||
|
out.SetValue(std::move(new_storage_intf), target_object_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FsMitmService::OpenDataStorageByDataId(sf::Out<std::shared_ptr<IStorageInterface>> out, ncm::ProgramId /* TODO: ncm::DataId */ data_id, u8 storage_id) {
|
||||||
|
/* Only mitm if we should override contents for the current process. */
|
||||||
|
R_UNLESS(this->client_info.override_status.IsProgramSpecific(), sm::mitm::ResultShouldForwardToSession());
|
||||||
|
|
||||||
|
/* Only mitm if there is actually an override romfs. */
|
||||||
|
R_UNLESS(mitm::fs::HasSdRomfsContent(data_id), sm::mitm::ResultShouldForwardToSession());
|
||||||
|
|
||||||
|
/* Try to open the process romfs. */
|
||||||
|
FsStorage data_storage;
|
||||||
|
R_TRY(fsOpenDataStorageByDataIdFwd(this->forward_service.get(), &data_storage, static_cast<u64>(data_id), static_cast<NcmStorageId>(storage_id)));
|
||||||
|
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(&data_storage.s)};
|
||||||
|
|
||||||
|
/* Try to get a storage from the cache. */
|
||||||
|
{
|
||||||
|
std::shared_ptr<IStorageInterface> cached_storage = GetStorageCacheEntry(data_id);
|
||||||
|
if (cached_storage != nullptr) {
|
||||||
|
out.SetValue(std::move(cached_storage), target_object_id);
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make a new layered romfs, and cache to storage. */
|
||||||
|
{
|
||||||
|
std::shared_ptr<IStorageInterface> new_storage_intf = nullptr;
|
||||||
|
|
||||||
|
/* Create the layered storage. */
|
||||||
|
FsFile data_file;
|
||||||
|
if (R_SUCCEEDED(OpenAtmosphereSdFile(&data_file, data_id, "romfs.bin", OpenMode_Read))) {
|
||||||
|
auto *layered_storage = new LayeredRomfsStorage(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), std::make_unique<ReadOnlyStorageAdapter>(new FileStorage(new RemoteFile(data_file))), data_id);
|
||||||
|
new_storage_intf = std::make_shared<IStorageInterface>(layered_storage);
|
||||||
|
} else {
|
||||||
|
auto *layered_storage = new LayeredRomfsStorage(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), nullptr, data_id);
|
||||||
|
new_storage_intf = std::make_shared<IStorageInterface>(layered_storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetStorageCacheEntry(data_id, &new_storage_intf);
|
||||||
|
out.SetValue(std::move(new_storage_intf), target_object_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
82
stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.hpp
Normal file
82
stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.hpp
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include <stratosphere/fssrv/fssrv_interface_adapters.hpp>
|
||||||
|
|
||||||
|
namespace ams::mitm::fs {
|
||||||
|
|
||||||
|
using IStorageInterface = ams::fssrv::impl::StorageInterfaceAdapter;
|
||||||
|
using IFileSystemInterface = ams::fssrv::impl::FileSystemInterfaceAdapter;
|
||||||
|
|
||||||
|
/* TODO: Consider re-enabling the mitm flag logic. */
|
||||||
|
|
||||||
|
class FsMitmService : public sf::IMitmServiceObject {
|
||||||
|
private:
|
||||||
|
enum class CommandId {
|
||||||
|
OpenFileSystemDeprecated = 0,
|
||||||
|
|
||||||
|
SetCurrentProcess = 1,
|
||||||
|
OpenFileSystemWithPatch = 7,
|
||||||
|
OpenFileSystemWithId = 8,
|
||||||
|
|
||||||
|
OpenSdCardFileSystem = 18,
|
||||||
|
|
||||||
|
OpenSaveDataFileSystem = 51,
|
||||||
|
|
||||||
|
OpenBisStorage = 12,
|
||||||
|
OpenDataStorageByCurrentProcess = 200,
|
||||||
|
OpenDataStorageByDataId = 202,
|
||||||
|
};
|
||||||
|
public:
|
||||||
|
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
|
||||||
|
static std::atomic_bool has_launched_qlaunch = false;
|
||||||
|
|
||||||
|
/* TODO: intercepting everything seems to cause issues with sleep mode, for some reason. */
|
||||||
|
/* Figure out why, and address it. */
|
||||||
|
/* TODO: This may be because pre-rewrite code really mismanaged domain objects in a way that would cause bad things. */
|
||||||
|
/* Need to verify if this is fixed now. */
|
||||||
|
if (client_info.program_id == ncm::ProgramId::AppletQlaunch || client_info.program_id == ncm::ProgramId::AppletMaintenanceMenu) {
|
||||||
|
has_launched_qlaunch = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return has_launched_qlaunch || client_info.program_id == ncm::ProgramId::Ns || !ncm::IsSystemProgramId(client_info.program_id);
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
SF_MITM_SERVICE_OBJECT_CTOR(FsMitmService) { /* ... */ }
|
||||||
|
protected:
|
||||||
|
/* Overridden commands. */
|
||||||
|
Result OpenFileSystemWithPatch(sf::Out<std::shared_ptr<IFileSystemInterface>> out, ncm::ProgramId program_id, u32 _filesystem_type);
|
||||||
|
Result OpenFileSystemWithId(sf::Out<std::shared_ptr<IFileSystemInterface>> out, const fssrv::sf::Path &path, ncm::ProgramId program_id, u32 _filesystem_type);
|
||||||
|
Result OpenSdCardFileSystem(sf::Out<std::shared_ptr<IFileSystemInterface>> out);
|
||||||
|
Result OpenSaveDataFileSystem(sf::Out<std::shared_ptr<IFileSystemInterface>> out, u8 space_id, const FsSaveDataAttribute &attribute);
|
||||||
|
Result OpenBisStorage(sf::Out<std::shared_ptr<IStorageInterface>> out, u32 bis_partition_id);
|
||||||
|
Result OpenDataStorageByCurrentProcess(sf::Out<std::shared_ptr<IStorageInterface>> out);
|
||||||
|
Result OpenDataStorageByDataId(sf::Out<std::shared_ptr<IStorageInterface>> out, ncm::ProgramId /* TODO: ncm::DataId */ data_id, u8 storage_id);
|
||||||
|
public:
|
||||||
|
DEFINE_SERVICE_DISPATCH_TABLE {
|
||||||
|
MAKE_SERVICE_COMMAND_META(OpenFileSystemWithPatch, hos::Version_200),
|
||||||
|
MAKE_SERVICE_COMMAND_META(OpenFileSystemWithId, hos::Version_200),
|
||||||
|
MAKE_SERVICE_COMMAND_META(OpenSdCardFileSystem),
|
||||||
|
MAKE_SERVICE_COMMAND_META(OpenSaveDataFileSystem),
|
||||||
|
MAKE_SERVICE_COMMAND_META(OpenBisStorage),
|
||||||
|
MAKE_SERVICE_COMMAND_META(OpenDataStorageByCurrentProcess),
|
||||||
|
MAKE_SERVICE_COMMAND_META(OpenDataStorageByDataId),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,269 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope 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/>.
|
|
||||||
*/
|
|
||||||
#include <cstring>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <switch.h>
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
|
|
||||||
#include "fs_path_utils.hpp"
|
|
||||||
|
|
||||||
Result FsPathUtils::VerifyPath(const char *path, size_t max_path_len, size_t max_name_len) {
|
|
||||||
const char *cur = path;
|
|
||||||
size_t name_len = 0;
|
|
||||||
|
|
||||||
for (size_t path_len = 0; path_len <= max_path_len && name_len <= max_name_len; path_len++) {
|
|
||||||
const char c = *(cur++);
|
|
||||||
/* If terminated, we're done. */
|
|
||||||
if (c == 0) {
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: Nintendo converts the path from utf-8 to utf-32, one character at a time. */
|
|
||||||
/* We should do this. */
|
|
||||||
|
|
||||||
/* Banned characters: :*?<>| */
|
|
||||||
if (c == ':' || c == '*' || c == '?' || c == '<' || c == '>' || c == '|') {
|
|
||||||
return ResultFsInvalidCharacter;
|
|
||||||
}
|
|
||||||
|
|
||||||
name_len++;
|
|
||||||
/* Check for separator. */
|
|
||||||
if (c == '/' || c == '\\') {
|
|
||||||
name_len = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultFsTooLongPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result FsPathUtils::ConvertPathForServiceObject(FsPath *out, const char *path) {
|
|
||||||
/* Check for nullptr. */
|
|
||||||
if (out == nullptr || path == nullptr) {
|
|
||||||
return ResultFsInvalidPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy string, NULL terminate. */
|
|
||||||
/* NOTE: Nintendo adds an extra char at 0x301 for NULL terminator */
|
|
||||||
/* But then forces 0x300 to NULL anyway... */
|
|
||||||
std::strncpy(out->str, path, sizeof(out->str) - 1);
|
|
||||||
out->str[sizeof(out->str)-1] = 0;
|
|
||||||
|
|
||||||
/* Replace any instances of \ with / */
|
|
||||||
for (size_t i = 0; i < sizeof(out->str); i++) {
|
|
||||||
if (out->str[i] == '\\') {
|
|
||||||
out->str[i] = '/';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Nintendo allows some liberties if the path is a windows path. Who knows why... */
|
|
||||||
const auto prefix_len = FsPathUtils::IsWindowsAbsolutePath(path) ? 2 : 0;
|
|
||||||
const size_t max_len = (FS_MAX_PATH-1) - prefix_len;
|
|
||||||
return FsPathUtils::VerifyPath(out->str + prefix_len, max_len, max_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result FsPathUtils::IsNormalized(bool *out, const char *path) {
|
|
||||||
/* Nintendo uses a state machine here. */
|
|
||||||
enum class PathState {
|
|
||||||
Start,
|
|
||||||
Normal,
|
|
||||||
FirstSeparator,
|
|
||||||
Separator,
|
|
||||||
CurrentDir,
|
|
||||||
ParentDir,
|
|
||||||
WindowsDriveLetter,
|
|
||||||
};
|
|
||||||
|
|
||||||
PathState state = PathState::Start;
|
|
||||||
|
|
||||||
for (const char *cur = path; *cur != 0; cur++) {
|
|
||||||
const char c = *cur;
|
|
||||||
switch (state) {
|
|
||||||
case PathState::Start:
|
|
||||||
if (IsWindowsDriveLetter(c)) {
|
|
||||||
state = PathState::WindowsDriveLetter;
|
|
||||||
} else if (c == '/') {
|
|
||||||
state = PathState::FirstSeparator;
|
|
||||||
} else {
|
|
||||||
return ResultFsInvalidPathFormat;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case PathState::Normal:
|
|
||||||
if (c == '/') {
|
|
||||||
state = PathState::Separator;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case PathState::FirstSeparator:
|
|
||||||
case PathState::Separator:
|
|
||||||
/* It is unclear why first separator and separator are separate states... */
|
|
||||||
if (c == '/') {
|
|
||||||
*out = false;
|
|
||||||
return ResultSuccess;
|
|
||||||
} else if (c == '.') {
|
|
||||||
state = PathState::CurrentDir;
|
|
||||||
} else {
|
|
||||||
state = PathState::Normal;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case PathState::CurrentDir:
|
|
||||||
if (c == '/') {
|
|
||||||
*out = false;
|
|
||||||
return ResultSuccess;
|
|
||||||
} else if (c == '.') {
|
|
||||||
state = PathState::ParentDir;
|
|
||||||
} else {
|
|
||||||
state = PathState::Normal;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case PathState::ParentDir:
|
|
||||||
if (c == '/') {
|
|
||||||
*out = false;
|
|
||||||
return ResultSuccess;
|
|
||||||
} else {
|
|
||||||
state = PathState::Normal;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case PathState::WindowsDriveLetter:
|
|
||||||
if (c == ':') {
|
|
||||||
*out = true;
|
|
||||||
return ResultSuccess;
|
|
||||||
} else {
|
|
||||||
return ResultFsInvalidPathFormat;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (state) {
|
|
||||||
case PathState::Start:
|
|
||||||
case PathState::WindowsDriveLetter:
|
|
||||||
return ResultFsInvalidPathFormat;
|
|
||||||
case PathState::FirstSeparator:
|
|
||||||
case PathState::Normal:
|
|
||||||
*out = true;
|
|
||||||
break;
|
|
||||||
case PathState::CurrentDir:
|
|
||||||
case PathState::ParentDir:
|
|
||||||
case PathState::Separator:
|
|
||||||
*out = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result FsPathUtils::Normalize(char *out, size_t max_out_size, const char *src, size_t *out_len) {
|
|
||||||
/* Paths must start with / */
|
|
||||||
if (src[0] != '/') {
|
|
||||||
return ResultFsInvalidPathFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool skip_next_sep = false;
|
|
||||||
size_t i = 0;
|
|
||||||
size_t len = 0;
|
|
||||||
|
|
||||||
while (src[i] != 0) {
|
|
||||||
if (src[i] == '/') {
|
|
||||||
/* Swallow separators. */
|
|
||||||
while (src[++i] == '/') { }
|
|
||||||
if (src[i] == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handle skip if needed */
|
|
||||||
if (!skip_next_sep) {
|
|
||||||
if (len + 1 == max_out_size) {
|
|
||||||
out[len] = 0;
|
|
||||||
if (out_len != nullptr) {
|
|
||||||
*out_len = len;
|
|
||||||
}
|
|
||||||
return ResultFsTooLongPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
out[len++] = '/';
|
|
||||||
|
|
||||||
/* TODO: N has some weird windows support stuff here under a bool. */
|
|
||||||
/* Boolean is normally false though? */
|
|
||||||
}
|
|
||||||
skip_next_sep = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* See length of current dir. */
|
|
||||||
size_t dir_len = 0;
|
|
||||||
while (src[i+dir_len] != '/' && src[i+dir_len] != 0) {
|
|
||||||
dir_len++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FsPathUtils::IsCurrentDirectory(&src[i])) {
|
|
||||||
skip_next_sep = true;
|
|
||||||
} else if (FsPathUtils::IsParentDirectory(&src[i])) {
|
|
||||||
if (len == 1) {
|
|
||||||
return ResultFsDirectoryUnobtainable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Walk up a directory. */
|
|
||||||
len -= 2;
|
|
||||||
while (out[len] != '/') {
|
|
||||||
len--;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Copy, possibly truncating. */
|
|
||||||
if (len + dir_len + 1 <= max_out_size) {
|
|
||||||
for (size_t j = 0; j < dir_len; j++) {
|
|
||||||
out[len++] = src[i+j];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const size_t copy_len = max_out_size - 1 - len;
|
|
||||||
for (size_t j = 0; j < copy_len; j++) {
|
|
||||||
out[len++] = src[i+j];
|
|
||||||
}
|
|
||||||
out[len] = 0;
|
|
||||||
if (out_len != nullptr) {
|
|
||||||
*out_len = len;
|
|
||||||
}
|
|
||||||
return ResultFsTooLongPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
i += dir_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (skip_next_sep) {
|
|
||||||
len--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len == 0 && max_out_size) {
|
|
||||||
out[len++] = '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (max_out_size < len - 1) {
|
|
||||||
return ResultFsTooLongPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* NULL terminate. */
|
|
||||||
out[len] = 0;
|
|
||||||
if (out_len != nullptr) {
|
|
||||||
*out_len = len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Assert normalized. */
|
|
||||||
bool normalized = false;
|
|
||||||
R_ASSERT(FsPathUtils::IsNormalized(&normalized, out));
|
|
||||||
if (!normalized) {
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
|
|
||||||
struct FsPath {
|
|
||||||
char str[FS_MAX_PATH];
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr FsPath EncodeConstantFsPath(const char *p) {
|
|
||||||
FsPath path = {0};
|
|
||||||
for (size_t i = 0; i < sizeof(path) - 1; i++) {
|
|
||||||
path.str[i] = p[i];
|
|
||||||
if (p[i] == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr size_t GetConstantFsPathLen(FsPath path) {
|
|
||||||
size_t len = 0;
|
|
||||||
for (size_t i = 0; i < sizeof(path) - 1; i++) {
|
|
||||||
if (path.str[i] == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
len++;
|
|
||||||
}
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
class FsPathUtils {
|
|
||||||
public:
|
|
||||||
static constexpr FsPath RootPath = EncodeConstantFsPath("/");
|
|
||||||
public:
|
|
||||||
static Result VerifyPath(const char *path, size_t max_path_len, size_t max_name_len);
|
|
||||||
static Result ConvertPathForServiceObject(FsPath *out, const char *path);
|
|
||||||
|
|
||||||
static Result IsNormalized(bool *out, const char *path);
|
|
||||||
static Result Normalize(char *out, size_t max_out_size, const char *src, size_t *out_size);
|
|
||||||
|
|
||||||
static bool IsWindowsDriveLetter(const char c) {
|
|
||||||
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool IsCurrentDirectory(const char *path) {
|
|
||||||
return path[0] == '.' && (path[1] == 0 || path[1] == '/');
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool IsParentDirectory(const char *path) {
|
|
||||||
return path[0] == '.' && path[1] == '.' && (path[2] == 0 || path[2] == '/');
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool IsWindowsAbsolutePath(const char *path) {
|
|
||||||
/* Nintendo uses this in path comparisons... */
|
|
||||||
return IsWindowsDriveLetter(path[0]) && path[1] == ':';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope 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/>.
|
|
||||||
*/
|
|
||||||
#include <cstring>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <switch.h>
|
|
||||||
|
|
||||||
#include "fs_save_utils.hpp"
|
|
||||||
|
|
||||||
Result FsSaveUtils::GetSaveDataSpaceIdString(const char **out_str, u8 space_id) {
|
|
||||||
const char *str = nullptr;
|
|
||||||
|
|
||||||
switch (space_id) {
|
|
||||||
case FsSaveDataSpaceId_NandSystem:
|
|
||||||
case 100: /* ProperSystem */
|
|
||||||
case 101: /* SafeMode */
|
|
||||||
str = "sys";
|
|
||||||
break;
|
|
||||||
case FsSaveDataSpaceId_NandUser:
|
|
||||||
str = "user";
|
|
||||||
break;
|
|
||||||
case FsSaveDataSpaceId_SdCard:
|
|
||||||
str = "sd_sys";
|
|
||||||
break;
|
|
||||||
case FsSaveDataSpaceId_TemporaryStorage:
|
|
||||||
str = "temp";
|
|
||||||
break;
|
|
||||||
case 4: /* SdUser */
|
|
||||||
str = "sd_user";
|
|
||||||
break;
|
|
||||||
default: /* Unexpected */
|
|
||||||
str = nullptr;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (str == nullptr) {
|
|
||||||
return ResultFsInvalidSaveDataSpaceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (out_str) {
|
|
||||||
*out_str = str;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result FsSaveUtils::GetSaveDataTypeString(const char **out_str, u8 save_data_type) {
|
|
||||||
const char *str = nullptr;
|
|
||||||
|
|
||||||
switch (save_data_type) {
|
|
||||||
case FsSaveDataType_SystemSaveData:
|
|
||||||
str = "system";
|
|
||||||
break;
|
|
||||||
case FsSaveDataType_SaveData:
|
|
||||||
str = "account";
|
|
||||||
break;
|
|
||||||
case FsSaveDataType_BcatDeliveryCacheStorage:
|
|
||||||
str = "bcat";
|
|
||||||
break;
|
|
||||||
case FsSaveDataType_DeviceSaveData:
|
|
||||||
str = "device";
|
|
||||||
break;
|
|
||||||
case FsSaveDataType_TemporaryStorage:
|
|
||||||
str = "temp";
|
|
||||||
break;
|
|
||||||
case FsSaveDataType_CacheStorage:
|
|
||||||
str = "cache";
|
|
||||||
break;
|
|
||||||
case 6: /* System Bcat Save */
|
|
||||||
str = "bcat_sys";
|
|
||||||
break;
|
|
||||||
default: /* Unexpected */
|
|
||||||
str = nullptr;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (str == nullptr) {
|
|
||||||
/* TODO: Better result? */
|
|
||||||
return ResultFsInvalidArgument;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (out_str) {
|
|
||||||
*out_str = str;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result FsSaveUtils::GetSaveDataDirectoryPath(FsPath &out_path, u8 space_id, u8 save_data_type, u64 title_id, u128 user_id, u64 save_id) {
|
|
||||||
const char *space_id_str, *save_type_str;
|
|
||||||
|
|
||||||
/* Get space_id, save_data_type strings. */
|
|
||||||
R_TRY(GetSaveDataSpaceIdString(&space_id_str, space_id));
|
|
||||||
R_TRY(GetSaveDataTypeString(&save_type_str, save_data_type));
|
|
||||||
|
|
||||||
/* Clear and initialize the path. */
|
|
||||||
std::memset(&out_path, 0, sizeof(out_path));
|
|
||||||
const bool is_system = (save_id != 0 && user_id == 0);
|
|
||||||
size_t out_path_len;
|
|
||||||
if (is_system) {
|
|
||||||
out_path_len = static_cast<size_t>(snprintf(out_path.str, sizeof(out_path.str), "/atmosphere/saves/sysnand/%s/%s/%016lx",
|
|
||||||
space_id_str, save_type_str, save_id));
|
|
||||||
} else {
|
|
||||||
out_path_len = static_cast<size_t>(snprintf(out_path.str, sizeof(out_path.str), "/atmosphere/saves/sysnand/%s/%s/%016lx/%016lx%016lx",
|
|
||||||
space_id_str, save_type_str, title_id, static_cast<u64>(user_id >> 64ul), static_cast<u64>(user_id)));
|
|
||||||
}
|
|
||||||
if (out_path_len >= sizeof(out_path)) {
|
|
||||||
/* TODO: Should we abort here? */
|
|
||||||
return ResultFsTooLongPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
|
|
||||||
#include "fs_path_utils.hpp"
|
|
||||||
#include "fs_ifilesystem.hpp"
|
|
||||||
|
|
||||||
class FsSaveUtils {
|
|
||||||
private:
|
|
||||||
static Result GetSaveDataSpaceIdString(const char **out_str, u8 space_id);
|
|
||||||
static Result GetSaveDataTypeString(const char **out_str, u8 save_data_type);
|
|
||||||
public:
|
|
||||||
static Result GetSaveDataDirectoryPath(FsPath &out_path, u8 space_id, u8 save_data_type, u64 title_id, u128 user_id, u64 save_id);
|
|
||||||
};
|
|
||||||
@@ -13,254 +13,53 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <switch.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "fs_shim.h"
|
#include "fs_shim.h"
|
||||||
|
|
||||||
/* Missing fsp-srv commands. */
|
/* Missing fsp-srv commands. */
|
||||||
Result fsOpenBisStorageFwd(Service* s, FsStorage* out, FsBisStorageId PartitionId) {
|
static Result _fsOpenSession(Service *s, Service* out, u32 cmd_id) {
|
||||||
IpcCommand c;
|
return serviceDispatch(s, cmd_id,
|
||||||
ipcInitialize(&c);
|
.out_num_objects = 1,
|
||||||
|
.out_objects = out,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
struct {
|
Result fsOpenSdCardFileSystemFwd(Service* s, FsFileSystem* out) {
|
||||||
u64 magic;
|
return _fsOpenSession(s, &out->s, 18);
|
||||||
u64 cmd_id;
|
}
|
||||||
u32 PartitionId;
|
|
||||||
} *raw;
|
|
||||||
|
|
||||||
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
|
Result fsOpenBisStorageFwd(Service* s, FsStorage* out, FsBisPartitionId partition_id) {
|
||||||
|
const u32 tmp = partition_id;
|
||||||
raw->magic = SFCI_MAGIC;
|
return serviceDispatchIn(s, 12, tmp,
|
||||||
raw->cmd_id = 12;
|
.out_num_objects = 1,
|
||||||
raw->PartitionId = PartitionId;
|
.out_objects = &out->s,
|
||||||
|
);
|
||||||
Result rc = serviceIpcDispatch(s);
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
IpcParsedCommand r;
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 result;
|
|
||||||
} *resp;
|
|
||||||
|
|
||||||
serviceIpcParse(s, &r, sizeof(*resp));
|
|
||||||
resp = r.Raw;
|
|
||||||
|
|
||||||
rc = resp->result;
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
serviceCreateSubservice(&out->s, s, &r, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out) {
|
Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out) {
|
||||||
IpcCommand c;
|
return _fsOpenSession(s, &out->s, 200);
|
||||||
ipcInitialize(&c);
|
|
||||||
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 cmd_id;
|
|
||||||
} *raw;
|
|
||||||
|
|
||||||
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
|
|
||||||
|
|
||||||
raw->magic = SFCI_MAGIC;
|
|
||||||
raw->cmd_id = 200;
|
|
||||||
|
|
||||||
Result rc = serviceIpcDispatch(s);
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
IpcParsedCommand r;
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 result;
|
|
||||||
} *resp;
|
|
||||||
|
|
||||||
serviceIpcParse(s, &r, sizeof(*resp));
|
|
||||||
resp = r.Raw;
|
|
||||||
|
|
||||||
rc = resp->result;
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
serviceCreateSubservice(&out->s, s, &r, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorageId storage_id, u64 data_id, FsStorage* out) {
|
Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorage* out, u64 data_id, NcmStorageId storage_id) {
|
||||||
IpcCommand c;
|
const struct {
|
||||||
ipcInitialize(&c);
|
u8 storage_id;
|
||||||
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 cmd_id;
|
|
||||||
FsStorageId storage_id;
|
|
||||||
u64 data_id;
|
u64 data_id;
|
||||||
} *raw;
|
} in = { storage_id, data_id };
|
||||||
|
|
||||||
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
|
return serviceDispatchIn(s, 202, in,
|
||||||
|
.out_num_objects = 1,
|
||||||
raw->magic = SFCI_MAGIC;
|
.out_objects = &out->s,
|
||||||
raw->cmd_id = 202;
|
);
|
||||||
raw->storage_id = storage_id;
|
|
||||||
raw->data_id = data_id;
|
|
||||||
|
|
||||||
Result rc = serviceIpcDispatch(s);
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
IpcParsedCommand r;
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 result;
|
|
||||||
} *resp;
|
|
||||||
|
|
||||||
serviceIpcParse(s, &r, sizeof(*resp));
|
|
||||||
resp = r.Raw;
|
|
||||||
|
|
||||||
rc = resp->result;
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
serviceCreateSubservice(&out->s, s, &r, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result fsOpenFileSystemWithPatchFwd(Service* s, FsFileSystem* out, u64 titleId, FsFileSystemType fsType) {
|
Result fsOpenSaveDataFileSystemFwd(Service* s, FsFileSystem* out, FsSaveDataSpaceId save_data_space_id, const FsSaveDataAttribute *attr) {
|
||||||
if (hosversionBefore(2, 0, 0)) {
|
const struct {
|
||||||
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
|
u8 save_data_space_id;
|
||||||
}
|
u8 pad[7];
|
||||||
|
FsSaveDataAttribute attr;
|
||||||
|
} in = { (u8)save_data_space_id, {0}, *attr };
|
||||||
|
|
||||||
IpcCommand c;
|
return serviceDispatchIn(s, 51, in,
|
||||||
ipcInitialize(&c);
|
.out_num_objects = 1,
|
||||||
|
.out_objects = &out->s,
|
||||||
struct {
|
);
|
||||||
u64 magic;
|
|
||||||
u64 cmd_id;
|
|
||||||
u32 fsType;
|
|
||||||
u64 titleId;
|
|
||||||
} *raw;
|
|
||||||
|
|
||||||
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
|
|
||||||
|
|
||||||
raw->magic = SFCI_MAGIC;
|
|
||||||
raw->cmd_id = 7;
|
|
||||||
raw->fsType = fsType;
|
|
||||||
raw->titleId = titleId;
|
|
||||||
|
|
||||||
Result rc = serviceIpcDispatch(s);
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
IpcParsedCommand r;
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 result;
|
|
||||||
} *resp;
|
|
||||||
|
|
||||||
serviceIpcParse(s, &r, sizeof(*resp));
|
|
||||||
resp = r.Raw;
|
|
||||||
|
|
||||||
rc = resp->result;
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
serviceCreateSubservice(&out->s, s, &r, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result fsOpenFileSystemWithIdFwd(Service* s, FsFileSystem* out, u64 titleId, FsFileSystemType fsType, const char* contentPath) {
|
|
||||||
if (hosversionBefore(2, 0, 0)) {
|
|
||||||
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
|
|
||||||
}
|
|
||||||
|
|
||||||
char sendStr[FS_MAX_PATH] = {0};
|
|
||||||
strncpy(sendStr, contentPath, sizeof(sendStr)-1);
|
|
||||||
|
|
||||||
IpcCommand c;
|
|
||||||
ipcInitialize(&c);
|
|
||||||
ipcAddSendStatic(&c, sendStr, sizeof(sendStr), 0);
|
|
||||||
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 cmd_id;
|
|
||||||
u32 fsType;
|
|
||||||
u64 titleId;
|
|
||||||
} *raw;
|
|
||||||
|
|
||||||
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
|
|
||||||
|
|
||||||
raw->magic = SFCI_MAGIC;
|
|
||||||
raw->cmd_id = 8;
|
|
||||||
raw->fsType = fsType;
|
|
||||||
raw->titleId = titleId;
|
|
||||||
|
|
||||||
Result rc = serviceIpcDispatch(s);
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
IpcParsedCommand r;
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 result;
|
|
||||||
} *resp;
|
|
||||||
|
|
||||||
serviceIpcParse(s, &r, sizeof(*resp));
|
|
||||||
resp = r.Raw;
|
|
||||||
|
|
||||||
rc = resp->result;
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
serviceCreateSubservice(&out->s, s, &r, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result fsOpenSaveDataFileSystemFwd(Service *s, FsFileSystem* out, u8 inval, FsSave *save) {
|
|
||||||
IpcCommand c;
|
|
||||||
ipcInitialize(&c);
|
|
||||||
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 cmd_id;
|
|
||||||
u64 inval;//Actually u8.
|
|
||||||
FsSave save;
|
|
||||||
} PACKED *raw;
|
|
||||||
|
|
||||||
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
|
|
||||||
|
|
||||||
raw->magic = SFCI_MAGIC;
|
|
||||||
raw->cmd_id = 51;
|
|
||||||
raw->inval = (u64)inval;
|
|
||||||
memcpy(&raw->save, save, sizeof(FsSave));
|
|
||||||
|
|
||||||
Result rc = serviceIpcDispatch(s);
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
IpcParsedCommand r;
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 result;
|
|
||||||
} *resp;
|
|
||||||
|
|
||||||
serviceIpcParse(s, &r, sizeof(*resp));
|
|
||||||
resp = r.Raw;
|
|
||||||
|
|
||||||
rc = resp->result;
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
serviceCreateSubservice(&out->s, s, &r, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* @file fs_shim.h
|
* @file fs_shim.h
|
||||||
* @brief Filesystem Services (fs) IPC wrapper. To be merged into libnx, eventually.
|
* @brief Filesystem Services (fs) IPC wrapper for fs.mitm.
|
||||||
* @author SciresM
|
* @author SciresM
|
||||||
* @copyright libnx Authors
|
* @copyright libnx Authors
|
||||||
*/
|
*/
|
||||||
@@ -12,12 +12,13 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Missing fsp-srv commands. */
|
/* Missing fsp-srv commands. */
|
||||||
Result fsOpenBisStorageFwd(Service* s, FsStorage* out, FsBisStorageId PartitionId);
|
Result fsOpenSdCardFileSystemFwd(Service* s, FsFileSystem* out);
|
||||||
|
Result fsOpenBisStorageFwd(Service* s, FsStorage* out, FsBisPartitionId partition_id);
|
||||||
Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out);
|
Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out);
|
||||||
Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorageId storage_id, u64 data_id, FsStorage* out);
|
Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorage* out, u64 data_id, NcmStorageId storage_id);
|
||||||
Result fsOpenFileSystemWithPatchFwd(Service* s, FsFileSystem* out, u64 titleId, FsFileSystemType fsType);
|
|
||||||
Result fsOpenFileSystemWithIdFwd(Service* s, FsFileSystem* out, u64 titleId, FsFileSystemType fsType, const char* contentPath);
|
Result fsOpenSaveDataFileSystemFwd(Service* s, FsFileSystem* out, FsSaveDataSpaceId save_data_space_id, const FsSaveDataAttribute *attr);
|
||||||
Result fsOpenSaveDataFileSystemFwd(Service* s, FsFileSystem* out, u8 inval, FsSave *save);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user