Compare commits

...

90 Commits

Author SHA1 Message Date
Michael Scire
7e6ff1f327 exosphere: support enabling usermode pmu regs (closes #703) 2019-12-07 15:35:34 -08:00
Michael Scire
ea49c2ccd1 Update changelog for 0.10.0 2019-12-07 15:23:25 -08:00
Michael Scire
d484872a73 git subrepo clone --force https://github.com/m4xw/emummc
subrepo:
  subdir:   "emummc"
  merged:   "bd81a674"
upstream:
  origin:   "https://github.com/m4xw/emummc"
  branch:   "develop"
  commit:   "bd81a674"
git-subrepo:
  version:  "0.4.0"
  origin:   "https://github.com/ingydotnet/git-subrepo"
  commit:   "5d6aba9"
2019-12-07 14:54:15 -08:00
Michael Scire
6ee525201c Update emummc README 2019-12-07 14:39:01 -08:00
Michael Scire
5ac7ae7edb Update emummc for 9.1.0 2019-12-07 14:36:31 -08:00
Michael Scire
0c590eb768 Fix double semi-colon 2019-12-07 14:03:47 -08:00
Michael Scire
bd40d4f237 Turn on fs.mitm multithreading 2019-12-07 14:02:07 -08:00
Michael Scire
788436b4a3 Bump version to 0.10.0 2019-12-07 13:47:52 -08:00
Michael Scire
2bb77237bc Increase default applet reservation size to make psel work 2019-12-07 13:45:14 -08:00
Michael Scire
33827fe3a3 Implement support for 9.1.0 2019-12-07 13:44:08 -08:00
Lioncash
48b0b2fc46 dmnt_cheat_vm: Correct register Restore and ClearRegs behavior
Previously they were performing the same behavior as the Save and
ClearSave opcode types.
2019-12-07 12:41:56 -08:00
Michael Scire
4f29eedfe9 ams_mitm: implement hbl_html redirection 2019-12-07 12:41:28 -08:00
Michael Scire
66372e2b2e ams_mitm: implement ns.mitm 2019-12-07 12:41:28 -08:00
Michael Scire
2bae1ad116 Delete pre-rewrite ams_mitm code 2019-12-07 12:41:28 -08:00
Michael Scire
bbdc643b6d ams_mitm: fix directory save bugs 2019-12-07 12:41:28 -08:00
Michael Scire
a66fcde0ee fix uninitialized data access 2019-12-07 12:41:28 -08:00
Michael Scire
7835486a4d ams_mitm: Implement savedata redirection 2019-12-07 12:41:28 -08:00
Michael Scire
90367aea0d ams_mitm: add titles dir compat shim (to remove in 0.10.1) 2019-12-07 12:41:28 -08:00
Michael Scire
16c638756c ams_mitm: implement automatic backups of biskeys/cal0 2019-12-07 12:41:28 -08:00
Michael Scire
b08a97d883 shared_from_this requires public inheritance 2019-12-07 12:41:28 -08:00
Michael Scire
746dbfe018 ams_mitm: Implement emummc Nintendo folder redirection 2019-12-07 12:41:28 -08:00
Michael Scire
733f2b3cdd ams_mitm: implement layeredfs 2019-12-07 12:41:28 -08:00
Michael Scire
ad64cb5212 os/dd: primitive fixes 2019-12-07 12:41:28 -08:00
Michael Scire
b1a9e8d0df compat fixes for libnx master 2019-12-07 12:41:28 -08:00
Michael Scire
4f9838df3c avoid lambda use in shared_ptr deleters 2019-12-07 12:41:28 -08:00
Michael Scire
f4ca2c02a7 fs.mitm: Implement bis protection 2019-12-07 12:41:28 -08:00
Michael Scire
e1391d4162 stop overriding user config on update 2019-12-07 12:41:28 -08:00
Michael Scire
8d9c51f204 set_mitm: embed defaults in code, allow for missing ini 2019-12-07 12:41:28 -08:00
Michael Scire
c10ba67973 ams_mitm: Implement system settings mitm 2019-12-07 12:41:28 -08:00
Michael Scire
55610694c8 ams_mitm: Implement set:sys firmwareversion mitm 2019-12-07 12:41:28 -08:00
Michael Scire
8764d94fd9 Implement set_mitm 2019-12-07 12:41:28 -08:00
Michael Scire
5228768841 ams_mitm: implement hid mitm 2019-12-07 12:41:28 -08:00
Michael Scire
9c68bea16c Add icon for reboot to payload (closes #644) 2019-12-07 12:41:28 -08:00
Michael Scire
5484740cab hbl: support different keys for any-app vs specific
This also makes the default configuration:

To override a generic app, hold R.
To override album, do not hold R.
2019-12-07 12:41:28 -08:00
Michael Scire
421324b498 mitm/cfg: pass around override status for decision-making 2019-12-07 12:41:28 -08:00
Michael Scire
37e065fa2d ams_mitm: implement bpc:mitm 2019-12-07 12:41:28 -08:00
Michael Scire
b758e3fc55 strat: update for latest libnx. 2019-12-07 12:41:28 -08:00
Michael Scire
393596ef9a ams_mitm: begin skeleton refactor 2019-12-07 12:41:28 -08:00
Michael Scire
02d4c97c6d sf: explicitly namespace in DEFINE_SERVICE_DISPATCH_TABLE 2019-12-07 12:41:28 -08:00
Michael Scire
2c5ef434f0 sf: fixes (basic mitm service functionality now confirmed working) 2019-12-07 12:41:28 -08:00
Michael Scire
122f3e4403 libstrat: implement mitm server registration 2019-12-07 12:41:28 -08:00
Michael Scire
ea3ebbaa7d strat: TitleId -> ProgramId, titles->contents 2019-12-07 12:41:28 -08:00
Michael Scire
1636668762 strat: fixes for compat with latest libnx 2019-12-07 12:41:28 -08:00
Michael Scire
c6b9a0c4bf sf: PrepareForErrorReply is common to all objects 2019-12-07 12:41:28 -08:00
Michael Scire
93a218abeb libstrat: enable lto 2019-12-07 12:41:28 -08:00
Michael Scire
d7a06057eb strat: split out common functionality for future meso use 2019-12-07 12:41:28 -08:00
Michael Scire
8cb77ac136 namespace sts -> namespace ams
namespace sts::ams -> ams::exosphere, ams::.

This is to facilitate future use of ams:: namespace code in
mesosphere, as we'll want to include ams::util, ams::result, ams::svc...
2019-12-07 12:41:28 -08:00
Michael Scire
4059dc6187 Results: Implement namespaced, type-safe results.
Because I was working on multiple things at once, this commit also:
- Adds wrappers for/linker flags to wrap CXX exceptions to make them
  abort. This saves ~0x8000 of memory in every system module.
- Broadly replaces lines of the pattern if (cond) { return ResultX; }
  with R_UNLESS(!cond, ResultX());.
- Reworks the R_TRY_CATCH macros (and the result macros in general).
2019-12-07 12:41:28 -08:00
Michael Scire
15773e4755 libstrat: fix domain issues. in/out objects now work. 2019-12-07 12:41:28 -08:00
Michael Scire
4f455dacf4 sf: implement mitm forwarding + domains. 2019-12-07 12:41:28 -08:00
Michael Scire
0b22af1206 libstrat: namespace remaining non-namespaced code. more new-ipc updates 2019-12-07 12:41:28 -08:00
Michael Scire
ae2fa2fa60 boot: reduce memory footprint 2019-12-07 12:41:28 -08:00
Michael Scire
6abd756e0c boot2: move to separate process 2019-12-07 12:41:28 -08:00
Michael Scire
535e49a38d strat: statically allocate additional threads 2019-12-07 12:41:28 -08:00
Michael Scire
fbb5730369 strat: update for latest libnx commit 2019-12-07 12:41:28 -08:00
Michael Scire
e5d62025d3 dmnt: update for new-ipc 2019-12-07 12:41:28 -08:00
Michael Scire
89c6fc6437 creport: update for new-ipc 2019-12-07 12:41:28 -08:00
Michael Scire
799c158b86 fatal: update for new-ipc 2019-12-07 12:41:28 -08:00
Michael Scire
8d16d2152b boot: update for api changes 2019-12-07 12:41:28 -08:00
Michael Scire
635ae4e3da spl: update for new-ipc (fixes two bugs in sf) 2019-12-07 12:41:28 -08:00
Michael Scire
59140d8dfa sf-impl: remove debug condition 2019-12-07 12:41:28 -08:00
Michael Scire
2cb8aadafc pm: statically allocate more resources to save memory 2019-12-07 12:41:28 -08:00
Michael Scire
aa0826bb70 pm: update for new-ipc 2019-12-07 12:41:28 -08:00
Michael Scire
8bd2a9a23b ldr: fix copy/paste, only 1 ldr:pm session needed 2019-12-07 12:41:28 -08:00
Michael Scire
2f959785e1 loader: update for new-ipc 2019-12-07 12:41:28 -08:00
Michael Scire
88a86a3363 sf: implement CopyFromCurrentDomain 2019-12-07 12:41:28 -08:00
Michael Scire
0c7827104f hipc: begin implementing domains. fixes ro + sm together 2019-12-07 12:41:28 -08:00
Michael Scire
c8ed190e5c new-ipc: implement deferral. sm now works. 2019-12-07 12:41:28 -08:00
Michael Scire
f4dcd1db9b sf: implement service framework enough for ro to work.
This completely re-does the whole interface for ipc servers.
2019-12-07 12:41:28 -08:00
Michael Scire
bd341d5c00 libstrat: update for latest libnx, delete ipc in prep for rewrite 2019-12-07 12:41:28 -08:00
Michael Scire
add18d868f sts: add STS_UNREACHABLE_DEFAULT_CASE() 2019-12-07 12:41:28 -08:00
Michael Scire
609a302e16 os: implement waitable management.
This implements waitable management for Events (and
implements Events). It also refactors PM to use new
Event/Waitable semantics, and also adds STS_ASSERT
as a macro for asserting a boolean expression. The
rest of stratosphere has been refactored to use
STS_ASSERT whenever possible.
2019-12-07 12:41:28 -08:00
Michael Scire
e07011be32 mitm: fix long-standing C descriptor issue. 2019-12-07 12:41:28 -08:00
Michael Scire
bb223eb5ae libstrat: namespace hossynch.hpp 2019-12-07 12:41:28 -08:00
hexkyz
73d904036d fusee: relax emummc multipart check for FAT32 2019-09-27 19:35:00 +01:00
Michael Scire
542833866e git subrepo push stratosphere/libstratosphere
subrepo:
  subdir:   "stratosphere/libstratosphere"
  merged:   "8bae7b4a"
upstream:
  origin:   "https://github.com/Atmosphere-NX/libstratosphere"
  branch:   "master"
  commit:   "8bae7b4a"
git-subrepo:
  version:  "0.4.0"
  origin:   "https://github.com/ingydotnet/git-subrepo"
  commit:   "5d6aba9"
2019-09-18 12:43:47 -07:00
Michael Scire
89c484414b dmnt: Truncate cheat names after maxlen instead of failing 2019-09-18 11:54:30 -07:00
Michael Scire
fe2dd671fb dmnt: fix edge case in debug events thread 2019-09-18 11:54:30 -07:00
Michael Scire
8abee1bdaa dmnt: fix some bugs in init 2019-09-18 11:54:30 -07:00
Michael Scire
78a730ddf6 dmnt: refactor to use sts:: namespace. 2019-09-18 11:54:30 -07:00
Michael Scire
a750e55f75 tmp-hid-mitm: less aggressively enable controllers
This fixes a crash in SSBU and possibly other games.
2019-09-14 13:37:31 -07:00
Michael Scire
c62c4846fc Bump version to 0.9.4. 2019-09-14 10:43:39 -07:00
Michael Scire
8db5b01507 hbl_html: enforce line ending = lf (fixes broken whitelist) 2019-09-14 10:43:39 -07:00
Michael Scire
a6e405c988 ldr: fix hbl_html redirection invocation 2019-09-14 10:43:39 -07:00
Michael Scire
6613fda4b1 ams_mitm: add temporary hid mitm on 9.x for compat 2019-09-14 10:43:39 -07:00
Michael Scire
a18a6e87df git subrepo pull emummc
subrepo:
  subdir:   "emummc"
  merged:   "5f5817e6"
upstream:
  origin:   "https://github.com/m4xw/emuMMC"
  branch:   "develop"
  commit:   "5f5817e6"
git-subrepo:
  version:  "0.4.0"
  origin:   "https://github.com/ingydotnet/git-subrepo"
  commit:   "5d6aba9"
2019-09-14 10:43:39 -07:00
Michael Scire
93d83c5bb9 ams: initial support for 9.0.0 2019-09-14 10:43:39 -07:00
Michael Scire
6ee8720028 boot: fix pinmux init off-by-one 2019-08-29 00:18:40 -07:00
Michael Scire
600d68bd1a ams_mitm: fix bis key generation for newer hardware 2019-08-29 00:14:23 -07:00
hexkyz
0c3a294cbe Minor information update regarding previously unknown mysteries 2019-08-22 20:52:40 +01:00
716 changed files with 28634 additions and 24540 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
common/defaults/hbl_html/accessible-urls/accessible-urls.txt text eol=lf

View File

@@ -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)

View File

@@ -7,11 +7,13 @@ stage2_entrypoint = 0xF0000000
[exosphere] [exosphere]
; Note: Disabling debugmode will cause parts of ams.tma to not work, in the future. ; Note: Disabling debugmode will cause parts of ams.tma to not work, in the future.
debugmode = 1 debugmode = 1
debugmode_user = 0 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

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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).

