mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-10-01 15:17:08 +02:00
Merge branch 'master' of https://github.com/Atmosphere-NX/Atmosphere into logmanager
This commit is contained in:
commit
69658dc72f
1
.gitignore
vendored
1
.gitignore
vendored
@ -31,6 +31,7 @@
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.lz4
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
|
10
Makefile
10
Makefile
@ -81,6 +81,7 @@ dist-no-debug: all
|
||||
cp config_templates/BCT.ini atmosphere-$(AMSVER)/atmosphere/config/BCT.ini
|
||||
cp config_templates/override_config.ini atmosphere-$(AMSVER)/atmosphere/config_templates/override_config.ini
|
||||
cp config_templates/system_settings.ini atmosphere-$(AMSVER)/atmosphere/config_templates/system_settings.ini
|
||||
cp config_templates/exosphere.ini atmosphere-$(AMSVER)/atmosphere/config_templates/exosphere.ini
|
||||
cp -r config_templates/kip_patches atmosphere-$(AMSVER)/atmosphere/kip_patches
|
||||
cp -r config_templates/hbl_html atmosphere-$(AMSVER)/atmosphere/hbl_html
|
||||
cp stratosphere/boot2/boot2.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000008/exefs.nsp
|
||||
@ -122,10 +123,11 @@ dist: dist-no-debug
|
||||
cp sept/sept-primary/sept-primary.elf atmosphere-$(AMSVER)-debug/sept-primary.elf
|
||||
cp sept/sept-secondary/sept-secondary.elf atmosphere-$(AMSVER)-debug/sept-secondary.elf
|
||||
cp sept/sept-secondary/key_derivation/key_derivation.elf atmosphere-$(AMSVER)-debug/sept-secondary-key-derivation.elf
|
||||
cp exosphere/exosphere.elf atmosphere-$(AMSVER)-debug/exosphere.elf
|
||||
cp exosphere/lp0fw/lp0fw.elf atmosphere-$(AMSVER)-debug/lp0fw.elf
|
||||
cp exosphere/sc7fw/sc7fw.elf atmosphere-$(AMSVER)-debug/sc7fw.elf
|
||||
cp exosphere/rebootstub/rebootstub.elf atmosphere-$(AMSVER)-debug/rebootstub.elf
|
||||
cp exosphere/loader_stub/loader_stub.elf atmosphere-$(AMSVER)-debug/exosphere-loader-stub.elf
|
||||
cp exosphere/program/program.elf atmosphere-$(AMSVER)-debug/exosphere-program.elf
|
||||
cp exosphere/warmboot/warmboot.elf atmosphere-$(AMSVER)-debug/exosphere-warmboot.elf
|
||||
cp exosphere/program/sc7fw/sc7fw.elf atmosphere-$(AMSVER)-debug/exosphere-sc7fw.elf
|
||||
cp exosphere/program/rebootstub/rebootstub.elf atmosphere-$(AMSVER)-debug/exosphere-rebootstub.elf
|
||||
cp mesosphere/kernel_ldr/kernel_ldr.elf atmosphere-$(AMSVER)-debug/kernel_ldr.elf
|
||||
cp stratosphere/ams_mitm/ams_mitm.elf atmosphere-$(AMSVER)-debug/ams_mitm.elf
|
||||
cp stratosphere/boot/boot.elf atmosphere-$(AMSVER)-debug/boot.elf
|
||||
|
@ -4,7 +4,7 @@
|
||||
; Control whether RO should ease its validation of NROs.
|
||||
; (note: this is normally not necessary, and ips patches can be used.)
|
||||
[ro]
|
||||
; ease_nro_restriction = u8!0x0
|
||||
; ease_nro_restriction = u8!0x1
|
||||
; Atmosphere custom settings
|
||||
[atmosphere]
|
||||
; Reboot from fatal automatically after some number of milliseconds.
|
||||
|
@ -1,8 +1,27 @@
|
||||
# Building Atmosphère
|
||||
The process for building Atmosphère is similar to building Fusée Gelée payloads and other Switch apps.
|
||||
Building Atmosphère is a very straightforward process that relies almost exclusively on tools provided by the [devkitPro](https://devkitpro.org) organization.
|
||||
|
||||
In order to build Atmosphère you must have devkitARM and devkitA64 installed on your computer. You can find instructions on how to install and setup devkitARM and devkitA64 on various OSes [here](https://devkitpro.org/wiki/Getting_Started). You'll need to install the following packages via (dkp-)pacman: switch-dev switch-freetype switch-libjpeg-turbo devkitARM devkitarm-rules
|
||||
## Dependencies
|
||||
+ [devkitA64](https://devkitpro.org)
|
||||
+ [devkitARM](https://devkitpro.org)
|
||||
+ [Python 2 or 3](https://www.python.org) (optional)
|
||||
+ [PyCryptodome](https://pypi.org/project/pycryptodome) (optional)
|
||||
|
||||
sept requires you have python installed with the pycryptodome PyPi packages (`pip install pycryptodome`). You may also want to install the zip package from your package manager of choice to support the `make dist` recipe.
|
||||
## Instructions
|
||||
1. Follow the guide located [here](https://devkitpro.org/wiki/Getting_Started) to install and configure all the tools necessary for the build process.
|
||||
|
||||
Once you have finished installing the devkitPro-provided toolchain/libraries, python, and the dependencies, simply clone the Atmosphère repo (clone with the -r flag), change your directory to it and run `make`.
|
||||
2. Install the following packages via (dkp-)pacman:
|
||||
+ `switch-dev`
|
||||
+ `switch-libjpeg-turbo`
|
||||
+ `devkitARM`
|
||||
+ `devkitarm-rules`
|
||||
|
||||
3. (Optional) In order to build [sept](components/sept.md) the pycryptodome PyPi package is required, which can be installed by running `pip install pycryptodome` under the installed Python environment of your choice or by installing the complete zip package to support the `make dist` recipe. This is an optional step included for advanced users who have the ability to provide the necessary encryption/signing keys themselves.
|
||||
|
||||
4. It is, instead, possible to build [sept](components/sept.md) by providing previously encrypted/signed binaries distributed by official Atmosphère release packages. In order to do so, export the following variables in your current environment:
|
||||
+ `SEPT_00_ENC_PATH` (must point to the `sept-secondary_00.enc` file)
|
||||
+ `SEPT_01_ENC_PATH` (must point to the `sept-secondary_01.enc` file)
|
||||
+ `SEPT_DEV_00_ENC_PATH` (must point to the `sept-secondary_dev_00.enc` file)
|
||||
+ `SEPT_DEV_01_ENC_PATH` (must point to the `sept-secondary_dev_01.enc` file)
|
||||
|
||||
5. Finally, clone the Atmosphère repository and run `make` under its root directory.
|
||||
|
@ -1,4 +1,32 @@
|
||||
# Changelog
|
||||
## 0.13.0
|
||||
+ `exosphère`, atmosphère's secure monitor re-implementation, was completely re-written.
|
||||
+ `exosphère` was the first component authored for the project in early 2018. It is written in C, and in a style very different from the rest of atmosphère's code.
|
||||
+ This has made the codebase difficult to maintain as time has gone on.
|
||||
+ `exosphère` was also written to conform to constraints and assumptions that simply no longer apply when cfw is not launched from the web browser, and when warmboothax is possible.
|
||||
+ Even beyond these issues, `exosphère` used all but 1KB of the 64KB of space available to it. This was a problem for a few reasons:
|
||||
+ Each new system update added requires additional space to support (to add new keys and reflect various changes); 10.0.0 support used up 3 of the 4KB we had left.
|
||||
+ atmosphère will want to have software support for mariko hardware, and this is not possible to fit in 1 KB.
|
||||
+ The `exosphère` rewrite (which was codenamed `exosphère2` during development) solves these problems.
|
||||
+ The new codebase is C++20 written in atmosphère's style.
|
||||
+ This solves the maintainability problem, and should make understanding how the secure monitor works *much* easier for those interested in using the code as a reference implementation.
|
||||
+ In addition, the new implementation currently uses ~59.5 of the 64KB available.
|
||||
+ Several potential code changes are planned that can save/grant access to an additional ~2-3 KB if needed.
|
||||
+ Unlike the first codebase, the new `exosphère` actually already has space allocated for future keys/etc. It is currently expected that the reserved space will never be required.
|
||||
+ The previous implementation chose not to implement a number of "unimportant" secure monitor functions due to space concerns. The new code has enough breathing room that it can implement them without worries. :)
|
||||
+ Finally, the groundwork for mariko support has been laid -- there are only a few minor changes needed for the new secure monitor implementation to work on both erista and mariko hardware.
|
||||
+ **Please note**: `exosphère` is only one of many components, and many more need changes to support running on mariko hardware.
|
||||
+ Software-side support for executing on mariko hardware is expected some time during Summer 2020, though it should also be noted that this is not a hard deadline.
|
||||
+ **Please note**: The new `exosphère` binary is not abi-compatible with the old one. Users who boot using hekate should wait for it to update before running 0.13.0 (or boot fusee-primary via hekate).
|
||||
+ atmosphère's api for target firmware was changed. All minor/micro system versions are now recognized, instead of only major versions.
|
||||
+ This was required in order to support firmware version 5.1.0, which made breaking changes to certain IPC APIs that caused atmosphère 0.12.0 to abort.
|
||||
+ **Please note**: this is (unavoidably) a breaking change. System modules using atmosphere-libs will need to update to understand what firmware version they are running.
|
||||
+ `emummc` was updated to include the new changes.
|
||||
+ `emummc` now uses an updated/improved/faster SDMMC driver.
|
||||
+ File-based emummc is now almost as fast as raw partition-based emummc.
|
||||
+ For those interested in atmosphère's future development plans, the project's [roadmap](https://github.com/Atmosphere-NX/Atmosphere/blob/f68d33b70aed8954cc2c539e5934bcaf37ba51da/docs/roadmap.md) was updated.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
|
||||
## 0.12.0
|
||||
+ Configuration for exosphere was moved to sd:/exosphere.ini.
|
||||
+ This is to facilitate BIS protection changes described below.
|
||||
@ -47,6 +75,7 @@
|
||||
+ A bug was fixed that could cause boot to fail sporadically due to cache/tlb mismanagement when doing physical ASLR of the kernel.
|
||||
+ A number of other minor issues were addressed (and more of Atmosphere was updated to reflect other changes in 10.0.x).
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
|
||||
## 0.11.1
|
||||
+ A bug was fixed that could cause owls to flicker under certain circumstances.
|
||||
+ For those interested in technical details, in 10.0.0 kernelldr/kernel no longer set cpuactlr_el1, assuming that it was set correctly by the secure monitor.
|
||||
@ -54,6 +83,7 @@
|
||||
+ This caused a variety of highly erratic symptoms, including causing basically any game to crash seemingly randomly.
|
||||
+ A number of other major inaccuracies in exosphere were corrected.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
|
||||
## 0.11.0
|
||||
+ Support was added for 10.0.0.
|
||||
+ Exosphere has been updated to reflect the new key import semantics in 10.0.0.
|
||||
@ -77,6 +107,7 @@
|
||||
+ In particular, code implementing the os namespace is significantly more accurate.
|
||||
+ In addition, Nintendo's allocators were implemented, allowing for identical memory efficiency versus Nintendo's implementations.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
|
||||
## 0.10.5
|
||||
+ Changes were made to the way fs.mitm builds images when providing a layeredfs romfs.
|
||||
+ Building romfs metadata previously had a memory cost of about ~4-5x the file table size.
|
||||
@ -89,6 +120,7 @@
|
||||
+ Romfs building can be made even more memory efficient, but unless games show up with even more absurdly huge file tables it seems not worth the speed trade-off.
|
||||
+ A bug was fixed that caused Atmosphere's fatal error context to not dump TLS for certain processes.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
|
||||
## 0.10.4
|
||||
+ With major thanks to @Adubbz for his work, the NCM system module has now been re-implemented.
|
||||
+ This was a major stepping stone towards the goal of having implementations everything in the Switch's package1/package2 firmware.
|
||||
@ -111,6 +143,7 @@
|
||||
+ Atmosphere's fatal error context now dumps 0x100 of TLS.
|
||||
+ This will make it much easier to fix bugs when an error report is dumped for whatever caused the crash.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
|
||||
## 0.10.3
|
||||
+ Support was added for 9.2.0.
|
||||
+ Support was added for redirecting manual html content for games.
|
||||
@ -125,6 +158,7 @@
|
||||
+ `ro` has been updated to reflect changes made in 9.1.0.
|
||||
+ The temporary auto-migration added in 0.10.0 has been removed, since the transitionary period is well over.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
|
||||
## 0.10.2
|
||||
+ hbl configuration was made more flexible.
|
||||
+ Up to eight specific program ids can now be specified to have their own override keys.
|
||||
@ -159,6 +193,7 @@
|
||||
+ For now, users may re-enable this mitm by use of a custom setting (`atmosphere!enable_deprecated_hid_mitm`) to ease the transition process some.
|
||||
+ Please note: support for this setting may be removed to save memory in a future atmosphere release.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
|
||||
## 0.10.1
|
||||
+ A bug was fixed that caused memory reallocation to the system pool to work improperly on firmware 5.0.0 and above.
|
||||
+ Atmosphere was always trying to deallocate memory away from the applet pool and towards the system pool.
|
||||
@ -187,6 +222,7 @@
|
||||
+ Please ensure your homebrew is updated.
|
||||
+ Random number generation now uses TinyMT instead of XorShift.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
|
||||
## 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.
|
||||
@ -251,6 +287,7 @@
|
||||
+ 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:
|
||||
@ -270,6 +307,7 @@
|
||||
+ 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
|
||||
+ 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).
|
||||
@ -293,6 +331,7 @@
|
||||
+ Incorrect display output ("2000-0000") has been fixed. Fatal will now correctly show 2162-0002 when this occurs.
|
||||
+ A longstanding bug in how fatal manages the displays has been fixed, and official display init behavior is now matched precisely.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
|
||||
## 0.9.2
|
||||
+ A number of emummc bugfixes were added (all thanks to @m4xw's hard work). The following is a summary of emummc changes:
|
||||
+ Support for file-based emummc instances was fixed.
|
||||
@ -316,10 +355,12 @@
|
||||
+ The rewritten modules consistently have lower memory footprints, and should be easier to maintain going forwards.
|
||||
+ The `sm`, `boot`, `spl`, `ro`, and `loader` modules have been tackled so far.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
|
||||
## 0.9.1
|
||||
+ Support was added for 8.1.0.
|
||||
+ Please note, emummc is still considered **beta/experimental** -- this is not the inevitable bugfix update for it, although some number of bugs have been fixed. :)
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
|
||||
## 0.9.0
|
||||
+ Creport output was improved significantly.
|
||||
+ Thread names are now dumped on crash in addition to 0x100 of TLS from each thread.
|
||||
@ -344,6 +385,7 @@
|
||||
+ This can be set to any arbitrary directory by setting `emummc!emummc_nintendo_path`.
|
||||
+ To create a backup usable for emummc, users may use tools provided by the [hekate](https://github.com/CTCaer/hekate) project.
|
||||
+ If, when using emummc, you encounter a bug, *please be sure to report it* -- that's the only way we can fix it. :)
|
||||
|
||||
## 0.8.10
|
||||
+ A bug was fixed that could cause incorrect system memory allocation on 5.0.0.
|
||||
+ 5.0.0 should now correctly have an additional 12 MiB allocated for sysmodules.
|
||||
@ -360,6 +402,7 @@
|
||||
+ NAND repair occurs when an unexpected shutdown or error happens during a system update.
|
||||
+ This fixes a final edge case where AutoRCM might be removed by HOS, which could cause a user to burn fuses.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
|
||||
## 0.8.9
|
||||
+ A number of bugs were fixed, including:
|
||||
+ A data abort was fixed when mounting certain partitions on NAND.
|
||||
@ -385,6 +428,7 @@
|
||||
+ `spl` (Secure Platform Services) is responsible for cryptographic operations, including all communications with the secure monitor (exosphère).
|
||||
+ In the future, this may be used to provide extensions to the API for interacting with exosphère from userland.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
|
||||
## 0.8.8
|
||||
+ Support was added for firmware version 8.0.0.
|
||||
+ Custom exception handlers were added to stratosphere modules.
|
||||
@ -392,6 +436,7 @@
|
||||
+ A bug was fixed in creport that caused games to hang when crashing under certain circumstances.
|
||||
+ A bug was fixed that prevented maintenance mode from booting on 7.0.0+.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
|
||||
## 0.8.7
|
||||
+ A few bugs were fixed that could cause fatal to fail to show an error under certain circumstances.
|
||||
+ A bug was fixed that caused an error when launching certain games (e.g. Hellblade: Senua's Sacrifice).
|
||||
@ -407,6 +452,7 @@
|
||||
+ Please note, this feature is **experimental**, and may cause problems. Please use at your own risk (and back up your saves before enabling it), as it still needs testing.
|
||||
+ This can be enabled by setting `atmosphere!fsmitm_redirect_saves_to_sd` to 1 in `system_settings.ini`.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
|
||||
## 0.8.6
|
||||
+ A number of bugs were fixed, including:
|
||||
+ A case of inverted logic was fixed in fs.mitm which prevented the flags system from working correctly.
|
||||
@ -444,6 +490,7 @@
|
||||
+ fs.mitm will also now cause requests to mount the HtmlDocument content for HBL's title to open the `sdmc:/atmosphere/hbl_html` folder.
|
||||
+ By default, this just contains a URL whitelist.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
|
||||
## 0.8.5
|
||||
+ Support was added for overriding content on a per-title basis, separate from HBL override.
|
||||
+ This allows for using mods on the same title that one uses to launch HBL.
|
||||
@ -465,6 +512,7 @@
|
||||
+ A bug was fixed that would cause Atmosphère's fatal screen to not show on 1.0.0-2.3.0.
|
||||
+ A bug was fixed that caused Atmosphère's automatic ProdInfo backups to be corrupt.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
|
||||
## 0.8.4
|
||||
+ Support for 7.0.0/7.0.1 was added.
|
||||
+ This is facilitated through a new payload, `sept`, which can be signed, encrypted, and then loaded by Nintendo's TSEC firmware.
|
||||
@ -480,6 +528,7 @@
|
||||
+ Performing a reboot from the reboot menu now reboots to atmosphere. This can be configured via `system_settings.ini`.
|
||||
+ Performing a shutdown from the reboot menu now works properly with AutoRCM, and does a real shutdown.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
|
||||
## 0.8.3
|
||||
+ A custom warmboot firmware was implemented, which does not perform anti-downgrade fuse checks.
|
||||
+ This fixes sleep mode when using a downgraded NAND.
|
||||
@ -500,6 +549,7 @@
|
||||
+ Fatal will now use this to reboot to sdmc:/atmosphere/reboot_payload.bin if present, when a vol button is pressed.
|
||||
+ An example homebrew ("reboot_to_payload") was also written and is now included with Atmosphère.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
|
||||
## 0.8.2
|
||||
+ A number of bugs were fixed causing users to sometimes see `Key Derivation Failed!`.
|
||||
+ KFUSE clock enable timings have been adjusted to allow time to stabilize before TSEC is granted access.
|
||||
@ -509,6 +559,7 @@
|
||||
+ A bug was fixed causing sleep mode to not work with debugmode enabled.
|
||||
+ As a result, debugmode is now enabled in the default BCT.ini.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
|
||||
## 0.8.1
|
||||
+ A bug was fixed causing users to see `Failed to enable SMMU!` if fusee had previously rebooted.
|
||||
+ This message will still occur sporadically if fusee is not launched from coldboot, but it can never happen twice in a row.
|
||||
@ -530,6 +581,7 @@
|
||||
+ On fatal error, the user can now choose to perform a standard reboot via the power button, or a reboot into RCM via either volume button.
|
||||
+ A custom message was added to `fatal` for when an Atmosphère API version mismatch is detected (2495-1623).
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
|
||||
## 0.8.0
|
||||
+ A custom `fatal` system module was added.
|
||||
+ This re-implements and extends Nintendo's fatal module, with the following features:
|
||||
@ -563,6 +615,7 @@
|
||||
+ By default, new keys will automatically be derived without user input.
|
||||
+ Support is also present for loading new keys from `atmosphere/prod.keys` or `atmosphere/dev.keys`
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
|
||||
## 0.7.5
|
||||
+ DRAM training was added to fusee-secondary, courtesy @hexkyz.
|
||||
+ This greatly improves the speed of memory accesses during boot, resulting in a boot time that is ~200-400% faster.
|
||||
@ -571,6 +624,7 @@
|
||||
+ This matches the improvement Nintendo added to official creport in 6.1.0.
|
||||
+ The code region detection heuristic was further improved by checking whether an address points to .rodata or .rwdata, instead of just .text.
|
||||
+ This means that a crash appears in a loaded NRO (or otherwise discontiguous) code region, creport will be able to detect all active code regions, and not just that one.
|
||||
|
||||
## 0.7.4
|
||||
+ [libstratosphere](https://github.com/Atmosphere-NX/libstratosphere) has been completely refactored/rewritten, and split into its own, separate submodule.
|
||||
+ While this is mostly "under the hood" for end-users, the refactor is faster (improving both boot-time and runtime performance), more accurate (many of the internal IPC structures are now bug-for-bug compatible with Nintendo's implementations), and significantly more stable (it fixes a large number of bugs present in the old library).
|
||||
@ -591,11 +645,13 @@
|
||||
+ PM now only gives full FS permissions to the active KIPs. This fixes a potential crash where new processes might be unable to be registered with FS.
|
||||
+ The `make dist` target now includes the branch in the generated zip name.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
|
||||
## 0.7.3
|
||||
+ Loader and fs.mitm now try to reload loader.ini before reading it. This allows for changing the override button combination/HBL title id at runtime.
|
||||
+ Added a MitM between set:sys and qlaunch, used to override the system version string displayed in system settings.
|
||||
+ The displayed system version will now display `<Actual version> (AMS <x>.<y>.<z>)`.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
|
||||
## 0.7.2
|
||||
+ Fixed a bug in fs.mitm's LayeredFS read implementation that caused some games to crash when trying to read files.
|
||||
+ Fixed a bug affecting 1.0.0 that caused games to crash with fatal error 2001-0106 on boot.
|
||||
|
4
docs/components/emummc.md
Normal file
4
docs/components/emummc.md
Normal file
@ -0,0 +1,4 @@
|
||||
# emummc
|
||||
emummc is a collaborative project that provides eMMC storage emulation.
|
||||
|
||||
Please refer to the project's repository [here](https://github.com/m4xw/emuMMC) for detailed instructions and documentation.
|
@ -1,10 +1,76 @@
|
||||
# Exosphère
|
||||
Exosphère is a reimplementation of Arm's TrustZone (TZ), also known as Secure Monitor (Secure_Monitor.bin). It has the highest privilege mode available on the Switch’s processor, and has access to everything on the console.
|
||||
|
||||
Exosphère will potentially play a big role in Jamais Vu and Déja Vu, which are upcoming software exploits for the Switch, allowing one to launch Atmosphère on a Fusée-Gélee patched (ipatched) Switch console, and will also enable one to launch into CFW directly from the Switch itself without the use of any sort of external device, such as a computer or RCM jig, provided they are on a low enough system firmware.
|
||||
|
||||
## TrustZone/Secure Monitor
|
||||
TrustZone is responsible for all the cryptographic operations on the Switch. The idea behind the way it operates is that all the keys stay in the TrustZone, and userspace only gets "handles" to them. This would make sure that keydata never leaks and is kept secure. It also has a few more responsibilities, such as power management, providing a source of random numbers, and providing access to various pieces of information that are stored in the fuses.
|
||||
# exosphère
|
||||
exosphère is a customized reimplementation of the Horizon OS's Secure Monitor.
|
||||
The Secure Monitor follows the same design principle as Arm's TrustZone and both terms can be used interchangeably in this context. It runs at the highest privilege mode (EL3) available to the main processor and is responsible for all the sensitive cryptographic operations needed by the system as well as power management for each CPU.
|
||||
|
||||
## Extensions
|
||||
Exosphère currently only contains one extension, an SMC allowing homebrew to find which version of Atmosphère is currently running, in order to find out what extensions are allowed to be used.
|
||||
exosphère expands the original Secure Monitor design by providing custom SMCs (Secure Monitor Calls) necessary to the homebrew ecosystem. Currently, these are:
|
||||
```
|
||||
uint32_t smc_ams_iram_copy(smc_args_t *args);
|
||||
uint32_t smc_ams_write_address(smc_args_t *args);
|
||||
uint32_t smc_ams_get_emummc_config(smc_args_t *args);
|
||||
```
|
||||
|
||||
Additionally, exosphère expands the functionality of two SMCs provided by the Horizon OS for getting/setting configuration items. The following custom configuration items are provided by exosphère:
|
||||
```
|
||||
CONFIGITEM_EXOSPHERE_VERSION = 65000,
|
||||
CONFIGITEM_NEEDS_REBOOT = 65001,
|
||||
CONFIGITEM_NEEDS_SHUTDOWN = 65002,
|
||||
CONFIGITEM_EXOSPHERE_VERHASH = 65003,
|
||||
CONFIGITEM_HAS_RCM_BUG_PATCH = 65004,
|
||||
CONFIGITEM_SHOULD_BLANK_PRODINFO = 65005,
|
||||
CONFIGITEM_ALLOW_CAL_WRITES = 65006,
|
||||
```
|
||||
|
||||
### smc_ams_iram_copy
|
||||
This function implements a copy of up to one page between DRAM and IRAM. Its arguments are:
|
||||
```
|
||||
args->X[1] = DRAM address (translated by kernel), must be 4-byte aligned.
|
||||
args->X[2] = IRAM address, must be 4-byte aligned.
|
||||
args->X[3] = Size (must be <= 0x1000 and 4-byte aligned).
|
||||
args->X[4] = 0 for read, 1 for write.
|
||||
```
|
||||
|
||||
### smc_ams_write_address
|
||||
This function implements a write to a DRAM page. Its arguments are:
|
||||
```
|
||||
args->X[1] = Virtual address, must be size-bytes aligned and readable by EL0.
|
||||
args->X[2] = Value.
|
||||
args->X[3] = Size (must be 1, 2, 4, or 8).
|
||||
```
|
||||
|
||||
### smc_ams_get_emummc_config
|
||||
This function retrieves configuration for the current [emummc](emummc.md) context. Its arguments are:
|
||||
```
|
||||
args->X[1] = MMC id, must be size-bytes aligned and readable by EL0.
|
||||
args->X[2] = Pointer to output (for paths for filebased + nintendo dir), must be at least 0x100 bytes.
|
||||
```
|
||||
|
||||
### CONFIGITEM_EXOSPHERE_VERSION
|
||||
This custom configuration item gets information about the current exosphere version.
|
||||
|
||||
### CONFIGITEM_NEEDS_REBOOT
|
||||
This custom configuration item is used to issue a system reboot into RCM or into a warmboot payload leveraging a secondary vulnerability to achieve code execution from warm booting.
|
||||
|
||||
### CONFIGITEM_NEEDS_SHUTDOWN
|
||||
This custom configuration item is used to issue a system shutdown with a warmboot payload leveraging a secondary vulnerability to achieve code execution from warm booting.
|
||||
|
||||
### CONFIGITEM_EXOSPHERE_VERHASH
|
||||
This custom configuration item gets information about the current exosphere git commit hash.
|
||||
|
||||
### CONFIGITEM_HAS_RCM_BUG_PATCH
|
||||
This custom configuration item gets whether the unit has the CVE-2018-6242 vulnerability patched.
|
||||
|
||||
### CONFIGITEM_SHOULD_BLANK_PRODINFO
|
||||
This custom configuration item gets whether the unit should simulate a "blanked" PRODINFO. See [here](../features/configurations.md) for more information.
|
||||
|
||||
### CONFIGITEM_ALLOW_CAL_WRITES
|
||||
This custom configuration item gets whether the unit should allow writing to the calibration partition.
|
||||
|
||||
## lp0fw
|
||||
This is a small, built-in payload that is responsible for waking up the system during a warm boot.
|
||||
|
||||
## sc7fw
|
||||
This is a small, built-in payload that is responsible for putting the system to sleep during a warm boot.
|
||||
|
||||
## rebootstub
|
||||
This is a small, built-in payload that provides functionality to reboot the system into any payload of choice.
|
||||
|
22
docs/components/fusee.md
Normal file
22
docs/components/fusee.md
Normal file
@ -0,0 +1,22 @@
|
||||
# fusée
|
||||
fusée is a custom bootloader used to start the Atmosphère environment.
|
||||
It is divided into three sub-components: fusée-primary, fusée-mtc and fusée-secondary.
|
||||
|
||||
fusée is also capable of chainloading other payloads (e.g.: Android).
|
||||
|
||||
fusée's behavior can be configured via the [BCT.ini](../features/configurations.md) file located on the SD card.
|
||||
|
||||
## fusée-primary
|
||||
fusée-primary is the first piece of Atmosphère's code that runs on the hardware.
|
||||
It is distributed as a standalone payload designed to be launched via RCM by abusing the CVE-2018-6242 vulnerability.
|
||||
|
||||
This payload is responsible for all the low-level hardware initialization required by the Nintendo Switch, plus the extra task of initializing the SD card and reading the next fusée sub-components from it.
|
||||
|
||||
## fusée-mtc
|
||||
fusée-mtc is an optional, but heavily recommended sub-component that performs DRAM memory training.
|
||||
This ensures a proper environment for running the final fusée sub-component.
|
||||
|
||||
## fusée-secondary
|
||||
fusée-secondary is the last fusée sub-component that runs on the system.
|
||||
It is responsible for configuring and bootstrapping the Atmosphère environment by mimicking the Horizon OS's design.
|
||||
This includes setting up the cryptosystem, mounting or emulating the eMMC, injecting or patching system modules and launching the exosphère component.
|
@ -1,73 +0,0 @@
|
||||
# BCT.ini
|
||||
BCT.ini is the configuration file used by fusée-primary and fusée-secondary. It is read by fusee-primary.bin to setup and boot fusee-secondary.bin and is also read by fusee-secondary.bin to configure Exosphère, specify the environment it should boot, or configure other miscellaneous options such as setting a custom boot splashscreen.
|
||||
|
||||
## Configuration
|
||||
This file is located in the `atmosphere` folder on your SD card. The default configuration file will look similar to this.
|
||||
```
|
||||
BCT0
|
||||
[stage1]
|
||||
stage2_path = atmosphere/fusee-secondary.bin
|
||||
stage2_addr = 0xF0000000
|
||||
stage2_entrypoint = 0xF0000000
|
||||
|
||||
[exosphere]
|
||||
; Note: Disabling debugmode will cause parts of ams.tma to not work, in the future.
|
||||
debugmode = 1
|
||||
debugmode_user = 0
|
||||
|
||||
[stratosphere]
|
||||
; To force-enable nogc, add nogc = 1
|
||||
; To force-disable nogc, add nogc = 0
|
||||
```
|
||||
|
||||
## Adding a Custom Boot Splashscreen
|
||||
Add the following lines to BCT.ini and change the value of `custom_splash` to the actual path and filename of your boot splashscreen.
|
||||
```
|
||||
[stage2]
|
||||
custom_splash = /path/to/your/bootlogo.bmp
|
||||
```
|
||||
|
||||
The boot splashscreen must be a BMP file, it must be 720x1280 (1280x720 rotated 90 degrees left/counterclockwise/anti-clockwise) resolution, and be in 32-bit ARGB format. You can use image editing software such as GIMP or Photoshop to export the image in this format.
|
||||
|
||||
## Configuring "nogc" Protection
|
||||
Nogc is a feature provided by fusée-secondary which disables the Nintendo Switch's Game Card reader. Its purpose is to prevent the reader from being updated when the console has been updated without burning fuses from a firmware lower than 4.0.0, to a newer firmware that is at least 4.0.0 or higher. By default, Atmosphère will protect the Game Card reader automatically, but you are free to change it.
|
||||
|
||||
To change its functionality, add the following line to the `stratosphere` section and change the value of `X` according to the following list.
|
||||
```
|
||||
nogc = X
|
||||
```
|
||||
```
|
||||
1 = force-enable nogc, so Atmosphère will always disable the Game Card reader.
|
||||
0 = force-disable nogc, so Atmosphère will always enable the Game Card reader.
|
||||
```
|
||||
|
||||
|
||||
## Changing Target Firmware
|
||||
Add the following line to the `exosphere` section and replace the `X` according to the following list if you have trouble booting past the firmware version detection.
|
||||
`target_firmware` is the OFW major version.
|
||||
```
|
||||
target_firmware = X
|
||||
```
|
||||
```
|
||||
1.0.0 = 1
|
||||
2.X.X = 2
|
||||
3.X.X = 3
|
||||
4.X.X = 4
|
||||
5.X.X = 5
|
||||
6.X.X = 6
|
||||
6.2.0 = 7
|
||||
7.X.X = 8
|
||||
```
|
||||
|
||||
Note that 6.X.X indicates 6.0.0 through 6.1.0.
|
||||
|
||||
## Configuring Debugging Modes
|
||||
By default, Atmosphère signals to the Horizon kernel that debugging is enabled while leaving usermode debugging disabled, since this can cause undesirable side-effects. If you wish to change these behaviours, go to the `exosphere` section and change the value of `X` according to the following list.
|
||||
```
|
||||
debugmode = X
|
||||
debugmode_user = X
|
||||
```
|
||||
```
|
||||
1 = enable
|
||||
0 = disable
|
||||
```
|
@ -1,20 +0,0 @@
|
||||
# Fusée
|
||||
Fusée (not to be confused with Fusée Gelée) is a custom bootloader needed to start Atmosphère and replaces Nintendo's Package1loader/bootloader. It currently utilizes the [Tegra X1 RCM Vulnerability](https://nvidia.custhelp.com/app/answers/detail/a_id/4660/~/security-notice%3A-nvidia-tegra-rcm-vulnerability) in order to function.
|
||||
|
||||
Fusée is split into two separate parts: fusée-primary and fusée-secondary. This is due to the RCM Vulnerability only allowing payloads of a limited filesize to be sent to the device.
|
||||
|
||||
As of June 2018, there are new Switch systems being sold that prevent Fusée (or any payload that requires the Fusée Gelée exploit) from working due to having an ipatched bootrom. All ipatched systems share the HAC-S-JXE-C3 product code. While Fusée cannot work on these ipatched units, they still come on firmware 4.1.0, which is vulnerable to the upcoming Déja Vu software exploit. Note that if you update past 4.1.0 on one of these ipatched units, your odds of being able to install Atmosphère or run any homebrew become practically non-existent.
|
||||
|
||||
Additionally, a hardware revision of the Switch known as “Mariko” is believed to be in development. No such units have been seen in stores yet, but it is expected Nintendo will roll them out silently. The Mariko units will most likely patch the bootrom vulnerability Fusée Gelée, which is currently used to access CFW, and will likely have their own proprietary bootloader.
|
||||
|
||||
## Fusée-Primary
|
||||
Fusée-primary is the payload file (fusee-primary.bin) sent to the Switch from an external device. Once sent, fusée-primary makes initial preparations before loading fusée-secondary from the Switch’s SD Card.
|
||||
|
||||
Fusée-primary can be configured via the [BCT.ini](../fusee/BCT.md) file located on the Switch’s SD card.
|
||||
|
||||
## Fusée-Secondary
|
||||
Fusée-secondary is a payload file that stays on the root of the Switch’s SD Card (fusee-secondary.bin). It is automatically launched once fusée-primary has finished, and is responsible for preparing the Switch’s hardware for future running environments, such as the homebrew menu. Fusée-secondary is also responsible for validating and launching Exosphère.
|
||||
|
||||
Fusée-secondary contains various [.kip modules](/docs/main.md#modules). These modules modify existing features in the OS, and can also add new ones.
|
||||
|
||||
Fusée is also capable of chainloading other payloads such as Linux.
|
@ -1,15 +0,0 @@
|
||||
# sept
|
||||
Sept is a payload that facilitates booting Atmosphère when targeting firmware version 7.0.0+.
|
||||
|
||||
It consists of a primary and a secondary payload.
|
||||
|
||||
## Sept-Primary
|
||||
Sept-primary is essentially a stand-in for Nintendo's package1ldr, on 7.0.0+. To use it, the caller (normally Fusée-secondary) loads the sept-primary binary to `0x4003F000`,
|
||||
loads the 7.0.0+ TSEC firmware to `0x40010F00`, and loads a signed, encrypted payload to `0x40016FE0`.
|
||||
|
||||
This signed, encrypted payload is normally Sept-secondary.
|
||||
|
||||
## Sept-Secondary
|
||||
Sept-secondary is a payload that performs 7.0.0+ key derivation, and then chainloads to `sept/payload.bin`.
|
||||
|
||||
It is normally stored encrypted/signed; if one wishes to build sept-secondary instead of using release builds, one must bring his/her own keys.
|
11
docs/components/libraries.md
Normal file
11
docs/components/libraries.md
Normal file
@ -0,0 +1,11 @@
|
||||
# libraries
|
||||
This is a collection of libraries for doing operating system development for the Nintendo Switch.
|
||||
|
||||
## libmesosphere
|
||||
libmesosphere is a work-in-progress C++ library implementing functionality for the Horizon Kernel.
|
||||
|
||||
## libstratosphere
|
||||
libstratosphere is a work-in-progress C++ library for development of system modules for the Nintendo Switch.
|
||||
|
||||
## libvapours
|
||||
Common boilerplate code for various purposes.
|
3
docs/components/mesosphere.md
Normal file
3
docs/components/mesosphere.md
Normal file
@ -0,0 +1,3 @@
|
||||
# mesosphère
|
||||
mesosphère is a work in progress customized kernel reimplementation.
|
||||
The Horizon OS's kernel follows microkernel design principles and runs at the EL1 level. It is currently subdivided into a loader (kernel_ldr) and the main kernel code.
|
35
docs/components/modules/ams_mitm.md
Normal file
35
docs/components/modules/ams_mitm.md
Normal file
@ -0,0 +1,35 @@
|
||||
# ams_mitm
|
||||
This module provides methods to intercept services provided by other system modules. It is further sub-divided according to the service it targets.
|
||||
|
||||
## bpc_mitm
|
||||
bpc_mitm enables intercepting requests to power control services. It currently intercepts:
|
||||
+ `am` system module (to intercept the Reboot/Power buttons in the overlay menu)
|
||||
+ `fatal` system module (to simplify payload reboot logic significantly)
|
||||
+ [nx-hbloader](https://github.com/switchbrew/nx-hbloader) (to allow homebrew to take advantage of the feature)
|
||||
|
||||
## fs_mitm
|
||||
fs_mitm enables intercepting file system operations. It can deny, delay, replace, or redirect any request made to the file system. It enables LayeredFS to function, which allows for replacement of game assets.
|
||||
|
||||
## hid_mitm
|
||||
hid_mitm enables intercepting requests to controller device services. It is currently disabled by default. If enabled, it intercepts:
|
||||
+ [nx-hbloader](https://github.com/switchbrew/nx-hbloader) (to help homebrew not need to be recompiled due to a breaking change introduced in the past)
|
||||
|
||||
Note that hid_mitm is currently deprecated and might be removed entirely in the future.
|
||||
|
||||
## ns_mitm
|
||||
ns_mitm enables intercepting requests to application control services. It currently intercepts:
|
||||
+ Web Applets (to facilitate nx-hbloader web browser launching)
|
||||
|
||||
## set_mitm
|
||||
set_mitm enables intercepting requests to the system settings service. It currently intercepts:
|
||||
+ `ns` system module and games (to allow for overriding game locales)
|
||||
+ All firmware debug settings requests (to allow modification of system settings not directly exposed to the user)
|
||||
|
||||
### Firmware Version
|
||||
set_mitm intercepts the `GetFirmwareVersion` command, if the requester is `qlaunch` or `maintenance`.
|
||||
It modifies the `display_version` field of the returned system version, causing the version to display
|
||||
in settings as `#.#.#|AMS #.#.#|?` with `? = S` when running under system eMMC or `? = E` when running under emulated eMMC. This allows users to easily verify what version of Atmosphère and what eMMC environment they are running.
|
||||
|
||||
### System Settings
|
||||
set_mitm intercepts the `GetSettingsItemValueSize` and `GetSettingsItemValue` commands for all requesters.
|
||||
It does so in order to enable user configuration of system settings, which are parsed from `/atmosphere/system_settings.ini` on boot. See [here](../../features/configurations.md) for more information on the system settings format.
|
4
docs/components/modules/boot.md
Normal file
4
docs/components/modules/boot.md
Normal file
@ -0,0 +1,4 @@
|
||||
# boot
|
||||
This module is a reimplementation of the Horizon OS's `boot` system module, which is responsible for initializing and configuring hardware.
|
||||
|
||||
Atmosphère's reimplementation displays its own black and white splash screen and battery icons as replacements for the original assets used during display initialization.
|
4
docs/components/modules/boot2.md
Normal file
4
docs/components/modules/boot2.md
Normal file
@ -0,0 +1,4 @@
|
||||
# boot2
|
||||
This module is a reimplementation of the Horizon OS's `boot2` system module, which is responsible for launching all the other necessary system modules.
|
||||
|
||||
Atmosphère's reimplementation allows launching user provided system modules from the SD card. See [here](../../features/configurations.md) for more information.
|
4
docs/components/modules/creport.md
Normal file
4
docs/components/modules/creport.md
Normal file
@ -0,0 +1,4 @@
|
||||
# creport
|
||||
This module is a reimplementation of the Horizon OS's `creport` system module, which is responsible for managing crash reports.
|
||||
|
||||
Atmosphère's reimplementation redirects writing of generated crash reports to the SD card under the folder `/atmosphere/crash_reports/`. It also prevents the automatic uploading of said crash reports.
|
42
docs/components/modules/dmnt.md
Normal file
42
docs/components/modules/dmnt.md
Normal file
@ -0,0 +1,42 @@
|
||||
# dmnt
|
||||
This module is a reimplementation of the Horizon OS's `dmnt` system module, which provides a debug monitor.
|
||||
|
||||
## Extensions
|
||||
Atmosphère implements an extension to provide cheat code functionality.
|
||||
|
||||
### Cheat Service
|
||||
A HIPC service API is provided for interacting with the cheat code manager through the service `dmnt:cht`. See [here](../../features/cheats.md) for more information on the cheat code format.
|
||||
|
||||
The SwIPC definition for `dmnt:cht` follows:
|
||||
```
|
||||
interface ams::dmnt::cheat::CheatService is dmnt:cht {
|
||||
[65000] HasCheatProcess() -> sf::Out<bool> out;
|
||||
[65001] GetCheatProcessEvent() -> sf::OutCopyHandle out_event;
|
||||
[65002] GetCheatProcessMetadata() -> sf::Out<CheatProcessMetadata> out_metadata;
|
||||
[65003] ForceOpenCheatProcess();
|
||||
[65004] PauseCheatProcess();
|
||||
[65005] ResumeCheatProcess();
|
||||
|
||||
[65100] GetCheatProcessMappingCount() -> sf::Out<u64> out_count;
|
||||
[65101] GetCheatProcessMappings(u64 offset) -> sf::OutArray<MemoryInfo> &mappings, sf::Out<u64> out_count;
|
||||
[65102] ReadCheatProcessMemory(u64 address, u64 out_size) -> sf::OutBuffer &buffer;
|
||||
[65103] WriteCheatProcessMemory(sf::InBuffer &buffer, u64 address, u64 in_size);
|
||||
[65104] QueryCheatProcessMemory(u64 address) -> sf::Out<MemoryInfo> mapping;
|
||||
|
||||
[65200] GetCheatCount() -> sf::Out<u64> out_count;
|
||||
[65201] GetCheats(u64 offset) -> sf::OutArray<CheatEntry> &cheats, sf::Out<u64> out_count;
|
||||
[65202] GetCheatById(u32 cheat_id) -> sf::Out<CheatEntry> cheat;
|
||||
[65203] ToggleCheat(u32 cheat_id);
|
||||
[65204] AddCheat(CheatDefinition &cheat, bool enabled) -> sf::Out<u32> out_cheat_id;
|
||||
[65205] RemoveCheat(u32 cheat_id);
|
||||
[65206] ReadStaticRegister(u8 which) -> sf::Out<u64> out;
|
||||
[65207] WriteStaticRegister(u8 which, u64 value);
|
||||
[65208] ResetStaticRegisters();
|
||||
|
||||
[65300] GetFrozenAddressCount() -> sf::Out<u64> out_count;
|
||||
[65301] GetFrozenAddresses(u64 offset) ->sf::OutArray<FrozenAddressEntry> &addresses, sf::Out<u64> out_count;
|
||||
[65302] GetFrozenAddress(u64 address) -> sf::Out<FrozenAddressEntry> entry;
|
||||
[65303] EnableFrozenAddress(u64 address, u64 width) -> sf::Out<u64> out_value;
|
||||
[65304] DisableFrozenAddress(u64 address);
|
||||
}
|
||||
```
|
4
docs/components/modules/eclct.stub.md
Normal file
4
docs/components/modules/eclct.stub.md
Normal file
@ -0,0 +1,4 @@
|
||||
# eclct.stub
|
||||
This module is a reimplementation of the Horizon OS's `eclct` system module, which collects error reports.
|
||||
|
||||
Atmosphère's reimplementation is a stub to remove any and all functionality pertaining to error report collection.
|
4
docs/components/modules/erpt.md
Normal file
4
docs/components/modules/erpt.md
Normal file
@ -0,0 +1,4 @@
|
||||
# erpt
|
||||
This module is a reimplementation of the Horizon OS's `erpt` system module, which is responsible for managing error reports.
|
||||
|
||||
Atmosphère's reimplementation redirects writing of generated error reports to the SD card under the folder `/atmosphere/erpt_reports/`.
|
4
docs/components/modules/fatal.md
Normal file
4
docs/components/modules/fatal.md
Normal file
@ -0,0 +1,4 @@
|
||||
# fatal
|
||||
This module is a reimplementation of the Horizon OS's `fatal` system module, which is responsible for managing fatal reports.
|
||||
|
||||
Atmosphère's reimplementation prevents error report creation and draws a custom error screen, showing registers and a backtrace. It also attempts to gather debugging info for any and all crashes and tries to save reports to the SD card under the folder `/atmosphere/fatal_reports/`.
|
4
docs/components/modules/jpegdec.md
Normal file
4
docs/components/modules/jpegdec.md
Normal file
@ -0,0 +1,4 @@
|
||||
# jpegdec
|
||||
This module is a reimplementation of the Horizon OS's `jpegdec` system module, which is responsible for JPEG format decoding.
|
||||
|
||||
Atmosphère's reimplementation allows two sessions instead of 1, so homebrew can use it for software JPEG decoding in addition to the OS itself.
|
106
docs/components/modules/loader.md
Normal file
106
docs/components/modules/loader.md
Normal file
@ -0,0 +1,106 @@
|
||||
# loader
|
||||
This module is a reimplementation of the Horizon OS's `ldr` system module, which is responsible for creating processes from executable NSO images and registering their access control.
|
||||
|
||||
## Extensions
|
||||
Atmosphère extends this module to allow executables to be replaced or patched by files stored on the SD card. Note that a few services are required for SD card access and therefore cannot be replaced or patched in this manner.
|
||||
|
||||
### Exefs Replacement
|
||||
Atmosphère's reimplementation allows replacing executable files in the file system.
|
||||
|
||||
#### Partition Replacement
|
||||
It is possible to replace the full exefs partition at once with a PFS0 file. In that case, Atmosphère will load the following file:
|
||||
```
|
||||
/atmosphere/contents/<program id>/exefs.nsp
|
||||
```
|
||||
|
||||
#### File Replacement
|
||||
When a process is created, loader will search for several NSO filenames in the program's exefs directory.
|
||||
These filenames are, in this order:
|
||||
- rtld
|
||||
- main
|
||||
- subsdk0
|
||||
- subsdk1
|
||||
- ...
|
||||
- subsdk9
|
||||
- sdk
|
||||
|
||||
Each NSO that is found will be loaded into the process contiguously. The process's entrypoint is at the first NSO to be loaded, usually `rtld` or `main`.
|
||||
|
||||
Additionally, when a process is loaded, loader will search for a `main.npdm` file in the exefs directory specifying the program's permissions.
|
||||
|
||||
Atmosphère extends this functionality by also searching for these files on the SD card. When searching for a file, loader will first check if it exists on the SD card. If it does, that file will be used instead. Otherwise, it will use the copy located in the exefs, if that is present. The following directory will be searched:
|
||||
```
|
||||
/atmosphere/contents/<program id>/exefs/
|
||||
```
|
||||
|
||||
This allows the replacement of applets, system modules, or even games with homebrew versions.
|
||||
|
||||
##### File Stubbing
|
||||
In order to prevent an NSO from being loaded even if it exists in the exefs, loader will also check if a stub file exists. If such a file exists, the NSO will not be loaded. The files should be named like `rtld.stub`, `main.stub`, etc. and may be empty.
|
||||
|
||||
##### Technical Semantics
|
||||
|
||||
loader's semantics for content override can (as you may observe from reading the above) be complicated to understand. The following is an abbreviated description of the very technical semantics by which loader decides what content to read when trying to read a file for a program id.
|
||||
|
||||
* If an external content filesystem exists for the program id, the external content filesystem is used directly with no further redirection.
|
||||
* Otherwise, if the program ID is being overridden with [nx-hbloader](https://github.com/switchbrew/nx-hbloader/releases) (see Homebrew Support below), the nsp filesystem for hbl is used directly with no further redirection.
|
||||
* Otherwise, if content redirection is enabled for the program ID (controlled by a configurable button combination) and a loose file exists on the SD card, the loose file is used.
|
||||
* Otherwise, if a stub file exists, a "Not Found" error is returned.
|
||||
* Otherwise, if an SD card executable filesystem ("exefs.nsp") exists, it is used without further redirection.
|
||||
* Finally, the "real"/base code file system is used without further redirection.
|
||||
|
||||
In addition, there are a few other technical details relevant to Atmosphere's redirection:
|
||||
* When overriding with nx-hbloader, the real code filesystem must exist. When "main.npdm" (a program capabilities descriptor file) is read, the content from the real code filesystem is read in order to determine whether an applet or an application is being overridden. This allows nx-hbloader to automatically support both applet and application environments.
|
||||
* When overriding applications, the real code filesystem must exist and contain valid content. This is required to perform accurate-to-Nintendo content verification procedures.
|
||||
* When programs are launched, both a program id and a "storage id" are specified by the launch requester. When the storage id specified is "none" (normally always invalid), Atmosphere assumes that a custom system module is attempting to be launched. This removes the aforementioned requirement on base content validity; the above procedure is still used to determine how to redirect content, however reads to the "real"/base code file system may return "Not Found" errors if the real/base code file system does not exist.
|
||||
|
||||
### NSO Patching
|
||||
When an NSO is loaded, Atmosphère's reimplementation will search for IPS patch files on the SD card in the following locations.
|
||||
```
|
||||
/atmosphere/exefs_patches/<patchset name>/<nso build id>.ips
|
||||
```
|
||||
|
||||
This organization allows patch sets affecting multiple NSOs to be distributed as a single directory and also allows patches from multiple patch sets to be stacked. Patches will be searched for in each patch set directory. The name of each patch file should match the hexadecimal build ID of the NSO to affect, except that trailing zero bytes may be left off. Because the NSO build ID is unique for every NSO, this means patches will only apply to the files they are meant to apply to.
|
||||
|
||||
Patch files are accepted in either IPS format or IPS32 format.
|
||||
|
||||
Because NSO files are compressed, patch files are not made between the original version of a compressed NSO and the modified version of such an NSO. Instead, they are made between the uncompressed version of an NSO and the modified (and still uncompressed) version of that NSO. This also means that a patch file cannot be manually applied to the compressed version of an NSO; it must be applied to the uncompressed version. Atmosphère's reimplementation will correctly apply these patches while loading the process regardless of whether the NSO it finds is compressed or not.
|
||||
|
||||
When authoring patches, [hactool](https://github.com/SciresM/hactool) can be used to find an NSO's build ID and to uncompress NSOs. Recent versions of the [ReSwitched IDA loaders](https://github.com/reswitched/loaders) can be used to load uncompressed NSOs into IDA in such a way that you can [apply patches to the input file](https://www.hex-rays.com/products/ida/support/idadoc/1618.shtml). From there, any IPS tool can be used to create the patch between the original NSO and the patched NSO. Note that if the NSO you are patching is larger than 16 MiB, you will have to use a tool that supports IPS32.
|
||||
|
||||
### Homebrew Support
|
||||
Atmosphère provides first class support for [nx-hbloader](https://github.com/switchbrew/nx-hbloader/releases) and [nx-hbmenu](https://github.com/switchbrew/nx-hbmenu/releases).
|
||||
|
||||
Launching of the nx-hbloader process is controlled by configurable button inputs. See [here](../../features/configurations.md) for more detailed information.
|
||||
|
||||
In addition, loader has extensions to enable homebrew to launch web applets. This normally requires the application launching the applet to have HTML Manual content inside an installed NCA. Atmosphère's reimplementation will automatically ensure that the commands used to check this succeed, and will redirect the relevant file system to the `/atmosphere/hbl_html/` subdirectory.
|
||||
|
||||
### IPC Commands
|
||||
Atmosphère's reimplementation extends the HIPC loader services' API with several custom commands.
|
||||
|
||||
The SwIPC definition for the `ldr:pm` extension commands follows:
|
||||
```
|
||||
interface ams::ldr::pm::ProcessManagerInterface is ldr:pm {
|
||||
...
|
||||
[65000] AtmosphereHasLaunchedProgram(ncm::ProgramId program_id) -> sf::Out<bool> out;
|
||||
[65001] AtmosphereGetProgramInfo(ncm::ProgramLocation &loc) -> sf::Out<ProgramInfo> out_program_info, sf::Out<cfg::OverrideStatus> out_status;
|
||||
[65002] AtmospherePinProgram(ncm::ProgramLocation &loc, cfg::OverrideStatus &override_status) -> sf::Out<PinId> out_id;
|
||||
}
|
||||
```
|
||||
|
||||
The SwIPC definition for the `ldr:dmnt` extension commands follows:
|
||||
```
|
||||
interface ams::ldr::dmnt::DebugMonitorInterface is ldr:dmnt {
|
||||
...
|
||||
[65000] AtmosphereHasLaunchedProgram(ncm::ProgramId program_id) -> sf::Out<bool> out;
|
||||
}
|
||||
```
|
||||
|
||||
The SwIPC definition for the `ldr:shel` extension commands follows:
|
||||
```
|
||||
interface ams::ldr::shell::ShellInterface is ldr:shel {
|
||||
...
|
||||
[65000] AtmosphereRegisterExternalCode(ncm::ProgramId program_id) -> sf::OutMoveHandle out;
|
||||
[65001] AtmosphereUnregisterExternalCode(ncm::ProgramId program_id);
|
||||
}
|
||||
```
|
4
docs/components/modules/ncm.md
Normal file
4
docs/components/modules/ncm.md
Normal file
@ -0,0 +1,4 @@
|
||||
# ncm
|
||||
This module is a reimplementation of the Horizon OS's `ncm` system module, which is responsible for content management.
|
||||
|
||||
Atmosphère's reimplementation is currently opt-in only. See [here](../../features/configurations.md) for more information.
|
4
docs/components/modules/pgl.md
Normal file
4
docs/components/modules/pgl.md
Normal file
@ -0,0 +1,4 @@
|
||||
# pgl
|
||||
This module is a reimplementation of the Horizon OS's `pgl` system module, which is responsible for launching programs and was introduced by firmware version `10.0.0`.
|
||||
|
||||
Currently, Atmosphère's reimplementation doesn't backport this module's functionalities to firmware versions lower than `10.0.0`.
|
30
docs/components/modules/pm.md
Normal file
30
docs/components/modules/pm.md
Normal file
@ -0,0 +1,30 @@
|
||||
# pm
|
||||
This module is a reimplementation of the Horizon OS's `pm` system module, which is responsible for tracking running processes on the system, and managing resource limits.
|
||||
|
||||
## Extensions
|
||||
Atmosphère extends this module with extra IPC commands and memory restriction changes.
|
||||
|
||||
### IPC Commands
|
||||
Atmosphère's reimplementation extends the HIPC loader services' API with several custom commands.
|
||||
|
||||
The SwIPC definition for the `pm:dmnt` extension commands follows:
|
||||
```
|
||||
interface ams::pm::dmnt::DebugMonitorServiceBase is pm:dmnt {
|
||||
...
|
||||
[65000] AtmosphereGetProcessInfo(os::ProcessId process_id) -> sf::OutCopyHandle out_process_handle, sf::Out<ncm::ProgramLocation> out_loc, sf::Out<cfg::OverrideStatus> out_status;
|
||||
[65001] AtmosphereGetCurrentLimitInfo(u32 group, u32 resource) -> sf::Out<s64> out_cur_val, sf::Out<s64> out_lim_val;
|
||||
}
|
||||
```
|
||||
|
||||
The SwIPC definition for the `pm:info` extension commands follows:
|
||||
```
|
||||
interface ams::pm::info::InformationService is pm:info {
|
||||
...
|
||||
[65000] AtmosphereGetProcessId(ncm::ProgramId program_id) -> sf::Out<os::ProcessId> out;
|
||||
[65001] AtmosphereHasLaunchedProgram(ncm::ProgramId program_id) -> sf::Out<bool> out;
|
||||
[65002] AtmosphereGetProcessInfo(os::ProcessId process_id) -> sf::Out<ncm::ProgramLocation> out_loc, sf::Out<cfg::OverrideStatus> out_status;
|
||||
}
|
||||
```
|
||||
|
||||
### Extra System Memory
|
||||
Atmosphère's reimplementation shrinks the APPLET memory pool by 24 MiB by default, giving this memory to the SYSTEM pool. This allows custom system modules to use more memory without hitting the SYSTEM memory limit.
|
16
docs/components/modules/ro.md
Normal file
16
docs/components/modules/ro.md
Normal file
@ -0,0 +1,16 @@
|
||||
# ro
|
||||
This module is a reimplementation of the Horizon OS's `ro` system module, which is responsible for loading dynamic libraries and was introduced by firmware version `3.0.0`.
|
||||
|
||||
Atmosphère's reimplementation backports this module's functionalities to firmware versions lower than `3.0.0` where said functionalities were provided by the `ldr` system module instead.
|
||||
|
||||
## Extensions
|
||||
Atmosphère extends this module to allow libraries to be patched by files stored on the SD card.
|
||||
|
||||
### NRO Patching
|
||||
When an NRO is loaded, Atmosphère's reimplementation will search for IPS patch files on the SD card in the following locations.
|
||||
```
|
||||
/atmosphere/nro_patches/<patchset name>/<nro build id>.ips
|
||||
```
|
||||
This organization allows patch sets affecting multiple NROs to be distributed as a single directory. Patches will be searched for in each patch set directory. The name of each patch file should match the hexadecimal build ID of the NRO to affect, except that trailing zero bytes may be left off. Because the NRO build ID is unique for every NRO, this means patches will only apply to the files they are meant to apply to.
|
||||
|
||||
Patch files are accepted in either IPS format or IPS32 format.
|
47
docs/components/modules/sm.md
Normal file
47
docs/components/modules/sm.md
Normal file
@ -0,0 +1,47 @@
|
||||
# sm
|
||||
This module is a reimplementation of the Horizon OS's `sm` system module, which is responsible for service management.
|
||||
|
||||
## Extensions
|
||||
Atmosphère extends this module with extra IPC commands and new services.
|
||||
|
||||
### Debug Monitor
|
||||
Atmosphère's reimplementation provides an interface `sm:dmnt` to allow a debug monitor to query the service manager's state.
|
||||
|
||||
The SwIPC definition for `sm:dmnt` follows:
|
||||
```
|
||||
interface ams::sm::DmntService is sm:dmnt {
|
||||
[65000] AtmosphereGetRecord(ServiceName service) -> sf::Out<ServiceRecord> record;
|
||||
[65001] AtmosphereListRecords(u64 offset) -> sf::OutArray<ServiceRecord> &records, sf::Out<u64> out_count;
|
||||
[65002] AtmosphereGetRecordSize() -> sf::Out<u64> record_size;
|
||||
}
|
||||
```
|
||||
|
||||
### IPC Commands
|
||||
Atmosphère's reimplementation extends the HIPC loader services' API with several custom commands.
|
||||
|
||||
The SwIPC definition for the `sm:` extension commands follows:
|
||||
```
|
||||
interface ams::sm::UserService is sm: {
|
||||
...
|
||||
[65000] AtmosphereInstallMitm(ServiceName service) -> sf::OutMoveHandle srv_h, sf::OutMoveHandle qry_h;
|
||||
[65001] AtmosphereUninstallMitm(ServiceName service);
|
||||
[65002] Deprecated_AtmosphereAssociatePidTidForMitm();
|
||||
[65003] AtmosphereAcknowledgeMitmSession(ServiceName service) -> sf::Out<MitmProcessInfo> client_info, sf::OutMoveHandle fwd_h;
|
||||
[65004] AtmosphereHasMitm(ServiceName service) -> sf::Out<bool> out;
|
||||
[65005] AtmosphereWaitMitm(ServiceName service);
|
||||
[65006] AtmosphereDeclareFutureMitm(ServiceName service);
|
||||
|
||||
[65100] AtmosphereHasService(ServiceName service) -> sf::Out<bool> out;
|
||||
[65101] AtmosphereWaitService(ServiceName service);
|
||||
}
|
||||
```
|
||||
|
||||
The SwIPC definition for the `sm:m` extension commands follows:
|
||||
```
|
||||
interface ams::sm::ManagerService is sm:m {
|
||||
...
|
||||
[65000] AtmosphereEndInitDefers(os::ProcessId process_id, sf::InBuffer &acid_sac, sf::InBuffer &aci_sac);
|
||||
[65001] AtmosphereHasMitm(ServiceName service) -> sf::Out<bool> out;
|
||||
[65002] AtmosphereRegisterProcess(os::ProcessId process_id, ncm::ProgramId program_id, cfg::OverrideStatus override_status, sf::InBuffer &acid_sac, sf::InBuffer &aci_sac);
|
||||
}
|
||||
```
|
2
docs/components/modules/spl.md
Normal file
2
docs/components/modules/spl.md
Normal file
@ -0,0 +1,2 @@
|
||||
# spl
|
||||
This module is a reimplementation of the Horizon OS's `spl` system module, which is responsible for providing secure platform services such as cryptographic operations.
|
14
docs/components/sept.md
Normal file
14
docs/components/sept.md
Normal file
@ -0,0 +1,14 @@
|
||||
# sept
|
||||
Sept is a payload that facilitates booting Atmosphère when targeting firmware version 7.0.0+.
|
||||
|
||||
It consists of a primary and a secondary payload.
|
||||
|
||||
## sept-primary
|
||||
sept-primary is essentially a stand-in for Nintendo's package1ldr, on 7.0.0+. To use it, the caller (normally fusée-secondary) loads the sept-primary binary to `0x4003F000`, loads the 7.0.0+ TSEC firmware to `0x40010F00`, and loads a signed, encrypted payload to `0x40016FE0`.
|
||||
|
||||
This signed, encrypted payload is normally sept-secondary.
|
||||
|
||||
## sept-secondary
|
||||
sept-secondary is a payload that performs 7.0.0+ key derivation, and then chainloads to `sept/payload.bin`.
|
||||
|
||||
It is normally stored encrypted/signed. Therefore, if one wishes to build sept-secondary instead of using release builds, one must bring their own keys.
|
@ -1,10 +1,21 @@
|
||||
# Stratosphère
|
||||
Stratosphère allows customization of the Horizon OS and Switch kernel. It includes custom sysmodules that extend the kernel and provide new features. It also includes a reimplementation of the loader sysmodules to hook important system actions.
|
||||
# stratosphère
|
||||
stratosphère provides customization of the Horizon OS at the system level. This includes a reimplementation of several system modules and additional, custom system modules that extend or add a variety of features.
|
||||
|
||||
The sysmodules that Stratosphère includes are:
|
||||
+ [boot](../modules/boot.md): This module boots the system and initalizes hardware.
|
||||
+ [creport](../modules/creport.md): Reimplementation of Nintendo’s crash report system. Dumps all error logs to the SD card instead of saving them to the NAND and sending them to Nintendo.
|
||||
+ [fs_mitm](../modules/fs_mitm.md): This module can log, deny, delay, replace, and redirect any request made to the File System.
|
||||
+ [loader](../modules/loader.md): Enables modifying the code of binaries that are not stored inside the kernel.
|
||||
+ [pm](../modules/pm.md): Reimplementation of Nintendo’s Process Manager.
|
||||
+ [sm](../modules/sm.md): Reimplementation of Nintendo’s Service Manager.
|
||||
## Modules
|
||||
The modules currently provided by stratosphère are:
|
||||
+ [ams_mitm](modules/ams_mitm.md)
|
||||
+ [boot](modules/boot.md)
|
||||
+ [boot2](modules/boot2.md)
|
||||
+ [creport](modules/creport.md)
|
||||
+ [dmnt](modules/dmnt.md)
|
||||
+ [eclct.stub](modules/eclct.stub.md)
|
||||
+ [erpt](modules/erpt.md)
|
||||
+ [fatal](modules/fatal.md)
|
||||
+ [jpegdec](modules/jpegdec.md)
|
||||
+ [loader](modules/loader.md)
|
||||
+ [ncm](modules/ncm.md)
|
||||
+ [pgl](modules/pgl.md)
|
||||
+ [pm](modules/pm.md)
|
||||
+ [ro](modules/ro.md)
|
||||
+ [sm](modules/sm.md)
|
||||
+ [spl](modules/spl.md)
|
||||
|
@ -1,4 +1,3 @@
|
||||
# Thermosphère
|
||||
Thermosphère is a hypervisor based implementation of emuNAND.
|
||||
|
||||
Thermosphère is currently planned to be included in a future release of Atmosphère.
|
||||
# thermosphère
|
||||
thermosphère is a work in progress hypervisor implementation.
|
||||
This aims to provide functionality at the EL2 level which remains unused by the Horizon OS.
|
||||
|
@ -1,2 +1,5 @@
|
||||
# Troposphère
|
||||
Troposphère contains various application-level modifications to the OS, such as launching homebrew directly from the homemenu or executing cheat/gameshark codes, similar to Luma3DS. Troposphère is not yet implemented in Atmosphère.
|
||||
# troposphère
|
||||
troposphère provides customization of the Horizon OS at the application level.
|
||||
|
||||
## reboot_to_payload
|
||||
Sample application to perform a system reboot into a payload of choice.
|
||||
|
@ -6,25 +6,24 @@ By default, Atmosphère will do the following when deciding whether to attach to
|
||||
|
||||
+ Retrieve information about the new application process from `pm` and `loader`.
|
||||
+ Check whether a user-defined key combination is held, and stop if not.
|
||||
+ This defaults to "L is not held", and can be configured the same way as `fs.mitm` override keys.
|
||||
+ This defaults to "L is not held", but can be configured with override keys.
|
||||
+ The ini key to configure this is `cheat_enable_key`.
|
||||
+ Check whether the process is a real application, and stop if not.
|
||||
+ This guards against applying cheat codes to the homebrew loader.
|
||||
+ Attempt to load cheats from `atmosphere/titles/<title_id>/cheats/<build_id>.txt`, where `build_id` is the hexadecimal representation of the first 8 bytes of the application's main executable's build id.
|
||||
+ This guards against applying cheat codes to the Homebrew Loader.
|
||||
+ Attempt to load cheats from `/atmosphere/contents/<program_id>/cheats/<build_id>.txt`, where `build_id` is the hexadecimal representation of the first 8 bytes of the application's main executable's build id.
|
||||
+ If no cheats are found, then the cheat manager will stop.
|
||||
+ Open a kernel debug session for the new application process.
|
||||
+ Signal to a system event that a new cheat process has been attached to.
|
||||
|
||||
This behavior ensures that cheat codes are only loaded when the user would want them to.
|
||||
|
||||
In cases where dmnt has not activated the cheat manager, but the user wants to make it do so anyway, the cheat manager's service API provides a `ForceOpenCheatProcess` command that homebrew can use. This command will cause the cheat manager to try to force itself to attach to the process.
|
||||
In cases where `dmnt` has not activated the cheat manager, but the user wants to make it do so anyway, the cheat manager's service API provides a `ForceOpenCheatProcess` command that homebrew can use. This command will cause the cheat manager to try to force itself to attach to the process.
|
||||
|
||||
By default, all cheat codes listed in the loaded .txt file will be toggled on. This is configurable by the user, and the default can be set to toggled off by editing the `atmosphere!dmnt_cheats_enabled_by_default` entry to 0 instead of 1.
|
||||
By default, all cheat codes listed in the loaded .txt file will be toggled on. This is configurable by the user by editing the `atmosphere!dmnt_cheats_enabled_by_default` [system setting](configurations.md).
|
||||
|
||||
Users may use homebrew programs to toggle cheats on and off at runtime via the cheat manager's service API.
|
||||
|
||||
## Cheat Code Compatibility
|
||||
|
||||
Atmosphère manages cheat code through the execution of a small, custom virtual machine. Care has been taken to ensure that Atmosphère's cheat code format is fully backwards compatible with the pre-existing cheat code format, though new features have been added and bugs in the pre-existing cheat code applier have been fixed. Here is a short summary of the changes from the pre-existing format:
|
||||
|
||||
+ A number of bugs were fixed in the processing of conditional instructions.
|
||||
@ -37,21 +36,18 @@ Atmosphère manages cheat code through the execution of a small, custom virtual
|
||||
+ The pre-existing implementation did not correctly synchronize with the application process, and thus would cause heavy lag under certain circumstances (especially around loading screens). This has been fixed in Atmosphère's implementation.
|
||||
|
||||
## Cheat Code Format
|
||||
|
||||
The following provides documentation of the instruction format for the virtual machine used to manage cheat codes.
|
||||
|
||||
Typically, instruction type is encoded in the upper nybble of the first instruction u32.
|
||||
|
||||
### Code Type 0: Store Static Value to Memory
|
||||
|
||||
Code type 0 allows writing a static value to a memory address.
|
||||
|
||||
#### Encoding
|
||||
|
||||
`0TMR00AA AAAAAAAA VVVVVVVV (VVVVVVVV)`
|
||||
|
||||
+ T: width of memory write (1, 2, 4, or 8 bytes)
|
||||
+ M: memory region to write to (0 = Main NSO, 1 = Heap)
|
||||
+ T: Width of memory write (1, 2, 4, or 8 bytes).
|
||||
+ M: Memory region to write to (0 = Main NSO, 1 = Heap).
|
||||
+ R: Register to use as an offset from memory region base.
|
||||
+ A: Immediate offset to use from memory region base.
|
||||
+ V: Value to write.
|
||||
@ -59,23 +55,20 @@ Code type 0 allows writing a static value to a memory address.
|
||||
---
|
||||
|
||||
### Code Type 1: Begin Conditional Block
|
||||
|
||||
Code type 1 performs a comparison of the contents of memory to a static value.
|
||||
|
||||
If the condition is not met, all instructions until the appropriate conditional block terminator are skipped.
|
||||
|
||||
#### Encoding
|
||||
|
||||
`1TMC00AA AAAAAAAA VVVVVVVV (VVVVVVVV)`
|
||||
|
||||
+ T: width of memory write (1, 2, 4, or 8 bytes)
|
||||
+ M: memory region to write to (0 = Main NSO, 1 = Heap)
|
||||
+ T: Width of memory write (1, 2, 4, or 8 bytes).
|
||||
+ M: Memory region to write to (0 = Main NSO, 1 = Heap).
|
||||
+ C: Condition to use, see below.
|
||||
+ A: Immediate offset to use from memory region base.
|
||||
+ V: Value to compare to.
|
||||
|
||||
#### Conditions
|
||||
|
||||
+ 1: >
|
||||
+ 2: >=
|
||||
+ 3: <
|
||||
@ -86,28 +79,23 @@ If the condition is not met, all instructions until the appropriate conditional
|
||||
---
|
||||
|
||||
### Code Type 2: End Conditional Block
|
||||
|
||||
Code type 2 marks the end of a conditional block (started by Code Type 1 or Code Type 8).
|
||||
|
||||
#### Encoding
|
||||
|
||||
`20000000`
|
||||
|
||||
---
|
||||
|
||||
### Code Type 3: Start/End Loop
|
||||
|
||||
Code type 3 allows for iterating in a loop a fixed number of times.
|
||||
|
||||
#### Start Loop Encoding
|
||||
|
||||
`300R0000 VVVVVVVV`
|
||||
|
||||
+ R: Register to use as loop counter.
|
||||
+ V: Number of iterations to loop.
|
||||
|
||||
#### End Loop Encoding
|
||||
|
||||
`310R0000`
|
||||
|
||||
+ R: Register to use as loop counter.
|
||||
@ -115,11 +103,9 @@ Code type 3 allows for iterating in a loop a fixed number of times.
|
||||
---
|
||||
|
||||
### Code Type 4: Load Register with Static Value
|
||||
|
||||
Code type 4 allows setting a register to a constant value.
|
||||
|
||||
#### Encoding
|
||||
|
||||
`400R0000 VVVVVVVV VVVVVVVV`
|
||||
|
||||
+ R: Register to use.
|
||||
@ -128,38 +114,33 @@ Code type 4 allows setting a register to a constant value.
|
||||
---
|
||||
|
||||
### Code Type 5: Load Register with Memory Value
|
||||
|
||||
Code type 5 allows loading a value from memory into a register, either using a fixed address or by dereferencing the destination register.
|
||||
|
||||
#### Load From Fixed Address Encoding
|
||||
|
||||
`5TMR00AA AAAAAAAA`
|
||||
|
||||
+ T: width of memory read (1, 2, 4, or 8 bytes)
|
||||
+ M: memory region to write to (0 = Main NSO, 1 = Heap)
|
||||
+ T: Width of memory read (1, 2, 4, or 8 bytes).
|
||||
+ M: Memory region to write to (0 = Main NSO, 1 = Heap).
|
||||
+ R: Register to load value into.
|
||||
+ A: Immediate offset to use from memory region base.
|
||||
|
||||
#### Load from Register Address Encoding
|
||||
|
||||
`5TMR10AA AAAAAAAA`
|
||||
|
||||
+ T: width of memory read (1, 2, 4, or 8 bytes)
|
||||
+ M: memory region to write to (0 = Main NSO, 1 = Heap)
|
||||
+ T: Width of memory read (1, 2, 4, or 8 bytes).
|
||||
+ M: Memory region to write to (0 = Main NSO, 1 = Heap).
|
||||
+ R: Register to load value into.
|
||||
+ A: Immediate offset to use from register R.
|
||||
|
||||
---
|
||||
|
||||
### Code Type 6: Store Static Value to Register Memory Address
|
||||
|
||||
Code type 6 allows writing a fixed value to a memory address specified by a register.
|
||||
|
||||
#### Encoding
|
||||
|
||||
`6T0RIor0 VVVVVVVV VVVVVVVV`
|
||||
|
||||
+ T: width of memory write (1, 2, 4, or 8 bytes)
|
||||
+ T: Width of memory write (1, 2, 4, or 8 bytes).
|
||||
+ R: Register used as base memory address.
|
||||
+ I: Increment register flag (0 = do not increment R, 1 = increment R by T).
|
||||
+ o: Offset register enable flag (0 = do not add r to address, 1 = add r to address).
|
||||
@ -169,22 +150,19 @@ Code type 6 allows writing a fixed value to a memory address specified by a regi
|
||||
---
|
||||
|
||||
### Code Type 7: Legacy Arithmetic
|
||||
|
||||
Code type 7 allows performing arithmetic on registers.
|
||||
|
||||
However, it has been deprecated by Code type 9, and is only kept for backwards compatibility.
|
||||
|
||||
#### Encoding
|
||||
|
||||
`7T0RC000 VVVVVVVV`
|
||||
|
||||
+ T: width of arithmetic operation (1, 2, 4, or 8 bytes)
|
||||
+ T: Width of arithmetic operation (1, 2, 4, or 8 bytes).
|
||||
+ R: Register to apply arithmetic to.
|
||||
+ C: Arithmetic operation to apply, see below.
|
||||
+ V: Value to use for arithmetic operation.
|
||||
|
||||
#### Arithmetic Types
|
||||
|
||||
+ 0: Addition
|
||||
+ 1: Subtraction
|
||||
+ 2: Multiplication
|
||||
@ -194,11 +172,9 @@ However, it has been deprecated by Code type 9, and is only kept for backwards c
|
||||
---
|
||||
|
||||
### Code Type 8: Begin Keypress Conditional Block
|
||||
|
||||
Code type 8 enters or skips a conditional block based on whether a key combination is pressed.
|
||||
|
||||
#### Encoding
|
||||
|
||||
`8kkkkkkk`
|
||||
|
||||
+ k: Keypad mask to check against, see below.
|
||||
@ -206,7 +182,6 @@ Code type 8 enters or skips a conditional block based on whether a key combinati
|
||||
Note that for multiple button combinations, the bitmasks should be ORd together.
|
||||
|
||||
#### Keypad Values
|
||||
|
||||
Note: This is the direct output of `hidKeysDown()`.
|
||||
|
||||
+ 0000001: A
|
||||
@ -239,31 +214,27 @@ Note: This is the direct output of `hidKeysDown()`.
|
||||
---
|
||||
|
||||
### Code Type 9: Perform Arithmetic
|
||||
|
||||
Code type 9 allows performing arithmetic on registers.
|
||||
|
||||
#### Register Arithmetic Encoding
|
||||
|
||||
`9TCRS0s0`
|
||||
|
||||
+ T: width of arithmetic operation (1, 2, 4, or 8 bytes)
|
||||
+ T: Width of arithmetic operation (1, 2, 4, or 8 bytes).
|
||||
+ C: Arithmetic operation to apply, see below.
|
||||
+ R: Register to store result in.
|
||||
+ S: Register to use as left-hand operand.
|
||||
+ s: Register to use as right-hand operand.
|
||||
|
||||
#### Immediate Value Arithmetic Encoding
|
||||
|
||||
`9TCRS100 VVVVVVVV (VVVVVVVV)`
|
||||
|
||||
+ T: width of arithmetic operation (1, 2, 4, or 8 bytes)
|
||||
+ T: Width of arithmetic operation (1, 2, 4, or 8 bytes).
|
||||
+ C: Arithmetic operation to apply, see below.
|
||||
+ R: Register to store result in.
|
||||
+ S: Register to use as left-hand operand.
|
||||
+ V: Value to use as right-hand operand.
|
||||
|
||||
#### Arithmetic Types
|
||||
|
||||
+ 0: Addition
|
||||
+ 1: Subtraction
|
||||
+ 2: Multiplication
|
||||
@ -278,14 +249,12 @@ Code type 9 allows performing arithmetic on registers.
|
||||
---
|
||||
|
||||
### Code Type 10: Store Register to Memory Address
|
||||
|
||||
Code type 10 allows writing a register to memory.
|
||||
|
||||
#### Encoding
|
||||
|
||||
`ATSRIOxa (aaaaaaaa)`
|
||||
|
||||
+ T: width of memory write (1, 2, 4, or 8 bytes)
|
||||
+ T: Width of memory write (1, 2, 4, or 8 bytes).
|
||||
+ S: Register to write to memory.
|
||||
+ R: Register to use as base address.
|
||||
+ I: Increment register flag (0 = do not increment R, 1 = increment R by T).
|
||||
@ -294,23 +263,21 @@ Code type 10 allows writing a register to memory.
|
||||
+ a: Value used as offset when O is 2, 4 or 5.
|
||||
|
||||
#### Offset Types
|
||||
|
||||
+ 0: No Offset
|
||||
+ 1: Use Offset Register
|
||||
+ 2: Use Fixed Offset
|
||||
+ 3: Memory Region + Base Register
|
||||
+ 4: Memory Region + Relative Address (ignore address register)
|
||||
+ 5: Memory Region + Relative Address + Offset Register
|
||||
|
||||
---
|
||||
|
||||
### Code Type 11: Reserved
|
||||
|
||||
Code Type 11 is currently reserved for future use.
|
||||
|
||||
---
|
||||
|
||||
### Code Type 12-15: Extended-Width Instruction
|
||||
|
||||
Code Types 12-15 signal to the VM to treat the upper two nybbles of the first dword as instruction type, instead of just the upper nybble.
|
||||
|
||||
This reserves an additional 64 opcodes for future use.
|
||||
@ -318,13 +285,11 @@ This reserves an additional 64 opcodes for future use.
|
||||
---
|
||||
|
||||
### Code Type 0xC0: Begin Register Conditional Block
|
||||
|
||||
Code type 0xC0 performs a comparison of the contents of a register and another value. This code support multiple operand types, see below.
|
||||
|
||||
If the condition is not met, all instructions until the appropriate conditional block terminator are skipped.
|
||||
|
||||
#### Encoding
|
||||
|
||||
```
|
||||
C0TcSX##
|
||||
C0TcS0Ma aaaaaaaa
|
||||
@ -335,19 +300,18 @@ C0TcS400 VVVVVVVV (VVVVVVVV)
|
||||
C0TcS5X0
|
||||
```
|
||||
|
||||
+ T: width of memory write (1, 2, 4, or 8 bytes)
|
||||
+ T: Width of memory write (1, 2, 4, or 8 bytes).
|
||||
+ c: Condition to use, see below.
|
||||
+ S: Source Register
|
||||
+ S: Source Register.
|
||||
+ X: Operand Type, see below.
|
||||
+ M: Memory Type (operand types 0 and 1)
|
||||
+ R: Address Register (operand types 2 and 3)
|
||||
+ a: Relative Address (operand types 0 and 2)
|
||||
+ r: Offset Register (operand types 1 and 3)
|
||||
+ X: Other Register (used for operand type 5)
|
||||
+ V: Value to compare to (operand type 4)
|
||||
+ M: Memory Type (operand types 0 and 1).
|
||||
+ R: Address Register (operand types 2 and 3).
|
||||
+ a: Relative Address (operand types 0 and 2).
|
||||
+ r: Offset Register (operand types 1 and 3).
|
||||
+ X: Other Register (operand type 5).
|
||||
+ V: Value to compare to (operand type 4).
|
||||
|
||||
#### Operand Type
|
||||
|
||||
+ 0: Memory Base + Relative Offset
|
||||
+ 1: Memory Base + Offset Register
|
||||
+ 2: Register + Relative Offset
|
||||
@ -356,10 +320,109 @@ C0TcS5X0
|
||||
+ 5: Other Register
|
||||
|
||||
#### Conditions
|
||||
|
||||
+ 1: >
|
||||
+ 2: >=
|
||||
+ 3: <
|
||||
+ 4: <=
|
||||
+ 5: ==
|
||||
+ 6: !=
|
||||
|
||||
---
|
||||
|
||||
### Code Type 0xC1: Save or Restore Register
|
||||
Code type 0xC1 performs saving or restoring of registers.
|
||||
|
||||
#### Encoding
|
||||
`C10D0Sx0`
|
||||
|
||||
+ D: Destination index.
|
||||
+ S: Source index.
|
||||
+ x: Operand Type, see below.
|
||||
|
||||
#### Operand Type
|
||||
+ 0: Restore register
|
||||
+ 1: Save register
|
||||
+ 2: Clear saved value
|
||||
+ 3: Clear register
|
||||
|
||||
---
|
||||
|
||||
### Code Type 0xC2: Save or Restore Register with Mask
|
||||
Code type 0xC2 performs saving or restoring of multiple registers using a bitmask.
|
||||
|
||||
#### Encoding
|
||||
`C2x0XXXX`
|
||||
|
||||
+ x: Operand Type, see below.
|
||||
+ X: 16-bit bitmask, bit i == save or restore register i.
|
||||
|
||||
#### Operand Type
|
||||
+ 0: Restore register
|
||||
+ 1: Save register
|
||||
+ 2: Clear saved value
|
||||
+ 3: Clear register
|
||||
|
||||
---
|
||||
|
||||
### Code Type 0xC3: Read or Write Static Register
|
||||
Code type 0xC3 reads or writes a static register with a given register.
|
||||
|
||||
#### Encoding
|
||||
`C3000XXx`
|
||||
|
||||
+ XX: Static register index, 0x00 to 0x7F for reading or 0x80 to 0xFF for writing.
|
||||
+ x: Register index.
|
||||
|
||||
---
|
||||
|
||||
### Code Type 0xF0: Double Extended-Width Instruction
|
||||
Code Type 0xF0 signals to the VM to treat the upper three nybbles of the first dword as instruction type, instead of just the upper nybble.
|
||||
|
||||
This reserves an additional 16 opcodes for future use.
|
||||
|
||||
---
|
||||
|
||||
### Code Type 0xFF0: Pause Process
|
||||
Code type 0xFF0 pauses the current process.
|
||||
|
||||
#### Encoding
|
||||
`FF0?????`
|
||||
|
||||
---
|
||||
|
||||
### Code Type 0xFF1: Resume Process
|
||||
Code type 0xFF1 resumes the current process.
|
||||
|
||||
#### Encoding
|
||||
`FF1?????`
|
||||
|
||||
---
|
||||
|
||||
### Code Type 0xFFF: Debug Log
|
||||
Code type 0xFFF writes a debug log to the SD card under the folder `/atmosphere/cheat_vm_logs/`.
|
||||
|
||||
#### Encoding
|
||||
```
|
||||
FFFTIX##
|
||||
FFFTI0Ma aaaaaaaa
|
||||
FFFTI1Mr
|
||||
FFFTI2Ra aaaaaaaa
|
||||
FFFTI3Rr
|
||||
FFFTI4X0
|
||||
```
|
||||
|
||||
+ T: Width of memory write (1, 2, 4, or 8 bytes).
|
||||
+ I: Log id.
|
||||
+ X: Operand Type, see below.
|
||||
+ M: Memory Type (operand types 0 and 1).
|
||||
+ R: Address Register (operand types 2 and 3).
|
||||
+ a: Relative Address (operand types 0 and 2).
|
||||
+ r: Offset Register (operand types 1 and 3).
|
||||
+ X: Value Register (operand type 4).
|
||||
|
||||
#### Operand Type
|
||||
+ 0: Memory Base + Relative Offset
|
||||
+ 1: Memory Base + Offset Register
|
||||
+ 2: Register + Relative Offset
|
||||
+ 3: Register + Offset Register
|
||||
+ 4: Register Value
|
153
docs/features/configurations.md
Normal file
153
docs/features/configurations.md
Normal file
@ -0,0 +1,153 @@
|
||||
# Configurations
|
||||
Atmosphère provides a variety of customizable configurations to better adjust to users' needs.
|
||||
|
||||
## BCT.ini
|
||||
This is the configuration file used by fusée.
|
||||
This file is located under the `/atmosphere/config/` folder on your SD card and a default template can be found inside the `/atmosphere/config_templates/` folder.
|
||||
|
||||
### Adding a Custom Boot Splashscreen
|
||||
Atmosphère provides its own default splashscreen which is displayed at boot time. However, this can be replaced at will.
|
||||
|
||||
The boot splashscreen must be a BMP file, it must be 720x1280 (1280x720 rotated 90 degrees left/counterclockwise/anti-clockwise) resolution, and be in 32-bit ARGB format. You can use image editing software such as GIMP or Photoshop to export the image in this format.
|
||||
|
||||
Add the following lines to BCT.ini and change the value of `custom_splash` to the actual path and filename of your boot splashscreen:
|
||||
```
|
||||
[stage2]
|
||||
custom_splash = /path/to/your/bootlogo.bmp
|
||||
```
|
||||
|
||||
### Configuring "nogc" Protection
|
||||
"nogc" is a feature provided by fusée-secondary which disables the Nintendo Switch's Game Card reader. Its purpose is to prevent the reader from being updated when the console has been updated, without burning fuses, from a lower firmware version. More specifically, from firmware versions 4.0.0 or 9.0.0 which introduced updates to the Game Card reader's firmware. By default, Atmosphère will protect the Game Card reader automatically, but you are free to change it.
|
||||
|
||||
To change its functionality, add the following line to the `stratosphere` section and change the value of `X` according to the following list:
|
||||
```
|
||||
[stratosphere]
|
||||
nogc = X
|
||||
```
|
||||
```
|
||||
1 = force-enable nogc, so Atmosphère will always disable the Game Card reader.
|
||||
0 = force-disable nogc, so Atmosphère will always enable the Game Card reader.
|
||||
```
|
||||
|
||||
### NCM opt-in
|
||||
Atmosphère provides a reimplementation of the [ncm](../components/modules/ncm.md) system module, but currently this is not enabled by default. If you wish to enable this reimplementation add the following line to the `stratosphere` section:
|
||||
```
|
||||
[stratosphere]
|
||||
enable_ncm = 1
|
||||
```
|
||||
|
||||
### Logging
|
||||
This is an advanced feature aimed at developers trying to debug boot time issues. It enables logging of the fusée stages to be displayed on screen.
|
||||
|
||||
Add the following lines to BCT.ini and change the value of `X` according to the following list:
|
||||
```
|
||||
[config]
|
||||
log_level = X
|
||||
```
|
||||
```
|
||||
0 = NONE
|
||||
1 = ERROR
|
||||
2 = WARNING
|
||||
3 = MANDATORY
|
||||
4 = INFO
|
||||
5 = DEBUG
|
||||
```
|
||||
|
||||
A special level is also provided to prevent prefix creation. To use it, do a bitwise OR with this mask:
|
||||
`0x100 = NO_PREFIX`
|
||||
|
||||
## emummc.ini
|
||||
This is the configuration file used for the [emummc](../components/emummc.md) component.
|
||||
This file is located under the `/emuMMC/` folder on your SD card.
|
||||
|
||||
Please refer to the project's repository [here](https://github.com/m4xw/emuMMC) for detailed instructions and documentation.
|
||||
|
||||
## exosphere.ini
|
||||
This is the configuration file used by exosphère.
|
||||
This file is located in the root of your SD card and a default template can be found inside the `/atmosphere/config_templates/` folder.
|
||||
|
||||
### Configuring Debugging Modes
|
||||
By default, Atmosphère signals to the Horizon kernel that debugging is enabled while leaving usermode debugging disabled, but this can cause undesirable side-effects. If you wish to change this behavior, go to the `exosphere` section and change the value of `X` according to the following list.
|
||||
```
|
||||
[exosphere]
|
||||
debugmode = X
|
||||
debugmode_user = X
|
||||
```
|
||||
```
|
||||
1 = enable
|
||||
0 = disable
|
||||
```
|
||||
|
||||
### Blanking PRODINFO
|
||||
Atmosphère provides a way for users to blank their factory installed calibration data (known as PRODINFO) in either emulated or system eMMC environments. You can find more detailed information on this inside the respective template file. Usage of this configuration is not encouraged.
|
||||
|
||||
## override_config.ini
|
||||
This file is located under the `/atmosphere/config/` folder on your SD card and a default template can be found inside the `/atmosphere/config_templates/` folder.
|
||||
|
||||
### Overrides Format
|
||||
Overrides are parsed from the `/atmosphere/config/override_config.ini` file during the boot process.
|
||||
|
||||
By default `override_config.ini` is not configured. It can be used to select the behavior of certain buttons and bind them to functionalities such as launching the Homebrew Menu or enabling the cheat code manager.
|
||||
|
||||
You can modify the override_key entries in `override_config.ini` with this list of valid buttons:
|
||||
| Formal Name | .ini Name |
|
||||
| ----------- | --------- |
|
||||
| A Button | A |
|
||||
| B Button | B |
|
||||
| X Button | X |
|
||||
| Y Button | Y |
|
||||
| Left Stick | LS |
|
||||
| Right Stick | RS |
|
||||
| L Button | L |
|
||||
| R Button | R |
|
||||
| ZL Button | ZL |
|
||||
| ZR Button | ZR |
|
||||
| + Button | PLUS |
|
||||
| - Button | MINUS |
|
||||
| Left Dpad | DLEFT |
|
||||
| Up Dpad | DUP |
|
||||
| Right Dpad | DRIGHT |
|
||||
| Down Dpad | DDOWN |
|
||||
| SL Button | SL |
|
||||
| SR Button | SR |
|
||||
|
||||
To invert the behavior of the override key, place an exclamation point in front of whatever button you wish to use. It will launch the actual game while holding down that button, instead of going into the Homebrew Menu. For example, `override_key=!R` will run the game only while holding down R when launching it, otherwise it will boot into the Homebrew Menu. Afterwards you may reinsert your SD card into your Switch and boot into Atmosphère as you normally would. You should now be able to boot into the Homebrew Menu by launching your designated program of choice.
|
||||
|
||||
## system_settings.ini
|
||||
This file is located under the `/atmosphere/config/` folder on your SD card and a default template can be found inside the `/atmosphere/config_templates/` folder.
|
||||
|
||||
### Settings Format
|
||||
Atmosphère provides a way to override the firmware debug settings used by the system. These can be parsed from the `/atmosphere/config/system_settings.ini` file during the boot process. This file is a normal ini file, with some specific interpretations.
|
||||
|
||||
The standard representation of a setting's identifier takes the form `name!key`. This is represented within `system_settings.ini` as a section `name`, with an entry `key`. For example:
|
||||
```
|
||||
[name]
|
||||
key = ...
|
||||
```
|
||||
|
||||
Settings can have variable types (strings, integral values, byte arrays, etc). To accommodate this, `system_settings.ini` must store values as a `type_identifier!value_store` pair. A number of different types are supported, with identifiers detailed below.
|
||||
Please note that a malformed value string will cause a fatal error to occur on boot. A full example of a custom setting is given below (setting `eupld!upload_enabled = 0`), for posterity:
|
||||
```
|
||||
[eupld]
|
||||
upload_enabled = u8!0x0
|
||||
```
|
||||
|
||||
#### Supported Types
|
||||
* Strings
|
||||
* Type identifiers: `str`, `string`
|
||||
* The value string is used directly as the setting, with null terminator appended.
|
||||
* Integral types
|
||||
* Type identifiers: `u8`, `u16`, `u32`, `u64`
|
||||
* The value string is parsed via a call to `strtoul(value, NULL, 0)`.
|
||||
* Setting bitwidth is determined by the identifier (8 for 1 byte, 16 for 2 bytes, and so on).
|
||||
* Raw bytes
|
||||
* Type identifiers: `hex`, `bytes`
|
||||
* The value string is parsed as a hexadecimal string.
|
||||
* The value string must be of even length, or a fatal error will be thrown on parse.
|
||||
|
||||
## Content Specific Flags
|
||||
Atmosphère supports customizing CFW behavior based on the presence of `flags` on the SD card.
|
||||
|
||||
The following flags are supported on a per-program basis, by placing `<flag_name>.flag` inside `/atmosphere/contents/<program_id>/flags/`:
|
||||
+ `boot2`, which indicates that the program should be launched during the `boot2` process.
|
||||
+ `redirect_save`, which indicates that the program wants its savedata to be redirected to the SD card.
|
@ -1,12 +0,0 @@
|
||||
# Flags
|
||||
Atmosphère supports customizing CFW behavior based on the presence of `flags` on the SD card.
|
||||
|
||||
The following flags are supported on a per-title basis, by placing `<flag_name>.flag` inside `/atmosphere/titles/<title_id>/flags/`:
|
||||
+ `boot2`, which indicates to PM that the title should be launched during the `boot2` process.
|
||||
+ `fsmitm`, which indicates that `fs.mitm` should override contents for the title even if it otherwise wouldn't.
|
||||
+ `fsmitm_disable`, which indicates that `fs.mitm` should not override contents for the title, even it it otherwise would.
|
||||
+ `bis_write`, which indicates that `fs.mitm` should allow the title to write to BIS partitions.
|
||||
+ `cal_read`, which indicates that `fs.mitm` should allow the title to read the CAL0/PRODINFO partition.
|
||||
|
||||
The following global flags are supported, by placing `<flag name>.flag` inside `/atmosphere/flags/`:
|
||||
+ `hbl_bis_write` and `hbl_cal_read` enable the BIS write and CAL0 read functionality for HBL, without needing to specify its title id.
|
45
docs/main.md
45
docs/main.md
@ -1,29 +1,30 @@
|
||||
# Atmosphère
|
||||
Atmosphère is a work-in-progress customized firmware for the Nintendo Switch. Atmosphère consists of several different components, each in charge of performing different system functions of the Nintendo Switch.
|
||||
Atmosphère is a work-in-progress customized firmware for the Nintendo Switch. Its design principle consists of a multi-layered approach where each layer replaces/modifies a different component of the Nintendo Switch's system.
|
||||
|
||||
The components of Atmosphère are:
|
||||
+ [Fusée](../docs/components/fusee/fusee.md), a custom bootloader.
|
||||
+ [Exosphère](../docs/components/exosphere.md), a fully-featured custom secure monitor.
|
||||
+ [Stratosphère](../docs/components/stratosphere.md), a set of custom system modules.
|
||||
+ [Thermosphère](../docs/components/thermosphere.md), a hypervisor-based emuNAND implementation. This component has not been implemented yet.
|
||||
+ [Troposphère](../docs/components/troposphere.md), Application-level patches to the Horizon OS. This component has also not been implemented yet.
|
||||
## Components
|
||||
Atmosphère provides six core components, mimicking to some degree the various layers of the Earth's atmosphere:
|
||||
+ [fusée](components/fusee.md)
|
||||
+ [exosphère](components/exosphere.md)
|
||||
+ [thermosphère](components/thermosphere.md)
|
||||
+ [mesosphère](components/mesosphere.md)
|
||||
+ [stratosphère](components/stratosphere.md)
|
||||
+ [troposphère](components/troposphere.md)
|
||||
|
||||
### Modules
|
||||
The Stratosphère component of Atmosphère contains various modules. These have a `.kip` extension. They provide custom features, extend existing features, or replace Nintendo sysmodules.
|
||||
Additionally, Atmosphère also provides the following secondary components:
|
||||
+ [emummc](components/emummc.md)
|
||||
+ [sept](components/sept.md)
|
||||
+ [libraries](components/libraries.md)
|
||||
|
||||
Stratosphère's modules include:
|
||||
+ [boot](../docs/modules/boot.md)
|
||||
+ [creport](../docs/modules/creport.md)
|
||||
+ [fs_mitm](../docs/modules/fs_mitm.md)
|
||||
+ [loader](../docs/modules/loader.md)
|
||||
+ [pm](../docs/modules/pm.md)
|
||||
+ [sm](../docs/modules/sm.md)
|
||||
## Features
|
||||
Atmosphère provides several original features which add or expand functionalities for the customized firmware environment:
|
||||
+ [Cheats](features/cheats.md)
|
||||
+ [Configurations](features/configurations.md)
|
||||
|
||||
### Building Atmosphère
|
||||
A guide to building Atmosphère can be found [here](../docs/building.md).
|
||||
## Building Atmosphère
|
||||
A guide to building Atmosphère can be found [here](building.md).
|
||||
|
||||
### Upcoming Features
|
||||
A list of planned features for Atmosphère can be found [here](../docs/roadmap.md).
|
||||
## Upcoming Features
|
||||
A list of planned features for Atmosphère can be found [here](roadmap.md).
|
||||
|
||||
### Release History
|
||||
A changelog of previous versions of Atmosphère can be found [here](../docs/changelog.md).
|
||||
## Release History
|
||||
A changelog of previous versions of Atmosphère can be found [here](changelog.md).
|
||||
|
@ -1,2 +0,0 @@
|
||||
# boot
|
||||
The boot module is responsible for booting the system and initalizing hardware. A second boot module known as boot2 is integrated with the [pm (process manager)](../modules/pm.md) sysmodule in Atmosphère, and launches other processes.
|
@ -1,2 +0,0 @@
|
||||
# creport
|
||||
creport is a reimplementation of Nintendo's crash reporter. Atmosphère's creport catches all error logs that would have been saved to the NAND and instead saves them to the SD card for debugging purposes. This is helpful because the errors no longer go to Nintendo and developers of homebrew can still see the errors to help with the debugging process. creport catches system errors, game crashes, and homebrew crashes.
|
@ -1,38 +0,0 @@
|
||||
# dmnt
|
||||
|
||||
dmnt is a reimplementation of Nintendo's debug monitor. It provides Atmosphère a rich set of debugging functionality, so that users can easily analyze the behaviors of programs. In addition, Atmosphère implements an extension in dmnt to provide cheat code functionality.
|
||||
|
||||
## Atmosphère Cheat Extension
|
||||
|
||||
In addition to the functionality provided by Nintendo's debug monitor, Atmosphère's dmnt has an extension for providing cheat code functionality. A HIPC Service API is provided for interacting with the cheat code manager, through the service `dmnt:cht`.
|
||||
|
||||
Those looking for more information on the cheat code functionality may wish to read `cheats.md`.
|
||||
|
||||
The SwIPC definition for `dmnt:cht` follows.
|
||||
```
|
||||
interface DmntCheatService is dmnt:cht {
|
||||
[65000] HasCheatProcess() -> bool;
|
||||
[65001] GetCheatProcessEvent() -> KObject;
|
||||
[65002] GetCheatProcessMetadata() -> CheatProcessMetadata;
|
||||
[65003] ForceOpenCheatProcess();
|
||||
|
||||
[65100] GetCheatProcessMappingCount() -> u64;
|
||||
[65101] GetCheatProcessMappings(u64 offset) -> buffer<MemoryInfo, 6>, u64 count;
|
||||
[65102] ReadCheatProcessMemory(u64 address, u64 size) -> buffer<u8, 6> data;
|
||||
[65103] WriteCheatProcessMemory(u64 address, u64 size, buffer<u8, 5> data);
|
||||
[65104] QueryCheatProcessMemory(u64 address) -> MemoryInfo;
|
||||
|
||||
[65200] GetCheatCount() -> u64;
|
||||
[65201] GetCheats(u64 offset) -> buffer<CheatEntry, 6>, u64 count;
|
||||
[65202] GetCheatById(u32 cheat_id) -> buffer<CheatEntry, 6> cheat;
|
||||
[65203] ToggleCheat(u32 cheat_id);
|
||||
[65204] AddCheat(buffer<CheatDefinition, 5> cheat, bool enabled) -> u32 cheat_id;
|
||||
[65203] RemoveCheat(u32 cheat_id);
|
||||
|
||||
[65300] GetFrozenAddressCount() -> u64;
|
||||
[65301] GetFrozenAddresses(u64 offset) -> buffer<FrozenAddressEntry, 6>, u64 count;
|
||||
[65302] GetFrozenAddress(u64 address) -> FrozenAddressEntry;
|
||||
[65303] EnableFrozenAddress(u64 address, u64 width) -> u64 value;
|
||||
[65304] DisableFrozenAddress(u64 address);
|
||||
}
|
||||
```
|
@ -1,2 +0,0 @@
|
||||
# fs_mitm
|
||||
fs_mitm is a sysmodule that enables intercepting file system operations. This module can log, deny, delay, replace, or redirect any request made to the filesystem. It enables LayeredFS to function, which allows for replacement of game assets.
|
@ -1,126 +0,0 @@
|
||||
# loader
|
||||
|
||||
loader is a reimplementation of the loader sysmodule. This module is responsible for creating processes from executable NSO images and registering their access control with the kernel, sm, and fs.
|
||||
|
||||
## Atmosphère Extensions
|
||||
|
||||
Atmosphère extends this module to allow executables to be replaced or patched by files stored on the SD card. Note that a few services are required for SD card access and therefore cannot be replaced or patched in this manner. This includes psc, bus, and pcv.
|
||||
|
||||
### Exefs Replacement
|
||||
|
||||
TODO: details on buttons affecting this.
|
||||
|
||||
When a process is created, loader will search for several NSO filenames in the title's exefs directory.
|
||||
These filenames are, in this order:
|
||||
|
||||
- rtld
|
||||
- main
|
||||
- subsdk0
|
||||
- subsdk1
|
||||
- ...
|
||||
- subsdk9
|
||||
- sdk
|
||||
|
||||
Each NSO that is found will be loaded into the process contiguously. The process's entrypoint is at the first NSO to be loaded, usually `rtld` or `main`.
|
||||
|
||||
Additionally, when a process is loaded, loader will search for a `main.npdm` file in the exefs directory specifying the title's permissions.
|
||||
|
||||
Atmosphère extends this functionality by also searching for these files on the SD card. When searching for a file, loader will first check if it exists on the SD card. If it does, that file will be used instead. Otherwise, it will use the copy located in the exefs, if that is present. The following directory will be searched.
|
||||
|
||||
```
|
||||
sdmc:/atmosphere/titles/<title id>/exefs/
|
||||
```
|
||||
|
||||
This allows the replacement of applets, sysmodules, or even games with homebrew versions.
|
||||
|
||||
In order to prevent an NSO from being loaded even if it exists in the exefs, loader will also check if a stub file exists. If such a file exists, the NSO will not be loaded. The files should be named like `rtld.stub`, `main.stub`, etc. and may be empty.
|
||||
|
||||
### NSO Patching
|
||||
|
||||
TODO: details on buttons affecting this.
|
||||
|
||||
When an NSO is loaded, the stratosphere implementatin of loader will search for IPS patch files on the SD card in the following locations.
|
||||
```
|
||||
sdmc:/atmosphere/exefs_patches/<patchset name>/<nso build id>.ips
|
||||
```
|
||||
This organization allows patchsets affecting multiple NSOs to be distributed as a single directory. Patches will be searched for in each patchset directory. The name of each patch file should match the hexadecimal build ID of the NSO to affect, except that trailing zero bytes may be left off. Because the NSO build ID is unique for every NSO, this means patches will only apply to the files they are meant to apply to.
|
||||
|
||||
Patch files are accepted in either IPS format or IPS32 format.
|
||||
|
||||
Because NSO files are compressed, patch files are not made between the original version of a compressed NSO and the modified version of such an NSO. Instead, they are made between the uncompressed version of an NSO and the modified (and still uncompressed) version of that NSO. This also means that a patch file cannot be manually applied to the compressed version of an NSO; it must be applied to the uncompressed version. The Stratosphere implementation of loader will correctly apply these patches while loading the process regardless of whether the NSO it finds is compressed or not.
|
||||
|
||||
When authoring patches, [hactool](https://github.com/SciresM/hactool) can be used to find an NSO's build ID and to uncompress NSOs. Recent versions of the [ReSwitched IDA loaders](https://github.com/reswitched/loaders) can be used to load uncompressed NSOs into IDA in such a way that you can [apply patches to the input file](https://www.hex-rays.com/products/ida/support/idadoc/1618.shtml). From there, any IPS tool can be used to create the patch between the original NSO and the patched NSO. Note that if the NSO you are patching is larger than 16 MiB, you will have to use a tool that supports IPS32.
|
||||
|
||||
### HBL Support
|
||||
|
||||
Atmosphère can use the loader module in order to turn any game on your Switch's home menu into a launchpoint for the Homebrew Menu, rather than launching it through the album applet. This allows one to launch the Homebrew Menu with access to the ~3.2GB of RAM that the Switch reserves for games and applications, as opposed to the 442MB of RAM we are limited to when launching the Homebrew Menu from the album. This also means that it is no longer necessary to install homebrew as `.nsp` files on your Switch so long as you are using this method, as the only reason to do so is to allow the homebrew to access all of the Switch's available memory.
|
||||
|
||||
In order to setup this method you will need the latest release of [hbmenu](https://github.com/switchbrew/nx-hbmenu/releases), and the latest release of [hbloader](https://github.com/switchbrew/nx-hbloader/releases). Place `hbmenu.nro` on the root of your Switch's SD Card, and place `hbl.nsp` in the atmosphere folder. From there, simply launch any title while holding the button specified in `loader.ini`.
|
||||
|
||||
In addition, loader has extensions to enable homebrew to launch web applets. This normally requires the application launching the applet have HTML Manual content inside an installed NCA; Atmosphère's loader will automatically ensure that the commands used to check this succeed, and will (in tandem with `fs.mitm`) redirect the relevant filesystem to the `sdmc:/atmosphere/hbl_html/` subdirectory.
|
||||
|
||||
### Button Overrides
|
||||
|
||||
By default `loader.ini` is configured to launch the Homebrew Menu when launching any game while holding down the override key (defaults to R). If you wish to change this, you can modify the override_key section of `loader.ini`. Alternatively, if you would like to only allow hbmenu on a specific app, configure `loader.ini` in the atmosphere folder by replacing the Title ID in the ini (title_id in the [hbl_config] section, it is the Title ID for the album by default) with the Title ID of whatever game you wish to use to launch the Homebrew Menu, and set override_any_app to false. A list of Title IDs for Switch Games can be found [here](https://switchbrew.org/wiki/Title_list/Games).
|
||||
|
||||
To invert the behaviour of the override key, place an exclamation point in front of whatever button you wish to use. It will launch the actual game while holding down that button, instead of going into the Homebrew Menu. For example, `override_key=!R` will run the game only while holding down R when launching it, otherwise it will boot into the Homebrew Menu. Afterwards you may reinsert your SD Card into your Switch and boot into Atmosphère as you normally would. You should now be able to boot into the Homebrew Menu by launching your designated title of choice.
|
||||
|
||||
A list of valid buttons can be found here:
|
||||
|
||||
| Formal Name | .ini Name |
|
||||
| ----------- | --------- |
|
||||
| A Button | A |
|
||||
| B Button | B |
|
||||
| X Button | X |
|
||||
| Y Button | Y |
|
||||
| Left Stick | LS |
|
||||
| Right Stick | RS |
|
||||
| L Button | L |
|
||||
| R Button | R |
|
||||
| ZL Button | ZL |
|
||||
| ZR Button | ZR |
|
||||
| + Button | PLUS |
|
||||
| - Button | MINUS |
|
||||
| Left Dpad | DLEFT |
|
||||
| Up Dpad | DUP |
|
||||
| Right Dpad | DRIGHT |
|
||||
| Down Dpad | DDOWN |
|
||||
| SL Button | SL |
|
||||
| SR Button | SR |
|
||||
|
||||
### SM MITM Integration
|
||||
|
||||
When the Stratosphere implementation of loader creates a new process, it notifies [sm](sm.md) through the `AtmosphereAssociatePidTidForMitm` command to notify any MITM services of new processes' identities.
|
||||
|
||||
### IPC: AtmosphereSetExternalContentSource and AtmosphereClearExternalContentSource
|
||||
|
||||
Two additional commands are added to the [`ldr:shel`](https://reswitched.github.io/SwIPC/ifaces.html#nn::ro::detail::ILdrShellInterface) interface, called `AtmosphereSetExternalContentSource` and `AtmosphereClearExternalContentSource`.
|
||||
Their command IDs are `65000` and `65001` on all system firmware versions.
|
||||
|
||||
`AtmosphereSetExternalContentSource` takes a `u64 tid` and returns a server-side session handle.
|
||||
The client is expected to implement the `IFileSystem` interface on the returned handle. The next
|
||||
time the title specified by the given title ID is launched, its ExeFS contents will be loaded from
|
||||
the custom `IFileSystem` instead of from SD card or original ExeFS. NSOs loaded from external
|
||||
content source may still be subject to exefs IPS patches. After the title is launched successfuly,
|
||||
the `IFileSystem` is closed and the external content source override is removed. If
|
||||
`AtmosphereSetExternalContentSource` is called on a title that already has an external content
|
||||
source set for it, the existing one will be removed and replaced with the new one. It is illegal to
|
||||
call `AtmosphereSetExternalContentSource` while the title is being launched.
|
||||
|
||||
If title launching fails, the external content source remains registered. The
|
||||
`AtmosphereClearExternalContentSource` command can be used to clear an external content source if
|
||||
title launch fails.
|
||||
|
||||
The `IFileSystem` only needs to implement `OpenFile` and `GetFileTimeStampRaw`. The paths received
|
||||
by the `IFileSystem`'s `OpenFile` command begin with slashes, as in `/main`, `/rtld`, and `/main.npdm`.
|
||||
A result code of 0x202 should be returned if the file does not exist. `GetFileTimeStampRaw` can just
|
||||
be a stub. The `IFile`s returned from `OpenFile` only need to implement `Read` and `GetSize`.
|
||||
|
||||
The SwIPC definitions for the extension commands follow.
|
||||
```
|
||||
interface nn::ldr::detail::IShellInterface is ldr:shel {
|
||||
...
|
||||
[65000] AtmosphereSetExternalContentSource(u64 tid) -> handle<copy, session_server> ifilesystem_handle;
|
||||
[65001] AtmosphereClearExternalContentSource(u64 tid);
|
||||
}
|
||||
```
|
@ -1,23 +0,0 @@
|
||||
# pm
|
||||
|
||||
pm is a reimplementation of Nintendo's process manager. This module is responsible for tracking running processes on the system, and managing resource limits. pm is also required to create and manage processes for homebrew applications.
|
||||
|
||||
## Atmosphère Extensions
|
||||
|
||||
There are a few ways in which the Stratosphere implementation of pm differs intentionally from the stock pm.
|
||||
|
||||
### IPC: AtmosphereGetProcessHandle
|
||||
|
||||
The Stratosphere implementation of pm adds an additional command to the [`pm:dmnt`](https://reswitched.github.io/SwIPC/ifaces.html#nn::pm::detail::IDebugMonitorInterface) interface, called `AtmosphereGetProcessHandle`. Its command ID is `65000` on all system firmware versions. It takes a `u64 process_id` and returns a process handle for the specified process, if that process is known. Notable exceptions include KIPs, which are not known to pm. If the specified process cannot be found, error code 0x20F is returned.
|
||||
|
||||
The SwIPC definition for this command follows.
|
||||
```
|
||||
interface nn::pm::detail::IDebugMonitorInterface is pm:dmnt {
|
||||
...
|
||||
[65000] AtmosphereGetProcessInfo(u64 pid) -> handle<copy, process> process_handle, u64 title_id, u64 storage_id;
|
||||
}
|
||||
```
|
||||
|
||||
### Extra System Memory for Sysmodules
|
||||
|
||||
The Stratosphere implementation of pm shrinks the APPLET memory pool by 24 MiB by default, giving this memory to the SYSTEM pool. This allows custom sysmodules to use more memory without hitting the SYSTEM memory limit.
|
@ -1,79 +0,0 @@
|
||||
# set_mitm
|
||||
set_mitm is a sysmodule that enables intercepting requests to the system settings service.
|
||||
|
||||
## Atmosphère Extensions
|
||||
|
||||
set_mitm intercepts the `GetFirmwareVersion` command, if the requester is `qlaunch` or `maintenance`.\
|
||||
It modifies the `display_version` field of the returned system version, causing the version to display\
|
||||
in settings as `#.#.# (AMS #.#.#)`. This allows users to easily verify what version of Atmosphère they are running.
|
||||
|
||||
set_mitm also intercepts the `GetSettingsItemValueSize` and `GetSettingsItemValue` commands for all requesters.\
|
||||
It does so in order to enable user configuration of system settings, which are parsed from `atmosphere/system_settings.ini` on boot.\
|
||||
The format for settings is described below.
|
||||
|
||||
### Atmosphère Settings Format
|
||||
|
||||
Settings are parsed from the `atmosphere/system_settings.ini` file during the boot process. This file is a normal ini file,\
|
||||
with some specific interpretations.
|
||||
|
||||
The standard representation of a system setting's identifier takes the form `name!key`. This is represented within\
|
||||
`system_settings.ini` as a section `name`, with an entry `key`. For example:
|
||||
|
||||
```
|
||||
[name]
|
||||
key = ...
|
||||
```
|
||||
|
||||
System settings can have variable types (strings, integral values, byte arrays, etc). To accommodate this, `system_settings.ini`\
|
||||
must store values as a `type_identifier!value_store` pair. A number of different types are supported, with identifiers detailed below.\
|
||||
Please note that a malformed value string will cause a fatal error to occur on boot. A full example of a custom setting is given below\
|
||||
(setting `eupld!upload_enabled = 0`), for posterity:
|
||||
|
||||
```
|
||||
[eupld]
|
||||
upload_enabled = u8!0x0
|
||||
```
|
||||
|
||||
### Supported Types
|
||||
|
||||
* Strings
|
||||
* Type identifiers: `str`, `string`
|
||||
* The value string is used directly as the setting, with null terminator appended.
|
||||
* Integral types
|
||||
* Type identifiers: `u8`, `u16`, `u32`, `u64`
|
||||
* The value string is parsed via a call to `strtoul(value, NULL, 0)`.
|
||||
* Setting bitwidth is determined by the identifier (8 for 1 byte, 16 for 2 bytes, and so on).
|
||||
* Raw bytes
|
||||
* Type identifiers: `hex`, `bytes`
|
||||
* The value string is parsed as a hexadecimal string.
|
||||
* The value string must be of even length, or a fatal error will be thrown on parse.
|
||||
|
||||
### Atmosphère Custom Settings
|
||||
|
||||
At the time of writing, Atmosphère implements two custom settings, found in the `atmosphere` section.\
|
||||
|
||||
While not used for set_mitm, `power_menu_reboot_function` is loaded and controls the reboot behaviour of the console. By default, this value\
|
||||
is "payload", where the console will automatically reboot into the RCM payload stored in `sdmc:/atmosphere/reboot_payload.bin`.\
|
||||
(This payload is also used for fatal, upon a serious crash.) Setting the value to "rcm" reboots directly into RCM, and setting the value\
|
||||
to "normal" skips these behaviours.
|
||||
|
||||
```
|
||||
[atmosphere]
|
||||
power_menu_reboot_function = str!payload
|
||||
```
|
||||
|
||||
`dmnt_cheats_enabled_by_default` controls the behaviour of dmnt's cheat functionality. By default, this value is "0x1", enabling any cheats\
|
||||
defined by the user. Check [cheats](../cheats.md) for more information about Atmosphère's cheat functionality.
|
||||
|
||||
```
|
||||
[atmosphere]
|
||||
dmnt_cheats_enabled_by_default = u8!0x1
|
||||
```
|
||||
|
||||
`dmnt_always_save_cheat_toggles` controls the behaviour of dmnt's cheat toggle functionality. By default, this value is "0x0", causing toggles to\
|
||||
only be saved on game quit if a toggle file existed on game boot. Check [cheats](../cheats.md) for more information about Atmosphère's cheat functionality.
|
||||
|
||||
```
|
||||
[atmosphere]
|
||||
dmnt_always_save_cheat_toggles = u8!0x0
|
||||
```
|
@ -1,125 +0,0 @@
|
||||
# sm
|
||||
|
||||
sm is a reimplementation of Nintendo's service manager. It allows Atmosphère to add or remove process handle limits, add new services, or intercept service calls. This allows high-level intercepting of Horizon OS functionality.
|
||||
|
||||
## Atmosphère Extensions
|
||||
|
||||
There are a few ways in which the Stratosphere implementation of sm differs intentionally from the stock sm.
|
||||
|
||||
### IPC: MITM Commands
|
||||
|
||||
The Stratosphere implementation of sm adds a few additional commands to the [`sm:`](https://reswitched.github.io/SwIPC/ifaces.html#nn::sm::detail::IUserInterface) port session.
|
||||
|
||||
Their SwIPC definitions follow.
|
||||
```
|
||||
interface nn::sm::detail::IUserInterface is sm: {
|
||||
...
|
||||
[65000] AtmosphereInstallMitm(ServiceName service) -> handle<port, move> service, handle<server_session, move> query;
|
||||
[65001] AtmosphereUninstallMitm(ServiceName service);
|
||||
[65002] AtmosphereAssociatePidTidForMitm(u64 pid, u64 tid);
|
||||
}
|
||||
```
|
||||
|
||||
Additionally, an interface `sm:dmnt` has been created to allow a debug monitor to query sm's state.
|
||||
|
||||
Its SwIPC definition follows.
|
||||
```
|
||||
interface nn::sm::detail::IDebugMonitorInterface is sm:dmnt {
|
||||
[65000] AtmosphereGetServiceRecord(ServiceName name) -> SmServiceRecord;
|
||||
[65001] AtmosphereListServiceRecords(u64 offset) -> buffer<SmServiceRecord, 6>, u64 count;
|
||||
[65002] AtmosphereGetServiceRecordSize() -> u64 record_size;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### AtmosphereInstallMitm
|
||||
|
||||
This command alters the registration for the named service, in order to allow services to intercept communication between client processes and their intended services. It is used by [fs_mitm](fs_mitm.md).
|
||||
|
||||
It takes the name of the service to install an MITM for, and returns two handles. The first is a port handle, similar to those returned from the [RegisterService](https://reswitched.github.io/SwIPC/ifaces.html#nn::sm::detail::IUserInterface(2)) command. The second is the server side of a session, called the query session. This session will used by sm to determine whether or not a new session should be intercepted, and to inform the MITM service of the identity of new processes.
|
||||
|
||||
The query session is expected to implement the following interface.
|
||||
```
|
||||
interface MitmQueryService {
|
||||
[65000] ShouldMitm(u64 pid) -> u64 should_mitm;
|
||||
[65001] AssociatePidTid(u64 pid, u64 tid);
|
||||
}
|
||||
```
|
||||
|
||||
The `ShouldMitm` command is invoked whenever a process attempts to make a new connection to the MITM'd service. It should return `0` if the process's connection should not be intercepted. Any other value will cause the process's connection to be intercepted. If the command returns an error code, the process's connection will not be intercepted.
|
||||
|
||||
The `AssociatePidTid` command is invoked on all MITM query sessions whenever a new process is created, in order to inform those services of the identity of a newly created process before it attempts to connect to any services.
|
||||
|
||||
If the process that installed the MITM attempts to connect to the service, it will always connect to the original service.
|
||||
|
||||
This command requires that the session be initialized, returning error code 0x415 if it is not.\
|
||||
If the given service name is invalid, error code 0xC15 is returned.\
|
||||
If the user does not have service registration permission for the named service, error code 0x1015 is returned.\
|
||||
If the service already has an MITM installed, error code 0x815 is returned.\
|
||||
If the service has not yet been registered, the request will be deferred until the service is registered in the same manner as IUserInterface::GetService.
|
||||
|
||||
#### AtmosphereUninstallMitm
|
||||
|
||||
Removes any installed MITM for the named service.
|
||||
|
||||
This command requires that the session be initialized, returning error code 0x415 if it is not.
|
||||
|
||||
#### AtmosphereAssociatePidTidForMitm
|
||||
|
||||
This command is used internally by the Stratosphere implementation of the [loader](loader.md) sysmodule, when a new process is created. It will call the `AssociatePidTid` command on every registered MITM query session.
|
||||
|
||||
If the given process ID refers to a kernel internal process, error code 0x1015 is returned. This command requires that the session be initialized, returning error code 0x415 if it is not.
|
||||
|
||||
#### AtmosphereGetServiceRecordSize
|
||||
|
||||
Retrieves `sizeof(SmServiceRecord)` for a service. The current format of `SmServiceRecord` structure follows.
|
||||
|
||||
```
|
||||
struct SmServiceRecord {
|
||||
uint64_t service_name;
|
||||
uint64_t owner_pid;
|
||||
uint64_t max_sessions;
|
||||
uint64_t mitm_pid;
|
||||
uint64_t mitm_waiting_ack_pid;
|
||||
bool is_light;
|
||||
bool mitm_waiting_ack;
|
||||
};
|
||||
```
|
||||
|
||||
#### AtmosphereGetServiceRecord
|
||||
|
||||
Retrieves a service registration record for a service.
|
||||
|
||||
#### AtmosphereListServiceRecords
|
||||
|
||||
Provides a list of service registrations records.
|
||||
|
||||
The command will return an array of `SmServiceRecord`s, skipping `offset` records. The number of records returned is indicated by `count`.
|
||||
If `count` is less than the size of the buffer divided by `sizeof(SmServiceRecord)` (the buffer was not completely filled), the end of the service registration list has been reached. Otherwise, client code
|
||||
should increment `offset` by `count` and call again. Client code should retrieve a record size using `AtmosphereGetServiceRecordSize`, and either make sure that the size of a record matches what it expects,
|
||||
or should make sure to use the correct size as the stride while iterating over the array of returned records. Example pseudocode is shown below.
|
||||
|
||||
```
|
||||
offset = 0;
|
||||
record_size = AtmosphereGetServiceRecordSize();
|
||||
do {
|
||||
SmServiceRecord records[16];
|
||||
count = AtmosphereListServiceRecords(offset, buffer(records));
|
||||
for (i = 0; i < count; i++) {
|
||||
SmServiceRecord record = {0};
|
||||
memcpy(&record, &records[i], min(record_size, sizeof(SmServiceRecord));
|
||||
/* process record */
|
||||
offset++;
|
||||
}
|
||||
} while(count == sizeof(records) / record_size);
|
||||
```
|
||||
|
||||
### Minimum Session Limit
|
||||
|
||||
When a service is registered, the sysmodule registering it must specify a limit on the number of sessions that are allowed to be active for that service at a time. This is used to ensure that services like `fs-pr`, `fs-ldr`, and `ldr:pm` can only be connected to once, adding an additional layer of safety over the regular service verification to ensure that those services are only connected to by the highly priveleged process they are intended to be used by.
|
||||
|
||||
By default, the Stratosphere implementation of PM will raise any session limits to at least 8, meaning that for services like `fs-pr` and those mentioned above, up to 8 processes will be able to connect to those sessions, leaving 7 sessions for homebrew to use.
|
||||
|
||||
### Weak Service Verification
|
||||
|
||||
In system firmware versions before 3.0.1, if a process did not call the [Initialize](https://reswitched.github.io/SwIPC/ifaces.html#nn::sm::detail::IUserInterface(0)) command on its `sm:` session, normally used to inform sm of the process's identity, sm would assume that the process was a kernel internal process and skip any service registration or access checks. The Stratosphere implementation of sm does not implement this vulnerability, and initialization is required on all firmware versions.
|
@ -1,16 +1,49 @@
|
||||
# Planned Features
|
||||
The following features are planned to be added in future versions of Atmosphère:
|
||||
+ Thermosphère, a hypervisor-based emunand implementation.
|
||||
+ A feature-rich debugging toolset (a component of Stratosphère).
|
||||
+ A custom debug monitor system module, providing an API for debugging Switch's processes. This may not be a reimplementation of Nintendo's own debug monitor.
|
||||
+ This should include a gdbstub implementation, possibly borrowing from Luma3DS's.
|
||||
+ This API should be additionally usable for RAM Editing/"Cheat Engine" purposes.
|
||||
+ A custom shell system module, providing an means for users to perform various RPC (with support for common/interesting functionality) on their Switch remotely. This may not be a reimplementation of Nintendo's own shell.
|
||||
+ This should support client connections over both Wi-Fi and USB.
|
||||
+ A custom logging system module, providing a means for other Atmosphère components (and possibly Nintendo's own system modules) to log debug output.
|
||||
+ This should support logging to the SD card, over Wi-Fi, and over USB.
|
||||
+ An application-level plugin system.
|
||||
+ This will, ideally, work somewhat like NTR-CFW's plugin system on the 3DS, allowing users to run their own code in a game's process in their own thread.
|
||||
+ An AR Code/Gameshark analog implementation, allowing for easy sharing/development of cheat codes to run on device.
|
||||
+ Further extensions to existing Atmosphère components.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
atmosphère has a number of features that are either works-in-progress or planned. Please note that while time-estimates are given, they are loose, and things may be completed sooner or later than advertised.
|
||||
|
||||
The following descriptions were last updated on June 15th, 2020.
|
||||
|
||||
## system updater api
|
||||
* **Description**: A planned extension api for stratosphere (tenatively `ams:su`), this will provide an interface for homebrew to safely install system upgrades or downgrades. This will allow for much more easily transitioning safely between different versions of the operating system.
|
||||
* **Development Status**: Backend/implementation completed; final stages (user-facing ipc api) to be written by SciresM.
|
||||
* **Estimated Time**: June 2020
|
||||
|
||||
## ams-on-mariko
|
||||
* **Description**: Atmosphere cannot run as-is on Mariko hardware. A large number of changes are needed in many components. Although exosphere's rewrite laid most groundwork on the secure monitor side, there is still work to do there -- and additional work is needed on the bootloader and stratosphere sides as well. Mariko support will also require further design thought; atmosphere's debugging design heavily relies on reboot-to-payload and (more generally) the ability to perform warmboot bootrom hax at will. This is not possible on Mariko, and will require a new design/software support for whatever solution is chosen.
|
||||
* **Development Status**: Planned.
|
||||
* **Estimated Time**: Summer 2020
|
||||
|
||||
## settings reimplementation
|
||||
* **Description**: A planned reimplementation of the settings system module, and with it a removal of the settings mitm. This will greatly simplify atmosphère's boot process, and will allow much more flexible control over the various system settings.
|
||||
* **Development Status**: Undergoing research/initial development by Adubbz.
|
||||
* **Estimated Time**: Mid 2020
|
||||
|
||||
## mesosphere
|
||||
* **Description**: mesosphère is a reimplementation of the Horizon operating system's Kernel. It aims to provide an open-source reference for Nintendo's code.
|
||||
* **Development Status**: Under semi-active development by SciresM; temporarily on pause while the System Updater API is completed.
|
||||
* **Estimated Time**: Mid-to-Late 2020
|
||||
|
||||
## tma reimplementation
|
||||
* **Description** tma ("target manager agent") is a system module that manages communication between the Switch and a client PC. Atmosphere's implementation will allow homebrew on the switch to communicate with a connected PC to do various operations such as exchanging data or interacting with files. It will also serve as the communicator for Atmosphère's planned debugger. This will also include PC-side software for interacting with the Switch.
|
||||
* **Development Status**: Planned. Switch-side code is fully implemented but needs heavy refactoring/rebasing, as the code was originally authored in 2018.
|
||||
* **Estimated Time**: Late 2020-2021.
|
||||
|
||||
## dmnt.gen2 reimplementation
|
||||
* **Description**: A reimplementation of the Switch's debug monitor, dmnt will provide an interface for debugging applications or system modules running on the Switch. This will include a gdbstub for debugging actively-running system components or applications.
|
||||
* **Development Status**: Planned
|
||||
* **Estimated Time**: 2021
|
||||
|
||||
## fs reimplementation
|
||||
* **Description**: Following mesosphère's completion, atmosphère will have reimplemented all components of the BootImagePackage firmware except for the filesystem services system module. Reimplementing fs will allow for fixing Nintendo bugs (such as corruption when using exFAT filesystems and encoding inconsistencies with UTF-8 and Shift-JIS).
|
||||
* **Development Status**: Planned.
|
||||
* **Estimated Time**: 2021-2022.
|
||||
|
||||
## thermosphère
|
||||
* **Description**: A general-purpose hypervisor, thermosphère will enable the virtualization of the Switch's operating system; this is planned to enable debugging of the Switch's kernel.
|
||||
* **Development Status**: Under semi-active development by TuxSH.
|
||||
* **Estimated Time**: 2020-2021.
|
||||
|
||||
## other planned features
|
||||
* **Description**: General system stability improvements to enhance the user's experience.
|
||||
* **Development Status**: Undergoing active development by all members of the atmosphère team.
|
||||
* **Estimated Time**: June 15th.
|
||||
|
@ -5,8 +5,8 @@
|
||||
;
|
||||
[subrepo]
|
||||
remote = https://github.com/m4xw/emuMMC
|
||||
branch = develop
|
||||
commit = b168ddf5fbb31013ff529a4859110c82b11eb361
|
||||
parent = c07f54f3709a4710e0aead6c91139fa0893b5e5c
|
||||
branch = exo2
|
||||
commit = 06ab9b895c4264ecc14d3bf9be1260e2096f6037
|
||||
parent = dccd41f6d25498c191a157123a27724203d3bc37
|
||||
method = rebase
|
||||
cmdver = 0.4.1
|
||||
|
@ -21,9 +21,10 @@ include $(DEVKITPRO)/libnx/switch_rules
|
||||
|
||||
ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE
|
||||
|
||||
DEFINES := -DINNER_HEAP_SIZE=0x80000
|
||||
# Current max usage is 0x4600. (512 * 34 FatFS file objects + 1 fsync buffer).
|
||||
DEFINES := -DINNER_HEAP_SIZE=0x8000
|
||||
|
||||
CFLAGS := -Wall -O2 -ffunction-sections -Wno-unused-function \
|
||||
CFLAGS := -Wall -O2 -ffunction-sections -fdata-sections -Wno-unused-function \
|
||||
$(ARCH) $(DEFINES)
|
||||
|
||||
CFLAGS += $(INCLUDE) -D__SWITCH__
|
||||
|
@ -117,6 +117,8 @@
|
||||
"svcReplyAndReceiveWithUserBuffer": "0x44",
|
||||
"svcCreateEvent": "0x45",
|
||||
"svcReadWriteRegister": "0x4E",
|
||||
"svcMapTransferMemory": "0x51",
|
||||
"svcUnmapTransferMemory": "0x52",
|
||||
"svcCreateInterruptEvent": "0x53",
|
||||
"svcQueryIoMapping": "0x55",
|
||||
"svcCreateDeviceAddressSpace": "0x56",
|
||||
@ -126,6 +128,8 @@
|
||||
"svcUnmapDeviceAddressSpace": "0x5c",
|
||||
"svcGetSystemInfo": "0x6f",
|
||||
"svcSetProcessMemoryPermission": "0x73",
|
||||
"svcMapProcessMemory": "0x74",
|
||||
"svcUnmapProcessMemory": "0x75",
|
||||
"svcCallSecureMonitor": "0x7f"
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@
|
||||
#define MMC_ALL_SEND_CID 2 /* bcr R2 */
|
||||
#define MMC_SET_RELATIVE_ADDR 3 /* ac [31:16] RCA R1 */
|
||||
#define MMC_SET_DSR 4 /* bc [31:16] RCA */
|
||||
#define MMC_SLEEP_AWAKE 5 /* ac [31:16] RCA 15:flg R1b */
|
||||
#define MMC_SLEEP_AWAKE 5 /* ac [31:16] RCA 15:flg R1b */
|
||||
#define MMC_SWITCH 6 /* ac [31:0] See below R1b */
|
||||
#define MMC_SELECT_CARD 7 /* ac [31:16] RCA R1 */
|
||||
#define MMC_SEND_EXT_CSD 8 /* adtc R1 */
|
||||
@ -51,7 +51,7 @@
|
||||
#define MMC_READ_SINGLE_BLOCK 17 /* adtc [31:0] data addr R1 */
|
||||
#define MMC_READ_MULTIPLE_BLOCK 18 /* adtc [31:0] data addr R1 */
|
||||
#define MMC_SEND_TUNING_BLOCK 19 /* adtc R1 */
|
||||
#define MMC_SEND_TUNING_BLOCK_HS200 21 /* adtc R1 */
|
||||
#define MMC_SEND_TUNING_BLOCK_HS200 21 /* adtc R1 */
|
||||
|
||||
/* class 3 */
|
||||
#define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */
|
||||
|
91
emummc/source/emmc/nx_sd.c
Normal file
91
emummc/source/emmc/nx_sd.c
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2019 CTCaer
|
||||
*
|
||||
* 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 "nx_sd.h"
|
||||
#include "sdmmc.h"
|
||||
#include "sdmmc_driver.h"
|
||||
#include "../soc/gpio.h"
|
||||
#include "../libs/fatfs/ff.h"
|
||||
|
||||
extern sdmmc_t sd_sdmmc;
|
||||
extern sdmmc_storage_t sd_storage;
|
||||
|
||||
static u32 sd_mode = SD_UHS_SDR104;
|
||||
|
||||
u32 nx_sd_mode_get()
|
||||
{
|
||||
return sd_mode;
|
||||
}
|
||||
|
||||
int nx_sd_init_retry(bool power_cycle)
|
||||
{
|
||||
u32 bus_width = SDMMC_BUS_WIDTH_4;
|
||||
u32 type = SDHCI_TIMING_UHS_SDR104;
|
||||
|
||||
// Power cycle SD card.
|
||||
if (power_cycle)
|
||||
{
|
||||
sd_mode--;
|
||||
sdmmc_storage_end(&sd_storage);
|
||||
}
|
||||
|
||||
// Get init parameters.
|
||||
switch (sd_mode)
|
||||
{
|
||||
case SD_INIT_FAIL: // Reset to max.
|
||||
return 0;
|
||||
case SD_1BIT_HS25:
|
||||
bus_width = SDMMC_BUS_WIDTH_1;
|
||||
type = SDHCI_TIMING_SD_HS25;
|
||||
break;
|
||||
case SD_4BIT_HS25:
|
||||
type = SDHCI_TIMING_SD_HS25;
|
||||
break;
|
||||
case SD_UHS_SDR82:
|
||||
type = SDHCI_TIMING_UHS_SDR82;
|
||||
break;
|
||||
case SD_UHS_SDR104:
|
||||
type = SDHCI_TIMING_UHS_SDR104;
|
||||
break;
|
||||
default:
|
||||
sd_mode = SD_UHS_SDR104;
|
||||
}
|
||||
|
||||
return sdmmc_storage_init_sd(&sd_storage, &sd_sdmmc, bus_width, type);
|
||||
}
|
||||
|
||||
bool nx_sd_initialize(bool power_cycle)
|
||||
{
|
||||
if (power_cycle)
|
||||
sdmmc_storage_end(&sd_storage);
|
||||
|
||||
int res = !nx_sd_init_retry(false);
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!res)
|
||||
return true;
|
||||
else
|
||||
{
|
||||
if (sd_mode == SD_INIT_FAIL)
|
||||
break;
|
||||
|
||||
res = !nx_sd_init_retry(true);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
36
emummc/source/emmc/nx_sd.h
Normal file
36
emummc/source/emmc/nx_sd.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
* Copyright (c) 2018-2019 CTCaer
|
||||
*
|
||||
* 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 NX_SD_H
|
||||
#define NX_SD_H
|
||||
|
||||
#include "../utils/types.h"
|
||||
|
||||
enum
|
||||
{
|
||||
SD_INIT_FAIL = 0,
|
||||
SD_1BIT_HS25 = 1,
|
||||
SD_4BIT_HS25 = 2,
|
||||
SD_UHS_SDR82 = 3,
|
||||
SD_UHS_SDR104 = 4
|
||||
};
|
||||
|
||||
u32 nx_sd_get_mode();
|
||||
int nx_sd_init_retry(bool power_cycle);
|
||||
bool nx_sd_initialize(bool power_cycle);
|
||||
|
||||
#endif
|
@ -1,8 +1,8 @@
|
||||
/*
|
||||
* include/linux/mmc/sd.h
|
||||
*
|
||||
* Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
|
||||
* Copyright (C) 2018 CTCaer
|
||||
* Copyright (c) 2005-2007 Pierre Ossman, All Rights Reserved.
|
||||
* Copyright (c) 2018 CTCaer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -40,7 +40,9 @@
|
||||
#define SD_ROCR_S18A SD_OCR_S18R /* 1.8V switching accepted by card */
|
||||
#define SD_OCR_XPC (1 << 28) /* SDXC power control */
|
||||
#define SD_OCR_CCS (1 << 30) /* Card Capacity Status */
|
||||
#define SD_OCR_VDD_27_34 (0x7F << 15) /* VDD voltage 2.7 ~ 3.4 */
|
||||
#define SD_OCR_VDD_32_33 (1 << 20) /* VDD voltage 3.2 ~ 3.3 */
|
||||
#define SD_OCR_VDD_18 (1 << 7) /* VDD voltage 1.8 */
|
||||
|
||||
/*
|
||||
* SD_SWITCH argument format:
|
||||
@ -104,6 +106,11 @@
|
||||
#define SD_SET_CURRENT_LIMIT_600 2
|
||||
#define SD_SET_CURRENT_LIMIT_800 3
|
||||
|
||||
#define SD_MAX_CURRENT_200 (1 << SD_SET_CURRENT_LIMIT_200)
|
||||
#define SD_MAX_CURRENT_400 (1 << SD_SET_CURRENT_LIMIT_400)
|
||||
#define SD_MAX_CURRENT_600 (1 << SD_SET_CURRENT_LIMIT_600)
|
||||
#define SD_MAX_CURRENT_800 (1 << SD_SET_CURRENT_LIMIT_800)
|
||||
|
||||
/*
|
||||
* SD_SWITCH mode
|
||||
*/
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
* Copyright (C) 2018 CTCaer
|
||||
* Copyright (c) 2018-2019 CTCaer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
@ -20,6 +20,7 @@
|
||||
#include <stdio.h>
|
||||
#include "sdmmc.h"
|
||||
#include "mmc.h"
|
||||
#include "nx_sd.h"
|
||||
#include "sd.h"
|
||||
#include "../utils/types.h"
|
||||
#include "../utils/util.h"
|
||||
@ -188,6 +189,7 @@ static int _sdmmc_storage_execute_cmd_type1_ex(sdmmc_storage_t *storage, u32 *re
|
||||
if (_sdmmc_storage_check_result(*resp))
|
||||
if (expected_state == 0x10 || R1_CURRENT_STATE(*resp) == expected_state)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -201,6 +203,7 @@ static int _sdmmc_storage_go_idle_state(sdmmc_storage_t *storage)
|
||||
{
|
||||
sdmmc_cmd_t cmd;
|
||||
sdmmc_init_cmd(&cmd, MMC_GO_IDLE_STATE, 0, SDMMC_RSP_TYPE_0, 0);
|
||||
|
||||
return sdmmc_execute_cmd(storage->sdmmc, &cmd, 0, 0);
|
||||
}
|
||||
|
||||
@ -210,7 +213,9 @@ static int _sdmmc_storage_get_cid(sdmmc_storage_t *storage, void *buf)
|
||||
sdmmc_init_cmd(&cmd, MMC_ALL_SEND_CID, 0, SDMMC_RSP_TYPE_2, 0);
|
||||
if (!sdmmc_execute_cmd(storage->sdmmc, &cmd, 0, 0))
|
||||
return 0;
|
||||
|
||||
sdmmc_get_rsp(storage->sdmmc, buf, 0x10, SDMMC_RSP_TYPE_2);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -225,7 +230,9 @@ static int _sdmmc_storage_get_csd(sdmmc_storage_t *storage, void *buf)
|
||||
sdmmc_init_cmd(&cmdbuf, MMC_SEND_CSD, storage->rca << 16, SDMMC_RSP_TYPE_2, 0);
|
||||
if (!sdmmc_execute_cmd(storage->sdmmc, &cmdbuf, 0, 0))
|
||||
return 0;
|
||||
|
||||
sdmmc_get_rsp(storage->sdmmc, buf, 0x10, SDMMC_RSP_TYPE_2);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -247,9 +254,9 @@ static int _sdmmc_storage_check_status(sdmmc_storage_t *storage)
|
||||
|
||||
static int _sdmmc_storage_readwrite_ex(sdmmc_storage_t *storage, u32 *blkcnt_out, u32 sector, u32 num_sectors, void *buf, u32 is_write)
|
||||
{
|
||||
u32 tmp = 0;
|
||||
sdmmc_cmd_t cmdbuf;
|
||||
sdmmc_req_t reqbuf;
|
||||
u32 tmp = 0;
|
||||
|
||||
sdmmc_init_cmd(&cmdbuf, is_write ? MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK, sector, SDMMC_RSP_TYPE_1, 0);
|
||||
|
||||
@ -261,12 +268,13 @@ static int _sdmmc_storage_readwrite_ex(sdmmc_storage_t *storage, u32 *blkcnt_out
|
||||
reqbuf.is_auto_cmd12 = 1;
|
||||
|
||||
if (!sdmmc_execute_cmd(storage->sdmmc, &cmdbuf, &reqbuf, blkcnt_out))
|
||||
{
|
||||
{
|
||||
sdmmc_stop_transmission(storage->sdmmc, &tmp);
|
||||
_sdmmc_storage_get_status(storage, &tmp, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -274,36 +282,58 @@ int sdmmc_storage_end(sdmmc_storage_t *storage)
|
||||
{
|
||||
if (!_sdmmc_storage_go_idle_state(storage))
|
||||
return 0;
|
||||
|
||||
sdmmc_end(storage->sdmmc);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _sdmmc_storage_readwrite(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf, u32 is_write)
|
||||
{
|
||||
u8 *bbuf = (u8 *)buf;
|
||||
|
||||
bool first_reinit = false;
|
||||
while (num_sectors)
|
||||
{
|
||||
u32 blkcnt = 0;
|
||||
//Retry 9 times on error.
|
||||
u32 retries = 10;
|
||||
// Retry 5 times if failed.
|
||||
u32 retries = 5;
|
||||
do
|
||||
{
|
||||
reinit_try:
|
||||
if (_sdmmc_storage_readwrite_ex(storage, &blkcnt, sector, MIN(num_sectors, 0xFFFF), bbuf, is_write))
|
||||
goto out;
|
||||
else
|
||||
retries--;
|
||||
|
||||
msleep(100);
|
||||
msleep(50);
|
||||
} while (retries);
|
||||
|
||||
// Disk IO failure! Reinit SD Card to a lower speed.
|
||||
if (storage->sdmmc->id == SDMMC_1)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (!first_reinit)
|
||||
res = nx_sd_initialize(true);
|
||||
else
|
||||
res = nx_sd_init_retry(true);
|
||||
|
||||
retries = 3;
|
||||
first_reinit = true;
|
||||
|
||||
if (res)
|
||||
goto reinit_try;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out:;
|
||||
DPRINTF("readwrite: %08X\n", blkcnt);
|
||||
out:
|
||||
DPRINTF("readwrite: %08X\n", blkcnt);
|
||||
sector += blkcnt;
|
||||
num_sectors -= blkcnt;
|
||||
bbuf += 512 * blkcnt;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -427,10 +457,10 @@ static int _mmc_storage_get_op_cond_inner(sdmmc_storage_t *storage, u32 *pout, u
|
||||
switch (power)
|
||||
{
|
||||
case SDMMC_POWER_1_8:
|
||||
arg = 0x40000080; //Sector access, voltage.
|
||||
arg = SD_OCR_CCS | SD_OCR_VDD_18;
|
||||
break;
|
||||
case SDMMC_POWER_3_3:
|
||||
arg = 0x403F8000; //Sector access, voltage.
|
||||
arg = SD_OCR_CCS | SD_OCR_VDD_27_34;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
@ -445,21 +475,24 @@ static int _mmc_storage_get_op_cond_inner(sdmmc_storage_t *storage, u32 *pout, u
|
||||
|
||||
static int _mmc_storage_get_op_cond(sdmmc_storage_t *storage, u32 power)
|
||||
{
|
||||
u32 timeout = get_tmr_ms() + 1500;
|
||||
u64 timeout = get_tmr_ms() + 1500;
|
||||
|
||||
while (1)
|
||||
{
|
||||
u32 cond = 0;
|
||||
if (!_mmc_storage_get_op_cond_inner(storage, &cond, power))
|
||||
break;
|
||||
|
||||
if (cond & MMC_CARD_BUSY)
|
||||
{
|
||||
if (cond & 0x40000000)
|
||||
if (cond & SD_OCR_CCS)
|
||||
storage->has_sector_access = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
if (get_tmr_ms() > timeout)
|
||||
break;
|
||||
|
||||
usleep(1000);
|
||||
}
|
||||
|
||||
@ -589,6 +622,7 @@ static int _mmc_storage_switch_buswidth(sdmmc_storage_t *storage, u32 bus_width)
|
||||
if (_sdmmc_storage_check_status(storage))
|
||||
{
|
||||
sdmmc_set_bus_width(storage->sdmmc, bus_width);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -599,14 +633,19 @@ static int _mmc_storage_enable_HS(sdmmc_storage_t *storage, int check)
|
||||
{
|
||||
if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS)))
|
||||
return 0;
|
||||
|
||||
if (check && !_sdmmc_storage_check_status(storage))
|
||||
return 0;
|
||||
if (!sdmmc_setup_clock(storage->sdmmc, 2))
|
||||
|
||||
if (!sdmmc_setup_clock(storage->sdmmc, SDHCI_TIMING_MMC_HS52))
|
||||
return 0;
|
||||
DPRINTF("[MMC] switched to HS\n");
|
||||
|
||||
DPRINTF("[MMC] switched to HS\n");
|
||||
storage->csd.busspeed = 52;
|
||||
|
||||
if (check || _sdmmc_storage_check_status(storage))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -614,12 +653,16 @@ static int _mmc_storage_enable_HS200(sdmmc_storage_t *storage)
|
||||
{
|
||||
if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS200)))
|
||||
return 0;
|
||||
if (!sdmmc_setup_clock(storage->sdmmc, 3))
|
||||
|
||||
if (!sdmmc_setup_clock(storage->sdmmc, SDHCI_TIMING_MMC_HS200))
|
||||
return 0;
|
||||
if (!sdmmc_config_tuning(storage->sdmmc, 3, MMC_SEND_TUNING_BLOCK_HS200))
|
||||
|
||||
if (!sdmmc_tuning_execute(storage->sdmmc, SDHCI_TIMING_MMC_HS200, MMC_SEND_TUNING_BLOCK_HS200))
|
||||
return 0;
|
||||
DPRINTF("[MMC] switched to HS200\n");
|
||||
|
||||
DPRINTF("[MMC] switched to HS200\n");
|
||||
storage->csd.busspeed = 200;
|
||||
|
||||
return _sdmmc_storage_check_status(storage);
|
||||
}
|
||||
|
||||
@ -627,41 +670,46 @@ static int _mmc_storage_enable_HS400(sdmmc_storage_t *storage)
|
||||
{
|
||||
if (!_mmc_storage_enable_HS200(storage))
|
||||
return 0;
|
||||
sdmmc_get_venclkctl(storage->sdmmc);
|
||||
|
||||
sdmmc_set_tap_value(storage->sdmmc);
|
||||
|
||||
if (!_mmc_storage_enable_HS(storage, 0))
|
||||
return 0;
|
||||
|
||||
if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_BUS_WIDTH, EXT_CSD_DDR_BUS_WIDTH_8)))
|
||||
return 0;
|
||||
|
||||
if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS400)))
|
||||
return 0;
|
||||
if (!sdmmc_setup_clock(storage->sdmmc, 4))
|
||||
|
||||
if (!sdmmc_setup_clock(storage->sdmmc, SDHCI_TIMING_MMC_HS400))
|
||||
return 0;
|
||||
DPRINTF("[MMC] switched to HS400\n");
|
||||
|
||||
DPRINTF("[MMC] switched to HS400\n");
|
||||
storage->csd.busspeed = 400;
|
||||
|
||||
return _sdmmc_storage_check_status(storage);
|
||||
}
|
||||
|
||||
static int _mmc_storage_enable_highspeed(sdmmc_storage_t *storage, u32 card_type, u32 type)
|
||||
{
|
||||
//TODO: this should be a config item.
|
||||
// --v
|
||||
if (!1 || sdmmc_get_voltage(storage->sdmmc) != SDMMC_POWER_1_8)
|
||||
if (sdmmc_get_io_power(storage->sdmmc) != SDMMC_POWER_1_8)
|
||||
goto out;
|
||||
|
||||
if (sdmmc_get_bus_width(storage->sdmmc) == SDMMC_BUS_WIDTH_8 &&
|
||||
card_type & EXT_CSD_CARD_TYPE_HS400_1_8V &&
|
||||
type == 4)
|
||||
card_type & EXT_CSD_CARD_TYPE_HS400_1_8V && type == SDHCI_TIMING_MMC_HS400)
|
||||
return _mmc_storage_enable_HS400(storage);
|
||||
|
||||
if (sdmmc_get_bus_width(storage->sdmmc) == SDMMC_BUS_WIDTH_8 ||
|
||||
(sdmmc_get_bus_width(storage->sdmmc) == SDMMC_BUS_WIDTH_4
|
||||
&& card_type & EXT_CSD_CARD_TYPE_HS200_1_8V
|
||||
&& (type == 4 || type == 3)))
|
||||
&& (type == SDHCI_TIMING_MMC_HS400 || type == SDHCI_TIMING_MMC_HS200)))
|
||||
return _mmc_storage_enable_HS200(storage);
|
||||
|
||||
out:;
|
||||
out:
|
||||
if (card_type & EXT_CSD_CARD_TYPE_HS_52)
|
||||
return _mmc_storage_enable_HS(storage, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -669,53 +717,54 @@ static int _mmc_storage_enable_bkops(sdmmc_storage_t *storage)
|
||||
{
|
||||
if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_SET_BITS, EXT_CSD_BKOPS_EN, EXT_CSD_BKOPS_LEVEL_2)))
|
||||
return 0;
|
||||
|
||||
return _sdmmc_storage_check_status(storage);
|
||||
}
|
||||
|
||||
int sdmmc_storage_init_mmc(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 id, u32 bus_width, u32 type)
|
||||
int sdmmc_storage_init_mmc(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 bus_width, u32 type)
|
||||
{
|
||||
memset(storage, 0, sizeof(sdmmc_storage_t));
|
||||
storage->sdmmc = sdmmc;
|
||||
storage->rca = 2; //TODO: this could be a config item.
|
||||
|
||||
if (!sdmmc_init(sdmmc, id, SDMMC_POWER_1_8, SDMMC_BUS_WIDTH_1, 0, 0))
|
||||
if (!sdmmc_init(sdmmc, SDMMC_4, SDMMC_POWER_1_8, SDMMC_BUS_WIDTH_1, SDHCI_TIMING_MMC_ID, SDMMC_AUTO_CAL_DISABLE))
|
||||
return 0;
|
||||
DPRINTF("[MMC] after init\n");
|
||||
DPRINTF("[MMC] after init\n");
|
||||
|
||||
usleep(1000 + (74000 + sdmmc->divisor - 1) / sdmmc->divisor);
|
||||
|
||||
if (!_sdmmc_storage_go_idle_state(storage))
|
||||
return 0;
|
||||
DPRINTF("[MMC] went to idle state\n");
|
||||
DPRINTF("[MMC] went to idle state\n");
|
||||
|
||||
if (!_mmc_storage_get_op_cond(storage, SDMMC_POWER_1_8))
|
||||
return 0;
|
||||
DPRINTF("[MMC] got op cond\n");
|
||||
DPRINTF("[MMC] got op cond\n");
|
||||
|
||||
if (!_sdmmc_storage_get_cid(storage, storage->raw_cid))
|
||||
return 0;
|
||||
DPRINTF("[MMC] got cid\n");
|
||||
DPRINTF("[MMC] got cid\n");
|
||||
|
||||
if (!_mmc_storage_set_relative_addr(storage))
|
||||
return 0;
|
||||
DPRINTF("[MMC] set relative addr\n");
|
||||
DPRINTF("[MMC] set relative addr\n");
|
||||
|
||||
if (!_sdmmc_storage_get_csd(storage, storage->raw_csd))
|
||||
return 0;
|
||||
DPRINTF("[MMC] got csd\n");
|
||||
DPRINTF("[MMC] got csd\n");
|
||||
_mmc_storage_parse_csd(storage);
|
||||
|
||||
if (!sdmmc_setup_clock(storage->sdmmc, 1))
|
||||
if (!sdmmc_setup_clock(storage->sdmmc, SDHCI_TIMING_MMC_LS26))
|
||||
return 0;
|
||||
DPRINTF("[MMC] after setup clock\n");
|
||||
DPRINTF("[MMC] after setup clock\n");
|
||||
|
||||
if (!_sdmmc_storage_select_card(storage))
|
||||
return 0;
|
||||
DPRINTF("[MMC] card selected\n");
|
||||
DPRINTF("[MMC] card selected\n");
|
||||
|
||||
if (!_sdmmc_storage_set_blocklen(storage, 512))
|
||||
return 0;
|
||||
DPRINTF("[MMC] set blocklen to 512\n");
|
||||
DPRINTF("[MMC] set blocklen to 512\n");
|
||||
|
||||
u32 *csd = (u32 *)storage->raw_csd;
|
||||
//Check system specification version, only version 4.0 and later support below features.
|
||||
@ -727,36 +776,29 @@ int sdmmc_storage_init_mmc(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 id, u32
|
||||
|
||||
if (!_mmc_storage_switch_buswidth(storage, bus_width))
|
||||
return 0;
|
||||
DPRINTF("[MMC] switched buswidth\n");
|
||||
|
||||
u8 *ext_csd = (u8 *)malloc(512);
|
||||
if (!_mmc_storage_get_ext_csd(storage, ext_csd))
|
||||
{
|
||||
free(ext_csd);
|
||||
DPRINTF("[MMC] switched buswidth\n");
|
||||
u8 buf[512];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
if (!_mmc_storage_get_ext_csd(storage, buf))
|
||||
return 0;
|
||||
}
|
||||
free(ext_csd);
|
||||
DPRINTF("[MMC] got ext_csd\n");
|
||||
DPRINTF("[MMC] got ext_csd\n");
|
||||
|
||||
_mmc_storage_parse_cid(storage); //This needs to be after csd and ext_csd
|
||||
|
||||
/* When auto BKOPS is enabled the mmc device should be powered all the time until we disable this and check status.
|
||||
Disable it for now until BKOPS disable added to power down sequence at sdmmc_storage_end().
|
||||
Additionally this works only when we put the device in idle mode which we don't after enabling it. */
|
||||
if (storage->ext_csd.bkops & 0x1 && !(storage->ext_csd.bkops_en & EXT_CSD_BKOPS_LEVEL_2) && 0)
|
||||
if (0 && storage->ext_csd.bkops & 0x1 && !(storage->ext_csd.bkops_en & EXT_CSD_BKOPS_LEVEL_2))
|
||||
{
|
||||
_mmc_storage_enable_bkops(storage);
|
||||
DPRINTF("[MMC] BKOPS enabled\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
DPRINTF("[MMC] BKOPS disabled\n");
|
||||
DPRINTF("[MMC] BKOPS enabled\n");
|
||||
}
|
||||
|
||||
if (!_mmc_storage_enable_highspeed(storage, storage->ext_csd.card_type, type))
|
||||
return 0;
|
||||
DPRINTF("[MMC] succesfully switched to highspeed mode\n");
|
||||
DPRINTF("[MMC] succesfully switched to HS mode\n");
|
||||
|
||||
sdmmc_sd_clock_ctrl(storage->sdmmc, 1);
|
||||
sdmmc_card_clock_ctrl(storage->sdmmc, SDMMC_AUTO_CAL_ENABLE);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -765,8 +807,10 @@ int sdmmc_storage_set_mmc_partition(sdmmc_storage_t *storage, u32 partition)
|
||||
{
|
||||
if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_PART_CONFIG, partition)))
|
||||
return 0;
|
||||
|
||||
if (!_sdmmc_storage_check_status(storage))
|
||||
return 0;
|
||||
|
||||
storage->partition = partition;
|
||||
return 1;
|
||||
}
|
||||
@ -780,6 +824,7 @@ static int _sd_storage_execute_app_cmd(sdmmc_storage_t *storage, u32 expected_st
|
||||
u32 tmp;
|
||||
if (!_sdmmc_storage_execute_cmd_type1_ex(storage, &tmp, MMC_APP_CMD, storage->rca << 16, 0, expected_state, mask))
|
||||
return 0;
|
||||
|
||||
return sdmmc_execute_cmd(storage->sdmmc, cmd, req, blkcnt_out);
|
||||
}
|
||||
|
||||
@ -787,6 +832,7 @@ static int _sd_storage_execute_app_cmd_type1(sdmmc_storage_t *storage, u32 *resp
|
||||
{
|
||||
if (!_sdmmc_storage_execute_cmd_type1(storage, MMC_APP_CMD, storage->rca << 16, 0, R1_STATE_TRAN))
|
||||
return 0;
|
||||
|
||||
return _sdmmc_storage_execute_cmd_type1_ex(storage, resp, cmd, arg, check_busy, expected_state, 0);
|
||||
}
|
||||
|
||||
@ -816,32 +862,27 @@ static int _sd_storage_get_op_cond_once(sdmmc_storage_t *storage, u32 *cond, int
|
||||
// This is needed for most cards. Do not set bit7 even if 1.8V is supported.
|
||||
arg |= SD_OCR_VDD_32_33;
|
||||
sdmmc_init_cmd(&cmdbuf, SD_APP_OP_COND, arg, SDMMC_RSP_TYPE_3, 0);
|
||||
|
||||
DPRINTF("[SD] before _sd_storage_execute_app_cmd\n");
|
||||
if (!_sd_storage_execute_app_cmd(storage, 0x10, is_version_1 ? 0x400000 : 0, &cmdbuf, 0, 0))
|
||||
return 0;
|
||||
DPRINTF("[SD] before sdmmc_get_rsp\n");
|
||||
|
||||
return sdmmc_get_rsp(storage->sdmmc, cond, 4, SDMMC_RSP_TYPE_3);
|
||||
}
|
||||
|
||||
static int _sd_storage_get_op_cond(sdmmc_storage_t *storage, int is_version_1, int supports_low_voltage)
|
||||
{
|
||||
u32 timeout = get_tmr_ms() + 1500;
|
||||
u64 timeout = get_tmr_ms() + 1500;
|
||||
|
||||
while (1)
|
||||
{
|
||||
u32 cond = 0;
|
||||
if (!_sd_storage_get_op_cond_once(storage, &cond, is_version_1, supports_low_voltage))
|
||||
{
|
||||
DPRINTF("[SD] _sd_storage_get_op_cond_once failed\r\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (cond & MMC_CARD_BUSY)
|
||||
{
|
||||
if (cond & SD_OCR_CCS)
|
||||
storage->has_sector_access = 1;
|
||||
|
||||
// Check if card supports 1.8V signaling.
|
||||
if (cond & SD_ROCR_S18A && supports_low_voltage)
|
||||
{
|
||||
//The low voltage regulator configuration is valid for SDMMC1 only.
|
||||
@ -852,7 +893,7 @@ static int _sd_storage_get_op_cond(sdmmc_storage_t *storage, int is_version_1, i
|
||||
return 0;
|
||||
storage->is_low_voltage = 1;
|
||||
|
||||
DPRINTF("-> switched to low voltage\n");
|
||||
DPRINTF("-> switched to low voltage\n");
|
||||
}
|
||||
}
|
||||
|
||||
@ -863,8 +904,6 @@ static int _sd_storage_get_op_cond(sdmmc_storage_t *storage, int is_version_1, i
|
||||
msleep(10); // Needs to be at least 10ms for some SD Cards
|
||||
}
|
||||
|
||||
DPRINTF("[SD] _sd_storage_get_op_cond Timeout\r\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -873,7 +912,7 @@ static int _sd_storage_get_rca(sdmmc_storage_t *storage)
|
||||
sdmmc_cmd_t cmdbuf;
|
||||
sdmmc_init_cmd(&cmdbuf, SD_SEND_RELATIVE_ADDR, 0, SDMMC_RSP_TYPE_4, 0);
|
||||
|
||||
u32 timeout = get_tmr_ms() + 1500;
|
||||
u64 timeout = get_tmr_ms() + 1500;
|
||||
|
||||
while (1)
|
||||
{
|
||||
@ -991,34 +1030,37 @@ int _sd_storage_switch(sdmmc_storage_t *storage, void *buf, int mode, int group,
|
||||
return _sdmmc_storage_check_result(tmp);
|
||||
}
|
||||
|
||||
void _sd_storage_set_current_limit(sdmmc_storage_t *storage, u8 *buf)
|
||||
void _sd_storage_set_current_limit(sdmmc_storage_t *storage, u16 current_limit, u8 *buf)
|
||||
{
|
||||
u32 pwr = SD_SET_CURRENT_LIMIT_800;
|
||||
u32 pwr = SD_SET_CURRENT_LIMIT_200;
|
||||
|
||||
if (current_limit & SD_MAX_CURRENT_800)
|
||||
pwr = SD_SET_CURRENT_LIMIT_800;
|
||||
else if (current_limit & SD_MAX_CURRENT_600)
|
||||
pwr = SD_SET_CURRENT_LIMIT_600;
|
||||
else if (current_limit & SD_MAX_CURRENT_400)
|
||||
pwr = SD_SET_CURRENT_LIMIT_400;
|
||||
|
||||
_sd_storage_switch(storage, buf, SD_SWITCH_SET, 3, pwr);
|
||||
|
||||
while (pwr > 0)
|
||||
if (((buf[15] >> 4) & 0x0F) == pwr)
|
||||
{
|
||||
pwr--;
|
||||
_sd_storage_switch(storage, buf, SD_SWITCH_SET, 3, pwr);
|
||||
if (((buf[15] >> 4) & 0x0F) == pwr)
|
||||
switch (pwr)
|
||||
{
|
||||
case SD_SET_CURRENT_LIMIT_800:
|
||||
DPRINTF("[SD] power limit raised to 800mA\n");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (pwr)
|
||||
{
|
||||
case SD_SET_CURRENT_LIMIT_800:
|
||||
DPRINTF("[SD] Power limit raised to 800mA\n");
|
||||
break;
|
||||
case SD_SET_CURRENT_LIMIT_600:
|
||||
DPRINTF("[SD] Power limit raised to 600mA\n");
|
||||
break;
|
||||
case SD_SET_CURRENT_LIMIT_400:
|
||||
DPRINTF("[SD] Power limit raised to 800mA\n");
|
||||
break;
|
||||
default:
|
||||
case SD_SET_CURRENT_LIMIT_200:
|
||||
DPRINTF("[SD] Power limit defaulted to 200mA\n");
|
||||
break;
|
||||
case SD_SET_CURRENT_LIMIT_600:
|
||||
DPRINTF("[SD] power limit raised to 600mA\n");
|
||||
break;
|
||||
case SD_SET_CURRENT_LIMIT_400:
|
||||
DPRINTF("[SD] power limit raised to 400mA\n");
|
||||
break;
|
||||
default:
|
||||
case SD_SET_CURRENT_LIMIT_200:
|
||||
DPRINTF("[SD] power limit defaulted to 200mA\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1026,62 +1068,91 @@ int _sd_storage_enable_highspeed(sdmmc_storage_t *storage, u32 hs_type, u8 *buf)
|
||||
{
|
||||
if (!_sd_storage_switch(storage, buf, SD_SWITCH_CHECK, 0, hs_type))
|
||||
return 0;
|
||||
DPRINTF("[SD] supports switch to (U)HS mode\n");
|
||||
|
||||
u32 type_out = buf[16] & 0xF;
|
||||
if (type_out != hs_type)
|
||||
return 0;
|
||||
DPRINTF("[SD] supports selected (U)HS mode\n");
|
||||
|
||||
if ((((u16)buf[0] << 8) | buf[1]) < 0x320)
|
||||
u16 total_pwr_consumption = ((u16)buf[0] << 8) | buf[1];
|
||||
DPRINTF("[SD] total max current: %d\n", total_pwr_consumption);
|
||||
|
||||
if (total_pwr_consumption <= 800)
|
||||
{
|
||||
if (!_sd_storage_switch(storage, buf, SD_SWITCH_SET, 0, hs_type))
|
||||
return 0;
|
||||
|
||||
if (type_out != (buf[16] & 0xF))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
DPRINTF("[SD] card max current over limit\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _sd_storage_enable_highspeed_low_volt(sdmmc_storage_t *storage, u32 type, u8 *buf)
|
||||
int _sd_storage_enable_uhs_low_volt(sdmmc_storage_t *storage, u32 type, u8 *buf)
|
||||
{
|
||||
// Try to raise the current limit to let the card perform better.
|
||||
_sd_storage_set_current_limit(storage, buf);
|
||||
|
||||
if (sdmmc_get_bus_width(storage->sdmmc) != SDMMC_BUS_WIDTH_4)
|
||||
return 0;
|
||||
|
||||
if (!_sd_storage_switch_get(storage, buf))
|
||||
return 0;
|
||||
//gfx_hexdump(0, (u8 *)buf, 64);
|
||||
|
||||
u8 access_mode = buf[13];
|
||||
u16 current_limit = buf[7] | buf[6] << 8;
|
||||
|
||||
// Try to raise the current limit to let the card perform better.
|
||||
_sd_storage_set_current_limit(storage, current_limit, buf);
|
||||
|
||||
u32 hs_type = 0;
|
||||
switch (type)
|
||||
{
|
||||
case 11:
|
||||
case SDHCI_TIMING_UHS_SDR104:
|
||||
case SDHCI_TIMING_UHS_SDR82:
|
||||
// Fall through if not supported.
|
||||
if (buf[13] & SD_MODE_UHS_SDR104)
|
||||
if (access_mode & SD_MODE_UHS_SDR104)
|
||||
{
|
||||
type = 11;
|
||||
hs_type = UHS_SDR104_BUS_SPEED;
|
||||
DPRINTF("[SD] Bus speed set to SDR104\n");
|
||||
storage->csd.busspeed = 104;
|
||||
DPRINTF("[SD] bus speed set to SDR104\n");
|
||||
switch (type)
|
||||
{
|
||||
case SDHCI_TIMING_UHS_SDR104:
|
||||
storage->csd.busspeed = 104;
|
||||
break;
|
||||
case SDHCI_TIMING_UHS_SDR82:
|
||||
storage->csd.busspeed = 82;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 10:
|
||||
if (buf[13] & SD_MODE_UHS_SDR50)
|
||||
case SDHCI_TIMING_UHS_SDR50:
|
||||
if (access_mode & SD_MODE_UHS_SDR50)
|
||||
{
|
||||
type = 10;
|
||||
type = SDHCI_TIMING_UHS_SDR50;
|
||||
hs_type = UHS_SDR50_BUS_SPEED;
|
||||
DPRINTF("[SD] Bus speed set to SDR50\n");
|
||||
DPRINTF("[SD] bus speed set to SDR50\n");
|
||||
storage->csd.busspeed = 50;
|
||||
break;
|
||||
}
|
||||
case 8:
|
||||
if (!(buf[13] & SD_MODE_UHS_SDR12))
|
||||
case SDHCI_TIMING_UHS_SDR25:
|
||||
if (access_mode & SD_MODE_UHS_SDR25)
|
||||
{
|
||||
type = SDHCI_TIMING_UHS_SDR25;
|
||||
hs_type = UHS_SDR50_BUS_SPEED;
|
||||
DPRINTF("[SD] bus speed set to SDR25\n");
|
||||
storage->csd.busspeed = 25;
|
||||
break;
|
||||
}
|
||||
case SDHCI_TIMING_UHS_SDR12:
|
||||
if (!(access_mode & SD_MODE_UHS_SDR12))
|
||||
return 0;
|
||||
type = 8;
|
||||
type = SDHCI_TIMING_UHS_SDR12;
|
||||
hs_type = UHS_SDR12_BUS_SPEED;
|
||||
DPRINTF("[SD] Bus speed set to SDR12\n");
|
||||
DPRINTF("[SD] bus speed set to SDR12\n");
|
||||
storage->csd.busspeed = 12;
|
||||
break;
|
||||
default:
|
||||
@ -1091,106 +1162,38 @@ int _sd_storage_enable_highspeed_low_volt(sdmmc_storage_t *storage, u32 type, u8
|
||||
|
||||
if (!_sd_storage_enable_highspeed(storage, hs_type, buf))
|
||||
return 0;
|
||||
DPRINTF("[SD] card accepted UHS\n");
|
||||
if (!sdmmc_setup_clock(storage->sdmmc, type))
|
||||
return 0;
|
||||
if (!sdmmc_config_tuning(storage->sdmmc, type, MMC_SEND_TUNING_BLOCK))
|
||||
DPRINTF("[SD] setup clock\n");
|
||||
if (!sdmmc_tuning_execute(storage->sdmmc, type, MMC_SEND_TUNING_BLOCK))
|
||||
return 0;
|
||||
DPRINTF("[SD] config tuning\n");
|
||||
return _sdmmc_storage_check_status(storage);
|
||||
}
|
||||
|
||||
int _sd_storage_enable_highspeed_high_volt(sdmmc_storage_t *storage, u8 *buf)
|
||||
int _sd_storage_enable_hs_high_volt(sdmmc_storage_t *storage, u8 *buf)
|
||||
{
|
||||
if (!_sd_storage_switch_get(storage, buf))
|
||||
return 0;
|
||||
if (!(buf[13] & SD_MODE_HIGH_SPEED))
|
||||
//gfx_hexdump(0, (u8 *)buf, 64);
|
||||
|
||||
u8 access_mode = buf[13];
|
||||
u16 current_limit = buf[7] | buf[6] << 8;
|
||||
|
||||
// Try to raise the current limit to let the card perform better.
|
||||
_sd_storage_set_current_limit(storage, current_limit, buf);
|
||||
|
||||
if (!(access_mode & SD_MODE_HIGH_SPEED))
|
||||
return 1;
|
||||
|
||||
if (!_sd_storage_enable_highspeed(storage, 1, buf))
|
||||
if (!_sd_storage_enable_highspeed(storage, HIGH_SPEED_BUS_SPEED, buf))
|
||||
return 0;
|
||||
|
||||
if (!_sdmmc_storage_check_status(storage))
|
||||
return 0;
|
||||
return sdmmc_setup_clock(storage->sdmmc, 7);
|
||||
}
|
||||
|
||||
static void _sd_storage_parse_ssr(sdmmc_storage_t *storage)
|
||||
{
|
||||
// unstuff_bits supports only 4 u32 so break into 2 x 16byte groups
|
||||
u32 raw_ssr1[4];
|
||||
u32 raw_ssr2[4];
|
||||
|
||||
raw_ssr1[3] = *(u32 *)&storage->raw_ssr[12];
|
||||
raw_ssr1[2] = *(u32 *)&storage->raw_ssr[8];
|
||||
raw_ssr1[1] = *(u32 *)&storage->raw_ssr[4];
|
||||
raw_ssr1[0] = *(u32 *)&storage->raw_ssr[0];
|
||||
|
||||
raw_ssr2[3] = *(u32 *)&storage->raw_ssr[28];
|
||||
raw_ssr2[2] = *(u32 *)&storage->raw_ssr[24];
|
||||
raw_ssr2[1] = *(u32 *)&storage->raw_ssr[20];
|
||||
raw_ssr2[0] = *(u32 *)&storage->raw_ssr[16];
|
||||
|
||||
storage->ssr.bus_width = (unstuff_bits(raw_ssr1, 510 - 384, 2) & SD_BUS_WIDTH_4) ? 4 : 1;
|
||||
switch(unstuff_bits(raw_ssr1, 440 - 384, 8))
|
||||
{
|
||||
case 0:
|
||||
storage->ssr.speed_class = 0;
|
||||
break;
|
||||
case 1:
|
||||
storage->ssr.speed_class = 2;
|
||||
break;
|
||||
case 2:
|
||||
storage->ssr.speed_class = 4;
|
||||
break;
|
||||
case 3:
|
||||
storage->ssr.speed_class = 6;
|
||||
break;
|
||||
case 4:
|
||||
storage->ssr.speed_class = 10;
|
||||
break;
|
||||
default:
|
||||
storage->ssr.speed_class = unstuff_bits(raw_ssr1, 440 - 384, 8);
|
||||
break;
|
||||
}
|
||||
storage->ssr.uhs_grade = unstuff_bits(raw_ssr1, 396 - 384, 4);
|
||||
storage->ssr.video_class = unstuff_bits(raw_ssr1, 384 - 384, 8);
|
||||
|
||||
storage->ssr.app_class = unstuff_bits(raw_ssr2, 336 - 256, 4);
|
||||
}
|
||||
|
||||
static int _sd_storage_get_ssr(sdmmc_storage_t *storage, u8 *buf)
|
||||
{
|
||||
sdmmc_cmd_t cmdbuf;
|
||||
sdmmc_init_cmd(&cmdbuf, SD_APP_SD_STATUS, 0, SDMMC_RSP_TYPE_1, 0);
|
||||
|
||||
sdmmc_req_t reqbuf;
|
||||
reqbuf.buf = buf;
|
||||
reqbuf.blksize = 64;
|
||||
reqbuf.num_sectors = 1;
|
||||
reqbuf.is_write = 0;
|
||||
reqbuf.is_multi_block = 0;
|
||||
reqbuf.is_auto_cmd12 = 0;
|
||||
|
||||
if (!(storage->csd.cmdclass & CCC_APP_SPEC))
|
||||
{
|
||||
DPRINTF("[SD] ssr: Card lacks mandatory SD Status function\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_sd_storage_execute_app_cmd(storage, R1_STATE_TRAN, 0, &cmdbuf, &reqbuf, 0))
|
||||
return 0;
|
||||
|
||||
u32 tmp = 0;
|
||||
sdmmc_get_rsp(storage->sdmmc, &tmp, 4, SDMMC_RSP_TYPE_1);
|
||||
//Prepare buffer for unstuff_bits
|
||||
for (int i = 0; i < 64; i+=4)
|
||||
{
|
||||
storage->raw_ssr[i + 3] = buf[i];
|
||||
storage->raw_ssr[i + 2] = buf[i + 1];
|
||||
storage->raw_ssr[i + 1] = buf[i + 2];
|
||||
storage->raw_ssr[i] = buf[i + 3];
|
||||
}
|
||||
_sd_storage_parse_ssr(storage);
|
||||
|
||||
return _sdmmc_storage_check_result(tmp);
|
||||
return sdmmc_setup_clock(storage->sdmmc, SDHCI_TIMING_SD_HS25);
|
||||
}
|
||||
|
||||
static void _sd_storage_parse_cid(sdmmc_storage_t *storage)
|
||||
@ -1232,45 +1235,65 @@ static void _sd_storage_parse_csd(sdmmc_storage_t *storage)
|
||||
}
|
||||
}
|
||||
|
||||
int sdmmc_storage_init_sd(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 id, u32 bus_width, u32 type)
|
||||
static bool _sdmmc_storage_supports_low_voltage(u32 bus_width, u32 type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case SDHCI_TIMING_UHS_SDR12:
|
||||
case SDHCI_TIMING_UHS_SDR25:
|
||||
case SDHCI_TIMING_UHS_SDR50:
|
||||
case SDHCI_TIMING_UHS_SDR104:
|
||||
case SDHCI_TIMING_UHS_SDR82:
|
||||
case SDHCI_TIMING_UHS_DDR50:
|
||||
if (bus_width == SDMMC_BUS_WIDTH_4)
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int sdmmc_storage_init_sd(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 bus_width, u32 type)
|
||||
{
|
||||
u8 buf[512];
|
||||
int is_version_1 = 0;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
memset(storage, 0, sizeof(sdmmc_storage_t));
|
||||
storage->sdmmc = sdmmc;
|
||||
|
||||
DPRINTF("[SD] before init\n");
|
||||
if (!sdmmc_init(sdmmc, id, SDMMC_POWER_3_3, SDMMC_BUS_WIDTH_1, 5, 0))
|
||||
if (!sdmmc_init(sdmmc, SDMMC_1, SDMMC_POWER_3_3, SDMMC_BUS_WIDTH_1, SDHCI_TIMING_SD_ID, SDMMC_AUTO_CAL_DISABLE))
|
||||
return 0;
|
||||
DPRINTF("[SD] after init\n");
|
||||
DPRINTF("[SD] after init\n");
|
||||
|
||||
usleep(1000 + (74000 + sdmmc->divisor - 1) / sdmmc->divisor);
|
||||
|
||||
if (!_sdmmc_storage_go_idle_state(storage))
|
||||
return 0;
|
||||
DPRINTF("[SD] went to idle state\n");
|
||||
DPRINTF("[SD] went to idle state\n");
|
||||
|
||||
is_version_1 = _sd_storage_send_if_cond(storage);
|
||||
if (is_version_1 == 2)
|
||||
return 0;
|
||||
DPRINTF("[SD] after send if cond\n");
|
||||
DPRINTF("[SD] after send if cond\n");
|
||||
|
||||
if (!_sd_storage_get_op_cond(storage, is_version_1, bus_width == SDMMC_BUS_WIDTH_4 && type == 11))
|
||||
bool supports_low_voltage = _sdmmc_storage_supports_low_voltage(bus_width, type);
|
||||
|
||||
if (!_sd_storage_get_op_cond(storage, is_version_1, supports_low_voltage))
|
||||
return 0;
|
||||
DPRINTF("[SD] got op cond\n");
|
||||
DPRINTF("[SD] got op cond\n");
|
||||
|
||||
if (!_sdmmc_storage_get_cid(storage, storage->raw_cid))
|
||||
return 0;
|
||||
DPRINTF("[SD] got cid\n");
|
||||
DPRINTF("[SD] got cid\n");
|
||||
_sd_storage_parse_cid(storage);
|
||||
|
||||
if (!_sd_storage_get_rca(storage))
|
||||
return 0;
|
||||
DPRINTF("[SD] got rca (= %04X)\n", storage->rca);
|
||||
DPRINTF("[SD] got rca (= %04X)\n", storage->rca);
|
||||
|
||||
if (!_sdmmc_storage_get_csd(storage, storage->raw_csd))
|
||||
return 0;
|
||||
DPRINTF("[SD] got csd\n");
|
||||
DPRINTF("[SD] got csd\n");
|
||||
|
||||
//Parse CSD.
|
||||
_sd_storage_parse_csd(storage);
|
||||
@ -1283,84 +1306,75 @@ int sdmmc_storage_init_sd(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 id, u32
|
||||
storage->sec_cnt = storage->csd.c_size << 10;
|
||||
break;
|
||||
default:
|
||||
DPRINTF("[SD] Unknown CSD structure %d\n", storage->csd.structure);
|
||||
DPRINTF("[SD] unknown CSD structure %d\n", storage->csd.structure);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!storage->is_low_voltage)
|
||||
{
|
||||
if (!sdmmc_setup_clock(storage->sdmmc, 6))
|
||||
if (!sdmmc_setup_clock(storage->sdmmc, SDHCI_TIMING_SD_DS12))
|
||||
return 0;
|
||||
DPRINTF("[SD] after setup clock\n");
|
||||
DPRINTF("[SD] after setup clock\n");
|
||||
}
|
||||
|
||||
if (!_sdmmc_storage_select_card(storage))
|
||||
return 0;
|
||||
DPRINTF("[SD] card selected\n");
|
||||
DPRINTF("[SD] card selected\n");
|
||||
|
||||
if (!_sdmmc_storage_set_blocklen(storage, 512))
|
||||
return 0;
|
||||
DPRINTF("[SD] set blocklen to 512\n");
|
||||
DPRINTF("[SD] set blocklen to 512\n");
|
||||
|
||||
u32 tmp = 0;
|
||||
if (!_sd_storage_execute_app_cmd_type1(storage, &tmp, SD_APP_SET_CLR_CARD_DETECT, 0, 0, R1_STATE_TRAN))
|
||||
return 0;
|
||||
DPRINTF("[SD] cleared card detect\n");
|
||||
DPRINTF("[SD] cleared card detect\n");
|
||||
|
||||
u8 *buf = (u8 *)malloc(512);
|
||||
if (!_sd_storage_get_scr(storage, buf))
|
||||
{
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DPRINTF("[SD] got scr\n");
|
||||
|
||||
//gfx_hexdump(0, storage->raw_scr, 8);
|
||||
DPRINTF("[SD] got scr\n");
|
||||
|
||||
// Check if card supports a wider bus and if it's not SD Version 1.X
|
||||
if (bus_width == SDMMC_BUS_WIDTH_4 && (storage->scr.bus_widths & 4) && (storage->scr.sda_vsn & 0xF))
|
||||
{
|
||||
if (!_sd_storage_execute_app_cmd_type1(storage, &tmp, SD_APP_SET_BUS_WIDTH, SD_BUS_WIDTH_4, 0, R1_STATE_TRAN))
|
||||
{
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
sdmmc_set_bus_width(storage->sdmmc, SDMMC_BUS_WIDTH_4);
|
||||
DPRINTF("[SD] switched to wide bus width\n");
|
||||
DPRINTF("[SD] switched to wide bus width\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
DPRINTF("[SD] SD does not support wide bus width\n");
|
||||
DPRINTF("[SD] SD does not support wide bus width\n");
|
||||
}
|
||||
|
||||
if (storage->is_low_voltage)
|
||||
{
|
||||
if (!_sd_storage_enable_highspeed_low_volt(storage, type, buf))
|
||||
{
|
||||
free(buf);
|
||||
if (!_sd_storage_enable_uhs_low_volt(storage, type, buf))
|
||||
return 0;
|
||||
}
|
||||
DPRINTF("[SD] enabled highspeed (low voltage)\n");
|
||||
DPRINTF("[SD] enabled UHS\n");
|
||||
|
||||
sdmmc_card_clock_ctrl(sdmmc, SDMMC_AUTO_CAL_ENABLE);
|
||||
}
|
||||
else if (type != 6 && (storage->scr.sda_vsn & 0xF) != 0)
|
||||
else if (type != SDHCI_TIMING_SD_DS12 && (storage->scr.sda_vsn & 0xF) != 0)
|
||||
{
|
||||
if (!_sd_storage_enable_highspeed_high_volt(storage, buf))
|
||||
{
|
||||
free(buf);
|
||||
if (!_sd_storage_enable_hs_high_volt(storage, buf))
|
||||
return 0;
|
||||
|
||||
DPRINTF("[SD] enabled HS\n");
|
||||
switch (bus_width)
|
||||
{
|
||||
case SDMMC_BUS_WIDTH_4:
|
||||
storage->csd.busspeed = 25;
|
||||
break;
|
||||
case SDMMC_BUS_WIDTH_1:
|
||||
storage->csd.busspeed = 6;
|
||||
break;
|
||||
}
|
||||
DPRINTF("[SD] enabled highspeed (high voltage)\n");
|
||||
storage->csd.busspeed = 25;
|
||||
}
|
||||
|
||||
sdmmc_sd_clock_ctrl(sdmmc, 1);
|
||||
|
||||
// Parse additional card info from sd status.
|
||||
if (_sd_storage_get_ssr(storage, buf))
|
||||
{
|
||||
DPRINTF("[SD] got sd status\n");
|
||||
}
|
||||
|
||||
free(buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -1400,17 +1414,17 @@ int sdmmc_storage_init_gc(sdmmc_storage_t *storage, sdmmc_t *sdmmc)
|
||||
memset(storage, 0, sizeof(sdmmc_storage_t));
|
||||
storage->sdmmc = sdmmc;
|
||||
|
||||
if (!sdmmc_init(sdmmc, SDMMC_2, SDMMC_POWER_1_8, SDMMC_BUS_WIDTH_8, 14, 0))
|
||||
if (!sdmmc_init(sdmmc, SDMMC_2, SDMMC_POWER_1_8, SDMMC_BUS_WIDTH_8, SDHCI_TIMING_MMC_DDR52, SDMMC_AUTO_CAL_DISABLE))
|
||||
return 0;
|
||||
DPRINTF("[gc] after init\n");
|
||||
DPRINTF("[gc] after init\n");
|
||||
|
||||
usleep(1000 + (10000 + sdmmc->divisor - 1) / sdmmc->divisor);
|
||||
|
||||
if (!sdmmc_config_tuning(storage->sdmmc, 14, MMC_SEND_TUNING_BLOCK_HS200))
|
||||
if (!sdmmc_tuning_execute(storage->sdmmc, SDHCI_TIMING_MMC_DDR52, MMC_SEND_TUNING_BLOCK_HS200))
|
||||
return 0;
|
||||
DPRINTF("[gc] after tuning\n");
|
||||
DPRINTF("[gc] after tuning\n");
|
||||
|
||||
sdmmc_sd_clock_ctrl(sdmmc, 1);
|
||||
sdmmc_card_clock_ctrl(sdmmc, SDMMC_AUTO_CAL_ENABLE);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
* Copyright (C) 2018 CTCaer
|
||||
* Copyright (c) 2018 CTCaer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
@ -95,12 +95,10 @@ typedef struct _sdmmc_storage_t
|
||||
u8 raw_cid[0x10];
|
||||
u8 raw_csd[0x10];
|
||||
u8 raw_scr[8];
|
||||
u8 raw_ssr[0x40];
|
||||
mmc_cid_t cid;
|
||||
mmc_csd_t csd;
|
||||
mmc_ext_csd_t ext_csd;
|
||||
sd_scr_t scr;
|
||||
sd_ssr_t ssr;
|
||||
} sdmmc_storage_t;
|
||||
|
||||
extern sdmmc_accessor_t *_current_accessor;
|
||||
@ -109,9 +107,9 @@ extern bool sdmmc_memcpy_buf;
|
||||
int sdmmc_storage_end(sdmmc_storage_t *storage);
|
||||
int sdmmc_storage_read(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf);
|
||||
int sdmmc_storage_write(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf);
|
||||
int sdmmc_storage_init_mmc(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 id, u32 bus_width, u32 type);
|
||||
int sdmmc_storage_init_mmc(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 bus_width, u32 type);
|
||||
int sdmmc_storage_set_mmc_partition(sdmmc_storage_t *storage, u32 partition);
|
||||
int sdmmc_storage_init_sd(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 id, u32 bus_width, u32 type);
|
||||
int sdmmc_storage_init_sd(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 bus_width, u32 type);
|
||||
int sdmmc_storage_init_gc(sdmmc_storage_t *storage, sdmmc_t *sdmmc);
|
||||
intptr_t sdmmc_calculate_dma_addr(sdmmc_accessor_t *_this, void *buf, unsigned int num_sectors);
|
||||
int sdmmc_calculate_dma_index(sdmmc_accessor_t *_this, void *buf, unsigned int num_sectors);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
* Copyright (c) 2018-2019 CTCaer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
@ -49,24 +50,158 @@
|
||||
#define SDMMC_MASKINT_NOERROR -1
|
||||
#define SDMMC_MASKINT_ERROR -2
|
||||
|
||||
/*! SDMMC host control 2 */
|
||||
#define SDHCI_CTRL_UHS_MASK 0xFFF8
|
||||
#define SDHCI_CTRL_VDD_330 0xFFF7
|
||||
#define SDHCI_CTRL_VDD_180 8
|
||||
#define SDHCI_CTRL_EXEC_TUNING 0x40
|
||||
#define SDHCI_CTRL_TUNED_CLK 0x80
|
||||
#define SDHCI_HOST_VERSION_4_EN 0x1000
|
||||
#define SDHCI_ADDRESSING_64BIT_EN 0x2000
|
||||
#define SDHCI_CTRL_PRESET_VAL_EN 0x8000
|
||||
/*! SDMMC present state. */
|
||||
#define SDHCI_CMD_INHIBIT 0x1
|
||||
#define SDHCI_DATA_INHIBIT 0x2
|
||||
#define SDHCI_DOING_WRITE 0x100
|
||||
#define SDHCI_DOING_READ 0x200
|
||||
#define SDHCI_SPACE_AVAILABLE 0x400
|
||||
#define SDHCI_DATA_AVAILABLE 0x800
|
||||
#define SDHCI_CARD_PRESENT 0x10000
|
||||
#define SDHCI_CD_STABLE 0x20000
|
||||
#define SDHCI_CD_LVL 0x40000
|
||||
#define SDHCI_WRITE_PROTECT 0x80000
|
||||
#define SDHCI_DATA_LVL_MASK 0xF00000
|
||||
#define SDHCI_DATA_0_LVL_MASK 0x100000
|
||||
#define SDHCI_CMD_LVL 0x1000000
|
||||
|
||||
/*! SDMMC transfer mode. */
|
||||
#define SDHCI_TRNS_DMA 0x01
|
||||
#define SDHCI_TRNS_BLK_CNT_EN 0x02
|
||||
#define SDHCI_TRNS_AUTO_CMD12 0x04
|
||||
#define SDHCI_TRNS_AUTO_CMD23 0x08
|
||||
#define SDHCI_TRNS_AUTO_SEL 0x0C
|
||||
#define SDHCI_TRNS_WRITE 0x00
|
||||
#define SDHCI_TRNS_READ 0x10
|
||||
#define SDHCI_TRNS_MULTI 0x20
|
||||
|
||||
/*! SDMMC command. */
|
||||
#define SDHCI_CMD_RESP_MASK 0x3
|
||||
#define SDHCI_CMD_RESP_NO_RESP 0x0
|
||||
#define SDHCI_CMD_RESP_LEN136 0x1
|
||||
#define SDHCI_CMD_RESP_LEN48 0x2
|
||||
#define SDHCI_CMD_RESP_LEN48_BUSY 0x3
|
||||
#define SDHCI_CMD_CRC 0x08
|
||||
#define SDHCI_CMD_INDEX 0x10
|
||||
#define SDHCI_CMD_DATA 0x20
|
||||
#define SDHCI_CMD_ABORTCMD 0xC0
|
||||
|
||||
/*! SDMMC host control. */
|
||||
#define SDHCI_CTRL_LED 0x01
|
||||
#define SDHCI_CTRL_4BITBUS 0x02
|
||||
#define SDHCI_CTRL_HISPD 0x04
|
||||
#define SDHCI_CTRL_DMA_MASK 0x18
|
||||
#define SDHCI_CTRL_SDMA 0x00
|
||||
#define SDHCI_CTRL_ADMA1 0x08
|
||||
#define SDHCI_CTRL_ADMA32 0x10
|
||||
#define SDHCI_CTRL_ADMA64 0x18
|
||||
#define SDHCI_CTRL_8BITBUS 0x20
|
||||
#define SDHCI_CTRL_CDTEST_INS 0x40
|
||||
#define SDHCI_CTRL_CDTEST_EN 0x80
|
||||
|
||||
/*! SDMMC host control 2. */
|
||||
#define SDHCI_CTRL_UHS_MASK 0xFFF8
|
||||
#define SDHCI_CTRL_VDD_180 8
|
||||
#define SDHCI_CTRL_DRV_TYPE_B 0x00
|
||||
#define SDHCI_CTRL_DRV_TYPE_A 0x10
|
||||
#define SDHCI_CTRL_DRV_TYPE_C 0x20
|
||||
#define SDHCI_CTRL_DRV_TYPE_D 0x30
|
||||
#define SDHCI_CTRL_EXEC_TUNING 0x40
|
||||
#define SDHCI_CTRL_TUNED_CLK 0x80
|
||||
#define SDHCI_HOST_VERSION_4_EN 0x1000
|
||||
#define SDHCI_ADDRESSING_64BIT_EN 0x2000
|
||||
#define SDHCI_CTRL_PRESET_VAL_EN 0x8000
|
||||
|
||||
/*! SDMMC power control. */
|
||||
#define SDHCI_POWER_ON 0x01
|
||||
#define SDHCI_POWER_180 0x0A
|
||||
#define SDHCI_POWER_300 0x0C
|
||||
#define SDHCI_POWER_330 0x0E
|
||||
#define SDHCI_POWER_MASK 0xF1
|
||||
|
||||
// /*! SDMMC max current. */
|
||||
// #define SDHCI_MAX_CURRENT_330_MASK 0xFF
|
||||
// #define SDHCI_MAX_CURRENT_180_MASK 0xFF0000
|
||||
// #define SDHCI_MAX_CURRENT_MULTIPLIER 4
|
||||
|
||||
/*! SDMMC clock control. */
|
||||
#define SDHCI_DIVIDER_SHIFT 8
|
||||
#define SDHCI_DIVIDER_HI_SHIFT 6
|
||||
#define SDHCI_DIV_MASK 0xFF00
|
||||
#define SDHCI_DIV_HI_MASK 0xC0
|
||||
#define SDHCI_PROG_CLOCK_MODE 0x20
|
||||
#define SDHCI_CLOCK_CARD_EN 0x4
|
||||
#define SDHCI_CLOCK_INT_STABLE 0x2
|
||||
#define SDHCI_CLOCK_INT_EN 0x1
|
||||
|
||||
/*! SDMMC software reset. */
|
||||
#define SDHCI_RESET_ALL 0x01
|
||||
#define SDHCI_RESET_CMD 0x02
|
||||
#define SDHCI_RESET_DATA 0x04
|
||||
|
||||
/*! SDMMC interrupt status and control. */
|
||||
#define SDHCI_INT_RESPONSE 0x1
|
||||
#define SDHCI_INT_DATA_END 0x2
|
||||
#define SDHCI_INT_BLK_GAP 0x4
|
||||
#define SDHCI_INT_DMA_END 0x8
|
||||
#define SDHCI_INT_SPACE_AVAIL 0x10
|
||||
#define SDHCI_INT_DATA_AVAIL 0x20
|
||||
#define SDHCI_INT_CARD_INSERT 0x40
|
||||
#define SDHCI_INT_CARD_REMOVE 0x80
|
||||
#define SDHCI_INT_CARD_INT 0x100
|
||||
#define SDHCI_INT_RETUNE 0x1000
|
||||
#define SDHCI_INT_CQE 0x4000
|
||||
#define SDHCI_INT_ERROR 0x8000
|
||||
|
||||
/*! SDMMC error interrupt status and control. */
|
||||
#define SDHCI_ERR_INT_TIMEOUT 0x1
|
||||
#define SDHCI_ERR_INT_CRC 0x2
|
||||
#define SDHCI_ERR_INT_END_BIT 0x4
|
||||
#define SDHCI_ERR_INT_INDEX 0x8
|
||||
#define SDHCI_ERR_INT_DATA_TIMEOUT 0x10
|
||||
#define SDHCI_ERR_INT_DATA_CRC 0x20
|
||||
#define SDHCI_ERR_INT_DATA_END_BIT 0x40
|
||||
#define SDHCI_ERR_INT_BUS_POWER 0x80
|
||||
#define SDHCI_ERR_INT_AUTO_CMD_ERR 0x100
|
||||
#define SDHCI_ERR_INT_ADMA_ERROR 0x200
|
||||
|
||||
#define SDHCI_ERR_INT_ALL_EXCEPT_ADMA_BUSPWR \
|
||||
(SDHCI_ERR_INT_AUTO_CMD_ERR | SDHCI_ERR_INT_DATA_END_BIT | \
|
||||
SDHCI_ERR_INT_DATA_CRC | SDHCI_ERR_INT_DATA_TIMEOUT | \
|
||||
SDHCI_ERR_INT_INDEX | SDHCI_ERR_INT_END_BIT | \
|
||||
SDHCI_ERR_INT_CRC | SDHCI_ERR_INT_TIMEOUT)
|
||||
|
||||
/*! SD bus speeds. */
|
||||
#define UHS_SDR12_BUS_SPEED 0
|
||||
#define HIGH_SPEED_BUS_SPEED 1
|
||||
#define UHS_SDR25_BUS_SPEED 1
|
||||
#define UHS_SDR50_BUS_SPEED 2
|
||||
#define UHS_SDR104_BUS_SPEED 3
|
||||
#define UHS_DDR50_BUS_SPEED 4
|
||||
#define HS400_BUS_SPEED 5
|
||||
#define UHS_SDR12_BUS_SPEED 0
|
||||
#define HIGH_SPEED_BUS_SPEED 1
|
||||
#define UHS_SDR25_BUS_SPEED 1
|
||||
#define UHS_SDR50_BUS_SPEED 2
|
||||
#define UHS_SDR104_BUS_SPEED 3
|
||||
#define UHS_DDR50_BUS_SPEED 4
|
||||
#define HS400_BUS_SPEED 5
|
||||
|
||||
/*! SDMMC timmings. */
|
||||
#define SDHCI_TIMING_MMC_ID 0
|
||||
#define SDHCI_TIMING_MMC_LS26 1
|
||||
#define SDHCI_TIMING_MMC_HS52 2
|
||||
#define SDHCI_TIMING_MMC_HS200 3
|
||||
#define SDHCI_TIMING_MMC_HS400 4
|
||||
#define SDHCI_TIMING_SD_ID 5
|
||||
#define SDHCI_TIMING_SD_DS12 6
|
||||
#define SDHCI_TIMING_SD_HS25 7
|
||||
#define SDHCI_TIMING_UHS_SDR12 8
|
||||
#define SDHCI_TIMING_UHS_SDR25 9
|
||||
#define SDHCI_TIMING_UHS_SDR50 10
|
||||
#define SDHCI_TIMING_UHS_SDR104 11
|
||||
#define SDHCI_TIMING_UHS_SDR82 12 // SDR104 with a 163.2MHz -> 81.6MHz clock.
|
||||
#define SDHCI_TIMING_UHS_DDR50 13
|
||||
#define SDHCI_TIMING_MMC_DDR52 14
|
||||
|
||||
#define SDHCI_CAN_64BIT 0x10000000
|
||||
|
||||
/*! SDMMC Low power features. */
|
||||
#define SDMMC_AUTO_CAL_DISABLE 0
|
||||
#define SDMMC_AUTO_CAL_ENABLE 1
|
||||
|
||||
/*! Helper for SWITCH command argument. */
|
||||
#define SDMMC_SWITCH(mode, index, value) (((mode) << 24) | ((index) << 16) | ((value) << 8))
|
||||
@ -78,8 +213,8 @@ typedef struct _sdmmc_t
|
||||
u32 id;
|
||||
u32 divisor;
|
||||
u32 clock_stopped;
|
||||
int no_sd;
|
||||
int sd_clock_enabled;
|
||||
int auto_cal_enabled;
|
||||
int card_clock_enabled;
|
||||
int venclkctl_set;
|
||||
u32 venclkctl_tap;
|
||||
u32 expected_rsp_type;
|
||||
@ -109,19 +244,21 @@ typedef struct _sdmmc_req_t
|
||||
int is_auto_cmd12;
|
||||
} sdmmc_req_t;
|
||||
|
||||
int sdmmc_get_voltage(sdmmc_t *sdmmc);
|
||||
u32 sdmmc_get_bus_width(sdmmc_t *sdmmc);
|
||||
int sdmmc_get_io_power(sdmmc_t *sdmmc);
|
||||
u32 sdmmc_get_bus_width(sdmmc_t *sdmmc);
|
||||
void sdmmc_set_bus_width(sdmmc_t *sdmmc, u32 bus_width);
|
||||
void sdmmc_get_venclkctl(sdmmc_t *sdmmc);
|
||||
int sdmmc_setup_clock(sdmmc_t *sdmmc, u32 type);
|
||||
void sdmmc_sd_clock_ctrl(sdmmc_t *sdmmc, int no_sd);
|
||||
int sdmmc_get_rsp(sdmmc_t *sdmmc, u32 *rsp, u32 size, u32 type);
|
||||
int sdmmc_config_tuning(sdmmc_t *sdmmc, u32 type, u32 cmd);
|
||||
int sdmmc_stop_transmission(sdmmc_t *sdmmc, u32 *rsp);
|
||||
int sdmmc_init(sdmmc_t *sdmmc, u32 id, u32 power, u32 bus_width, u32 type, int no_sd);
|
||||
void sdmmc_set_tap_value(sdmmc_t *sdmmc);
|
||||
int sdmmc_setup_clock(sdmmc_t *sdmmc, u32 type);
|
||||
void sdmmc_card_clock_ctrl(sdmmc_t *sdmmc, int auto_cal_enable);
|
||||
int sdmmc_get_rsp(sdmmc_t *sdmmc, u32 *rsp, u32 size, u32 type);
|
||||
int sdmmc_tuning_execute(sdmmc_t *sdmmc, u32 type, u32 cmd);
|
||||
int sdmmc_stop_transmission(sdmmc_t *sdmmc, u32 *rsp);
|
||||
int sdmmc_get_sd_power_enabled();
|
||||
bool sdmmc_get_sd_inserted();
|
||||
int sdmmc_init(sdmmc_t *sdmmc, u32 id, u32 power, u32 bus_width, u32 type, int auto_cal_enable);
|
||||
void sdmmc_end(sdmmc_t *sdmmc);
|
||||
void sdmmc_init_cmd(sdmmc_cmd_t *cmdbuf, u16 cmd, u32 arg, u32 rsp_type, u32 check_busy);
|
||||
int sdmmc_execute_cmd(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, sdmmc_req_t *req, u32 *blkcnt_out);
|
||||
int sdmmc_enable_low_voltage(sdmmc_t *sdmmc);
|
||||
int sdmmc_execute_cmd(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, sdmmc_req_t *req, u32 *blkcnt_out);
|
||||
int sdmmc_enable_low_voltage(sdmmc_t *sdmmc);
|
||||
|
||||
#endif
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
* Copyright (c) 2018-2019 CTCaer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
@ -19,49 +20,14 @@
|
||||
|
||||
#include "../utils/types.h"
|
||||
|
||||
#define TEGRA_MMC_PWRCTL_SD_BUS_POWER 0x1
|
||||
#define TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8 0xA
|
||||
#define TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_0 0xC
|
||||
#define TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3 0xE
|
||||
#define TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_MASK 0xF1
|
||||
|
||||
#define TEGRA_MMC_HOSTCTL_1BIT 0x00
|
||||
#define TEGRA_MMC_HOSTCTL_4BIT 0x02
|
||||
#define TEGRA_MMC_HOSTCTL_8BIT 0x20
|
||||
|
||||
#define TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE 0x1
|
||||
#define TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE 0x2
|
||||
#define TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE 0x4
|
||||
#define TEGRA_MMC_CLKCON_CLKGEN_SELECT 0x20
|
||||
|
||||
#define TEGRA_MMC_SWRST_SW_RESET_FOR_ALL 0x1
|
||||
#define TEGRA_MMC_SWRST_SW_RESET_FOR_CMD_LINE 0x2
|
||||
#define TEGRA_MMC_SWRST_SW_RESET_FOR_DAT_LINE 0x4
|
||||
|
||||
#define TEGRA_MMC_TRNMOD_DMA_ENABLE 0x1
|
||||
#define TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE 0x2
|
||||
#define TEGRA_MMC_TRNMOD_AUTO_CMD12 0x4
|
||||
#define TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_WRITE 0x0
|
||||
#define TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ 0x10
|
||||
#define TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT 0x20
|
||||
|
||||
#define TEGRA_MMC_TRNMOD_CMD_CRC_CHECK 0x8
|
||||
#define TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK 0x10
|
||||
#define TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER 0x20
|
||||
|
||||
#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_MASK 0x3
|
||||
#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE 0x0
|
||||
#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136 0x1
|
||||
#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48 0x2
|
||||
#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY 0x3
|
||||
|
||||
#define TEGRA_MMC_NORINTSTS_CMD_COMPLETE 0x1
|
||||
#define TEGRA_MMC_NORINTSTS_XFER_COMPLETE 0x2
|
||||
#define TEGRA_MMC_NORINTSTS_DMA_INTERRUPT 0x8
|
||||
#define TEGRA_MMC_NORINTSTS_ERR_INTERRUPT 0x8000
|
||||
#define TEGRA_MMC_NORINTSTS_CMD_TIMEOUT 0x10000
|
||||
|
||||
#define TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY 0x20
|
||||
#define TEGRA_MMC_VNDR_TUN_CTRL0_TAP_VAL_UPDATED_BY_HW 0x20000
|
||||
#define TEGRA_MMC_DLLCAL_CFG_EN_CALIBRATE 0x80000000
|
||||
#define TEGRA_MMC_DLLCAL_CFG_STATUS_DLL_ACTIVE 0x80000000
|
||||
#define TEGRA_MMC_SDMEMCOMPPADCTRL_PAD_E_INPUT_PWRD 0x80000000
|
||||
#define TEGRA_MMC_SDMEMCOMPPADCTRL_COMP_VREF_SEL_MASK 0xFFFFFFF0
|
||||
#define TEGRA_MMC_AUTOCALCFG_AUTO_CAL_ENABLE 0x20000000
|
||||
#define TEGRA_MMC_AUTOCALCFG_AUTO_CAL_START 0x80000000
|
||||
#define TEGRA_MMC_AUTOCALSTS_AUTO_CAL_ACTIVE 0x80000000
|
||||
|
||||
typedef struct _t210_sdmmc_t
|
||||
{
|
||||
@ -77,56 +43,66 @@ typedef struct _t210_sdmmc_t
|
||||
vu32 rspreg3;
|
||||
vu32 bdata;
|
||||
vu32 prnsts;
|
||||
vu8 hostctl;
|
||||
vu8 pwrcon;
|
||||
vu8 blkgap;
|
||||
vu8 wakcon;
|
||||
vu8 hostctl;
|
||||
vu8 pwrcon;
|
||||
vu8 blkgap;
|
||||
vu8 wakcon;
|
||||
vu16 clkcon;
|
||||
vu8 timeoutcon;
|
||||
vu8 swrst;
|
||||
vu8 timeoutcon;
|
||||
vu8 swrst;
|
||||
vu16 norintsts;
|
||||
vu16 errintsts;
|
||||
vu16 norintstsen;
|
||||
vu16 errintstsen;
|
||||
vu16 norintsigen;
|
||||
vu16 errintsigen;
|
||||
vu16 norintstsen; // Enable irq status.
|
||||
vu16 errintstsen; // Enable irq status.
|
||||
vu16 norintsigen; // Enable irq signal to LIC/GIC.
|
||||
vu16 errintsigen; // Enable irq signal to LIC/GIC.
|
||||
vu16 acmd12errsts;
|
||||
vu16 hostctl2;
|
||||
vu32 capareg;
|
||||
vu32 capareg_1;
|
||||
vu32 maxcurr;
|
||||
vu8 res3[4];
|
||||
vu8 rsvd0[4]; // 4C-4F reserved for more max current.
|
||||
vu16 setacmd12err;
|
||||
vu16 setinterr;
|
||||
vu8 admaerr;
|
||||
vu8 res4[3];
|
||||
vu8 admaerr;
|
||||
vu8 rsvd1[3]; // 55-57 reserved.
|
||||
vu32 admaaddr;
|
||||
vu32 admaaddr_hi;
|
||||
vu8 res5[156];
|
||||
vu16 slotintstatus;
|
||||
vu8 rsvd2[156]; // 60-FB reserved.
|
||||
vu16 slotintsts;
|
||||
vu16 hcver;
|
||||
vu32 venclkctl;
|
||||
vu32 venspictl;
|
||||
vu32 venspiintsts;
|
||||
vu32 venceatactl;
|
||||
vu32 vensysswctl;
|
||||
vu32 venerrintsts;
|
||||
vu32 vencapover;
|
||||
vu32 venbootctl;
|
||||
vu32 venbootacktout;
|
||||
vu32 venbootdattout;
|
||||
vu32 vendebouncecnt;
|
||||
vu32 venmiscctl;
|
||||
vu32 res6[34];
|
||||
vu32 maxcurrover;
|
||||
vu32 maxcurrover_hi;
|
||||
vu32 unk0[32]; // 0x12C
|
||||
vu32 veniotrimctl;
|
||||
vu32 vendllcal;
|
||||
vu8 res7[8];
|
||||
vu32 dllcfgstatus;
|
||||
vu32 vendllcalcfg;
|
||||
vu32 vendllctl0;
|
||||
vu32 vendllctl1;
|
||||
vu32 vendllcalcfgsts;
|
||||
vu32 ventunctl0;
|
||||
vu32 field_1C4;
|
||||
vu8 field_1C8[24];
|
||||
vu32 ventunctl1;
|
||||
vu32 ventunsts0;
|
||||
vu32 ventunsts1;
|
||||
vu32 venclkgatehystcnt;
|
||||
vu32 venpresetval0;
|
||||
vu32 venpresetval1;
|
||||
vu32 venpresetval2;
|
||||
vu32 sdmemcmppadctl;
|
||||
vu32 autocalcfg;
|
||||
vu32 autocalintval;
|
||||
vu32 autocalsts;
|
||||
vu32 iospare;
|
||||
vu32 mcciffifoctl;
|
||||
vu32 timeoutwcoal;
|
||||
} t210_sdmmc_t;
|
||||
|
||||
#endif
|
||||
|
@ -18,14 +18,12 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../soc/gpio.h"
|
||||
#include "../utils/fatal.h"
|
||||
#include "../libs/fatfs/diskio.h"
|
||||
#include "emummc.h"
|
||||
#include "emummc_ctx.h"
|
||||
#include "../utils/fatal.h"
|
||||
#include "../libs/fatfs/diskio.h"
|
||||
|
||||
static bool sdmmc_first_init = false;
|
||||
static bool storageMMCinitialized = false;
|
||||
static bool storageSDinitialized = false;
|
||||
|
||||
// hekate sdmmmc vars
|
||||
@ -35,6 +33,7 @@ sdmmc_t sd_sdmmc;
|
||||
sdmmc_storage_t sd_storage;
|
||||
|
||||
// init vars
|
||||
bool init_done = false;
|
||||
bool custom_driver = true;
|
||||
|
||||
// FS funcs
|
||||
@ -51,8 +50,8 @@ volatile int *active_partition;
|
||||
volatile Handle *sdmmc_das_handle;
|
||||
|
||||
// FatFS
|
||||
file_based_ctxt f_emu;
|
||||
static bool fat_mounted = false;
|
||||
static file_based_ctxt f_emu;
|
||||
|
||||
static void _sdmmc_ensure_device_attached(void)
|
||||
{
|
||||
@ -68,8 +67,6 @@ static void _sdmmc_ensure_device_attached(void)
|
||||
|
||||
static void _sdmmc_ensure_initialized(void)
|
||||
{
|
||||
static bool init_done = false;
|
||||
|
||||
// First Initial init
|
||||
if (!sdmmc_first_init)
|
||||
{
|
||||
@ -78,10 +75,11 @@ static void _sdmmc_ensure_initialized(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
// The boot sysmodule will eventually kill power to SD. Detect this, and reinitialize when it happens.
|
||||
// The boot sysmodule will eventually kill power to SD.
|
||||
// Detect this, and reinitialize when it happens.
|
||||
if (!init_done)
|
||||
{
|
||||
if (gpio_read(GPIO_PORT_E, GPIO_PIN_4) == 0)
|
||||
if (sdmmc_get_sd_power_enabled() == 0)
|
||||
{
|
||||
sdmmc_finalize();
|
||||
sdmmc_initialize();
|
||||
@ -118,8 +116,6 @@ static void _file_based_emmc_finalize(void)
|
||||
|
||||
void sdmmc_finalize(void)
|
||||
{
|
||||
_file_based_emmc_finalize();
|
||||
|
||||
if (!sdmmc_storage_end(&sd_storage))
|
||||
{
|
||||
fatal_abort(Fatal_InitSD);
|
||||
@ -132,7 +128,6 @@ static void _file_based_emmc_initialize(void)
|
||||
{
|
||||
char path[sizeof(emuMMC_ctx.storagePath) + 0x20];
|
||||
memset(&path, 0, sizeof(path));
|
||||
memset(&f_emu, 0, sizeof(file_based_ctxt));
|
||||
|
||||
memcpy(path, (void *)emuMMC_ctx.storagePath, sizeof(emuMMC_ctx.storagePath));
|
||||
strcat(path, "/eMMC/");
|
||||
@ -141,18 +136,24 @@ static void _file_based_emmc_initialize(void)
|
||||
// Open BOOT0 physical partition.
|
||||
memcpy(path + path_len, "BOOT0", 6);
|
||||
if (f_open(&f_emu.fp_boot0, path, FA_READ | FA_WRITE) != FR_OK)
|
||||
fatal_abort(Fatal_InitSD);
|
||||
fatal_abort(Fatal_FatfsFileOpen);
|
||||
if (!f_expand_cltbl(&f_emu.fp_boot0, 0x400, f_emu.clmt_boot0, f_size(&f_emu.fp_boot0)))
|
||||
fatal_abort(Fatal_FatfsMemExhaustion);
|
||||
|
||||
// Open BOOT1 physical partition.
|
||||
memcpy(path + path_len, "BOOT1", 6);
|
||||
if (f_open(&f_emu.fp_boot1, path, FA_READ | FA_WRITE) != FR_OK)
|
||||
fatal_abort(Fatal_InitSD);
|
||||
fatal_abort(Fatal_FatfsFileOpen);
|
||||
if (!f_expand_cltbl(&f_emu.fp_boot1, 0x400, f_emu.clmt_boot1, f_size(&f_emu.fp_boot1)))
|
||||
fatal_abort(Fatal_FatfsMemExhaustion);
|
||||
|
||||
// Open handles for GPP physical partition files.
|
||||
_file_based_update_filename(path, path_len, 00);
|
||||
|
||||
if (f_open(&f_emu.fp_gpp[0], path, FA_READ | FA_WRITE) != FR_OK)
|
||||
fatal_abort(Fatal_InitSD);
|
||||
fatal_abort(Fatal_FatfsFileOpen);
|
||||
if (!f_expand_cltbl(&f_emu.fp_gpp[0], 0x400, &f_emu.clmt_gpp[0], f_size(&f_emu.fp_gpp[0])))
|
||||
fatal_abort(Fatal_FatfsMemExhaustion);
|
||||
|
||||
f_emu.part_size = f_size(&f_emu.fp_gpp[0]) >> 9;
|
||||
|
||||
@ -171,38 +172,27 @@ static void _file_based_emmc_initialize(void)
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!f_expand_cltbl(&f_emu.fp_gpp[f_emu.parts], 0x400, &f_emu.clmt_gpp[f_emu.parts * 0x400], f_size(&f_emu.fp_gpp[f_emu.parts])))
|
||||
fatal_abort(Fatal_FatfsMemExhaustion);
|
||||
}
|
||||
}
|
||||
|
||||
bool sdmmc_initialize(void)
|
||||
{
|
||||
if (!storageMMCinitialized)
|
||||
{
|
||||
if (sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4))
|
||||
{
|
||||
if (sdmmc_storage_set_mmc_partition(&storage, FS_EMMC_PARTITION_GPP))
|
||||
storageMMCinitialized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
fatal_abort(Fatal_InitMMC);
|
||||
}
|
||||
}
|
||||
|
||||
if (!storageSDinitialized)
|
||||
{
|
||||
int retries = 5;
|
||||
int retries = 3;
|
||||
while (retries)
|
||||
{
|
||||
if (sdmmc_storage_init_sd(&sd_storage, &sd_sdmmc, SDMMC_1, SDMMC_BUS_WIDTH_4, 11))
|
||||
if (nx_sd_initialize(false))
|
||||
{
|
||||
storageSDinitialized = true;
|
||||
|
||||
// File based emummc.
|
||||
if ((emuMMC_ctx.EMMC_Type == emuMMC_SD_File) && !fat_mounted)
|
||||
{
|
||||
f_emu.sd_fs = (FATFS *)malloc(sizeof(FATFS));
|
||||
if (f_mount(f_emu.sd_fs, "", 1) != FR_OK)
|
||||
if (f_mount(&f_emu.sd_fs, "", 1) != FR_OK)
|
||||
fatal_abort(Fatal_InitSD);
|
||||
else
|
||||
fat_mounted = true;
|
||||
@ -214,7 +204,6 @@ bool sdmmc_initialize(void)
|
||||
}
|
||||
|
||||
retries--;
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
if (!storageSDinitialized)
|
||||
@ -223,7 +212,7 @@ bool sdmmc_initialize(void)
|
||||
}
|
||||
}
|
||||
|
||||
return storageMMCinitialized && storageSDinitialized;
|
||||
return storageSDinitialized;
|
||||
}
|
||||
|
||||
sdmmc_accessor_t *sdmmc_accessor_get(int mmc_id)
|
||||
@ -295,38 +284,36 @@ static uint64_t emummc_read_write_inner(void *buf, unsigned int sector, unsigned
|
||||
}
|
||||
|
||||
// File based emummc.
|
||||
FIL *fp_tmp = NULL;
|
||||
FIL *fp = NULL;
|
||||
switch (*active_partition)
|
||||
{
|
||||
case FS_EMMC_PARTITION_GPP:
|
||||
if (f_emu.parts)
|
||||
{
|
||||
fp_tmp = &f_emu.fp_gpp[sector / f_emu.part_size];
|
||||
fp = &f_emu.fp_gpp[sector / f_emu.part_size];
|
||||
sector = sector % f_emu.part_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
fp_tmp = &f_emu.fp_gpp[0];
|
||||
fp = &f_emu.fp_gpp[0];
|
||||
}
|
||||
break;
|
||||
case FS_EMMC_PARTITION_BOOT1:
|
||||
fp_tmp = &f_emu.fp_boot1;
|
||||
fp = &f_emu.fp_boot1;
|
||||
break;
|
||||
case FS_EMMC_PARTITION_BOOT0:
|
||||
fp_tmp = &f_emu.fp_boot0;
|
||||
fp = &f_emu.fp_boot0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (f_lseek(fp_tmp, sector << 9) != FR_OK)
|
||||
{
|
||||
; //TODO. Out of range. close stuff and fatal?
|
||||
}
|
||||
if (f_lseek(fp, sector << 9) != FR_OK)
|
||||
return 0; // Out of bounds.
|
||||
|
||||
uint64_t res = 0;
|
||||
if (!is_write)
|
||||
res = !(f_read(fp_tmp, buf, num_sectors << 9, NULL));
|
||||
res = !f_read_fast(fp, buf, num_sectors << 9);
|
||||
else
|
||||
res = !(f_write(fp_tmp, buf, num_sectors << 9, NULL));
|
||||
res = !f_write_fast(fp, buf, num_sectors << 9);
|
||||
|
||||
return res;
|
||||
}
|
||||
@ -402,12 +389,15 @@ uint64_t sdmmc_wrapper_read(void *buf, uint64_t bufSize, int mmc_id, unsigned in
|
||||
if (first_sd_read)
|
||||
{
|
||||
first_sd_read = false;
|
||||
// Because some SD cards have issues with emuMMC's driver
|
||||
// we currently swap to FS's driver after first SD read
|
||||
// TODO: Fix remaining driver issues
|
||||
custom_driver = false;
|
||||
// FS will handle sd mutex w/o custom driver from here on
|
||||
unlock_mutex(sd_mutex);
|
||||
if (emuMMC_ctx.EMMC_Type == emuMMC_SD)
|
||||
{
|
||||
// Because some SD cards have issues with emuMMC's driver
|
||||
// we currently swap to FS's driver after first SD read
|
||||
// for raw based emuMMC
|
||||
custom_driver = false;
|
||||
// FS will handle sd mutex w/o custom driver from here on
|
||||
unlock_mutex(sd_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
// Call hekates driver.
|
||||
|
@ -28,6 +28,7 @@ extern "C" {
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../emmc/nx_sd.h"
|
||||
#include "../emmc/sdmmc.h"
|
||||
#include "../soc/i2c.h"
|
||||
#include "../soc/gpio.h"
|
||||
@ -55,15 +56,17 @@ uint64_t sdmmc_wrapper_controller_close(int mmc_id);
|
||||
uint64_t sdmmc_wrapper_read(void *buf, uint64_t bufSize, int mmc_id, unsigned int sector, unsigned int num_sectors);
|
||||
uint64_t sdmmc_wrapper_write(int mmc_id, unsigned int sector, unsigned int num_sectors, void *buf, uint64_t bufSize);
|
||||
|
||||
// TODO: check if FatFS internal buffers are good (perf wise) to have a x16 alignment.
|
||||
typedef struct _file_based_ctxt
|
||||
{
|
||||
FATFS sd_fs;
|
||||
uint64_t parts;
|
||||
uint64_t part_size;
|
||||
FATFS *sd_fs;
|
||||
FIL fp_boot0;
|
||||
DWORD clmt_boot0[0x400];
|
||||
FIL fp_boot1;
|
||||
DWORD clmt_boot1[0x400];
|
||||
FIL fp_gpp[32];
|
||||
DWORD clmt_gpp[0x8000];
|
||||
} file_based_ctxt;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -44,7 +44,7 @@ typedef struct _emuMMC_ctx_t
|
||||
enum FS_VER fs_ver;
|
||||
enum emuMMC_Type EMMC_Type;
|
||||
enum emuMMC_Type SD_Type;
|
||||
|
||||
|
||||
/* Partition based */
|
||||
u64 EMMC_StoragePartitionOffset;
|
||||
u64 SD_StoragePartitionOffset;
|
||||
|
@ -1,10 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
* Copyright (c) 2018-2019 CTCaer
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
/*----------------------------------------------------------------------------/
|
||||
/ FatFs - Generic FAT Filesystem Module R0.13c (p3) /
|
||||
/ FatFs - Generic FAT Filesystem Module R0.13c (p4) /
|
||||
/-----------------------------------------------------------------------------/
|
||||
/
|
||||
/ Copyright (C) 2018, ChaN, all right reserved.
|
||||
/ Copyright (c) 2018 naehrwert
|
||||
/ Copyright (C) 2018-2019 CTCaer
|
||||
/
|
||||
/ FatFs module is an open source software. Redistribution and use of FatFs in
|
||||
/ source and binary forms, with or without modification, are permitted provided
|
||||
@ -515,7 +530,7 @@ static WCHAR LfnBuf[FF_MAX_LFN + 1]; /* LFN working buffer */
|
||||
#define FREE_NAMBUF() ff_memfree(lfn)
|
||||
#endif
|
||||
#define LEAVE_MKFS(res) { if (!work) ff_memfree(buf); return res; }
|
||||
#define MAX_MALLOC 0x8000 /* Must be >=FF_MAX_SS */
|
||||
#define MAX_MALLOC 0x4000 /* Must be >=FF_MAX_SS */
|
||||
|
||||
#else
|
||||
#error Wrong setting of FF_USE_LFN
|
||||
@ -3879,6 +3894,109 @@ FRESULT f_read (
|
||||
|
||||
|
||||
|
||||
#ifdef FF_FASTFS
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Fast Read Aligned Sized File Without a Cache */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
#if FF_USE_FASTSEEK
|
||||
FRESULT f_read_fast (
|
||||
FIL* fp, /* Pointer to the file object */
|
||||
const void* buff, /* Pointer to the data to be written */
|
||||
UINT btr /* Number of bytes to read */
|
||||
)
|
||||
{
|
||||
FRESULT res;
|
||||
FATFS *fs;
|
||||
UINT csize_bytes;
|
||||
DWORD clst;
|
||||
DWORD wbytes;
|
||||
UINT count;
|
||||
FSIZE_t work_sector = 0;
|
||||
FSIZE_t sector_base = 0;
|
||||
BYTE *wbuff = (BYTE*)buff;
|
||||
|
||||
res = validate(&fp->obj, &fs); /* Check validity of the file object */
|
||||
if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) {
|
||||
EFSPRINTF("FOV");
|
||||
LEAVE_FF(fs, res); /* Check validity */
|
||||
}
|
||||
|
||||
if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
|
||||
FSIZE_t remain = fp->obj.objsize - fp->fptr;
|
||||
if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */
|
||||
|
||||
csize_bytes = fs->csize * SS(fs);
|
||||
DWORD csect = (UINT)((fp->fptr / SS(fs)) & (fs->csize - 1)); /* Sector offset in the cluster */
|
||||
|
||||
/* If inside a cluster, read the sectors and align to cluster. */
|
||||
if (csect) {
|
||||
wbytes = MIN(btr, (fs->csize - csect) * SS(fs));
|
||||
f_read(fp, wbuff, wbytes, (void *)0);
|
||||
wbuff += wbytes;
|
||||
btr -= wbytes;
|
||||
if (!btr)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!fp->fptr) { /* On the top of the file? */
|
||||
clst = fp->obj.sclust; /* Follow from the origin */
|
||||
} else {
|
||||
if (fp->cltbl) clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
|
||||
else { EFSPRINTF("CLTBL"); ABORT(fs, FR_CLTBL_NO_INIT); }
|
||||
}
|
||||
|
||||
if (clst < 2) { EFSPRINTF("CCHK"); ABORT(fs, FR_INT_ERR); }
|
||||
else if (clst == 0xFFFFFFFF) { EFSPRINTF("DSKC"); ABORT(fs, FR_DISK_ERR); }
|
||||
|
||||
fp->clust = clst; /* Set working cluster */
|
||||
|
||||
wbytes = MIN(btr, csize_bytes);
|
||||
sector_base = clst2sect(fs, fp->clust);
|
||||
count = wbytes / SS(fs);
|
||||
fp->fptr += wbytes;
|
||||
btr -= wbytes;
|
||||
|
||||
if (!btr) { /* Final cluster/sectors read. */
|
||||
if (disk_read(fs->pdrv, wbuff, sector_base, count) != RES_OK) ABORT(fs, FR_DISK_ERR);
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (btr) {
|
||||
clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
|
||||
|
||||
if (clst < 2) { EFSPRINTF("CCHK2"); ABORT(fs, FR_INT_ERR); }
|
||||
else if (clst == 0xFFFFFFFF) { EFSPRINTF("DSKC"); ABORT(fs, FR_DISK_ERR); }
|
||||
|
||||
fp->clust = clst;
|
||||
|
||||
work_sector = clst2sect(fs, fp->clust);
|
||||
wbytes = MIN(btr, csize_bytes);
|
||||
if ((work_sector - sector_base) == count) count += wbytes / SS(fs);
|
||||
else {
|
||||
if (disk_read(fs->pdrv, wbuff, sector_base, count) != RES_OK) ABORT(fs, FR_DISK_ERR);
|
||||
wbuff += count * SS(fs);
|
||||
|
||||
sector_base = work_sector;
|
||||
count = wbytes / SS(fs);
|
||||
}
|
||||
|
||||
fp->fptr += wbytes;
|
||||
btr -= wbytes;
|
||||
|
||||
if (!btr) { /* Final cluster/sectors read. */
|
||||
if (disk_read(fs->pdrv, wbuff, sector_base, count) != RES_OK) ABORT(fs, FR_DISK_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
LEAVE_FF(fs, FR_OK);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#if !FF_FS_READONLY
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Write File */
|
||||
@ -4018,6 +4136,117 @@ FRESULT f_write (
|
||||
|
||||
|
||||
|
||||
#ifdef FF_FASTFS
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Fast Write Aligned Sized File Without a Cache */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
#if FF_USE_FASTSEEK
|
||||
FRESULT f_write_fast (
|
||||
FIL* fp, /* Pointer to the file object */
|
||||
const void* buff, /* Pointer to the data to be written */
|
||||
UINT btw /* Number of bytes to write */
|
||||
)
|
||||
{
|
||||
FRESULT res;
|
||||
FATFS *fs;
|
||||
UINT csize_bytes;
|
||||
DWORD clst;
|
||||
DWORD wbytes;
|
||||
UINT count;
|
||||
FSIZE_t work_sector = 0;
|
||||
FSIZE_t sector_base = 0;
|
||||
BYTE *wbuff = (BYTE*)buff;
|
||||
|
||||
res = validate(&fp->obj, &fs); /* Check validity of the file object */
|
||||
if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) {
|
||||
EFSPRINTF("FOV");
|
||||
LEAVE_FF(fs, res); /* Check validity */
|
||||
}
|
||||
|
||||
if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
|
||||
/* Check fptr wrap-around (file size cannot reach 4 GiB at FAT volume) */
|
||||
if ((!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) {
|
||||
btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr);
|
||||
}
|
||||
|
||||
csize_bytes = fs->csize * SS(fs);
|
||||
DWORD csect = (UINT)((fp->fptr / SS(fs)) & (fs->csize - 1)); /* Sector offset in the cluster */
|
||||
|
||||
/* If inside a cluster, write the sectors and align to cluster. */
|
||||
if (csect) {
|
||||
wbytes = MIN(btw, (fs->csize - csect) * SS(fs));
|
||||
f_write(fp, wbuff, wbytes, (void *)0);
|
||||
/* Ensure flushing of it. FatFS is not notified for next write if raw. */
|
||||
f_sync(fp);
|
||||
wbuff += wbytes;
|
||||
btw -= wbytes;
|
||||
if (!btw)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!fp->fptr) { /* On the top of the file? */
|
||||
clst = fp->obj.sclust; /* Follow from the origin */
|
||||
} else {
|
||||
if (fp->cltbl) clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
|
||||
else { EFSPRINTF("CLTBL"); ABORT(fs, FR_CLTBL_NO_INIT); }
|
||||
}
|
||||
|
||||
if (clst < 2) { EFSPRINTF("CCHK"); ABORT(fs, FR_INT_ERR); }
|
||||
else if (clst == 0xFFFFFFFF) { EFSPRINTF("DSKC"); ABORT(fs, FR_DISK_ERR); }
|
||||
|
||||
fp->clust = clst; /* Set working cluster */
|
||||
|
||||
wbytes = MIN(btw, csize_bytes);
|
||||
sector_base = clst2sect(fs, fp->clust);
|
||||
count = wbytes / SS(fs);
|
||||
fp->fptr += wbytes;
|
||||
btw -= wbytes;
|
||||
|
||||
if (!btw) { /* Final cluster/sectors write. */
|
||||
if (disk_write(fs->pdrv, wbuff, sector_base, count) != RES_OK) ABORT(fs, FR_DISK_ERR);
|
||||
fp->flag &= (BYTE)~FA_DIRTY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (btw) {
|
||||
clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
|
||||
|
||||
if (clst < 2) { EFSPRINTF("CCHK2"); ABORT(fs, FR_INT_ERR); }
|
||||
else if (clst == 0xFFFFFFFF) { EFSPRINTF("DSKC"); ABORT(fs, FR_DISK_ERR); }
|
||||
|
||||
fp->clust = clst;
|
||||
|
||||
work_sector = clst2sect(fs, fp->clust);
|
||||
wbytes = MIN(btw, csize_bytes);
|
||||
if ((work_sector - sector_base) == count) count += wbytes / SS(fs);
|
||||
else {
|
||||
if (disk_write(fs->pdrv, wbuff, sector_base, count) != RES_OK) ABORT(fs, FR_DISK_ERR);
|
||||
wbuff += count * SS(fs);
|
||||
|
||||
sector_base = work_sector;
|
||||
count = wbytes / SS(fs);
|
||||
}
|
||||
|
||||
fp->fptr += wbytes;
|
||||
btw -= wbytes;
|
||||
|
||||
if (!btw) { /* Final cluster/sectors write. */
|
||||
if (disk_write(fs->pdrv, wbuff, sector_base, count) != RES_OK) ABORT(fs, FR_DISK_ERR);
|
||||
fp->flag &= (BYTE)~FA_DIRTY;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
fp->flag |= FA_MODIFIED; /* Set file change flag */
|
||||
|
||||
LEAVE_FF(fs, FR_OK);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Synchronize the File */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
@ -4231,9 +4460,9 @@ FRESULT f_getcwd (
|
||||
TCHAR *tp = buff;
|
||||
#if FF_VOLUMES >= 2
|
||||
UINT vl;
|
||||
#endif
|
||||
#if FF_STR_VOLUME_ID
|
||||
const char *vp;
|
||||
#endif
|
||||
#endif
|
||||
FILINFO fno;
|
||||
DEF_NAMBUF
|
||||
@ -4474,6 +4703,37 @@ FRESULT f_lseek (
|
||||
|
||||
|
||||
|
||||
#ifdef FF_FASTFS
|
||||
#if FF_USE_FASTSEEK
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Seek File Read/Write Pointer */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DWORD *f_expand_cltbl (
|
||||
FIL* fp, /* Pointer to the file object */
|
||||
UINT tblsz, /* Size of table */
|
||||
DWORD *tbl, /* Table pointer */
|
||||
FSIZE_t ofs /* File pointer from top of file */
|
||||
)
|
||||
{
|
||||
if (fp->flag & FA_WRITE) f_lseek(fp, ofs); /* Expand file if write is enabled */
|
||||
fp->cltbl = (DWORD *)tbl;
|
||||
fp->cltbl[0] = tblsz;
|
||||
if (f_lseek(fp, CREATE_LINKMAP)) { /* Create cluster link table */
|
||||
fp->cltbl = (void *)0;
|
||||
EFSPRINTF("CLTBLSZ");
|
||||
return (void *)0;
|
||||
}
|
||||
f_lseek(fp, 0);
|
||||
|
||||
return fp->cltbl;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#if FF_FS_MINIMIZE <= 1
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Create a Directory Object */
|
||||
@ -4714,7 +4974,7 @@ FRESULT f_getfree (
|
||||
/* Get logical drive */
|
||||
res = find_volume(&path, &fs, 0);
|
||||
if (res == FR_OK) {
|
||||
*fatfs = fs; /* Return ptr to the fs object */
|
||||
if (fatfs) *fatfs = fs; /* Return ptr to the fs object */
|
||||
/* If free_clst is valid, return it without full FAT scan */
|
||||
if (fs->free_clst <= fs->n_fatent - 2) {
|
||||
*nclst = fs->free_clst;
|
||||
|
@ -246,7 +246,12 @@ typedef enum {
|
||||
FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
|
||||
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
|
||||
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */
|
||||
#ifdef FF_FASTFS
|
||||
FR_INVALID_PARAMETER, /* (19) Given parameter is invalid */
|
||||
FR_CLTBL_NO_INIT /* (20) The cluster table for fast seek/read/write was not created */
|
||||
#else
|
||||
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
|
||||
#endif
|
||||
} FRESULT;
|
||||
|
||||
|
||||
@ -258,6 +263,10 @@ FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a f
|
||||
FRESULT f_close (FIL* fp); /* Close an open file object */
|
||||
FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */
|
||||
FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */
|
||||
#ifdef FF_FASTFS
|
||||
FRESULT f_read_fast (FIL* fp, const void* buff, UINT btr); /* Fast read data from the file */
|
||||
FRESULT f_write_fast (FIL* fp, const void* buff, UINT btw); /* Fast write data to the file */
|
||||
#endif
|
||||
FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */
|
||||
FRESULT f_truncate (FIL* fp); /* Truncate the file */
|
||||
FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */
|
||||
@ -279,7 +288,10 @@ FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get numbe
|
||||
FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
|
||||
FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
|
||||
FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
|
||||
FRESULT f_expand (FIL* fp, FSIZE_t szf, BYTE opt); /* Allocate a contiguous block to the file */
|
||||
#ifdef FF_FASTFS
|
||||
DWORD *f_expand_cltbl (FIL* fp, UINT tblsz, DWORD *tbl, FSIZE_t ofs); /* Expand file and populate cluster table */
|
||||
#endif
|
||||
FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */
|
||||
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
|
||||
FRESULT f_mkfs (const TCHAR* path, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */
|
||||
FRESULT f_fdisk (BYTE pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */
|
||||
|
@ -15,7 +15,7 @@
|
||||
/ and optional writing functions as well. */
|
||||
|
||||
|
||||
#define FF_FS_MINIMIZE 0
|
||||
#define FF_FS_MINIMIZE 2
|
||||
/* This option defines minimization level to remove some basic API functions.
|
||||
/
|
||||
/ 0: Basic functions are fully enabled.
|
||||
@ -41,8 +41,13 @@
|
||||
#define FF_USE_MKFS 0
|
||||
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
|
||||
|
||||
#define FF_FASTFS 1
|
||||
|
||||
#ifdef FF_FASTFS
|
||||
#define FF_USE_FASTSEEK 1
|
||||
#else
|
||||
#define FF_USE_FASTSEEK 0
|
||||
#endif
|
||||
/* This option switches fast seek function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
@ -50,7 +55,7 @@
|
||||
/* This option switches f_expand function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_CHMOD 1
|
||||
#define FF_USE_CHMOD 0
|
||||
/* This option switches attribute manipulation functions, f_chmod() and f_utime().
|
||||
/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
|
||||
|
||||
@ -239,7 +244,7 @@
|
||||
#define FF_FS_NORTC 1
|
||||
#define FF_NORTC_MON 1
|
||||
#define FF_NORTC_MDAY 1
|
||||
#define FF_NORTC_YEAR 2019
|
||||
#define FF_NORTC_YEAR 2020
|
||||
/* The option FF_FS_NORTC switches timestamp function. If the system does not have
|
||||
/ any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable
|
||||
/ the timestamp function. Every object modified by FatFs will have a fixed timestamp
|
||||
|
@ -34,7 +34,6 @@
|
||||
#define MERGE2(a, b) a ## b
|
||||
#define CVTBL(tbl, cp) MERGE2(tbl, cp)
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Code Conversion Tables */
|
||||
/*------------------------------------------------------------------------*/
|
||||
@ -623,5 +622,4 @@ DWORD ff_wtoupper ( /* Returns up-converted code point */
|
||||
return uni;
|
||||
}
|
||||
|
||||
|
||||
#endif /* #if FF_USE_LFN */
|
||||
|
@ -33,19 +33,22 @@
|
||||
void __init();
|
||||
void __initheap(void);
|
||||
void setup_hooks(void);
|
||||
void setup_nintendo_paths(void);
|
||||
void __libc_init_array(void);
|
||||
void setup_nintendo_paths(void);
|
||||
void hook_function(uintptr_t source, uintptr_t target);
|
||||
|
||||
void *__stack_top;
|
||||
uintptr_t text_base;
|
||||
size_t fs_code_size;
|
||||
u8 *fs_rw_mapping = NULL;
|
||||
Handle self_proc_handle = 0;
|
||||
char inner_heap[INNER_HEAP_SIZE];
|
||||
size_t inner_heap_size = INNER_HEAP_SIZE;
|
||||
|
||||
extern char _start;
|
||||
extern char __argdata__;
|
||||
|
||||
// Nintendo Path
|
||||
// TODO
|
||||
static char nintendo_path[0x80] = "Nintendo";
|
||||
|
||||
// 1.0.0 requires special path handling because it has separate album and contents paths.
|
||||
@ -60,6 +63,7 @@ static const fs_offsets_t *fs_offsets;
|
||||
// Defined by linkerscript
|
||||
#define INJECTED_SIZE ((uintptr_t)&__argdata__ - (uintptr_t)&_start)
|
||||
#define INJECT_OFFSET(type, offset) (type)(text_base + INJECTED_SIZE + offset)
|
||||
#define FS_CODE_BASE INJECT_OFFSET(uintptr_t, 0)
|
||||
|
||||
#define GENERATE_ADD(register, register_target, value) (0x91000000 | value << 10 | register << 5 | register_target)
|
||||
#define GENERATE_ADRP(register, page_addr) (0x90000000 | ((((page_addr) >> 12) & 0x3) << 29) | ((((page_addr) >> 12) & 0x1FFFFC) << 3) | ((register) & 0x1F))
|
||||
@ -148,15 +152,117 @@ void __initheap(void)
|
||||
fake_heap_end = (char *)addr + size;
|
||||
}
|
||||
|
||||
static void _receive_process_handle_thread(void *_session_handle) {
|
||||
Result rc;
|
||||
|
||||
// Convert the argument to a handle we can use.
|
||||
Handle session_handle = (Handle)(uintptr_t)_session_handle;
|
||||
|
||||
// Receive the request from the client thread.
|
||||
memset(armGetTls(), 0, 0x10);
|
||||
s32 idx = 0;
|
||||
rc = svcReplyAndReceive(&idx, &session_handle, 1, INVALID_HANDLE, UINT64_MAX);
|
||||
if (rc != 0)
|
||||
{
|
||||
fatal_abort(Fatal_BadResult);
|
||||
}
|
||||
|
||||
// Set the process handle.
|
||||
self_proc_handle = ((u32 *)armGetTls())[3];
|
||||
|
||||
// Close the session.
|
||||
svcCloseHandle(session_handle);
|
||||
|
||||
// Terminate ourselves.
|
||||
svcExitThread();
|
||||
|
||||
// This code will never execute.
|
||||
while (true);
|
||||
}
|
||||
|
||||
static void _init_process_handle(void) {
|
||||
Result rc;
|
||||
u8 temp_thread_stack[0x1000];
|
||||
|
||||
// Create a new session to transfer our process handle to ourself
|
||||
Handle server_handle, client_handle;
|
||||
rc = svcCreateSession(&server_handle, &client_handle, 0, 0);
|
||||
if (rc != 0)
|
||||
{
|
||||
fatal_abort(Fatal_BadResult);
|
||||
}
|
||||
|
||||
// Create a new thread to receive our handle.
|
||||
Handle thread_handle;
|
||||
rc = svcCreateThread(&thread_handle, _receive_process_handle_thread, (void *)(uintptr_t)server_handle, temp_thread_stack + sizeof(temp_thread_stack), 0x20, 3);
|
||||
if (rc != 0)
|
||||
{
|
||||
fatal_abort(Fatal_BadResult);
|
||||
}
|
||||
|
||||
// Start the new thread.
|
||||
rc = svcStartThread(thread_handle);
|
||||
if (rc != 0)
|
||||
{
|
||||
fatal_abort(Fatal_BadResult);
|
||||
}
|
||||
|
||||
// Send the message.
|
||||
static const u32 SendProcessHandleMessage[4] = { 0x00000000, 0x80000000, 0x00000002, CUR_PROCESS_HANDLE };
|
||||
memcpy(armGetTls(), SendProcessHandleMessage, sizeof(SendProcessHandleMessage));
|
||||
svcSendSyncRequest(client_handle);
|
||||
|
||||
// Close the session handle.
|
||||
svcCloseHandle(client_handle);
|
||||
|
||||
// Wait for the thread to be done.
|
||||
rc = svcWaitSynchronizationSingle(thread_handle, UINT64_MAX);
|
||||
if (rc != 0)
|
||||
{
|
||||
fatal_abort(Fatal_BadResult);
|
||||
}
|
||||
|
||||
// Close the thread handle.
|
||||
svcCloseHandle(thread_handle);
|
||||
}
|
||||
|
||||
static void _map_fs_rw(void) {
|
||||
Result rc;
|
||||
|
||||
do {
|
||||
fs_rw_mapping = (u8 *)(smcGenerateRandomU64() & 0xFFFFFF000ull);
|
||||
rc = svcMapProcessMemory(fs_rw_mapping, self_proc_handle, FS_CODE_BASE, fs_code_size);
|
||||
} while (rc == 0xDC01 || rc == 0xD401);
|
||||
|
||||
if (rc != 0)
|
||||
{
|
||||
fatal_abort(Fatal_BadResult);
|
||||
}
|
||||
}
|
||||
|
||||
static void _unmap_fs_rw(void) {
|
||||
Result rc = svcUnmapProcessMemory(fs_rw_mapping, self_proc_handle, FS_CODE_BASE, fs_code_size);
|
||||
if (rc != 0)
|
||||
{
|
||||
fatal_abort(Fatal_BadResult);
|
||||
}
|
||||
|
||||
fs_rw_mapping = NULL;
|
||||
}
|
||||
|
||||
static void _write32(uintptr_t source, u32 value) {
|
||||
*((u32 *)(fs_rw_mapping + (source - FS_CODE_BASE))) = value;
|
||||
}
|
||||
|
||||
void hook_function(uintptr_t source, uintptr_t target)
|
||||
{
|
||||
u32 branch_opcode = GENERATE_BRANCH(source, target);
|
||||
smcWriteAddress32((void *)source, branch_opcode);
|
||||
_write32(source, branch_opcode);
|
||||
}
|
||||
|
||||
void write_nop(uintptr_t source)
|
||||
{
|
||||
smcWriteAddress32((void *)source, GENERATE_NOP());
|
||||
_write32(source, GENERATE_NOP());
|
||||
}
|
||||
|
||||
void write_adrp_add(int reg, uintptr_t pc, uintptr_t add_rel_offset, intptr_t destination)
|
||||
@ -167,8 +273,8 @@ void write_adrp_add(int reg, uintptr_t pc, uintptr_t add_rel_offset, intptr_t de
|
||||
uint32_t opcode_adrp = GENERATE_ADRP(reg, offset);
|
||||
uint32_t opcode_add = GENERATE_ADD(reg, reg, (destination & 0x00000FFF));
|
||||
|
||||
smcWriteAddress32((void *)pc, opcode_adrp);
|
||||
smcWriteAddress32((void *)add_opcode_location, opcode_add);
|
||||
_write32(pc, opcode_adrp);
|
||||
_write32(add_opcode_location, opcode_add);
|
||||
}
|
||||
|
||||
void setup_hooks(void)
|
||||
@ -306,14 +412,21 @@ void __init()
|
||||
|
||||
text_base = meminfo.addr;
|
||||
|
||||
// Get code size
|
||||
svcQueryMemory(&meminfo, &pageinfo, FS_CODE_BASE);
|
||||
fs_code_size = meminfo.size;
|
||||
|
||||
load_emummc_ctx();
|
||||
|
||||
fs_offsets = get_fs_offsets(emuMMC_ctx.fs_ver);
|
||||
|
||||
_init_process_handle();
|
||||
_map_fs_rw();
|
||||
setup_hooks();
|
||||
populate_function_pointers();
|
||||
write_nops();
|
||||
setup_nintendo_paths();
|
||||
_unmap_fs_rw();
|
||||
|
||||
clock_enable_i2c5();
|
||||
i2c_init();
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include "smc.h"
|
||||
#include "../utils/fatal.h"
|
||||
|
||||
void smcRebootToRcm(void)
|
||||
{
|
||||
@ -117,45 +118,6 @@ Result smcReadWriteRegister(u32 phys_addr, u32 value, u32 mask)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static Result _smcWriteAddress(void *dst_addr, u64 val, u32 size)
|
||||
{
|
||||
SecmonArgs args;
|
||||
args.X[0] = 0xF0000003; /* smcAmsWriteAddress */
|
||||
args.X[1] = (u64)dst_addr; /* DRAM address */
|
||||
args.X[2] = val; /* value */
|
||||
args.X[3] = size; /* Amount to write */
|
||||
Result rc = svcCallSecureMonitor(&args);
|
||||
if (rc == 0)
|
||||
{
|
||||
if (args.X[0] != 0)
|
||||
{
|
||||
/* SPL result n = SMC result n */
|
||||
rc = (26u | ((u32)args.X[0] << 9u));
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result smcWriteAddress8(void *dst_addr, u8 val)
|
||||
{
|
||||
return _smcWriteAddress(dst_addr, val, 1);
|
||||
}
|
||||
|
||||
Result smcWriteAddress16(void *dst_addr, u16 val)
|
||||
{
|
||||
return _smcWriteAddress(dst_addr, val, 2);
|
||||
}
|
||||
|
||||
Result smcWriteAddress32(void *dst_addr, u32 val)
|
||||
{
|
||||
return _smcWriteAddress(dst_addr, val, 4);
|
||||
}
|
||||
|
||||
Result smcWriteAddress64(void *dst_addr, u64 val)
|
||||
{
|
||||
return _smcWriteAddress(dst_addr, val, 8);
|
||||
}
|
||||
|
||||
Result smcGetEmummcConfig(exo_emummc_mmc_t mmc_id, exo_emummc_config_t *out_cfg, void *out_paths)
|
||||
{
|
||||
SecmonArgs args;
|
||||
@ -177,4 +139,38 @@ Result smcGetEmummcConfig(exo_emummc_mmc_t mmc_id, exo_emummc_config_t *out_cfg,
|
||||
}
|
||||
return rc;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Result smcGenerateRandomBytes(void *dst, u32 size)
|
||||
{
|
||||
SecmonArgs args;
|
||||
args.X[0] = 0xC3000006; /* smcGenerateRandomBytes */
|
||||
args.X[1] = size;
|
||||
Result rc = svcCallSecureMonitor(&args);
|
||||
if (rc == 0)
|
||||
{
|
||||
if (args.X[0] != 0)
|
||||
{
|
||||
/* SPL result n = SMC result n */
|
||||
rc = (26u | ((u32)args.X[0] << 9u));
|
||||
}
|
||||
if (rc == 0)
|
||||
{
|
||||
memcpy(dst, &args.X[1], size);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
u64 smcGenerateRandomU64(void)
|
||||
{
|
||||
u64 random;
|
||||
|
||||
Result rc = smcGenerateRandomBytes(&random, sizeof(random));
|
||||
if (rc != 0)
|
||||
{
|
||||
fatal_abort(Fatal_BadResult);
|
||||
}
|
||||
|
||||
return random;
|
||||
}
|
||||
|
@ -78,13 +78,11 @@ Result smcCopyFromIram(void *dst_addr, uintptr_t iram_addr, u32 size);
|
||||
|
||||
Result smcReadWriteRegister(u32 phys_addr, u32 value, u32 mask);
|
||||
|
||||
Result smcWriteAddress8(void *dst_addr, u8 val);
|
||||
Result smcWriteAddress16(void *dst_addr, u16 val);
|
||||
Result smcWriteAddress32(void *dst_addr, u32 val);
|
||||
Result smcWriteAddress64(void *dst_addr, u64 val);
|
||||
|
||||
Result smcGetEmummcConfig(exo_emummc_mmc_t mmc_id, exo_emummc_config_t *out_cfg, void *out_paths);
|
||||
|
||||
Result smcGenerateRandomBytes(void *dst, u32 size);
|
||||
u64 smcGenerateRandomU64(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -106,6 +106,104 @@ Result svcSetProcessMemoryPermission(Handle proc, u64 addr, u64 size, u32 perm);
|
||||
*/
|
||||
Result svcSetMemoryPermission(void* addr, u64 size, u32 perm);
|
||||
|
||||
/**
|
||||
* @brief Creates a thread.
|
||||
* @return Result code.
|
||||
* @note Syscall number 0x08.
|
||||
*/
|
||||
Result svcCreateThread(Handle* out, void* entry, void* arg, void* stack_top, int prio, int cpuid);
|
||||
|
||||
/**
|
||||
* @brief Starts a freshly created thread.
|
||||
* @return Result code.
|
||||
* @note Syscall number 0x09.
|
||||
*/
|
||||
Result svcStartThread(Handle handle);
|
||||
|
||||
/**
|
||||
* @brief Exits the current thread.
|
||||
* @note Syscall number 0x0A.
|
||||
*/
|
||||
void __attribute__((noreturn)) svcExitThread(void);
|
||||
|
||||
/**
|
||||
* @brief Closes a handle, decrementing the reference count of the corresponding kernel object.
|
||||
* This might result in the kernel freeing the object.
|
||||
* @param handle Handle to close.
|
||||
* @return Result code.
|
||||
* @note Syscall number 0x16.
|
||||
*/
|
||||
Result svcCloseHandle(Handle handle);
|
||||
|
||||
/**
|
||||
* @brief Waits on one or more synchronization objects, optionally with a timeout.
|
||||
* @return Result code.
|
||||
* @note Syscall number 0x18.
|
||||
* @note \p handleCount must not be greater than \ref MAX_WAIT_OBJECTS. This is a Horizon kernel limitation.
|
||||
* @note This is the raw syscall, which can be cancelled by \ref svcCancelSynchronization or other means. \ref waitHandles or \ref waitMultiHandle should normally be used instead.
|
||||
*/
|
||||
Result svcWaitSynchronization(s32* index, const Handle* handles, s32 handleCount, u64 timeout);
|
||||
|
||||
/**
|
||||
* @brief Waits on a single synchronization object, optionally with a timeout.
|
||||
* @return Result code.
|
||||
* @note Wrapper for \ref svcWaitSynchronization.
|
||||
* @note This is the raw syscall, which can be cancelled by \ref svcCancelSynchronization or other means. \ref waitSingleHandle should normally be used instead.
|
||||
*/
|
||||
static inline Result svcWaitSynchronizationSingle(Handle handle, u64 timeout) {
|
||||
s32 tmp;
|
||||
return svcWaitSynchronization(&tmp, &handle, 1, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates an IPC session.
|
||||
* @return Result code.
|
||||
* @note Syscall number 0x40.
|
||||
* @warning This is a privileged syscall. Use \ref envIsSyscallHinted to check if it is available.
|
||||
*/
|
||||
Result svcCreateSession(Handle *server_handle, Handle *client_handle, u32 unk0, u64 unk1);//unk* are normally 0?
|
||||
|
||||
/**
|
||||
* @brief Sends an IPC synchronization request to a session.
|
||||
* @return Result code.
|
||||
* @note Syscall number 0x21.
|
||||
*/
|
||||
Result svcSendSyncRequest(Handle session);
|
||||
|
||||
/**
|
||||
* @brief Performs IPC input/output.
|
||||
* @return Result code.
|
||||
* @note Syscall number 0x43.
|
||||
* @warning This is a privileged syscall. Use \ref envIsSyscallHinted to check if it is available.
|
||||
*/
|
||||
Result svcReplyAndReceive(s32* index, const Handle* handles, s32 handleCount, Handle replyTarget, u64 timeout);
|
||||
|
||||
/**
|
||||
* @brief Maps the src address from the supplied process handle into the current process.
|
||||
* @param[in] dst Address to which map the memory in the current process.
|
||||
* @param[in] proc Process handle.
|
||||
* @param[in] src Source mapping address.
|
||||
* @param[in] size Size of the memory.
|
||||
* @return Result code.
|
||||
* @remark This allows mapping code and rodata with RW- permission.
|
||||
* @note Syscall number 0x74.
|
||||
* @warning This is a privileged syscall. Use \ref envIsSyscallHinted to check if it is available.
|
||||
*/
|
||||
Result svcMapProcessMemory(void* dst, Handle proc, u64 src, u64 size);
|
||||
|
||||
/**
|
||||
* @brief Undoes the effects of \ref svcMapProcessMemory.
|
||||
* @param[in] dst Destination mapping address
|
||||
* @param[in] proc Process handle.
|
||||
* @param[in] src Address of the memory in the process.
|
||||
* @param[in] size Size of the memory.
|
||||
* @return Result code.
|
||||
* @remark This allows mapping code and rodata with RW- permission.
|
||||
* @note Syscall number 0x75.
|
||||
* @warning This is a privileged syscall. Use \ref envIsSyscallHinted to check if it is available.
|
||||
*/
|
||||
Result svcUnmapProcessMemory(void* dst, Handle proc, u64 src, u64 size);
|
||||
|
||||
/**
|
||||
* @brief Calls a secure monitor function (TrustZone, EL3).
|
||||
* @param regs Arguments to pass to the secure monitor.
|
||||
|
@ -56,6 +56,69 @@ SVC_BEGIN svcSetProcessMemoryPermission
|
||||
RET
|
||||
SVC_END
|
||||
|
||||
SVC_BEGIN svcCreateThread
|
||||
STR X0, [SP, #-16]!
|
||||
SVC 0x8
|
||||
LDR X2, [SP], #16
|
||||
STR W1, [X2]
|
||||
RET
|
||||
SVC_END
|
||||
|
||||
SVC_BEGIN svcStartThread
|
||||
SVC 0x9
|
||||
RET
|
||||
SVC_END
|
||||
|
||||
SVC_BEGIN svcExitThread
|
||||
SVC 0xA
|
||||
RET
|
||||
SVC_END
|
||||
|
||||
SVC_BEGIN svcCloseHandle
|
||||
SVC 0x16
|
||||
RET
|
||||
SVC_END
|
||||
|
||||
SVC_BEGIN svcWaitSynchronization
|
||||
STR X0, [SP, #-16]!
|
||||
SVC 0x18
|
||||
LDR X2, [SP], #16
|
||||
STR W1, [X2]
|
||||
RET
|
||||
SVC_END
|
||||
|
||||
SVC_BEGIN svcCreateSession
|
||||
STP X0, X1, [SP, #-16]!
|
||||
SVC 0x40
|
||||
LDP X3, X4, [SP], #16
|
||||
STR W1, [X3]
|
||||
STR W2, [X4]
|
||||
RET
|
||||
SVC_END
|
||||
|
||||
SVC_BEGIN svcSendSyncRequest
|
||||
SVC 0x21
|
||||
RET
|
||||
SVC_END
|
||||
|
||||
SVC_BEGIN svcReplyAndReceive
|
||||
STR X0, [SP, #-16]!
|
||||
SVC 0x43
|
||||
LDR X2, [SP], #16
|
||||
STR W1, [X2]
|
||||
RET
|
||||
SVC_END
|
||||
|
||||
SVC_BEGIN svcMapProcessMemory
|
||||
SVC 0x74
|
||||
RET
|
||||
SVC_END
|
||||
|
||||
SVC_BEGIN svcUnmapProcessMemory
|
||||
SVC 0x75
|
||||
RET
|
||||
SVC_END
|
||||
|
||||
SVC_BEGIN svcCallSecureMonitor
|
||||
STR X0, [SP, #-16]!
|
||||
MOV X8, X0
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Defining registers address and its bit definitions of MAX77620 and MAX20024
|
||||
*
|
||||
* Copyright (C) 2016 NVIDIA CORPORATION. All rights reserved.
|
||||
* Copyright (c) 2016 NVIDIA CORPORATION. All rights reserved.
|
||||
* Copyright (c) 2019 CTCaer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@ -19,9 +19,19 @@
|
||||
#define MAX77620_CNFGGLBL1_LBDAC_EN (1 << 7)
|
||||
#define MAX77620_CNFGGLBL1_MPPLD (1 << 6)
|
||||
#define MAX77620_CNFGGLBL1_LBHYST ((1 << 5) | (1 << 4))
|
||||
#define MAX77620_CNFGGLBL1_LBHYST_N (1 << 4)
|
||||
#define MAX77620_CNFGGLBL1_LBDAC 0x0E
|
||||
#define MAX77620_CNFGGLBL1_LBDAC_N (1 << 1)
|
||||
#define MAX77620_CNFGGLBL1_LBHYST_100 (0 << 4)
|
||||
#define MAX77620_CNFGGLBL1_LBHYST_200 (1 << 4)
|
||||
#define MAX77620_CNFGGLBL1_LBHYST_300 (2 << 4)
|
||||
#define MAX77620_CNFGGLBL1_LBHYST_400 (3 << 4)
|
||||
#define MAX77620_CNFGGLBL1_LBDAC_MASK 0x0E
|
||||
#define MAX77620_CNFGGLBL1_LBDAC_2700 (0 << 1)
|
||||
#define MAX77620_CNFGGLBL1_LBDAC_2800 (1 << 1)
|
||||
#define MAX77620_CNFGGLBL1_LBDAC_2900 (2 << 1)
|
||||
#define MAX77620_CNFGGLBL1_LBDAC_3000 (3 << 1)
|
||||
#define MAX77620_CNFGGLBL1_LBDAC_3100 (4 << 1)
|
||||
#define MAX77620_CNFGGLBL1_LBDAC_3200 (5 << 1)
|
||||
#define MAX77620_CNFGGLBL1_LBDAC_3300 (6 << 1)
|
||||
#define MAX77620_CNFGGLBL1_LBDAC_3400 (7 << 1)
|
||||
#define MAX77620_CNFGGLBL1_LBRSTEN (1 << 0)
|
||||
|
||||
#define MAX77620_REG_CNFGGLBL2 0x01
|
||||
@ -130,7 +140,7 @@
|
||||
#define MAX77620_POWER_MODE_DISABLE 0
|
||||
#define MAX20024_LDO_CFG2_MPOK_MASK (1 << 2)
|
||||
#define MAX77620_LDO_CFG2_ADE_MASK (1 << 1)
|
||||
#define MAX77620_LDO_CFG2_ADE_DISABLE 0
|
||||
#define MAX77620_LDO_CFG2_ADE_DISABLE (0 << 1)
|
||||
#define MAX77620_LDO_CFG2_ADE_ENABLE (1 << 1)
|
||||
#define MAX77620_LDO_CFG2_SS_MASK (1 << 0)
|
||||
#define MAX77620_LDO_CFG2_SS_FAST (1 << 0)
|
||||
@ -153,6 +163,24 @@
|
||||
#define MAX77620_REG_PUE_GPIO 0x3E
|
||||
#define MAX77620_REG_PDE_GPIO 0x3F
|
||||
#define MAX77620_REG_AME_GPIO 0x40
|
||||
#define MAX77620_CNFG_GPIO_DRV_MASK (1 << 0)
|
||||
#define MAX77620_CNFG_GPIO_DRV_PUSHPULL (1 << 0)
|
||||
#define MAX77620_CNFG_GPIO_DRV_OPENDRAIN (0 << 0)
|
||||
#define MAX77620_CNFG_GPIO_DIR_MASK (1 << 1)
|
||||
#define MAX77620_CNFG_GPIO_DIR_INPUT (1 << 1)
|
||||
#define MAX77620_CNFG_GPIO_DIR_OUTPUT (0 << 1)
|
||||
#define MAX77620_CNFG_GPIO_INPUT_VAL_MASK (1 << 2)
|
||||
#define MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK (1 << 3)
|
||||
#define MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH (1 << 3)
|
||||
#define MAX77620_CNFG_GPIO_OUTPUT_VAL_LOW (0 << 3)
|
||||
#define MAX77620_CNFG_GPIO_INT_MASK (0x3 << 4)
|
||||
#define MAX77620_CNFG_GPIO_INT_FALLING (1 << 4)
|
||||
#define MAX77620_CNFG_GPIO_INT_RISING (1 << 5)
|
||||
#define MAX77620_CNFG_GPIO_DBNC_MASK (0x3 << 6)
|
||||
#define MAX77620_CNFG_GPIO_DBNC_None (0x0 << 6)
|
||||
#define MAX77620_CNFG_GPIO_DBNC_8ms (0x1 << 6)
|
||||
#define MAX77620_CNFG_GPIO_DBNC_16ms (0x2 << 6)
|
||||
#define MAX77620_CNFG_GPIO_DBNC_32ms (0x3 << 6)
|
||||
|
||||
#define MAX77620_REG_ONOFFCNFG1 0x41
|
||||
#define MAX77620_ONOFFCNFG1_SFT_RST (1 << 7)
|
||||
@ -259,25 +287,6 @@
|
||||
#define MAX77620_SD_CFG1_FSRADE_SD_DISABLE 0
|
||||
#define MAX77620_SD_CFG1_FSRADE_SD_ENABLE (1 << 0)
|
||||
|
||||
#define MAX77620_CNFG_GPIO_DRV_MASK (1 << 0)
|
||||
#define MAX77620_CNFG_GPIO_DRV_PUSHPULL (1 << 0)
|
||||
#define MAX77620_CNFG_GPIO_DRV_OPENDRAIN 0
|
||||
#define MAX77620_CNFG_GPIO_DIR_MASK (1 << 1)
|
||||
#define MAX77620_CNFG_GPIO_DIR_INPUT (1 << 1)
|
||||
#define MAX77620_CNFG_GPIO_DIR_OUTPUT 0
|
||||
#define MAX77620_CNFG_GPIO_INPUT_VAL_MASK (1 << 2)
|
||||
#define MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK (1 << 3)
|
||||
#define MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH (1 << 3)
|
||||
#define MAX77620_CNFG_GPIO_OUTPUT_VAL_LOW 0
|
||||
#define MAX77620_CNFG_GPIO_INT_MASK (0x3 << 4)
|
||||
#define MAX77620_CNFG_GPIO_INT_FALLING (1 << 4)
|
||||
#define MAX77620_CNFG_GPIO_INT_RISING (1 << 5)
|
||||
#define MAX77620_CNFG_GPIO_DBNC_MASK (0x3 << 6)
|
||||
#define MAX77620_CNFG_GPIO_DBNC_None (0x0 << 6)
|
||||
#define MAX77620_CNFG_GPIO_DBNC_8ms (0x1 << 6)
|
||||
#define MAX77620_CNFG_GPIO_DBNC_16ms (0x2 << 6)
|
||||
#define MAX77620_CNFG_GPIO_DBNC_32ms (0x3 << 6)
|
||||
|
||||
#define MAX77620_IRQ_LVL2_GPIO_EDGE0 (1 << 0)
|
||||
#define MAX77620_IRQ_LVL2_GPIO_EDGE1 (1 << 1)
|
||||
#define MAX77620_IRQ_LVL2_GPIO_EDGE2 (1 << 2)
|
||||
|
@ -157,9 +157,3 @@ void max77620_config_default()
|
||||
}
|
||||
i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_SD_CFG2, 4);
|
||||
}
|
||||
|
||||
void max77620_low_battery_monitor_config()
|
||||
{
|
||||
i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_CNFGGLBL1,
|
||||
MAX77620_CNFGGLBL1_LBDAC_EN | MAX77620_CNFGGLBL1_LBHYST_N | MAX77620_CNFGGLBL1_LBDAC_N);
|
||||
}
|
||||
|
@ -24,16 +24,16 @@
|
||||
* Switch Power domains (max77620):
|
||||
* Name | Usage | uV step | uV min | uV default | uV max | Init
|
||||
*-------+---------------+---------+--------+------------+---------+------------------
|
||||
* sd0 | core | 12500 | 600000 | 625000 | 1400000 | 1.125V (pkg1.1)
|
||||
* sd0 | SoC | 12500 | 600000 | 625000 | 1400000 | 1.125V (pkg1.1)
|
||||
* sd1 | SDRAM | 12500 | 600000 | 1125000 | 1125000 | 1.1V (pkg1.1)
|
||||
* sd2 | ldo{0-1, 7-8} | 12500 | 600000 | 1325000 | 1350000 | 1.325V (pcv)
|
||||
* sd3 | 1.8V general | 12500 | 600000 | 1800000 | 1800000 |
|
||||
* ldo0 | Display Panel | 25000 | 800000 | 1200000 | 1200000 | 1.2V (pkg1.1)
|
||||
* ldo1 | XUSB, PCIE | 25000 | 800000 | 1050000 | 1050000 | 1.05V (pcv)
|
||||
* ldo2 | SDMMC1 | 50000 | 800000 | 1800000 | 3300000 |
|
||||
* ldo3 | GC ASIC | 50000 | 800000 | 3100000 | 3100000 | 3.1V (pcv)
|
||||
* ldo3 | GC ASIC | 50000 | 800000 | 3100000 | 3100000 | 3.1V (pcv)
|
||||
* ldo4 | RTC | 12500 | 800000 | 850000 | 850000 |
|
||||
* ldo5 | GC ASIC | 50000 | 800000 | 1800000 | 1800000 | 1.8V (pcv)
|
||||
* ldo5 | GC ASIC | 50000 | 800000 | 1800000 | 1800000 | 1.8V (pcv)
|
||||
* ldo6 | Touch, ALS | 50000 | 800000 | 2900000 | 2900000 | 2.9V
|
||||
* ldo7 | XUSB | 50000 | 800000 | 1050000 | 1050000 |
|
||||
* ldo8 | XUSB, DC | 50000 | 800000 | 1050000 | 1050000 |
|
||||
@ -71,6 +71,8 @@
|
||||
/* MAX77621_VOUT */
|
||||
#define MAX77621_VOUT_ENABLE (1 << 7)
|
||||
#define MAX77621_VOUT_MASK 0x7F
|
||||
#define MAX77621_VOUT_0_95V 0x37
|
||||
#define MAX77621_VOUT_1_09V 0x4F
|
||||
|
||||
/* MAX77621_VOUT_DVC_DVS */
|
||||
#define MAX77621_DVS_VOUT_MASK 0x7F
|
||||
@ -111,6 +113,5 @@ int max77620_regulator_set_voltage(u32 id, u32 mv);
|
||||
int max77620_regulator_enable(u32 id, int enable);
|
||||
int max77620_regulator_set_volt_and_flags(u32 id, u32 mv, u8 flags);
|
||||
void max77620_config_default();
|
||||
void max77620_low_battery_monitor_config();
|
||||
|
||||
#endif
|
||||
|
@ -23,6 +23,10 @@ static const sclock_t _clock_i2c5 = {
|
||||
CLK_RST_CONTROLLER_RST_DEVICES_H, CLK_RST_CONTROLLER_CLK_OUT_ENB_H, CLK_RST_CONTROLLER_CLK_SOURCE_I2C5, 0xF, 0, 4 //81.6MHz -> 400KHz
|
||||
};
|
||||
|
||||
static sclock_t _clock_sdmmc_legacy_tm = {
|
||||
CLK_RST_CONTROLLER_RST_DEVICES_Y, CLK_RST_CONTROLLER_CLK_OUT_ENB_Y, CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC_LEGACY_TM, 1, 4, 66
|
||||
};
|
||||
|
||||
void clock_enable(const sclock_t *clk)
|
||||
{
|
||||
// Put clock into reset.
|
||||
@ -34,6 +38,8 @@ void clock_enable(const sclock_t *clk)
|
||||
CLOCK(clk->source) = clk->clk_div | (clk->clk_src << 29);
|
||||
// Enable.
|
||||
CLOCK(clk->enable) = (CLOCK(clk->enable) & ~(1 << clk->index)) | (1 << clk->index);
|
||||
usleep(2);
|
||||
|
||||
// Take clock off reset.
|
||||
CLOCK(clk->reset) &= ~(1 << clk->index);
|
||||
}
|
||||
@ -56,6 +62,33 @@ void clock_disable_i2c5()
|
||||
clock_disable(&_clock_i2c5);
|
||||
}
|
||||
|
||||
static void _clock_enable_pllc4()
|
||||
{
|
||||
if ((CLOCK(CLK_RST_CONTROLLER_PLLC4_BASE) & (PLLCX_BASE_ENABLE | PLLCX_BASE_LOCK | 0xFFFFFF))
|
||||
== (PLLCX_BASE_ENABLE | PLLCX_BASE_LOCK | (104 << 8) | 4))
|
||||
return;
|
||||
|
||||
// Enable Phase and Frequency lock detection.
|
||||
//CLOCK(CLK_RST_CONTROLLER_PLLC4_MISC) = PLLC4_MISC_EN_LCKDET;
|
||||
|
||||
// Disable PLL and IDDQ in case they are on.
|
||||
CLOCK(CLK_RST_CONTROLLER_PLLC4_BASE) &= ~PLLCX_BASE_ENABLE;
|
||||
CLOCK(CLK_RST_CONTROLLER_PLLC4_BASE) &= ~PLLC4_BASE_IDDQ;
|
||||
(void)CLOCK(CLK_RST_CONTROLLER_PLLC4_BASE);
|
||||
usleep(10);
|
||||
|
||||
// Set PLLC4 dividers.
|
||||
CLOCK(CLK_RST_CONTROLLER_PLLC4_BASE) = (104 << 8) | 4; // DIVM: 4, DIVP: 1.
|
||||
|
||||
// Enable PLLC4 and wait for Phase and Frequency lock.
|
||||
CLOCK(CLK_RST_CONTROLLER_PLLC4_BASE) |= PLLCX_BASE_ENABLE;
|
||||
(void)CLOCK(CLK_RST_CONTROLLER_PLLC4_BASE);
|
||||
while (!(CLOCK(CLK_RST_CONTROLLER_PLLC4_BASE) & PLLCX_BASE_LOCK))
|
||||
;
|
||||
|
||||
msleep(1); // Wait a bit for PLL to stabilize.
|
||||
}
|
||||
|
||||
#define L_SWR_SDMMC1_RST (1 << 14)
|
||||
#define L_SWR_SDMMC2_RST (1 << 9)
|
||||
#define L_SWR_SDMMC4_RST (1 << 15)
|
||||
@ -194,57 +227,103 @@ static void _clock_sdmmc_clear_enable(u32 id)
|
||||
}
|
||||
}
|
||||
|
||||
static u32 _clock_sdmmc_table[8] = { 0 };
|
||||
static void _clock_sdmmc_config_legacy_tm()
|
||||
{
|
||||
sclock_t *clk = &_clock_sdmmc_legacy_tm;
|
||||
if (!(CLOCK(clk->enable) & (1 << clk->index)))
|
||||
clock_enable(clk);
|
||||
}
|
||||
|
||||
#define PLLP_OUT0 0x0
|
||||
typedef struct _clock_sdmmc_t
|
||||
{
|
||||
u32 clock;
|
||||
u32 real_clock;
|
||||
} clock_sdmmc_t;
|
||||
|
||||
static int _clock_sdmmc_config_clock_source_inner(u32 *pout, u32 id, u32 val)
|
||||
static clock_sdmmc_t _clock_sdmmc_table[4] = { 0 };
|
||||
|
||||
#define SDMMC_CLOCK_SRC_PLLP_OUT0 0x0
|
||||
#define SDMMC_CLOCK_SRC_PLLC4_OUT2 0x3
|
||||
#define SDMMC4_CLOCK_SRC_PLLC4_OUT2_LJ 0x1
|
||||
|
||||
static int _clock_sdmmc_config_clock_host(u32 *pclock, u32 id, u32 val)
|
||||
{
|
||||
u32 divisor = 0;
|
||||
u32 source = PLLP_OUT0;
|
||||
u32 source = SDMMC_CLOCK_SRC_PLLP_OUT0;
|
||||
|
||||
if (id > SDMMC_4)
|
||||
return 0;
|
||||
|
||||
// Get IO clock divisor.
|
||||
switch (val)
|
||||
{
|
||||
case 25000:
|
||||
*pout = 24728;
|
||||
divisor = 31;
|
||||
*pclock = 24728;
|
||||
divisor = 31; // 16.5 div.
|
||||
break;
|
||||
case 26000:
|
||||
*pout = 25500;
|
||||
divisor = 30;
|
||||
*pclock = 25500;
|
||||
divisor = 30; // 16 div.
|
||||
break;
|
||||
case 40800:
|
||||
*pout = 40800;
|
||||
divisor = 18;
|
||||
*pclock = 40800;
|
||||
divisor = 18; // 10 div.
|
||||
break;
|
||||
case 50000:
|
||||
*pout = 48000;
|
||||
divisor = 15;
|
||||
*pclock = 48000;
|
||||
divisor = 15; // 8.5 div.
|
||||
break;
|
||||
case 52000:
|
||||
*pout = 51000;
|
||||
divisor = 14;
|
||||
*pclock = 51000;
|
||||
divisor = 14; // 8 div.
|
||||
break;
|
||||
case 100000:
|
||||
*pout = 90667;
|
||||
divisor = 7;
|
||||
source = SDMMC_CLOCK_SRC_PLLC4_OUT2;
|
||||
*pclock = 99840;
|
||||
divisor = 2; // 2 div.
|
||||
break;
|
||||
case 164000:
|
||||
*pclock = 163200;
|
||||
divisor = 3; // 2.5 div.
|
||||
break;
|
||||
case 200000:
|
||||
*pout = 163200;
|
||||
divisor = 3;
|
||||
break;
|
||||
case 208000:
|
||||
*pout = 204000;
|
||||
divisor = 2;
|
||||
switch (id)
|
||||
{
|
||||
case SDMMC_1:
|
||||
source = SDMMC_CLOCK_SRC_PLLC4_OUT2;
|
||||
break;
|
||||
case SDMMC_2:
|
||||
source = SDMMC4_CLOCK_SRC_PLLC4_OUT2_LJ;
|
||||
break;
|
||||
case SDMMC_3:
|
||||
source = SDMMC_CLOCK_SRC_PLLC4_OUT2;
|
||||
break;
|
||||
case SDMMC_4:
|
||||
source = SDMMC4_CLOCK_SRC_PLLC4_OUT2_LJ;
|
||||
break;
|
||||
}
|
||||
*pclock = 199680;
|
||||
divisor = 0; // 1 div.
|
||||
break;
|
||||
default:
|
||||
*pout = 24728;
|
||||
divisor = 31;
|
||||
*pclock = 24728;
|
||||
divisor = 31; // 16.5 div.
|
||||
}
|
||||
|
||||
_clock_sdmmc_table[2 * id] = val;
|
||||
_clock_sdmmc_table[2 * id + 1] = *pout;
|
||||
_clock_sdmmc_table[id].clock = val;
|
||||
_clock_sdmmc_table[id].real_clock = *pclock;
|
||||
|
||||
// PLLC4 and LEGACY_TM clocks are already initialized,
|
||||
// because we init at the first eMMC read.
|
||||
// // Enable PLLC4 if in use by any SDMMC.
|
||||
// if (source)
|
||||
// _clock_enable_pllc4();
|
||||
|
||||
// // Set SDMMC legacy timeout clock.
|
||||
// _clock_sdmmc_config_legacy_tm();
|
||||
|
||||
|
||||
// Set SDMMC clock.
|
||||
switch (id)
|
||||
{
|
||||
case SDMMC_1:
|
||||
@ -264,69 +343,75 @@ static int _clock_sdmmc_config_clock_source_inner(u32 *pout, u32 id, u32 val)
|
||||
return 1;
|
||||
}
|
||||
|
||||
void clock_sdmmc_config_clock_source(u32 *pout, u32 id, u32 val)
|
||||
void clock_sdmmc_config_clock_source(u32 *pclock, u32 id, u32 val)
|
||||
{
|
||||
if (_clock_sdmmc_table[2 * id] == val)
|
||||
if (_clock_sdmmc_table[id].clock == val)
|
||||
{
|
||||
*pout = _clock_sdmmc_table[2 * id + 1];
|
||||
*pclock = _clock_sdmmc_table[id].real_clock;
|
||||
}
|
||||
else
|
||||
{
|
||||
int is_enabled = _clock_sdmmc_is_enabled(id);
|
||||
if (is_enabled)
|
||||
_clock_sdmmc_clear_enable(id);
|
||||
_clock_sdmmc_config_clock_source_inner(pout, id, val);
|
||||
_clock_sdmmc_config_clock_host(pclock, id, val);
|
||||
if (is_enabled)
|
||||
_clock_sdmmc_set_enable(id);
|
||||
_clock_sdmmc_is_reset(id);
|
||||
}
|
||||
}
|
||||
|
||||
void clock_sdmmc_get_params(u32 *pout, u16 *pdivisor, u32 type)
|
||||
void clock_sdmmc_get_card_clock_div(u32 *pclock, u16 *pdivisor, u32 type)
|
||||
{
|
||||
// Get Card clock divisor.
|
||||
switch (type)
|
||||
{
|
||||
case 0:
|
||||
*pout = 26000;
|
||||
case SDHCI_TIMING_MMC_ID: // Actual IO Freq: 380.59 KHz.
|
||||
*pclock = 26000;
|
||||
*pdivisor = 66;
|
||||
break;
|
||||
case 1:
|
||||
*pout = 26000;
|
||||
case SDHCI_TIMING_MMC_LS26:
|
||||
*pclock = 26000;
|
||||
*pdivisor = 1;
|
||||
break;
|
||||
case 2:
|
||||
*pout = 52000;
|
||||
case SDHCI_TIMING_MMC_HS52:
|
||||
*pclock = 52000;
|
||||
*pdivisor = 1;
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
case 11:
|
||||
*pout = 200000;
|
||||
case SDHCI_TIMING_MMC_HS200:
|
||||
case SDHCI_TIMING_MMC_HS400:
|
||||
case SDHCI_TIMING_UHS_SDR104:
|
||||
*pclock = 200000;
|
||||
*pdivisor = 1;
|
||||
break;
|
||||
case 5:
|
||||
*pout = 25000;
|
||||
case SDHCI_TIMING_SD_ID: // Actual IO Freq: 380.43 KHz.
|
||||
*pclock = 25000;
|
||||
*pdivisor = 64;
|
||||
break;
|
||||
case 6:
|
||||
case 8:
|
||||
*pout = 25000;
|
||||
case SDHCI_TIMING_SD_DS12:
|
||||
case SDHCI_TIMING_UHS_SDR12:
|
||||
*pclock = 25000;
|
||||
*pdivisor = 1;
|
||||
break;
|
||||
case 7:
|
||||
*pout = 50000;
|
||||
case SDHCI_TIMING_SD_HS25:
|
||||
case SDHCI_TIMING_UHS_SDR25:
|
||||
*pclock = 50000;
|
||||
*pdivisor = 1;
|
||||
break;
|
||||
case 10:
|
||||
*pout = 100000;
|
||||
case SDHCI_TIMING_UHS_SDR50:
|
||||
*pclock = 100000;
|
||||
*pdivisor = 1;
|
||||
break;
|
||||
case 13:
|
||||
*pout = 40800;
|
||||
case SDHCI_TIMING_UHS_SDR82:
|
||||
*pclock = 164000;
|
||||
*pdivisor = 1;
|
||||
break;
|
||||
case 14:
|
||||
*pout = 200000;
|
||||
case SDHCI_TIMING_UHS_DDR50:
|
||||
*pclock = 40800;
|
||||
*pdivisor = 1;
|
||||
break;
|
||||
case SDHCI_TIMING_MMC_DDR52: // Actual IO Freq: 49.92 MHz.
|
||||
*pclock = 200000;
|
||||
*pdivisor = 2;
|
||||
break;
|
||||
}
|
||||
@ -339,15 +424,15 @@ int clock_sdmmc_is_not_reset_and_enabled(u32 id)
|
||||
|
||||
void clock_sdmmc_enable(u32 id, u32 val)
|
||||
{
|
||||
u32 div = 0;
|
||||
u32 clock = 0;
|
||||
|
||||
if (_clock_sdmmc_is_enabled(id))
|
||||
_clock_sdmmc_clear_enable(id);
|
||||
_clock_sdmmc_set_reset(id);
|
||||
_clock_sdmmc_config_clock_source_inner(&div, id, val);
|
||||
_clock_sdmmc_config_clock_host(&clock, id, val);
|
||||
_clock_sdmmc_set_enable(id);
|
||||
_clock_sdmmc_is_reset(id);
|
||||
usleep((100000 + div - 1) / div);
|
||||
usleep((100000 + clock - 1) / clock);
|
||||
_clock_sdmmc_clear_reset(id);
|
||||
_clock_sdmmc_is_reset(id);
|
||||
}
|
||||
|
@ -35,12 +35,16 @@
|
||||
#define CLK_RST_CONTROLLER_MISC_CLK_ENB 0x48
|
||||
#define CLK_RST_CONTROLLER_OSC_CTRL 0x50
|
||||
#define CLK_RST_CONTROLLER_PLLC_BASE 0x80
|
||||
#define CLK_RST_CONTROLLER_PLLC_OUT 0x84
|
||||
#define CLK_RST_CONTROLLER_PLLC_MISC 0x88
|
||||
#define CLK_RST_CONTROLLER_PLLC_MISC_1 0x8C
|
||||
#define CLK_RST_CONTROLLER_PLLM_BASE 0x90
|
||||
#define CLK_RST_CONTROLLER_PLLM_MISC1 0x98
|
||||
#define CLK_RST_CONTROLLER_PLLM_MISC2 0x9C
|
||||
#define CLK_RST_CONTROLLER_PLLP_BASE 0xA0
|
||||
#define CLK_RST_CONTROLLER_PLLD_BASE 0xD0
|
||||
#define CLK_RST_CONTROLLER_PLLD_MISC1 0xD8
|
||||
#define CLK_RST_CONTROLLER_PLLD_MISC 0xDC
|
||||
#define CLK_RST_CONTROLLER_PLLX_BASE 0xE0
|
||||
#define CLK_RST_CONTROLLER_PLLX_MISC 0xE4
|
||||
#define CLK_RST_CONTROLLER_PLLE_BASE 0xE8
|
||||
@ -50,6 +54,7 @@
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_PWM 0x110
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C1 0x124
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C5 0x128
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_DISP1 0x138
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_VI 0x148
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC1 0x150
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC2 0x154
|
||||
@ -57,11 +62,13 @@
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTA 0x178
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTB 0x17C
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_HOST1X 0x180
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C2 0x198
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_EMC 0x19C
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTC 0x1A0
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C3 0x1B8
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC3 0x1BC
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTD 0x1C0
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_CSITE 0x1D4
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_EMC 0x19C
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_TSEC 0x1F4
|
||||
#define CLK_RST_CONTROLLER_CLK_OUT_ENB_X 0x280
|
||||
#define CLK_RST_CONTROLLER_CLK_ENB_X_SET 0x284
|
||||
@ -95,9 +102,13 @@
|
||||
#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRC 0x3A0
|
||||
#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD 0x3A4
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_MSELECT 0x3B4
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C4 0x3C4
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_SYS 0x400
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_SOR1 0x410
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_SE 0x42C
|
||||
#define CLK_RST_CONTROLLER_RST_DEV_V_CLR 0x434
|
||||
#define CLK_RST_CONTROLLER_CLK_ENB_V_SET 0x440
|
||||
#define CLK_RST_CONTROLLER_CLK_ENB_V_CLR 0x444
|
||||
#define CLK_RST_CONTROLLER_CLK_ENB_W_SET 0x448
|
||||
#define CLK_RST_CONTROLLER_CLK_ENB_W_CLR 0x44C
|
||||
#define CLK_RST_CONTROLLER_RST_CPUG_CMPLX_SET 0x450
|
||||
@ -108,16 +119,32 @@
|
||||
#define CLK_RST_CONTROLLER_PLLX_MISC_3 0x518
|
||||
#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRE 0x554
|
||||
#define CLK_RST_CONTROLLER_SPARE_REG0 0x55C
|
||||
#define CLK_RST_CONTROLLER_PLLC4_BASE 0x5A4
|
||||
#define CLK_RST_CONTROLLER_PLLC4_MISC 0x5A8
|
||||
#define CLK_RST_CONTROLLER_PLLC_MISC_2 0x5D0
|
||||
#define CLK_RST_CONTROLLER_PLLC4_OUT 0x5E4
|
||||
#define CLK_RST_CONTROLLER_PLLMB_BASE 0x5E8
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_DSIA_LP 0x620
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C6 0x65C
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_EMC_DLL 0x664
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_UART_FST_MIP_CAL 0x66C
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_UART_FST_MIPI_CAL 0x66C
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC_LEGACY_TM 0x694
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_NVENC 0x6A0
|
||||
#define CLK_RST_CONTROLLER_SE_SUPER_CLK_DIVIDER 0x704
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTAPE 0x710
|
||||
|
||||
#define CLK_NO_SOURCE 0x0
|
||||
|
||||
/*! PLL control and status bits */
|
||||
#define PLLCX_BASE_ENABLE (1 << 30)
|
||||
#define PLLCX_BASE_REF_DIS (1 << 29)
|
||||
#define PLLCX_BASE_LOCK (1 << 27)
|
||||
|
||||
#define PLLC4_MISC_EN_LCKDET (1 << 30)
|
||||
#define PLLC4_BASE_IDDQ (1 << 18)
|
||||
#define PLLC4_OUT3_CLKEN (1 << 1)
|
||||
#define PLLC4_OUT3_RSTN_CLR (1 << 0)
|
||||
|
||||
/*! Generic clock descriptor. */
|
||||
typedef struct _sclock_t
|
||||
{
|
||||
@ -136,8 +163,8 @@ void clock_disable(const sclock_t *clk);
|
||||
/*! Clock control for specific hardware portions. */
|
||||
void clock_enable_i2c5();
|
||||
void clock_disable_i2c5();
|
||||
void clock_sdmmc_config_clock_source(u32 *pout, u32 id, u32 val);
|
||||
void clock_sdmmc_get_params(u32 *pout, u16 *pdivisor, u32 type);
|
||||
void clock_sdmmc_config_clock_source(u32 *pclock, u32 id, u32 val);
|
||||
void clock_sdmmc_get_card_clock_div(u32 *pclock, u16 *pdivisor, u32 type);
|
||||
int clock_sdmmc_is_not_reset_and_enabled(u32 id);
|
||||
void clock_sdmmc_enable(u32 id, u32 val);
|
||||
void clock_sdmmc_disable(u32 id);
|
||||
|
@ -49,12 +49,17 @@ static int _i2c_send_pkt(u32 idx, u32 x, u8 *buf, u32 size)
|
||||
vu32 *base = (vu32 *)QueryIoMapping(i2c_addrs[I2C_5], 0x1000);
|
||||
base[I2C_CMD_ADDR0] = x << 1; //Set x (send mode).
|
||||
base[I2C_CMD_DATA1] = tmp; //Set value.
|
||||
base[I2C_CNFG] = (2 * size - 2) | 0x2800; //Set size and send mode.
|
||||
_i2c_wait(base); //Kick transaction.
|
||||
base[I2C_CNFG] = ((size - 1) << 1) | 0x2800; //Set size and send mode.
|
||||
_i2c_wait(base); //Kick transaction.
|
||||
|
||||
base[I2C_CNFG] = (base[I2C_CNFG] & 0xFFFFFDFF) | 0x200;
|
||||
|
||||
u32 timeout = get_tmr_ms() + 1500;
|
||||
while (base[I2C_STATUS] & 0x100)
|
||||
;
|
||||
{
|
||||
if (get_tmr_ms() > timeout)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (base[I2C_STATUS] << 28)
|
||||
return 0;
|
||||
@ -68,14 +73,18 @@ static int _i2c_recv_pkt(u32 idx, u8 *buf, u32 size, u32 x)
|
||||
return 0;
|
||||
|
||||
vu32 *base = (vu32 *)QueryIoMapping(i2c_addrs[I2C_5], 0x1000);
|
||||
|
||||
base[I2C_CMD_ADDR0] = (x << 1) | 1; // Set x (recv mode).
|
||||
base[I2C_CNFG] = (size - 1) << 1 | 0x2840; // Set size and recv mode.
|
||||
_i2c_wait(base); // Kick transaction.
|
||||
base[I2C_CMD_ADDR0] = (x << 1) | 1; // Set x (recv mode).
|
||||
base[I2C_CNFG] = ((size - 1) << 1) | 0x2840; // Set size and recv mode.
|
||||
_i2c_wait(base); // Kick transaction.
|
||||
|
||||
base[I2C_CNFG] = (base[I2C_CNFG] & 0xFFFFFDFF) | 0x200;
|
||||
|
||||
u32 timeout = get_tmr_ms() + 1500;
|
||||
while (base[I2C_STATUS] & 0x100)
|
||||
;
|
||||
{
|
||||
if (get_tmr_ms() > timeout)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (base[I2C_STATUS] << 28)
|
||||
return 0;
|
||||
|
@ -25,6 +25,7 @@
|
||||
#define APBDEV_PMC_PWRGATE_TOGGLE 0x30
|
||||
#define APBDEV_PMC_PWRGATE_STATUS 0x38
|
||||
#define APBDEV_PMC_NO_IOPOWER 0x44
|
||||
#define PMC_NO_IOPOWER_SDMMC1_IO_EN (1 << 12)
|
||||
#define APBDEV_PMC_SCRATCH0 0x50
|
||||
#define APBDEV_PMC_SCRATCH1 0x54
|
||||
#define APBDEV_PMC_SCRATCH20 0xA0
|
||||
@ -37,6 +38,7 @@
|
||||
#define APBDEV_PMC_SCRATCH33 0x120
|
||||
#define APBDEV_PMC_SCRATCH40 0x13C
|
||||
#define APBDEV_PMC_OSC_EDPD_OVER 0x1A4
|
||||
#define PMC_OSC_EDPD_OVER_OSC_CTRL_OVER 0x400000
|
||||
#define APBDEV_PMC_RST_STATUS 0x1B4
|
||||
#define APBDEV_PMC_IO_DPD_REQ 0x1B8
|
||||
#define APBDEV_PMC_IO_DPD2_REQ 0x1C0
|
||||
@ -51,9 +53,11 @@
|
||||
#define APBDEV_PMC_REG_SHORT 0x2CC
|
||||
#define APBDEV_PMC_SEC_DISABLE3 0x2D8
|
||||
#define APBDEV_PMC_SECURE_SCRATCH21 0x334
|
||||
#define PMC_FUSE_PRIVATEKEYDISABLE_TZ_STICKY_BIT 0x10
|
||||
#define APBDEV_PMC_SECURE_SCRATCH32 0x360
|
||||
#define APBDEV_PMC_SECURE_SCRATCH49 0x3A4
|
||||
#define APBDEV_PMC_CNTRL2 0x440
|
||||
#define PMC_CNTRL2_HOLD_CKE_LOW_EN 0x1000
|
||||
#define APBDEV_PMC_IO_DPD3_REQ 0x45C
|
||||
#define APBDEV_PMC_IO_DPD4_REQ 0x464
|
||||
#define APBDEV_PMC_UTMIP_PAD_CFG1 0x4C4
|
||||
|
@ -88,7 +88,9 @@ intptr_t QueryIoMapping(u64 addr, u64 size);
|
||||
#define APB_MISC_PP_PINMUX_GLOBAL 0x40
|
||||
#define APB_MISC_GP_LCD_BL_PWM_CFGPADCTRL 0xA34
|
||||
#define APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL 0xA98
|
||||
#define APB_MISC_GP_EMMC2_PAD_CFGPADCTRL 0xA9C
|
||||
#define APB_MISC_GP_EMMC4_PAD_CFGPADCTRL 0xAB4
|
||||
#define APB_MISC_GP_EMMC4_PAD_PUPD_CFGPADCTRL 0xABC
|
||||
#define APB_MISC_GP_WIFI_EN_CFGPADCTRL 0xB64
|
||||
#define APB_MISC_GP_WIFI_RST_CFGPADCTRL 0xB68
|
||||
|
||||
|
@ -29,8 +29,12 @@ enum FatalReason
|
||||
Fatal_UnknownVersion,
|
||||
Fatal_BadResult,
|
||||
Fatal_GetConfig,
|
||||
Fatal_OpenAccessor,
|
||||
Fatal_CloseAccessor,
|
||||
Fatal_IoMapping,
|
||||
Fatal_FatfsMount,
|
||||
Fatal_FatfsFileOpen,
|
||||
Fatal_FatfsMemExhaustion,
|
||||
Fatal_Max
|
||||
};
|
||||
|
||||
|
@ -31,30 +31,33 @@
|
||||
#define BIT(n) (1U<<(n))
|
||||
#endif
|
||||
|
||||
typedef signed char s8;
|
||||
typedef short s16;
|
||||
typedef short SHORT;
|
||||
typedef int s32;
|
||||
typedef int INT;
|
||||
typedef long LONG;
|
||||
typedef long long int s64;
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned short WCHAR;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned int UINT;
|
||||
typedef unsigned long DWORD;
|
||||
typedef unsigned long long QWORD;
|
||||
typedef unsigned long long int u64;
|
||||
typedef volatile unsigned char vu8;
|
||||
typedef volatile unsigned short vu16;
|
||||
typedef volatile unsigned int vu32;
|
||||
typedef int8_t s8;
|
||||
typedef int16_t s16;
|
||||
typedef int16_t SHORT;
|
||||
typedef int32_t s32;
|
||||
typedef int32_t INT;
|
||||
typedef int64_t LONG;
|
||||
typedef int64_t s64;
|
||||
typedef uint8_t u8;
|
||||
typedef uint8_t BYTE;
|
||||
typedef uint16_t u16;
|
||||
typedef uint16_t WORD;
|
||||
typedef uint16_t WCHAR;
|
||||
typedef uint32_t u32;
|
||||
typedef uint32_t UINT;
|
||||
typedef uint32_t DWORD;
|
||||
typedef uint64_t QWORD;
|
||||
typedef uint64_t u64;
|
||||
typedef volatile uint8_t vu8;
|
||||
typedef volatile uint16_t vu16;
|
||||
typedef volatile uint32_t vu32;
|
||||
|
||||
typedef u32 Handle; ///< Kernel object handle.
|
||||
typedef u32 Result; ///< Function error code result type.
|
||||
|
||||
#define INVALID_HANDLE ((Handle) 0)
|
||||
#define CUR_PROCESS_HANDLE ((Handle) 0xFFFF8001)
|
||||
|
||||
#ifndef __cplusplus
|
||||
typedef int bool;
|
||||
#define true 1
|
||||
|
@ -96,7 +96,7 @@ u64 get_tmr_us()
|
||||
void msleep(u64 milliseconds)
|
||||
{
|
||||
u64 now = get_tmr_ms();
|
||||
while (get_tmr_ms() - now < milliseconds)
|
||||
while (((u64)get_tmr_ms() - now) < milliseconds)
|
||||
;
|
||||
//svcSleepThread(1000000 * milliseconds);
|
||||
}
|
||||
@ -105,7 +105,7 @@ void msleep(u64 milliseconds)
|
||||
void usleep(u64 microseconds)
|
||||
{
|
||||
u64 now = get_tmr_us();
|
||||
while (get_tmr_us() - now < microseconds)
|
||||
while (((u64)get_tmr_us() - now) < microseconds)
|
||||
;
|
||||
//svcSleepThread(1000 * microseconds);
|
||||
}
|
||||
|
@ -22,8 +22,8 @@
|
||||
#include "../emuMMC/emummc_ctx.h"
|
||||
|
||||
intptr_t QueryIoMapping(u64 addr, u64 size);
|
||||
#define byte_swap_32(num) ((num >> 24) & 0xff) | ((num << 8) & 0xff0000) | \
|
||||
((num >> 8 )& 0xff00) | ((num << 24) & 0xff000000)
|
||||
#define byte_swap_32(num) (((num >> 24) & 0xff) | ((num << 8) & 0xff0000) | \
|
||||
((num >> 8 )& 0xff00) | ((num << 24) & 0xff000000))
|
||||
|
||||
typedef struct _cfg_op_t
|
||||
{
|
||||
@ -38,6 +38,12 @@ void usleep(u64 ticks);
|
||||
void msleep(u64 milliseconds);
|
||||
void exec_cfg(u32 *base, const cfg_op_t *ops, u32 num_ops);
|
||||
|
||||
static inline void *armGetTls(void) {
|
||||
void *ret;
|
||||
__asm__ __volatile__("MRS %x[data], TPIDRRO_EL0" : [data]"=r"(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
extern volatile emuMMC_ctx_t emuMMC_ctx;
|
||||
|
||||
#endif
|
||||
|
@ -1,178 +1,47 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGETS := exosphere.bin warmboot.bin program.lz4
|
||||
CLEAN_TARGETS := exosphere-clean program-clean boot_code-clean warmboot-clean
|
||||
|
||||
ifeq ($(strip $(DEVKITPRO)),)
|
||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||
endif
|
||||
SUBFOLDERS := $(MODULES)
|
||||
|
||||
TOPDIR ?= $(CURDIR)
|
||||
include $(DEVKITPRO)/devkitA64/base_rules
|
||||
all: exosphere.bin warmboot.bin
|
||||
|
||||
AMSBRANCH := $(shell git symbolic-ref --short HEAD)
|
||||
AMSHASH = $(shell git rev-parse --short=16 HEAD)
|
||||
AMSREV := $(AMSBRANCH)-$(shell git rev-parse --short HEAD)
|
||||
clean: $(CLEAN_TARGETS)
|
||||
@rm -f exosphere.bin
|
||||
|
||||
ifneq (, $(strip $(shell git status --porcelain 2>/dev/null)))
|
||||
AMSREV := $(AMSREV)-dirty
|
||||
endif
|
||||
exosphere.bin: program.lz4 boot_code.lz4
|
||||
$(MAKE) -C loader_stub
|
||||
@cp loader_stub/loader_stub.bin exosphere.bin
|
||||
@printf LENY >> exosphere.bin
|
||||
@echo "Built exosphere.bin..."
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# DATA is a list of directories containing data files
|
||||
# INCLUDES is a list of directories containing header files
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
BUILD := build
|
||||
SOURCES := src src/dbg
|
||||
DATA := data
|
||||
INCLUDES := include ../libraries/libvapours/include
|
||||
program.lz4: build_program
|
||||
@cp program/program.lz4 program.lz4
|
||||
@cp program/boot_code.lz4 boot_code.lz4
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -march=armv8-a -mtune=cortex-a57 -mgeneral-regs-only #<- important
|
||||
DEFINES := -D__CCPLEX__ -DATMOSPHERE_GIT_BRANCH=\"$(AMSBRANCH)\" -DATMOSPHERE_GIT_REV=\"$(AMSREV)\" -DATMOSPHERE_RELEASE_VERSION_HASH="0x$(AMSHASH)"
|
||||
CFLAGS := \
|
||||
-g \
|
||||
-Os \
|
||||
-ffunction-sections \
|
||||
-fdata-sections \
|
||||
-fomit-frame-pointer \
|
||||
-fno-asynchronous-unwind-tables \
|
||||
-fno-unwind-tables \
|
||||
-std=gnu11 \
|
||||
-Werror \
|
||||
-Wall \
|
||||
$(ARCH) $(DEFINES)
|
||||
build_program:
|
||||
$(MAKE) -C program
|
||||
|
||||
CFLAGS += $(INCLUDE)
|
||||
warmboot.bin: build_warmboot
|
||||
@cp warmboot/warmboot.bin warmboot.bin
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
|
||||
build_warmboot:
|
||||
$(MAKE) -C warmboot
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -specs=$(TOPDIR)/linker.specs -nostartfiles -nostdlib -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||
boot_code.lz4: program.lz4
|
||||
|
||||
LIBS :=
|
||||
exosphere-clean:
|
||||
$(MAKE) -C loader_stub clean
|
||||
@rm -f exosphere.bin
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS :=
|
||||
program-clean:
|
||||
$(MAKE) -C program clean
|
||||
@rm -f program.lz4
|
||||
|
||||
warmboot-clean:
|
||||
$(MAKE) -C warmboot clean
|
||||
@rm -f warmboot.bin
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
boot_code-clean:
|
||||
@rm -f boot_code.lz4
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir)) \
|
||||
$(TOPDIR)/lp0fw \
|
||||
$(TOPDIR)/sc7fw \
|
||||
$(TOPDIR)/rebootstub
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) sc7fw.bin lp0fw.bin rebootstub.bin
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
|
||||
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
|
||||
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
.PHONY: $(BUILD) build_sc7fw build_lp0fw build_rebootstub clean all
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(BUILD)
|
||||
|
||||
check_sc7fw:
|
||||
@$(MAKE) -C sc7fw all
|
||||
|
||||
check_lp0fw:
|
||||
@$(MAKE) -C lp0fw all
|
||||
|
||||
check_rebootstub:
|
||||
@$(MAKE) -C rebootstub all
|
||||
|
||||
$(BUILD): check_sc7fw check_lp0fw check_rebootstub
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@$(MAKE) -C $(TOPDIR)/sc7fw clean
|
||||
@$(MAKE) -C $(TOPDIR)/lp0fw clean
|
||||
@$(MAKE) -C $(TOPDIR)/rebootstub clean
|
||||
@rm -fr $(BUILD) $(TARGET).bin $(TARGET).elf
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all : $(OUTPUT).bin
|
||||
|
||||
$(OUTPUT).bin : $(OUTPUT).elf
|
||||
$(OBJCOPY) -S -O binary $< $@
|
||||
@echo built ... $(notdir $@)
|
||||
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
%.elf:
|
||||
@echo linking $(notdir $@)
|
||||
$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
@$(NM) -CSn $@ > $(notdir $*.lst)
|
||||
|
||||
$(OFILES_SRC) : $(HFILES_BIN)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o %_bin.h: %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
||||
.PHONY: all clean build_program $(CLEAN_TARGETS)
|
||||
|
@ -1,6 +0,0 @@
|
||||
Exosphère
|
||||
=====
|
||||
|
||||

|
||||
|
||||
Exosphère is a Secure Monitor implementation for the Nintendo Switch.
|
@ -1,269 +0,0 @@
|
||||
OUTPUT_ARCH(aarch64)
|
||||
ENTRY(__start_cold)
|
||||
|
||||
MEMORY
|
||||
{
|
||||
NULL : ORIGIN = 0, LENGTH = 4K
|
||||
ccrt0 : ORIGIN = 0x040006000, LENGTH = 4K
|
||||
glob : ORIGIN = 0x040020000, LENGTH = 128K
|
||||
tzram : ORIGIN = 0x07C010000, LENGTH = 64K
|
||||
/*
|
||||
The warmboot crt0 is preceeded by the exception vector page and the L2 and L3 translation tables.
|
||||
Normally the main code immediately follows the warmboot crt0, aligned to 256 bytes.
|
||||
We can't ensure or replicate that behavior properly so we'll just give 2K to the warmboot crt0.
|
||||
*/
|
||||
warmboot_crt0 : ORIGIN = ORIGIN(tzram) + 12K, LENGTH = 2K
|
||||
|
||||
/* 8K are the MMU L2 and L3 tables & 2K from the evt page */
|
||||
main : ORIGIN = 0x1F01E0000 + LENGTH(warmboot_crt0), LENGTH = LENGTH(tzram) - LENGTH(pk2ldr) - LENGTH(evt) - LENGTH(warmboot_crt0) - 10K
|
||||
pk2ldr : ORIGIN = ORIGIN(main) - LENGTH(warmboot_crt0) + LENGTH(tzram), LENGTH = 8K
|
||||
/* The first half of the page are exception entry stacks, the other half are the vectors themselves */
|
||||
evt : ORIGIN = ORIGIN(pk2ldr) + 40K + 2K, LENGTH = 2K
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
PROVIDE(__start__ = 0x040006000);
|
||||
. = __start__;
|
||||
|
||||
.cold_crt0 :
|
||||
{
|
||||
. = ALIGN(64);
|
||||
__cold_crt0_start__ = ABSOLUTE(.);
|
||||
__glob_origin__ = ORIGIN(glob);
|
||||
KEEP (*(.cold_crt0.text.start)) /* MUST be first */
|
||||
KEEP (*(.cold_crt0.text*))
|
||||
KEEP (coldboot_init.o(.text*))
|
||||
*(.cold_crt0.rodata*)
|
||||
coldboot_init.o(.rodata*)
|
||||
*(.cold_crt0.data*)
|
||||
coldboot_init.o(.data*)
|
||||
. = ALIGN(8);
|
||||
*(.cold_crt0.bss*)
|
||||
coldboot_init.o(.bss* COMMON)
|
||||
. = ALIGN(64);
|
||||
__cold_crt0_end__ = ABSOLUTE(.);
|
||||
} >ccrt0 AT>glob
|
||||
|
||||
.pk2ldr :
|
||||
{
|
||||
. = ALIGN(4096);
|
||||
__pk2ldr_lma__ = LOADADDR(.pk2ldr);
|
||||
__pk2ldr_start__ = ABSOLUTE(.);
|
||||
KEEP (package2.o(.text*))
|
||||
package2.o(.rodata*)
|
||||
package2.o(.data*)
|
||||
. = ALIGN(8);
|
||||
} >pk2ldr AT>glob
|
||||
|
||||
.pk2ldr.bss :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
__pk2ldr_bss_start__ = ABSOLUTE(.);
|
||||
package2.o(.bss* COMMON)
|
||||
. = ALIGN(8);
|
||||
__pk2ldr_end__ = ABSOLUTE(.);
|
||||
} >pk2ldr AT>glob
|
||||
|
||||
.vectors :
|
||||
{
|
||||
. = ALIGN(2048);
|
||||
__vectors_lma__ = LOADADDR(.vectors);
|
||||
__vectors_start__ = ABSOLUTE(.);
|
||||
KEEP (*(.vectors*))
|
||||
. = ALIGN(8);
|
||||
__vectors_end__ = ABSOLUTE(.);
|
||||
} >evt AT>glob
|
||||
|
||||
.warm_crt0 :
|
||||
{
|
||||
. = ALIGN(64);
|
||||
__warmboot_crt0_lma__ = LOADADDR(.warm_crt0);
|
||||
__warmboot_crt0_start__ = ABSOLUTE(.);
|
||||
KEEP (*(.warm_crt0.text.start)) /* Should be first */
|
||||
KEEP (*(.warm_crt0.text*))
|
||||
KEEP (warmboot_init.o(.text*))
|
||||
*(.warm_crt0.rodata*)
|
||||
warmboot_init.o(.rodata*)
|
||||
*(.warm_crt0.data*)
|
||||
warmboot_init.o(.data*)
|
||||
. = ALIGN(8);
|
||||
*(.warm_crt0.bss*)
|
||||
warmboot_init.o(.bss*)
|
||||
. = ALIGN(64);
|
||||
__warmboot_crt0_end__ = ABSOLUTE(.);
|
||||
} >warmboot_crt0 AT>glob
|
||||
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(256);
|
||||
__main_lma__ = LOADADDR(.text);
|
||||
__main_start__ = ABSOLUTE(.);
|
||||
*(.text.unlikely .text.*_unlikely .text.unlikely.*)
|
||||
*(.text.exit .text.exit.*)
|
||||
*(.text.startup .text.startup.*)
|
||||
*(.text.hot .text.hot.*)
|
||||
*(.text .stub .text.* .gnu.linkonce.t.*)
|
||||
. = ALIGN(8);
|
||||
} >main AT>glob
|
||||
|
||||
.init :
|
||||
{
|
||||
KEEP( *(.init) )
|
||||
. = ALIGN(8);
|
||||
} >main AT>glob
|
||||
|
||||
.plt :
|
||||
{
|
||||
*(.plt)
|
||||
*(.iplt)
|
||||
. = ALIGN(8);
|
||||
} >main AT>glob
|
||||
|
||||
|
||||
.fini :
|
||||
{
|
||||
KEEP( *(.fini) )
|
||||
. = ALIGN(8);
|
||||
} >main AT>glob
|
||||
|
||||
.rodata :
|
||||
{
|
||||
*(.rodata .rodata.* .gnu.linkonce.r.*)
|
||||
SORT(CONSTRUCTORS)
|
||||
. = ALIGN(8);
|
||||
} >main AT>glob
|
||||
|
||||
.got : { __got_start__ = ABSOLUTE(.); *(.got) *(.igot) } >main AT>glob
|
||||
.got.plt : { *(.got.plt) *(.igot.plt) __got_end__ = ABSOLUTE(.);} >main AT>glob
|
||||
|
||||
.preinit_array :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
PROVIDE (__preinit_array_start = .);
|
||||
KEEP (*(.preinit_array))
|
||||
PROVIDE (__preinit_array_end = .);
|
||||
. = ALIGN(8);
|
||||
} >main AT>glob
|
||||
|
||||
.init_array :
|
||||
{
|
||||
PROVIDE (__init_array_start = .);
|
||||
KEEP (*(SORT(.init_array.*)))
|
||||
KEEP (*(.init_array))
|
||||
PROVIDE (__init_array_end = .);
|
||||
} >main AT>glob
|
||||
|
||||
.fini_array :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
PROVIDE (__fini_array_start = .);
|
||||
KEEP (*(.fini_array))
|
||||
KEEP (*(SORT(.fini_array.*)))
|
||||
PROVIDE (__fini_array_end = .);
|
||||
. = ALIGN(8);
|
||||
} >main AT>glob
|
||||
|
||||
.ctors :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
|
||||
KEEP (*(SORT(.ctors.*)))
|
||||
KEEP (*(.ctors))
|
||||
. = ALIGN(8);
|
||||
} >main AT>glob
|
||||
|
||||
.dtors ALIGN(8) :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
KEEP (*crtbegin.o(.dtors))
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
|
||||
KEEP (*(SORT(.dtors.*)))
|
||||
KEEP (*(.dtors))
|
||||
. = ALIGN(8);
|
||||
} >main AT>glob
|
||||
|
||||
.data ALIGN(8) :
|
||||
{
|
||||
*(.data .data.* .gnu.linkonce.d.*)
|
||||
CONSTRUCTORS
|
||||
. = ALIGN(8);
|
||||
} >main AT>glob
|
||||
|
||||
|
||||
.eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } >main AT>glob
|
||||
.eh_frame : { KEEP (*(.eh_frame)) *(.eh_frame.*) } >main AT>glob
|
||||
.gcc_except_table : { *(.gcc_except_table .gcc_except_table.*) } >main AT>glob
|
||||
.gnu_extab : { *(.gnu_extab*) } >main AT>glob
|
||||
.exception_ranges : { *(.exception_ranges .exception_ranges*) } >main AT>glob
|
||||
|
||||
.dynamic : { *(.dynamic) } >main AT>glob
|
||||
.interp : { *(.interp) } >main AT>glob
|
||||
.note.gnu.build-id : { *(.note.gnu.build-id) } >main AT>glob
|
||||
.hash : { *(.hash) } >main AT>glob
|
||||
.gnu.hash : { *(.gnu.hash) } >main AT>glob
|
||||
.gnu.version : { *(.gnu.version) } >main AT>glob
|
||||
.gnu.version_d : { *(.gnu.version_d) } >main AT>glob
|
||||
.gnu.version_r : { *(.gnu.version_r) } >main AT>glob
|
||||
.dynsym : { *(.dynsym) } >main AT>glob
|
||||
.dynstr : { *(.dynstr) } >main AT>glob
|
||||
.rela.dyn : { *(.rela.*); __main_end__ = ABSOLUTE(.);} >main AT>glob
|
||||
|
||||
.bss :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
__main_bss_start__ = ABSOLUTE(.);
|
||||
__loaded_end_lma__ = LOADADDR(.bss);
|
||||
*(.dynbss)
|
||||
*(.bss .bss.* .gnu.linkonce.b.*)
|
||||
*(COMMON)
|
||||
. = ALIGN(8);
|
||||
__main_end__ = ABSOLUTE(.);
|
||||
} >main AT>glob
|
||||
|
||||
__end__ = ABSOLUTE(.) ;
|
||||
|
||||
. = ALIGN(0x1000);
|
||||
__argdata__ = ABSOLUTE(.) ;
|
||||
|
||||
/* ==================
|
||||
==== Metadata ====
|
||||
================== */
|
||||
|
||||
/* Discard sections that difficult post-processing */
|
||||
/DISCARD/ : { *(.group .comment .note) }
|
||||
|
||||
/* Stabs debugging sections. */
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
|
||||
/* DWARF debug sections.
|
||||
Symbols in the DWARF debugging sections are relative to the beginning
|
||||
of the section so we begin them at 0. */
|
||||
|
||||
/* DWARF 1 */
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
|
||||
/* GNU DWARF 1 extensions */
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
|
||||
/* DWARF 1.1 and DWARF 2 */
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
|
||||
/* DWARF 2 */
|
||||
.debug_info 0 : { *(.debug_info) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
%rename link old_link
|
||||
|
||||
*link:
|
||||
%(old_link) -T %:getenv(TOPDIR /linker.ld) --nmagic --gc-sections
|
130
exosphere/loader_stub/Makefile
Normal file
130
exosphere/loader_stub/Makefile
Normal file
@ -0,0 +1,130 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
# Define the atmosphere board and cpu
|
||||
#---------------------------------------------------------------------------------
|
||||
export ATMOSPHERE_BOARD := nx-hac-001
|
||||
export ATMOSPHERE_CPU := arm-cortex-a57
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# pull in common atmosphere configuration
|
||||
#---------------------------------------------------------------------------------
|
||||
include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/exosphere.mk
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
export TOPDIR := $(CURDIR)
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir)) \
|
||||
$(TOPDIR)/../program
|
||||
|
||||
CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \
|
||||
$(notdir $(wildcard $(dir)/*.c))))
|
||||
CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c)))
|
||||
CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c)))
|
||||
CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c)))
|
||||
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \
|
||||
$(notdir $(wildcard $(dir)/*.cpp))))
|
||||
CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp)))
|
||||
CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp)))
|
||||
CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp)))
|
||||
|
||||
SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \
|
||||
$(notdir $(wildcard $(dir)/*.s))))
|
||||
SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s)))
|
||||
SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s)))
|
||||
SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s)))
|
||||
|
||||
BINFILES := program.lz4 boot_code.lz4
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
|
||||
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
|
||||
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(subst -,_,$(BINFILES))))
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I.
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib -L$(dir)/$(ATMOSPHERE_LIBRARY_DIR))
|
||||
|
||||
.PHONY: $(BUILD) clean all check_program
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(BUILD) check_program
|
||||
|
||||
$(BUILD): check_program
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
check_program:
|
||||
@$(MAKE) -C ../program all
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) $(OUTPUT).bin $(OUTPUT).elf
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all : $(OUTPUT).bin
|
||||
|
||||
$(OUTPUT).bin : $(OUTPUT).elf
|
||||
$(OBJCOPY) -S -O binary --set-section-flags .bss=alloc,load,contents $< $@
|
||||
@echo built ... $(notdir $@)
|
||||
|
||||
$(OUTPUT).elf : $(OFILES) ../../../libraries/libexosphere/$(ATMOSPHERE_LIBRARY_DIR)/libexosphere.a
|
||||
|
||||
%.elf:
|
||||
@echo linking $(notdir $@)
|
||||
$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
@$(NM) -CSn $@ > $(notdir $*.lst)
|
||||
|
||||
$(OFILES_SRC) : $(HFILES_BIN)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o %_bin.h: %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
%.lz4.o %_lz4.h: %.lz4
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
182
exosphere/loader_stub/loader_stub.ld
Normal file
182
exosphere/loader_stub/loader_stub.ld
Normal file
@ -0,0 +1,182 @@
|
||||
OUTPUT_ARCH(aarch64)
|
||||
ENTRY(_start)
|
||||
|
||||
MEMORY
|
||||
{
|
||||
NULL : ORIGIN = 0, LENGTH = 4K
|
||||
ldr_stub : ORIGIN = 0x040030000, LENGTH = 128K
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* =========== CODE section =========== */
|
||||
PROVIDE(__start__ = 0x040030000);
|
||||
. = __start__;
|
||||
__code_start = . ;
|
||||
|
||||
.crt0 :
|
||||
{
|
||||
KEEP (*(.crt0 .crt0.*))
|
||||
. = ALIGN(8);
|
||||
} >ldr_stub
|
||||
|
||||
.text :
|
||||
{
|
||||
*(.text.unlikely .text.*_unlikely .text.unlikely.*)
|
||||
*(.text.exit .text.exit.*)
|
||||
*(.text.startup .text.startup.*)
|
||||
*(.text.hot .text.hot.*)
|
||||
*(.text .stub .text.* .gnu.linkonce.t.*)
|
||||
. = ALIGN(8);
|
||||
} >ldr_stub
|
||||
|
||||
.init :
|
||||
{
|
||||
KEEP( *(.init) )
|
||||
. = ALIGN(8);
|
||||
} >ldr_stub
|
||||
|
||||
.plt :
|
||||
{
|
||||
*(.plt)
|
||||
*(.iplt)
|
||||
. = ALIGN(8);
|
||||
} >ldr_stub
|
||||
|
||||
.fini :
|
||||
{
|
||||
KEEP( *(.fini) )
|
||||
. = ALIGN(8);
|
||||
} >ldr_stub
|
||||
|
||||
|
||||
/* =========== RODATA section =========== */
|
||||
. = ALIGN(8);
|
||||
__rodata_start = . ;
|
||||
|
||||
.rodata :
|
||||
{
|
||||
*(.rodata .rodata.* .gnu.linkonce.r.*)
|
||||
. = ALIGN(8);
|
||||
} >ldr_stub
|
||||
|
||||
.eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } >ldr_stub
|
||||
.eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } >ldr_stub
|
||||
.gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } >ldr_stub
|
||||
.gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } >ldr_stub
|
||||
|
||||
.hash : { *(.hash) } >ldr_stub
|
||||
|
||||
/* =========== DATA section =========== */
|
||||
. = ALIGN(8);
|
||||
__data_start = . ;
|
||||
|
||||
.eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } >ldr_stub
|
||||
.gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } >ldr_stub
|
||||
.gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } >ldr_stub
|
||||
.exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } >ldr_stub
|
||||
|
||||
.preinit_array ALIGN(8) :
|
||||
{
|
||||
PROVIDE (__preinit_array_start = .);
|
||||
KEEP (*(.preinit_array))
|
||||
PROVIDE (__preinit_array_end = .);
|
||||
} >ldr_stub
|
||||
|
||||
.init_array ALIGN(8) :
|
||||
{
|
||||
PROVIDE (__init_array_start = .);
|
||||
KEEP (*(SORT(.init_array.*)))
|
||||
KEEP (*(.init_array))
|
||||
PROVIDE (__init_array_end = .);
|
||||
} >ldr_stub
|
||||
|
||||
.fini_array ALIGN(8) :
|
||||
{
|
||||
PROVIDE (__fini_array_start = .);
|
||||
KEEP (*(.fini_array))
|
||||
KEEP (*(SORT(.fini_array.*)))
|
||||
PROVIDE (__fini_array_end = .);
|
||||
} >ldr_stub
|
||||
|
||||
.ctors ALIGN(8) :
|
||||
{
|
||||
KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
|
||||
KEEP (*(SORT(.ctors.*)))
|
||||
KEEP (*(.ctors))
|
||||
} >ldr_stub
|
||||
|
||||
.dtors ALIGN(8) :
|
||||
{
|
||||
KEEP (*crtbegin.o(.dtors))
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
|
||||
KEEP (*(SORT(.dtors.*)))
|
||||
KEEP (*(.dtors))
|
||||
} >ldr_stub
|
||||
|
||||
__got_start__ = .;
|
||||
|
||||
.got : { *(.got) *(.igot) } >ldr_stub
|
||||
.got.plt : { *(.got.plt) *(.igot.plt) } >ldr_stub
|
||||
|
||||
__got_end__ = .;
|
||||
|
||||
.data ALIGN(8) :
|
||||
{
|
||||
*(.data .data.* .gnu.linkonce.d.*)
|
||||
SORT(CONSTRUCTORS)
|
||||
} >ldr_stub
|
||||
|
||||
__bss_start__ = .;
|
||||
.bss ALIGN(8) :
|
||||
{
|
||||
*(.dynbss)
|
||||
*(.bss .bss.* .gnu.linkonce.b.*)
|
||||
*(COMMON)
|
||||
. = ALIGN(16);
|
||||
} >ldr_stub
|
||||
__bss_end__ = .;
|
||||
|
||||
__end__ = ABSOLUTE(.) ;
|
||||
|
||||
/* ==================
|
||||
==== Metadata ====
|
||||
================== */
|
||||
|
||||
/* Discard sections that difficult post-processing */
|
||||
/DISCARD/ : { *(.group .comment .note .interp) }
|
||||
|
||||
/* Stabs debugging sections. */
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
|
||||
/* DWARF debug sections.
|
||||
Symbols in the DWARF debugging sections are relative to the beginning
|
||||
of the section so we begin them at 0. */
|
||||
|
||||
/* DWARF 1 */
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
|
||||
/* GNU DWARF 1 extensions */
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
|
||||
/* DWARF 1.1 and DWARF 2 */
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
|
||||
/* DWARF 2 */
|
||||
.debug_info 0 : { *(.debug_info) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
}
|
7
exosphere/loader_stub/loader_stub.specs
Normal file
7
exosphere/loader_stub/loader_stub.specs
Normal file
@ -0,0 +1,7 @@
|
||||
%rename link old_link
|
||||
|
||||
*link:
|
||||
%(old_link) -T %:getenv(TOPDIR /loader_stub.ld) --gc-sections --nmagic -nostdlib -nostartfiles
|
||||
|
||||
*startfile:
|
||||
crti%O%s crtbegin%O%s
|
47
exosphere/loader_stub/source/secmon_loader_error.cpp
Normal file
47
exosphere/loader_stub/source/secmon_loader_error.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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 <exosphere.hpp>
|
||||
#include "secmon_loader_error.hpp"
|
||||
|
||||
namespace ams::diag {
|
||||
|
||||
NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) {
|
||||
ams::secmon::loader::ErrorReboot();
|
||||
}
|
||||
|
||||
NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
|
||||
ams::secmon::loader::ErrorReboot();
|
||||
}
|
||||
|
||||
NORETURN void AbortImpl() {
|
||||
ams::secmon::loader::ErrorReboot();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace ams::secmon::loader {
|
||||
|
||||
NORETURN void ErrorReboot() {
|
||||
/* Invalidate the security engine. */
|
||||
/* TODO */
|
||||
|
||||
/* Reboot. */
|
||||
while (true) {
|
||||
wdt::Reboot();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -13,14 +13,11 @@
|
||||
* 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 EXOSPHERE_BPMPFW_SC7_H
|
||||
#define EXOSPHERE_BPMPFW_SC7_H
|
||||
#include <exosphere.hpp>
|
||||
#pragma once
|
||||
|
||||
#include "utils.h"
|
||||
namespace ams::secmon::loader {
|
||||
|
||||
void sc7_entry_main(void);
|
||||
NORETURN void ErrorReboot();
|
||||
|
||||
void reboot(void);
|
||||
|
||||
#endif
|
||||
}
|
43
exosphere/loader_stub/source/secmon_loader_main.cpp
Normal file
43
exosphere/loader_stub/source/secmon_loader_main.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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 <exosphere.hpp>
|
||||
#include "secmon_loader_uncompress.hpp"
|
||||
#include "program_lz4.h"
|
||||
#include "boot_code_lz4.h"
|
||||
|
||||
namespace ams::secmon::loader {
|
||||
|
||||
NORETURN void UncompressAndExecute() {
|
||||
/* Uncompress the program image. */
|
||||
Uncompress(secmon::MemoryRegionPhysicalTzramFullProgramImage.GetPointer(), secmon::MemoryRegionPhysicalTzramFullProgramImage.GetSize(), program_lz4, program_lz4_size);
|
||||
|
||||
|
||||
/* Copy the boot image to the end of IRAM */
|
||||
u8 *relocated_boot_code = secmon::MemoryRegionPhysicalIramBootCodeImage.GetEndPointer<u8>() - boot_code_lz4_size;
|
||||
std::memcpy(relocated_boot_code, boot_code_lz4, boot_code_lz4_size);
|
||||
|
||||
/* Uncompress the boot image. */
|
||||
Uncompress(secmon::MemoryRegionPhysicalIramBootCodeImage.GetPointer(), secmon::MemoryRegionPhysicalIramBootCodeImage.GetSize(), relocated_boot_code, boot_code_lz4_size);
|
||||
|
||||
|
||||
/* Jump to the boot image. */
|
||||
reinterpret_cast<void (*)()>(secmon::MemoryRegionPhysicalIramBootCodeImage.GetAddress())();
|
||||
|
||||
/* We will never reach this point. */
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
}
|
103
exosphere/loader_stub/source/secmon_loader_uncompress.cpp
Normal file
103
exosphere/loader_stub/source/secmon_loader_uncompress.cpp
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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 <exosphere.hpp>
|
||||
#include "secmon_loader_uncompress.hpp"
|
||||
|
||||
namespace ams::secmon::loader {
|
||||
|
||||
namespace {
|
||||
|
||||
class Lz4Uncompressor {
|
||||
private:
|
||||
const u8 *src;
|
||||
size_t src_size;
|
||||
size_t src_offset;
|
||||
u8 *dst;
|
||||
size_t dst_size;
|
||||
size_t dst_offset;
|
||||
public:
|
||||
Lz4Uncompressor(void *dst, size_t dst_size, const void *src, size_t src_size) : src(static_cast<const u8 *>(src)), src_size(src_size), src_offset(0), dst(static_cast<u8 *>(dst)), dst_size(dst_size), dst_offset(0) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
void Uncompress() {
|
||||
while (true) {
|
||||
/* Read a control byte. */
|
||||
const u8 control = this->ReadByte();
|
||||
|
||||
/* Copy what it specifies we should copy. */
|
||||
this->Copy(this->GetCopySize(control >> 4));
|
||||
|
||||
/* If we've exceeded size, we're done. */
|
||||
if (this->src_offset >= this->src_size) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Read the wide copy offset. */
|
||||
u16 wide_offset = this->ReadByte();
|
||||
AMS_ABORT_UNLESS(this->CanRead());
|
||||
wide_offset |= (this->ReadByte() << 8);
|
||||
|
||||
/* Determine the copy size. */
|
||||
const size_t wide_copy_size = this->GetCopySize(control & 0xF);
|
||||
|
||||
/* Copy bytes. */
|
||||
const size_t end_offset = this->dst_offset + wide_copy_size + 4;
|
||||
for (size_t cur_offset = this->dst_offset; cur_offset < end_offset; this->dst_offset = (++cur_offset)) {
|
||||
AMS_ABORT_UNLESS(wide_offset <= cur_offset);
|
||||
|
||||
this->dst[cur_offset] = this->dst[cur_offset - wide_offset];
|
||||
}
|
||||
}
|
||||
}
|
||||
private:
|
||||
u8 ReadByte() {
|
||||
return this->src[this->src_offset++];
|
||||
}
|
||||
|
||||
bool CanRead() const {
|
||||
return this->src_offset < this->src_size;
|
||||
}
|
||||
|
||||
size_t GetCopySize(u8 control) {
|
||||
size_t size = control;
|
||||
|
||||
if (control >= 0xF) {
|
||||
do {
|
||||
AMS_ABORT_UNLESS(this->CanRead());
|
||||
control = this->ReadByte();
|
||||
size += control;
|
||||
} while (control == 0xFF);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void Copy(size_t size) {
|
||||
__builtin_memcpy(this->dst + this->dst_offset, this->src + this->src_offset, size);
|
||||
this->dst_offset += size;
|
||||
this->src_offset += size;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void Uncompress(void *dst, size_t dst_size, const void *src, size_t src_size) {
|
||||
/* Create an execute a decompressor. */
|
||||
Lz4Uncompressor(dst, dst_size, src, src_size).Uncompress();
|
||||
}
|
||||
|
||||
}
|
@ -13,18 +13,11 @@
|
||||
* 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 "utils.h"
|
||||
#include "i2c.h"
|
||||
#include "timer.h"
|
||||
#include <vapours.hpp>
|
||||
#pragma once
|
||||
|
||||
namespace ams::secmon::loader {
|
||||
|
||||
void Uncompress(void *dst, size_t dst_size, const void *src, size_t src_size);
|
||||
|
||||
void do_shutdown(void) {
|
||||
/* Initialize i2c. */
|
||||
i2c_init();
|
||||
|
||||
/* Stop alarm, shutdown. */
|
||||
i2c_stop_rtc_alarm();
|
||||
i2c_send_shutdown_cmd();
|
||||
|
||||
while (true) { }
|
||||
}
|
105
exosphere/loader_stub/source/start.s
Normal file
105
exosphere/loader_stub/source/start.s
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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/>.
|
||||
*/
|
||||
|
||||
/* For some reason GAS doesn't know about it, even with .cpu cortex-a57 */
|
||||
#define cpuactlr_el1 s3_1_c15_c2_0
|
||||
#define cpuectlr_el1 s3_1_c15_c2_1
|
||||
|
||||
.macro RESET_CORE
|
||||
mov x0, #(1 << 63)
|
||||
msr cpuactlr_el1, x0 /* disable regional clock gating */
|
||||
isb
|
||||
mov x0, #3
|
||||
msr rmr_el3, x0
|
||||
isb
|
||||
dsb sy
|
||||
/* Nintendo forgot to copy-paste the branch instruction below. */
|
||||
1:
|
||||
wfi
|
||||
b 1b
|
||||
.endm
|
||||
|
||||
.macro ERRATUM_INVALIDATE_BTB_AT_BOOT
|
||||
/* Nintendo copy-pasted https://github.com/ARM-software/arm-trusted-firmware/blob/master/plat/nvidia/tegra/common/aarch64/tegra_helpers.S#L312 */
|
||||
/*
|
||||
* Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
/* The following comments are mine. */
|
||||
|
||||
/*
|
||||
Enable invalidates of branch target buffer, then flush
|
||||
the entire instruction cache at the local level, and
|
||||
with the reg change, the branch target buffer, then disable
|
||||
invalidates of the branch target buffer again.
|
||||
*/
|
||||
mrs x0, cpuactlr_el1
|
||||
orr x0, x0, #1
|
||||
msr cpuactlr_el1, x0
|
||||
|
||||
dsb sy
|
||||
isb
|
||||
ic iallu
|
||||
dsb sy
|
||||
isb
|
||||
|
||||
mrs x0, cpuactlr_el1
|
||||
bic x0, x0, #1
|
||||
msr cpuactlr_el1, x0
|
||||
|
||||
.rept 7
|
||||
nop /* wait long enough for the write to cpuactlr_el1 to have completed */
|
||||
.endr
|
||||
|
||||
/* if the OS lock is set, disable it and request a warm reset */
|
||||
mrs x0, oslsr_el1
|
||||
ands x0, x0, #2
|
||||
b.eq 2f
|
||||
mov x0, xzr
|
||||
msr oslar_el1, x0
|
||||
|
||||
RESET_CORE
|
||||
|
||||
.rept 65
|
||||
nop /* guard against speculative excecution */
|
||||
.endr
|
||||
|
||||
2:
|
||||
/* set the OS lock */
|
||||
mov x0, #1
|
||||
msr oslar_el1, x0
|
||||
.endm
|
||||
|
||||
.section .crt0.text.start, "ax", %progbits
|
||||
.align 6
|
||||
.global _start
|
||||
_start:
|
||||
/* mask all interrupts */
|
||||
msr daifset, #0xF
|
||||
|
||||
/* Fixup hardware erratum */
|
||||
ERRATUM_INVALIDATE_BTB_AT_BOOT
|
||||
|
||||
/* Set the stack pointer to a temporary location. */
|
||||
ldr x20, =0x7C020000
|
||||
mov sp, x20
|
||||
|
||||
/* Call our init array functions. */
|
||||
bl __libc_init_array
|
||||
|
||||
/* Uncompress the program and iram boot code images. */
|
||||
b _ZN3ams6secmon6loader20UncompressAndExecuteEv
|
@ -1,24 +0,0 @@
|
||||
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
|
||||
OUTPUT_ARCH(arm)
|
||||
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x40010000;
|
||||
|
||||
__start__ = ABSOLUTE(.);
|
||||
|
||||
.text : ALIGN(4) { *(.text.start) *(.text*); . = ALIGN(4); }
|
||||
.rodata : ALIGN(4) { *(.rodata*); . = ALIGN(4); }
|
||||
.bss : ALIGN(8) { __bss_start__ = .; *(.bss* COMMON); . = ALIGN(8); __bss_end__ = .; }
|
||||
|
||||
. = ALIGN(4);
|
||||
|
||||
__end__ = ABSOLUTE(.);
|
||||
|
||||
__total_size__ = (__end__ - __start__);
|
||||
__executable_size__ = (__end__ - _start);
|
||||
|
||||
__stack_top__ = 0x40013000;
|
||||
__stack_bottom__ = 0x40012000;
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
%rename link old_link
|
||||
|
||||
*link:
|
||||
%(old_link) -T %:getenv(TOPDIR /linker.ld) --nmagic --gc-sections
|
||||
|
||||
*startfile:
|
||||
crti%O%s crtbegin%O%s
|
@ -1,138 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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 <stdint.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "car.h"
|
||||
#include "timer.h"
|
||||
#include "pmc.h"
|
||||
#include "emc.h"
|
||||
#include "lp0.h"
|
||||
|
||||
static inline uint32_t get_special_clk_reg(CarDevice dev) {
|
||||
switch (dev) {
|
||||
case CARDEVICE_UARTA: return 0x178;
|
||||
case CARDEVICE_UARTB: return 0x17C;
|
||||
case CARDEVICE_I2C1: return 0x124;
|
||||
case CARDEVICE_I2C5: return 0x128;
|
||||
case CARDEVICE_ACTMON: return 0x3E8;
|
||||
case CARDEVICE_BPMP: return 0;
|
||||
default: reboot();
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t get_special_clk_val(CarDevice dev) {
|
||||
switch (dev) {
|
||||
case CARDEVICE_UARTA: return 0;
|
||||
case CARDEVICE_UARTB: return 0;
|
||||
case CARDEVICE_I2C1: return (6 << 29);
|
||||
case CARDEVICE_I2C5: return (6 << 29);
|
||||
case CARDEVICE_ACTMON: return (6 << 29);
|
||||
case CARDEVICE_BPMP: return 0;
|
||||
default: reboot();
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t g_clk_reg_offsets[NUM_CAR_BANKS] = {0x010, 0x014, 0x018, 0x360, 0x364, 0x280, 0x298};
|
||||
static uint32_t g_rst_reg_offsets[NUM_CAR_BANKS] = {0x004, 0x008, 0x00C, 0x358, 0x35C, 0x28C, 0x2A4};
|
||||
|
||||
static uint32_t g_clk_clr_reg_offsets[NUM_CAR_BANKS] = {0x324, 0x32C, 0x334, 0x444, 0x44C, 0x228, 0x2A0};
|
||||
|
||||
void car_configure_oscillators(void) {
|
||||
/* Enable the crystal oscillator, setting drive strength to the saved value in PMC. */
|
||||
CLK_RST_CONTROLLER_OSC_CTRL_0 = (CLK_RST_CONTROLLER_OSC_CTRL_0 & 0xFFFFFC0E) | 1 | (((APBDEV_PMC_OSC_EDPD_OVER_0 >> 1) & 0x3F) << 4);
|
||||
|
||||
/* Set CLK_M_DIVISOR to 1 (causes actual division by 2.) */
|
||||
CLK_RST_CONTROLLER_SPARE_REG0_0 = (1 << 2);
|
||||
/* Reading the register after writing it is required to ensure value takes. */
|
||||
(void)(CLK_RST_CONTROLLER_SPARE_REG0_0);
|
||||
|
||||
/* Set TIMERUS_USEC_CFG to cycle at 0x60 / 0x5 = 19.2 MHz. */
|
||||
/* Value is (dividend << 8) | (divisor). */
|
||||
TIMERUS_USEC_CFG_0 = 0x45F;
|
||||
}
|
||||
|
||||
void car_mbist_workaround(void) {
|
||||
/* This code works around MBIST bug. */
|
||||
|
||||
/* Clear LVL2_CLK_GATE_OVR* registers. */
|
||||
CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRA_0 = 0;
|
||||
CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRB_0 = 0;
|
||||
CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRC_0 = 0;
|
||||
CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD_0 = 0;
|
||||
CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRE_0 = 0;
|
||||
|
||||
/* Clear bit patterns in CAR. */
|
||||
/* L: Reset all but RTC, TMR, GPIO, BPMP Cache (CACHE2). */
|
||||
MAKE_CAR_REG(g_clk_clr_reg_offsets[0]) = MAKE_CAR_REG(g_clk_reg_offsets[0]) & 0x7FFFFECF;
|
||||
/* H: Reset all but MC, PMC, FUSE, EMC. */
|
||||
MAKE_CAR_REG(g_clk_clr_reg_offsets[1]) = MAKE_CAR_REG(g_clk_reg_offsets[1]) & 0xFDFFFF3E;
|
||||
/* U: Reset all but CSITE, IRAM[A-D], BPMP Cache RAM (CRAM2). */
|
||||
MAKE_CAR_REG(g_clk_clr_reg_offsets[2]) = MAKE_CAR_REG(g_clk_reg_offsets[2]) & 0xFE0FFDFF;
|
||||
/* V: Reset all but MSELECT, S/PDIF audio doubler, TZRAM, SE. */
|
||||
MAKE_CAR_REG(g_clk_clr_reg_offsets[3]) = MAKE_CAR_REG(g_clk_reg_offsets[3]) & 0x3FBFFFF7;
|
||||
/* W: Reset all but PCIERX[0-5], ENTROPY. */
|
||||
MAKE_CAR_REG(g_clk_clr_reg_offsets[4]) = MAKE_CAR_REG(g_clk_reg_offsets[4]) & 0xFFDFFF03;
|
||||
/* X: Reset all but ETC, MCLK, MCLK2, I2C6, EMC_DLL, GPU, DBGAPB, PLLG_REF, . */
|
||||
MAKE_CAR_REG(g_clk_clr_reg_offsets[5]) = MAKE_CAR_REG(g_clk_reg_offsets[5]) & 0xDCFFB87F;
|
||||
/* Y: Reset all but MC_CDPA, MC_CCPA. */
|
||||
MAKE_CAR_REG(g_clk_clr_reg_offsets[6]) = MAKE_CAR_REG(g_clk_reg_offsets[6]) & 0xFFFFFCFF;
|
||||
|
||||
/* Enable clock to MC1, if CH1 is enabled in EMC. */
|
||||
if (EMC_FBIO_CFG7_0 & 4) { /* CH1_ENABLE */
|
||||
CLK_RST_CONTROLLER_CLK_ENB_W_SET_0 |= 0x40000000; /* SET_CLK_ENB_MC1 */
|
||||
}
|
||||
}
|
||||
|
||||
void clk_enable(CarDevice dev) {
|
||||
uint32_t special_reg;
|
||||
if ((special_reg = get_special_clk_reg(dev))) {
|
||||
MAKE_CAR_REG(special_reg) = get_special_clk_val(dev);
|
||||
}
|
||||
MAKE_CAR_REG(g_clk_reg_offsets[dev >> 5]) |= BIT(dev & 0x1F);
|
||||
}
|
||||
|
||||
void clk_disable(CarDevice dev) {
|
||||
MAKE_CAR_REG(g_clk_reg_offsets[dev >> 5]) &= ~(BIT(dev & 0x1F));
|
||||
}
|
||||
|
||||
void rst_enable(CarDevice dev) {
|
||||
MAKE_CAR_REG(g_rst_reg_offsets[dev >> 5]) |= BIT(dev & 0x1F);
|
||||
}
|
||||
|
||||
void rst_disable(CarDevice dev) {
|
||||
MAKE_CAR_REG(g_rst_reg_offsets[dev >> 5]) &= ~(BIT(dev & 0x1F));
|
||||
}
|
||||
|
||||
void clkrst_enable(CarDevice dev) {
|
||||
clk_enable(dev);
|
||||
rst_disable(dev);
|
||||
}
|
||||
|
||||
void clkrst_disable(CarDevice dev) {
|
||||
rst_enable(dev);
|
||||
clk_disable(dev);
|
||||
}
|
||||
|
||||
void clkrst_reboot(CarDevice dev) {
|
||||
clkrst_disable(dev);
|
||||
clkrst_enable(dev);
|
||||
}
|
||||
|
||||
void clkrst_enable_fuse_regs(bool enable) {
|
||||
CLK_RST_CONTROLLER_MISC_CLK_ENB_0 = ((CLK_RST_CONTROLLER_MISC_CLK_ENB_0 & 0xEFFFFFFF) | ((enable & 1) << 28));
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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/>.
|
||||
*/
|
||||
|
||||
#ifndef EXOSPHERE_WARMBOOT_BIN_CLOCK_AND_RESET_H
|
||||
#define EXOSPHERE_WARMBOOT_BIN_CLOCK_AND_RESET_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define CAR_BASE 0x60006000
|
||||
|
||||
#define MAKE_CAR_REG(n) MAKE_REG32(CAR_BASE + n)
|
||||
|
||||
#define CLK_RST_CONTROLLER_MISC_CLK_ENB_0 MAKE_CAR_REG(0x048)
|
||||
#define CLK_RST_CONTROLLER_OSC_CTRL_0 MAKE_CAR_REG(0x050)
|
||||
#define CLK_RST_CONTROLLER_PLLX_BASE_0 MAKE_CAR_REG(0x0E0)
|
||||
#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD_0 MAKE_CAR_REG(0x3A4)
|
||||
#define CLK_RST_CONTROLLER_RST_CPUG_CMPLX_SET_0 MAKE_CAR_REG(0x450)
|
||||
#define CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR_0 MAKE_CAR_REG(0x454)
|
||||
|
||||
#define CLK_RST_CONTROLLER_SUPER_CCLKG_DIVIDER_0 MAKE_CAR_REG(0x36C)
|
||||
#define CLK_RST_CONTROLLER_SUPER_CCLKP_DIVIDER_0 MAKE_CAR_REG(0x374)
|
||||
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C5_0 MAKE_CAR_REG(0x128)
|
||||
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_DVFS_REF_0 MAKE_CAR_REG(0x62C)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_DVFS_SOC_0 MAKE_CAR_REG(0x630)
|
||||
|
||||
#define CLK_RST_CONTROLLER_CPU_SOFTRST_CTRL2_0 MAKE_CAR_REG(0x388)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_MSELECT_0 MAKE_CAR_REG(0x3B4)
|
||||
|
||||
#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRA_0 MAKE_CAR_REG(0x0F8)
|
||||
#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRB_0 MAKE_CAR_REG(0x0FC)
|
||||
#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRC_0 MAKE_CAR_REG(0x3A0)
|
||||
#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD_0 MAKE_CAR_REG(0x3A4)
|
||||
#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRE_0 MAKE_CAR_REG(0x554)
|
||||
|
||||
#define CLK_RST_CONTROLLER_CCLKG_BURST_POLICY_0 MAKE_CAR_REG(0x368)
|
||||
#define CLK_RST_CONTROLLER_CCLKP_BURST_POLICY_0 MAKE_CAR_REG(0x370)
|
||||
|
||||
#define CLK_RST_CONTROLLER_RST_DEVICES_H_0 MAKE_CAR_REG(0x008)
|
||||
|
||||
#define CLK_RST_CONTROLLER_SPARE_REG0_0 MAKE_CAR_REG(0x55C)
|
||||
|
||||
#define CLK_RST_CONTROLLER_RST_DEV_H_SET_0 MAKE_CAR_REG(0x308)
|
||||
#define CLK_RST_CONTROLLER_RST_DEV_U_SET_0 MAKE_CAR_REG(0x310)
|
||||
|
||||
#define CLK_RST_CONTROLLER_RST_DEV_H_CLR_0 MAKE_CAR_REG(0x30C)
|
||||
#define CLK_RST_CONTROLLER_RST_DEV_U_CLR_0 MAKE_CAR_REG(0x314)
|
||||
#define CLK_RST_CONTROLLER_RST_DEV_V_CLR_0 MAKE_CAR_REG(0x434)
|
||||
|
||||
#define CLK_RST_CONTROLLER_CLK_ENB_L_SET_0 MAKE_CAR_REG(0x320)
|
||||
#define CLK_RST_CONTROLLER_CLK_ENB_H_SET_0 MAKE_CAR_REG(0x328)
|
||||
#define CLK_RST_CONTROLLER_CLK_ENB_U_SET_0 MAKE_CAR_REG(0x330)
|
||||
#define CLK_RST_CONTROLLER_CLK_ENB_V_SET_0 MAKE_CAR_REG(0x440)
|
||||
#define CLK_RST_CONTROLLER_CLK_ENB_W_SET_0 MAKE_CAR_REG(0x448)
|
||||
#define CLK_RST_CONTROLLER_CLK_ENB_Y_SET_0 MAKE_CAR_REG(0x29C)
|
||||
|
||||
#define CLK_RST_CONTROLLER_CLK_ENB_H_CLR_0 MAKE_CAR_REG(0x32C)
|
||||
#define CLK_RST_CONTROLLER_CLK_ENB_W_CLR_0 MAKE_CAR_REG(0x44C)
|
||||
|
||||
#define NUM_CAR_BANKS 7
|
||||
|
||||
typedef enum {
|
||||
CARDEVICE_UARTA = ((0 << 5) | 0x6),
|
||||
CARDEVICE_UARTB = ((0 << 5) | 0x7),
|
||||
CARDEVICE_UARTC = ((1 << 5) | 0x17),
|
||||
CARDEVICE_I2C1 = ((0 << 5) | 0xC),
|
||||
CARDEVICE_I2C5 = ((1 << 5) | 0xF),
|
||||
CARDEVICE_TZRAM = ((3 << 5) | 0x1E),
|
||||
CARDEVICE_SE = ((3 << 5) | 0x1F),
|
||||
CARDEVICE_HOST1X = ((0 << 5) | 0x1C),
|
||||
CARDEVICE_TSEC = ((2 << 5) | 0x13),
|
||||
CARDEVICE_SOR_SAFE = ((6 << 5) | 0x1E),
|
||||
CARDEVICE_SOR0 = ((5 << 5) | 0x16),
|
||||
CARDEVICE_SOR1 = ((5 << 5) | 0x17),
|
||||
CARDEVICE_KFUSE = ((1 << 5) | 0x8),
|
||||
CARDEVICE_CL_DVFS = ((4 << 5) | 0x1B),
|
||||
CARDEVICE_CORESIGHT = ((2 << 5) | 0x9),
|
||||
CARDEVICE_ACTMON = ((3 << 5) | 0x17),
|
||||
CARDEVICE_BPMP = ((0 << 5) | 0x1)
|
||||
} CarDevice;
|
||||
|
||||
void car_configure_oscillators(void);
|
||||
void car_mbist_workaround(void);
|
||||
|
||||
void clk_enable(CarDevice dev);
|
||||
void clk_disable(CarDevice dev);
|
||||
void rst_enable(CarDevice dev);
|
||||
void rst_disable(CarDevice dev);
|
||||
|
||||
void clkrst_enable(CarDevice dev);
|
||||
void clkrst_disable(CarDevice dev);
|
||||
void clkrst_reboot(CarDevice dev);
|
||||
|
||||
void clkrst_enable_fuse_regs(bool enable);
|
||||
|
||||
#endif
|
@ -1,163 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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 <stdint.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "cluster.h"
|
||||
#include "car.h"
|
||||
#include "timer.h"
|
||||
#include "pmc.h"
|
||||
#include "misc.h"
|
||||
#include "i2c.h"
|
||||
#include "flow.h"
|
||||
#include "sysreg.h"
|
||||
|
||||
static void cluster_pmc_enable_partition(uint32_t mask, uint32_t toggle) {
|
||||
/* Set toggle if unset. */
|
||||
if (!(APBDEV_PMC_PWRGATE_STATUS_0 & mask)) {
|
||||
APBDEV_PMC_PWRGATE_TOGGLE_0 = toggle;
|
||||
}
|
||||
|
||||
/* Wait until toggle set. */
|
||||
while (!(APBDEV_PMC_PWRGATE_STATUS_0 & mask)) { }
|
||||
|
||||
/* Remove clamping. */
|
||||
APBDEV_PMC_REMOVE_CLAMPING_CMD_0 = mask;
|
||||
while (APBDEV_PMC_CLAMP_STATUS_0 & mask) { }
|
||||
}
|
||||
|
||||
void cluster_initialize_cpu(void) {
|
||||
/* Hold CoreSight in reset. */
|
||||
CLK_RST_CONTROLLER_RST_DEV_U_SET_0 = 0x200;
|
||||
|
||||
/* CAR2PMC_CPU_ACK_WIDTH should be set to 0. */
|
||||
CLK_RST_CONTROLLER_CPU_SOFTRST_CTRL2_0 &= 0xFFFFF000;
|
||||
|
||||
/* Restore SB_AA64_RESET values from PMC scratch. */
|
||||
SB_AA64_RESET_LOW_0 = APBDEV_PMC_SECURE_SCRATCH34_0 | 1;
|
||||
SB_AA64_RESET_HIGH_0 = APBDEV_PMC_SECURE_SCRATCH35_0;
|
||||
|
||||
/* Set CDIV_ENB for CCLKG/CCLKP. */
|
||||
CLK_RST_CONTROLLER_SUPER_CCLKG_DIVIDER_0 = 0x80000000;
|
||||
CLK_RST_CONTROLLER_SUPER_CCLKP_DIVIDER_0 = 0x80000000;
|
||||
|
||||
/* Enable CoreSight clock, take CoreSight out of reset. */
|
||||
CLK_RST_CONTROLLER_CLK_ENB_U_SET_0 = 0x200;
|
||||
CLK_RST_CONTROLLER_RST_DEV_U_CLR_0 = 0x200;
|
||||
|
||||
/* Configure MSELECT to divide by 4, enable MSELECT clock. */
|
||||
CLK_RST_CONTROLLER_CLK_SOURCE_MSELECT_0 = 6; /* (6/2) + 1 = 4. */
|
||||
CLK_RST_CONTROLLER_CLK_ENB_V_SET_0 = 0x8;
|
||||
|
||||
/* Wait 2 us, then take MSELECT out of reset. */
|
||||
timer_wait(2);
|
||||
CLK_RST_CONTROLLER_RST_DEV_V_CLR_0 = 0x8;
|
||||
|
||||
/* Set MSELECT WRAP_TO_SLAVE_INCR[0-2], clear ERR_RESP_EN_SLAVE[1-2]. */
|
||||
MSELECT_CONFIG_0 = (MSELECT_CONFIG_0 & 0xFCFFFFFF) | 0x38000000;
|
||||
|
||||
/* Clear PLLX_ENABLE. */
|
||||
CLK_RST_CONTROLLER_PLLX_BASE_0 &= 0xBFFFFFFF;
|
||||
|
||||
/* Clear PMC scratch 190, disable PMC DPD then wait 10 us. */
|
||||
APBDEV_PMC_SCRATCH190_0 &= 0xFFFFFFFE;
|
||||
APBDEV_PMC_DPD_SAMPLE_0 = 0;
|
||||
timer_wait(10);
|
||||
|
||||
/* Configure UART2 via GPIO controller 2 G. */
|
||||
MAKE_REG32(0x6000D108) |= 4; /* GPIO_CNF */
|
||||
MAKE_REG32(0x6000D118) |= 4; /* GPIO_OE */
|
||||
MAKE_REG32(0x6000D128) &= ~4; /* GPIO_OUT */
|
||||
|
||||
/* Set CL_DVFS RSVD0 + TRISTATE, read register to make it stick. */
|
||||
PINMUX_AUX_DVFS_PWM_0 = 0x11;
|
||||
(void)PINMUX_AUX_DVFS_PWM_0;
|
||||
|
||||
/* Configure I2C. */
|
||||
PINMUX_AUX_PWR_I2C_SCL_0 = 0x40;
|
||||
PINMUX_AUX_PWR_I2C_SDA_0 = 0x40;
|
||||
|
||||
/* Enable clock to CL_DVFS, and set its source/divider. */
|
||||
CLK_RST_CONTROLLER_CLK_ENB_W_SET_0 = 0x08000000;
|
||||
CLK_RST_CONTROLLER_CLK_SOURCE_DVFS_REF_0 = 0xE;
|
||||
CLK_RST_CONTROLLER_CLK_SOURCE_DVFS_SOC_0 = 0xE;
|
||||
|
||||
/* Power on I2C5, wait 5 us, set source + take out of reset. */
|
||||
CLK_RST_CONTROLLER_CLK_ENB_H_SET_0 = 0x8000;
|
||||
CLK_RST_CONTROLLER_RST_DEV_H_SET_0 = 0x8000;
|
||||
timer_wait(5);
|
||||
CLK_RST_CONTROLLER_CLK_SOURCE_I2C5_0 = 0x4;
|
||||
CLK_RST_CONTROLLER_RST_DEV_H_CLR_0 = 0x8000;
|
||||
|
||||
/* Enable the PMIC, wait 2ms. */
|
||||
i2c_enable_pmic();
|
||||
timer_wait(2000);
|
||||
|
||||
/* Enable power to the CRAIL partition. */
|
||||
cluster_pmc_enable_partition(1, 0x100);
|
||||
|
||||
/* Remove SW clamp to CRAIL. */
|
||||
APBDEV_PMC_SET_SW_CLAMP_0 = 0;
|
||||
APBDEV_PMC_REMOVE_CLAMPING_CMD_0 = 1;
|
||||
while (APBDEV_PMC_CLAMP_STATUS_0 & 1) { }
|
||||
|
||||
/* Nintendo manually counts down from 8. I am not sure why this happens. */
|
||||
{
|
||||
volatile int32_t counter = 8;
|
||||
while (counter >= 0) {
|
||||
counter--;
|
||||
}
|
||||
}
|
||||
|
||||
/* Power off I2C5. */
|
||||
CLK_RST_CONTROLLER_RST_DEV_H_SET_0 = 0x8000;
|
||||
CLK_RST_CONTROLLER_CLK_ENB_H_CLR_0 = 0x8000;
|
||||
|
||||
/* Disable clock to CL_DVFS */
|
||||
CLK_RST_CONTROLLER_CLK_ENB_W_CLR_0 = 0x08000000;
|
||||
|
||||
/* Perform RAM repair if necessary. */
|
||||
flow_perform_ram_repair();
|
||||
|
||||
/* Enable power to the non-CPU partition. */
|
||||
cluster_pmc_enable_partition(0x8000, 0x10F);
|
||||
|
||||
/* Enable clock to PLLP_OUT_CPU, wait 2 us. */
|
||||
CLK_RST_CONTROLLER_CLK_ENB_Y_SET_0 = 0x80000000;
|
||||
timer_wait(2);
|
||||
|
||||
/* Enable clock to CPU, CPUG, wait 10 us. */
|
||||
CLK_RST_CONTROLLER_CLK_ENB_L_SET_0 = 1;
|
||||
CLK_RST_CONTROLLER_CLK_ENB_V_SET_0 = 1;
|
||||
timer_wait(10);
|
||||
|
||||
/* Set CPU clock sources to PLLP_OUT_0 + state to RUN, wait 10 us. */
|
||||
CLK_RST_CONTROLLER_CCLKG_BURST_POLICY_0 = 0x20004444;
|
||||
CLK_RST_CONTROLLER_CCLKP_BURST_POLICY_0 = 0x20004444;
|
||||
timer_wait(10);
|
||||
|
||||
/* Take non-CPU out of reset (write CLR_NONCPURESET). */
|
||||
CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR_0 = 0x20000000;
|
||||
}
|
||||
|
||||
void cluster_power_on_cpu(void) {
|
||||
/* Enable power to CE0 partition. */
|
||||
cluster_pmc_enable_partition(0x4000, 0x10E);
|
||||
|
||||
/* Clear CPU reset. */
|
||||
CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR_0 = 0x10001;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user