View File

@@ -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

View File

@@ -1,21 +1,21 @@
# emuMMC # emuMMC
*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
**This allows loading eMMC from SD or even SD from eMMC** **This allows loading eMMC from SD or even SD from eMMC**
* On the fly hooking / patching, fully self-infesting * On the fly hooking / patching, fully self-infesting
**Only one payload required for all versions!** **Only one payload required for all versions!**
* File-based SDMMC backend support (from SD) * File-based SDMMC backend support (from SD)
**This allows loading eMMC images from hekate-backups (split or not)** **This allows loading eMMC images from hekate-backups (split or not)**
* SDMMC device based sector offset (*currently eMMC only*) * SDMMC device based sector offset (*currently eMMC only*)
**Raw partition support for eMMC from SD with less performance overhead** **Raw partition support for eMMC from SD with less performance overhead**
* Full support for `/Nintendo` folder redirection to a arbitrary path * Full support for `/Nintendo` folder redirection to a arbitrary path
**No 8 char length restriction!** **No 8 char length restriction!**
* exosphere based context configuration * exosphere based context configuration
**This includes full support for multiple emuMMC images** **This includes full support for multiple emuMMC images**
## Compiling ## Compiling

View File

@@ -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);
} }

View File

@@ -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,
}; };

View 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__

View 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__

View 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__

View 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__

View File

@@ -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
} }

View File

@@ -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);

View File

@@ -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

View File

@@ -13,7 +13,7 @@
* 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 <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
@@ -64,7 +64,7 @@ void setup_dram_magic_numbers(void) {
void bootup_misc_mmio(void) { void bootup_misc_mmio(void) {
/* Initialize Fuse registers. */ /* Initialize Fuse registers. */
fuse_init(); fuse_init();
/* Verify Security Engine sanity. */ /* Verify Security Engine sanity. */
se_set_in_context_save_mode(false); se_set_in_context_save_mode(false);
/* TODO: se_verify_keys_unreadable(); */ /* TODO: se_verify_keys_unreadable(); */
@@ -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;
@@ -111,7 +114,7 @@ void bootup_misc_mmio(void) {
MAKE_MC_REG(MC_SECURITY_CFG1) = 0; MAKE_MC_REG(MC_SECURITY_CFG1) = 0;
MAKE_MC_REG(MC_SECURITY_CFG3) = 3; MAKE_MC_REG(MC_SECURITY_CFG3) = 3;
configure_default_carveouts(); configure_default_carveouts();
/* Mark registers secure world only. */ /* Mark registers secure world only. */
if (exosphere_get_target_firmware() == ATMOSPHERE_TARGET_FIRMWARE_100) { if (exosphere_get_target_firmware() == ATMOSPHERE_TARGET_FIRMWARE_100) {
APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0 = APB_SSER0_SATA_AUX | APB_SSER0_DTV | APB_SSER0_QSPI | APB_SSER0_SATA | APB_SSER0_LA; APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0 = APB_SSER0_SATA_AUX | APB_SSER0_DTV | APB_SSER0_QSPI | APB_SSER0_SATA | APB_SSER0_LA;
@@ -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;
@@ -176,7 +177,7 @@ void bootup_misc_mmio(void) {
(void)MAKE_MC_REG(MC_SMMU_TLB_CONFIG); (void)MAKE_MC_REG(MC_SMMU_TLB_CONFIG);
MAKE_MC_REG(MC_SMMU_CONFIG) = 1; /* Enable SMMU. */ MAKE_MC_REG(MC_SMMU_CONFIG) = 1; /* Enable SMMU. */
(void)MAKE_MC_REG(MC_SMMU_TLB_CONFIG); (void)MAKE_MC_REG(MC_SMMU_TLB_CONFIG);
/* Clear RESET Vector, setup CPU Secure Boot RESET Vectors. */ /* Clear RESET Vector, setup CPU Secure Boot RESET Vectors. */
uint32_t reset_vec; uint32_t reset_vec;
if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_500) { if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_500) {
@@ -204,7 +205,7 @@ void bootup_misc_mmio(void) {
intr_set_enabled(INTERRUPT_ID_SECURITY_ENGINE, 1); intr_set_enabled(INTERRUPT_ID_SECURITY_ENGINE, 1);
intr_set_cpu_mask(INTERRUPT_ID_SECURITY_ENGINE, 8); intr_set_cpu_mask(INTERRUPT_ID_SECURITY_ENGINE, 8);
intr_set_edge_level(INTERRUPT_ID_SECURITY_ENGINE, 0); intr_set_edge_level(INTERRUPT_ID_SECURITY_ENGINE, 0);
if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) { if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) {
intr_set_priority(INTERRUPT_ID_ACTIVITY_MONITOR_4X, 0); intr_set_priority(INTERRUPT_ID_ACTIVITY_MONITOR_4X, 0);
intr_set_group(INTERRUPT_ID_ACTIVITY_MONITOR_4X, 0); intr_set_group(INTERRUPT_ID_ACTIVITY_MONITOR_4X, 0);
@@ -218,7 +219,7 @@ void bootup_misc_mmio(void) {
uart_config(UART_A); uart_config(UART_A);
clkrst_reboot(CARDEVICE_UARTA); clkrst_reboot(CARDEVICE_UARTA);
uart_init(UART_A, 115200); uart_init(UART_A, 115200);
intr_register_handler(INTERRUPT_ID_SECURITY_ENGINE, se_operation_completed); intr_register_handler(INTERRUPT_ID_SECURITY_ENGINE, se_operation_completed);
if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) { if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) {
intr_register_handler(INTERRUPT_ID_ACTIVITY_MONITOR_4X, actmon_interrupt_handler); intr_register_handler(INTERRUPT_ID_ACTIVITY_MONITOR_4X, actmon_interrupt_handler);

View File

@@ -13,7 +13,7 @@
* 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 <stdint.h> #include <stdint.h>
#include <atmosphere/version.h> #include <atmosphere/version.h>
@@ -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) {
@@ -63,14 +64,14 @@ uint32_t configitem_set(bool privileged, ConfigItem item, uint64_t value) {
/* Set SVC handler to jump to reboot stub in IRAM. */ /* Set SVC handler to jump to reboot stub in IRAM. */
MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x520ull) = 0x4003F000; MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x520ull) = 0x4003F000;
MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x53Cull) = 0x6000F208; MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x53Cull) = 0x6000F208;
/* Copy reboot stub payload. */ /* Copy reboot stub payload. */
ams_map_irampage(0x4003F000); ams_map_irampage(0x4003F000);
for (unsigned int i = 0; i < rebootstub_bin_size; i += 4) { for (unsigned int i = 0; i < rebootstub_bin_size; i += 4) {
MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_AMS_IRAM_PAGE) + i) = read32le(rebootstub_bin, i); MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_AMS_IRAM_PAGE) + i) = read32le(rebootstub_bin, i);
} }
ams_unmap_irampage(); ams_unmap_irampage();
/* Ensure stub is flushed. */ /* Ensure stub is flushed. */
flush_dcache_all(); flush_dcache_all();
break; break;
@@ -95,7 +96,7 @@ uint32_t configitem_set(bool privileged, ConfigItem item, uint64_t value) {
/* Set SVC handler to jump to reboot stub in IRAM. */ /* Set SVC handler to jump to reboot stub in IRAM. */
MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x520ull) = 0x4003F000; MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x520ull) = 0x4003F000;
MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x53Cull) = 0x6000F208; MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x53Cull) = 0x6000F208;
/* Copy reboot stub payload. */ /* Copy reboot stub payload. */
ams_map_irampage(0x4003F000); ams_map_irampage(0x4003F000);
for (unsigned int i = 0; i < rebootstub_bin_size; i += 4) { for (unsigned int i = 0; i < rebootstub_bin_size; i += 4) {
@@ -104,7 +105,7 @@ uint32_t configitem_set(bool privileged, ConfigItem item, uint64_t value) {
/* Tell rebootstub to shut down. */ /* Tell rebootstub to shut down. */
MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_AMS_IRAM_PAGE) + 0x10) = 0x0; MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_AMS_IRAM_PAGE) + 0x10) = 0x0;
ams_unmap_irampage(); ams_unmap_irampage();
MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x400ull) = 0x10; MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x400ull) = 0x10;
while (1) { } while (1) { }
} }
@@ -112,7 +113,7 @@ uint32_t configitem_set(bool privileged, ConfigItem item, uint64_t value) {
default: default:
return 2; return 2;
} }
return 0; return 0;
} }
@@ -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;
@@ -262,7 +271,7 @@ uint32_t configitem_get(bool privileged, ConfigItem item, uint64_t *p_outvalue)
break; break;
case CONFIGITEM_EXOSPHERE_VERSION: case CONFIGITEM_EXOSPHERE_VERSION:
/* UNOFFICIAL: Gets information about the current exosphere version. */ /* UNOFFICIAL: Gets information about the current exosphere version. */
*p_outvalue = ((uint64_t)(ATMOSPHERE_RELEASE_VERSION_MAJOR & 0xFF) << 32ull) | *p_outvalue = ((uint64_t)(ATMOSPHERE_RELEASE_VERSION_MAJOR & 0xFF) << 32ull) |
((uint64_t)(ATMOSPHERE_RELEASE_VERSION_MINOR & 0xFF) << 24ull) | ((uint64_t)(ATMOSPHERE_RELEASE_VERSION_MINOR & 0xFF) << 24ull) |
((uint64_t)(ATMOSPHERE_RELEASE_VERSION_MICRO & 0xFF) << 16ull) | ((uint64_t)(ATMOSPHERE_RELEASE_VERSION_MICRO & 0xFF) << 16ull) |
((uint64_t)(exosphere_get_target_firmware() & 0xFF) << 8ull) | ((uint64_t)(exosphere_get_target_firmware() & 0xFF) << 8ull) |

View File

@@ -13,7 +13,7 @@
* 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/>.
*/ */
#ifndef EXOSPHERE_CFG_ITEM_H #ifndef EXOSPHERE_CFG_ITEM_H
#define EXOSPHERE_CFG_ITEM_H #define EXOSPHERE_CFG_ITEM_H
@@ -38,7 +38,7 @@ typedef enum {
CONFIGITEM_NEWHARDWARETYPE_5X = 15, CONFIGITEM_NEWHARDWARETYPE_5X = 15,
CONFIGITEM_NEWKEYGENERATION_5X = 16, CONFIGITEM_NEWKEYGENERATION_5X = 16,
CONFIGITEM_PACKAGE2HASH_5X = 17, CONFIGITEM_PACKAGE2HASH_5X = 17,
/* These are unofficial, for usage by Exosphere. */ /* These are unofficial, for usage by Exosphere. */
CONFIGITEM_EXOSPHERE_VERSION = 65000, CONFIGITEM_EXOSPHERE_VERSION = 65000,
CONFIGITEM_NEEDS_REBOOT = 65001, CONFIGITEM_NEEDS_REBOOT = 65001,
@@ -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);

View File

@@ -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();

View File

@@ -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);

View File

@@ -13,7 +13,7 @@
* 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 "i2c.h" #include "i2c.h"
#include "utils.h" #include "utils.h"
#include "timers.h" #include "timers.h"
@@ -32,7 +32,7 @@ bool i2c_read(volatile tegra_i2c_t *regs, uint8_t device, void *dst, size_t dst_
/* Configure I2C pinmux. */ /* Configure I2C pinmux. */
void i2c_config(I2CDevice id) { void i2c_config(I2CDevice id) {
volatile tegra_pinmux_t *pinmux = pinmux_get_regs(); volatile tegra_pinmux_t *pinmux = pinmux_get_regs();
switch (id) { switch (id) {
case I2C_1: case I2C_1:
pinmux->gen1_i2c_scl = PINMUX_INPUT; pinmux->gen1_i2c_scl = PINMUX_INPUT;
@@ -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;
} }
@@ -88,7 +88,7 @@ void i2c_init(I2CDevice id) {
regs->I2C_INTERRUPT_STATUS_REGISTER_0 = int_status; regs->I2C_INTERRUPT_STATUS_REGISTER_0 = int_status;
} }
/* Sets a bit in a PMIC register over I2C during CPU shutdown. */ /* Sets a bit in a PMIC register over I2C during CPU shutdown. */
void i2c_send_pmic_cpu_shutdown_cmd(void) { void i2c_send_pmic_cpu_shutdown_cmd(void) {
uint32_t val = 0; uint32_t val = 0;
/* PMIC == Device 4:3C. */ /* PMIC == Device 4:3C. */
@@ -162,7 +162,7 @@ void i2c_load_config(volatile tegra_i2c_t *regs) {
bool i2c_query(I2CDevice id, uint8_t device, uint8_t r, void *dst, size_t dst_size) { bool i2c_query(I2CDevice id, uint8_t device, uint8_t r, void *dst, size_t dst_size) {
volatile tegra_i2c_t *regs = i2c_get_registers_from_id(id); volatile tegra_i2c_t *regs = i2c_get_registers_from_id(id);
uint32_t val = r; uint32_t val = r;
/* Write single byte register ID to device. */ /* Write single byte register ID to device. */
if (!i2c_write(regs, device, &val, 1)) { if (!i2c_write(regs, device, &val, 1)) {
return false; return false;
@@ -171,12 +171,12 @@ bool i2c_query(I2CDevice id, uint8_t device, uint8_t r, void *dst, size_t dst_si
if (dst_size > 4) { if (dst_size > 4) {
return false; return false;
} }
return i2c_read(regs, device, dst, dst_size); return i2c_read(regs, device, dst, dst_size);
} }
/* Writes a value to a register over I2C. */ /* Writes a value to a register over I2C. */
bool i2c_send(I2CDevice id, uint8_t device, uint8_t r, void *src, size_t src_size) { bool i2c_send(I2CDevice id, uint8_t device, uint8_t r, void *src, size_t src_size) {
uint32_t val = r; uint32_t val = r;
if (src_size == 0) { if (src_size == 0) {
return true; return true;
@@ -240,7 +240,7 @@ bool i2c_read(volatile tegra_i2c_t *regs, uint8_t device, void *dst, size_t dst_
while (regs->I2C_I2C_STATUS_0 & 0x100) { while (regs->I2C_I2C_STATUS_0 & 0x100) {
/* Wait until not busy. */ /* Wait until not busy. */
} }
/* Ensure success. */ /* Ensure success. */
if ((regs->I2C_I2C_STATUS_0 & 0xF) != 0) { if ((regs->I2C_I2C_STATUS_0 & 0xF) != 0) {
return false; return false;

View File

@@ -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) {

View File

@@ -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)

View File

@@ -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

View File

@@ -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;
} }
} }

View File

@@ -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 {

View File

@@ -13,7 +13,7 @@
* 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 <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
@@ -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();
} }
@@ -132,7 +133,7 @@ static void setup_bpmp_sc7_firmware(void) {
BPMP_VECTOR_UNK = 0x40003004; /* Reboot. */ BPMP_VECTOR_UNK = 0x40003004; /* Reboot. */
BPMP_VECTOR_IRQ = 0x40003004; /* Reboot. */ BPMP_VECTOR_IRQ = 0x40003004; /* Reboot. */
BPMP_VECTOR_FIQ = 0x40003004; /* Reboot. */ BPMP_VECTOR_FIQ = 0x40003004; /* Reboot. */
/* Hold the BPMP in reset. */ /* Hold the BPMP in reset. */
MAKE_CAR_REG(0x300) = 2; MAKE_CAR_REG(0x300) = 2;
@@ -141,7 +142,7 @@ static void setup_bpmp_sc7_firmware(void) {
for (unsigned int i = 0; i < sc7fw_bin_size; i += 4) { for (unsigned int i = 0; i < sc7fw_bin_size; i += 4) {
write32le(lp0_entry_code, i, read32le(sc7fw_bin, i)); write32le(lp0_entry_code, i, read32le(sc7fw_bin, i));
} }
flush_dcache_range(lp0_entry_code, lp0_entry_code + sc7fw_bin_size); flush_dcache_range(lp0_entry_code, lp0_entry_code + sc7fw_bin_size);
/* Take the BPMP out of reset. */ /* Take the BPMP out of reset. */
@@ -181,7 +182,7 @@ static void save_tzram_state(void) {
flush_dcache_range(tzram_encryption_dst, tzram_encryption_dst + LP0_TZRAM_SAVE_SIZE); flush_dcache_range(tzram_encryption_dst, tzram_encryption_dst + LP0_TZRAM_SAVE_SIZE);
flush_dcache_range(tzram_encryption_src, tzram_encryption_src + LP0_TZRAM_SAVE_SIZE); flush_dcache_range(tzram_encryption_src, tzram_encryption_src + LP0_TZRAM_SAVE_SIZE);
/* Use the all-zero cmac buffer as an IV. */ /* Use the all-zero cmac buffer as an IV. */
se_aes_256_cbc_encrypt(KEYSLOT_SWITCH_LP0TZRAMKEY, tzram_encryption_dst, LP0_TZRAM_SAVE_SIZE, tzram_encryption_src, LP0_TZRAM_SAVE_SIZE, tzram_cmac); se_aes_256_cbc_encrypt(KEYSLOT_SWITCH_LP0TZRAMKEY, tzram_encryption_dst, LP0_TZRAM_SAVE_SIZE, tzram_encryption_src, LP0_TZRAM_SAVE_SIZE, tzram_cmac);
flush_dcache_range(tzram_encryption_dst, tzram_encryption_dst + LP0_TZRAM_SAVE_SIZE); flush_dcache_range(tzram_encryption_dst, tzram_encryption_dst + LP0_TZRAM_SAVE_SIZE);
@@ -189,12 +190,12 @@ static void save_tzram_state(void) {
for (unsigned int i = 0; i < LP0_TZRAM_SAVE_SIZE; i += 4) { for (unsigned int i = 0; i < LP0_TZRAM_SAVE_SIZE; i += 4) {
write32le(tzram_store_address, i, read32le(tzram_encryption_dst, i)); write32le(tzram_store_address, i, read32le(tzram_encryption_dst, i));
} }
flush_dcache_range(tzram_store_address, tzram_store_address + LP0_TZRAM_SAVE_SIZE); flush_dcache_range(tzram_store_address, tzram_store_address + LP0_TZRAM_SAVE_SIZE);
/* Compute CMAC. */ /* Compute CMAC. */
se_compute_aes_256_cmac(KEYSLOT_SWITCH_LP0TZRAMKEY, tzram_cmac, sizeof(tzram_cmac), tzram_encryption_src, LP0_TZRAM_SAVE_SIZE); se_compute_aes_256_cmac(KEYSLOT_SWITCH_LP0TZRAMKEY, tzram_cmac, sizeof(tzram_cmac), tzram_encryption_src, LP0_TZRAM_SAVE_SIZE);
/* Write CMAC, lock registers. */ /* Write CMAC, lock registers. */
APBDEV_PMC_SECURE_SCRATCH112_0 = tzram_cmac[0]; APBDEV_PMC_SECURE_SCRATCH112_0 = tzram_cmac[0];
APBDEV_PMC_SECURE_SCRATCH113_0 = tzram_cmac[1]; APBDEV_PMC_SECURE_SCRATCH113_0 = tzram_cmac[1];
@@ -240,7 +241,7 @@ void save_se_and_power_down_cpu(void) {
/* Save context for warmboot to restore. */ /* Save context for warmboot to restore. */
save_tzram_state(); save_tzram_state();
save_se_state(); save_se_state();
/* Patch the bootrom to disable warmboot signature checks. */ /* Patch the bootrom to disable warmboot signature checks. */
MAKE_REG32(PMC_BASE + 0x118) = 0x2202E012; MAKE_REG32(PMC_BASE + 0x118) = 0x2202E012;
MAKE_REG32(PMC_BASE + 0x11C) = 0x6001DC28; MAKE_REG32(PMC_BASE + 0x11C) = 0x6001DC28;
@@ -248,14 +249,14 @@ void save_se_and_power_down_cpu(void) {
if (!configitem_is_retail()) { if (!configitem_is_retail()) {
uart_send(UART_A, "OYASUMI", 8); uart_send(UART_A, "OYASUMI", 8);
} }
finalize_powerdown(); finalize_powerdown();
} }
uint32_t cpu_suspend(uint64_t power_state, uint64_t entrypoint, uint64_t argument) { uint32_t cpu_suspend(uint64_t power_state, uint64_t entrypoint, uint64_t argument) {
/* TODO: 6.0.0 introduces heavy deja vu mitigations. */ /* TODO: 6.0.0 introduces heavy deja vu mitigations. */
/* Exosphere may want to implement these. */ /* Exosphere may want to implement these. */
/* Ensure SMC call is to enter deep sleep. */ /* Ensure SMC call is to enter deep sleep. */
if ((power_state & 0x17FFF) != 0x1001B) { if ((power_state & 0x17FFF) != 0x1001B) {
return 0xFFFFFFFD; return 0xFFFFFFFD;
@@ -285,7 +286,7 @@ uint32_t cpu_suspend(uint64_t power_state, uint64_t entrypoint, uint64_t argumen
/* Prepare the current core for sleep. */ /* Prepare the current core for sleep. */
configure_flow_regs_for_sleep(); configure_flow_regs_for_sleep();
/* Save core context. */ /* Save core context. */
set_core_entrypoint_and_argument(get_core_id(), entrypoint, argument); set_core_entrypoint_and_argument(get_core_id(), entrypoint, argument);
save_current_core_context(); save_current_core_context();

View File

@@ -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;

View File

@@ -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;
} }

View File

@@ -13,7 +13,7 @@
* 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 <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
#include "utils.h" #include "utils.h"
@@ -29,7 +29,7 @@ __attribute__ ((noreturn)) void panic(uint32_t code) {
if (APBDEV_PMC_SCRATCH200_0 == 0) { if (APBDEV_PMC_SCRATCH200_0 == 0) {
APBDEV_PMC_SCRATCH200_0 = code; APBDEV_PMC_SCRATCH200_0 = code;
} }
/* // Uncomment for Debugging. /* // Uncomment for Debugging.
uint64_t temp_reg; uint64_t temp_reg;
MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_DEBUG_IRAM)) = APBDEV_PMC_SCRATCH200_0; MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_DEBUG_IRAM)) = APBDEV_PMC_SCRATCH200_0;
@@ -38,8 +38,8 @@ __attribute__ ((noreturn)) void panic(uint32_t code) {
SAVE_SYSREG64(FAR_EL3, 0x20); SAVE_SYSREG64(FAR_EL3, 0x20);
MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x450ull) = 0x2; MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x450ull) = 0x2;
MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x400ull) = 0x10; */ MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x400ull) = 0x10; */
/* TODO: Custom Panic Driver, which displays to screen without rebooting. */ /* TODO: Custom Panic Driver, which displays to screen without rebooting. */
/* For now, just use NX BOOTLOADER's panic. */ /* For now, just use NX BOOTLOADER's panic. */
fuse_disable_programming(); fuse_disable_programming();
@@ -50,7 +50,7 @@ __attribute__ ((noreturn)) void panic(uint32_t code) {
__attribute__ ((noreturn)) void generic_panic(void) { __attribute__ ((noreturn)) void generic_panic(void) {
/* //Uncomment for Debugging. /* //Uncomment for Debugging.
uint64_t temp_reg; uint64_t temp_reg;
do { __asm__ __volatile__ ("mov %0, LR" : "=r"(temp_reg) :: "memory"); } while (false); do { __asm__ __volatile__ ("mov %0, LR" : "=r"(temp_reg) :: "memory"); } while (false);
MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_DEBUG_IRAM) + 0x28) = (uint32_t)((temp_reg >> 0) & 0xFFFFFFFFULL); MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_DEBUG_IRAM) + 0x28) = (uint32_t)((temp_reg >> 0) & 0xFFFFFFFFULL);
MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_DEBUG_IRAM) + 0x28 + 4) = (uint32_t)((temp_reg >> 32) & 0xFFFFFFFFULL); MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_DEBUG_IRAM) + 0x28 + 4) = (uint32_t)((temp_reg >> 32) & 0xFFFFFFFFULL);

View File

@@ -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

View File

@@ -13,7 +13,7 @@
* 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 "utils.h" #include "utils.h"
#include "exception_handlers.h" #include "exception_handlers.h"
#include "panic.h" #include "panic.h"
@@ -37,35 +37,40 @@ 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) {
fatal_error("Unexpected magic in BCT.ini!\n"); fatal_error("Unexpected magic in BCT.ini!\n");
} }
/* Return pointer to first line of the ini. */ /* Return pointer to first line of the ini. */
const char *bct0 = g_bct0_buffer; const char *bct0 = g_bct0_buffer;
while (*bct0 && *bct0 != '\n') { while (*bct0 && *bct0 != '\n') {
bct0++; bct0++;
} }
if (!bct0) { if (!bct0) {
fatal_error("BCT.ini has no newline!\n"); fatal_error("BCT.ini has no newline!\n");
} }
return bct0; return bct0;
} }
@@ -87,7 +92,7 @@ static int config_ini_handler(void *user, const char *section, const char *name,
static void setup_display(void) { static void setup_display(void) {
g_framebuffer = (void *)0xC0000000; g_framebuffer = (void *)0xC0000000;
/* Zero-fill the framebuffer and register it as printk provider. */ /* Zero-fill the framebuffer and register it as printk provider. */
video_init(g_framebuffer); video_init(g_framebuffer);
@@ -105,7 +110,7 @@ static void setup_display(void) {
static void cleanup_display(void) { static void cleanup_display(void) {
/* Turn off the backlight. */ /* Turn off the backlight. */
display_backlight(false); display_backlight(false);
/* Terminate the display. */ /* Terminate the display. */
display_end(); display_end();
} }
@@ -116,7 +121,7 @@ static void setup_env(void) {
/* Set up the exception handlers. */ /* Set up the exception handlers. */
setup_exception_handlers(); setup_exception_handlers();
/* Mount the SD card. */ /* Mount the SD card. */
mount_sd(); mount_sd();
} }
@@ -137,13 +142,13 @@ int main(void) {
stage2_args_t *stage2_args; stage2_args_t *stage2_args;
uint32_t stage2_version = 0; uint32_t stage2_version = 0;
ScreenLogLevel log_level = SCREEN_LOG_LEVEL_NONE; ScreenLogLevel log_level = SCREEN_LOG_LEVEL_NONE;
/* Initialize the boot environment. */ /* Initialize the boot environment. */
setup_env(); setup_env();
/* Check for panics. */ /* Check for panics. */
check_and_display_panic(); check_and_display_panic();
/* Load the BCT0 configuration ini off of the SD. */ /* Load the BCT0 configuration ini off of the SD. */
bct0 = load_config(); bct0 = load_config();
@@ -151,19 +156,19 @@ int main(void) {
if (ini_parse_string(bct0, config_ini_handler, &log_level) < 0) { if (ini_parse_string(bct0, config_ini_handler, &log_level) < 0) {
fatal_error("Failed to parse BCT.ini!\n"); fatal_error("Failed to parse BCT.ini!\n");
} }
/* Override the global logging level. */ /* Override the global logging level. */
log_set_log_level(log_level); log_set_log_level(log_level);
if (log_level != SCREEN_LOG_LEVEL_NONE) { if (log_level != SCREEN_LOG_LEVEL_NONE) {
/* Initialize the display for debugging. */ /* Initialize the display for debugging. */
setup_display(); setup_display();
} }
/* Say hello. */ /* Say hello. */
print(SCREEN_LOG_LEVEL_DEBUG | SCREEN_LOG_LEVEL_NO_PREFIX, "Welcome to Atmosph\xe8re Fus\xe9" "e!\n"); print(SCREEN_LOG_LEVEL_DEBUG | SCREEN_LOG_LEVEL_NO_PREFIX, "Welcome to Atmosph\xe8re Fus\xe9" "e!\n");
print(SCREEN_LOG_LEVEL_DEBUG, "Using color linear framebuffer at 0x%p!\n", g_framebuffer); print(SCREEN_LOG_LEVEL_DEBUG, "Using color linear framebuffer at 0x%p!\n", g_framebuffer);
/* Load the loader payload into DRAM. */ /* Load the loader payload into DRAM. */
load_stage2(bct0); load_stage2(bct0);
@@ -175,14 +180,14 @@ int main(void) {
memcpy(&stage2_args->log_level, &log_level, sizeof(log_level)); memcpy(&stage2_args->log_level, &log_level, sizeof(log_level));
strcpy(stage2_args->bct0, bct0); strcpy(stage2_args->bct0, bct0);
g_chainloader_argc = 2; g_chainloader_argc = 2;
/* Terminate the boot environment. */ /* Terminate the boot environment. */
cleanup_env(); cleanup_env();
if (log_level != SCREEN_LOG_LEVEL_NONE) { if (log_level != SCREEN_LOG_LEVEL_NONE) {
/* Wait a while for debugging. */ /* Wait a while for debugging. */
mdelay(1000); mdelay(1000);
/* Terminate the display for debugging. */ /* Terminate the display for debugging. */
cleanup_display(); cleanup_display();
} }

View File

@@ -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

View File

@@ -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;

View File

@@ -13,7 +13,7 @@
* 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/>.
*/ */
#ifndef FUSEE_EXOSPHERE_CONFIG_H #ifndef FUSEE_EXOSPHERE_CONFIG_H
#define FUSEE_EXOSPHERE_CONFIG_H #define FUSEE_EXOSPHERE_CONFIG_H
@@ -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

View File

@@ -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) {

View File

@@ -14,7 +14,7 @@
* 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 <string.h> #include <string.h>
#include "utils.h" #include "utils.h"
#include "se.h" #include "se.h"
@@ -102,7 +102,7 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(100, proc_id_recv)[] = {0xA9BF
*/ */
static const uint8_t MAKE_KERNEL_PATTERN_NAME(200, proc_id_send)[] = {0x48, 0x31, 0x41, 0xF9, 0xE9, 0x03, 0x18, 0x2A, 0x29, 0xF5, 0x7E, 0xD3, 0xC8, 0x6A, 0x29, 0xF8}; static const uint8_t MAKE_KERNEL_PATTERN_NAME(200, proc_id_send)[] = {0x48, 0x31, 0x41, 0xF9, 0xE9, 0x03, 0x18, 0x2A, 0x29, 0xF5, 0x7E, 0xD3, 0xC8, 0x6A, 0x29, 0xF8};
static const instruction_t MAKE_KERNEL_PATCH_NAME(200, proc_id_send)[] = {0xA9BF2FEA, 0x2A1803EB, 0xD37EF56B, 0xF86B6B8B, 0x92FFFFE9, 0x8A090168, 0xD2FFFFE9, 0x8A09016B, 0xD2FFFFC9, 0xEB09017F, 0x54000040, 0xF9413148, 0xA8C12FEA}; static const instruction_t MAKE_KERNEL_PATCH_NAME(200, proc_id_send)[] = {0xA9BF2FEA, 0x2A1803EB, 0xD37EF56B, 0xF86B6B8B, 0x92FFFFE9, 0x8A090168, 0xD2FFFFE9, 0x8A09016B, 0xD2FFFFC9, 0xEB09017F, 0x54000040, 0xF9413148, 0xA8C12FEA};
/* /*
stp x10, x11, [sp, #-0x10]! stp x10, x11, [sp, #-0x10]!
mov w10, w15 mov w10, w15
lsl x10, x10, #2 lsl x10, x10, #2
@@ -138,7 +138,7 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(200, proc_id_recv)[] = {0xA9BF
*/ */
static const uint8_t MAKE_KERNEL_PATTERN_NAME(300, proc_id_send)[] = {0x48, 0x55, 0x41, 0xF9, 0xE9, 0x03, 0x18, 0x2A, 0x29, 0xF5, 0x7E, 0xD3, 0xC8, 0x6A, 0x29, 0xF8}; static const uint8_t MAKE_KERNEL_PATTERN_NAME(300, proc_id_send)[] = {0x48, 0x55, 0x41, 0xF9, 0xE9, 0x03, 0x18, 0x2A, 0x29, 0xF5, 0x7E, 0xD3, 0xC8, 0x6A, 0x29, 0xF8};
static const instruction_t MAKE_KERNEL_PATCH_NAME(300, proc_id_send)[] = {0xA9BF2FEA, 0x2A1803EB, 0xD37EF56B, 0xF86B6B8B, 0x92FFFFE9, 0x8A090168, 0xD2FFFFE9, 0x8A09016B, 0xD2FFFFC9, 0xEB09017F, 0x54000040, 0xF9415548, 0xA8C12FEA}; static const instruction_t MAKE_KERNEL_PATCH_NAME(300, proc_id_send)[] = {0xA9BF2FEA, 0x2A1803EB, 0xD37EF56B, 0xF86B6B8B, 0x92FFFFE9, 0x8A090168, 0xD2FFFFE9, 0x8A09016B, 0xD2FFFFC9, 0xEB09017F, 0x54000040, 0xF9415548, 0xA8C12FEA};
/* /*
stp x10, x11, [sp, #-0x10]! stp x10, x11, [sp, #-0x10]!
mov w10, w15 mov w10, w15
lsl x10, x10, #2 lsl x10, x10, #2
@@ -174,7 +174,7 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(300, proc_id_recv)[] = {0xA9BF
*/ */
static const uint8_t MAKE_KERNEL_PATTERN_NAME(302, proc_id_send)[] = {0x48, 0x55, 0x41, 0xF9, 0xE9, 0x03, 0x18, 0x2A, 0x29, 0xF5, 0x7E, 0xD3, 0xC8, 0x6A, 0x29, 0xF8}; static const uint8_t MAKE_KERNEL_PATTERN_NAME(302, proc_id_send)[] = {0x48, 0x55, 0x41, 0xF9, 0xE9, 0x03, 0x18, 0x2A, 0x29, 0xF5, 0x7E, 0xD3, 0xC8, 0x6A, 0x29, 0xF8};
static const instruction_t MAKE_KERNEL_PATCH_NAME(302, proc_id_send)[] = {0xA9BF2FEA, 0x2A1803EB, 0xD37EF56B, 0xF86B6B8B, 0x92FFFFE9, 0x8A090168, 0xD2FFFFE9, 0x8A09016B, 0xD2FFFFC9, 0xEB09017F, 0x54000040, 0xF9415548, 0xA8C12FEA}; static const instruction_t MAKE_KERNEL_PATCH_NAME(302, proc_id_send)[] = {0xA9BF2FEA, 0x2A1803EB, 0xD37EF56B, 0xF86B6B8B, 0x92FFFFE9, 0x8A090168, 0xD2FFFFE9, 0x8A09016B, 0xD2FFFFC9, 0xEB09017F, 0x54000040, 0xF9415548, 0xA8C12FEA};
/* /*
stp x10, x11, [sp, #-0x10]! stp x10, x11, [sp, #-0x10]!
mov w10, w15 mov w10, w15
lsl x10, x10, #2 lsl x10, x10, #2
@@ -193,7 +193,7 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(302, proc_id_send)[] = {0xA9BF
*/ */
static const uint8_t MAKE_KERNEL_PATTERN_NAME(302, proc_id_recv)[] = {0x08, 0x55, 0x41, 0xF9, 0xE9, 0x03, 0x0F, 0x2A, 0x29, 0xF5, 0x7E, 0xD3, 0x48, 0x6B, 0x29, 0xF8}; static const uint8_t MAKE_KERNEL_PATTERN_NAME(302, proc_id_recv)[] = {0x08, 0x55, 0x41, 0xF9, 0xE9, 0x03, 0x0F, 0x2A, 0x29, 0xF5, 0x7E, 0xD3, 0x48, 0x6B, 0x29, 0xF8};
static const instruction_t MAKE_KERNEL_PATCH_NAME(302, proc_id_recv)[] = {0xA9BF2FEA, 0x2A0F03EA, 0xD37EF54A, 0xF9405FEB, 0xF86A696A, 0xF9407BEB, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000040, 0xF9415568, 0xA8C12FEA}; static const instruction_t MAKE_KERNEL_PATCH_NAME(302, proc_id_recv)[] = {0xA9BF2FEA, 0x2A0F03EA, 0xD37EF54A, 0xF9405FEB, 0xF86A696A, 0xF9407BEB, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000040, 0xF9415568, 0xA8C12FEA};
/* /*
mov w10, w23 mov w10, w23
lsl x10, x10, #2 lsl x10, x10, #2
ldr x10, [x28, x10] ldr x10, [x28, x10]
@@ -210,7 +210,7 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(302, proc_id_recv)[] = {0xA9BF
*/ */
static const uint8_t MAKE_KERNEL_PATTERN_NAME(400, proc_id_send)[] = {0xEA, 0x53, 0x40, 0xF9, 0x48, 0x59, 0x41, 0xF9, 0xE9, 0x03, 0x17, 0x2A, 0x29, 0xF5, 0x7E, 0xD3}; static const uint8_t MAKE_KERNEL_PATTERN_NAME(400, proc_id_send)[] = {0xEA, 0x53, 0x40, 0xF9, 0x48, 0x59, 0x41, 0xF9, 0xE9, 0x03, 0x17, 0x2A, 0x29, 0xF5, 0x7E, 0xD3};
static const instruction_t MAKE_KERNEL_PATCH_NAME(400, proc_id_send)[] = {0x2A1703EA, 0xD37EF54A, 0xF86A6B8A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000060, 0xF94053EA, 0xF9415948, 0xF94053EA}; static const instruction_t MAKE_KERNEL_PATCH_NAME(400, proc_id_send)[] = {0x2A1703EA, 0xD37EF54A, 0xF86A6B8A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000060, 0xF94053EA, 0xF9415948, 0xF94053EA};
/* /*
ldr x13, [sp,#0x70] ldr x13, [sp,#0x70]
mov w10, w14 mov w10, w14
lsl x10, x10, #2 lsl x10, x10, #2
@@ -244,7 +244,7 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(400, proc_id_recv)[] = {0xF940
*/ */
static const uint8_t MAKE_KERNEL_PATTERN_NAME(500, proc_id_send)[] = {0xEA, 0x43, 0x40, 0xF9, 0x48, 0x59, 0x41, 0xF9, 0xE9, 0x03, 0x17, 0x2A, 0x29, 0xF5, 0x7E, 0xD3}; static const uint8_t MAKE_KERNEL_PATTERN_NAME(500, proc_id_send)[] = {0xEA, 0x43, 0x40, 0xF9, 0x48, 0x59, 0x41, 0xF9, 0xE9, 0x03, 0x17, 0x2A, 0x29, 0xF5, 0x7E, 0xD3};
static const instruction_t MAKE_KERNEL_PATCH_NAME(500, proc_id_send)[] = {0x2A1703EA, 0xD37EF54A, 0xF86A6B6A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000060, 0xF94043EA, 0xF9415948, 0xF94043EA}; static const instruction_t MAKE_KERNEL_PATCH_NAME(500, proc_id_send)[] = {0x2A1703EA, 0xD37EF54A, 0xF86A6B6A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000060, 0xF94043EA, 0xF9415948, 0xF94043EA};
/* /*
ldr x13, [sp, #0x70] ldr x13, [sp, #0x70]
mov w10, w21 mov w10, w21
lsl x10, x10, #2 lsl x10, x10, #2
@@ -257,7 +257,7 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(500, proc_id_send)[] = {0x2A17
cmp x10, x9 cmp x10, x9
beq #8 beq #8
ldr x8, [x24,#0x2b0] ldr x8, [x24,#0x2b0]
ldr x10, [sp,#0xd8] ldr x10, [sp,#0xd8]
*/ */
static const uint8_t MAKE_KERNEL_PATTERN_NAME(500, proc_id_recv)[] = {0x08, 0x5B, 0x41, 0xF9, 0xEA, 0x6F, 0x40, 0xF9, 0xE9, 0x03, 0x15, 0x2A, 0x29, 0xF5, 0x7E, 0xD3}; static const uint8_t MAKE_KERNEL_PATTERN_NAME(500, proc_id_recv)[] = {0x08, 0x5B, 0x41, 0xF9, 0xEA, 0x6F, 0x40, 0xF9, 0xE9, 0x03, 0x15, 0x2A, 0x29, 0xF5, 0x7E, 0xD3};
static const instruction_t MAKE_KERNEL_PATCH_NAME(500, proc_id_recv)[] = {0xF9403BED, 0x2A1503EA, 0xD37EF54A, 0xF86A69AA, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000040, 0xF9415B08, 0xF9406FEA}; static const instruction_t MAKE_KERNEL_PATCH_NAME(500, proc_id_recv)[] = {0xF9403BED, 0x2A1503EA, 0xD37EF54A, 0xF86A69AA, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000040, 0xF9415B08, 0xF9406FEA};
@@ -274,7 +274,7 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(500, proc_id_recv)[] = {0xF940
mov x9, #0xfffe000000000000 mov x9, #0xfffe000000000000
cmp x10, x9 cmp x10, x9
beq #0x20 beq #0x20
stp x8, x9, [sp, #-0x10]! stp x8, x9, [sp, #-0x10]!
ldr x8, [x24] ldr x8, [x24]
ldr x8, [x8, #0x38] ldr x8, [x8, #0x38]
@@ -282,13 +282,13 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(500, proc_id_recv)[] = {0xF940
blr x8 blr x8
ldp x8, x9, [sp],#0x10 ldp x8, x9, [sp],#0x10
mov x8, x0 mov x8, x0
ldp x10, x11, [sp],#0x10 ldp x10, x11, [sp],#0x10
mov x0, x8 mov x0, x8
*/ */
static const uint8_t MAKE_KERNEL_PATTERN_NAME(600, proc_id_send)[] = {0x08, 0x03, 0x40, 0xF9, 0x08, 0x1D, 0x40, 0xF9, 0xE0, 0x03, 0x18, 0xAA, 0x00, 0x01, 0x3F, 0xD6, 0xE8, 0x03, 0x15, 0x2A, 0xB5, 0x0A, 0x00, 0x11, 0x08, 0xF5, 0x7E, 0xD3}; static const uint8_t MAKE_KERNEL_PATTERN_NAME(600, proc_id_send)[] = {0x08, 0x03, 0x40, 0xF9, 0x08, 0x1D, 0x40, 0xF9, 0xE0, 0x03, 0x18, 0xAA, 0x00, 0x01, 0x3F, 0xD6, 0xE8, 0x03, 0x15, 0x2A, 0xB5, 0x0A, 0x00, 0x11, 0x08, 0xF5, 0x7E, 0xD3};
static const instruction_t MAKE_KERNEL_PATCH_NAME(600, proc_id_send)[] = {0xA9BF2FEA, 0xF94037EB, 0x2A1503EA, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF9400308, 0xF9401D08, 0xAA1803E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0}; static const instruction_t MAKE_KERNEL_PATCH_NAME(600, proc_id_send)[] = {0xA9BF2FEA, 0xF94037EB, 0x2A1503EA, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF9400308, 0xF9401D08, 0xAA1803E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0};
/* /*
stp x10, x11, [sp, #-0x10]! stp x10, x11, [sp, #-0x10]!
ldr x11, [sp, #0x80] ldr x11, [sp, #0x80]
mov w10, w21 mov w10, w21
@@ -301,7 +301,7 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(600, proc_id_send)[] = {0xA9BF
mov x9, #0xfffe000000000000 mov x9, #0xfffe000000000000
cmp x10, x9 cmp x10, x9
beq #0x20 beq #0x20
stp x8, x9, [sp, #-0x10]! stp x8, x9, [sp, #-0x10]!
ldr x8, [x24] ldr x8, [x24]
ldr x8, [x8, #0x38] ldr x8, [x8, #0x38]
@@ -309,7 +309,7 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(600, proc_id_send)[] = {0xA9BF
blr x8 blr x8
ldp x8, x9, [sp],#0x10 ldp x8, x9, [sp],#0x10
mov x8, x0 mov x8, x0
ldp x10, x11, [sp],#0x10 ldp x10, x11, [sp],#0x10
mov x0, x8 mov x0, x8
*/ */
@@ -328,7 +328,7 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(600, proc_id_recv)[] = {0xA9BF
mov x9, #0xfffe000000000000 mov x9, #0xfffe000000000000
cmp x10, x9 cmp x10, x9
beq #0x20 beq #0x20
stp x8, x9, [sp, #-0x10]! stp x8, x9, [sp, #-0x10]!
ldr x8, [x21] ldr x8, [x21]
ldr x8, [x8, #0x38] ldr x8, [x8, #0x38]
@@ -336,13 +336,13 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(600, proc_id_recv)[] = {0xA9BF
blr x8 blr x8
ldp x8, x9, [sp],#0x10 ldp x8, x9, [sp],#0x10
mov x8, x0 mov x8, x0
ldp x10, x11, [sp],#0x10 ldp x10, x11, [sp],#0x10
mov x0, x8 mov x0, x8
*/ */
static const uint8_t MAKE_KERNEL_PATTERN_NAME(700, proc_id_send)[] = {0xA8, 0x02, 0x40, 0xF9, 0x08, 0x1D, 0x40, 0xF9, 0xE0, 0x03, 0x15, 0xAA, 0x00, 0x01, 0x3F, 0xD6, 0xE8, 0x03, 0x19, 0x2A, 0x39, 0x0B, 0x00, 0x11, 0x08, 0xF5, 0x7E, 0xD3}; static const uint8_t MAKE_KERNEL_PATTERN_NAME(700, proc_id_send)[] = {0xA8, 0x02, 0x40, 0xF9, 0x08, 0x1D, 0x40, 0xF9, 0xE0, 0x03, 0x15, 0xAA, 0x00, 0x01, 0x3F, 0xD6, 0xE8, 0x03, 0x19, 0x2A, 0x39, 0x0B, 0x00, 0x11, 0x08, 0xF5, 0x7E, 0xD3};
static const instruction_t MAKE_KERNEL_PATCH_NAME(700, proc_id_send)[] = {0xA9BF2FEA, 0xF9403BEB, 0x2A1903EA, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF94002A8, 0xF9401D08, 0xAA1503E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0}; static const instruction_t MAKE_KERNEL_PATCH_NAME(700, proc_id_send)[] = {0xA9BF2FEA, 0xF9403BEB, 0x2A1903EA, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF94002A8, 0xF9401D08, 0xAA1503E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0};
/* /*
stp x10, x11, [sp, #-0x10]! stp x10, x11, [sp, #-0x10]!
ldr x11, [sp, #0x98] ldr x11, [sp, #0x98]
mov w10, w22 mov w10, w22
@@ -355,7 +355,7 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(700, proc_id_send)[] = {0xA9BF
mov x9, #0xfffe000000000000 mov x9, #0xfffe000000000000
cmp x10, x9 cmp x10, x9
beq #0x20 beq #0x20
stp x8, x9, [sp, #-0x10]! stp x8, x9, [sp, #-0x10]!
ldr x8, [x27] ldr x8, [x27]
ldr x8, [x8, #0x38] ldr x8, [x8, #0x38]
@@ -363,7 +363,7 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(700, proc_id_send)[] = {0xA9BF
blr x8 blr x8
ldp x8, x9, [sp],#0x10 ldp x8, x9, [sp],#0x10
mov x8, x0 mov x8, x0
ldp x10, x11, [sp],#0x10 ldp x10, x11, [sp],#0x10
mov x0, x8 mov x0, x8
*/ */
@@ -383,7 +383,7 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(700, proc_id_recv)[] = {0xA9BF
mov x9, #0xfffe000000000000 mov x9, #0xfffe000000000000
cmp x10, x9 cmp x10, x9
beq #0x20 beq #0x20
stp x8, x9, [sp, #-0x10]! stp x8, x9, [sp, #-0x10]!
ldr x8, [x21] ldr x8, [x21]
ldr x8, [x8, #0x38] ldr x8, [x8, #0x38]
@@ -391,13 +391,13 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(700, proc_id_recv)[] = {0xA9BF
blr x8 blr x8
ldp x8, x9, [sp],#0x10 ldp x8, x9, [sp],#0x10
mov x8, x0 mov x8, x0
ldp x10, x11, [sp],#0x10 ldp x10, x11, [sp],#0x10
mov x0, x8 mov x0, x8
*/ */
static const uint8_t MAKE_KERNEL_PATTERN_NAME(800, proc_id_send)[] = {0xA8, 0x02, 0x40, 0xF9, 0x08, 0x1D, 0x40, 0xF9, 0xE0, 0x03, 0x15, 0xAA, 0x00, 0x01, 0x3F, 0xD6, 0xE8, 0x03, 0x19, 0x2A, 0x39, 0x0B, 0x00, 0x11, 0x08, 0xF5, 0x7E, 0xD3}; static const uint8_t MAKE_KERNEL_PATTERN_NAME(800, proc_id_send)[] = {0xA8, 0x02, 0x40, 0xF9, 0x08, 0x1D, 0x40, 0xF9, 0xE0, 0x03, 0x15, 0xAA, 0x00, 0x01, 0x3F, 0xD6, 0xE8, 0x03, 0x19, 0x2A, 0x39, 0x0B, 0x00, 0x11, 0x08, 0xF5, 0x7E, 0xD3};
static const instruction_t MAKE_KERNEL_PATCH_NAME(800, proc_id_send)[] = {0xA9BF2FEA, 0xF9403BEB, 0x2A1903EA, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF94002A8, 0xF9401D08, 0xAA1503E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0}; static const instruction_t MAKE_KERNEL_PATCH_NAME(800, proc_id_send)[] = {0xA9BF2FEA, 0xF9403BEB, 0x2A1903EA, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF94002A8, 0xF9401D08, 0xAA1503E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0};
/* /*
stp x10, x11, [sp, #-0x10]! stp x10, x11, [sp, #-0x10]!
ldr x11, [sp, #0x98] ldr x11, [sp, #0x98]
mov w10, w22 mov w10, w22
@@ -410,7 +410,7 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(800, proc_id_send)[] = {0xA9BF
mov x9, #0xfffe000000000000 mov x9, #0xfffe000000000000
cmp x10, x9 cmp x10, x9
beq #0x20 beq #0x20
stp x8, x9, [sp, #-0x10]! stp x8, x9, [sp, #-0x10]!
ldr x8, [x27] ldr x8, [x27]
ldr x8, [x8, #0x38] ldr x8, [x8, #0x38]
@@ -418,19 +418,75 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(800, proc_id_send)[] = {0xA9BF
blr x8 blr x8
ldp x8, x9, [sp],#0x10 ldp x8, x9, [sp],#0x10
mov x8, x0 mov x8, x0
ldp x10, x11, [sp],#0x10 ldp x10, x11, [sp],#0x10
mov x0, x8 mov x0, x8
*/ */
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)
} }
}; };
@@ -680,7 +770,7 @@ static const kernel_info_t g_kernel_infos[] = {
uint8_t *search_pattern(void *_mem, size_t mem_size, const void *_pattern, size_t pattern_size) { uint8_t *search_pattern(void *_mem, size_t mem_size, const void *_pattern, size_t pattern_size) {
const uint8_t *pattern = (const uint8_t *)_pattern; const uint8_t *pattern = (const uint8_t *)_pattern;
uint8_t *mem = (uint8_t *)_mem; uint8_t *mem = (uint8_t *)_mem;
uint32_t table[0x100]; uint32_t table[0x100];
for (unsigned int i = 0; i < sizeof(table)/sizeof(uint32_t); i++) { for (unsigned int i = 0; i < sizeof(table)/sizeof(uint32_t); i++) {
table[i] = (uint32_t)pattern_size; table[i] = (uint32_t)pattern_size;
@@ -688,7 +778,7 @@ uint8_t *search_pattern(void *_mem, size_t mem_size, const void *_pattern, size_
for (unsigned int i = 0; i < pattern_size - 1; i++) { for (unsigned int i = 0; i < pattern_size - 1; i++) {
table[pattern[i]] = (uint32_t)pattern_size - i - 1; table[pattern[i]] = (uint32_t)pattern_size - i - 1;
} }
for (unsigned int i = 0; i <= mem_size - pattern_size; i += table[mem[i + pattern_size - 1]]) { for (unsigned int i = 0; i <= mem_size - pattern_size; i += table[mem[i + pattern_size - 1]]) {
if (pattern[pattern_size - 1] == mem[i + pattern_size - 1] && memcmp(pattern, mem + i, pattern_size - 1) == 0) { if (pattern[pattern_size - 1] == mem[i + pattern_size - 1] && memcmp(pattern, mem + i, pattern_size - 1) == 0) {
return mem + i; return mem + i;
@@ -701,7 +791,7 @@ const kernel_info_t *get_kernel_info(void *kernel, size_t size) {
uint8_t calculated_hash[0x20]; uint8_t calculated_hash[0x20];
uint8_t calculated_partial_hash[0x20]; uint8_t calculated_partial_hash[0x20];
se_calculate_sha256(calculated_hash, kernel, size); se_calculate_sha256(calculated_hash, kernel, size);
for (unsigned int i = 0; i < sizeof(g_kernel_infos)/sizeof(kernel_info_t); i++) { for (unsigned int i = 0; i < sizeof(g_kernel_infos)/sizeof(kernel_info_t); i++) {
if (g_kernel_infos[i].hash_size == 0 || size <= g_kernel_infos[i].hash_size) { if (g_kernel_infos[i].hash_size == 0 || size <= g_kernel_infos[i].hash_size) {
if (memcmp(calculated_hash, g_kernel_infos[i].hash, sizeof(calculated_hash)) == 0) { if (memcmp(calculated_hash, g_kernel_infos[i].hash, sizeof(calculated_hash)) == 0) {
@@ -720,24 +810,24 @@ const kernel_info_t *get_kernel_info(void *kernel, size_t size) {
void package2_patch_kernel(void *_kernel, size_t size, bool is_sd_kernel, void **out_ini1) { void package2_patch_kernel(void *_kernel, size_t size, bool is_sd_kernel, void **out_ini1) {
const kernel_info_t *kernel_info = get_kernel_info(_kernel, size); const kernel_info_t *kernel_info = get_kernel_info(_kernel, size);
*out_ini1 = NULL; *out_ini1 = NULL;
/* Apply IPS patches. */ /* Apply IPS patches. */
apply_kernel_ips_patches(_kernel, size); apply_kernel_ips_patches(_kernel, size);
if (kernel_info == NULL && !is_sd_kernel) { if (kernel_info == NULL && !is_sd_kernel) {
/* Should this be fatal? */ /* Should this be fatal? */
fatal_error("kernel_patcher: unable to identify kernel!\n"); fatal_error("kernel_patcher: unable to identify kernel!\n");
} }
if (kernel_info == NULL && is_sd_kernel) { if (kernel_info == NULL && is_sd_kernel) {
return; return;
} }
if (kernel_info->embedded_ini_offset != 0) { if (kernel_info->embedded_ini_offset != 0) {
*out_ini1 = (void *)((uintptr_t)_kernel + kernel_info->embedded_ini_offset); *out_ini1 = (void *)((uintptr_t)_kernel + kernel_info->embedded_ini_offset);
*((volatile uint64_t *)((uintptr_t)_kernel + kernel_info->embedded_ini_ptr)) = (uint64_t)size; *((volatile uint64_t *)((uintptr_t)_kernel + kernel_info->embedded_ini_ptr)) = (uint64_t)size;
} }
/* Apply hooks and patches. */ /* Apply hooks and patches. */
uint8_t *kernel = (uint8_t *)_kernel; uint8_t *kernel = (uint8_t *)_kernel;
size_t free_space_offset = kernel_info->free_code_space_offset; size_t free_space_offset = kernel_info->free_code_space_offset;
@@ -759,7 +849,7 @@ void package2_patch_kernel(void *_kernel, size_t size, bool is_sd_kernel, void *
/* TODO: What should be done in this case? */ /* TODO: What should be done in this case? */
fatal_error("kernel_patcher: insufficient space to apply patches!\n"); fatal_error("kernel_patcher: insufficient space to apply patches!\n");
} }
uint8_t *pattern_loc = search_pattern(kernel, size, kernel_info->patches[i].pattern, kernel_info->patches[i].pattern_size); uint8_t *pattern_loc = search_pattern(kernel, size, kernel_info->patches[i].pattern, kernel_info->patches[i].pattern_size);
if (pattern_loc == NULL) { if (pattern_loc == NULL) {
/* TODO: Should we print an error/abort here? */ /* TODO: Should we print an error/abort here? */
@@ -768,7 +858,7 @@ void package2_patch_kernel(void *_kernel, size_t size, bool is_sd_kernel, void *
/* Patch kernel to branch to our hook at the desired place. */ /* Patch kernel to branch to our hook at the desired place. */
volatile instruction_t *hook_start = (instruction_t *)(pattern_loc + kernel_info->patches[i].pattern_hook_offset); volatile instruction_t *hook_start = (instruction_t *)(pattern_loc + kernel_info->patches[i].pattern_hook_offset);
*hook_start = MAKE_BRANCH((uint32_t)((uintptr_t)hook_start - (uintptr_t)kernel), free_space_offset); *hook_start = MAKE_BRANCH((uint32_t)((uintptr_t)hook_start - (uintptr_t)kernel), free_space_offset);
/* Insert hook into free space. */ /* Insert hook into free space. */
volatile instruction_t *payload = (instruction_t *)(kernel + free_space_offset); volatile instruction_t *payload = (instruction_t *)(kernel + free_space_offset);
for (unsigned int p = 0; p < kernel_info->patches[i].payload_num_instructions; p++) { for (unsigned int p = 0; p < kernel_info->patches[i].payload_num_instructions; p++) {
@@ -777,7 +867,7 @@ void package2_patch_kernel(void *_kernel, size_t size, bool is_sd_kernel, void *
if (kernel_info->patches[i].branch_back_offset) { if (kernel_info->patches[i].branch_back_offset) {
payload[kernel_info->patches[i].payload_num_instructions] = MAKE_BRANCH(free_space_offset + sizeof(instruction_t) * kernel_info->patches[i].payload_num_instructions, (uint32_t)(kernel_info->patches[i].branch_back_offset + (uintptr_t)hook_start - (uintptr_t)kernel)); payload[kernel_info->patches[i].payload_num_instructions] = MAKE_BRANCH(free_space_offset + sizeof(instruction_t) * kernel_info->patches[i].payload_num_instructions, (uint32_t)(kernel_info->patches[i].branch_back_offset + (uintptr_t)hook_start - (uintptr_t)kernel));
} }
free_space_offset += hook_size; free_space_offset += hook_size;
free_space_size -= hook_size; free_space_size -= hook_size;
} }

View File

@@ -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);

View File

@@ -13,7 +13,7 @@
* 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 <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -46,7 +46,7 @@ static void setup_env(void) {
if (console_init() < 0) { if (console_init() < 0) {
generic_panic(); generic_panic();
} }
/* Set up exception handlers. */ /* Set up exception handlers. */
setup_exception_handlers(); setup_exception_handlers();
@@ -80,7 +80,7 @@ int main(int argc, void **argv) {
if (argc != STAGE2_ARGC) { if (argc != STAGE2_ARGC) {
generic_panic(); generic_panic();
} }
g_stage2_args = &g_stage2_args_store; g_stage2_args = &g_stage2_args_store;
memcpy(g_stage2_args, (stage2_args_t *)argv[STAGE2_ARGV_ARGUMENT_STRUCT], sizeof(*g_stage2_args)); memcpy(g_stage2_args, (stage2_args_t *)argv[STAGE2_ARGV_ARGUMENT_STRUCT], sizeof(*g_stage2_args));
@@ -88,20 +88,20 @@ int main(int argc, void **argv) {
if (g_stage2_args->version != 0) { if (g_stage2_args->version != 0) {
generic_panic(); generic_panic();
} }
/* Override the global logging level. */ /* Override the global logging level. */
log_set_log_level(g_stage2_args->log_level); log_set_log_level(g_stage2_args->log_level);
/* Initialize the boot environment. */ /* Initialize the boot environment. */
setup_env(); setup_env();
print(SCREEN_LOG_LEVEL_DEBUG | SCREEN_LOG_LEVEL_NO_PREFIX, u8"Welcome to Atmosphère Fusée Stage 2!\n"); print(SCREEN_LOG_LEVEL_DEBUG | SCREEN_LOG_LEVEL_NO_PREFIX, u8"Welcome to Atmosphère Fusée Stage 2!\n");
print(SCREEN_LOG_LEVEL_DEBUG, "Stage 2 executing from: %s\n", (const char *)argv[STAGE2_ARGV_PROGRAM_PATH]); print(SCREEN_LOG_LEVEL_DEBUG, "Stage 2 executing from: %s\n", (const char *)argv[STAGE2_ARGV_PROGRAM_PATH]);
/* 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));
@@ -110,17 +110,17 @@ int main(int argc, void **argv) {
/* This will load all remaining binaries off of the SD. */ /* This will load all remaining binaries off of the SD. */
load_payload(g_stage2_args->bct0); load_payload(g_stage2_args->bct0);
print(SCREEN_LOG_LEVEL_INFO, "Loaded payloads!\n"); print(SCREEN_LOG_LEVEL_INFO, "Loaded payloads!\n");
g_do_nxboot = (loader_ctx->chainload_entrypoint == 0); g_do_nxboot = (loader_ctx->chainload_entrypoint == 0);
if (g_do_nxboot) { if (g_do_nxboot) {
print(SCREEN_LOG_LEVEL_INFO, "Now performing nxboot.\n"); print(SCREEN_LOG_LEVEL_INFO, "Now performing nxboot.\n");
/* Start boot. */ /* Start boot. */
uint32_t boot_memaddr = nxboot_main(); uint32_t boot_memaddr = nxboot_main();
/* Terminate the boot environment. */ /* Terminate the boot environment. */
cleanup_env(); cleanup_env();
/* Finish boot. */ /* Finish boot. */
nxboot_finish(boot_memaddr); nxboot_finish(boot_memaddr);
} else { } else {
@@ -129,7 +129,7 @@ int main(int argc, void **argv) {
print(SCREEN_LOG_LEVEL_INFO, "Now chainloading.\n"); print(SCREEN_LOG_LEVEL_INFO, "Now chainloading.\n");
g_chainloader_argc = 1; g_chainloader_argc = 1;
strcpy(g_chainloader_arg_data, path); strcpy(g_chainloader_arg_data, path);
/* Terminate the boot environment. */ /* Terminate the boot environment. */
cleanup_env(); cleanup_env();
} }

View File

@@ -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) {

View File

@@ -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)

View File

@@ -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

View File

@@ -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();
}
} }
} }
@@ -484,7 +499,7 @@ uint32_t nxboot_main(void) {
FILE *boot0, *pk2file; FILE *boot0, *pk2file;
void *exosphere_memaddr; void *exosphere_memaddr;
exo_emummc_config_t exo_emummc_cfg; exo_emummc_config_t exo_emummc_cfg;
/* Configure emummc or mount the real NAND. */ /* Configure emummc or mount the real NAND. */
if (!nxboot_configure_emummc(&exo_emummc_cfg)) { if (!nxboot_configure_emummc(&exo_emummc_cfg)) {
emummc = NULL; emummc = NULL;
@@ -649,7 +664,7 @@ uint32_t nxboot_main(void) {
fatal_error("[NXBOOT] Failed to get TSEC key!\n"); fatal_error("[NXBOOT] Failed to get TSEC key!\n");
} }
} }
/* Display splash screen. */ /* Display splash screen. */
display_splash_screen_bmp(loader_ctx->custom_splash_path, (void *)0xC0000000); display_splash_screen_bmp(loader_ctx->custom_splash_path, (void *)0xC0000000);
@@ -801,7 +816,7 @@ uint32_t nxboot_main(void) {
/* Wait for the splash screen to have been displayed for as long as it should be. */ /* Wait for the splash screen to have been displayed for as long as it should be. */
splash_screen_wait_delay(); splash_screen_wait_delay();
/* Return the memory address for booting CPU0. */ /* Return the memory address for booting CPU0. */
return (uint32_t)exosphere_memaddr; return (uint32_t)exosphere_memaddr;
} }

View File

@@ -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;
} }

View File

@@ -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;
} }

View File

@@ -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))

View File

@@ -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

View File

@@ -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) {

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View 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());
}
}

View File

@@ -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);
};
}

View 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();
}
}

View 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);
}

View 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();
}
}

View 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();
}

View File

@@ -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;
} }

View 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;
};
}

View 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();
}
}
}

View File

@@ -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();
}

View File

@@ -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();
}
}

View 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();
}
}

View File

@@ -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;
} }

View File

@@ -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;
} }

View File

@@ -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),
};
};

View File

@@ -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();
} }

View File

@@ -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),
};
};

View File

@@ -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();
}

View 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();
}
}

View 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);
}

View File

@@ -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();
}

View File

@@ -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;
}

View File

@@ -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;
}
};

View File

@@ -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);
}

View File

@@ -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;
};

View File

@@ -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;
}

View File

@@ -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;
};

View File

@@ -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;
}
}

View File

@@ -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;
};

View File

@@ -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);
}
};

View File

@@ -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);
}
};

View File

@@ -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));
}
};

View File

@@ -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);
};
};

View 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();
}
}

View 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),
};
};
}

View File

@@ -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;
}

View File

@@ -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] == ':';
}
};

View File

@@ -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;
}

View File

@@ -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);
};

View File

@@ -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;
} }

Some files were not shown because too many files have changed in this diff Show More